works at a basic level

need to add shrink-on-load etc.
This commit is contained in:
John Cupitt 2021-03-17 16:38:55 +00:00
parent c2eebacf5a
commit c0ec1757cb
2 changed files with 147 additions and 21 deletions

View File

@ -20,6 +20,7 @@
- add vips_fitsload_source(), vips_niftiload_source()
- png and gif load note background colour as metadata [781545872]
- add vips_image_[set|get]_array_double()
- add vips_jp2kload(), vips_jp2kload_source()
22/12/20 start 8.10.6
- don't seek on bad file descriptors [kleisauke]

View File

@ -35,9 +35,9 @@
*/
/*
*/
#define DEBUG
#define VIPS_DEBUG
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
@ -58,6 +58,10 @@
#include "pforeign.h"
/* Surely enough ... does anyone do multispectral imaging with jp2k?
*/
#define MAX_BANDS (100)
typedef struct _VipsForeignLoadJp2k {
VipsForeignLoad parent_object;
@ -77,6 +81,9 @@ typedef struct _VipsForeignLoadJp2k {
*/
int n_errors;
/* An array of int* plane pointers for unpack.
*/
int *planes[MAX_BANDS];
} VipsForeignLoadJp2k;
typedef VipsForeignLoadClass VipsForeignLoadJp2kClass;
@ -89,9 +96,19 @@ vips_foreign_load_jp2k_dispose( GObject *gobject )
{
VipsForeignLoadJp2k *jp2k = (VipsForeignLoadJp2k *) gobject;
#ifdef DEBUG
printf( "vips_foreign_load_jp2k_dispose:\n" );
#endif /*DEBUG*/
/*
* FIXME ... do we need this? seems to just cause warnings
*
if( jp2k->codec &&
jp2k->stream )
opj_end_decompress( jp2k->codec, jp2k->stream );
opj_end_decompress( jp2k->codec, jp2k->stream );
*
*/
opj_destroy_cstr_info( &jp2k->info );
VIPS_FREEF( opj_destroy_codec, jp2k->codec );
VIPS_FREEF( opj_stream_destroy, jp2k->stream );
@ -131,7 +148,7 @@ vips_foreign_load_jp2k_seek_source( OPJ_OFF_T position, void *client )
{
VipsSource *source = VIPS_SOURCE( client );
if( vips_source_seek( source, position, SEEK_SET ) )
if( vips_source_seek( source, position, SEEK_SET ) == -1 )
/* openjpeg seek uses FALSE for both end of stream and error.
*/
return( OPJ_FALSE );
@ -173,6 +190,16 @@ vips_foreign_load_jp2k_build( VipsObject *object )
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
VipsForeignLoadJp2k *jp2k = (VipsForeignLoadJp2k *) object;
#ifdef DEBUG
printf( "vips_foreign_load_jp2k_build:\n" );
#endif /*DEBUG*/
/* Default parameters.
*/
jp2k->parameters.decod_format = -1;
jp2k->parameters.cod_format = -1;
opj_set_default_decoder_parameters( &jp2k->parameters );
/* Link the openjpeg stream to our VipsSource.
*/
if( jp2k->source ) {
@ -240,20 +267,28 @@ vips_foreign_load_jp2k_error_callback( const char *msg, void *client )
jp2k->n_errors += 1;
}
/* The openjpeg info and warning callbacks are incredibly chatty.
*/
static void
vips_foreign_load_jp2k_warning_callback( const char *msg, void *client )
{
#ifdef DEBUG
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( client );
g_warning( "%s: %s", class->nickname, msg );
#endif /*DEBUG*/
}
/* The openjpeg info and warning callbacks are incredibly chatty.
*/
static void
vips_foreign_load_jp2k_info_callback( const char *msg, void *client )
{
#ifdef DEBUG
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( client );
g_info( "%s: %s", class->nickname, msg );
#endif /*DEBUG*/
}
static void
@ -311,7 +346,6 @@ vips_foreign_load_jp2k_print( VipsForeignLoadJp2k *jp2k )
jp2k->info->tw, jp2k->info->th );
printf( "nbcomps = %u\n",
jp2k->info->nbcomps );
}
#endif /*DEBUG*/
@ -442,7 +476,12 @@ vips_foreign_load_jp2k_header( VipsForeignLoad *load )
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
VipsForeignLoadJp2k *jp2k = (VipsForeignLoadJp2k *) load;
#ifdef DEBUG
printf( "vips_foreign_load_jp2k_header:\n" );
#endif /*DEBUG*/
jp2k->format = vips_foreign_load_jp2k_get_format( jp2k->source );
vips_source_rewind( jp2k->source );
if( !(jp2k->codec = opj_create_decompress( jp2k->format )) ) {
vips_error( class->nickname,
"%s", _( "format not supported by openjpeg" ) );
@ -461,15 +500,18 @@ vips_foreign_load_jp2k_header( VipsForeignLoad *load )
*/
opj_codec_set_threads( jp2k->codec, vips_concurrency_get() );
if( vips_source_rewind( jp2k->source ) ||
!opj_read_header( jp2k->stream, jp2k->codec, &jp2k->image ) )
if( !opj_read_header( jp2k->stream, jp2k->codec, &jp2k->image ) )
return( -1 );
if( !(jp2k->info = opj_get_cstr_info( jp2k->codec )) ) {
vips_error( class->nickname,
"%s", _( "unable to read codec info" ) );
return( -1 );
}
if( jp2k->image->numcomps > MAX_BANDS ) {
vips_error( class->nickname,
"%s", _( "too many image bands" ) );
return( -1 );
}
#ifdef DEBUG
vips_foreign_load_jp2k_print( jp2k );
@ -484,6 +526,46 @@ vips_foreign_load_jp2k_header( VipsForeignLoad *load )
return( 0 );
}
#define REPACK( TYPE ) { \
TYPE *tq = (TYPE *) q; \
\
for( x = 0; x < length; x++ ) \
for( i = 0; i < b; i++ ) \
*tq++ = planes[i][x]; \
}
/* Write a line of pels to vips from openjpeg. openjpeg stores everything as
* plane-separated arrays of int32.
*/
static void
vips_foreign_load_jp2k_repack( VipsForeignLoadJp2k *jp2k,
VipsImage *image, VipsPel *q,
int left, int top, int length )
{
int **planes = jp2k->planes;
int b = jp2k->image->numcomps;
int x, i;
for( i = 0; i < b; i++ )
planes[i] = jp2k->image->comps[i].data +
top * jp2k->image->x1 + left;
switch( image->BandFmt ) {
case VIPS_FORMAT_UCHAR:
REPACK( unsigned char );
break;
case VIPS_FORMAT_USHORT:
REPACK( unsigned short );
break;
default:
g_assert_not_reached();
break;
}
}
/* Loop over the output region, painting in tiles from the file.
*/
static int
@ -495,22 +577,66 @@ vips_foreign_load_jp2k_generate( VipsRegion *out,
int tile_height = jp2k->info->tdy;
VipsRect *r = &out->valid;
int tile_index;
tile_index = 0;
#ifdef DEBUG
printf( "vips_foreign_load_jp2k_generate: decoding tile_index %d\n",
tile_index );
#endif /*DEBUG*/
if( !opj_get_decoded_tile( jp2k->codec, jp2k->stream, jp2k->image,
tile_index ) )
return( -1 );
int x, y, z;
#ifdef DEBUG
printf( "vips_foreign_load_jp2k_generate: decode success!\n" );
printf( "vips_foreign_load_jp2k_generate:\n" );
#endif /*DEBUG*/
vips_foreign_load_jp2k_print_image( jp2k->image );
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 ) {
/* Coordinate of the tile on this page that xy falls in.
*/
int xs = ((r->left + x) / tile_width) * tile_width;
int ys = ((r->top + y) / tile_height) * tile_height;
int tile_index = ys * jp2k->info->tw + xs;
/* Fetch the tile.
*/
if( !opj_get_decoded_tile( jp2k->codec,
jp2k->stream, jp2k->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_jp2k_repack( jp2k,
out->im, q,
hit.left - tile.left,
hit.top - tile.top + z,
hit.width );
}
x += hit.width;
}
/* This will be the same for all tiles in the row we've just
* done.
*/
y += hit.height;
}
return( 0 );
}
@ -518,7 +644,6 @@ vips_foreign_load_jp2k_generate( VipsRegion *out,
static int
vips_foreign_load_jp2k_load( VipsForeignLoad *load )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
VipsForeignLoadJp2k *jp2k = (VipsForeignLoadJp2k *) load;
int tile_width = jp2k->info->tdx;
int tile_height = jp2k->info->tdy;
@ -527,7 +652,7 @@ vips_foreign_load_jp2k_load( VipsForeignLoad *load )
vips_object_local_array( VIPS_OBJECT( load ), 3 );
#ifdef DEBUG
printf( "vips_foreign_load_jp2k_load: loading image\n" );
printf( "vips_foreign_load_jp2k_load:\n" );
#endif /*DEBUG*/
/* Tile cache: keep enough for two complete rows of tiles.