load header done
This commit is contained in:
parent
c7f69718da
commit
2a249a3049
@ -1454,6 +1454,9 @@ EXIF metadata support with libexif: $with_libexif
|
||||
|
||||
## File format support
|
||||
JPEG load/save with libjpeg: $with_jpeg
|
||||
JXL load/save with libjxl: $with_libjxl
|
||||
JPEG2000 load/save with libopenjp2: $with_libopenjp2
|
||||
(requires libopenjp2 2.2 or later)
|
||||
PNG load with libspng: $with_libspng
|
||||
(requires libspng-0.6 or later)
|
||||
PNG load/save with libpng: $with_png
|
||||
@ -1464,7 +1467,6 @@ TIFF load/save with libtiff: $with_tiff
|
||||
image pyramid save: $with_gsf
|
||||
(requires libgsf-1 1.14.26 or later)
|
||||
HEIC/AVIF load/save with libheif: $with_heif
|
||||
JXL load/save with libjxl: $with_libjxl
|
||||
WebP load/save with libwebp: $with_libwebp
|
||||
(requires libwebp, libwebpmux, libwebpdemux 0.6.0 or later)
|
||||
PDF load with PDFium: $with_pdfium
|
||||
@ -1473,8 +1475,6 @@ PDF load with poppler-glib: $with_poppler
|
||||
SVG load with librsvg-2.0: $with_rsvg
|
||||
(requires librsvg-2.0 2.34.0 or later)
|
||||
EXR load with OpenEXR: $with_OpenEXR
|
||||
JPEG2000 load/save with libopenjp2: $with_libopenjp2
|
||||
(requires libopenjp2 2.2 or later)
|
||||
slide load with OpenSlide: $with_openslide
|
||||
(requires openslide-3.3.0 or later)
|
||||
Matlab load with matio: $with_matio
|
||||
|
@ -75,6 +75,8 @@ typedef struct _VipsForeignLoadJxl {
|
||||
/* Base image properties.
|
||||
*/
|
||||
JxlBasicInfo info;
|
||||
size_t icc_size;
|
||||
uint8_t *icc_data;
|
||||
|
||||
/* Decompress state.
|
||||
*/
|
||||
@ -120,15 +122,25 @@ vips_foreign_load_jxl_dispose( GObject *gobject )
|
||||
|
||||
VIPS_FREEF( JxlThreadParallelRunnerDestroy, jxl->runner );
|
||||
VIPS_FREEF( JxlDecoderDestroy, jxl->decoder );
|
||||
VIPS_FREE( jxl->icc_data );
|
||||
|
||||
G_OBJECT_CLASS( vips_foreign_load_jxl_parent_class )->
|
||||
dispose( gobject );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_jxl_error( VipsForeignLoadJxl *jxl, const char *details )
|
||||
{
|
||||
VipsObjectClass *klass = VIPS_OBJECT_GET_CLASS( object );
|
||||
|
||||
/* TODO ... jxl has no way to get error messages at the moemnt.
|
||||
*/
|
||||
vips_error( klass->nickname, "%s", details );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_load_jxl_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *klass = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsForeignLoadJxl *jxl = (VipsForeignLoadJxl *) object;
|
||||
|
||||
JxlDecoderStatus status;
|
||||
@ -141,18 +153,18 @@ vips_foreign_load_jxl_build( VipsObject *object )
|
||||
vips_concurrency_get() );
|
||||
jxl->decoder = JxlDecoderCreate( nullptr );
|
||||
|
||||
/* We are only interested in end of header and end of pixel data.
|
||||
*/
|
||||
if( JxlDecoderSubscribeEvents( jxl->decoder,
|
||||
JXL_DEC_BASIC_INFO |
|
||||
JXL_DEC_COLOR_ENCODING |
|
||||
JXL_DEC_FULL_IMAGE ) != JXL_DEC_SUCCESS ) {
|
||||
vips_error( klass->nickname,
|
||||
"%s", _( "JxlDecoderSubscribeEvents failed" ) );
|
||||
vips_foreign_load_jxl_error( jxl, "JxlDecoderSubscribeEvents" );
|
||||
return( -1 );
|
||||
}
|
||||
if( JxlDecoderSetParallelRunner( jxl->decoder,
|
||||
JxlThreadParallelRunner, jxl->runner ) != JXL_DEC_SUCCESS ) {
|
||||
vips_error( klass->nickname,
|
||||
"%s", _( "JxlDecoderSetParallelRunner failed" ) );
|
||||
vips_foreign_load_jxl_error( jxl,
|
||||
"JxlDecoderSetParallelRunner" );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
@ -163,115 +175,31 @@ vips_foreign_load_jxl_build( VipsObject *object )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Always read as scRGBA.
|
||||
*/
|
||||
static JxlPixelFormat vips_foreign_load_jxl_format =
|
||||
{ 4, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0 };
|
||||
|
||||
static int
|
||||
vips_foreign_load_jxl_set_header( VipsForeignLoadJxl *jxl, VipsImage *out )
|
||||
{
|
||||
VipsObjectClass *klass = VIPS_OBJECT_GET_CLASS( jxl );
|
||||
opj_image_comp_t *first = &jxl->image->comps[0];
|
||||
|
||||
VipsBandFormat format;
|
||||
VipsInterpretation interpretation;
|
||||
|
||||
/* OpenJPEG only supports up to 31 bpp. Treat it as 32.
|
||||
*/
|
||||
if( first->prec <= 8 )
|
||||
format = first->sgnd ? VIPS_FORMAT_CHAR : VIPS_FORMAT_UCHAR;
|
||||
else if( first->prec <= 16 )
|
||||
format = first->sgnd ? VIPS_FORMAT_SHORT : VIPS_FORMAT_USHORT;
|
||||
else
|
||||
format = first->sgnd ? VIPS_FORMAT_INT : VIPS_FORMAT_UINT;
|
||||
|
||||
switch( jxl->image->color_space ) {
|
||||
case OPJ_CLRSPC_SYCC:
|
||||
case OPJ_CLRSPC_EYCC:
|
||||
/* Map these to RGB.
|
||||
*/
|
||||
interpretation = vips_format_sizeof( format ) == 1 ?
|
||||
VIPS_INTERPRETATION_sRGB :
|
||||
VIPS_INTERPRETATION_RGB16;
|
||||
jxl->ycc_to_rgb = TRUE;
|
||||
break;
|
||||
|
||||
case OPJ_CLRSPC_GRAY:
|
||||
interpretation = vips_format_sizeof( format ) == 1 ?
|
||||
VIPS_INTERPRETATION_B_W :
|
||||
VIPS_INTERPRETATION_GREY16;
|
||||
break;
|
||||
|
||||
case OPJ_CLRSPC_SRGB:
|
||||
interpretation = vips_format_sizeof( format ) == 1 ?
|
||||
VIPS_INTERPRETATION_sRGB :
|
||||
VIPS_INTERPRETATION_RGB16;
|
||||
break;
|
||||
|
||||
case OPJ_CLRSPC_CMYK:
|
||||
interpretation = VIPS_INTERPRETATION_CMYK;
|
||||
break;
|
||||
|
||||
case OPJ_CLRSPC_UNSPECIFIED:
|
||||
/* Try to guess something sensible.
|
||||
*/
|
||||
if( jxl->image->numcomps < 3 )
|
||||
interpretation = vips_format_sizeof( format ) == 1 ?
|
||||
VIPS_INTERPRETATION_B_W :
|
||||
VIPS_INTERPRETATION_GREY16;
|
||||
else
|
||||
interpretation = vips_format_sizeof( format ) == 1 ?
|
||||
VIPS_INTERPRETATION_sRGB :
|
||||
VIPS_INTERPRETATION_RGB16;
|
||||
|
||||
/* Unspecified with three bands and subsampling on bands 2 and
|
||||
* 3 is usually YCC.
|
||||
*/
|
||||
if( jxl->image->numcomps == 3 &&
|
||||
jxl->image->comps[0].dx == 1 &&
|
||||
jxl->image->comps[0].dy == 1 &&
|
||||
jxl->image->comps[1].dx > 1 &&
|
||||
jxl->image->comps[1].dy > 1 &&
|
||||
jxl->image->comps[2].dx > 1 &&
|
||||
jxl->image->comps[2].dy > 1)
|
||||
jxl->ycc_to_rgb = TRUE;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
vips_error( klass->nickname,
|
||||
_( "unsupported colourspace %d" ),
|
||||
jxl->image->color_space );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Even though this is a tiled reader, we hint thinstrip since with
|
||||
* the cache we are quite happy serving that if anything downstream
|
||||
/* Even though this is a full image reader, we hint thinstrip since
|
||||
* we are quite happy serving that if anything downstream
|
||||
* would like it.
|
||||
*/
|
||||
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );
|
||||
|
||||
vips_image_init_fields( out,
|
||||
first->w, first->h, jxl->image->numcomps, format,
|
||||
VIPS_CODING_NONE, interpretation, 1.0, 1.0 );
|
||||
jxl->info.width, jxl->info.height, 4, VIPS_FORMAT_FLOAT,
|
||||
VIPS_CODING_NONE, VIPS_INTERPRETATION_scRGB, 1.0, 1.0 );
|
||||
|
||||
/* openjpeg allows left and top of the coordinate grid to be
|
||||
* non-zero. These are always in unshrunk coordinates.
|
||||
*/
|
||||
out->Xoffset =
|
||||
-VIPS_ROUND_INT( (double) jxl->image->x0 / jxl->shrink );
|
||||
out->Yoffset =
|
||||
-VIPS_ROUND_INT( (double) jxl->image->y0 / jxl->shrink );
|
||||
|
||||
if( jxl->image->icc_profile_buf &&
|
||||
jxl->image->icc_profile_len > 0 )
|
||||
vips_image_set_blob_copy( out, VIPS_META_ICC_NAME,
|
||||
jxl->image->icc_profile_buf,
|
||||
jxl->image->icc_profile_len );
|
||||
|
||||
/* Map number of layers in image to pages.
|
||||
*/
|
||||
if( jxl->info &&
|
||||
jxl->info->m_default_tile_info.tccp_info )
|
||||
vips_image_set_int( out, VIPS_META_N_PAGES,
|
||||
jxl->info->m_default_tile_info.tccp_info->
|
||||
numresolutions );
|
||||
if( jxl->icc_data &&
|
||||
jxl->icc_size > 0 ) {
|
||||
vips_image_set_blob( out, VIPS_META_ICC_NAME, vips_free,
|
||||
jxl->icc_data, jxl->icc_size );
|
||||
jxl->icc_data = nullptr;
|
||||
jxl->icc_size = 0;
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@ -371,10 +299,11 @@ vips_foreign_load_jxl_process( VipsForeignLoadJxl *jxl )
|
||||
|
||||
while( (status = JxlDecoderProcessInput( jx->decoder )) ==
|
||||
JXL_DEC_NEED_MORE_INPUT ) {
|
||||
size_t bytes_remaining;
|
||||
size_t unused;
|
||||
|
||||
bytes_remaining = JxlDecoderReleaseInput( jxl->decoder );
|
||||
if( vips_foreign_load_jxl_fill_input( jxl, bytes_remaining ) )
|
||||
unused = JxlDecoderReleaseInput( jxl->decoder );
|
||||
if( vips_foreign_load_jxl_fill_input( jxl, unused ) &&
|
||||
unused == 0 )
|
||||
return( JXL_DEC_ERROR );
|
||||
JxlDecoderSetInput( jxl->decoder,
|
||||
jxl->input_buffer, jxl->bytes_remaining );
|
||||
@ -386,17 +315,28 @@ vips_foreign_load_jxl_process( VipsForeignLoadJxl *jxl )
|
||||
return( status );
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
vips_foreign_load_jxl_header( VipsForeignLoad *load )
|
||||
{
|
||||
VipsObjectClass *klass = VIPS_OBJECT_GET_CLASS( load );
|
||||
VipsForeignLoadJxl *jxl = (VipsForeignLoadJxl *) load;
|
||||
|
||||
JxlDecoderStatus status;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_foreign_load_jxl_header:\n" );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
/* Even though this is a full image reader, we hint thinstrip since
|
||||
* we are quite happy serving that if anything downstream
|
||||
* would like it.
|
||||
*/
|
||||
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );
|
||||
|
||||
vips_image_init_fields( out,
|
||||
jxl->info.width, first->h, jxl->image->numcomps, format,
|
||||
VIPS_CODING_NONE, interpretation, 1.0, 1.0 );
|
||||
|
||||
if( vips_foreign_load_jxl_fill_input( jxl, 0 ) )
|
||||
return( -1 );
|
||||
JxlDecoderSetInput( jx->decoder,
|
||||
@ -405,89 +345,48 @@ vips_foreign_load_jxl_header( VipsForeignLoad *load )
|
||||
/* Read to the end of the header.
|
||||
*/
|
||||
do {
|
||||
JxlDecoderStatus status;
|
||||
|
||||
status = vips_foreign_load_jxl_process( jxl );
|
||||
if( status == JXL_DEC_ERROR )
|
||||
switch( (status = vips_foreign_load_jxl_process( jxl )) ) {
|
||||
case JXL_DEC_ERROR:
|
||||
vips_foreign_load_jxl_error( jxl,
|
||||
"JxlDecoderProcessInput" );
|
||||
return( -1 );
|
||||
|
||||
case JXL_DEC_BASIC_INFO:
|
||||
if( JxlDecoderGetBasicInfo( jxl->decoder,
|
||||
&jxl->info ) ) {
|
||||
vips_foreign_load_jxl_error( jxl,
|
||||
"JxlDecoderGetBasicInfo" );
|
||||
return( -1 );
|
||||
}
|
||||
break;
|
||||
|
||||
case JXL_DEC_COLOR_ENCODING:
|
||||
if( JxlDecoderGetICCProfileSize( jxl->decoder,
|
||||
&vips_foreign_load_jxl_format,
|
||||
JXL_COLOR_PROFILE_TARGET_DATA,
|
||||
&jxl->icc_size ) ) {
|
||||
vips_foreign_load_jxl_error( jxl,
|
||||
"JxlDecoderGetICCProfileSize" );
|
||||
return( -1 );
|
||||
}
|
||||
if( !(jxl->icc_data = vips_malloc( nullptr,
|
||||
jxl->icc_size )) )
|
||||
return( -1 );
|
||||
if( JxlDecoderGetColorAsICCProfile(
|
||||
jxl->decoder,
|
||||
JXL_COLOR_PROFILE_TARGET_DATA,
|
||||
jxl->icc_data, jxl->icc_size ) ) {
|
||||
vips_foreign_load_jxl_error( jxl,
|
||||
"JxlDecoderGetColorAsICCProfile" );
|
||||
return( -1 );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} while( status != JXL_DEC_COLOR_ENCODING );
|
||||
|
||||
JxlDecoderGetBasicInfo( jxl->decoder, &jxl->info );
|
||||
|
||||
|
||||
jxl->format = vips_foreign_load_jxl_get_format( jxl->source );
|
||||
vips_source_rewind( jxl->source );
|
||||
if( !(jxl->codec = opj_create_decompress( jxl->format )) )
|
||||
return( -1 );
|
||||
|
||||
vips_foreign_load_jxl_attach_handlers( jxl, jxl->codec );
|
||||
|
||||
jxl->shrink = 1 << jxl->page;
|
||||
jxl->parameters.cp_reduce = jxl->page;
|
||||
if( !opj_setup_decoder( jxl->codec, &jxl->parameters ) )
|
||||
return( -1 );
|
||||
|
||||
#ifdef HAVE_LIBJXL_THREADING
|
||||
/* Use eg. VIPS_CONCURRENCY etc. to set n-cpus, if this openjpeg has
|
||||
* stable support.
|
||||
*/
|
||||
opj_codec_set_threads( jxl->codec, vips_concurrency_get() );
|
||||
#endif /*HAVE_LIBJXL_THREADING*/
|
||||
|
||||
if( !opj_read_header( jxl->stream, jxl->codec, &jxl->image ) )
|
||||
return( -1 );
|
||||
if( !(jxl->info = opj_get_cstr_info( jxl->codec )) )
|
||||
return( -1 );
|
||||
|
||||
#ifdef DEBUG
|
||||
vips_foreign_load_jxl_print( jxl );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
/* We only allow images where all components have the same format.
|
||||
*/
|
||||
if( jxl->image->numcomps > MAX_BANDS ) {
|
||||
vips_error( klass->nickname,
|
||||
"%s", _( "too many image bands" ) );
|
||||
return( -1 );
|
||||
}
|
||||
if( jxl->image->numcomps == 0 ) {
|
||||
vips_error( klass->nickname,
|
||||
"%s", _( "no image components" ) );
|
||||
return( -1 );
|
||||
}
|
||||
first = &jxl->image->comps[0];
|
||||
for( i = 1; i < jxl->image->numcomps; i++ ) {
|
||||
opj_image_comp_t *this = &jxl->image->comps[i];
|
||||
|
||||
if( this->x0 != first->x0 ||
|
||||
this->y0 != first->y0 ||
|
||||
this->w * this->dx != first->w * first->dx ||
|
||||
this->h * this->dy != first->h * first->dy ||
|
||||
this->resno_decoded != first->resno_decoded ||
|
||||
this->factor != first->factor ) {
|
||||
vips_error( klass->nickname,
|
||||
"%s", _( "components differ in geometry" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( this->prec != first->prec ||
|
||||
this->bpp != first->bpp ||
|
||||
this->sgnd != first->sgnd ) {
|
||||
vips_error( klass->nickname,
|
||||
"%s", _( "components differ in precision" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* If dx/dy are not 1, we'll need to upsample components during
|
||||
* tile packing.
|
||||
*/
|
||||
if( this->dx != first->dx ||
|
||||
this->dy != first->dy ||
|
||||
first->dx != 1 ||
|
||||
first->dy != 1 )
|
||||
jxl->upsample = TRUE;
|
||||
}
|
||||
|
||||
if( vips_foreign_load_jxl_set_header( jxl, load->out ) )
|
||||
return( -1 );
|
||||
|
||||
@ -497,281 +396,11 @@ vips_foreign_load_jxl_header( VipsForeignLoad *load )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#define PACK( TYPE ) { \
|
||||
TYPE *tq = (TYPE *) q; \
|
||||
\
|
||||
for( x = 0; x < length; x++ ) { \
|
||||
for( i = 0; i < b; i++ ) \
|
||||
tq[i] = planes[i][x]; \
|
||||
\
|
||||
tq += b; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define PACK_UPSAMPLE( TYPE ) { \
|
||||
TYPE *tq = (TYPE *) q; \
|
||||
\
|
||||
for( x = 0; x < length; x++ ) { \
|
||||
for( i = 0; i < b; i++ ) { \
|
||||
int dx = jxl->image->comps[i].dx; \
|
||||
int pixel = planes[i][x / dx]; \
|
||||
\
|
||||
tq[i] = pixel; \
|
||||
} \
|
||||
\
|
||||
tq += b; \
|
||||
} \
|
||||
}
|
||||
|
||||
/* Pack the set of openjpeg components into a libvips region. left/top are the
|
||||
* offsets into the tile in pixel coordinates where we should start reading.
|
||||
*/
|
||||
static void
|
||||
vips_foreign_load_jxl_pack( VipsForeignLoadJxl *jxl,
|
||||
VipsImage *image, VipsPel *q,
|
||||
int left, int top, int length )
|
||||
{
|
||||
int *planes[MAX_BANDS];
|
||||
int b = jxl->image->numcomps;
|
||||
|
||||
int x, i;
|
||||
|
||||
for( i = 0; i < b; i++ ) {
|
||||
opj_image_comp_t *comp = &jxl->image->comps[i];
|
||||
|
||||
planes[i] = comp->data + (top / comp->dy) * comp->w +
|
||||
(left / comp->dx);
|
||||
}
|
||||
|
||||
if( jxl->upsample )
|
||||
switch( image->BandFmt ) {
|
||||
case VIPS_FORMAT_CHAR:
|
||||
case VIPS_FORMAT_UCHAR:
|
||||
PACK_UPSAMPLE( unsigned char );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_SHORT:
|
||||
case VIPS_FORMAT_USHORT:
|
||||
PACK_UPSAMPLE( unsigned short );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_INT:
|
||||
case VIPS_FORMAT_UINT:
|
||||
PACK_UPSAMPLE( unsigned int );
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
}
|
||||
else
|
||||
/* Fast no-upsample path.
|
||||
*/
|
||||
switch( image->BandFmt ) {
|
||||
case VIPS_FORMAT_CHAR:
|
||||
case VIPS_FORMAT_UCHAR:
|
||||
PACK( unsigned char );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_SHORT:
|
||||
case VIPS_FORMAT_USHORT:
|
||||
PACK( unsigned short );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_INT:
|
||||
case VIPS_FORMAT_UINT:
|
||||
PACK( unsigned int );
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ycc->rgb coversion adapted from openjpeg src/bin/common/color.c
|
||||
*
|
||||
* See also https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion
|
||||
*/
|
||||
#define YCC_TO_RGB( TYPE ) { \
|
||||
TYPE *tq = (TYPE *) q; \
|
||||
\
|
||||
for( x = 0; x < length; x++ ) { \
|
||||
int y = tq[0]; \
|
||||
int cb = tq[1] - offset; \
|
||||
int cr = tq[2] - offset; \
|
||||
\
|
||||
int r, g, b; \
|
||||
\
|
||||
r = y + (int)(1.402 * (float)cr); \
|
||||
tq[0] = VIPS_CLIP( 0, r, upb ); \
|
||||
\
|
||||
g = y - (int)(0.344 * (float)cb + 0.714 * (float)cr); \
|
||||
tq[1] = VIPS_CLIP( 0, g, upb ); \
|
||||
\
|
||||
b = y + (int)(1.772 * (float)cb); \
|
||||
tq[2] = VIPS_CLIP( 0, b, upb ); \
|
||||
\
|
||||
tq += 3; \
|
||||
} \
|
||||
}
|
||||
|
||||
/* YCC->RGB for a line of pels.
|
||||
*/
|
||||
static void
|
||||
vips_foreign_load_jxl_ycc_to_rgb( VipsForeignLoadJxl *jxl,
|
||||
VipsPel *q, int length )
|
||||
{
|
||||
VipsForeignLoad *load = (VipsForeignLoad *) jxl;
|
||||
int prec = jxl->image->comps[0].prec;
|
||||
int offset = 1 << (prec - 1);
|
||||
int upb = (1 << prec) - 1;
|
||||
|
||||
int x;
|
||||
|
||||
switch( load->out->BandFmt ) {
|
||||
case VIPS_FORMAT_CHAR:
|
||||
case VIPS_FORMAT_UCHAR:
|
||||
YCC_TO_RGB( unsigned char );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_SHORT:
|
||||
case VIPS_FORMAT_USHORT:
|
||||
YCC_TO_RGB( unsigned short );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_INT:
|
||||
case VIPS_FORMAT_UINT:
|
||||
YCC_TO_RGB( unsigned int );
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop over the output region, painting in tiles from the file.
|
||||
*/
|
||||
static int
|
||||
vips_foreign_load_jxl_generate( VipsRegion *out,
|
||||
void *seq, void *a, void *b, gboolean *stop )
|
||||
{
|
||||
VipsForeignLoad *load = (VipsForeignLoad *) a;
|
||||
VipsForeignLoadJxl *jxl = (VipsForeignLoadJxl *) load;
|
||||
VipsRect *r = &out->valid;
|
||||
|
||||
/* jxl get smaller with the layer size.
|
||||
*/
|
||||
int tile_width = VIPS_ROUND_UINT(
|
||||
(double) jxl->info->tdx / jxl->shrink );
|
||||
int tile_height = VIPS_ROUND_UINT(
|
||||
(double) jxl->info->tdy / jxl->shrink );
|
||||
|
||||
/* ... so tiles_across is always the same.
|
||||
*/
|
||||
int tiles_across = jxl->info->tw;
|
||||
|
||||
int x, y, z;
|
||||
|
||||
#ifdef DEBUG_VERBOSE
|
||||
printf( "vips_foreign_load_jxl_generate: "
|
||||
"left = %d, top = %d, width = %d, height = %d\n",
|
||||
r->left, r->top, r->width, r->height );
|
||||
#endif /*DEBUG_VERBOSE*/
|
||||
|
||||
/* If openjpeg has flagged an error, the library is not in a known
|
||||
* state and it's not safe to call again.
|
||||
*/
|
||||
if( jxl->n_errors )
|
||||
return( 0 );
|
||||
|
||||
y = 0;
|
||||
while( y < r->height ) {
|
||||
VipsRect tile, hit;
|
||||
|
||||
/* Not necessary, but it stops static analyzers complaining
|
||||
* about a used-before-set.
|
||||
*/
|
||||
hit.height = 0;
|
||||
|
||||
x = 0;
|
||||
while( x < r->width ) {
|
||||
/* Tile the xy falls in, in tile numbers.
|
||||
*/
|
||||
int tx = (r->left + x) / tile_width;
|
||||
int ty = (r->top + y) / tile_height;
|
||||
|
||||
/* Pixel coordinates of the tile that xy falls in.
|
||||
*/
|
||||
int xs = tx * tile_width;
|
||||
int ys = ty * tile_height;
|
||||
|
||||
int tile_index = ty * tiles_across + tx;
|
||||
|
||||
/* Fetch the tile.
|
||||
*/
|
||||
#ifdef DEBUG_VERBOSE
|
||||
printf( " fetch tile %d\n", tile_index );
|
||||
#endif /*DEBUG_VERBOSE*/
|
||||
if( !opj_get_decoded_tile( jxl->codec,
|
||||
jxl->stream, jxl->image, tile_index ) )
|
||||
return( -1 );
|
||||
|
||||
/* Intersect tile with request to get pixels we need
|
||||
* to copy out.
|
||||
*/
|
||||
tile.left = xs;
|
||||
tile.top = ys;
|
||||
tile.width = tile_width;
|
||||
tile.height = tile_height;
|
||||
vips_rect_intersectrect( &tile, r, &hit );
|
||||
|
||||
/* Unpack hit pixels to buffer in vips layout.
|
||||
*/
|
||||
for( z = 0; z < hit.height; z++ ) {
|
||||
VipsPel *q = VIPS_REGION_ADDR( out,
|
||||
hit.left, hit.top + z );
|
||||
|
||||
vips_foreign_load_jxl_pack( jxl,
|
||||
out->im, q,
|
||||
hit.left - tile.left,
|
||||
hit.top - tile.top + z,
|
||||
hit.width );
|
||||
|
||||
if( jxl->ycc_to_rgb )
|
||||
vips_foreign_load_jxl_ycc_to_rgb( jxl,
|
||||
q, hit.width );
|
||||
}
|
||||
|
||||
x += hit.width;
|
||||
}
|
||||
|
||||
/* This will be the same for all tiles in the row we've just
|
||||
* done.
|
||||
*/
|
||||
y += hit.height;
|
||||
}
|
||||
|
||||
if( load->fail &&
|
||||
jxl->n_errors > 0 )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_load_jxl_load( VipsForeignLoad *load )
|
||||
{
|
||||
VipsForeignLoadJxl *jxl = (VipsForeignLoadJxl *) load;
|
||||
|
||||
/* jxl tiles get smaller with the layer size, but we don't want tiny
|
||||
* tiles for the libvips tile cache, so leave them at the base size.
|
||||
*/
|
||||
int tile_width = jxl->info->tdx;
|
||||
int tile_height = jxl->info->tdy;
|
||||
int tiles_across = jxl->info->tw;
|
||||
|
||||
VipsImage **t = (VipsImage **)
|
||||
vips_object_local_array( VIPS_OBJECT( load ), 3 );
|
||||
|
||||
@ -783,20 +412,7 @@ vips_foreign_load_jxl_load( VipsForeignLoad *load )
|
||||
if( vips_foreign_load_jxl_set_header( jxl, t[0] ) )
|
||||
return( -1 );
|
||||
|
||||
if( vips_image_generate( t[0],
|
||||
NULL, vips_foreign_load_jxl_generate, NULL, jxl, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
/* Copy to out, adding a cache. Enough tiles for two complete
|
||||
* rows, plus 50%.
|
||||
*/
|
||||
if( vips_tilecache( t[0], &t[1],
|
||||
"tile_width", tile_width,
|
||||
"tile_height", tile_height,
|
||||
"max_tiles", 3 * tiles_across,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
if( vips_image_write( t[1], load->real ) )
|
||||
if( vips_image_write( t[0], load->real ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
@ -814,20 +430,13 @@ vips_foreign_load_jxl_class_init( VipsForeignLoadJxlClass *klass )
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "jxlload_base";
|
||||
object_class->description = _( "load JPEG2000 image" );
|
||||
object_class->description = _( "load JPEG-XL image" );
|
||||
object_class->build = vips_foreign_load_jxl_build;
|
||||
|
||||
load_class->get_flags = vips_foreign_load_jxl_get_flags;
|
||||
load_class->header = vips_foreign_load_jxl_header;
|
||||
load_class->load = vips_foreign_load_jxl_load;
|
||||
|
||||
VIPS_ARG_INT( klass, "page", 20,
|
||||
_( "Page" ),
|
||||
_( "Load this page from the image" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadJxl, page ),
|
||||
0, 100000, 0 );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
@ -871,7 +480,7 @@ vips_foreign_load_jxl_file_build( VipsObject *object )
|
||||
}
|
||||
|
||||
const char *vips__jxl_suffs[] =
|
||||
{ ".j2k", ".jp2", ".jpt", ".j2c", ".jpc", NULL };
|
||||
{ ".jxl", NULL };
|
||||
|
||||
static int
|
||||
vips_foreign_load_jxl_is_a( const char *filename )
|
||||
@ -1079,22 +688,7 @@ vips_foreign_load_jxl_source_init(
|
||||
* @out: (out): decompressed image
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @page: %gint, load this page
|
||||
*
|
||||
* Read a JPEG2000 image. The loader supports 8, 16 and 32-bit int pixel
|
||||
* values, signed and unsigned.
|
||||
* It supports greyscale, RGB, YCC, CMYK and
|
||||
* multispectral colour spaces.
|
||||
* It will read any ICC profile on
|
||||
* the image.
|
||||
*
|
||||
* It will only load images where all channels are the same format.
|
||||
*
|
||||
* Use @page to set the page to load, where page 0 is the base resolution
|
||||
* image and higher-numbered pages are x2 reductions. Use the metadata item
|
||||
* "n-pages" to find the number of pyramid layers.
|
||||
* Read a JPEG-XL image.
|
||||
*
|
||||
* See also: vips_image_new_from_file().
|
||||
*
|
||||
@ -1120,10 +714,6 @@ vips_jxlload( const char *filename, VipsImage **out, ... )
|
||||
* @out: (out): image to write
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @page: %gint, load this page
|
||||
*
|
||||
* Exactly as vips_jxlload(), but read from a source.
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
@ -1154,10 +744,6 @@ vips_jxlload_buffer( void *buf, size_t len, VipsImage **out, ... )
|
||||
* @out: (out): decompressed image
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @page: %gint, load this page
|
||||
*
|
||||
* Exactly as vips_jxlload(), but read from a source.
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
|
@ -2,7 +2,7 @@ EXTRA_DIST = \
|
||||
README-ns \
|
||||
README.md \
|
||||
patches \
|
||||
update.sh \
|
||||
update.sh \
|
||||
utils
|
||||
|
||||
noinst_LTLIBRARIES = libnsgif.la
|
||||
|
Loading…
Reference in New Issue
Block a user