add jp2k load of untiled images

with this patch, untiled jp2k images are loaded in chunks, saving loads
of memory (but runs much slower)
This commit is contained in:
John Cupitt 2021-11-04 15:26:04 +00:00
parent 3488c6b410
commit 30fdc3df77
2 changed files with 115 additions and 16 deletions

View File

@ -18,6 +18,7 @@
- added VipsForeignPpmFormat, @format arg to ppm savers
- add fail-on to give better control over loader error sensitivity
- add hyperbolic functions sinh, cosh, tanh, asinh, acosh, atanh [hroskes]
- add untiled jp2k load
16/8/21 started 8.11.4
- fix off-by-one error in new rank fast path

View File

@ -2,6 +2,8 @@
*
* 18/3/20
* - from heifload.c
* 4/11/21
* - add untiled load
*/
/*
@ -355,6 +357,9 @@ vips_foreign_load_jp2k_print( VipsForeignLoadJp2k *jp2k )
jp2k->info->tw, jp2k->info->th );
printf( "nbcomps = %u, tile_info = %p\n",
jp2k->info->nbcomps, jp2k->info->tile_info );
if( jp2k->info->tw == 1 &&
jp2k->info->th == 1 )
printf( "untiled\n" );
}
#endif /*DEBUG*/
@ -724,10 +729,84 @@ vips_foreign_load_jp2k_ycc_to_rgb( opj_image_t *image, VipsImage *im,
}
}
/* Loop over the output region, painting in tiles from the file.
/* Read a tile from an untiled jp2k file.
*/
static int
vips_foreign_load_jp2k_generate( VipsRegion *out,
vips_foreign_load_jp2k_generate_untiled( VipsRegion *out,
void *seq, void *a, void *b, gboolean *stop )
{
VipsForeignLoad *load = (VipsForeignLoad *) a;
VipsForeignLoadJp2k *jp2k = (VipsForeignLoadJp2k *) load;
VipsRect *r = &out->valid;
VipsRect opj;
VipsRect image;
int y;
#ifdef DEBUG_VERBOSE
printf( "vips_foreign_load_jp2k_generate_untiled: "
"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( jp2k->n_errors )
return( 0 );
/* Coordinates are always in the highest res level.
*/
opj.left = r->left * jp2k->shrink;
opj.top = r->top * jp2k->shrink;
opj.width = r->width * jp2k->shrink;
opj.height = r->height * jp2k->shrink;
/* And must be clipped against the image size.
*/
image.left = 0;
image.top = 0;
image.width = jp2k->info->tdx;
image.height = jp2k->info->tdy;
vips_rect_intersectrect( &opj, &image, &opj );
if( !opj_set_decode_area( jp2k->codec, jp2k->image,
opj.left, opj.top,
VIPS_RECT_RIGHT( &opj ), VIPS_RECT_BOTTOM( &opj ) ) )
return( -1 );
if( !opj_decode( jp2k->codec, jp2k->stream, jp2k->image ) )
return( -1 );
/* Unpack decoded pixels to buffer in vips layout.
*/
for( y = 0; y < r->height; y++ ) {
VipsPel *q = VIPS_REGION_ADDR( out, r->left, r->top + y );
vips_foreign_load_jp2k_pack( jp2k->upsample,
jp2k->image, out->im, q, 0, y, r->width );
if( jp2k->ycc_to_rgb )
vips_foreign_load_jp2k_ycc_to_rgb( jp2k->image,
out->im, q, r->width );
}
/* jp2k files can't be truncated (they fail to open), so all we can
* spot is errors.
*/
if( load->fail_on >= VIPS_FAIL_ON_ERROR &&
jp2k->n_errors > 0 )
return( -1 );
return( 0 );
}
/* Read a tile from the file. libvips tiles can be much larger or smaller than
* openjpeg tiles, so we must loop over the output region, painting in
* tiles from the file.
*/
static int
vips_foreign_load_jp2k_generate_tiled( VipsRegion *out,
void *seq, void *a, void *b, gboolean *stop )
{
VipsForeignLoad *load = (VipsForeignLoad *) a;
@ -841,17 +920,13 @@ static int
vips_foreign_load_jp2k_load( VipsForeignLoad *load )
{
VipsForeignLoadJp2k *jp2k = (VipsForeignLoadJp2k *) load;
/* jp2k 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 = jp2k->info->tdx;
int tile_height = jp2k->info->tdy;
int tiles_across = jp2k->info->tw;
VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( load ), 3 );
int vips_tile_width;
int vips_tile_height;
int vips_tiles_across;
#ifdef DEBUG
printf( "vips_foreign_load_jp2k_load:\n" );
#endif /*DEBUG*/
@ -860,19 +935,42 @@ vips_foreign_load_jp2k_load( VipsForeignLoad *load )
if( vips_foreign_load_jp2k_set_header( jp2k, t[0] ) )
return( -1 );
if( vips_image_generate( t[0],
NULL, vips_foreign_load_jp2k_generate, NULL, jp2k, NULL ) )
return( -1 );
/* Untiled jp2k images need a different read API.
*/
if( jp2k->info->tw == 1 &&
jp2k->info->th == 1 ) {
vips_tile_width = 512;
vips_tile_height = 512;
vips_tiles_across =
VIPS_ROUND_UP( t[0]->Xsize, vips_tile_width ) /
vips_tile_width;
if( vips_image_generate( t[0],
NULL, vips_foreign_load_jp2k_generate_untiled, NULL,
jp2k, NULL ) )
return( -1 );
}
else {
vips_tile_width = jp2k->info->tdx;
vips_tile_height = jp2k->info->tdy;
vips_tiles_across = jp2k->info->tw;
if( vips_image_generate( t[0],
NULL, vips_foreign_load_jp2k_generate_tiled, NULL,
jp2k, 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,
"tile_width", vips_tile_width,
"tile_height", vips_tile_height,
"max_tiles", 3 * vips_tiles_across,
NULL ) )
return( -1 );
if( vips_image_write( t[1], load->real ) )
return( -1 );