Skip to content

Commit 9f3860e

Browse files
committed
fix up scrgb support in JXL
after some discussion on discord with the libjxl maintainers see #2830 to do: save scrgb to eg. JPEG fails right now :( we need to do scRGB->sRGB in jpegsave
1 parent fe22f80 commit 9f3860e

File tree

2 files changed

+61
-37
lines changed

2 files changed

+61
-37
lines changed

libvips/foreign/jxlload.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@
3535

3636
/*
3737
#define DEBUG_VERBOSE
38+
*/
3839
#define DEBUG
39-
*/
4040

4141
#ifdef HAVE_CONFIG_H
4242
#include <config.h>

libvips/foreign/jxlsave.c

Lines changed: 60 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
*/
3535

3636
/*
37-
#define DEBUG
3837
*/
38+
#define DEBUG
3939

4040
#ifdef HAVE_CONFIG_H
4141
#include <config.h>
@@ -227,9 +227,12 @@ vips_foreign_save_jxl_build( VipsObject *object )
227227
{
228228
VipsForeignSave *save = (VipsForeignSave *) object;
229229
VipsForeignSaveJxl *jxl = (VipsForeignSaveJxl *) object;
230+
VipsImage **t = (VipsImage **) vips_object_local_array( object, 5 );
230231

231232
JxlEncoderOptions *options;
232233
JxlEncoderStatus status;
234+
VipsImage *in;
235+
VipsBandFormat format;
233236

234237
if( VIPS_OBJECT_CLASS( vips_foreign_save_jxl_parent_class )->
235238
build( object ) )
@@ -260,11 +263,28 @@ vips_foreign_save_jxl_build( VipsObject *object )
260263
return( -1 );
261264
}
262265

266+
in = save->ready;
267+
268+
/* Fix the input image format. JXL uses float for 0-1 linear (ie.
269+
* scRGB) only. We must convert eg. sRGB float to 8-bit for save.
270+
*/
271+
if( in->Type == VIPS_INTERPRETATION_scRGB )
272+
format = VIPS_FORMAT_FLOAT;
273+
else if( in->Type == VIPS_INTERPRETATION_RGB16 ||
274+
in->Type == VIPS_INTERPRETATION_GREY16 )
275+
format = VIPS_FORMAT_USHORT;
276+
else
277+
format = VIPS_FORMAT_UCHAR;
278+
279+
if( vips_cast( in, &t[0], format, NULL ) )
280+
return( -1 );
281+
in = t[0];
282+
263283
#ifdef HAVE_LIBJXL_JXLENCODERINITBASICINFO
264284
JxlEncoderInitBasicInfo( &jxl->info );
265285
#endif
266286

267-
switch( save->ready->BandFmt ) {
287+
switch( in->BandFmt ) {
268288
case VIPS_FORMAT_UCHAR:
269289
jxl->info.bits_per_sample = 8;
270290
jxl->info.exponent_bits_per_sample = 0;
@@ -288,7 +308,7 @@ vips_foreign_save_jxl_build( VipsObject *object )
288308
break;
289309
}
290310

291-
switch( save->ready->Type ) {
311+
switch( in->Type ) {
292312
case VIPS_INTERPRETATION_B_W:
293313
case VIPS_INTERPRETATION_GREY16:
294314
jxl->info.num_color_channels = 1;
@@ -301,18 +321,18 @@ vips_foreign_save_jxl_build( VipsObject *object )
301321
break;
302322

303323
default:
304-
jxl->info.num_color_channels = save->ready->Bands;
324+
jxl->info.num_color_channels = in->Bands;
305325
}
306326
jxl->info.num_extra_channels = VIPS_MAX( 0,
307-
save->ready->Bands - jxl->info.num_color_channels );
327+
in->Bands - jxl->info.num_color_channels );
308328

309-
jxl->info.xsize = save->ready->Xsize;
310-
jxl->info.ysize = save->ready->Ysize;
311-
jxl->format.num_channels = save->ready->Bands;
329+
jxl->info.xsize = in->Xsize;
330+
jxl->info.ysize = in->Ysize;
331+
jxl->format.num_channels = in->Bands;
312332
jxl->format.endianness = JXL_NATIVE_ENDIAN;
313333
jxl->format.align = 0;
314334

315-
if( vips_image_hasalpha( save->ready ) ) {
335+
if( vips_image_hasalpha( in ) ) {
316336
jxl->info.alpha_bits = jxl->info.bits_per_sample;
317337
jxl->info.alpha_exponent_bits =
318338
jxl->info.exponent_bits_per_sample;
@@ -322,15 +342,15 @@ vips_foreign_save_jxl_build( VipsObject *object )
322342
jxl->info.alpha_bits = 0;
323343
}
324344

325-
if( vips_image_get_typeof( save->ready, "stonits" ) ) {
345+
if( vips_image_get_typeof( in, "stonits" ) ) {
326346
double stonits;
327347

328-
if( vips_image_get_double( save->ready, "stonits", &stonits ) )
348+
if( vips_image_get_double( in, "stonits", &stonits ) )
329349
return( -1 );
330350
jxl->info.intensity_target = stonits;
331351
}
332352

333-
/* We will be setting the ICC profile, or calling
353+
/* We will be setting the ICC profile, and/or calling
334354
* JxlEncoderSetColorEncoding().
335355
*/
336356
jxl->info.uses_original_profile = TRUE;
@@ -340,13 +360,13 @@ vips_foreign_save_jxl_build( VipsObject *object )
340360
return( -1 );
341361
}
342362

343-
/* Set ICC profile, sRGB, or scRGB.
363+
/* Set ICC profile.
344364
*/
345-
if( vips_image_get_typeof( save->ready, VIPS_META_ICC_NAME ) ) {
365+
if( vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ) {
346366
const void *data;
347367
size_t length;
348368

349-
if( vips_image_get_blob( save->ready,
369+
if( vips_image_get_blob( in,
350370
VIPS_META_ICC_NAME, &data, &length ) )
351371
return( -1 );
352372

@@ -360,36 +380,38 @@ vips_foreign_save_jxl_build( VipsObject *object )
360380
return( -1 );
361381
}
362382
}
363-
else {
364-
if( save->ready->Type == VIPS_INTERPRETATION_scRGB ) {
383+
384+
/* libjxl will use linear 0 - 1 by default for float, so we don't need
385+
* to call JxlColorEncodingSetToLinearSRGB() or
386+
* JxlColorEncodingSetToSRGB().
387+
*/
388+
if( in->Type == VIPS_INTERPRETATION_scRGB ) {
365389
#ifdef DEBUG
366-
printf( "setting sRGB colourspace\n" );
390+
printf( "setting scRGB colourspace\n" );
367391
#endif /*DEBUG*/
368392

369-
JxlColorEncodingSetToLinearSRGB( &jxl->color_encoding,
370-
jxl->format.num_channels < 3 );
371-
}
372-
else {
393+
JxlColorEncodingSetToLinearSRGB( &jxl->color_encoding,
394+
jxl->format.num_channels < 3 );
395+
}
396+
else {
373397
#ifdef DEBUG
374-
printf( "setting scRGB colourspace\n" );
398+
printf( "setting sRGB colourspace\n" );
375399
#endif /*DEBUG*/
376400

377-
JxlColorEncodingSetToSRGB( &jxl->color_encoding,
378-
jxl->format.num_channels < 3 );
379-
}
401+
JxlColorEncodingSetToSRGB( &jxl->color_encoding,
402+
jxl->format.num_channels < 3 );
403+
}
380404

381-
if( JxlEncoderSetColorEncoding( jxl->encoder,
382-
&jxl->color_encoding ) ) {
383-
vips_foreign_save_jxl_error( jxl,
384-
"JxlEncoderSetColorEncoding" );
385-
return( -1 );
386-
}
405+
if( JxlEncoderSetColorEncoding( jxl->encoder, &jxl->color_encoding ) ) {
406+
vips_foreign_save_jxl_error( jxl,
407+
"JxlEncoderSetColorEncoding" );
408+
return( -1 );
387409
}
388410

389411
/* Render the entire image in memory. libjxl seems to be missing
390412
* tile-based write at the moment.
391413
*/
392-
if( vips_image_wio_input( save->ready ) )
414+
if( vips_image_wio_input( in ) )
393415
return( -1 );
394416

395417
options = JxlEncoderOptionsCreate( jxl->encoder, NULL );
@@ -409,8 +431,8 @@ vips_foreign_save_jxl_build( VipsObject *object )
409431
#endif /*DEBUG*/
410432

411433
if( JxlEncoderAddImageFrame( options, &jxl->format,
412-
VIPS_IMAGE_ADDR( save->ready, 0, 0 ),
413-
VIPS_IMAGE_SIZEOF_IMAGE( save->ready ) ) ) {
434+
VIPS_IMAGE_ADDR( in, 0, 0 ),
435+
VIPS_IMAGE_SIZEOF_IMAGE( in ) ) ) {
414436
vips_foreign_save_jxl_error( jxl, "JxlEncoderAddImageFrame" );
415437
return( -1 );
416438
}
@@ -485,7 +507,9 @@ vips_foreign_save_jxl_class_init( VipsForeignSaveJxlClass *class )
485507

486508
foreign_class->suffs = vips__jxl_suffs;
487509

488-
save_class->saveable = VIPS_SAVEABLE_ANY;
510+
/* This lets throuigh scRGB too, which we then save as jxl float.
511+
*/
512+
save_class->saveable = VIPS_SAVEABLE_RGBA;
489513
save_class->format_table = bandfmt_jxl;
490514

491515
VIPS_ARG_INT( class, "tier", 10,

0 commit comments

Comments
 (0)