From 11832a6f1ddb17dafdbd7c8d1b45d69c54d53efc Mon Sep 17 00:00:00 2001 From: Sven Neumann Date: Mon, 18 May 1998 00:54:11 +0000 Subject: [PATCH] Updated tiff plug-in. From Nicolas Lamb: This is a total replacement for the TIFF loading code, probably 50% new code and the rest rewritten. It is faster (for boring images, such as RGB and 8-bit Grayscale) and more stable. --Sven --- ChangeLog | 4 + plug-ins/common/tiff.c | 619 +++++++++++++++++------------------------ plug-ins/tiff/tiff.c | 619 +++++++++++++++++------------------------ 3 files changed, 516 insertions(+), 726 deletions(-) diff --git a/ChangeLog b/ChangeLog index b7cf96908e..e9df414d96 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Mon May 18 02:50:03 MEST 1998 Sven Neumann + + * plug-ins/tiff/tiff.c: updated tiff plug-in + Sun May 17 17:31:05 PDT 1998 Manish Singh * plug-ins/maze: updated maze plugin diff --git a/plug-ins/common/tiff.c b/plug-ins/common/tiff.c index abe72d2262..af4a02e0dd 100644 --- a/plug-ins/common/tiff.c +++ b/plug-ins/common/tiff.c @@ -1,7 +1,7 @@ /* tiff loading and saving for the GIMP * -Peter Mattis - * various fixes along the route to GIMP 1.0 - * -Nick Lamb and others (list yourselves here people) + * The TIFF loading code has been completely revamped by Nick Lamb + * njl195@zepler.org.uk -- 18 May 1998 * * The code for this filter is based on "tifftopnm" and "pnmtotiff", * 2 programs that are a part of the netpbm package. @@ -57,8 +57,8 @@ static void run (char *name, GParam **return_vals); static gint32 load_image (char *filename); static gint save_image (char *filename, - gint32 image_ID, - gint32 drawable_ID); + gint32 image, + gint32 drawable); static gint save_dialog (); @@ -123,9 +123,9 @@ query () gimp_install_procedure ("file_tiff_load", "loads files of the tiff file format", "FIXME: write help for tiff_load", - "Spencer Kimball & Peter Mattis", - "Spencer Kimball & Peter Mattis", - "1995-1996", + "Spencer Kimball, Peter Mattis & Nick Lamb", + "Nick Lamb ", + "1995-1996,1998", "/Tiff", NULL, PROC_PLUG_IN, @@ -139,7 +139,7 @@ query () "Spencer Kimball & Peter Mattis", "1995-1996", "/Tiff", - "RGB*, GRAY*", + "RGB*, GRAY*, INDEXED", PROC_PLUG_IN, nsave_args, 0, save_args, NULL); @@ -159,7 +159,7 @@ run (char *name, static GParam values[2]; GRunModeType run_mode; GStatusType status = STATUS_SUCCESS; - gint32 image_ID; + gint32 image; run_mode = param[0].data.d_int32; @@ -170,14 +170,14 @@ run (char *name, if (strcmp (name, "file_tiff_load") == 0) { - image_ID = load_image (param[1].data.d_string); + image = load_image (param[1].data.d_string); - if (image_ID != -1) + if (image != -1) { *nreturn_vals = 2; values[0].data.d_status = STATUS_SUCCESS; values[1].type = PARAM_IMAGE; - values[1].data.d_image = image_ID; + values[1].data.d_image = image; } else { @@ -240,410 +240,307 @@ run (char *name, } } -static gint32 -load_image (char *filename) -{ +static gint32 load_image (char *filename) { TIFF *tif; - int col; + unsigned short bps, spp, photomet; + int cols, rows, maxval, alpha; + int image, layer, tile_height; + unsigned short *redmap, *greenmap, *bluemap; + guchar cmap[768]; + int image_type= 0, layer_type= 0; + unsigned short *extra_types, extra = 0; + + int col, row, start, i, j; unsigned char sample; int bitsleft; - int cols, rows, grayscale; - int alpha; int gray_val, red_val, green_val, blue_val, alpha_val; - int numcolors; - int row, i; - guchar *buf, *s; - guchar *dest, *d; - int maxval; - int image_type; - int layer_type; - unsigned short bps, spp, photomet; - unsigned short *redcolormap; - unsigned short *greencolormap; - unsigned short *bluecolormap; - unsigned short k, num_extra = 0; - unsigned short *extra_samples; - int image_ID; - int layer_ID; + + guchar *source, *s, *dest, *d; GDrawable *drawable; GPixelRgn pixel_rgn; - int tile_height; - int y, yend; char *name; - typedef struct { - gint32 ID; - GDrawable *drawable; - GPixelRgn pixel_rgn; - guchar *pixels, *pixel; - } channel_data; - - channel_data *channel; - guchar colors[3]= {0, 0, 0}; - tif = TIFFOpen (filename, "r"); - if (!tif) - { - g_warning ("can't open \"%s\"\n", filename); - gimp_quit (); - } + typedef struct { + gint32 ID; + GDrawable *drawable; + GPixelRgn pixel_rgn; + guchar *pixels; + guchar *pixel; + } channel_data; - name = malloc (strlen (filename) + 12); + channel_data *channel= NULL; + + tif = TIFFOpen (filename, "r"); + if (!tif) { + g_error ("TIFF Can't open \"%s\"\n", filename); + gimp_quit (); + } + + name = g_malloc (strlen (filename) + 12); sprintf (name, "Loading %s:", filename); gimp_progress_init (name); - free (name); + g_free (name); if (!TIFFGetField (tif, TIFFTAG_BITSPERSAMPLE, &bps)) bps = 1; + + if (bps > 8) { + g_error("TIFF Can't handle samples wider than 8-bit\n"); + gimp_quit(); + } + if (!TIFFGetField (tif, TIFFTAG_SAMPLESPERPIXEL, &spp)) spp = 1; - if (!TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &num_extra, &extra_samples)) - alpha = 0; - if (!TIFFGetField (tif, TIFFTAG_PHOTOMETRIC, &photomet)) - { - g_warning ("error getting photometric\n"); - gimp_quit (); - } + if (!TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &extra, &extra_types)) + extra = 0; + + if (!TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &cols)) { + g_error ("TIFF Can't get image width\n"); + gimp_quit (); + } + + if (!TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &rows)) { + g_error ("TIFF Can't get image length\n"); + gimp_quit (); + } + + if (!TIFFGetField (tif, TIFFTAG_PHOTOMETRIC, &photomet)) { + g_error ("TIFF Can't get photometric\n"); + gimp_quit (); + } /* test if the extrasample represents an associated alpha channel... */ - if (num_extra > 0 && (extra_samples [0] == EXTRASAMPLE_ASSOCALPHA)) + if (extra > 0 && (extra_types[0] == EXTRASAMPLE_ASSOCALPHA)) { alpha = 1; - else + } else { alpha = 0; + } - if (spp > 3) alpha = 1; /* Kludge - like all the rest of this -- njl195 */ - - TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &cols); - TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &rows); + /* some programs seem to think alpha etc. aren't "extra" samples (?) */ + if (spp > 3) { + extra= spp - 3; + } maxval = (1 << bps) - 1; - if (maxval == 1 && spp == 1) - { - grayscale = 1; + + switch (photomet) { + case PHOTOMETRIC_MINISBLACK: + case PHOTOMETRIC_MINISWHITE: image_type = GRAY; layer_type = (alpha) ? GRAYA_IMAGE : GRAY_IMAGE; - } - else - { - switch (photomet) - { - case PHOTOMETRIC_MINISBLACK: - grayscale = 1; - image_type = GRAY; - layer_type = (alpha) ? GRAYA_IMAGE : GRAY_IMAGE; - break; + break; - case PHOTOMETRIC_MINISWHITE: - grayscale = 1; - image_type = GRAY; - layer_type = (alpha) ? GRAYA_IMAGE : GRAY_IMAGE; - break; + case PHOTOMETRIC_RGB: + image_type = RGB; + layer_type = (alpha) ? RGBA_IMAGE : RGB_IMAGE; + break; - case PHOTOMETRIC_PALETTE: - if (alpha) - g_print ("ignoring alpha channel on indexed color image\n"); + case PHOTOMETRIC_PALETTE: + image_type = INDEXED; + layer_type = (alpha) ? INDEXEDA_IMAGE : INDEXED_IMAGE; + break; - if (!TIFFGetField (tif, TIFFTAG_COLORMAP, &redcolormap, - &greencolormap, &bluecolormap)) - { - g_print ("error getting colormaps\n"); - gimp_quit (); - } - numcolors = maxval + 1; - maxval = 255; - grayscale = 0; + case PHOTOMETRIC_MASK: + g_error ("TIFF Can't handle PHOTOMETRIC_MASK\n"); + gimp_quit (); + break; + default: + g_error ("TIFF Unknown photometric: %d\n", photomet); + gimp_quit (); + } - for (i = 0; i < numcolors; i++) - { - redcolormap[i] >>= 8; - greencolormap[i] >>= 8; - bluecolormap[i] >>= 8; - } + if ((image = gimp_image_new (cols, rows, image_type)) == -1) { + g_error ("TIFF Can't allocate new image\n"); + gimp_quit (); + } + gimp_image_set_filename (image, filename); - if (numcolors > 256) - { - image_type = RGB; - layer_type = (alpha) ? RGBA_IMAGE : RGB_IMAGE; - } - else - { - image_type = INDEXED; - layer_type = (alpha) ? INDEXEDA_IMAGE : INDEXED_IMAGE; - } - break; - - case PHOTOMETRIC_RGB: - grayscale = 0; - image_type = RGB; - layer_type = (alpha) ? RGBA_IMAGE : RGB_IMAGE; - break; - - case PHOTOMETRIC_MASK: - g_print ("don't know how to handle PHOTOMETRIC_MASK\n"); - gimp_quit (); - - default: - g_print ("unknown photometric: %d\n", photomet); - gimp_quit (); - } - } - - - image_ID = gimp_image_new (cols, rows, image_type); - if (image_ID == -1) - { - g_print ("can't allocate new image\n"); + /* Install colormap for INDEXED images only */ + if (image_type == INDEXED) { + if (!TIFFGetField (tif, TIFFTAG_COLORMAP, &redmap, &greenmap, &bluemap)) { + g_error ("TIFF Can't get colormaps\n"); gimp_quit (); } - gimp_image_set_filename (image_ID, filename); - if ((image_type == INDEXED) && (numcolors <= 256)) - { - guchar *cmap; - gint i, j; - - cmap = g_new (guchar, numcolors * 3); - for (i = 0, j = 0; i < numcolors; i++) - { - cmap[j++] = redcolormap[i]; - cmap[j++] = greencolormap[i]; - cmap[j++] = bluecolormap[i]; - } - - gimp_image_set_cmap (image_ID, cmap, numcolors); - g_free (cmap); + for (i = 0, j = 0; i <= maxval; i++) { + cmap[j++] = redmap[i] >> 8; + cmap[j++] = greenmap[i] >> 8; + cmap[j++] = bluemap[i] >> 8; } + gimp_image_set_cmap (image, cmap, maxval + 1); + } - layer_ID = gimp_layer_new (image_ID, "Background", - cols, rows, layer_type, + layer = gimp_layer_new (image, "Background", cols, rows, layer_type, 100, NORMAL_MODE); - gimp_image_add_layer (image_ID, layer_ID, 0); - - buf = g_new (guchar, TIFFScanlineSize (tif)); - - drawable = gimp_drawable_get (layer_ID); + gimp_image_add_layer (image, layer, 0); + drawable = gimp_drawable_get (layer); + source= g_new (guchar, TIFFScanlineSize (tif)); tile_height = gimp_tile_height (); dest = g_new (guchar, tile_height * cols * drawable->bpp); gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, cols, rows, TRUE, FALSE); - if (num_extra - alpha > 0) - channel = g_new (channel_data, num_extra - alpha); + if (extra - alpha > 0) + channel = g_new (channel_data, extra - alpha); - for (k= 0; alpha + k < num_extra; ++k) - { - channel[k].ID= gimp_channel_new(image_ID, "TIFF Channel", cols, rows, - 100.0, colors); - gimp_image_add_channel(image_ID, channel[k].ID, 0); - channel[k].drawable= gimp_drawable_get (channel[k].ID); - channel[k].pixels= g_new(guchar, tile_height * cols); + /* Add alpha channels as appropriate */ + for (i= 0; alpha + i < extra; ++i) { + channel[i].ID= gimp_channel_new(image, "TIFF Channel", cols, rows, + 100.0, colors); + gimp_image_add_channel(image, channel[i].ID, 0); + channel[i].drawable= gimp_drawable_get (channel[i].ID); + channel[i].pixels= g_new(guchar, tile_height * cols); - gimp_pixel_rgn_init (&(channel[k].pixel_rgn), channel[k].drawable, 0, 0, + gimp_pixel_rgn_init (&(channel[i].pixel_rgn), channel[i].drawable, 0, 0, cols, rows, TRUE, FALSE); - } + } -#define NEXTSAMPLE \ +/* Step through all <= 8-bit samples in an image */ + +#define NEXTSAMPLE(var) \ { \ - if ( bitsleft == 0 ) \ + if (bitsleft == 0) \ { \ s++; \ bitsleft = 8; \ } \ bitsleft -= bps; \ - sample = ( *s >> bitsleft ) & maxval; \ + var = ( *s >> bitsleft ) & maxval; \ } - for (y = 0; y < rows; y = yend) - { - yend = y + tile_height; - yend = MIN (yend, rows); + for (start= 0, row = 0; row < rows; ++row) { + d= dest + cols * (row % tile_height) * drawable->bpp; - for (row = y; row < yend; row++) - { - if (TIFFReadScanline (tif, buf, row, 0) < 0) - { - g_print ("bad data read on line %d\n", row); - gimp_quit (); + /* Special cases: Scanline is compatible with GIMP storage */ + if (extra == 0 && bps == 8) { + if (TIFFReadScanline (tif, d, row, 0) < 0) { + g_error ("TIFF Bad data read on line %d\n", row); + gimp_quit (); + } + + /* Or read in and process each sample -- slower */ + } else { + if (TIFFReadScanline (tif, source, row, 0) < 0) { + g_error ("TIFF Bad data read on line %d\n", row); + gimp_quit (); + } + + for (i= 0; alpha + i < extra; ++i) { + channel[i].pixel= channel[i].pixels + cols * (row % tile_height); + } + + /* Set s/bitleft ready to use NEXTSAMPLE macro */ + + s= source; + bitsleft= 8; + + switch (photomet) { + case PHOTOMETRIC_MINISBLACK: + for (col = 0; col < cols; col++) { + NEXTSAMPLE(gray_val); + if (alpha) { + NEXTSAMPLE(alpha_val); + if (alpha_val) + *d++ = (gray_val * 65025) / (alpha_val * maxval); + else + *d++ = 0; + *d++ = alpha_val; + } else { + *d++ = (gray_val * 255) / maxval; + } + for (i= 0; alpha + i < extra; ++i) { + NEXTSAMPLE(sample); + *channel[i].pixel++ = sample; + } + } + break; + + case PHOTOMETRIC_MINISWHITE: + for (col = 0; col < cols; col++) { + NEXTSAMPLE(gray_val); + if (alpha) { + NEXTSAMPLE(alpha_val); + if (alpha_val) + *d++ = ((maxval - gray_val) * 65025) / (alpha_val * maxval); + else + *d++ = 0; + *d++ = alpha_val; + } else { + *d++ = ((maxval - gray_val) * 255) / maxval; + } + for (i= 0; alpha + i < extra; ++i) { + NEXTSAMPLE(sample); + *channel[i].pixel++ = sample; + } + } + break; + + case PHOTOMETRIC_PALETTE: + for (col = 0; col < cols; col++) { + NEXTSAMPLE(*d++); + if (alpha) { + NEXTSAMPLE(*d++); + } + for (i= 0; alpha + i < extra; ++i) { + NEXTSAMPLE(sample); + *channel[i].pixel++ = sample; + } + } + break; + + case PHOTOMETRIC_RGB: + for (col = 0; col < cols; col++) { + NEXTSAMPLE(red_val) + NEXTSAMPLE(green_val) + NEXTSAMPLE(blue_val) + if (alpha) { + NEXTSAMPLE(alpha_val) + if (alpha_val) { + *d++ = (red_val * 255) / alpha_val; + *d++ = (green_val * 255) / alpha_val; + *d++ = (blue_val * 255) / alpha_val; + } else { + *d++ = 0; + *d++ = 0; + *d++ = 0; + } + *d++ = alpha_val; + } else { + *d++ = red_val; + *d++ = green_val; + *d++ = blue_val; } + for (i= 0; alpha + i < extra; ++i) { + NEXTSAMPLE(sample); + *channel[i].pixel++ = sample; + } + } + break; - bitsleft = 8; - s = buf; - d = dest + cols * (row - y) * drawable->bpp; - for (k= 0; alpha + k < num_extra; ++k) - channel[k].pixel= channel[k].pixels + cols * (row - y); - - switch (photomet) - { - case PHOTOMETRIC_MINISBLACK: - for (col = 0; col < cols; col++) - { - NEXTSAMPLE; - gray_val = sample; - if (alpha) - { - NEXTSAMPLE; - alpha_val = sample; - if (alpha_val) - *d++ = (gray_val * 65025) / (alpha_val * maxval); - else - *d++ = 0; - *d++ = alpha_val; - } - else - *d++ = (gray_val * 255) / maxval; - for (k= 0; alpha + k < num_extra; ++k) - { - NEXTSAMPLE; - *channel[k].pixel++ = sample; - } - } - break; - - case PHOTOMETRIC_MINISWHITE: - for (col = 0; col < cols; col++) - { - NEXTSAMPLE; - gray_val = sample; - if (alpha) - { - NEXTSAMPLE; - alpha_val = sample; - if (alpha_val) - *d++ = ((maxval - gray_val) * 65025) - / (alpha_val * maxval); - else - *d++ = 0; - *d++ = alpha_val; - } - else - *d++ = ((maxval - gray_val) * 255) / maxval ; - for (k= 0; alpha + k < num_extra; ++k) - { - NEXTSAMPLE; - *channel[k].pixel++ = sample; - } - } - break; - - case PHOTOMETRIC_PALETTE: - if (numcolors > 256) - for (col = 0; col < cols; col++) - { - NEXTSAMPLE; - red_val = redcolormap[sample]; - green_val = greencolormap[sample]; - blue_val = bluecolormap[sample]; - if (alpha) - { - NEXTSAMPLE; - alpha_val = sample; - if (alpha_val) - { - *d++ = (red_val * 255) / alpha_val; - *d++ = (green_val * 255) / alpha_val; - *d++ = (blue_val * 255) / alpha_val; - } - else - { - *d++ = 0; - *d++ = 0; - *d++ = 0; - } - *d++ = alpha_val; - } - else - { - *d++ = red_val; - *d++ = green_val; - *d++ = blue_val; - } - for (k= 0; alpha + k < num_extra; ++k) - { - NEXTSAMPLE; - *channel[k].pixel++ = sample; - } - } - else - for (col = 0; col < cols; col++) - { - NEXTSAMPLE; - if (alpha) - { - *d++ = sample; - NEXTSAMPLE; - *d++ = sample; - } - else - { - *d++ = sample; - } - for (k= 0; alpha + k < num_extra; ++k) - { - NEXTSAMPLE; - *channel[k].pixel++ = sample; - } - } - break; - - case PHOTOMETRIC_RGB: - for (col = 0; col < cols; col++) - { - NEXTSAMPLE; - red_val = sample; - NEXTSAMPLE; - green_val = sample; - NEXTSAMPLE; - blue_val = sample; - if (alpha) - { - NEXTSAMPLE; - alpha_val = sample; - if (alpha_val) - { - *d++ = (red_val * 255) / alpha_val; - *d++ = (green_val * 255) / alpha_val; - *d++ = (blue_val * 255) / alpha_val; - } - else - { - *d++ = 0; - *d++ = 0; - *d++ = 0; - } - *d++ = alpha_val; - } - else - { - *d++ = red_val; - *d++ = green_val; - *d++ = blue_val; - } - for (k= 0; alpha + k < num_extra; ++k) - { - NEXTSAMPLE; - *channel[k].pixel++ = sample; - } - } - break; - - default: - g_print ("unknown photometric: %d\n", photomet); - gimp_quit (); - } - } - - gimp_pixel_rgn_set_rect (&pixel_rgn, dest, 0, y, cols, yend - y); - for (k= 0; alpha + k < num_extra; ++k) - gimp_pixel_rgn_set_rect(&(channel[k].pixel_rgn), - channel[k].pixels, 0, y, cols, yend -y); - gimp_progress_update ((double) row / (double) rows); + default: + /* This case was handled earlier */ + g_assert_not_reached(); + } } + + if (((row + 1) % tile_height) == 0 || row + 1 == rows) { + gimp_pixel_rgn_set_rect (&pixel_rgn, dest, 0, start, cols, 1+row-start); + for (i= 0; alpha + i < extra; ++i) { + gimp_pixel_rgn_set_rect(&(channel[i].pixel_rgn), channel[i].pixels, + 0, start, cols, 1+row-start); + } + gimp_progress_update ((double) row / (double) rows); + start= row + 1; + } + } + gimp_drawable_flush (drawable); gimp_drawable_detach (drawable); - return image_ID; + return image; } /* @@ -669,11 +566,7 @@ load_image (char *filename) ** other special, indirect and consequential damages. */ -static gint -save_image (char *filename, - gint32 image_ID, - gint32 drawable_ID) -{ +static gint save_image (char *filename, gint32 image, gint32 layer) { TIFF *tif; unsigned short red[256]; unsigned short grn[256]; @@ -720,8 +613,8 @@ save_image (char *filename, gimp_progress_init (name); free (name); - drawable = gimp_drawable_get (drawable_ID); - drawable_type = gimp_drawable_type (drawable_ID); + drawable = gimp_drawable_get (layer); + drawable_type = gimp_drawable_type (layer); gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, drawable->width, drawable->height, FALSE, FALSE); cols = drawable->width; @@ -764,7 +657,7 @@ save_image (char *filename, bytesperrow = cols; alpha = 0; - cmap = gimp_image_get_cmap (image_ID, &colors); + cmap = gimp_image_get_cmap (image, &colors); for (i = 0; i < colors; i++) { @@ -798,13 +691,13 @@ save_image (char *filename, TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, photometric); TIFFSetField (tif, TIFFTAG_FILLORDER, fillorder); TIFFSetField (tif, TIFFTAG_DOCUMENTNAME, filename); - TIFFSetField (tif, TIFFTAG_IMAGEDESCRIPTION, "created with The GIMP"); + TIFFSetField (tif, TIFFTAG_IMAGEDESCRIPTION, "Created with The GIMP"); TIFFSetField (tif, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel); TIFFSetField (tif, TIFFTAG_ROWSPERSTRIP, rowsperstrip); /* TIFFSetField( tif, TIFFTAG_STRIPBYTECOUNTS, rows / rowsperstrip ); */ TIFFSetField (tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); - if (gimp_drawable_type (drawable_ID) == INDEXED_IMAGE) + if (drawable_type == INDEXED_IMAGE) TIFFSetField (tif, TIFFTAG_COLORMAP, red, grn, blu); /* array to rearrange data */ diff --git a/plug-ins/tiff/tiff.c b/plug-ins/tiff/tiff.c index abe72d2262..af4a02e0dd 100644 --- a/plug-ins/tiff/tiff.c +++ b/plug-ins/tiff/tiff.c @@ -1,7 +1,7 @@ /* tiff loading and saving for the GIMP * -Peter Mattis - * various fixes along the route to GIMP 1.0 - * -Nick Lamb and others (list yourselves here people) + * The TIFF loading code has been completely revamped by Nick Lamb + * njl195@zepler.org.uk -- 18 May 1998 * * The code for this filter is based on "tifftopnm" and "pnmtotiff", * 2 programs that are a part of the netpbm package. @@ -57,8 +57,8 @@ static void run (char *name, GParam **return_vals); static gint32 load_image (char *filename); static gint save_image (char *filename, - gint32 image_ID, - gint32 drawable_ID); + gint32 image, + gint32 drawable); static gint save_dialog (); @@ -123,9 +123,9 @@ query () gimp_install_procedure ("file_tiff_load", "loads files of the tiff file format", "FIXME: write help for tiff_load", - "Spencer Kimball & Peter Mattis", - "Spencer Kimball & Peter Mattis", - "1995-1996", + "Spencer Kimball, Peter Mattis & Nick Lamb", + "Nick Lamb ", + "1995-1996,1998", "/Tiff", NULL, PROC_PLUG_IN, @@ -139,7 +139,7 @@ query () "Spencer Kimball & Peter Mattis", "1995-1996", "/Tiff", - "RGB*, GRAY*", + "RGB*, GRAY*, INDEXED", PROC_PLUG_IN, nsave_args, 0, save_args, NULL); @@ -159,7 +159,7 @@ run (char *name, static GParam values[2]; GRunModeType run_mode; GStatusType status = STATUS_SUCCESS; - gint32 image_ID; + gint32 image; run_mode = param[0].data.d_int32; @@ -170,14 +170,14 @@ run (char *name, if (strcmp (name, "file_tiff_load") == 0) { - image_ID = load_image (param[1].data.d_string); + image = load_image (param[1].data.d_string); - if (image_ID != -1) + if (image != -1) { *nreturn_vals = 2; values[0].data.d_status = STATUS_SUCCESS; values[1].type = PARAM_IMAGE; - values[1].data.d_image = image_ID; + values[1].data.d_image = image; } else { @@ -240,410 +240,307 @@ run (char *name, } } -static gint32 -load_image (char *filename) -{ +static gint32 load_image (char *filename) { TIFF *tif; - int col; + unsigned short bps, spp, photomet; + int cols, rows, maxval, alpha; + int image, layer, tile_height; + unsigned short *redmap, *greenmap, *bluemap; + guchar cmap[768]; + int image_type= 0, layer_type= 0; + unsigned short *extra_types, extra = 0; + + int col, row, start, i, j; unsigned char sample; int bitsleft; - int cols, rows, grayscale; - int alpha; int gray_val, red_val, green_val, blue_val, alpha_val; - int numcolors; - int row, i; - guchar *buf, *s; - guchar *dest, *d; - int maxval; - int image_type; - int layer_type; - unsigned short bps, spp, photomet; - unsigned short *redcolormap; - unsigned short *greencolormap; - unsigned short *bluecolormap; - unsigned short k, num_extra = 0; - unsigned short *extra_samples; - int image_ID; - int layer_ID; + + guchar *source, *s, *dest, *d; GDrawable *drawable; GPixelRgn pixel_rgn; - int tile_height; - int y, yend; char *name; - typedef struct { - gint32 ID; - GDrawable *drawable; - GPixelRgn pixel_rgn; - guchar *pixels, *pixel; - } channel_data; - - channel_data *channel; - guchar colors[3]= {0, 0, 0}; - tif = TIFFOpen (filename, "r"); - if (!tif) - { - g_warning ("can't open \"%s\"\n", filename); - gimp_quit (); - } + typedef struct { + gint32 ID; + GDrawable *drawable; + GPixelRgn pixel_rgn; + guchar *pixels; + guchar *pixel; + } channel_data; - name = malloc (strlen (filename) + 12); + channel_data *channel= NULL; + + tif = TIFFOpen (filename, "r"); + if (!tif) { + g_error ("TIFF Can't open \"%s\"\n", filename); + gimp_quit (); + } + + name = g_malloc (strlen (filename) + 12); sprintf (name, "Loading %s:", filename); gimp_progress_init (name); - free (name); + g_free (name); if (!TIFFGetField (tif, TIFFTAG_BITSPERSAMPLE, &bps)) bps = 1; + + if (bps > 8) { + g_error("TIFF Can't handle samples wider than 8-bit\n"); + gimp_quit(); + } + if (!TIFFGetField (tif, TIFFTAG_SAMPLESPERPIXEL, &spp)) spp = 1; - if (!TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &num_extra, &extra_samples)) - alpha = 0; - if (!TIFFGetField (tif, TIFFTAG_PHOTOMETRIC, &photomet)) - { - g_warning ("error getting photometric\n"); - gimp_quit (); - } + if (!TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &extra, &extra_types)) + extra = 0; + + if (!TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &cols)) { + g_error ("TIFF Can't get image width\n"); + gimp_quit (); + } + + if (!TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &rows)) { + g_error ("TIFF Can't get image length\n"); + gimp_quit (); + } + + if (!TIFFGetField (tif, TIFFTAG_PHOTOMETRIC, &photomet)) { + g_error ("TIFF Can't get photometric\n"); + gimp_quit (); + } /* test if the extrasample represents an associated alpha channel... */ - if (num_extra > 0 && (extra_samples [0] == EXTRASAMPLE_ASSOCALPHA)) + if (extra > 0 && (extra_types[0] == EXTRASAMPLE_ASSOCALPHA)) { alpha = 1; - else + } else { alpha = 0; + } - if (spp > 3) alpha = 1; /* Kludge - like all the rest of this -- njl195 */ - - TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &cols); - TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &rows); + /* some programs seem to think alpha etc. aren't "extra" samples (?) */ + if (spp > 3) { + extra= spp - 3; + } maxval = (1 << bps) - 1; - if (maxval == 1 && spp == 1) - { - grayscale = 1; + + switch (photomet) { + case PHOTOMETRIC_MINISBLACK: + case PHOTOMETRIC_MINISWHITE: image_type = GRAY; layer_type = (alpha) ? GRAYA_IMAGE : GRAY_IMAGE; - } - else - { - switch (photomet) - { - case PHOTOMETRIC_MINISBLACK: - grayscale = 1; - image_type = GRAY; - layer_type = (alpha) ? GRAYA_IMAGE : GRAY_IMAGE; - break; + break; - case PHOTOMETRIC_MINISWHITE: - grayscale = 1; - image_type = GRAY; - layer_type = (alpha) ? GRAYA_IMAGE : GRAY_IMAGE; - break; + case PHOTOMETRIC_RGB: + image_type = RGB; + layer_type = (alpha) ? RGBA_IMAGE : RGB_IMAGE; + break; - case PHOTOMETRIC_PALETTE: - if (alpha) - g_print ("ignoring alpha channel on indexed color image\n"); + case PHOTOMETRIC_PALETTE: + image_type = INDEXED; + layer_type = (alpha) ? INDEXEDA_IMAGE : INDEXED_IMAGE; + break; - if (!TIFFGetField (tif, TIFFTAG_COLORMAP, &redcolormap, - &greencolormap, &bluecolormap)) - { - g_print ("error getting colormaps\n"); - gimp_quit (); - } - numcolors = maxval + 1; - maxval = 255; - grayscale = 0; + case PHOTOMETRIC_MASK: + g_error ("TIFF Can't handle PHOTOMETRIC_MASK\n"); + gimp_quit (); + break; + default: + g_error ("TIFF Unknown photometric: %d\n", photomet); + gimp_quit (); + } - for (i = 0; i < numcolors; i++) - { - redcolormap[i] >>= 8; - greencolormap[i] >>= 8; - bluecolormap[i] >>= 8; - } + if ((image = gimp_image_new (cols, rows, image_type)) == -1) { + g_error ("TIFF Can't allocate new image\n"); + gimp_quit (); + } + gimp_image_set_filename (image, filename); - if (numcolors > 256) - { - image_type = RGB; - layer_type = (alpha) ? RGBA_IMAGE : RGB_IMAGE; - } - else - { - image_type = INDEXED; - layer_type = (alpha) ? INDEXEDA_IMAGE : INDEXED_IMAGE; - } - break; - - case PHOTOMETRIC_RGB: - grayscale = 0; - image_type = RGB; - layer_type = (alpha) ? RGBA_IMAGE : RGB_IMAGE; - break; - - case PHOTOMETRIC_MASK: - g_print ("don't know how to handle PHOTOMETRIC_MASK\n"); - gimp_quit (); - - default: - g_print ("unknown photometric: %d\n", photomet); - gimp_quit (); - } - } - - - image_ID = gimp_image_new (cols, rows, image_type); - if (image_ID == -1) - { - g_print ("can't allocate new image\n"); + /* Install colormap for INDEXED images only */ + if (image_type == INDEXED) { + if (!TIFFGetField (tif, TIFFTAG_COLORMAP, &redmap, &greenmap, &bluemap)) { + g_error ("TIFF Can't get colormaps\n"); gimp_quit (); } - gimp_image_set_filename (image_ID, filename); - if ((image_type == INDEXED) && (numcolors <= 256)) - { - guchar *cmap; - gint i, j; - - cmap = g_new (guchar, numcolors * 3); - for (i = 0, j = 0; i < numcolors; i++) - { - cmap[j++] = redcolormap[i]; - cmap[j++] = greencolormap[i]; - cmap[j++] = bluecolormap[i]; - } - - gimp_image_set_cmap (image_ID, cmap, numcolors); - g_free (cmap); + for (i = 0, j = 0; i <= maxval; i++) { + cmap[j++] = redmap[i] >> 8; + cmap[j++] = greenmap[i] >> 8; + cmap[j++] = bluemap[i] >> 8; } + gimp_image_set_cmap (image, cmap, maxval + 1); + } - layer_ID = gimp_layer_new (image_ID, "Background", - cols, rows, layer_type, + layer = gimp_layer_new (image, "Background", cols, rows, layer_type, 100, NORMAL_MODE); - gimp_image_add_layer (image_ID, layer_ID, 0); - - buf = g_new (guchar, TIFFScanlineSize (tif)); - - drawable = gimp_drawable_get (layer_ID); + gimp_image_add_layer (image, layer, 0); + drawable = gimp_drawable_get (layer); + source= g_new (guchar, TIFFScanlineSize (tif)); tile_height = gimp_tile_height (); dest = g_new (guchar, tile_height * cols * drawable->bpp); gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, cols, rows, TRUE, FALSE); - if (num_extra - alpha > 0) - channel = g_new (channel_data, num_extra - alpha); + if (extra - alpha > 0) + channel = g_new (channel_data, extra - alpha); - for (k= 0; alpha + k < num_extra; ++k) - { - channel[k].ID= gimp_channel_new(image_ID, "TIFF Channel", cols, rows, - 100.0, colors); - gimp_image_add_channel(image_ID, channel[k].ID, 0); - channel[k].drawable= gimp_drawable_get (channel[k].ID); - channel[k].pixels= g_new(guchar, tile_height * cols); + /* Add alpha channels as appropriate */ + for (i= 0; alpha + i < extra; ++i) { + channel[i].ID= gimp_channel_new(image, "TIFF Channel", cols, rows, + 100.0, colors); + gimp_image_add_channel(image, channel[i].ID, 0); + channel[i].drawable= gimp_drawable_get (channel[i].ID); + channel[i].pixels= g_new(guchar, tile_height * cols); - gimp_pixel_rgn_init (&(channel[k].pixel_rgn), channel[k].drawable, 0, 0, + gimp_pixel_rgn_init (&(channel[i].pixel_rgn), channel[i].drawable, 0, 0, cols, rows, TRUE, FALSE); - } + } -#define NEXTSAMPLE \ +/* Step through all <= 8-bit samples in an image */ + +#define NEXTSAMPLE(var) \ { \ - if ( bitsleft == 0 ) \ + if (bitsleft == 0) \ { \ s++; \ bitsleft = 8; \ } \ bitsleft -= bps; \ - sample = ( *s >> bitsleft ) & maxval; \ + var = ( *s >> bitsleft ) & maxval; \ } - for (y = 0; y < rows; y = yend) - { - yend = y + tile_height; - yend = MIN (yend, rows); + for (start= 0, row = 0; row < rows; ++row) { + d= dest + cols * (row % tile_height) * drawable->bpp; - for (row = y; row < yend; row++) - { - if (TIFFReadScanline (tif, buf, row, 0) < 0) - { - g_print ("bad data read on line %d\n", row); - gimp_quit (); + /* Special cases: Scanline is compatible with GIMP storage */ + if (extra == 0 && bps == 8) { + if (TIFFReadScanline (tif, d, row, 0) < 0) { + g_error ("TIFF Bad data read on line %d\n", row); + gimp_quit (); + } + + /* Or read in and process each sample -- slower */ + } else { + if (TIFFReadScanline (tif, source, row, 0) < 0) { + g_error ("TIFF Bad data read on line %d\n", row); + gimp_quit (); + } + + for (i= 0; alpha + i < extra; ++i) { + channel[i].pixel= channel[i].pixels + cols * (row % tile_height); + } + + /* Set s/bitleft ready to use NEXTSAMPLE macro */ + + s= source; + bitsleft= 8; + + switch (photomet) { + case PHOTOMETRIC_MINISBLACK: + for (col = 0; col < cols; col++) { + NEXTSAMPLE(gray_val); + if (alpha) { + NEXTSAMPLE(alpha_val); + if (alpha_val) + *d++ = (gray_val * 65025) / (alpha_val * maxval); + else + *d++ = 0; + *d++ = alpha_val; + } else { + *d++ = (gray_val * 255) / maxval; + } + for (i= 0; alpha + i < extra; ++i) { + NEXTSAMPLE(sample); + *channel[i].pixel++ = sample; + } + } + break; + + case PHOTOMETRIC_MINISWHITE: + for (col = 0; col < cols; col++) { + NEXTSAMPLE(gray_val); + if (alpha) { + NEXTSAMPLE(alpha_val); + if (alpha_val) + *d++ = ((maxval - gray_val) * 65025) / (alpha_val * maxval); + else + *d++ = 0; + *d++ = alpha_val; + } else { + *d++ = ((maxval - gray_val) * 255) / maxval; + } + for (i= 0; alpha + i < extra; ++i) { + NEXTSAMPLE(sample); + *channel[i].pixel++ = sample; + } + } + break; + + case PHOTOMETRIC_PALETTE: + for (col = 0; col < cols; col++) { + NEXTSAMPLE(*d++); + if (alpha) { + NEXTSAMPLE(*d++); + } + for (i= 0; alpha + i < extra; ++i) { + NEXTSAMPLE(sample); + *channel[i].pixel++ = sample; + } + } + break; + + case PHOTOMETRIC_RGB: + for (col = 0; col < cols; col++) { + NEXTSAMPLE(red_val) + NEXTSAMPLE(green_val) + NEXTSAMPLE(blue_val) + if (alpha) { + NEXTSAMPLE(alpha_val) + if (alpha_val) { + *d++ = (red_val * 255) / alpha_val; + *d++ = (green_val * 255) / alpha_val; + *d++ = (blue_val * 255) / alpha_val; + } else { + *d++ = 0; + *d++ = 0; + *d++ = 0; + } + *d++ = alpha_val; + } else { + *d++ = red_val; + *d++ = green_val; + *d++ = blue_val; } + for (i= 0; alpha + i < extra; ++i) { + NEXTSAMPLE(sample); + *channel[i].pixel++ = sample; + } + } + break; - bitsleft = 8; - s = buf; - d = dest + cols * (row - y) * drawable->bpp; - for (k= 0; alpha + k < num_extra; ++k) - channel[k].pixel= channel[k].pixels + cols * (row - y); - - switch (photomet) - { - case PHOTOMETRIC_MINISBLACK: - for (col = 0; col < cols; col++) - { - NEXTSAMPLE; - gray_val = sample; - if (alpha) - { - NEXTSAMPLE; - alpha_val = sample; - if (alpha_val) - *d++ = (gray_val * 65025) / (alpha_val * maxval); - else - *d++ = 0; - *d++ = alpha_val; - } - else - *d++ = (gray_val * 255) / maxval; - for (k= 0; alpha + k < num_extra; ++k) - { - NEXTSAMPLE; - *channel[k].pixel++ = sample; - } - } - break; - - case PHOTOMETRIC_MINISWHITE: - for (col = 0; col < cols; col++) - { - NEXTSAMPLE; - gray_val = sample; - if (alpha) - { - NEXTSAMPLE; - alpha_val = sample; - if (alpha_val) - *d++ = ((maxval - gray_val) * 65025) - / (alpha_val * maxval); - else - *d++ = 0; - *d++ = alpha_val; - } - else - *d++ = ((maxval - gray_val) * 255) / maxval ; - for (k= 0; alpha + k < num_extra; ++k) - { - NEXTSAMPLE; - *channel[k].pixel++ = sample; - } - } - break; - - case PHOTOMETRIC_PALETTE: - if (numcolors > 256) - for (col = 0; col < cols; col++) - { - NEXTSAMPLE; - red_val = redcolormap[sample]; - green_val = greencolormap[sample]; - blue_val = bluecolormap[sample]; - if (alpha) - { - NEXTSAMPLE; - alpha_val = sample; - if (alpha_val) - { - *d++ = (red_val * 255) / alpha_val; - *d++ = (green_val * 255) / alpha_val; - *d++ = (blue_val * 255) / alpha_val; - } - else - { - *d++ = 0; - *d++ = 0; - *d++ = 0; - } - *d++ = alpha_val; - } - else - { - *d++ = red_val; - *d++ = green_val; - *d++ = blue_val; - } - for (k= 0; alpha + k < num_extra; ++k) - { - NEXTSAMPLE; - *channel[k].pixel++ = sample; - } - } - else - for (col = 0; col < cols; col++) - { - NEXTSAMPLE; - if (alpha) - { - *d++ = sample; - NEXTSAMPLE; - *d++ = sample; - } - else - { - *d++ = sample; - } - for (k= 0; alpha + k < num_extra; ++k) - { - NEXTSAMPLE; - *channel[k].pixel++ = sample; - } - } - break; - - case PHOTOMETRIC_RGB: - for (col = 0; col < cols; col++) - { - NEXTSAMPLE; - red_val = sample; - NEXTSAMPLE; - green_val = sample; - NEXTSAMPLE; - blue_val = sample; - if (alpha) - { - NEXTSAMPLE; - alpha_val = sample; - if (alpha_val) - { - *d++ = (red_val * 255) / alpha_val; - *d++ = (green_val * 255) / alpha_val; - *d++ = (blue_val * 255) / alpha_val; - } - else - { - *d++ = 0; - *d++ = 0; - *d++ = 0; - } - *d++ = alpha_val; - } - else - { - *d++ = red_val; - *d++ = green_val; - *d++ = blue_val; - } - for (k= 0; alpha + k < num_extra; ++k) - { - NEXTSAMPLE; - *channel[k].pixel++ = sample; - } - } - break; - - default: - g_print ("unknown photometric: %d\n", photomet); - gimp_quit (); - } - } - - gimp_pixel_rgn_set_rect (&pixel_rgn, dest, 0, y, cols, yend - y); - for (k= 0; alpha + k < num_extra; ++k) - gimp_pixel_rgn_set_rect(&(channel[k].pixel_rgn), - channel[k].pixels, 0, y, cols, yend -y); - gimp_progress_update ((double) row / (double) rows); + default: + /* This case was handled earlier */ + g_assert_not_reached(); + } } + + if (((row + 1) % tile_height) == 0 || row + 1 == rows) { + gimp_pixel_rgn_set_rect (&pixel_rgn, dest, 0, start, cols, 1+row-start); + for (i= 0; alpha + i < extra; ++i) { + gimp_pixel_rgn_set_rect(&(channel[i].pixel_rgn), channel[i].pixels, + 0, start, cols, 1+row-start); + } + gimp_progress_update ((double) row / (double) rows); + start= row + 1; + } + } + gimp_drawable_flush (drawable); gimp_drawable_detach (drawable); - return image_ID; + return image; } /* @@ -669,11 +566,7 @@ load_image (char *filename) ** other special, indirect and consequential damages. */ -static gint -save_image (char *filename, - gint32 image_ID, - gint32 drawable_ID) -{ +static gint save_image (char *filename, gint32 image, gint32 layer) { TIFF *tif; unsigned short red[256]; unsigned short grn[256]; @@ -720,8 +613,8 @@ save_image (char *filename, gimp_progress_init (name); free (name); - drawable = gimp_drawable_get (drawable_ID); - drawable_type = gimp_drawable_type (drawable_ID); + drawable = gimp_drawable_get (layer); + drawable_type = gimp_drawable_type (layer); gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, drawable->width, drawable->height, FALSE, FALSE); cols = drawable->width; @@ -764,7 +657,7 @@ save_image (char *filename, bytesperrow = cols; alpha = 0; - cmap = gimp_image_get_cmap (image_ID, &colors); + cmap = gimp_image_get_cmap (image, &colors); for (i = 0; i < colors; i++) { @@ -798,13 +691,13 @@ save_image (char *filename, TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, photometric); TIFFSetField (tif, TIFFTAG_FILLORDER, fillorder); TIFFSetField (tif, TIFFTAG_DOCUMENTNAME, filename); - TIFFSetField (tif, TIFFTAG_IMAGEDESCRIPTION, "created with The GIMP"); + TIFFSetField (tif, TIFFTAG_IMAGEDESCRIPTION, "Created with The GIMP"); TIFFSetField (tif, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel); TIFFSetField (tif, TIFFTAG_ROWSPERSTRIP, rowsperstrip); /* TIFFSetField( tif, TIFFTAG_STRIPBYTECOUNTS, rows / rowsperstrip ); */ TIFFSetField (tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); - if (gimp_drawable_type (drawable_ID) == INDEXED_IMAGE) + if (drawable_type == INDEXED_IMAGE) TIFFSetField (tif, TIFFTAG_COLORMAP, red, grn, blu); /* array to rearrange data */