1 /* $Header: /cvsroot/osrs/libtiff/contrib/pds/tif_imageiter.c,v 1.1.1.1 1999/07/27 21:50:27 mike Exp $ */
2 
3 /*
4  * Copyright (c) 1991-1996 Sam Leffler
5  * Copyright (c) 1991-1996 Silicon Graphics, Inc.
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and
8  * its documentation for any purpose is hereby granted without fee, provided
9  * that (i) the above copyright notices and this permission notice appear in
10  * all copies of the software and related documentation, and (ii) the names of
11  * Sam Leffler and Silicon Graphics may not be used in any advertising or
12  * publicity relating to the software without the specific, prior written
13  * permission of Sam Leffler and Silicon Graphics.
14  *
15  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
17  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
18  *
19  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
20  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
21  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
22  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
23  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
24  * OF THIS SOFTWARE.
25  */
26 
27 /*
28  * TIFF Library
29  *
30  * Written by Conrad J. Poelman, PL/WSAT, Kirtland AFB, NM on 26 Mar 96.
31  *
32  * This file contains code to allow a calling program to "iterate" over each
33  * pixels in an image as it is read from the file. The iterator takes care of
34  * reading strips versus (possibly clipped) tiles, decoding the information
35  * according to the decoding method, and so on, so that calling program can
36  * ignore those details. The calling program does, however, need to be
37  * conscious of the type of the pixel data that it is receiving.
38  *
39  * For reasons of efficiency, the callback function actually gets called for
40  * "blocks" of pixels rather than for individual pixels. The format of the
41  * callback arguments is given below.
42  *
43  * This code was taken from TIFFReadRGBAImage() in tif_getimage.c of the original
44  * TIFF distribution, and simplified and generalized to provide this general
45  * iteration capability. Those routines could certainly be re-implemented in terms
46  * of a TIFFImageIter if desired.
47  *
48  */
49 #include "tiffiop.h"
50 #include "tif_imgiter.h"
51 #include <assert.h>
52 #include <stdio.h>
53 
54 static	int gtTileContig(TIFFImageIter*, void *udata, uint32, uint32);
55 static	int gtTileSeparate(TIFFImageIter*, void *udata, uint32, uint32);
56 static	int gtStripContig(TIFFImageIter*, void *udata, uint32, uint32);
57 static	int gtStripSeparate(TIFFImageIter*, void *udata, uint32, uint32);
58 
59 static	const char photoTag[] = "PhotometricInterpretation";
60 
61 static int
isCCITTCompression(TIFF * tif)62 isCCITTCompression(TIFF* tif)
63 {
64     uint16 compress;
65     TIFFGetField(tif, TIFFTAG_COMPRESSION, &compress);
66     return (compress == COMPRESSION_CCITTFAX3 ||
67 	    compress == COMPRESSION_CCITTFAX4 ||
68 	    compress == COMPRESSION_CCITTRLE ||
69 	    compress == COMPRESSION_CCITTRLEW);
70 }
71 
72 int
TIFFImageIterBegin(TIFFImageIter * img,TIFF * tif,int stop,char emsg[1024])73 TIFFImageIterBegin(TIFFImageIter* img, TIFF* tif, int stop, char emsg[1024])
74 {
75     uint16* sampleinfo;
76     uint16 extrasamples;
77     uint16 planarconfig;
78     int colorchannels;
79 
80     img->tif = tif;
81     img->stoponerr = stop;
82     TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &img->bitspersample);
83     img->alpha = 0;
84     TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &img->samplesperpixel);
85     TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES,
86 	&extrasamples, &sampleinfo);
87     if (extrasamples == 1)
88 	switch (sampleinfo[0]) {
89 	case EXTRASAMPLE_ASSOCALPHA:	/* data is pre-multiplied */
90 	case EXTRASAMPLE_UNASSALPHA:	/* data is not pre-multiplied */
91 	    img->alpha = sampleinfo[0];
92 	    break;
93 	}
94     colorchannels = img->samplesperpixel - extrasamples;
95     TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planarconfig);
96     if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &img->photometric)) {
97 	switch (colorchannels) {
98 	case 1:
99 	    if (isCCITTCompression(tif))
100 		img->photometric = PHOTOMETRIC_MINISWHITE;
101 	    else
102 		img->photometric = PHOTOMETRIC_MINISBLACK;
103 	    break;
104 	case 3:
105 	    img->photometric = PHOTOMETRIC_RGB;
106 	    break;
107 	default:
108 	    sprintf(emsg, "Missing needed %s tag", photoTag);
109 	    return (0);
110 	}
111     }
112     switch (img->photometric) {
113     case PHOTOMETRIC_PALETTE:
114 	if (!TIFFGetField(tif, TIFFTAG_COLORMAP,
115 	    &img->redcmap, &img->greencmap, &img->bluecmap)) {
116 	    TIFFError(TIFFFileName(tif), "Missing required \"Colormap\" tag");
117 	    return (0);
118 	}
119 	/* fall thru... */
120     case PHOTOMETRIC_MINISWHITE:
121     case PHOTOMETRIC_MINISBLACK:
122 /* This should work now so skip the check - BSR
123 	if (planarconfig == PLANARCONFIG_CONTIG && img->samplesperpixel != 1) {
124 	    sprintf(emsg,
125 		"Sorry, can not handle contiguous data with %s=%d, and %s=%d",
126 		photoTag, img->photometric,
127 		"Samples/pixel", img->samplesperpixel);
128 	    return (0);
129 	}
130  */
131 	break;
132     case PHOTOMETRIC_YCBCR:
133 	if (planarconfig != PLANARCONFIG_CONTIG) {
134 	    sprintf(emsg, "Sorry, can not handle YCbCr images with %s=%d",
135 		"Planarconfiguration", planarconfig);
136 	    return (0);
137 	}
138 	/* It would probably be nice to have a reality check here. */
139 	{ uint16 compress;
140 	  TIFFGetField(tif, TIFFTAG_COMPRESSION, &compress);
141 	  if (compress == COMPRESSION_JPEG && planarconfig == PLANARCONFIG_CONTIG) {
142 	    /* can rely on libjpeg to convert to RGB */
143 	    /* XXX should restore current state on exit */
144 	    TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
145 	    img->photometric = PHOTOMETRIC_RGB;
146 	  }
147 	}
148 	break;
149     case PHOTOMETRIC_RGB:
150 	if (colorchannels < 3) {
151 	    sprintf(emsg, "Sorry, can not handle RGB image with %s=%d",
152 		"Color channels", colorchannels);
153 	    return (0);
154 	}
155 	break;
156     case PHOTOMETRIC_SEPARATED: {
157 	uint16 inkset;
158 	TIFFGetFieldDefaulted(tif, TIFFTAG_INKSET, &inkset);
159 	if (inkset != INKSET_CMYK) {
160 	    sprintf(emsg, "Sorry, can not handle separated image with %s=%d",
161 		"InkSet", inkset);
162 	    return (0);
163 	}
164 	if (img->samplesperpixel != 4) {
165 	    sprintf(emsg, "Sorry, can not handle separated image with %s=%d",
166 		"Samples/pixel", img->samplesperpixel);
167 	    return (0);
168 	}
169 	break;
170     }
171     default:
172 	sprintf(emsg, "Sorry, can not handle image with %s=%d",
173 	    photoTag, img->photometric);
174 	return (0);
175     }
176     TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &img->width);
177     TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &img->height);
178 
179     TIFFGetFieldDefaulted(tif, TIFFTAG_ORIENTATION, &img->orientation);
180     switch (img->orientation) {
181     case ORIENTATION_BOTRIGHT:
182     case ORIENTATION_RIGHTBOT:	/* XXX */
183     case ORIENTATION_LEFTBOT:	/* XXX */
184 	TIFFWarning(TIFFFileName(tif), "using bottom-left orientation");
185 	img->orientation = ORIENTATION_BOTLEFT;
186 	/* fall thru... */
187     case ORIENTATION_BOTLEFT:
188 	break;
189     case ORIENTATION_TOPRIGHT:
190     case ORIENTATION_RIGHTTOP:	/* XXX */
191     case ORIENTATION_LEFTTOP:	/* XXX */
192     default:
193 	TIFFWarning(TIFFFileName(tif), "using top-left orientation");
194 	img->orientation = ORIENTATION_TOPLEFT;
195 	/* fall thru... */
196     case ORIENTATION_TOPLEFT:
197 	break;
198     }
199 
200     img->isContig =
201 	!(planarconfig == PLANARCONFIG_SEPARATE && colorchannels > 1);
202     if (img->isContig) {
203 	img->get = TIFFIsTiled(tif) ? gtTileContig : gtStripContig;
204     } else {
205 	img->get = TIFFIsTiled(tif) ? gtTileSeparate : gtStripSeparate;
206     }
207     return (1);
208 }
209 
210 int
TIFFImageIterGet(TIFFImageIter * img,void * udata,uint32 w,uint32 h)211 TIFFImageIterGet(TIFFImageIter* img, void *udata, uint32 w, uint32 h)
212 {
213     if (img->get == NULL) {
214 	TIFFError(TIFFFileName(img->tif), "No \"get\" routine setup");
215 	return (0);
216     }
217     if (img->callback.any == NULL) {
218 	TIFFError(TIFFFileName(img->tif),
219 	    "No \"put\" routine setupl; probably can not handle image format");
220 	return (0);
221     }
222     return (*img->get)(img, udata, w, h);
223 }
224 
TIFFImageIterEnd(TIFFImageIter * img)225 TIFFImageIterEnd(TIFFImageIter* img)
226 {
227     /* Nothing to free... ? */
228 }
229 
230 /*
231  * Read the specified image into an ABGR-format raster.
232  */
233 int
TIFFReadImageIter(TIFF * tif,uint32 rwidth,uint32 rheight,uint8 * raster,int stop)234 TIFFReadImageIter(TIFF* tif,
235     uint32 rwidth, uint32 rheight, uint8* raster, int stop)
236 {
237     char emsg[1024];
238     TIFFImageIter img;
239     int ok;
240 
241     if (TIFFImageIterBegin(&img, tif, stop, emsg)) {
242 	/* XXX verify rwidth and rheight against width and height */
243 	ok = TIFFImageIterGet(&img, raster, rwidth, img.height);
244 	TIFFImageIterEnd(&img);
245     } else {
246 	TIFFError(TIFFFileName(tif), emsg);
247 	ok = 0;
248     }
249     return (ok);
250 }
251 
252 
253 /*
254  * Get an tile-organized image that has
255  *	PlanarConfiguration contiguous if SamplesPerPixel > 1
256  * or
257  *	SamplesPerPixel == 1
258  */
259 static int
gtTileContig(TIFFImageIter * img,void * udata,uint32 w,uint32 h)260 gtTileContig(TIFFImageIter* img, void *udata, uint32 w, uint32 h)
261 {
262     TIFF* tif = img->tif;
263     ImageIterTileContigRoutine callback = img->callback.contig;
264     uint16 orientation;
265     uint32 col, row;
266     uint32 tw, th;
267     u_char* buf;
268     int32 fromskew;
269     uint32 nrow;
270 
271     buf = (u_char*) _TIFFmalloc(TIFFTileSize(tif));
272     if (buf == 0) {
273 	TIFFError(TIFFFileName(tif), "No space for tile buffer");
274 	return (0);
275     }
276     TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tw);
277     TIFFGetField(tif, TIFFTAG_TILELENGTH, &th);
278     orientation = img->orientation;
279     for (row = 0; row < h; row += th) {
280 	nrow = (row + th > h ? h - row : th);
281 	for (col = 0; col < w; col += tw) {
282 	    if (TIFFReadTile(tif, buf, col, row, 0, 0) < 0 && img->stoponerr)
283 		break;
284 	    if (col + tw > w) {
285 		/*
286 		 * Tile is clipped horizontally.  Calculate
287 		 * visible portion and skewing factors.
288 		 */
289 		uint32 npix = w - col;
290 		fromskew = tw - npix;
291 		(*callback)(img, udata, col, row, npix, nrow, fromskew, buf);
292 	    } else {
293 		(*callback)(img, udata, col, row, tw, nrow, 0, buf);
294 	    }
295 	}
296     }
297     _TIFFfree(buf);
298     return (1);
299 }
300 
301 /*
302  * Get an tile-organized image that has
303  *	 SamplesPerPixel > 1
304  *	 PlanarConfiguration separated
305  * We assume that all such images are RGB.
306  */
307 static int
gtTileSeparate(TIFFImageIter * img,void * udata,uint32 w,uint32 h)308 gtTileSeparate(TIFFImageIter* img, void *udata, uint32 w, uint32 h)
309 {
310     TIFF* tif = img->tif;
311     ImageIterTileSeparateRoutine callback = img->callback.separate;
312     uint16 orientation;
313     uint32 col, row;
314     uint32 tw, th;
315     u_char* buf;
316     u_char* r;
317     u_char* g;
318     u_char* b;
319     u_char* a;
320     tsize_t tilesize;
321     int32 fromskew;
322     int alpha = img->alpha;
323     uint32 nrow;
324 
325     tilesize = TIFFTileSize(tif);
326     buf = (u_char*) _TIFFmalloc(4*tilesize);
327     if (buf == 0) {
328 	TIFFError(TIFFFileName(tif), "No space for tile buffer");
329 	return (0);
330     }
331     r = buf;
332     g = r + tilesize;
333     b = g + tilesize;
334     a = b + tilesize;
335     if (!alpha)
336 	memset(a, 0xff, tilesize);
337     TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tw);
338     TIFFGetField(tif, TIFFTAG_TILELENGTH, &th);
339     orientation = img->orientation;
340     for (row = 0; row < h; row += th) {
341 	nrow = (row + th > h ? h - row : th);
342 	for (col = 0; col < w; col += tw) {
343 	    if (TIFFReadTile(tif, r, col, row,0,0) < 0 && img->stoponerr)
344 		break;
345 	    if (TIFFReadTile(tif, g, col, row,0,1) < 0 && img->stoponerr)
346 		break;
347 	    if (TIFFReadTile(tif, b, col, row,0,2) < 0 && img->stoponerr)
348 		break;
349 	    if (alpha && TIFFReadTile(tif,a,col,row,0,3) < 0 && img->stoponerr)
350 		break;
351 	    if (col + tw > w) {
352 		/*
353 		 * Tile is clipped horizontally.  Calculate
354 		 * visible portion and skewing factors.
355 		 */
356 		uint32 npix = w - col;
357 		fromskew = tw - npix;
358 		(*callback)(img, udata, col, row, npix, nrow, fromskew, r, g, b, a);
359 	    } else {
360 		(*callback)(img, udata, col, row, tw, nrow, 0, r, g, b, a);
361 	    }
362 	}
363     }
364     _TIFFfree(buf);
365     return (1);
366 }
367 
368 /*
369  * Get a strip-organized image that has
370  *	PlanarConfiguration contiguous if SamplesPerPixel > 1
371  * or
372  *	SamplesPerPixel == 1
373  */
374 static int
gtStripContig(TIFFImageIter * img,void * udata,uint32 w,uint32 h)375 gtStripContig(TIFFImageIter* img, void *udata, uint32 w, uint32 h)
376 {
377     TIFF* tif = img->tif;
378     ImageIterTileContigRoutine callback = img->callback.contig;
379     uint16 orientation;
380     uint32 row, nrow;
381     u_char* buf;
382     uint32 rowsperstrip;
383     uint32 imagewidth = img->width;
384     tsize_t scanline;
385     int32 fromskew;
386 
387     buf = (u_char*) _TIFFmalloc(TIFFStripSize(tif));
388     if (buf == 0) {
389 	TIFFError(TIFFFileName(tif), "No space for strip buffer");
390 	return (0);
391     }
392     orientation = img->orientation;
393     TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
394     scanline = TIFFScanlineSize(tif);
395     fromskew = (w < imagewidth ? imagewidth - w : 0);
396     for (row = 0; row < h; row += rowsperstrip) {
397 	nrow = (row + rowsperstrip > h ? h - row : rowsperstrip);
398 	if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, row, 0),
399 	    buf, nrow*scanline) < 0 && img->stoponerr)
400 		break;
401 	(*callback)(img, udata, 0, row, w, nrow, fromskew, buf);
402     }
403     _TIFFfree(buf);
404     return (1);
405 }
406 
407 /*
408  * Get a strip-organized image with
409  *	 SamplesPerPixel > 1
410  *	 PlanarConfiguration separated
411  * We assume that all such images are RGB.
412  */
413 static int
gtStripSeparate(TIFFImageIter * img,void * udata,uint32 w,uint32 h)414 gtStripSeparate(TIFFImageIter* img, void *udata, uint32 w, uint32 h)
415 {
416     TIFF* tif = img->tif;
417     ImageIterTileSeparateRoutine callback = img->callback.separate;
418     uint16 orientation;
419     u_char *buf;
420     u_char *r, *g, *b, *a;
421     uint32 row, nrow;
422     tsize_t scanline;
423     uint32 rowsperstrip;
424     uint32 imagewidth = img->width;
425     tsize_t stripsize;
426     int32 fromskew;
427     int alpha = img->alpha;
428 
429     stripsize = TIFFStripSize(tif);
430     r = buf = (u_char *)_TIFFmalloc(4*stripsize);
431     if (buf == 0) {
432 	TIFFError(TIFFFileName(tif), "No space for tile buffer");
433 	return (0);
434     }
435     g = r + stripsize;
436     b = g + stripsize;
437     a = b + stripsize;
438     if (!alpha)
439 	memset(a, 0xff, stripsize);
440     orientation = img->orientation;
441     TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
442     scanline = TIFFScanlineSize(tif);
443     fromskew = (w < imagewidth ? imagewidth - w : 0);
444     for (row = 0; row < h; row += rowsperstrip) {
445 	nrow = (row + rowsperstrip > h ? h - row : rowsperstrip);
446 	if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, row, 0),
447 	    r, nrow*scanline) < 0 && img->stoponerr)
448 	    break;
449 	if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, row, 1),
450 	    g, nrow*scanline) < 0 && img->stoponerr)
451 	    break;
452 	if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, row, 2),
453 	    b, nrow*scanline) < 0 && img->stoponerr)
454 	    break;
455 	if (alpha &&
456 	    (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, row, 3),
457 	    a, nrow*scanline) < 0 && img->stoponerr))
458 	    break;
459 	(*callback)(img, udata, 0, row, w, nrow, fromskew, r, g, b, a);
460     }
461     _TIFFfree(buf);
462     return (1);
463 }
464 
DECLAREContigCallbackFunc(TestContigCallback)465 DECLAREContigCallbackFunc(TestContigCallback)
466 {
467     printf("Contig Callback called with x = %d, y = %d, w = %d, h = %d, fromskew = %d\n",
468 	   x, y, w, h, fromskew);
469 }
470 
471 
DECLARESepCallbackFunc(TestSepCallback)472 DECLARESepCallbackFunc(TestSepCallback)
473 {
474     printf("Sep Callback called with x = %d, y = %d, w = %d, h = %d, fromskew = %d\n",
475 	   x, y, w, h, fromskew);
476 }
477 
478 
479 #ifdef MAIN
main(int argc,char ** argv)480 main(int argc, char **argv)
481 {
482     char emsg[1024];
483     TIFFImageIter img;
484     int ok;
485     int stop = 1;
486 
487     TIFF *tif;
488     unsigned long nx, ny;
489     unsigned short BitsPerSample, SamplesPerPixel;
490     int isColorMapped, isPliFile;
491     unsigned char *ColorMap;
492     unsigned char *data;
493 
494     if (argc < 2) {
495 	fprintf(stderr,"usage: %s tiff_file\n",argv[0]);
496 	exit(1);
497     }
498     tif = (TIFF *)PLIGetImage(argv[1], (void *) &data, &ColorMap,
499 			      &nx, &ny, &BitsPerSample, &SamplesPerPixel,
500 			      &isColorMapped, &isPliFile);
501     if (tif != NULL) {
502 
503 	if (TIFFImageIterBegin(&img, tif, stop, emsg)) {
504 	    /* Here need to set data and callback function! */
505 	    if (img.isContig) {
506 		img.callback = TestContigCallback;
507 	    } else {
508 		img.callback = TestSepCallback;
509 	    }
510 	    ok = TIFFImageIterGet(&img, NULL, img.width, img.height);
511 	    TIFFImageIterEnd(&img);
512 	} else {
513 	    TIFFError(TIFFFileName(tif), emsg);
514 	}
515     }
516 
517 }
518 #endif
519