Merge branch 'master' into revise-composite
This commit is contained in:
commit
447e0f3ea2
@ -37,6 +37,7 @@
|
||||
- dzsave to szi will write all associated images
|
||||
- remove old c++ and python interfaces
|
||||
- vipsthumbnail can thumbnail animated and multipage images
|
||||
- deprecate webpload @shrink, use @scale instead
|
||||
|
||||
31/3/19 started 8.7.5
|
||||
- better buffer sizing in tiff reader [omira-sch]
|
||||
|
@ -143,9 +143,18 @@ vips_foreign_save_magick_next_image( VipsForeignSaveMagick *magick )
|
||||
!vips_image_get_int( im, "gif-delay", &number ) )
|
||||
image->delay = (size_t) number;
|
||||
|
||||
/* ImageMagick uses iterations like this (at least in gif save):
|
||||
* 0 - set 0 loops (infinite)
|
||||
* 1 - don't write the netscape extension block
|
||||
* 2 - loop once
|
||||
* 3 - loop twice etc.
|
||||
*
|
||||
* We have the simple gif meaning, so we must add one unless it's
|
||||
* zero.
|
||||
*/
|
||||
if( vips_image_get_typeof( im, "gif-loop" ) &&
|
||||
!vips_image_get_int( im, "gif-loop", &number ) )
|
||||
image->iterations = (size_t) number;
|
||||
image->iterations = (size_t) (number ? number + 1 : 0);
|
||||
|
||||
if( vips_image_get_typeof( im, "gif-comment" ) &&
|
||||
!vips_image_get_string( im, "gif-comment", &str ) )
|
||||
|
@ -297,8 +297,12 @@ vips_foreign_load_pdf_header( VipsForeignLoad *load )
|
||||
poppler_page_get_size( pdf->page, &width, &height );
|
||||
pdf->pages[i].left = 0;
|
||||
pdf->pages[i].top = top;
|
||||
pdf->pages[i].width = width * pdf->scale;
|
||||
pdf->pages[i].height = height * pdf->scale;
|
||||
/* We do round to nearest, in the same way that vips_resize()
|
||||
* does round to nearest. Without this, things like
|
||||
* shrink-on-load will break.
|
||||
*/
|
||||
pdf->pages[i].width = VIPS_RINT( width * pdf->scale );
|
||||
pdf->pages[i].height = VIPS_RINT( height * pdf->scale );
|
||||
|
||||
if( pdf->pages[i].width > pdf->image.width )
|
||||
pdf->image.width = pdf->pages[i].width;
|
||||
|
@ -341,10 +341,14 @@ vips_foreign_load_pdf_header( VipsForeignLoad *load )
|
||||
return( -1 );
|
||||
pdf->pages[i].left = 0;
|
||||
pdf->pages[i].top = top;
|
||||
pdf->pages[i].width =
|
||||
FPDF_GetPageWidth( pdf->page ) * pdf->scale;
|
||||
pdf->pages[i].height =
|
||||
FPDF_GetPageHeight( pdf->page ) * pdf->scale;
|
||||
/* We do round to nearest, in the same way that vips_resize()
|
||||
* does round to nearest. Without this, things like
|
||||
* shrink-on-load will break.
|
||||
*/
|
||||
pdf->pages[i].width = VIPS_RINT(
|
||||
FPDF_GetPageWidth( pdf->page ) * pdf->scale );
|
||||
pdf->pages[i].height = VIPS_RINT(
|
||||
FPDF_GetPageHeight( pdf->page ) * pdf->scale );
|
||||
|
||||
if( pdf->pages[i].width > pdf->image.width )
|
||||
pdf->image.width = pdf->pages[i].width;
|
||||
|
@ -226,14 +226,14 @@ int vips__iswebp_buffer( const void *buf, size_t len );
|
||||
int vips__iswebp( const char *filename );
|
||||
|
||||
int vips__webp_read_file_header( const char *name, VipsImage *out,
|
||||
int page, int n, int shrink );
|
||||
int page, int n, double scale );
|
||||
int vips__webp_read_file( const char *name, VipsImage *out,
|
||||
int page, int n, int shrink );
|
||||
int page, int n, double scale );
|
||||
|
||||
int vips__webp_read_buffer_header( const void *buf, size_t len, VipsImage *out,
|
||||
int page, int n, int shrink );
|
||||
int page, int n, double scale );
|
||||
int vips__webp_read_buffer( const void *buf, size_t len, VipsImage *out,
|
||||
int page, int n, int shrink );
|
||||
int page, int n, double scale );
|
||||
|
||||
int vips__webp_write_file( VipsImage *out, const char *filename,
|
||||
int Q, gboolean lossless, VipsForeignWebpPreset preset,
|
||||
|
@ -2345,25 +2345,7 @@ rtiff_new_buffer( const void *buf, size_t len, VipsImage *out,
|
||||
return( rtiff );
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
FIXME ... Unused for now, perhaps if we add another format flag.
|
||||
|
||||
static int
|
||||
istiffpyramid( const char *name )
|
||||
{
|
||||
TIFF *tif;
|
||||
|
||||
vips__tiff_init();
|
||||
|
||||
if( (tif = get_directory( name, 2 )) ) {
|
||||
// We can see page 2 ... assume it is.
|
||||
TIFFClose( tif );
|
||||
return( 1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
/* For istiffpyramid(), see vips_thumbnail_get_tiff_pyramid().
|
||||
*/
|
||||
|
||||
int
|
||||
@ -2379,7 +2361,8 @@ vips__tiff_read( const char *filename, VipsImage *out,
|
||||
|
||||
vips__tiff_init();
|
||||
|
||||
if( !(rtiff = rtiff_new_filename( filename, out, page, n, autorotate )) )
|
||||
if( !(rtiff = rtiff_new_filename( filename, out,
|
||||
page, n, autorotate )) )
|
||||
return( -1 );
|
||||
|
||||
if( rtiff->header.tiled ) {
|
||||
@ -2394,7 +2377,7 @@ vips__tiff_read( const char *filename, VipsImage *out,
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* On a header-only read, we can just swap width/height if orientaion is 6 or
|
||||
/* On a header-only read, we can just swap width/height if orientation is 6 or
|
||||
* 8.
|
||||
*/
|
||||
static void
|
||||
|
@ -17,6 +17,9 @@
|
||||
* - could memleak on some read errors
|
||||
* 24/4/19
|
||||
* - fix bg handling in animations
|
||||
* 30/4/19
|
||||
* - deprecate shrink, use scale instead, and make it a double ... this
|
||||
* lets us do faster and more accurate thumbnailing
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -90,9 +93,9 @@ typedef struct {
|
||||
*/
|
||||
int n;
|
||||
|
||||
/* Shrink-on-load factor. Use this to set scaled_width.
|
||||
/* Scale-on-load factor. Use this to set scaled_width.
|
||||
*/
|
||||
int shrink;
|
||||
double scale;
|
||||
|
||||
/* Size of final output image.
|
||||
*/
|
||||
@ -150,6 +153,39 @@ typedef struct {
|
||||
VipsRect dispose_rect;
|
||||
} Read;
|
||||
|
||||
const char *
|
||||
vips__error_webp( VP8StatusCode code )
|
||||
{
|
||||
switch( code ) {
|
||||
case VP8_STATUS_OK:
|
||||
return( "VP8_STATUS_OK" );
|
||||
|
||||
case VP8_STATUS_OUT_OF_MEMORY:
|
||||
return( "VP8_STATUS_OUT_OF_MEMORY" );
|
||||
|
||||
case VP8_STATUS_INVALID_PARAM:
|
||||
return( "VP8_STATUS_INVALID_PARAM" );
|
||||
|
||||
case VP8_STATUS_BITSTREAM_ERROR:
|
||||
return( "VP8_STATUS_BITSTREAM_ERROR" );
|
||||
|
||||
case VP8_STATUS_UNSUPPORTED_FEATURE:
|
||||
return( "VP8_STATUS_UNSUPPORTED_FEATURE" );
|
||||
|
||||
case VP8_STATUS_SUSPENDED:
|
||||
return( "VP8_STATUS_SUSPENDED" );
|
||||
|
||||
case VP8_STATUS_USER_ABORT:
|
||||
return( "VP8_STATUS_USER_ABORT" );
|
||||
|
||||
case VP8_STATUS_NOT_ENOUGH_DATA:
|
||||
return( "VP8_STATUS_NOT_ENOUGH_DATA" );
|
||||
|
||||
default:
|
||||
return( "<unkown>" );
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
vips_image_paint_area( VipsImage *image, const VipsRect *r, const VipsPel *ink )
|
||||
{
|
||||
@ -230,32 +266,34 @@ blend_pixel( guint32 A, guint32 B )
|
||||
return( setRGBA( rR, gR, bR, aR ) );
|
||||
}
|
||||
|
||||
/* Blend sub into frame at left, top.
|
||||
*/
|
||||
static void
|
||||
vips_image_paint_image( VipsImage *image,
|
||||
VipsImage *ink, int x, int y, gboolean blend )
|
||||
vips_image_paint_image( VipsImage *frame,
|
||||
VipsImage *sub, int left, int top, gboolean blend )
|
||||
{
|
||||
VipsRect valid = { 0, 0, image->Xsize, image->Ysize };
|
||||
VipsRect sub = { x, y, ink->Xsize, ink->Ysize };
|
||||
int ps = VIPS_IMAGE_SIZEOF_PEL( image );
|
||||
VipsRect frame_rect = { 0, 0, frame->Xsize, frame->Ysize };
|
||||
VipsRect sub_rect = { left, top, sub->Xsize, sub->Ysize };
|
||||
int ps = VIPS_IMAGE_SIZEOF_PEL( frame );
|
||||
|
||||
VipsRect ovl;
|
||||
|
||||
g_assert( VIPS_IMAGE_SIZEOF_PEL( ink ) == ps );
|
||||
g_assert( VIPS_IMAGE_SIZEOF_PEL( sub ) == ps );
|
||||
|
||||
/* Disable blend if we are not RGBA.
|
||||
*/
|
||||
if( image->Bands != 4 )
|
||||
if( frame->Bands != 4 )
|
||||
blend = FALSE;
|
||||
|
||||
vips_rect_intersectrect( &valid, &sub, &ovl );
|
||||
vips_rect_intersectrect( &frame_rect, &sub_rect, &ovl );
|
||||
if( !vips_rect_isempty( &ovl ) ) {
|
||||
VipsPel *p, *q;
|
||||
int i;
|
||||
int x, y;
|
||||
|
||||
p = VIPS_IMAGE_ADDR( ink, ovl.left - x, ovl.top - y );
|
||||
q = VIPS_IMAGE_ADDR( image, ovl.left, ovl.top );
|
||||
p = VIPS_IMAGE_ADDR( sub, ovl.left - left, ovl.top - top );
|
||||
q = VIPS_IMAGE_ADDR( frame, ovl.left, ovl.top );
|
||||
|
||||
for( i = 0; i < ovl.height; i++ ) {
|
||||
for( y = 0; y < ovl.height; y++ ) {
|
||||
if( blend ) {
|
||||
guint32 *A = (guint32 *) p;
|
||||
guint32 *B = (guint32 *) q;
|
||||
@ -267,8 +305,8 @@ vips_image_paint_image( VipsImage *image,
|
||||
memcpy( (char *) q, (char *) p,
|
||||
ovl.width * ps );
|
||||
|
||||
p += VIPS_IMAGE_SIZEOF_LINE( ink );
|
||||
q += VIPS_IMAGE_SIZEOF_LINE( image );
|
||||
p += VIPS_IMAGE_SIZEOF_LINE( sub );
|
||||
q += VIPS_IMAGE_SIZEOF_LINE( frame );
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -325,7 +363,7 @@ read_free( Read *read )
|
||||
|
||||
static Read *
|
||||
read_new( const char *filename, const void *data, size_t length,
|
||||
int page, int n, int shrink )
|
||||
int page, int n, double scale )
|
||||
{
|
||||
Read *read;
|
||||
|
||||
@ -337,7 +375,7 @@ read_new( const char *filename, const void *data, size_t length,
|
||||
read->length = length;
|
||||
read->page = page;
|
||||
read->n = n;
|
||||
read->shrink = shrink;
|
||||
read->scale = scale;
|
||||
read->delay = 100;
|
||||
read->fd = 0;
|
||||
read->demux = NULL;
|
||||
@ -394,14 +432,17 @@ read_header( Read *read, VipsImage *out )
|
||||
|
||||
canvas_width = WebPDemuxGetI( read->demux, WEBP_FF_CANVAS_WIDTH );
|
||||
canvas_height = WebPDemuxGetI( read->demux, WEBP_FF_CANVAS_HEIGHT );
|
||||
read->frame_width = canvas_width / read->shrink;
|
||||
read->frame_height = canvas_height / read->shrink;
|
||||
/* We round-to-nearest cf. pdfload etc.
|
||||
*/
|
||||
read->frame_width = VIPS_RINT( canvas_width * read->scale );
|
||||
read->frame_height = VIPS_RINT( canvas_height * read->scale );
|
||||
|
||||
if( read->shrink > 1 ) {
|
||||
read->config.options.use_scaling = 1;
|
||||
read->config.options.scaled_width = read->frame_width;
|
||||
read->config.options.scaled_height = read->frame_height;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf( "webp2vips: canvas_width = %d\n", canvas_width );
|
||||
printf( "webp2vips: canvas_height = %d\n", canvas_height );
|
||||
printf( "webp2vips: frame_width = %d\n", read->frame_width );
|
||||
printf( "webp2vips: frame_height = %d\n", read->frame_height );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
flags = WebPDemuxGetI( read->demux, WEBP_FF_FORMAT_FLAGS );
|
||||
|
||||
@ -521,11 +562,11 @@ read_header( Read *read, VipsImage *out )
|
||||
|
||||
int
|
||||
vips__webp_read_file_header( const char *filename, VipsImage *out,
|
||||
int page, int n, int shrink )
|
||||
int page, int n, double scale )
|
||||
{
|
||||
Read *read;
|
||||
|
||||
if( !(read = read_new( filename, NULL, 0, page, n, shrink )) ) {
|
||||
if( !(read = read_new( filename, NULL, 0, page, n, scale )) ) {
|
||||
vips_error( "webp2vips",
|
||||
_( "unable to open \"%s\"" ), filename );
|
||||
return( -1 );
|
||||
@ -541,6 +582,9 @@ vips__webp_read_file_header( const char *filename, VipsImage *out,
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Read a single frame -- a width * height block of pixels. This will get
|
||||
* blended into the accumulator at some offset.
|
||||
*/
|
||||
static VipsImage *
|
||||
read_frame( Read *read,
|
||||
int width, int height, const guint8 *data, size_t length )
|
||||
@ -553,7 +597,7 @@ read_frame( Read *read,
|
||||
|
||||
frame = vips_image_new_memory();
|
||||
vips_image_init_fields( frame,
|
||||
width, height,
|
||||
width, height,
|
||||
read->alpha ? 4 : 3,
|
||||
VIPS_FORMAT_UCHAR, VIPS_CODING_NONE,
|
||||
VIPS_INTERPRETATION_sRGB,
|
||||
@ -568,6 +612,11 @@ read_frame( Read *read,
|
||||
read->config.output.u.RGBA.rgba = VIPS_IMAGE_ADDR( frame, 0, 0 );
|
||||
read->config.output.u.RGBA.stride = VIPS_IMAGE_SIZEOF_LINE( frame );
|
||||
read->config.output.u.RGBA.size = VIPS_IMAGE_SIZEOF_IMAGE( frame );
|
||||
if( read->scale != 1.0 ) {
|
||||
read->config.options.use_scaling = 1;
|
||||
read->config.options.scaled_width = width;
|
||||
read->config.options.scaled_height = height;
|
||||
}
|
||||
|
||||
if( WebPDecode( data, length, &read->config ) != VP8_STATUS_OK ) {
|
||||
g_object_unref( frame );
|
||||
@ -582,16 +631,26 @@ static int
|
||||
read_next_frame( Read *read )
|
||||
{
|
||||
VipsImage *frame;
|
||||
VipsRect area;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "read_next_frame:\n" );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
/* Area of this frame, in output image coordinates. We must rint(),
|
||||
* since we need the same rules as the overall image scale, or we'll
|
||||
* sometimes have missing pixels on edges.
|
||||
*/
|
||||
area.left = VIPS_RINT( read->iter.x_offset * read->scale );
|
||||
area.top = VIPS_RINT( read->iter.y_offset * read->scale );
|
||||
area.width = VIPS_RINT( read->iter.width * read->scale );
|
||||
area.height = VIPS_RINT( read->iter.height * read->scale );
|
||||
|
||||
/* Dispose from the previous frame.
|
||||
*/
|
||||
if( read->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ) {
|
||||
/* We must clear the pixels occupied by this webp frame (not
|
||||
* the whole of the read frame) to 0 (transparent).
|
||||
/* We must clear the pixels occupied by the previous webp
|
||||
* frame (not the whole of the read frame) to 0 (transparent).
|
||||
*
|
||||
* We do not clear to WEBP_FF_BACKGROUND_COLOR. That's only
|
||||
* used to composite down to RGB. Perhaps we
|
||||
@ -606,17 +665,14 @@ read_next_frame( Read *read )
|
||||
/* Note this frame's dispose for next time.
|
||||
*/
|
||||
read->dispose_method = read->iter.dispose_method;
|
||||
read->dispose_rect.left = read->iter.x_offset;
|
||||
read->dispose_rect.top = read->iter.y_offset;
|
||||
read->dispose_rect.width = read->iter.width;
|
||||
read->dispose_rect.height = read->iter.height;
|
||||
read->dispose_rect = area;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "webp2vips: frame_num = %d\n", read->iter.frame_num );
|
||||
printf( " x_offset = %d\n", read->iter.x_offset );
|
||||
printf( " y_offset = %d\n", read->iter.y_offset );
|
||||
printf( " width = %d\n", read->iter.width );
|
||||
printf( " height = %d\n", read->iter.height );
|
||||
printf( " left = %d\n", area.left );
|
||||
printf( " top = %d\n", area.top );
|
||||
printf( " width = %d\n", area.width );
|
||||
printf( " height = %d\n", area.height );
|
||||
printf( " duration = %d\n", read->iter.duration );
|
||||
printf( " dispose = " );
|
||||
if( read->iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND )
|
||||
@ -637,14 +693,14 @@ read_next_frame( Read *read )
|
||||
"not all frames have equal duration" );
|
||||
|
||||
if( !(frame = read_frame( read,
|
||||
read->iter.width, read->iter.height,
|
||||
area.width, area.height,
|
||||
read->iter.fragment.bytes, read->iter.fragment.size )) )
|
||||
return( -1 );
|
||||
|
||||
/* Now blend or copy the new pixels into our accumulator.
|
||||
*/
|
||||
vips_image_paint_image( read->frame, frame,
|
||||
read->iter.x_offset, read->iter.y_offset,
|
||||
area.left, area.top,
|
||||
read->iter.blend_method == WEBP_MUX_BLEND );
|
||||
|
||||
g_object_unref( frame );
|
||||
@ -715,11 +771,11 @@ read_image( Read *read, VipsImage *out )
|
||||
|
||||
int
|
||||
vips__webp_read_file( const char *filename, VipsImage *out,
|
||||
int page, int n, int shrink )
|
||||
int page, int n, double scale )
|
||||
{
|
||||
Read *read;
|
||||
|
||||
if( !(read = read_new( filename, NULL, 0, page, n, shrink )) ) {
|
||||
if( !(read = read_new( filename, NULL, 0, page, n, scale )) ) {
|
||||
vips_error( "webp2vips",
|
||||
_( "unable to open \"%s\"" ), filename );
|
||||
return( -1 );
|
||||
@ -737,11 +793,11 @@ vips__webp_read_file( const char *filename, VipsImage *out,
|
||||
|
||||
int
|
||||
vips__webp_read_buffer_header( const void *buf, size_t len, VipsImage *out,
|
||||
int page, int n, int shrink )
|
||||
int page, int n, double scale )
|
||||
{
|
||||
Read *read;
|
||||
|
||||
if( !(read = read_new( NULL, buf, len, page, n, shrink )) ) {
|
||||
if( !(read = read_new( NULL, buf, len, page, n, scale )) ) {
|
||||
vips_error( "webp2vips",
|
||||
"%s", _( "unable to open buffer" ) );
|
||||
return( -1 );
|
||||
@ -759,11 +815,11 @@ vips__webp_read_buffer_header( const void *buf, size_t len, VipsImage *out,
|
||||
|
||||
int
|
||||
vips__webp_read_buffer( const void *buf, size_t len, VipsImage *out,
|
||||
int page, int n, int shrink )
|
||||
int page, int n, double scale )
|
||||
{
|
||||
Read *read;
|
||||
|
||||
if( !(read = read_new( NULL, buf, len, page, n, shrink )) ) {
|
||||
if( !(read = read_new( NULL, buf, len, page, n, scale )) ) {
|
||||
vips_error( "webp2vips",
|
||||
"%s", _( "unable to open buffer" ) );
|
||||
return( -1 );
|
||||
|
@ -6,6 +6,8 @@
|
||||
* - add @shrink
|
||||
* 1/11/18
|
||||
* - add @page, @n
|
||||
* 30/4/19
|
||||
* - deprecate @shrink, use @scale instead
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -64,9 +66,13 @@ typedef struct _VipsForeignLoadWebp {
|
||||
*/
|
||||
int n;
|
||||
|
||||
/* Shrink by this much during load.
|
||||
/* Scale by this much during load.
|
||||
*/
|
||||
int shrink;
|
||||
double scale;
|
||||
|
||||
/* Old and deprecated scaling path.
|
||||
*/
|
||||
int shrink;
|
||||
} VipsForeignLoadWebp;
|
||||
|
||||
typedef VipsForeignLoadClass VipsForeignLoadWebpClass;
|
||||
@ -120,10 +126,20 @@ vips_foreign_load_webp_class_init( VipsForeignLoadWebpClass *class )
|
||||
G_STRUCT_OFFSET( VipsForeignLoadWebp, n ),
|
||||
-1, 100000, 1 );
|
||||
|
||||
VIPS_ARG_INT( class, "shrink", 22,
|
||||
VIPS_ARG_DOUBLE( class, "scale", 22,
|
||||
_( "Scale" ),
|
||||
_( "Scale factor on load" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadWebp, scale ),
|
||||
0.0, 1024.0, 1.0 );
|
||||
|
||||
/* Old and deprecated scaling API. A float param lets do
|
||||
* shrink-on-load for thumbnail faster and more accurately.
|
||||
*/
|
||||
VIPS_ARG_INT( class, "shrink", 23,
|
||||
_( "Shrink" ),
|
||||
_( "Shrink factor on load" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadWebp, shrink ),
|
||||
1, 1024, 1 );
|
||||
|
||||
@ -134,6 +150,7 @@ vips_foreign_load_webp_init( VipsForeignLoadWebp *webp )
|
||||
{
|
||||
webp->n = 1;
|
||||
webp->shrink = 1;
|
||||
webp->scale = 1.0;
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignLoadWebpFile {
|
||||
@ -168,8 +185,15 @@ vips_foreign_load_webp_file_header( VipsForeignLoad *load )
|
||||
VipsForeignLoadWebp *webp = (VipsForeignLoadWebp *) load;
|
||||
VipsForeignLoadWebpFile *file = (VipsForeignLoadWebpFile *) load;
|
||||
|
||||
/* BC for the old API.
|
||||
*/
|
||||
if( !vips_object_argument_isset( VIPS_OBJECT( load ), "scale" ) &&
|
||||
vips_object_argument_isset( VIPS_OBJECT( load ), "shrink" ) &&
|
||||
webp->shrink != 0 )
|
||||
webp->scale = 1.0 / webp->shrink;
|
||||
|
||||
if( vips__webp_read_file_header( file->filename, load->out,
|
||||
webp->page, webp->n, webp->shrink ) )
|
||||
webp->page, webp->n, webp->scale ) )
|
||||
return( -1 );
|
||||
|
||||
VIPS_SETSTR( load->out->filename, file->filename );
|
||||
@ -184,7 +208,7 @@ vips_foreign_load_webp_file_load( VipsForeignLoad *load )
|
||||
VipsForeignLoadWebpFile *file = (VipsForeignLoadWebpFile *) load;
|
||||
|
||||
if( vips__webp_read_file( file->filename, load->real,
|
||||
webp->page, webp->n, webp->shrink ) )
|
||||
webp->page, webp->n, webp->scale ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
@ -249,7 +273,7 @@ vips_foreign_load_webp_buffer_header( VipsForeignLoad *load )
|
||||
|
||||
if( vips__webp_read_buffer_header( buffer->buf->data,
|
||||
buffer->buf->length, load->out,
|
||||
webp->page, webp->n, webp->shrink ) )
|
||||
webp->page, webp->n, webp->scale ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
@ -263,7 +287,7 @@ vips_foreign_load_webp_buffer_load( VipsForeignLoad *load )
|
||||
|
||||
if( vips__webp_read_buffer( buffer->buf->data, buffer->buf->length,
|
||||
load->real,
|
||||
webp->page, webp->n, webp->shrink ) )
|
||||
webp->page, webp->n, webp->scale ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
@ -317,7 +341,7 @@ vips_foreign_load_webp_buffer_init( VipsForeignLoadWebpBuffer *buffer )
|
||||
*
|
||||
* * @page: %gint, page (frame) to read
|
||||
* * @n: %gint, load this many pages
|
||||
* * @shrink: %gint, shrink by this much on load
|
||||
* * @scale: %gdouble, scale by this much on load
|
||||
*
|
||||
* Read a WebP file into a VIPS image.
|
||||
*
|
||||
@ -328,7 +352,8 @@ vips_foreign_load_webp_buffer_init( VipsForeignLoadWebpBuffer *buffer )
|
||||
* left. Set to -1 to mean "until the end of the document". Use vips_grid()
|
||||
* to change page layout.
|
||||
*
|
||||
* Use @shrink to specify a shrink-on-load factor.
|
||||
* Use @scale to specify a scale-on-load factor. For example, 2.0 to double
|
||||
* the size on load.
|
||||
*
|
||||
* The loader supports ICC, EXIF and XMP metadata.
|
||||
*
|
||||
@ -360,7 +385,7 @@ vips_webpload( const char *filename, VipsImage **out, ... )
|
||||
*
|
||||
* * @page: %gint, page (frame) to read
|
||||
* * @n: %gint, load this many pages
|
||||
* * @shrink: %gint, shrink by this much on load
|
||||
* * @scale: %gdouble, scale by this much on load
|
||||
*
|
||||
* Read a WebP-formatted memory block into a VIPS image. Exactly as
|
||||
* vips_webpload(), but read from a memory buffer.
|
||||
|
@ -635,7 +635,7 @@ write_vips( VipsRegion *region, VipsRect *area, void *a, void *b )
|
||||
size_t nwritten, count;
|
||||
void *buf;
|
||||
|
||||
count = region->bpl * area->height;
|
||||
count = (size_t) region->bpl * area->height;
|
||||
buf = VIPS_REGION_ADDR( region, 0, area->top );
|
||||
|
||||
do {
|
||||
|
@ -109,17 +109,16 @@ typedef struct _VipsThumbnail {
|
||||
int input_height;
|
||||
int page_height;
|
||||
VipsAngle angle; /* From vips_autorot_get_angle() */
|
||||
int n_pages; /* Pages in this image, not original */
|
||||
|
||||
/* For openslide, we need to read out the size of each level too.
|
||||
*
|
||||
* These are filled out for pyr tiffs as well.
|
||||
*/
|
||||
int level_count;
|
||||
int level_width[MAX_LEVELS];
|
||||
int level_height[MAX_LEVELS];
|
||||
|
||||
/* Try to get n-pages too, for pyr tiff load.
|
||||
*/
|
||||
int n_pages;
|
||||
|
||||
/* For HEIF, try to fetch the size of the stored thumbnail.
|
||||
*/
|
||||
int heif_thumbnail_width;
|
||||
@ -191,14 +190,16 @@ vips_thumbnail_read_header( VipsThumbnail *thumbnail, VipsImage *image )
|
||||
thumbnail->input_width = image->Xsize;
|
||||
thumbnail->input_height = image->Ysize;
|
||||
thumbnail->angle = vips_autorot_get_angle( image );
|
||||
thumbnail->page_height = vips_image_get_page_height( image );
|
||||
|
||||
if( vips_image_get_typeof( image, VIPS_META_N_PAGES ) ) {
|
||||
int n_pages;
|
||||
|
||||
if( !vips_image_get_int( image, VIPS_META_N_PAGES, &n_pages ) )
|
||||
thumbnail->n_pages =
|
||||
VIPS_CLIP( 1, n_pages, MAX_LEVELS );
|
||||
}
|
||||
/* The "n-pages" metadata item is the number of pages in the document,
|
||||
* not the number we've read out into this image. We calculate
|
||||
* ourselves from page_height.
|
||||
*
|
||||
* vips_image_get_page_height() verifies that Ysize is a simple
|
||||
* multiple of page_height.
|
||||
*/
|
||||
thumbnail->n_pages = thumbnail->input_height / thumbnail->page_height;
|
||||
|
||||
/* For openslide, read out the level structure too.
|
||||
*/
|
||||
@ -226,6 +227,7 @@ vips_thumbnail_read_header( VipsThumbnail *thumbnail, VipsImage *image )
|
||||
}
|
||||
|
||||
/* This may not be a pyr tiff, so no error if we can't find the layers.
|
||||
* We just look for two or more pages following roughly /2 shrinks.
|
||||
*/
|
||||
static void
|
||||
vips_thumbnail_get_tiff_pyramid( VipsThumbnail *thumbnail )
|
||||
@ -233,6 +235,11 @@ vips_thumbnail_get_tiff_pyramid( VipsThumbnail *thumbnail )
|
||||
VipsThumbnailClass *class = VIPS_THUMBNAIL_GET_CLASS( thumbnail );
|
||||
int i;
|
||||
|
||||
/* Only one page? Can't be.
|
||||
*/
|
||||
if( thumbnail->n_pages < 2 )
|
||||
return;
|
||||
|
||||
for( i = 0; i < thumbnail->n_pages; i++ ) {
|
||||
VipsImage *page;
|
||||
int level_width;
|
||||
@ -246,13 +253,10 @@ vips_thumbnail_get_tiff_pyramid( VipsThumbnail *thumbnail )
|
||||
level_height = page->Ysize;
|
||||
VIPS_UNREF( page );
|
||||
|
||||
/* Try to sanity-check the size of the pages. Do they look
|
||||
* like a pyramid?
|
||||
*/
|
||||
expected_level_width = thumbnail->input_width / (1 << i);
|
||||
expected_level_height = thumbnail->input_height / (1 << i);
|
||||
|
||||
/* Won't be exact due to rounding etc.
|
||||
/* This won't be exact due to rounding etc.
|
||||
*/
|
||||
if( abs( level_width - expected_level_width ) > 5 ||
|
||||
level_width < 2 )
|
||||
@ -405,7 +409,7 @@ vips_thumbnail_find_jpegshrink( VipsThumbnail *thumbnail,
|
||||
return( 1 );
|
||||
}
|
||||
|
||||
/* Find the best openslide level.
|
||||
/* Find the best pyramid (openslide or tiff) level.
|
||||
*/
|
||||
static int
|
||||
vips_thumbnail_find_pyrlevel( VipsThumbnail *thumbnail,
|
||||
@ -444,7 +448,7 @@ vips_thumbnail_open( VipsThumbnail *thumbnail )
|
||||
g_info( "input size is %d x %d",
|
||||
thumbnail->input_width, thumbnail->input_height );
|
||||
|
||||
/* For tiff, we need to make a separate get_info() for each page to
|
||||
/* For tiff, we need a separate ->open() for each page to
|
||||
* get all the pyramid levels.
|
||||
*/
|
||||
if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) )
|
||||
@ -456,6 +460,10 @@ vips_thumbnail_open( VipsThumbnail *thumbnail )
|
||||
if( vips_isprefix( "VipsForeignLoadHeif", thumbnail->loader ) )
|
||||
vips_thumbnail_get_heif_thumb_info( thumbnail );
|
||||
|
||||
/* We read the openslide level structure in
|
||||
* vips_thumbnail_read_header().
|
||||
*/
|
||||
|
||||
factor = 1.0;
|
||||
|
||||
if( vips_isprefix( "VipsForeignLoadJpeg", thumbnail->loader ) ) {
|
||||
@ -465,20 +473,28 @@ vips_thumbnail_open( VipsThumbnail *thumbnail )
|
||||
g_info( "loading jpeg with factor %g pre-shrink", factor );
|
||||
}
|
||||
else if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) ||
|
||||
vips_isprefix( "VipsForeignLoadOpenslide", thumbnail->loader ) ) {
|
||||
vips_isprefix( "VipsForeignLoadOpenslide",
|
||||
thumbnail->loader ) ) {
|
||||
factor = vips_thumbnail_find_pyrlevel( thumbnail,
|
||||
thumbnail->input_width, thumbnail->input_height );
|
||||
|
||||
g_info( "loading pyr level %g", factor );
|
||||
}
|
||||
else if( vips_isprefix( "VipsForeignLoadPdf", thumbnail->loader ) ||
|
||||
vips_isprefix( "VipsForeignLoadSvg", thumbnail->loader ) ) {
|
||||
else if( vips_isprefix( "VipsForeignLoadPdf", thumbnail->loader ) ) {
|
||||
factor = 1.0 /
|
||||
vips_thumbnail_calculate_common_shrink( thumbnail,
|
||||
thumbnail->input_width,
|
||||
thumbnail->page_height );
|
||||
|
||||
g_info( "loading PDF with factor %g pre-scale", factor );
|
||||
}
|
||||
else if( vips_isprefix( "VipsForeignLoadSvg", thumbnail->loader ) ) {
|
||||
factor = 1.0 /
|
||||
vips_thumbnail_calculate_common_shrink( thumbnail,
|
||||
thumbnail->input_width,
|
||||
thumbnail->input_height );
|
||||
|
||||
g_info( "loading PDF/SVG with factor %g pre-scale", factor );
|
||||
g_info( "loading SVG with factor %g pre-scale", factor );
|
||||
}
|
||||
else if( vips_isprefix( "VipsForeignLoadHeif", thumbnail->loader ) ) {
|
||||
/* 'factor' is a gboolean which enables thumbnail load instead
|
||||
@ -493,24 +509,14 @@ vips_thumbnail_open( VipsThumbnail *thumbnail )
|
||||
factor = 0.0;
|
||||
|
||||
}
|
||||
|
||||
/* Webp supports shrink-on-load, but unfortunately the filter is just
|
||||
* too odd.
|
||||
*
|
||||
* Perhaps reenable this if webp improves.
|
||||
*
|
||||
* vips_thumbnail_file_open() and vips_thumbnail_buffer_open() would
|
||||
* need additional cases as well.
|
||||
*
|
||||
else if( vips_isprefix( "VipsForeignLoadWebp", thumbnail->loader ) ) {
|
||||
factor = VIPS_MAX( 1.0,
|
||||
factor = 1.0 /
|
||||
vips_thumbnail_calculate_common_shrink( thumbnail,
|
||||
thumbnail->input_width,
|
||||
thumbnail->input_height ) );
|
||||
thumbnail->page_height );
|
||||
|
||||
g_info( "loading webp with factor %g pre-shrink", factor );
|
||||
g_info( "loading webp with factor %g pre-scale", factor );
|
||||
}
|
||||
*/
|
||||
|
||||
if( !(im = class->open( thumbnail, factor )) )
|
||||
return( NULL );
|
||||
@ -529,6 +535,8 @@ vips_thumbnail_build( VipsObject *object )
|
||||
VIPS_INTERPRETATION_scRGB : VIPS_INTERPRETATION_sRGB;
|
||||
|
||||
VipsImage *in;
|
||||
int preshrunk_page_height;
|
||||
int output_page_height;
|
||||
double hshrink;
|
||||
double vshrink;
|
||||
|
||||
@ -569,10 +577,9 @@ vips_thumbnail_build( VipsObject *object )
|
||||
return( -1 );
|
||||
in = t[0];
|
||||
|
||||
/* So page_height is after pre-shrink, but before the main shrink
|
||||
* stage.
|
||||
/* After pre-shrink, but before the main shrink stage.
|
||||
*/
|
||||
thumbnail->page_height = vips_image_get_page_height( in );
|
||||
preshrunk_page_height = vips_image_get_page_height( in );
|
||||
|
||||
/* RAD needs special unpacking.
|
||||
*/
|
||||
@ -653,17 +660,17 @@ vips_thumbnail_build( VipsObject *object )
|
||||
in = t[3];
|
||||
}
|
||||
|
||||
/* Shrink to page_height, so we work for multi-page images.
|
||||
/* Shrink to preshrunk_page_height, so we work for multi-page images.
|
||||
*/
|
||||
vips_thumbnail_calculate_shrink( thumbnail,
|
||||
in->Xsize, thumbnail->page_height, &hshrink, &vshrink );
|
||||
in->Xsize, preshrunk_page_height, &hshrink, &vshrink );
|
||||
|
||||
/* In toilet-roll mode, we must adjust vshrink so that we exactly hit
|
||||
* page_height or we'll have pixels straddling pixel boundaries.
|
||||
* page_height or we'll have pixels straddling page boundaries.
|
||||
*/
|
||||
if( in->Ysize > thumbnail->page_height ) {
|
||||
if( in->Ysize > preshrunk_page_height ) {
|
||||
int target_page_height = VIPS_RINT(
|
||||
thumbnail->page_height / vshrink );
|
||||
preshrunk_page_height / vshrink );
|
||||
int target_image_height = target_page_height *
|
||||
thumbnail->n_pages;
|
||||
|
||||
@ -676,8 +683,9 @@ vips_thumbnail_build( VipsObject *object )
|
||||
return( -1 );
|
||||
in = t[4];
|
||||
|
||||
thumbnail->page_height = VIPS_RINT( thumbnail->page_height / vshrink );
|
||||
vips_image_set_int( in, VIPS_META_PAGE_HEIGHT, thumbnail->page_height );
|
||||
output_page_height = VIPS_RINT( preshrunk_page_height / vshrink );
|
||||
vips_image_set_int( in,
|
||||
VIPS_META_PAGE_HEIGHT, output_page_height );
|
||||
|
||||
if( have_premultiplied ) {
|
||||
g_info( "unpremultiplying alpha" );
|
||||
@ -942,7 +950,8 @@ vips_thumbnail_file_open( VipsThumbnail *thumbnail, double factor )
|
||||
NULL ) );
|
||||
}
|
||||
else if( vips_isprefix( "VipsForeignLoadPdf", thumbnail->loader ) ||
|
||||
vips_isprefix( "VipsForeignLoadSvg", thumbnail->loader ) ) {
|
||||
vips_isprefix( "VipsForeignLoadSvg", thumbnail->loader ) ||
|
||||
vips_isprefix( "VipsForeignLoadWebp", thumbnail->loader ) ) {
|
||||
return( vips_image_new_from_file( file->filename,
|
||||
"access", VIPS_ACCESS_SEQUENTIAL,
|
||||
"scale", factor,
|
||||
@ -1137,7 +1146,8 @@ vips_thumbnail_buffer_open( VipsThumbnail *thumbnail, double factor )
|
||||
NULL ) );
|
||||
}
|
||||
else if( vips_isprefix( "VipsForeignLoadPdf", thumbnail->loader ) ||
|
||||
vips_isprefix( "VipsForeignLoadSvg", thumbnail->loader ) ) {
|
||||
vips_isprefix( "VipsForeignLoadSvg", thumbnail->loader ) ||
|
||||
vips_isprefix( "VipsForeignLoadWebp", thumbnail->loader ) ) {
|
||||
return( vips_image_new_from_buffer(
|
||||
buffer->buf->data, buffer->buf->length,
|
||||
buffer->option_string,
|
||||
|
@ -597,7 +597,7 @@ class TestForeign:
|
||||
def pdf_valid(im):
|
||||
a = im(10, 10)
|
||||
assert_almost_equal_objects(a, [35, 31, 32, 255])
|
||||
assert im.width == 1133
|
||||
assert im.width == 1134
|
||||
assert im.height == 680
|
||||
assert im.bands == 4
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user