static int vips_sequential_build( VipsObject *object ) { VipsConversion *conversion = VIPS_CONVERSION( object ); VipsSequential *sequential = (VipsSequential *) object; VipsImage *t; VIPS_DEBUG_MSG( "vips_sequential_build\n" ); if( VIPS_OBJECT_CLASS( vips_sequential_parent_class )->build( object ) ) return( -1 ); if( vips_image_pio_input( sequential->in ) ) return( -1 ); if( vips_linecache( sequential->in, &t, "tile_height", sequential->tile_height, "access", sequential->access, NULL ) ) return( -1 ); vips_object_local( object, t ); if( vips_image_pipelinev( conversion->out, VIPS_DEMAND_STYLE_THINSTRIP, t, NULL ) ) return( -1 ); if( vips_image_generate( conversion->out, vips_start_one, vips_sequential_generate, vips_stop_one, t, sequential ) ) return( -1 ); return( 0 ); }
static int vips_worley_build( VipsObject *object ) { VipsCreate *create = VIPS_CREATE( object ); VipsWorley *worley = (VipsWorley *) object; if( VIPS_OBJECT_CLASS( vips_worley_parent_class )->build( object ) ) return( -1 ); /* Be careful if width is a multiple of cell_size. */ worley->cells_across = ROUND_UP( worley->width, worley->cell_size ) / worley->cell_size; worley->cells_down = ROUND_UP( worley->height, worley->cell_size ) / worley->cell_size; worley->seed = g_random_double() * 0xffffffffu; vips_image_init_fields( create->out, worley->width, worley->height, 1, VIPS_FORMAT_INT, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 ); vips_image_pipelinev( create->out, VIPS_DEMAND_STYLE_ANY, NULL ); if( vips_image_generate( create->out, vips_worley_start, vips_worley_gen, vips_worley_stop, worley, NULL ) ) return( -1 ); return( 0 ); }
static void vips_foreign_load_svg_parse( VipsForeignLoadSvg *svg, VipsImage *out ) { RsvgDimensionData dimensions; int width; int height; double scale; double res; /* Calculate dimensions at default dpi/scale. */ rsvg_handle_set_dpi( svg->page, 72.0 ); rsvg_handle_get_dimensions( svg->page, &dimensions ); width = dimensions.width; height = dimensions.height; /* Calculate dimensions at required dpi/scale. */ scale = svg->scale * svg->dpi / 72.0; if( scale != 1.0 ) { rsvg_handle_set_dpi( svg->page, svg->dpi * svg->scale ); rsvg_handle_get_dimensions( svg->page, &dimensions ); if( width == dimensions.width && height == dimensions.height ) { /* SVG without width and height always reports the same * dimensions regardless of dpi. Apply dpi/scale using * cairo instead. */ svg->cairo_scale = scale; width = width * scale; height = height * scale; } else { /* SVG with width and height reports correctly scaled * dimensions. */ width = dimensions.width; height = dimensions.height; } } /* We need pixels/mm for vips. */ res = svg->dpi / 25.4; vips_image_init_fields( out, width, height, 4, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, res, res ); /* We render to a linecache, so fat strips work well. */ vips_image_pipelinev( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL ); }
static int read_header( Read *read, VipsImage *out ) { vips_image_init_fields( out, read->config.input.width, read->config.input.height, read->config.input.has_alpha ? 4 : 3, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); return( 0 ); }
static Read * read_new( const char *filename, VipsImage *out ) { Read *read; int64_t w, h; read = VIPS_NEW( out, Read ); memset( read, 0, sizeof( *read ) ); g_signal_connect( out, "close", G_CALLBACK( read_destroy_cb ), read ); GDALAllRegister(); read->hDataset = GDALOpen( filename, GA_ReadOnly ); if( read->hDataset == NULL ) { vips_error( "gdal2vips", "%s", _( "unsupported gdal format" ) ); return( NULL ); } vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); w = GDALGetRasterXSize (read->hDataset); h = GDALGetRasterYSize (read->hDataset); GDALRasterBandH hBand; hBand = GDALGetRasterBand( read->hDataset, 1 ); if( hBand == NULL ) { vips_error( "gdal2vips", "%s", _( "can not open raster band" ) ); return( NULL ); } GDALGetBlockSize( hBand, &read->tile_width, &read->tile_height ); read->data_type = GDALGetRasterDataType( hBand ); // int vipsFormat = VIPS_FORMAT_UCHAR; // switch( dataType ) { // case GDT_Byte: vipsFormat = VIPS_FORMAT_UCHAR; break; // case GDT_UInt16: vipsFormat = VIPS_FORMAT_USHORT; break; // } vips_image_init_fields( out, w, h, 3, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_RGB, 1.0, 1.0 ); return( read ); }
/* Walk the pyramid allocating resources. */ static int pyramid_fill( Write *write ) { Layer *layer; for( layer = write->layer; layer; layer = layer->below ) { VipsRect strip_size; layer->image = vips_image_new(); if( vips_image_pipelinev( layer->image, VIPS_DEMAND_STYLE_ANY, write->im, NULL ) ) return( -1 ); layer->image->Xsize = layer->width; layer->image->Ysize = layer->height; layer->strip = vips_region_new( layer->image ); layer->copy = vips_region_new( layer->image ); /* The regions will get used in the bg thread callback, so * make sure we don't own them. */ vips__region_no_ownership( layer->strip ); vips__region_no_ownership( layer->copy ); /* Build a line of tiles here. * * Expand the strip if necessary to make sure we have an even * number of lines. */ strip_size.left = 0; strip_size.top = 0; strip_size.width = layer->image->Xsize; strip_size.height = write->tileh; if( (strip_size.height & 1) == 1 ) strip_size.height += 1; if( vips_region_buffer( layer->strip, &strip_size ) ) return( -1 ); if( !(layer->tif = vips__tiff_openout( layer->lname, write->bigtiff )) || write_tiff_header( write, layer ) ) return( -1 ); } return( 0 ); }
static int vips_point_build( VipsObject *object ) { VipsCreate *create = VIPS_CREATE( object ); VipsPoint *point = VIPS_POINT( object ); VipsPointClass *class = VIPS_POINT_GET_CLASS( point ); VipsImage **t = (VipsImage **) vips_object_local_array( object, 4 ); VipsImage *in; if( VIPS_OBJECT_CLASS( vips_point_parent_class )->build( object ) ) return( -1 ); t[0] = vips_image_new(); vips_image_init_fields( t[0], point->width, point->height, 1, VIPS_FORMAT_FLOAT, VIPS_CODING_NONE, class->interpretation, 1.0, 1.0 ); vips_image_pipelinev( t[0], VIPS_DEMAND_STYLE_ANY, NULL ); if( vips_image_generate( t[0], NULL, vips_point_gen, NULL, point, NULL ) ) return( -1 ); in = t[0]; if( point->uchar ) { float min = class->min; float max = class->max; float range = max - min; if( vips_linear1( in, &t[2], 255.0 / range, -min * 255.0 / range, "uchar", TRUE, NULL ) ) return( -1 ); in = t[2]; /* We don't want FOURIER or whatever in this case. */ in->Type = VIPS_INTERPRETATION_MULTIBAND; }
static int vips_gaussnoise_build( VipsObject *object ) { VipsCreate *create = VIPS_CREATE( object ); VipsGaussnoise *gaussnoise = (VipsGaussnoise *) object; if( VIPS_OBJECT_CLASS( vips_gaussnoise_parent_class )->build( object ) ) return( -1 ); vips_image_init_fields( create->out, gaussnoise->width, gaussnoise->height, 1, VIPS_FORMAT_FLOAT, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 ); vips_image_pipelinev( create->out, VIPS_DEMAND_STYLE_ANY, NULL ); if( vips_image_generate( create->out, NULL, vips_gaussnoise_gen, NULL, gaussnoise, NULL ) ) return( -1 ); return( 0 ); }
static int vips_byteswap_build( VipsObject *object ) { VipsConversion *conversion = VIPS_CONVERSION( object ); VipsByteswap *byteswap = (VipsByteswap *) object; if( VIPS_OBJECT_CLASS( vips_byteswap_parent_class )->build( object ) ) return( -1 ); if( vips_image_pio_input( byteswap->in ) ) return( -1 ); if( vips_image_pipelinev( conversion->out, VIPS_DEMAND_STYLE_THINSTRIP, byteswap->in, NULL ) ) return( -1 ); if( vips_image_generate( conversion->out, vips_start_one, vips_byteswap_gen, vips_stop_one, byteswap->in, byteswap ) ) return( -1 ); return( 0 ); }
static void vips_foreign_load_svg_parse( VipsForeignLoadSvg *svg, VipsImage *out ) { RsvgDimensionData dimensions; double res; rsvg_handle_get_dimensions( svg->page, &dimensions ); /* We need pixels/mm for vips. */ res = svg->dpi / 25.4; vips_image_init_fields( out, dimensions.width * svg->scale, dimensions.height * svg->scale, 4, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, res, res ); /* We render to a linecache, so fat strips work well. */ vips_image_pipelinev( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL ); }
static int vips_black_build( VipsObject *object ) { VipsCreate *create = VIPS_CREATE( object ); VipsBlack *black = (VipsBlack *) object; if( VIPS_OBJECT_CLASS( vips_black_parent_class )->build( object ) ) return( -1 ); vips_image_init_fields( create->out, black->width, black->height, black->bands, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, black->bands == 1 ? VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_MULTIBAND, 1.0, 1.0 ); vips_image_pipelinev( create->out, VIPS_DEMAND_STYLE_ANY, NULL ); if( vips_image_generate( create->out, NULL, vips_black_gen, NULL, NULL, NULL ) ) return( -1 ); return( 0 ); }
static int vips_flip_build( VipsObject *object ) { VipsConversion *conversion = VIPS_CONVERSION( object ); VipsFlip *flip = (VipsFlip *) object; VipsGenerateFn generate_fn; if( VIPS_OBJECT_CLASS( vips_flip_parent_class )->build( object ) ) return( -1 ); if( vips_image_pio_input( flip->in ) ) return( -1 ); if( vips_image_pipelinev( conversion->out, VIPS_DEMAND_STYLE_THINSTRIP, flip->in, NULL ) ) return( -1 ); if( flip->direction == VIPS_DIRECTION_HORIZONTAL ) { generate_fn = vips_flip_horizontal_gen; conversion->out->Xoffset = flip->in->Xsize; conversion->out->Yoffset = 0; } else { generate_fn = vips_flip_vertical_gen; conversion->out->Xoffset = 0; conversion->out->Yoffset = flip->in->Ysize; } if( vips_image_generate( conversion->out, vips_start_one, generate_fn, vips_stop_one, flip->in, flip ) ) return( -1 ); return( 0 ); }
static int read_header( Read *read, VipsImage *out ) { vips_image_init_fields( out, read->width, read->height, read->config.input.has_alpha ? 4 : 3, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); #ifdef HAVE_LIBWEBPMUX { WebPData bitstream; WebPMux *mux; int i; /* We have to parse the whole file again to get the metadata out. * * Don't make parse failure an error. We don't want to refuse to read * any pixels because of some malformed metadata. */ bitstream.bytes = read->data; bitstream.size = read->length; if( !(mux = WebPMuxCreate( &bitstream, 0 )) ) { vips_warn( "webp", "%s", _( "unable to read image metadata" ) ); return( 0 ); } for( i = 0; i < vips__n_webp_names; i++ ) { const char *vips = vips__webp_names[i].vips; const char *webp = vips__webp_names[i].webp; WebPData data; if( WebPMuxGetChunk( mux, webp, &data ) == WEBP_MUX_OK ) { void *blob; if( !(blob = vips_malloc( NULL, data.size )) ) { WebPMuxDelete( mux ); return( -1 ); } memcpy( blob, data.bytes, data.size ); vips_image_set_blob( out, vips, (VipsCallbackFn) vips_free, blob, data.size ); } } WebPMuxDelete( mux ); /* We may have read some exif ... parse into the header. */ if( vips__exif_parse( out ) ) return( -1 ); } #endif /*HAVE_LIBWEBPMUX*/ return( 0 ); }
static int vips_copy_build( VipsObject *object ) { VipsConversion *conversion = VIPS_CONVERSION( object ); VipsCopy *copy = (VipsCopy *) object; int i; if( VIPS_OBJECT_CLASS( vips_copy_parent_class )->build( object ) ) return( -1 ); if( vips_image_pio_input( copy->in ) ) return( -1 ); if( vips_image_pipelinev( conversion->out, VIPS_DEMAND_STYLE_THINSTRIP, copy->in, NULL ) ) return( -1 ); /* Use props to adjust header fields. */ for( i = 0; i < VIPS_NUMBER( vips_copy_names ); i++ ) { const char *name = vips_copy_names[i]; GParamSpec *pspec; VipsArgumentClass *argument_class; VipsArgumentInstance *argument_instance; if( vips_object_get_argument( object, name, &pspec, &argument_class, &argument_instance ) ) return( -1 ); if( argument_instance->assigned ) { GType type = G_PARAM_SPEC_VALUE_TYPE( pspec ); GValue value = { 0, }; g_value_init( &value, type ); g_object_get_property( G_OBJECT( object ), name, &value ); #ifdef VIPS_DEBUG { char *str; str = g_strdup_value_contents( &value ); printf( "vips_copy_build: %s = %s\n", name, str ); g_free( str ); } #endif /* VIPS_DEBUG */ g_object_set_property( G_OBJECT( conversion->out ), name, &value ); g_value_unset( &value ); } } if( vips_image_generate( conversion->out, vips_start_one, vips_copy_gen, vips_stop_one, copy->in, copy ) ) return( -1 ); return( 0 ); }
static int vips_fits_get_header( VipsFits *fits, VipsImage *out ) { int status; int bitpix; int width, height, bands, format, type; int keysexist; int i; status = 0; if( fits_get_img_paramll( fits->fptr, 10, &bitpix, &fits->naxis, fits->naxes, &status ) ) { vips_fits_error( status ); return( -1 ); } #ifdef VIPS_DEBUG VIPS_DEBUG_MSG( "naxis = %d\n", fits->naxis ); for( i = 0; i < fits->naxis; i++ ) VIPS_DEBUG_MSG( "%d) %lld\n", i, fits->naxes[i] ); #endif /*VIPS_DEBUG*/ height = 1; bands = 1; switch( fits->naxis ) { /* If you add more dimensions here, adjust data read below. See also * the definition of MAX_DIMENSIONS above. */ case 10: case 9: case 8: case 7: case 6: case 5: case 4: for( i = fits->naxis; i > 3; i-- ) if( fits->naxes[i - 1] != 1 ) { vips_error( "fits", "%s", _( "dimensions above 3 " "must be size 1" ) ); return( -1 ); } case 3: bands = fits->naxes[2]; case 2: height = fits->naxes[1]; case 1: width = fits->naxes[0]; break; default: vips_error( "fits", _( "bad number of axis %d" ), fits->naxis ); return( -1 ); } /* Are we in one-band mode? */ if( fits->band_select != -1 ) bands = 1; /* Get image format. We want the 'raw' format of the image, our caller * can convert using the meta info if they want. */ for( i = 0; i < VIPS_NUMBER( fits2vips_formats ); i++ ) if( fits2vips_formats[i][0] == bitpix ) break; if( i == VIPS_NUMBER( fits2vips_formats ) ) { vips_error( "fits", _( "unsupported bitpix %d\n" ), bitpix ); return( -1 ); } format = fits2vips_formats[i][1]; fits->datatype = fits2vips_formats[i][2]; if( bands == 1 ) { if( format == VIPS_FORMAT_USHORT ) type = VIPS_INTERPRETATION_GREY16; else type = VIPS_INTERPRETATION_B_W; } else if( bands == 3 ) { if( format == VIPS_FORMAT_USHORT ) type = VIPS_INTERPRETATION_RGB16; else type = VIPS_INTERPRETATION_RGB; } else type = VIPS_INTERPRETATION_MULTIBAND; vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); vips_image_init_fields( out, width, height, bands, format, VIPS_CODING_NONE, type, 1.0, 1.0 ); /* Read all keys into meta. */ if( fits_get_hdrspace( fits->fptr, &keysexist, NULL, &status ) ) { vips_fits_error( status ); return( -1 ); } for( i = 0; i < keysexist; i++ ) { char record[81]; char vipsname[100]; if( fits_read_record( fits->fptr, i + 1, record, &status ) ) { vips_fits_error( status ); return( -1 ); } VIPS_DEBUG_MSG( "fits2vips: setting meta on vips image:\n" ); VIPS_DEBUG_MSG( " record == \"%s\"\n", record ); /* FITS lets keys repeat. For example, HISTORY appears many * times, each time with a fresh line of history attached. We * have to include the key index in the vips name we assign. */ vips_snprintf( vipsname, 100, "fits-%d", i ); vips_image_set_string( out, vipsname, record ); } return( 0 ); }
/* Read a cinfo to a VIPS image. Set invert_pels if the pixel reader needs to * do 255-pel. */ static int read_jpeg_header( ReadJpeg *jpeg, VipsImage *out ) { struct jpeg_decompress_struct *cinfo = &jpeg->cinfo; jpeg_saved_marker_ptr p; VipsInterpretation interpretation; double xres, yres; /* Capture app2 sections here for assembly. */ void *app2_data[MAX_APP2_SECTIONS] = { 0 }; size_t app2_data_length[MAX_APP2_SECTIONS] = { 0 }; size_t data_length; int i; /* Read JPEG header. libjpeg will set out_color_space sanely for us * for YUV YCCK etc. */ jpeg_read_header( cinfo, TRUE ); cinfo->scale_denom = jpeg->shrink; cinfo->scale_num = 1; jpeg_calc_output_dimensions( cinfo ); jpeg->invert_pels = FALSE; switch( cinfo->out_color_space ) { case JCS_GRAYSCALE: interpretation = VIPS_INTERPRETATION_B_W; break; case JCS_CMYK: interpretation = VIPS_INTERPRETATION_CMYK; /* Photoshop writes CMYK JPEG inverted :-( Maybe this is a * way to spot photoshop CMYK JPGs. */ if( cinfo->saw_Adobe_marker ) jpeg->invert_pels = TRUE; break; case JCS_RGB: default: interpretation = VIPS_INTERPRETATION_sRGB; break; } /* Get the jfif resolution. exif may overwrite this later. */ xres = 1.0; yres = 1.0; if( cinfo->saw_JFIF_marker && cinfo->X_density != 1U && cinfo->Y_density != 1U ) { #ifdef DEBUG printf( "read_jpeg_header: seen jfif _density %d, %d\n", cinfo->X_density, cinfo->Y_density ); #endif /*DEBUG*/ switch( cinfo->density_unit ) { case 0: /* None. Just set. */ xres = cinfo->X_density; yres = cinfo->Y_density; break; case 1: /* Pixels per inch. */ xres = cinfo->X_density / 25.4; yres = cinfo->Y_density / 25.4; break; case 2: /* Pixels per cm. */ xres = cinfo->X_density / 10.0; yres = cinfo->Y_density / 10.0; break; default: vips_warn( "VipsJpeg", "%s", _( "unknown JFIF resolution unit" ) ); break; } #ifdef DEBUG printf( "read_jpeg_header: seen jfif resolution %g, %g p/mm\n", xres, yres ); #endif /*DEBUG*/ } /* Set VIPS header. */ vips_image_init_fields( out, cinfo->output_width, cinfo->output_height, cinfo->output_components, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, interpretation, xres, yres ); vips_image_pipelinev( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL ); /* Interlaced jpegs need lots of memory to read, so our caller needs * to know. */ (void) vips_image_set_int( out, "jpeg-multiscan", jpeg_has_multiple_scans( cinfo ) ); /* Look for EXIF and ICC profile. */ for( p = cinfo->marker_list; p; p = p->next ) { #ifdef DEBUG { printf( "read_jpeg_header: seen %d bytes of APP%d\n", p->data_length, p->marker - JPEG_APP0 ); for( i = 0; i < 10; i++ ) printf( "\t%d) '%c' (%d)\n", i, p->data[i], p->data[i] ); } #endif /*DEBUG*/ switch( p->marker ) { case JPEG_APP0 + 1: /* Possible EXIF or XMP data. */ if( p->data_length > 4 && vips_isprefix( "Exif", (char *) p->data ) ) { if( parse_exif( out, p->data, p->data_length ) || attach_blob( out, VIPS_META_EXIF_NAME, p->data, p->data_length ) ) return( -1 ); } if( p->data_length > 4 && vips_isprefix( "http", (char *) p->data ) && attach_blob( out, VIPS_META_XMP_NAME, p->data, p->data_length ) ) return( -1 ); break; case JPEG_APP0 + 2: /* Possible ICC profile. */ if( p->data_length > 14 && vips_isprefix( "ICC_PROFILE", (char *) p->data ) ) { /* cur_marker numbers from 1, according to * spec. */ int cur_marker = p->data[12] - 1; if( cur_marker >= 0 && cur_marker < MAX_APP2_SECTIONS ) { app2_data[cur_marker] = p->data + 14; app2_data_length[cur_marker] = p->data_length - 14; } } break; case JPEG_APP0 + 13: /* Possible IPCT data block. */ if( p->data_length > 5 && vips_isprefix( "Photo", (char *) p->data ) && attach_blob( out, VIPS_META_IPCT_NAME, p->data, p->data_length ) ) return( -1 ); break; default: break; } } /* Assemble ICC sections. */ data_length = 0; for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ ) data_length += app2_data_length[i]; if( data_length ) { unsigned char *data; int p; #ifdef DEBUG printf( "read_jpeg_header: assembled %zd byte ICC profile\n", data_length ); #endif /*DEBUG*/ if( !(data = vips_malloc( NULL, data_length )) ) return( -1 ); p = 0; for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ ) { memcpy( data + p, app2_data[i], app2_data_length[i] ); p += app2_data_length[i]; } vips_image_set_blob( out, VIPS_META_ICC_NAME, (VipsCallbackFn) vips_free, data, data_length ); } return( 0 ); }
static int parse_header( Read *read ) { VipsImage *im = read->im; Image *image = read->image; Image *p; int i; #ifdef DEBUG printf( "parse_header: filename = %s\n", read->filename ); printf( "GetImageChannelDepth(AllChannels) = %zd\n", GetImageChannelDepth( image, AllChannels, &image->exception ) ); printf( "GetImageDepth() = %zd\n", GetImageDepth( image, &image->exception ) ); printf( "image->depth = %zd\n", image->depth ); printf( "GetImageType() = %d\n", GetImageType( image, &image->exception ) ); printf( "IsGrayImage() = %d\n", IsGrayImage( image, &image->exception ) ); printf( "IsMonochromeImage() = %d\n", IsMonochromeImage( image, &image->exception ) ); printf( "IsOpaqueImage() = %d\n", IsOpaqueImage( image, &image->exception ) ); printf( "image->columns = %zd\n", image->columns ); printf( "image->rows = %zd\n", image->rows ); #endif /*DEBUG*/ im->Xsize = image->columns; im->Ysize = image->rows; read->frame_height = image->rows; if( (im->Bands = get_bands( image )) < 0 ) return( -1 ); /* Depth can be 'fractional'. You'd think we should use * GetImageDepth() but that seems unreliable. 16-bit mono DICOM images * are reported as depth 1, for example. */ im->BandFmt = -1; if( image->depth >= 1 && image->depth <= 8 ) im->BandFmt = VIPS_FORMAT_UCHAR; if( image->depth >= 9 && image->depth <= 16 ) im->BandFmt = VIPS_FORMAT_USHORT; #ifdef UseHDRI if( image->depth == 32 ) im->BandFmt = VIPS_FORMAT_FLOAT; if( image->depth == 64 ) im->BandFmt = VIPS_FORMAT_DOUBLE; #else /*!UseHDRI*/ if( image->depth == 32 ) im->BandFmt = VIPS_FORMAT_UINT; #endif /*UseHDRI*/ if( im->BandFmt == -1 ) { vips_error( "magick2vips", _( "unsupported bit depth %d" ), (int) image->depth ); return( -1 ); } switch( image->colorspace ) { case GRAYColorspace: if( im->BandFmt == VIPS_FORMAT_USHORT ) im->Type = VIPS_INTERPRETATION_GREY16; else im->Type = VIPS_INTERPRETATION_B_W; break; case RGBColorspace: if( im->BandFmt == VIPS_FORMAT_USHORT ) im->Type = VIPS_INTERPRETATION_RGB16; else im->Type = VIPS_INTERPRETATION_RGB; break; case sRGBColorspace: if( im->BandFmt == VIPS_FORMAT_USHORT ) im->Type = VIPS_INTERPRETATION_RGB16; else im->Type = VIPS_INTERPRETATION_sRGB; break; case CMYKColorspace: im->Type = VIPS_INTERPRETATION_CMYK; break; default: vips_error( "magick2vips", _( "unsupported colorspace %d" ), (int) image->colorspace ); return( -1 ); } switch( image->units ) { case PixelsPerInchResolution: im->Xres = image->x_resolution / 25.4; im->Yres = image->y_resolution / 25.4; break; case PixelsPerCentimeterResolution: im->Xres = image->x_resolution / 10.0; im->Yres = image->y_resolution / 10.0; break; default: im->Xres = 1.0; im->Yres = 1.0; break; } /* Other fields. */ im->Coding = VIPS_CODING_NONE; vips_image_pipelinev( im, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); /* Three ways to loop over attributes / properties :-( */ #ifdef HAVE_RESETIMAGEPROPERTYITERATOR { char *key; /* This is the most recent imagemagick API, test for this first. */ ResetImagePropertyIterator( image ); while( (key = GetNextImageProperty( image )) ) { char name_text[256]; VipsBuf name = VIPS_BUF_STATIC( name_text ); vips_buf_appendf( &name, "magick-%s", key ); vips_image_set_string( im, vips_buf_all( &name ), GetImageProperty( image, key ) ); } } #elif defined(HAVE_RESETIMAGEATTRIBUTEITERATOR) { const ImageAttribute *attr; /* magick6.1-ish and later, deprecated in 6.5ish. */ ResetImageAttributeIterator( image ); while( (attr = GetNextImageAttribute( image )) ) { char name_text[256]; VipsBuf name = VIPS_BUF_STATIC( name_text ); vips_buf_appendf( &name, "magick-%s", attr->key ); vips_image_set_string( im, vips_buf_all( &name ), attr->value ); } } #else { const ImageAttribute *attr; /* GraphicsMagick is missing the iterator: we have to loop ourselves. * ->attributes is marked as private in the header, but there's no * getter so we have to access it directly. */ for( attr = image->attributes; attr; attr = attr->next ) { char name_text[256]; VipsBuf name = VIPS_BUF_STATIC( name_text ); vips_buf_appendf( &name, "magick-%s", attr->key ); vips_image_set_string( im, vips_buf_all( &name ), attr->value ); } } #endif /* Do we have a set of equal-sized frames? Append them. FIXME ... there must be an attribute somewhere from dicom read which says this is a volumetric image */ read->n_frames = 0; for( p = image; p; (p = GetNextImageInList( p )) ) { if( p->columns != (unsigned int) im->Xsize || p->rows != (unsigned int) im->Ysize || get_bands( p ) != im->Bands ) break; read->n_frames += 1; } if( p ) /* Nope ... just do the first image in the list. */ read->n_frames = 1; #ifdef DEBUG printf( "image has %d frames\n", read->n_frames ); #endif /*DEBUG*/ /* If all_frames is off, just get the first one. */ if( !read->all_frames ) read->n_frames = 1; /* Record frame pointers. */ im->Ysize *= read->n_frames; if( !(read->frames = VIPS_ARRAY( NULL, read->n_frames, Image * )) ) return( -1 ); p = image; for( i = 0; i < read->n_frames; i++ ) { read->frames[i] = p; p = GetNextImageInList( p ); } return( 0 ); }
static ReadSlide * readslide_new( const char *filename, VipsImage *out, int level, gboolean autocrop, const char *associated ) { ReadSlide *rslide; int64_t w, h; const char *error; const char *background; const char * const *properties; char *associated_names; if( level && associated ) { vips_error( "openslide2vips", "%s", _( "specify only one of level or associated " "image" ) ); return( NULL ); } rslide = VIPS_NEW( out, ReadSlide ); memset( rslide, 0, sizeof( *rslide ) ); g_signal_connect( out, "close", G_CALLBACK( readslide_destroy_cb ), rslide ); rslide->level = level; rslide->autocrop = autocrop; rslide->associated = g_strdup( associated ); /* Non-crazy defaults, override below if we can. */ rslide->tile_width = 256; rslide->tile_height = 256; rslide->osr = openslide_open( filename ); if( rslide->osr == NULL ) { vips_error( "openslide2vips", "%s", _( "unsupported slide format" ) ); return( NULL ); } error = openslide_get_error( rslide->osr ); if( error ) { vips_error( "openslide2vips", _( "opening slide: %s" ), error ); return( NULL ); } if( level < 0 || level >= openslide_get_level_count( rslide->osr ) ) { vips_error( "openslide2vips", "%s", _( "invalid slide level" ) ); return( NULL ); } if( associated && check_associated_image( rslide->osr, associated ) ) return( NULL ); if( associated ) { openslide_get_associated_image_dimensions( rslide->osr, associated, &w, &h ); vips_image_set_string( out, "slide-associated-image", associated ); vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); } else { char buf[256]; const char *value; openslide_get_level_dimensions( rslide->osr, level, &w, &h ); rslide->downsample = openslide_get_level_downsample( rslide->osr, level ); vips_image_set_int( out, "slide-level", level ); vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); /* Try to get tile width/height. An undocumented, experimental * feature. */ vips_snprintf( buf, 256, "openslide.level[%d].tile-width", level ); if( (value = openslide_get_property_value( rslide->osr, buf )) ) rslide->tile_width = atoi( value ); vips_snprintf( buf, 256, "openslide.level[%d].tile-height", level ); if( (value = openslide_get_property_value( rslide->osr, buf )) ) rslide->tile_height = atoi( value ); if( value ) VIPS_DEBUG_MSG( "readslide_new: found tile-size\n" ); /* Some images have a bounds in the header. Crop to * that if autocrop is set. */ if( rslide->autocrop ) if( !get_bounds( rslide->osr, &rslide->bounds ) ) rslide->autocrop = FALSE; if( rslide->autocrop ) { VipsRect image; rslide->bounds.left /= rslide->downsample; rslide->bounds.top /= rslide->downsample; rslide->bounds.width /= rslide->downsample; rslide->bounds.height /= rslide->downsample; /* Clip against image size. */ image.left = 0; image.top = 0; image.width = w; image.height = h; vips_rect_intersectrect( &rslide->bounds, &image, &rslide->bounds ); /* If we've clipped to nothing, ignore bounds. */ if( vips_rect_isempty( &rslide->bounds ) ) rslide->autocrop = FALSE; } if( rslide->autocrop ) { w = rslide->bounds.width; h = rslide->bounds.height; } } rslide->bg = 0xffffff; if( (background = openslide_get_property_value( rslide->osr, OPENSLIDE_PROPERTY_NAME_BACKGROUND_COLOR )) ) rslide->bg = strtoul( background, NULL, 16 ); if( w <= 0 || h <= 0 || rslide->downsample < 0 ) { vips_error( "openslide2vips", _( "getting dimensions: %s" ), openslide_get_error( rslide->osr ) ); return( NULL ); } if( w > INT_MAX || h > INT_MAX ) { vips_error( "openslide2vips", "%s", _( "image dimensions overflow int" ) ); return( NULL ); } if( !rslide->autocrop ) { rslide->bounds.left = 0; rslide->bounds.top = 0; rslide->bounds.width = w; rslide->bounds.height = h; } vips_image_init_fields( out, w, h, 4, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_RGB, 1.0, 1.0 ); for( properties = openslide_get_property_names( rslide->osr ); *properties != NULL; properties++ ) vips_image_set_string( out, *properties, openslide_get_property_value( rslide->osr, *properties ) ); associated_names = g_strjoinv( ", ", (char **) openslide_get_associated_image_names( rslide->osr ) ); vips_image_set_string( out, "slide-associated-images", associated_names ); VIPS_FREE( associated_names ); return( rslide ); }
/* Read a png header. */ static int png2vips_header( Read *read, VipsImage *out ) { png_uint_32 width, height; int bit_depth, color_type; int interlace_type; png_uint_32 res_x, res_y; int unit_type; png_charp name; int compression_type; /* Well thank you, libpng. */ #if PNG_LIBPNG_VER < 10400 png_charp profile; #else png_bytep profile; #endif png_uint_32 proflen; int bands; VipsInterpretation interpretation; double Xres, Yres; if( setjmp( png_jmpbuf( read->pPng ) ) ) return( -1 ); png_get_IHDR( read->pPng, read->pInfo, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL ); /* png_get_channels() gives us 1 band for palette images ... so look * at colour_type for output bands. * * Ignore alpha, we detect that separately below. */ switch( color_type ) { case PNG_COLOR_TYPE_PALETTE: bands = 3; break; case PNG_COLOR_TYPE_GRAY_ALPHA: case PNG_COLOR_TYPE_GRAY: bands = 1; break; case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: bands = 3; break; default: vips_error( "png2vips", "%s", _( "unsupported color type" ) ); return( -1 ); } if( bit_depth > 8 ) { if( bands < 3 ) interpretation = VIPS_INTERPRETATION_GREY16; else interpretation = VIPS_INTERPRETATION_RGB16; } else { if( bands < 3 ) interpretation = VIPS_INTERPRETATION_B_W; else interpretation = VIPS_INTERPRETATION_sRGB; } /* Expand palette images. */ if( color_type == PNG_COLOR_TYPE_PALETTE ) png_set_palette_to_rgb( read->pPng ); /* Expand transparency. */ if( png_get_valid( read->pPng, read->pInfo, PNG_INFO_tRNS ) ) { png_set_tRNS_to_alpha( read->pPng ); bands += 1; } else if( color_type == PNG_COLOR_TYPE_GRAY_ALPHA || color_type == PNG_COLOR_TYPE_RGB_ALPHA ) { /* Some images have no transparency chunk, but still set * color_type to alpha. */ bands += 1; } /* Expand <8 bit images to full bytes. */ if( color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8 ) png_set_expand_gray_1_2_4_to_8( read->pPng ); /* If we're an INTEL byte order machine and this is 16bits, we need * to swap bytes. */ if( bit_depth > 8 && !vips_amiMSBfirst() ) png_set_swap( read->pPng ); /* Get resolution. Default to 72 pixels per inch, the usual png value. */ unit_type = PNG_RESOLUTION_METER; res_x = (72 / 2.54 * 100); res_y = (72 / 2.54 * 100); png_get_pHYs( read->pPng, read->pInfo, &res_x, &res_y, &unit_type ); switch( unit_type ) { case PNG_RESOLUTION_METER: Xres = res_x / 1000.0; Yres = res_y / 1000.0; break; default: Xres = res_x; Yres = res_y; break; } /* Set VIPS header. */ vips_image_init_fields( out, width, height, bands, bit_depth > 8 ? VIPS_FORMAT_USHORT : VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, interpretation, Xres, Yres ); /* Sequential mode needs thinstrip to work with things like * vips_shrink(). */ vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); /* Fetch the ICC profile. @name is useless, something like "icc" or * "ICC Profile" etc. Ignore it. * * @profile was png_charpp in libpngs < 1.5, png_bytepp is the * modern one. Ignore the warning, if any. */ if( png_get_iCCP( read->pPng, read->pInfo, &name, &compression_type, &profile, &proflen ) ) { void *profile_copy; #ifdef DEBUG printf( "png2vips_header: attaching %zd bytes of ICC profile\n", proflen ); printf( "png2vips_header: name = \"%s\"\n", name ); #endif /*DEBUG*/ if( !(profile_copy = vips_malloc( NULL, proflen )) ) return( -1 ); memcpy( profile_copy, profile, proflen ); vips_image_set_blob( out, VIPS_META_ICC_NAME, (VipsCallbackFn) vips_free, profile_copy, proflen ); } /* Sanity-check line size. */ png_read_update_info( read->pPng, read->pInfo ); if( png_get_rowbytes( read->pPng, read->pInfo ) != VIPS_IMAGE_SIZEOF_LINE( out ) ) { vips_error( "vipspng", "%s", _( "unable to read PNG header" ) ); return( -1 ); } return( 0 ); }
static int read_csv( FILE *fp, VipsImage *out, int skip, int lines, const char *whitespace, const char *separator, gboolean read_image ) { int i; char whitemap[256]; char sepmap[256]; const char *p; fpos_t pos; int columns; int ch; double d; double *buf; int y; /* Make our char maps. */ for( i = 0; i < 256; i++ ) { whitemap[i] = 0; sepmap[i] = 0; } for( p = whitespace; *p; p++ ) whitemap[(int) *p] = 1; for( p = separator; *p; p++ ) sepmap[(int) *p] = 1; /* Skip first few lines. */ for( i = 0; i < skip; i++ ) if( !skip_line( fp ) ) { vips_error( "csv2vips", "%s", _( "end of file while skipping start" ) ); return( -1 ); } /* Parse the first line to get number of columns. Only bother checking * fgetpos() the first time we use it: assume it's working after this. */ if( fgetpos( fp, &pos ) ) { vips_error_system( errno, "csv2vips", "%s", _( "unable to seek" ) ); return( -1 ); } for( columns = 0; (ch = read_double( fp, whitemap, sepmap, skip + 1, columns + 1, &d )) == 0; columns++ ) ; (void) fsetpos( fp, &pos ); if( columns == 0 ) { vips_error( "csv2vips", "%s", _( "empty line" ) ); return( -1 ); } /* If lines is -1, we have to scan the whole file to get the * number of lines out. */ if( lines == -1 ) { (void) fgetpos( fp, &pos ); for( lines = 0; skip_line( fp ); lines++ ) ; (void) fsetpos( fp, &pos ); } vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); vips_image_init_fields( out, columns, lines, 1, VIPS_FORMAT_DOUBLE, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 ); /* Just reading the header? We are done. */ if( !read_image ) return( 0 ); if( !(buf = VIPS_ARRAY( out, VIPS_IMAGE_N_ELEMENTS( out ), double )) ) return( -1 ); for( y = 0; y < lines; y++ ) { int x; for( x = 0; x < columns; x++ ) { int lineno = y + skip + 1; int colno = x + 1; ch = read_double( fp, whitemap, sepmap, lineno, colno, &d ); if( ch == EOF ) { vips_error( "csv2vips", _( "unexpected EOF, line %d col %d" ), lineno, colno ); return( -1 ); } else if( ch == '\n' ) { vips_error( "csv2vips", _( "unexpected EOL, line %d col %d" ), lineno, colno ); return( -1 ); } else if( ch ) /* Parse error. */ return( -1 ); buf[x] = d; } if( vips_image_write_line( out, y, (VipsPel *) buf ) ) return( -1 ); /* Skip over the '\n' to the next line. */ skip_line( fp ); } return( 0 ); }
/* Build a pyramid. * * width/height is the size of this layer, real_* the subsection of the layer * which is real pixels (as opposed to background). */ static Layer * pyramid_build( VipsForeignSaveDz *dz, Layer *above, int width, int height, VipsRect *real_pixels ) { VipsForeignSave *save = VIPS_FOREIGN_SAVE( dz ); Layer *layer = VIPS_NEW( dz, Layer ); VipsRect strip; int limit; layer->dz = dz; layer->width = width; layer->height = height; layer->tiles_across = ROUND_UP( width, dz->tile_size ) / dz->tile_size; layer->tiles_down = ROUND_UP( height, dz->tile_size ) / dz->tile_size; layer->real_pixels = *real_pixels; layer->image = NULL; layer->strip = NULL; layer->copy = NULL; if( !above ) /* Top of pyramid. */ layer->sub = 1; else layer->sub = above->sub * 2; layer->below = NULL; layer->above = above; /* We round the image size up to an even number to make x2 shrink * easy. */ layer->image = vips_image_new(); if( vips_image_pipelinev( layer->image, VIPS_DEMAND_STYLE_ANY, save->ready, NULL ) ) { layer_free( layer ); return( NULL ); } layer->image->Xsize = width + (width & 1); layer->image->Ysize = height + (height & 1); layer->strip = vips_region_new( layer->image ); layer->copy = vips_region_new( layer->image ); /* The regions will get used in the bg thread callback, so make sure * we don't own them. */ vips__region_no_ownership( layer->strip ); vips__region_no_ownership( layer->copy ); /* Build a line of tiles here. Normally strips are height + 2 * * overlap, but the first row is missing the top edge. * * Expand the strip if necessary to make sure we have an even * number of lines. */ layer->y = 0; layer->write_y = 0; strip.left = 0; strip.top = 0; strip.width = layer->image->Xsize; strip.height = dz->tile_size + dz->overlap; if( (strip.height & 1) == 1 ) strip.height += 1; if( vips_region_buffer( layer->strip, &strip ) ) { layer_free( layer ); return( NULL ); } switch( dz->depth ) { case VIPS_FOREIGN_DZ_DEPTH_ONEPIXEL: limit = 1; break; case VIPS_FOREIGN_DZ_DEPTH_ONETILE: limit = dz->tile_size; break; case VIPS_FOREIGN_DZ_DEPTH_ONE: limit = VIPS_MAX( width, height ); break; default: g_assert( 0 ); limit = dz->tile_size; break; } if( width > limit || height > limit ) { /* Round up, so eg. a 5 pixel wide image becomes 3 a layer * down. * * For the rect, round left/top down, round bottom/right up, * so we get all possible pixels. */ VipsRect halfrect; halfrect.left = real_pixels->left / 2; halfrect.top = real_pixels->top / 2; halfrect.width = (VIPS_RECT_RIGHT( real_pixels ) + 1) / 2 - halfrect.left; halfrect.height = (VIPS_RECT_BOTTOM( real_pixels ) + 1) / 2 - halfrect.top; if( !(layer->below = pyramid_build( dz, layer, (width + 1) / 2, (height + 1) / 2, &halfrect )) ) { layer_free( layer ); return( NULL ); } layer->n = layer->below->n + 1; } else layer->n = 0; #ifdef DEBUG printf( "pyramid_build:\n" ); printf( "\tn = %d\n", layer->n ); printf( "\twidth = %d, height = %d\n", width, height ); printf( "\tXsize = %d, Ysize = %d\n", layer->image->Xsize, layer->image->Ysize ); printf( "\treal_pixels.left = %d, real_pixels.top = %d\n", real_pixels->left, real_pixels->top ); printf( "\treal_pixels.width = %d, real_pixels.height = %d\n", real_pixels->width, real_pixels->height ); #endif return( layer ); }