static int vips_shrink_gen( VipsRegion *or, void *vseq, void *a, void *b, gboolean *stop ) { VipsShrinkSequence *seq = (VipsShrinkSequence *) vseq; VipsShrink *shrink = (VipsShrink *) b; VipsRegion *ir = seq->ir; VipsRect *r = &or->valid; /* How do we chunk up the image? We don't want to prepare the whole of * the input region corresponding to *r since it could be huge. * * Each pixel of *r will depend on roughly mw x mh * pixels, so we walk *r in chunks which map to the tile size. * * Make sure we can't ask for a zero step. */ int xstep = shrink->mw > VIPS__TILE_WIDTH ? 1 : VIPS__TILE_WIDTH / shrink->mw; int ystep = shrink->mh > VIPS__TILE_HEIGHT ? 1 : VIPS__TILE_HEIGHT / shrink->mh; int x, y; #ifdef DEBUG printf( "vips_shrink_gen: generating %d x %d at %d x %d\n", r->width, r->height, r->left, r->top ); #endif /*DEBUG*/ for( y = 0; y < r->height; y += ystep ) for( x = 0; x < r->width; x += xstep ) { /* Clip the this rect against the demand size. */ int width = VIPS_MIN( xstep, r->width - x ); int height = VIPS_MIN( ystep, r->height - y ); VipsRect s; s.left = (r->left + x) * shrink->xshrink; s.top = (r->top + y) * shrink->yshrink; s.width = ceil( width * shrink->xshrink ); s.height = ceil( height * shrink->yshrink ); #ifdef DEBUG printf( "shrink_gen: requesting %d x %d at %d x %d\n", s.width, s.height, s.left, s.top ); #endif /*DEBUG*/ if( vips_region_prepare( ir, &s ) ) return( -1 ); vips_shrink_gen2( shrink, seq, or, ir, r->left + x, r->top + y, width, height ); } return( 0 ); }
/* Return a ref to a window that encloses top/height. */ VipsWindow * vips_window_ref( IMAGE *im, int top, int height ) { VipsWindow *window; g_mutex_lock( im->sslock ); if( !(window = vips_window_find( im, top, height )) ) { /* No existing window ... make a new one. Ask for a larger * window than we strictly need. There's no point making tiny * windows. */ int margin = VIPS_MIN( vips__window_margin_pixels, vips__window_margin_bytes / VIPS_IMAGE_SIZEOF_LINE( im ) ); top -= margin; height += margin * 2; top = VIPS_CLIP( 0, top, im->Ysize - 1 ); height = VIPS_CLIP( 0, height, im->Ysize - top ); if( !(window = vips_window_new( im, top, height )) ) { g_mutex_unlock( im->sslock ); return( NULL ); } } g_mutex_unlock( im->sslock ); return( window ); }
static int vips_shrink_gen( VipsRegion *or, void *vseq, void *a, void *b, gboolean *stop ) { VipsShrinkSequence *seq = (VipsShrinkSequence *) vseq; VipsShrink *shrink = (VipsShrink *) b; VipsRegion *ir = seq->ir; VipsRect *r = &or->valid; /* How do we chunk up the image? We don't want to prepare the whole of * the input region corresponding to *r since it could be huge. * * Each pixel of *r will depend on roughly mw x mh * pixels, so we walk *r in chunks which map to the tile size. * * Make sure we can't ask for a zero step. * * We don't chunk horizontally. We want "vips shrink x.jpg b.jpg 100 * 100" to run sequentially. If we chunk horizontally, we will fetch * 100x100 lines from the top of the image, then 100x100 100 lines * down, etc. for each thread, then when they've finished, fetch * 100x100, 100 pixels across from the top of the image. This will * break sequentiality. */ int ystep = shrink->mh > VIPS__TILE_HEIGHT ? 1 : VIPS__TILE_HEIGHT / shrink->mh; int y; #ifdef DEBUG printf( "vips_shrink_gen: generating %d x %d at %d x %d\n", r->width, r->height, r->left, r->top ); #endif /*DEBUG*/ for( y = 0; y < r->height; y += ystep ) { /* Clip the this rect against the demand size. */ int height = VIPS_MIN( ystep, r->height - y ); VipsRect s; s.left = r->left * shrink->xshrink; s.top = (r->top + y) * shrink->yshrink; s.width = ceil( r->width * shrink->xshrink ); s.height = ceil( height * shrink->yshrink ); #ifdef DEBUG printf( "shrink_gen: requesting %d x %d at %d x %d\n", s.width, s.height, s.left, s.top ); #endif /*DEBUG*/ if( vips_region_prepare( ir, &s ) ) return( -1 ); vips_shrink_gen2( shrink, seq, or, ir, r->left, r->top + y, r->width, height ); } return( 0 ); }
static int read_jpeg_generate( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRect *r = &or->valid; ReadJpeg *jpeg = (ReadJpeg *) a; struct jpeg_decompress_struct *cinfo = &jpeg->cinfo; int sz = cinfo->output_width * cinfo->output_components; int y; #ifdef DEBUG printf( "read_jpeg_generate: line %d, %d rows\n", r->top, r->height ); #endif /*DEBUG*/ /* We're inside a tilecache where tiles are the full image width, so * this should always be true. */ g_assert( r->left == 0 ); g_assert( r->width == or->im->Xsize ); g_assert( VIPS_RECT_BOTTOM( r ) <= or->im->Ysize ); /* Tiles should always be on a 8-pixel boundary. */ g_assert( r->top % 8 == 0 ); /* Tiles should always be a strip in height, unless it's the final * strip. */ g_assert( r->height == VIPS_MIN( 8, or->im->Ysize - r->top ) ); /* Here for longjmp() from vips__new_error_exit(). */ if( setjmp( jpeg->eman.jmp ) ) return( -1 ); for( y = 0; y < r->height; y++ ) { JSAMPROW row_pointer[1]; row_pointer[0] = (JSAMPLE *) VIPS_REGION_ADDR( or, 0, r->top + y ); jpeg_read_scanlines( cinfo, &row_pointer[0], 1 ); if( jpeg->invert_pels ) { int x; for( x = 0; x < sz; x++ ) row_pointer[0][x] = 255 - row_pointer[0][x]; } } return( 0 ); }
static int png2vips_generate( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRect *r = &or->valid; Read *read = (Read *) a; int y; #ifdef DEBUG printf( "png2vips_generate: line %d, %d rows\n", r->top, r->height ); #endif /*DEBUG*/ /* We're inside a tilecache where tiles are the full image width, so * this should always be true. */ g_assert( r->left == 0 ); g_assert( r->width == or->im->Xsize ); g_assert( VIPS_RECT_BOTTOM( r ) <= or->im->Ysize ); /* Tiles should always be a strip in height, unless it's the final * strip. */ g_assert( r->height == VIPS_MIN( 8, or->im->Ysize - r->top ) ); /* And check that y_pos is correct. It should be, since we are inside * a vips_sequential(). */ g_assert( r->top == read->y_pos ); for( y = 0; y < r->height; y++ ) { png_bytep q = (png_bytep) VIPS_REGION_ADDR( or, 0, r->top + y ); /* We need to catch and ignore errors from read_row(). */ if( !setjmp( png_jmpbuf( read->pPng ) ) ) png_read_row( read->pPng, q, NULL ); else { #ifdef DEBUG printf( "png2vips_generate: png_read_row() failed, " "line %d\n", r->top + y ); printf( "png2vips_generate: file %s\n", read->name ); printf( "png2vips_generate: thread %p\n", g_thread_self() ); #endif /*DEBUG*/ } read->y_pos += 1; } return( 0 ); }
static int vips__openslide_generate( VipsRegion *out, void *seq, void *_rslide, void *unused, gboolean *stop ) { ReadSlide *rslide = _rslide; VipsRect *r = &out->valid; const char *error; int x, y; VIPS_DEBUG_MSG( "vips__openslide_generate: %dx%d @ %dx%d\n", r->width, r->height, r->left, r->top ); /* Fill in tile-sized chunks. Some versions of OpenSlide can fail for * very large requests. */ for( y = 0; y < r->height; y += TILE_HEIGHT ) for( x = 0; x < r->width; x += TILE_WIDTH ) { int w = VIPS_MIN( TILE_WIDTH, r->width - x ); int h = VIPS_MIN( TILE_HEIGHT, r->height - y ); openslide_read_region( rslide->osr, (uint32_t *) VIPS_REGION_ADDR( out, r->left + x, r->top + y ), (r->left + x) * rslide->downsample, (r->top + y) * rslide->downsample, rslide->layer, w, h ); } error = openslide_get_error( rslide->osr ); if( error ) { vips_error( "openslide2vips", _( "reading region: %s" ), error ); return( -1 ); } return( 0 ); }
/* Save an exif value to a string in a way that we can restore. We only bother * for the simple formats (that a client might try to change) though. * * Keep in sync with vips_exif_from_s() below. */ static void vips_exif_to_s( ExifData *ed, ExifEntry *entry, VipsBuf *buf ) { unsigned long i; int iv; ExifRational rv; ExifSRational srv; char txt[256]; if( entry->format == EXIF_FORMAT_ASCII ) { /* libexif does not null-terminate strings. Copy out and add * the \0 ourselves. */ int len = VIPS_MIN( 254, entry->size ); memcpy( txt, entry->data, len ); txt[len] = '\0'; vips_buf_appendf( buf, "%s ", txt ); } else if( entry->components < 10 && !vips_exif_get_int( ed, entry, 0, &iv ) ) { for( i = 0; i < entry->components; i++ ) { vips_exif_get_int( ed, entry, i, &iv ); vips_buf_appendf( buf, "%d ", iv ); } } else if( entry->components < 10 && !vips_exif_get_rational( ed, entry, 0, &rv ) ) { for( i = 0; i < entry->components; i++ ) { vips_exif_get_rational( ed, entry, i, &rv ); vips_buf_appendf( buf, "%u/%u ", rv.numerator, rv.denominator ); } } else if( entry->components < 10 && !vips_exif_get_srational( ed, entry, 0, &srv ) ) { for( i = 0; i < entry->components; i++ ) { vips_exif_get_srational( ed, entry, i, &srv ); vips_buf_appendf( buf, "%d/%d ", srv.numerator, srv.denominator ); } } else vips_buf_appendf( buf, "%s ", exif_entry_get_value( entry, txt, 256 ) ); vips_buf_appendf( buf, "(%s, %s, %lu components, %d bytes)", exif_entry_get_value( entry, txt, 256 ), exif_format_get_name( entry->format ), entry->components, entry->size ); }
/* Fetch a token. If it's a string token terminated by a '[', fetch up to the * matching ']' as well, for example ".jpg[Q=90]". * * Return NULL for end of tokens. */ const char * vips__token_segment( const char *p, VipsToken *token, char *string, int size ) { const char *q; if( !(q = vips__token_must( p, token, string, size )) ) return( NULL ); /* If we stopped on [, read up to the matching ]. */ if( *token == VIPS_TOKEN_STRING && q[0] == '[' ) { VipsToken sub_token; char sub_string[VIPS_PATH_MAX]; int depth; int i; depth = 0; do { if( !(q = vips__token_must( q, &sub_token, sub_string, VIPS_PATH_MAX )) ) return( NULL ); switch( sub_token ) { case VIPS_TOKEN_LEFT: depth += 1; break; case VIPS_TOKEN_RIGHT: depth -= 1; break; default: break; } } while( !(sub_token == VIPS_TOKEN_RIGHT && depth == 0) ); i = VIPS_MIN( q - p, size ); vips_strncpy( string, p, i + 1 ); } return( q ); }
/* Write tileh scanlines, less for the last strip. */ static int layer_write_strip( Write *write, Layer *layer, VipsRegion *strip ) { VipsImage *im = layer->image; VipsRect *area = &strip->valid; int height = VIPS_MIN( write->tileh, area->height ); int y; #ifdef DEBUG_VERBOSE printf( "Writing %d pixel strip at height %d to image %s\n", height, area->top, TIFFFileName( layer->tif ) ); #endif /*DEBUG_VERBOSE*/ for( y = 0; y < height; y++ ) { VipsPel *p = VIPS_REGION_ADDR( strip, 0, area->top + y ); /* Any repacking necessary. */ if( im->Coding == VIPS_CODING_LABQ ) { LabQ2LabC( write->tbuf, p, im->Xsize ); p = write->tbuf; } else if( im->BandFmt == VIPS_FORMAT_SHORT && im->Type == VIPS_INTERPRETATION_LABS ) { LabS2Lab16( write->tbuf, p, im->Xsize ); p = write->tbuf; } else if( write->onebit ) { eightbit2onebit( write, write->tbuf, p, im->Xsize ); p = write->tbuf; } else if( (im->Bands == 1 || im->Bands == 2) && write->miniswhite ) { invert_band0( write, write->tbuf, p, im->Xsize ); p = write->tbuf; } if( TIFFWriteScanline( layer->tif, p, area->top + y, 0 ) < 0 ) return( -1 ); } return( 0 ); }
static int vips_worley_distance( VipsWorley *worley, Cell cells[9], int x, int y ) { int distance; int i, j; distance = worley->cell_size * 1.5; for( i = 0; i < 9; i++ ) { Cell *cell = &cells[i]; for( j = 0; j < cell->n_features; j++ ) { int d = vips_hypot( x - cell->feature_x[j], y - cell->feature_y[j] ); distance = VIPS_MIN( distance, d ); } } return( distance ); }
/* Calculate the shrink factors. * * We shrink in two stages: first, a shrink with a block average. This can * only accurately shrink by integer factors. We then do a second shrink with * a supplied interpolator to get the exact size we want. */ static int calculate_shrink( int width, int height, double *residual ) { /* Calculate the horizontal and vertical shrink we'd need to fit the * image to the bounding box, and pick the biggest. * * In crop mode we aim to fill the bounding box, so we must use the * smaller axis. */ double horizontal = (double) width / thumbnail_width; double vertical = (double) height / thumbnail_height; double factor = crop_image ? VIPS_MIN( horizontal, vertical ) : VIPS_MAX( horizontal, vertical ); /* If the shrink factor is <= 1.0, we need to zoom rather than shrink. * Just set the factor to 1 in this case. */ double factor2 = factor < 1.0 ? 1.0 : factor; /* Int component of shrink. */ int shrink = floor( factor2 ); if( residual ) { /* Width after int shrink. */ int iwidth = width / shrink; /* Therefore residual scale factor is. */ *residual = (width / factor) / iwidth; } return( shrink ); }
/* Subsample a VipsRegion. We fetch in VIPS_MAX_WIDTH pixel-wide strips, * left-to-right across the input. */ static int vips_subsample_line_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRegion *ir = (VipsRegion *) seq; VipsSubsample *subsample = (VipsSubsample *) b; VipsImage *in = (VipsImage *) a; VipsRect *r = &or->valid; int le = r->left; int ri = VIPS_RECT_RIGHT( r ); int to = r->top; int bo = VIPS_RECT_BOTTOM( r ); int ps = VIPS_IMAGE_SIZEOF_PEL( in ); int owidth = VIPS_MAX_WIDTH / subsample->xfac; VipsRect s; int x, y; int z, k; /* Loop down the region. */ for( y = to; y < bo; y++ ) { VipsPel *q = VIPS_REGION_ADDR( or, le, y ); VipsPel *p; /* Loop across the region, in owidth sized pieces. */ for( x = le; x < ri; x += owidth ) { /* How many pixels do we make this time? */ int ow = VIPS_MIN( owidth, ri - x ); /* Ask for this many from input ... can save a * little here! */ int iw = ow * subsample->xfac - (subsample->xfac - 1); /* Ask for input. */ s.left = x * subsample->xfac; s.top = y * subsample->yfac; s.width = iw; s.height = 1; if( vips_region_prepare( ir, &s ) ) return( -1 ); /* Append new pels to output. */ p = VIPS_REGION_ADDR( ir, s.left, s.top ); for( z = 0; z < ow; z++ ) { for( k = 0; k < ps; k++ ) q[k] = p[k]; q += ps; p += ps * subsample->xfac; } } } return( 0 ); }
/* Zoom a VipsRegion. */ static int vips_zoom_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRegion *ir = (VipsRegion *) seq; VipsZoom *zoom = (VipsZoom *) b; /* Output area we are building. */ const VipsRect *r = &or->valid; const int ri = VIPS_RECT_RIGHT( r ); const int bo = VIPS_RECT_BOTTOM(r); VipsRect s; int left, right, top, bottom; int width, height; /* Area of input we need. We have to round out, as we may have * part-pixels all around the edges. */ left = ROUND_DOWN( r->left, zoom->xfac ); right = ROUND_UP( ri, zoom->xfac ); top = ROUND_DOWN( r->top, zoom->yfac ); bottom = ROUND_UP( bo, zoom->yfac ); width = right - left; height = bottom - top; s.left = left / zoom->xfac; s.top = top / zoom->yfac; s.width = width / zoom->xfac; s.height = height / zoom->yfac; if( vips_region_prepare( ir, &s ) ) return( -1 ); /* Find the part of the output (if any) which uses only whole pels. */ left = ROUND_UP( r->left, zoom->xfac ); right = ROUND_DOWN( ri, zoom->xfac ); top = ROUND_UP( r->top, zoom->yfac ); bottom = ROUND_DOWN( bo, zoom->yfac ); width = right - left; height = bottom - top; /* Stage 1: we just paint the whole pels in the centre of the region. * As we know they are not clipped, we can do it quickly. */ if( width > 0 && height > 0 ) vips_zoom_paint_whole( or, ir, zoom, left, right, top, bottom ); /* Just fractional pixels left. Paint in the top, left, right and * bottom parts. */ if( top - r->top > 0 ) /* Some top pixels. */ vips_zoom_paint_part( or, ir, zoom, r->left, ri, r->top, VIPS_MIN( top, bo ) ); if( left - r->left > 0 && height > 0 ) /* Left pixels. */ vips_zoom_paint_part( or, ir, zoom, r->left, VIPS_MIN( left, ri ), top, bottom ); if( ri - right > 0 && height > 0 ) /* Right pixels. */ vips_zoom_paint_part( or, ir, zoom, VIPS_MAX( right, r->left ), ri, top, bottom ); if( bo - bottom > 0 && height >= 0 ) /* Bottom pixels. */ vips_zoom_paint_part( or, ir, zoom, r->left, ri, VIPS_MAX( bottom, r->top ), bo ); return( 0 ); }
/* Paint the part of the region containing only part-pels. */ static void vips_zoom_paint_part( VipsRegion *or, VipsRegion *ir, VipsZoom *zoom, const int left, const int right, const int top, const int bottom ) { const int ps = VIPS_IMAGE_SIZEOF_PEL( ir->im ); const int ls = VIPS_REGION_LSKIP( or ); const int rs = ps * (right - left); /* Start position in input. */ const int ix = left / zoom->xfac; const int iy = top / zoom->yfac; /* Pels down to yfac boundary, pels down to bottom. Do the smallest of * these for first y loop. */ const int ptbound = (iy + 1) * zoom->yfac - top; const int ptbot = bottom - top; int yt = VIPS_MIN( ptbound, ptbot ); int x, y, z, i; /* Only know this. */ g_assert( right - left >= 0 && bottom - top >= 0 ); /* Have to loop over output. */ for( y = top; y < bottom; ) { VipsPel *p = VIPS_REGION_ADDR( ir, ix, y / zoom->yfac ); VipsPel *q = VIPS_REGION_ADDR( or, left, y ); VipsPel *r; /* Output pels until we jump the input pointer. */ int xt = (ix + 1) * zoom->xfac - left; /* Loop for this output line. */ r = q; for( x = left; x < right; x++ ) { /* Copy 1 pel. */ for( i = 0; i < ps; i++ ) r[i] = p[i]; r += ps; /* Move input if on boundary. */ --xt; if( xt == 0 ) { xt = zoom->xfac; p += ps; } } /* Repeat that output line until the bottom of this pixel * boundary, or we hit bottom. */ r = q + ls; for( z = 1; z < yt; z++ ) { memcpy( r, q, rs ); r += ls; } /* Move y on by the number of lines we wrote. */ y += yt; /* Reset yt for next iteration. */ yt = zoom->yfac; } }
static int png2vips_generate( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRect *r = &or->valid; Read *read = (Read *) a; int y; #ifdef DEBUG printf( "png2vips_generate: line %d, %d rows\n", r->top, r->height ); printf( "png2vips_generate: y_top = %d\n", read->y_pos ); #endif /*DEBUG*/ /* We're inside a tilecache where tiles are the full image width, so * this should always be true. */ g_assert( r->left == 0 ); g_assert( r->width == or->im->Xsize ); g_assert( VIPS_RECT_BOTTOM( r ) <= or->im->Ysize ); /* Tiles should always be a strip in height, unless it's the final * strip. */ g_assert( r->height == VIPS_MIN( 8, or->im->Ysize - r->top ) ); /* And check that y_pos is correct. It should be, since we are inside * a vips_sequential(). */ if( r->top != read->y_pos ) { vips_error( "vipspng", _( "out of order read at line %d" ), read->y_pos ); return( -1 ); } for( y = 0; y < r->height; y++ ) { png_bytep q = (png_bytep) VIPS_REGION_ADDR( or, 0, r->top + y ); /* We need to catch and ignore errors from read_row(). */ if( !setjmp( png_jmpbuf( read->pPng ) ) ) png_read_row( read->pPng, q, NULL ); else { #ifdef DEBUG printf( "png2vips_generate: png_read_row() failed, " "line %d\n", r->top + y ); printf( "png2vips_generate: file %s\n", read->name ); printf( "png2vips_generate: thread %p\n", g_thread_self() ); #endif /*DEBUG*/ } read->y_pos += 1; } /* Turn errors back on. png_read_end() can trigger them too. */ if( setjmp( png_jmpbuf( read->pPng ) ) ) return( -1 ); /* We need to shut down the reader immediately at the end of read or * we won't detach ready for the next image. */ if( read->y_pos >= read->out->Ysize ) { png_read_end( read->pPng, NULL ); read_destroy( read ); } return( 0 ); }
/* Entropy-style smartcrop. Repeatedly discard low interest areas. This should * be faster for very large images. */ static int vips_smartcrop_entropy( VipsSmartcrop *smartcrop, VipsImage *in, int *left, int *top ) { int max_slice_size; int width; int height; *left = 0; *top = 0; width = in->Xsize; height = in->Ysize; /* How much do we trim by each iteration? Aim for 8 steps in the axis * that needs trimming most. */ max_slice_size = VIPS_MAX( ceil( (width - smartcrop->width) / 8.0 ), ceil( (height - smartcrop->height) / 8.0 ) ); /* Repeatedly take a slice off width and height until we * reach the target. */ while( width > smartcrop->width || height > smartcrop->height ) { const int slice_width = VIPS_MIN( width - smartcrop->width, max_slice_size ); const int slice_height = VIPS_MIN( height - smartcrop->height, max_slice_size ); if( slice_width > 0 ) { double left_score; double right_score; if( vips_smartcrop_score( smartcrop, in, *left, *top, slice_width, height, &left_score ) ) return( -1 ); if( vips_smartcrop_score( smartcrop, in, *left + width - slice_width, *top, slice_width, height, &right_score ) ) return( -1 ); width -= slice_width; if( left_score < right_score ) *left += slice_width; } if( slice_height > 0 ) { double top_score; double bottom_score; if( vips_smartcrop_score( smartcrop, in, *left, *top, width, slice_height, &top_score ) ) return( -1 ); if( vips_smartcrop_score( smartcrop, in, *left, *top + height - slice_height, width, slice_height, &bottom_score ) ) return( -1 ); height -= slice_height; if( top_score < bottom_score ) *top += slice_height; } } return( 0 ); }
static int vips_join_build( VipsObject *object ) { VipsConversion *conversion = VIPS_CONVERSION( object ); VipsJoin *join = (VipsJoin *) object; int x, y; VipsImage *t; if( VIPS_OBJECT_CLASS( vips_join_parent_class )->build( object ) ) return( -1 ); switch( join->direction ) { case VIPS_DIRECTION_HORIZONTAL: x = join->in1->Xsize + join->shim; switch( join->align ) { case VIPS_ALIGN_LOW: y = 0; break; case VIPS_ALIGN_CENTRE: y = join->in1->Ysize / 2 - join->in2->Ysize / 2; break; case VIPS_ALIGN_HIGH: y = join->in1->Ysize - join->in2->Ysize; break; default: g_assert( 0 ); /* Keep -Wall happy. */ return( 0 ); } break; case VIPS_DIRECTION_VERTICAL: y = join->in1->Ysize + join->shim; switch( join->align ) { case VIPS_ALIGN_LOW: x = 0; break; case VIPS_ALIGN_CENTRE: x = join->in1->Xsize / 2 - join->in2->Xsize / 2; break; case VIPS_ALIGN_HIGH: x = join->in1->Xsize - join->in2->Xsize; break; default: g_assert( 0 ); /* Keep -Wall happy. */ return( 0 ); } break; default: g_assert( 0 ); /* Keep -Wall happy. */ return( 0 ); } if( vips_insert( join->in1, join->in2, &t, x, y, "expand", TRUE, "background", join->background, NULL ) ) return( -1 ); if( !join->expand ) { VipsImage *t2; int left, top, width, height; switch( join->direction ) { case VIPS_DIRECTION_HORIZONTAL: left = 0; top = VIPS_MAX( 0, y ) - y; width = t->Xsize; height = VIPS_MIN( join->in1->Ysize, join->in2->Ysize ); break; case VIPS_DIRECTION_VERTICAL: left = VIPS_MAX( 0, x ) - x; top = 0; width = VIPS_MIN( join->in1->Xsize, join->in2->Xsize ); height = t->Ysize; break; default: g_assert( 0 ); /* Keep -Wall happy. */ return( 0 ); } if( vips_extract_area( t, &t2, left, top, width, height, NULL ) ) { g_object_unref( t ); return( -1 ); } g_object_unref( t ); t = t2; } if( vips_image_write( t, conversion->out ) ) { g_object_unref( t ); return( -1 ); } g_object_unref( t ); return( 0 ); }
/* Break a command-line argument into tokens separated by whitespace. * * Strings can't be adjacent, so "hello world" (without quotes) is a single * string. Strings are written (with \" escaped) into @string. If the string * is larger than @size, it is silently null-termionated and truncated. * * Return NULL for end of tokens. */ const char * vips__token_get( const char *p, VipsToken *token, char *string, int size ) { const char *q; int ch; int n; int i; /* Parse this token with p. */ if( !p ) return( NULL ); /* Skip initial whitespace. */ p += strspn( p, " \t\n\r" ); if( !p[0] ) return( NULL ); switch( (ch = p[0]) ) { case '{': case '[': case '(': case '<': *token = VIPS_TOKEN_LEFT; p += 1; break; case ')': case ']': case '}': case '>': *token = VIPS_TOKEN_RIGHT; p += 1; break; case '=': *token = VIPS_TOKEN_EQUALS; p += 1; break; case ',': *token = VIPS_TOKEN_COMMA; p += 1; break; case '"': case '\'': /* Parse a quoted string. Copy up to ", interpret any \", * error if no closing ". */ *token = VIPS_TOKEN_STRING; do { /* Number of characters until the next quote * character or end of string. */ if( (q = strchr( p + 1, ch )) ) n = q - p + 1; else n = strlen( p + 1 ); /* How much can we copy to the buffer? */ i = VIPS_MIN( n, size ); vips_strncpy( string, p + 1, i ); /* We might have stopped at an escaped quote. If the * string was not truncated, swap the preceding * backslash for a quote. */ if( p[n + 1] == ch && p[n] == '\\' && i == n ) string[i - 1] = ch; string += i; size -= i; p += n + 1; } while( p[0] && p[-1] == '\\' ); p += 1; break; default: /* It's an unquoted string: read up to the next non-string * character. We don't allow two strings next to each other, * so the next break must be bracket, equals, comma. */ *token = VIPS_TOKEN_STRING; n = strcspn( p, "<[{()}]>=," ); i = VIPS_MIN( n, size ); vips_strncpy( string, p, i + 1 ); p += n; /* We remove leading whitespace, so we trim trailing * whitespace from unquoted strings too. Only if the string * hasn't been truncated. */ if( i == n ) while( i > 0 && isspace( string[i - 1] ) ) { string[i - 1] = '\0'; i--; } break; } return( p ); }
static int png2vips_generate( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRect *r = &or->valid; Read *read = (Read *) a; int y; #ifdef DEBUG printf( "png2vips_generate: line %d, %d rows\n", r->top, r->height ); printf( "png2vips_generate: y_top = %d\n", read->y_pos ); #endif /*DEBUG*/ /* We're inside a tilecache where tiles are the full image width, so * this should always be true. */ g_assert( r->left == 0 ); g_assert( r->width == or->im->Xsize ); g_assert( VIPS_RECT_BOTTOM( r ) <= or->im->Ysize ); /* Tiles should always be a strip in height, unless it's the final * strip. */ g_assert( r->height == VIPS_MIN( 8, or->im->Ysize - r->top ) ); /* And check that y_pos is correct. It should be, since we are inside * a vips_sequential(). */ if( r->top != read->y_pos ) { vips_error( "vipspng", _( "out of order read at line %d" ), read->y_pos ); return( -1 ); } for( y = 0; y < r->height; y++ ) { png_bytep q = (png_bytep) VIPS_REGION_ADDR( or, 0, r->top + y ); /* We need to catch errors from read_row(). */ if( !setjmp( png_jmpbuf( read->pPng ) ) ) png_read_row( read->pPng, q, NULL ); else { /* We've failed to read some pixels. Knock this * operation out of cache. */ vips_foreign_load_invalidate( read->out ); #ifdef DEBUG printf( "png2vips_generate: png_read_row() failed, " "line %d\n", r->top + y ); printf( "png2vips_generate: file %s\n", read->name ); printf( "png2vips_generate: thread %p\n", g_thread_self() ); #endif /*DEBUG*/ /* And bail if fail is on. We have to add an error * message, since the handler we install just does * g_warning(). */ if( read->fail ) { vips_error( "vipspng", "%s", _( "libpng read error" ) ); return( -1 ); } } read->y_pos += 1; } /* Catch errors from png_read_end(). This can fail on a truncated * file. */ if( setjmp( png_jmpbuf( read->pPng ) ) ) { if( read->fail ) { vips_error( "vipspng", "%s", _( "libpng read error" ) ); return( -1 ); } return( 0 ); } /* We need to shut down the reader immediately at the end of read or * we won't detach ready for the next image. */ if( read->y_pos >= read->out->Ysize ) { png_read_end( read->pPng, NULL ); read_destroy( read ); } return( 0 ); }