1 /* Panorama_Tools - Generate, Edit and Convert Panoramic Images
2 Copyright (C) 1998,1999 - Helmut Dersch der@fh-furtwangen.de
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this software; see the file COPYING. If not, a copy
16 can be downloaded from http://www.gnu.org/licenses/gpl.html, or
17 obtained by writing to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19
20 /*------------------------------------------------------------*/
21
22 // Functions to read tiff format
23
24
25 #include <stdio.h>
26 #include <assert.h>
27
28 #include "panorama.h"
29 #include "filter.h"
30
31 #include "tiffio.h"
32
33 #include "ZComb.h"
34
35
36 #include "pttiff.h"
37 #include "metadata.h"
38 #include "ptstitch.h"
39
40
41 int readplanarTIFF(Image * im, TIFF * tif);
42 void RGBAtoARGB(uint8_t * buf, int width, int bitsPerPixel);
43 void ARGBtoRGBA(uint8_t * buf, int width, int bitsPerPixel);
44 int readtif(Image * im, TIFF * tif);
45
46
readplanarTIFF(Image * im,TIFF * tif)47 int readplanarTIFF(Image * im, TIFF * tif)
48 {
49 uint8_t *buf;
50 int32_t y;
51 short SamplesPerPixel;
52
53 TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &SamplesPerPixel);
54 if (SamplesPerPixel > 4)
55 return -1;
56 if (SamplesPerPixel == 3)
57 {
58 im->bitsPerPixel = im->bitsPerPixel * 3 / 4;
59 im->bytesPerLine = im->bytesPerLine * 3 / 4;
60 }
61
62 buf = (uint8_t *) malloc((size_t) TIFFScanlineSize(tif));
63 if (buf == NULL)
64 {
65 PrintError("Not enough memory");
66 return -1;
67 }
68
69 for (y = 0; y < im->height; y++)
70 {
71 TIFFReadScanline(tif, buf, (uint32) y, 0);
72 RGBAtoARGB(buf, im->width, im->bitsPerPixel);
73 memcpy(*(im->data) + y * im->bytesPerLine, buf,
74 (size_t) (im->bytesPerLine));
75 }
76 free(buf);
77 ThreeToFourBPP(im);
78 return 0;
79
80 }
81
82
83
84 // image is allocated, but not image data
85
readtif(Image * im,TIFF * tif)86 int readtif(Image * im, TIFF * tif)
87 {
88 short BitsPerSample, tPhotoMetric, config;
89 uint32_t w, h;
90 unsigned long **hdl_raster;
91
92 if (tif == NULL || im == NULL)
93 return -1;
94
95 TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
96 TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
97 TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &BitsPerSample);
98 TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &tPhotoMetric);
99 TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &config);
100
101 SetImageDefaults(im);
102 im->width = w;
103 im->height = h;
104 im->bitsPerPixel = 32 * BitsPerSample / 8;
105 im->bytesPerLine = im->width * im->bitsPerPixel / 8;
106 im->dataSize = im->bytesPerLine * im->height;
107
108
109 hdl_raster = (unsigned long **) mymalloc((size_t) im->dataSize);
110 if (hdl_raster == NULL)
111 {
112 PrintError("Not enough memory");
113 return -1;
114 }
115
116 im->data = (uint8_t **) hdl_raster;
117
118 if (tPhotoMetric == PHOTOMETRIC_RGB && config == PLANARCONFIG_CONTIG)
119 {
120 return readplanarTIFF(im, tif);
121 }
122
123 if (TIFFReadRGBAImage
124 (tif, (uint32) w, (uint32) h, (uint32 *) * (im->data), 0))
125 {
126 // Convert agbr to argb; flip image vertically
127 unsigned char *cline, *ct, *cb;
128 int h2 = im->height / 2, y;
129 // Only do the conversion once
130 size_t localBytesPerLine = im->bytesPerLine;
131
132 cline = (unsigned char *) malloc(localBytesPerLine);
133 if (cline == NULL)
134 {
135 PrintError("Not enough memory");
136 return -1;
137 }
138
139 ct = *im->data;
140 cb = *im->data + (im->height - 1) * im->bytesPerLine;
141
142 for (y = 0; y < h2;
143 y++, ct += im->bytesPerLine, cb -= im->bytesPerLine)
144 {
145 RGBAtoARGB(ct, im->width, im->bitsPerPixel);
146 RGBAtoARGB(cb, im->width, im->bitsPerPixel);
147 memcpy(cline, ct, localBytesPerLine);
148 memcpy(ct, cb, localBytesPerLine);
149 memcpy(cb, cline, localBytesPerLine);
150 }
151 if (im->height != 2 * h2)
152 { // odd number of scanlines
153 RGBAtoARGB(*im->data + y * im->bytesPerLine, im->width,
154 im->bitsPerPixel);
155 }
156 if (cline)
157 free(cline);
158 }
159 else
160 {
161 PrintError("Could not read tiff-data");
162 return -1;
163 }
164 return 0;
165 }
166
167 /**
168 * Populates the CropInfo struct with data about cropping of
169 * the TIFF file specified by filename
170 */
getCropInformationFromTiff(TIFF * tif,CropInfo * c)171 void getCropInformationFromTiff(TIFF * tif, CropInfo * c)
172 {
173 float x_position, x_resolution, y_position, y_resolution;
174
175 //these are the actual, physical dimensions of the TIFF file
176 TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &(c->cropped_width));
177 TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &(c->cropped_height));
178
179 //If nothing is stored in these tags, then this must be an "uncropped" TIFF
180 //file, in which case, the "full size" width/height is the same as the
181 //"cropped" width and height
182 if (TIFFGetField(tif, TIFFTAG_PIXAR_IMAGEFULLWIDTH, &(c->full_width)) ==
183 0)
184 (c->full_width = c->cropped_width);
185 if (TIFFGetField(tif, TIFFTAG_PIXAR_IMAGEFULLLENGTH, &(c->full_height)) ==
186 0)
187 (c->full_height = c->cropped_height);
188
189 if (TIFFGetField(tif, TIFFTAG_XPOSITION, &x_position) == 0)
190 x_position = 0;
191 if (TIFFGetField(tif, TIFFTAG_XRESOLUTION, &x_resolution) == 0)
192 x_resolution = 0;
193 if (TIFFGetField(tif, TIFFTAG_YPOSITION, &y_position) == 0)
194 y_position = 0;
195 if (TIFFGetField(tif, TIFFTAG_YRESOLUTION, &y_resolution) == 0)
196 y_resolution = 0;
197
198 //offset in pixels of "cropped" image from top left corner of
199 //full image (rounded to nearest integer)
200 c->x_offset = (uint32) ((x_position * x_resolution) + 0.49);
201 c->y_offset = (uint32) ((y_position * y_resolution) + 0.49);
202
203 //printf("%s: %dx%d @ %d,%d", filename, c->cropped_width, c->cropped_height, c->x_offset, c->y_offset);
204 }
205
206
setCropInformationInTiff(TIFF * tiffFile,CropInfo * crop_info)207 void setCropInformationInTiff(TIFF * tiffFile, CropInfo * crop_info)
208 {
209 char *errMsg = "Could not set TIFF tag";
210 float pixels_per_resolution_unit = 150.0;
211
212 //If crop_info==NULL then this isn't a "cropped TIFF", so don't include
213 //cropped TIFF tags
214 if (crop_info == NULL)
215 return;
216
217 //The X offset in ResolutionUnits of the left side of the image, with
218 //respect to the left side of the page.
219 if (TIFFSetField
220 (tiffFile, TIFFTAG_XPOSITION,
221 (float) crop_info->x_offset / pixels_per_resolution_unit) == 0)
222 dieWithError(errMsg);
223 //The Y offset in ResolutionUnits of the top of the image, with
224 //respect to the top of the page.
225 if (TIFFSetField
226 (tiffFile, TIFFTAG_YPOSITION,
227 (float) crop_info->y_offset / pixels_per_resolution_unit) == 0)
228 dieWithError(errMsg);
229
230 //The number of pixels per ResolutionUnit in the ImageWidth
231 if (TIFFSetField
232 (tiffFile, TIFFTAG_XRESOLUTION,
233 (float) pixels_per_resolution_unit) == 0)
234 dieWithError(errMsg);
235 //The number of pixels per ResolutionUnit in the ImageLength (height)
236 if (TIFFSetField
237 (tiffFile, TIFFTAG_YRESOLUTION,
238 (float) pixels_per_resolution_unit) == 0)
239 dieWithError(errMsg);
240
241 //The size of the picture represented by an image. Note: 2 = Inches. This
242 //is required so that the computation of pixel offset using XPOSITION/YPOSITION and
243 //XRESOLUTION/YRESOLUTION is valid (See tag description for XPOSITION/YPOSITION).
244 if (TIFFSetField(tiffFile, TIFFTAG_RESOLUTIONUNIT, (uint16_t) 2) == 0)
245 dieWithError(errMsg);
246
247 // TIFFTAG_PIXAR_IMAGEFULLWIDTH and TIFFTAG_PIXAR_IMAGEFULLLENGTH
248 // are set when an image has been cropped out of a larger image.
249 // They reflect the size of the original uncropped image.
250 // The TIFFTAG_XPOSITION and TIFFTAG_YPOSITION can be used
251 // to determine the position of the smaller image in the larger one.
252 if (TIFFSetField
253 (tiffFile, TIFFTAG_PIXAR_IMAGEFULLWIDTH, crop_info->full_width) == 0)
254 dieWithError(errMsg);
255 if (TIFFSetField
256 (tiffFile, TIFFTAG_PIXAR_IMAGEFULLLENGTH,
257 crop_info->full_height) == 0)
258 dieWithError(errMsg);
259 }
260
261
262
readTIFF(Image * im,fullPath * sfile)263 int readTIFF(Image * im, fullPath * sfile)
264 {
265 char filename[512];
266 TIFF *tif;
267 int result = 0;
268
269
270
271 #ifdef __Mac__
272 unsigned char the_pcUnixFilePath[512]; //added by Kekus Digital
273 Str255 the_cString;
274 Boolean the_bReturnValue;
275 CFStringRef the_FilePath;
276 CFURLRef the_Url; //till here
277 #endif
278
279 if (FullPathtoString(sfile, filename))
280 {
281 PrintError("Could not get filename");
282 return -1;
283 }
284
285 #ifdef __Mac__
286 CopyCStringToPascal(filename, the_cString); //Added by Kekus Digital
287 the_FilePath =
288 CFStringCreateWithPascalString(kCFAllocatorDefault, the_cString,
289 kCFStringEncodingUTF8);
290 the_Url =
291 CFURLCreateWithFileSystemPath(kCFAllocatorDefault, the_FilePath,
292 kCFURLHFSPathStyle, false);
293 the_bReturnValue =
294 CFURLGetFileSystemRepresentation(the_Url, true, the_pcUnixFilePath,
295 512);
296
297 strcpy(filename, the_pcUnixFilePath); //till here
298 #endif
299
300 tif = TIFFOpen(filename, "r");
301
302 if (!tif)
303 {
304 PrintError("Could not open tiff-file");
305 return -1;
306 }
307 result = readtif(im, tif);
308
309 //Store name of TIFF file
310 strncpy(im->name, filename, 255);
311
312 getCropInformationFromTiff(tif, &(im->cropInformation));
313
314 TIFFClose(tif);
315 return result;
316 }
317
writeCroppedTIFF(Image * im,fullPath * sfile,CropInfo * crop_info)318 int writeCroppedTIFF(Image * im, fullPath * sfile, CropInfo * crop_info)
319 {
320 char string[512];
321 TIFF *tif;
322 uint8_t *buf;
323 unsigned int y;
324 size_t bufsize;
325
326 #ifdef __Mac__
327 unsigned char the_pcUnixFilePath[512]; //added by Kekus Digital
328 Str255 the_cString;
329 Boolean the_bReturnValue;
330 CFStringRef the_FilePath;
331 CFURLRef the_Url; //till here
332 #endif
333
334 if (FullPathtoString(sfile, string))
335 {
336 PrintError("Could not get filename");
337 return -1;
338 }
339
340 #ifdef __Mac__
341 CopyCStringToPascal(string, the_cString); //added by Kekus Digital
342 the_FilePath =
343 CFStringCreateWithPascalString(kCFAllocatorDefault, the_cString,
344 kCFStringEncodingUTF8);
345 the_Url =
346 CFURLCreateWithFileSystemPath(kCFAllocatorDefault, the_FilePath,
347 kCFURLHFSPathStyle, false);
348 the_bReturnValue =
349 CFURLGetFileSystemRepresentation(the_Url, true, the_pcUnixFilePath,
350 512);
351
352 strcpy(string, the_pcUnixFilePath); //till here
353 #endif
354
355 tif = TIFFOpen(string, "w");
356 if (!tif)
357 {
358 PrintError("Could not create TIFF-file");
359 return -1;
360 }
361
362 // Rik's mask-from-focus hacking
363 if (ZCombSeeImage(im, string))
364 {
365 PrintError("failed ZCombSeeImage");
366 }
367 // end Rik's mask-from-focus hacking (for the moment...)
368
369 TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, im->width);
370 TIFFSetField(tif, TIFFTAG_IMAGELENGTH, im->height);
371 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
372 TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
373
374 // Thomas Rauscher, Add for 32 bit (float) support
375 //
376 // TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, im->bitsPerPixel < 48 ? 8 : 16 );
377 // TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, im->bitsPerPixel == 24 || im->bitsPerPixel == 48 ? 3 : 4);
378
379 switch (im->bitsPerPixel)
380 {
381 case 24:
382 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
383 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
384 break;
385 case 32:
386 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
387 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 4);
388 break;
389 case 48:
390 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 16);
391 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
392 break;
393 case 64:
394 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 16);
395 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 4);
396 break;
397 case 96:
398 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32);
399 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
400 TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP);
401 break;
402 case 128:
403 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32);
404 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 4);
405 TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP);
406 break;
407 }
408 TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS);
409
410
411 //"1" indicates that The 0th row represents the visual top of the image,
412 //and the 0th column represents the visual left-hand side.
413 TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
414
415
416 //TIFFTAG_ROWSPERSTRIP indicates the number of rows per "strip" of TIFF data. The original PTStitcher
417 //set this value to the panorama height whch meant that the entire image
418 //was contained in one strip. This is not only explicitly discouraged by the
419 //TIFF specification ("Use of a single strip is not recommended. Choose RowsPerStrip
420 //such that each strip is about 8K bytes, even if the data is not compressed,
421 //since it makes buffering simpler for readers. The 8K value is fairly
422 //arbitrary, but seems to work well."), but is also makes it impossible
423 //for programs to read the output from Pano Tools to perform random
424 //access on the data which leads to unnecessarily inefficient approaches to
425 //manipulating these images).
426 //
427 //In practice, most panoramas generated these days (Feb 2006) contain more than
428 //2000 pixels per row (equal to 8KB mentioned above), so it is easiest to
429 //hard-code this value to one, which also enables complete random access to
430 //the output files by subsequent blending/processing applications
431
432
433 TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, 1);
434
435 if (crop_info != NULL)
436 setCropInformationInTiff(tif, crop_info);
437
438 bufsize = TIFFScanlineSize(tif);
439 if ((uint32_t)bufsize < im->bytesPerLine)
440 bufsize = im->bytesPerLine;
441 buf = (uint8_t *) malloc(bufsize);
442 if (buf == NULL)
443 {
444 PrintError("Not enough memory");
445 return -1;
446 }
447
448 for (y = 0; (uint32_t) y < im->height; y++)
449 {
450 memcpy(buf, *(im->data) + y * im->bytesPerLine,
451 (size_t) im->bytesPerLine);
452 ARGBtoRGBA(buf, im->width, im->bitsPerPixel);
453 TIFFWriteScanline(tif, buf, y, 1);
454 }
455 TIFFClose(tif);
456 free(buf);
457 return 0;
458
459 }
460
461
writeTIFF(Image * im,fullPath * sfile)462 int writeTIFF(Image * im, fullPath * sfile)
463 {
464 return writeCroppedTIFF(im, sfile, &(im->cropInformation));
465 }
466
467
468
RGBAtoARGB(uint8_t * buf,int width,int bitsPerPixel)469 void RGBAtoARGB(uint8_t * buf, int width, int bitsPerPixel)
470 {
471 int x;
472 switch (bitsPerPixel)
473 {
474 case 32:
475 {
476 uint8_t pix;
477 for (x = 0; x < width; x++, buf += 4)
478 {
479 pix = buf[3];
480 buf[3] = buf[2];
481 buf[2] = buf[1];
482 buf[1] = buf[0];
483 buf[0] = pix;
484 }
485 }
486 break;
487 case 64:
488 {
489 uint16_t *bufs = (uint16_t *) buf, pix;
490 for (x = 0; x < width; x++, bufs += 4)
491 {
492 pix = bufs[3];
493 bufs[3] = bufs[2];
494 bufs[2] = bufs[1];
495 bufs[1] = bufs[0];
496 bufs[0] = pix;
497 }
498 }
499 break;
500 case 128:
501 {
502 float *bufs = (float *) buf, pix;
503 for (x = 0; x < width; x++, bufs += 4)
504 {
505 pix = bufs[3];
506 bufs[3] = bufs[2];
507 bufs[2] = bufs[1];
508 bufs[1] = bufs[0];
509 bufs[0] = pix;
510 }
511 }
512 break;
513 }
514 }
515
ARGBtoRGBA(uint8_t * buf,int width,int bitsPerPixel)516 void ARGBtoRGBA(uint8_t * buf, int width, int bitsPerPixel)
517 {
518 int x;
519 switch (bitsPerPixel)
520 {
521 case 32:
522 {
523 uint8_t pix;
524 for (x = 0; x < width; x++, buf += 4)
525 {
526 pix = buf[0];
527 buf[0] = buf[1];
528 buf[1] = buf[2];
529 buf[2] = buf[3];
530 buf[3] = pix;
531 }
532 }
533 break;
534 case 64:
535 {
536 uint16_t *bufs = (uint16_t *) buf, pix;
537 for (x = 0; x < width; x++, bufs += 4)
538 {
539 pix = bufs[0];
540 bufs[0] = bufs[1];
541 bufs[1] = bufs[2];
542 bufs[2] = bufs[3];
543 bufs[3] = pix;
544 }
545 }
546 break;
547 case 128:
548 {
549 float *bufs = (float *) buf, pix;
550 for (x = 0; x < width; x++, bufs += 4)
551 {
552 pix = bufs[0];
553 bufs[0] = bufs[1];
554 bufs[1] = bufs[2];
555 bufs[2] = bufs[3];
556 bufs[3] = pix;
557 }
558 }
559 break;
560 }
561 }
562
563
564 //////////////////////////////////////////////////////////////////////
565 // NEW tiff routines
566 //////////////////////////////////////////////////////////////////////
567
568
569
panoTiffGetCropInformation(pano_Tiff * file)570 int panoTiffGetCropInformation(pano_Tiff * file)
571 {
572 /*
573 Read the crop information of a TIFF file
574
575 Cropped TIFFs have the following properties:
576
577 * Their image width and length is the size of the cropped region
578 * The full size of the image is in PIXAR_IMAGEFULLWIDTH and PIXAR_IMAGEFULLHEIGHT
579 * If these 2 records do not exist then assume it is uncropped
580
581
582 */
583
584 float x_position, x_resolution, y_position, y_resolution;
585 pano_CropInfo *c;
586
587 assert(file != NULL);
588 assert(file->tiff != NULL);
589
590 c = &(file->metadata.cropInfo);
591 c->croppedWidth = 0;
592
593 file->metadata.isCropped = FALSE;
594
595 if (TIFFGetField(file->tiff, TIFFTAG_IMAGEWIDTH, &(c->croppedWidth)) == 0
596 || TIFFGetField(file->tiff, TIFFTAG_IMAGELENGTH,
597 &(c->croppedHeight)) == 0) {
598 PrintError("Error reading file size from TIFF");
599 return FALSE;
600 }
601
602
603
604 if (TIFFGetField(file->tiff, TIFFTAG_XPOSITION, &x_position) != 0) {
605 // This means this is a cropped image
606
607 file->metadata.isCropped = TRUE;
608
609
610
611 // we should get a x resoltion, y position and yresolution, if not,
612 // we will issue an error, as it can be cropped on just "one dimension"
613
614 if (TIFFGetField(file->tiff, TIFFTAG_XRESOLUTION, &x_resolution) == 0) {
615 PrintError("Cropped Image contains XPosition but not XResoulion tag. "
616 "Report to developers if you think this is a bug");
617 return FALSE;
618 }
619 if (TIFFGetField(file->tiff, TIFFTAG_YRESOLUTION, &y_resolution) == 0) {
620 PrintError("Cropped image contains XPosition and YPosition, but it does not contain Y Resolution. "
621 "Report to developers you think this is a bug");
622 return FALSE;
623 }
624
625 if (TIFFGetField(file->tiff, TIFFTAG_YPOSITION, &y_position) == 0) {
626 PrintError("Cropped image contains XPosition but not YPosition. "
627 "Report to developers you think this is a bug");
628 return FALSE;
629 }
630
631 //offset in pixels of "cropped" image from top left corner of
632 //full image (rounded to nearest integer)
633 // The position of the file is given in "real" dimensions, we have to rescale them
634
635 c->xOffset = (uint32) ((x_position * x_resolution) + 0.49);
636 c->yOffset = (uint32) ((y_position * y_resolution) + 0.49);
637
638 // One problem is that some images do not contain FULL size. if they don't have
639 // then assume it
640
641 if (TIFFGetField(file->tiff, TIFFTAG_PIXAR_IMAGEFULLWIDTH, &(c->fullWidth)) == 0)
642 c->fullWidth = c->xOffset + c->croppedWidth;
643
644 if (TIFFGetField(file->tiff, TIFFTAG_PIXAR_IMAGEFULLLENGTH, &(c->fullHeight)) == 0)
645 c->fullHeight = c->yOffset + c->croppedHeight;
646 } else {
647
648 // Not cropped, then initilize fields accordingly
649
650 x_position = 0;
651 x_resolution = 0;
652 y_position = 0;
653 y_resolution = 0;
654 c->xOffset = 0;
655 c->yOffset = 0;
656 c->fullHeight = c->croppedHeight;
657 c->fullWidth = c->croppedWidth;
658
659 }
660
661
662 #if 0
663 // used for debugging
664 printf("%dx%d @ %d,%d", c->croppedWidth, c->croppedHeight, c->xOffset, c->yOffset);
665
666 printf("get 3 width %d length %d\n", (int) c->croppedWidth,
667 (int) c->croppedHeight);
668 printf("get 3 full %d length %d\n", (int) c->fullWidth,
669 (int) c->fullHeight);
670 printf("cropped %d\n", (int) file->metadata.isCropped);
671 #endif
672
673 return TRUE;
674
675 }
676
677 // checks if an "absolute" row is inside the ROI
panoTiffRowInsideROI(pano_Tiff * image,int row)678 int panoTiffRowInsideROI(pano_Tiff * image, int row)
679 {
680 // We are in the ROI if the row is bigger than the yoffset
681 // and the row is less or equal to the offset + height
682
683
684 assert(image != NULL);
685 assert(row >= 0);
686
687 return panoROIRowInside(&(image->metadata.cropInfo), row);
688
689
690 }
691
692
693
panoTiffIsCropped(pano_Tiff * file)694 int panoTiffIsCropped(pano_Tiff * file)
695 {
696 return file->metadata.isCropped;
697 }
698
panoTiffBytesPerLine(pano_Tiff * file)699 int panoTiffBytesPerLine(pano_Tiff * file)
700 {
701 return file->metadata.bytesPerLine;
702 }
703
panoTiffSamplesPerPixel(pano_Tiff * file)704 int panoTiffSamplesPerPixel(pano_Tiff * file)
705 {
706 return file->metadata.samplesPerPixel;
707 }
708
709
710
panoTiffBitsPerPixel(pano_Tiff * file)711 int panoTiffBitsPerPixel(pano_Tiff * file)
712 {
713 return file->metadata.bitsPerPixel;
714 }
715
panoTiffBytesPerPixel(pano_Tiff * file)716 int panoTiffBytesPerPixel(pano_Tiff * file)
717 {
718 return file->metadata.bytesPerPixel;
719 }
720
panoTiffImageHeight(pano_Tiff * file)721 int panoTiffImageHeight(pano_Tiff * file)
722 {
723 return file->metadata.imageHeight;
724 }
725
panoTiffImageWidth(pano_Tiff * file)726 int panoTiffImageWidth(pano_Tiff * file)
727 {
728 return file->metadata.imageWidth;
729 }
730
panoTiffXOffset(pano_Tiff * file)731 int panoTiffXOffset(pano_Tiff * file)
732 {
733 return file->metadata.cropInfo.xOffset;
734 }
735
panoTiffYOffset(pano_Tiff * file)736 int panoTiffYOffset(pano_Tiff * file)
737 {
738 return file->metadata.cropInfo.yOffset;
739 }
740
panoTiffImageMetadata(pano_Tiff * file)741 pano_ImageMetadata *panoTiffImageMetadata(pano_Tiff * file)
742 {
743 return &file->metadata;
744 }
745
panoTiffFullImageWidth(pano_Tiff * file)746 int panoTiffFullImageWidth(pano_Tiff * file)
747 {
748 return file->metadata.cropInfo.fullWidth;
749 }
750
panoTiffFullImageHeight(pano_Tiff * file)751 int panoTiffFullImageHeight(pano_Tiff * file)
752 {
753 return file->metadata.cropInfo.fullHeight;
754 }
755
756
757
758
759
760 // Read an "absolute" row relative to the cropped area of the TIFF
panoTiffReadScanLineFullSize(pano_Tiff * file,void * buffer,int row)761 int panoTiffReadScanLineFullSize(pano_Tiff * file, void *buffer, int row)
762 {
763 // Reads a scan line only if inside ROI, otherwise it only "zeroes" data
764 int bytesPerLine;
765 int bytesPerPixel;
766
767 assert(file != NULL);
768
769 if (row > panoTiffFullImageHeight(file)) {
770 PrintError("Trying to read row %d beyond end of file", row);
771 return FALSE;
772 }
773 bytesPerPixel = panoTiffBytesPerPixel(file);
774
775 bytesPerLine = panoTiffFullImageWidth(file) * bytesPerPixel;
776
777 //printf("Bytes per line %d %d\n", bytesPerLine, panoTiffFullImageWidth(file));
778
779 assert(panoTiffIsCropped(file) ||
780 panoTiffFullImageWidth(file) == panoTiffImageWidth(file));
781
782
783 bzero(buffer, bytesPerLine);
784
785 if (panoTiffRowInsideROI(file, row)) {
786 if (TIFFReadScanline
787 (file->tiff, (uint8_t *)(buffer) + panoTiffXOffset(file) * bytesPerPixel,
788 row - panoTiffYOffset(file), 0) != 1) {
789 PrintError("Error reading row %d in tiff file", row);
790 return FALSE;
791 }
792 }
793 return TRUE;
794 }
795
panoTiffWriteScanLineFullSize(pano_Tiff * file,void * buffer,int row)796 int panoTiffWriteScanLineFullSize(pano_Tiff * file, void *buffer, int row)
797 {
798 // Reads a scan line only if inside ROI, otherwise it only "zeroes" data
799 int bytesPerPixel;
800
801 assert(file != NULL);
802
803 if (row > panoTiffFullImageHeight(file)) {
804 PrintError("Trying to read row %d beyond end of file", row);
805 return FALSE;
806 }
807 bytesPerPixel = panoTiffBytesPerPixel(file);
808
809 assert(panoTiffIsCropped(file) ||
810 panoTiffFullImageWidth(file) == panoTiffImageWidth(file));
811
812
813 if (panoTiffRowInsideROI(file, row)) {
814 if (TIFFWriteScanline
815 (file->tiff, (uint8_t *)(buffer) + panoTiffXOffset(file) * bytesPerPixel,
816 row - panoTiffYOffset(file), 0) != 1) {
817 PrintError("Error writing row %d in tiff file", row);
818 return FALSE;
819 }
820 }
821 return TRUE;
822 }
823
824
825
panoTiffSetCropInformation(pano_Tiff * file)826 int panoTiffSetCropInformation(pano_Tiff * file)
827 {
828 pano_CropInfo *cropInfo;
829 pano_ImageMetadata *metadata;
830 TIFF *tiffFile;
831 int result;
832
833 assert(file != NULL);
834
835 tiffFile = file->tiff;
836 assert(tiffFile != NULL);
837 metadata = &(file->metadata);
838 cropInfo = &(metadata->cropInfo);
839
840
841 if (!panoTiffIsCropped(file))
842 return TRUE;
843 //MRDL: Photoshop sometimes writes out files with a TIFFTAG_XRESOLUTION of 0.
844 //If input files are from Photoshop, this values propogates from input
845 //file to metadata, and can mess up setting of XPOSITION here...
846 if (metadata->xPixelsPerResolution == 0 || metadata->yPixelsPerResolution == 0)
847 {
848 metadata->xPixelsPerResolution = PANO_DEFAULT_PIXELS_PER_RESOLUTION;
849 metadata->yPixelsPerResolution = PANO_DEFAULT_PIXELS_PER_RESOLUTION;
850 }
851
852 //The X offset in ResolutionUnits of the left side of the image, with
853 //respect to the left side of the page.
854 //The Y offset in ResolutionUnits of the top of the image, with
855 //respect to the top of the page.
856
857 result =
858 TIFFSetField(tiffFile, TIFFTAG_XPOSITION,
859 (float) cropInfo->xOffset /
860 metadata->xPixelsPerResolution)
861 && TIFFSetField(tiffFile, TIFFTAG_YPOSITION,
862 (float) cropInfo->yOffset /
863 metadata->yPixelsPerResolution);
864
865 //The number of pixels per ResolutionUnit in the ImageWidth
866 //The number of pixels per ResolutionUnit in the ImageLength (height)
867 result = result &&
868 TIFFSetField(tiffFile, TIFFTAG_XRESOLUTION,
869 metadata->xPixelsPerResolution)
870 && TIFFSetField(tiffFile, TIFFTAG_YRESOLUTION,
871 metadata->yPixelsPerResolution);
872
873 //The size of the picture represented by an image. This
874 //is required so that the computation of pixel offset using XPOSITION/YPOSITION and
875 //XRESOLUTION/YRESOLUTION is valid (See tag description for XPOSITION/YPOSITION).
876 result = result &&
877 TIFFSetField(tiffFile, TIFFTAG_RESOLUTIONUNIT,
878 metadata->resolutionUnits);
879
880 // TIFFTAG_PIXAR_IMAGEFULLWIDTH and TIFFTAG_PIXAR_IMAGEFULLLENGTH
881 // are set when an image has been cropped out of a larger image.
882 // They reflect the size of the original uncropped image.
883 // The TIFFTAG_XPOSITION and TIFFTAG_YPOSITION can be used
884 // to determine the position of the smaller image in the larger one.
885 result = result &&
886 TIFFSetField(tiffFile, TIFFTAG_PIXAR_IMAGEFULLWIDTH,
887 cropInfo->fullWidth)
888 && TIFFSetField(tiffFile, TIFFTAG_PIXAR_IMAGEFULLLENGTH,
889 cropInfo->fullHeight);
890 if (!result) {
891 PrintError("Unable to set metadata of output tiff file");
892 return FALSE;
893 }
894 return result;
895 }
896
panoTiffGetString(pano_Tiff * tiffFile,ttag_t tiffTag)897 char *panoTiffGetString(pano_Tiff *tiffFile, ttag_t tiffTag)
898 {
899 char *temp;
900 char *returnValue;
901 if (TIFFGetField(tiffFile->tiff, tiffTag, &temp) == 0) {
902 // If the tag does not exist just return
903 return NULL;
904 }
905 else {
906 // Allocate its memory
907 returnValue = calloc(strlen(temp) + 1, 1);
908
909 if (returnValue == NULL)
910 return NULL;
911 // copy to the new location and return
912 strcpy(returnValue, temp);
913 return returnValue;
914 }
915 }
916
panoTiffGetImageProperties(pano_Tiff * tiff)917 int panoTiffGetImageProperties(pano_Tiff * tiff)
918 {
919 /*
920 Retrieve the properties of the image that we need to keep
921 */
922
923
924 TIFF *tiffFile;
925 pano_ImageMetadata *metadata;
926 int result;
927 void *ptr;
928
929 assert(tiff != NULL);
930
931 tiffFile = tiff->tiff;
932
933 metadata = &tiff->metadata;
934
935 assert(tiffFile != NULL);
936
937 //printf("get\n");
938
939 if (!panoTiffGetCropInformation(tiff)) {
940 goto error;
941 }
942
943
944 // These are tags that are expected to be present
945
946 result = TIFFGetField(tiffFile, TIFFTAG_IMAGEWIDTH, &metadata->imageWidth)
947 && TIFFGetField(tiffFile, TIFFTAG_IMAGELENGTH, &metadata->imageHeight)
948 && TIFFGetField(tiffFile, TIFFTAG_BITSPERSAMPLE,
949 &metadata->bitsPerSample)
950 && TIFFGetField(tiffFile, TIFFTAG_SAMPLESPERPIXEL,
951 &metadata->samplesPerPixel)
952 && TIFFGetField(tiffFile, TIFFTAG_COMPRESSION,
953 &metadata->compression.type)
954 && TIFFGetField(tiffFile, TIFFTAG_ROWSPERSTRIP,
955 &metadata->rowsPerStrip);
956
957
958 if (!result)
959 goto error;
960
961 if (metadata->compression.type == COMPRESSION_LZW) {
962
963 // set default compression predictor
964 metadata->compression.predictor = 2; //horizontal differencing
965
966 // unleess it comes in the file
967 TIFFGetField(tiffFile, TIFFTAG_PREDICTOR,
968 &(metadata->compression.predictor));
969 }
970
971 metadata->bytesPerLine = TIFFScanlineSize(tiffFile);
972 if (metadata->bytesPerLine <= 0) {
973 PrintError("File did not include proper bytes per line information.");
974 return 0;
975 }
976
977 // These are optional tags
978
979
980
981 if (TIFFGetField(tiffFile, TIFFTAG_ICCPROFILE, &(metadata->iccProfile.size),
982 &ptr)) {
983
984 if ((metadata->iccProfile.data = calloc(metadata->iccProfile.size, 1)) == NULL) {
985 PrintError("Not enough memory");
986 return 0;
987 }
988 memcpy(metadata->iccProfile.data, ptr, metadata->iccProfile.size);
989 }
990
991 tiff->metadata.copyright = panoTiffGetString(tiff, TIFFTAG_COPYRIGHT);
992 tiff->metadata.datetime = panoTiffGetString(tiff, TIFFTAG_DATETIME);
993 tiff->metadata.imageDescription = panoTiffGetString(tiff, TIFFTAG_IMAGEDESCRIPTION);
994 tiff->metadata.artist = panoTiffGetString(tiff, TIFFTAG_ARTIST);
995
996 TIFFGetField(tiffFile, TIFFTAG_PAGENUMBER, &metadata->imageNumber, &metadata->imageTotalNumber);
997
998 //printf("...........................REad and allocate ICC Profile %d %x\n",
999 //(int)(metadata->iccProfile.size), (int)(metadata->iccProfile.data));
1000
1001 if (TIFFGetField
1002 (tiffFile, TIFFTAG_RESOLUTIONUNIT, &(metadata->resolutionUnits)) == 0)
1003 metadata->resolutionUnits = PANO_DEFAULT_TIFF_RESOLUTION_UNITS;
1004
1005 if (TIFFGetField
1006 (tiffFile, TIFFTAG_XRESOLUTION,
1007 &(metadata->xPixelsPerResolution)) == 0)
1008 metadata->xPixelsPerResolution =
1009 PANO_DEFAULT_PIXELS_PER_RESOLUTION;
1010
1011 if (TIFFGetField
1012 (tiffFile, TIFFTAG_YRESOLUTION,
1013 &(metadata->yPixelsPerResolution)) == 0)
1014 metadata->yPixelsPerResolution =
1015 PANO_DEFAULT_PIXELS_PER_RESOLUTION;
1016
1017 // Compute rest of the fields
1018
1019 // let us truly hope the size of a byte never changes :)
1020 metadata->bytesPerPixel =
1021 (metadata->samplesPerPixel * metadata->bitsPerSample) / 8;
1022 metadata->bitsPerPixel = metadata->bytesPerPixel * 8;
1023
1024 //printf("get2 bits per sample %d\n", metadata->bitsPerSample);
1025 // printf("get2 bits per pixel %d\n", metadata->bitsPerPixel);
1026 //printf("get2 bytes per pixel %d\n", metadata->bytesPerPixel);
1027
1028 return 1;
1029
1030 error:
1031 PrintError("Error retrieving metadata from TIFF file");
1032 return 0;
1033
1034 }
1035
panoTiffSetImageProperties(pano_Tiff * file)1036 int panoTiffSetImageProperties(pano_Tiff * file)
1037 {
1038 int returnValue = 1;
1039 TIFF *tiffFile;
1040 pano_ImageMetadata *metadata;
1041
1042 assert(file != NULL);
1043
1044 tiffFile = file->tiff;
1045 assert(tiffFile != NULL);
1046
1047 metadata = &(file->metadata);
1048
1049 assert(metadata != NULL);
1050
1051 // Each of the invocations below returns 1 if ok, 0 if error.
1052
1053 //printf("samples per pixel %d\n", (int) metadata->samplesPerPixel);
1054 //printf("samples width %d\n", (int) metadata->imageWidth);
1055 //printf("compression %d\n", (int) metadata->compression.type);
1056 returnValue =
1057 TIFFSetField(tiffFile, TIFFTAG_IMAGEWIDTH, metadata->imageWidth)
1058 && TIFFSetField(tiffFile, TIFFTAG_IMAGELENGTH, metadata->imageHeight)
1059 && TIFFSetField(tiffFile, TIFFTAG_BITSPERSAMPLE,
1060 metadata->bitsPerSample)
1061 && TIFFSetField(tiffFile, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)
1062 && TIFFSetField(tiffFile, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)
1063 && TIFFSetField(tiffFile, TIFFTAG_SAMPLESPERPIXEL,
1064 metadata->samplesPerPixel)
1065 && TIFFSetField(tiffFile, TIFFTAG_COMPRESSION,
1066 metadata->compression.type)
1067 && TIFFSetField(tiffFile, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT)
1068 && TIFFSetField(tiffFile, TIFFTAG_ROWSPERSTRIP,
1069 metadata->rowsPerStrip)
1070 && TIFFSetField(tiffFile, TIFFTAG_RESOLUTIONUNIT,
1071 metadata->resolutionUnits)
1072 && TIFFSetField(tiffFile, TIFFTAG_XRESOLUTION,
1073 metadata->xPixelsPerResolution)
1074 && TIFFSetField(tiffFile, TIFFTAG_YRESOLUTION,
1075 metadata->yPixelsPerResolution)
1076 && TIFFSetField(tiffFile, TIFFTAG_PAGENUMBER, metadata->imageNumber,
1077 metadata->imageTotalNumber);
1078
1079
1080
1081
1082 if (returnValue && metadata->bitsPerSample == 32)
1083 {
1084 // If it is 96 or 128 it is floatint point
1085 returnValue = TIFFSetField(tiffFile, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP);
1086 }
1087 // Take care of special cases
1088
1089 if (returnValue) {
1090
1091
1092 }
1093
1094 // Only set ICCprofile if size > 0
1095
1096 if (returnValue && metadata->iccProfile.size > 0) {
1097 returnValue =
1098 TIFFSetField(tiffFile, TIFFTAG_ICCPROFILE,
1099 (uint32)metadata->iccProfile.size,
1100 (void*)(metadata->iccProfile.data));
1101 //100, data);
1102 }
1103
1104 if (returnValue && metadata->compression.type == COMPRESSION_LZW) {
1105 returnValue =
1106 TIFFSetField(tiffFile, TIFFTAG_PREDICTOR,
1107 metadata->compression.predictor);
1108 }
1109
1110 // String fields
1111 //printf("TO set tricky fields\n");
1112
1113 if (returnValue && metadata->copyright != NULL)
1114 returnValue = TIFFSetField(tiffFile, TIFFTAG_COPYRIGHT, metadata->copyright);
1115
1116 if (returnValue && metadata->artist != NULL)
1117 returnValue = TIFFSetField(tiffFile, TIFFTAG_ARTIST, metadata->artist);
1118
1119 if (returnValue && metadata->datetime!=NULL)
1120 returnValue = TIFFSetField(tiffFile, TIFFTAG_DATETIME, metadata->datetime);
1121
1122 if (returnValue && metadata->imageDescription != NULL)
1123 returnValue = TIFFSetField(tiffFile, TIFFTAG_IMAGEDESCRIPTION, metadata->imageDescription);
1124
1125
1126 returnValue = returnValue &&
1127 TIFFSetField(tiffFile, TIFFTAG_SOFTWARE, "Created by Panotools version " VERSION);
1128
1129 // printf("TO set crop\n");
1130 if (returnValue && panoTiffIsCropped(file)) {
1131 //printf("TO set crop 2\n");
1132 returnValue = panoTiffSetCropInformation(file);
1133 }
1134
1135 return returnValue;
1136
1137 }
1138
1139
1140
panoTiffReadPlannar(Image * im,pano_Tiff * tif)1141 int panoTiffReadPlannar(Image * im, pano_Tiff * tif)
1142 {
1143 uint8_t *buf;
1144 uint32 row;
1145 short samplesPerPixel;
1146 int bytesRead;
1147 int bitsPerPixel;
1148
1149 samplesPerPixel = panoTiffSamplesPerPixel(tif);
1150
1151
1152 // We can't read more than 4 samples per pixel
1153 if (samplesPerPixel > 4 || samplesPerPixel < 3) {
1154 PrintError("We only support 3 or 4 samples per pixel in TIFF");
1155 return 0;
1156 }
1157 // REmember, we need the values of the TIFF,
1158 // not the values in the image struct
1159 // (which will might not be the same
1160 bytesRead = panoTiffBytesPerLine(tif);
1161 bitsPerPixel = panoTiffBitsPerPixel(tif);
1162
1163 // printf("9 Bytes per line %d (im %d) %d %d\n", panoTiffBytesPerLine(tif),
1164 // im->bytesPerLine, im->bitsPerPixel, panoTiffBitsPerPixel(tif));
1165 buf = calloc(bytesRead, 1);
1166 if (buf == NULL) {
1167 PrintError("Not enough memory");
1168 return 0;
1169 }
1170
1171 for (row = 0; row < im->height; row++) {
1172 if (TIFFReadScanline(tif->tiff, buf, row, 0) != 1) {
1173 PrintError("Error reading TIFF file");
1174 goto error;
1175 }
1176
1177 RGBAtoARGB(buf, im->width, bitsPerPixel);
1178
1179 memcpy(*(im->data) + row * im->bytesPerLine, buf,
1180 (size_t) bytesRead);
1181 }
1182
1183 // If we don't have an alpha channel we need to rebuild it
1184 if (samplesPerPixel == 3) {
1185 ThreeToFourBPP(im);
1186 }
1187 return 1;
1188
1189 error:
1190 free(buf);
1191 return 0;
1192 }
1193
1194
1195
1196
1197
1198
1199
panoTiffClose(pano_Tiff * file)1200 void panoTiffClose(pano_Tiff * file)
1201 {
1202 panoMetadataFree(&file->metadata);
1203 TIFFClose(file->tiff);
1204 free(file);
1205 }
1206
1207
1208 // create the tiff according to most of the needed data
panoTiffCreateGeneral(char * fileName,pano_ImageMetadata * metadata,int uncropped)1209 pano_Tiff *panoTiffCreateGeneral(char *fileName,
1210 pano_ImageMetadata * metadata, int uncropped)
1211 {
1212 pano_Tiff *panoTiff;
1213
1214 // Allocate the struct's memory
1215 if ((panoTiff = calloc(sizeof(pano_Tiff), 1)) == NULL) {
1216 PrintError("Not enough memory");
1217 return NULL;
1218 }
1219
1220 // Open file and retrieve metadata
1221 panoTiff->tiff = TIFFOpen(fileName, "w");
1222 if (panoTiff->tiff == NULL) {
1223 PrintError("Unable to create output file [%s]", fileName);
1224 free(panoTiff);
1225 return NULL;
1226 }
1227
1228 //printf("Copy metadata from %d\n", (int) metadata->cropInfo.fullWidth);
1229 if (!panoMetadataCopy(&panoTiff->metadata, metadata)) {
1230 panoTiffClose(panoTiff);
1231 return NULL;
1232 }
1233
1234 if (uncropped) {
1235 panoUnCropMetadata(&panoTiff->metadata);
1236 }
1237
1238 //printf("Copy metadata %d\n", (int) panoTiff->metadata.cropInfo.fullWidth);
1239 if (!panoTiffSetImageProperties(panoTiff)) {
1240 panoTiffClose(panoTiff);
1241 return NULL;
1242 }
1243 //printf("After set image properties\n");
1244
1245 // return value
1246 return panoTiff;
1247 }
1248
panoTiffCreateUnCropped(char * fileName,pano_ImageMetadata * metadata)1249 pano_Tiff *panoTiffCreateUnCropped(char *fileName,
1250 pano_ImageMetadata * metadata)
1251 {
1252 // If the file is uncropped it creates a cropped version
1253 return panoTiffCreateGeneral(fileName, metadata, TRUE);
1254 }
1255
1256
panoTiffCreate(char * fileName,pano_ImageMetadata * metadata)1257 pano_Tiff *panoTiffCreate(char *fileName, pano_ImageMetadata * metadata)
1258 {
1259 return panoTiffCreateGeneral(fileName, metadata, FALSE);
1260 }
1261
panoTiffOpen(char * fileName)1262 pano_Tiff *panoTiffOpen(char *fileName)
1263 {
1264 pano_Tiff *panoTiff;
1265
1266
1267 // Allocate the struct's memory
1268 if ((panoTiff = calloc(sizeof(*panoTiff), 1)) == NULL) {
1269 PrintError("Not enough memory");
1270 return NULL;
1271 }
1272
1273 // Open file and retrieve metadata
1274 panoTiff->tiff = TIFFOpen(fileName, "r");
1275
1276 if (panoTiff->tiff == NULL) {
1277 PrintError("Unable to open file %s", fileName);
1278 goto error;
1279 }
1280
1281 if (!panoTiffGetImageProperties(panoTiff)) {
1282 TIFFClose(panoTiff->tiff);
1283 PrintError("Unable to get properties of tiff file %s", fileName);
1284 goto error;
1285 }
1286 // return value
1287 return panoTiff;
1288
1289 error:
1290 free(panoTiff);
1291 return NULL;
1292
1293 }
1294
1295
1296 // image is allocated, but not image data
1297
panoTiffReadData(Image * im,pano_Tiff * tif)1298 int panoTiffReadData(Image * im, pano_Tiff * tif)
1299 {
1300 short tPhotoMetric, config;
1301
1302 assert(im != NULL);
1303 // Assume that it is unallocated
1304 assert(im->data == NULL);
1305
1306 assert(tif != NULL);
1307
1308 TIFFGetField(tif->tiff, TIFFTAG_PHOTOMETRIC, &tPhotoMetric);
1309 TIFFGetField(tif->tiff, TIFFTAG_PLANARCONFIG, &config);
1310
1311
1312 // Set general parameters. The width and height are of the data
1313
1314 if ((im->data = (unsigned char **) mymalloc( im->dataSize) ) == NULL) {
1315 PrintError("Not enough memory");
1316 return 0;
1317 }
1318
1319 if (tPhotoMetric == PHOTOMETRIC_RGB && config == PLANARCONFIG_CONTIG) {
1320 if (!panoTiffReadPlannar(im, tif))
1321 goto error;
1322 return TRUE;
1323 }
1324
1325 // I changed the stopOnError to 1 so the function reports an error
1326 // as soon as it happens
1327
1328 if (TIFFReadRGBAImage(tif->tiff, (uint32) panoTiffImageWidth(tif),
1329 (uint32) panoTiffImageHeight(tif),
1330 (uint32 *) * (im->data), 1)) {
1331 // Convert agbr to argb; flip image vertically
1332
1333 unsigned char *cline, *ct, *cb;
1334 int h2 = im->height / 2, y;
1335 // Only do the conversion once
1336 size_t localBytesPerLine = im->bytesPerLine;
1337
1338 cline = (unsigned char *) calloc(localBytesPerLine, 1);
1339 if (cline == NULL)
1340 {
1341 PrintError("Not enough memory");
1342 goto error;
1343 }
1344
1345 ct = *im->data;
1346 cb = *im->data + (im->height - 1) * im->bytesPerLine;
1347
1348 for (y = 0; y < h2;
1349 y++, ct += im->bytesPerLine, cb -= im->bytesPerLine) {
1350 RGBAtoARGB(ct, im->width, im->bitsPerPixel);
1351 RGBAtoARGB(cb, im->width, im->bitsPerPixel);
1352 memcpy(cline, ct, localBytesPerLine);
1353 memcpy(ct, cb, localBytesPerLine);
1354 memcpy(cb, cline, localBytesPerLine);
1355 }
1356 if (im->height != 2 * h2) { // odd number of scanlines
1357 RGBAtoARGB(*im->data + y * im->bytesPerLine, im->width,
1358 im->bitsPerPixel);
1359 }
1360 free(cline);
1361 }
1362 else {
1363 PrintError("Could not read tiff-data");
1364 goto error;
1365 }
1366 return 1;
1367
1368 error:
1369 myfree((void**)im->data);
1370 im->data = NULL;
1371 return 0;
1372 }
1373
1374
1375
1376
1377 // Output an image to a file
panoTiffWrite(Image * im,char * fileName)1378 int panoTiffWrite(Image * im, char *fileName)
1379 {
1380 pano_Tiff *tif = NULL;
1381 void *buf = 0;
1382 unsigned int y;
1383 size_t bufsize;
1384
1385 // Make sure that the metadata is there...
1386 assert(im->metadata.imageWidth != 0 &&
1387 im->metadata.imageHeight != 0);
1388
1389
1390 // first verify the value of some of the metadata fields
1391
1392 assert(im->bitsPerPixel != 0);
1393
1394 switch (im->bitsPerPixel) {
1395 case 96:
1396 case 24:
1397 case 48:
1398 im->metadata.samplesPerPixel = 3;
1399 break;
1400 case 32:
1401 case 64:
1402 case 128:
1403 im->metadata.samplesPerPixel = 4;
1404 break;
1405 default:
1406 PrintError("Illegal value for bits per pixel in TIFF image to write %s", fileName);
1407 return FALSE;
1408 }
1409 im->metadata.bitsPerSample = (uint16_t)im->bitsPerPixel/im->metadata.samplesPerPixel;
1410
1411
1412 tif = panoTiffCreate(fileName, &im->metadata);
1413
1414
1415 if (!tif) {
1416 PrintError("Could not create TIFF-file");
1417 return 0;
1418 }
1419
1420 // Rik's mask-from-focus hacking
1421 if (ZCombSeeImage(im, fileName)) {
1422 PrintError("failed ZCombSeeImage");
1423 }
1424 // end Rik's mask-from-focus hacking (for the moment...)
1425
1426 bufsize = TIFFScanlineSize(tif->tiff);
1427
1428 if ((uint32_t)bufsize < im->bytesPerLine)
1429 bufsize = im->bytesPerLine;
1430
1431 buf = calloc(bufsize, 1);
1432 if (buf == NULL) {
1433 PrintError("Not enough memory");
1434 goto error;
1435 }
1436
1437 for (y = 0; (uint32_t) y < im->height; y++) {
1438 // printf("Here 1 buffsize %d bytesperline %d width %d\n", bufsize, im->bytesPerLine, im->width);
1439 memcpy(buf, *(im->data) + y * im->bytesPerLine,
1440 (size_t) im->bytesPerLine);
1441 ARGBtoRGBA(buf, im->width, im->bitsPerPixel);
1442 if (TIFFWriteScanline(tif->tiff, buf, y, 0) != 1) {
1443 PrintError("Unable to write to TIFF");
1444 goto error;
1445 }
1446 }
1447 panoTiffClose(tif);
1448 free(buf);
1449 return 1;
1450
1451 error:
1452 if (buf != NULL)
1453 free(buf);
1454
1455 if (tif != NULL)
1456 panoTiffClose(tif);
1457
1458 return 0;
1459 }
1460
1461
panoUpdateMetadataFromTiff(Image * im,pano_Tiff * tiff)1462 int panoUpdateMetadataFromTiff(Image *im, pano_Tiff *tiff)
1463 {
1464 int bytesPerLine;
1465
1466 if (!panoMetadataCopy(&im->metadata, &tiff->metadata)) {
1467 return FALSE;
1468 }
1469 // printf("IMage width %d %d\n",im->width, panoTiffImageWidth(tiff));
1470 //printf("Bites per pixel %d\n",(int)im->bitsPerPixel);
1471
1472 im->width = panoTiffImageWidth(tiff);
1473 im->height = panoTiffImageHeight(tiff);
1474
1475 // We will allocate enough memory for the 3 samples + Alpha Channel
1476 // Regardless of the actual number of samples in the image
1477
1478 im->bytesPerLine = panoTiffBytesPerLine(tiff);
1479 im->bitsPerPixel = panoTiffBitsPerPixel(tiff);
1480
1481 // Even if we only find 3 samples we will end with 4
1482 switch (panoTiffSamplesPerPixel(tiff))
1483 {
1484 case 3:
1485 bytesPerLine = panoTiffBytesPerLine(tiff) * 4 / 3;
1486
1487 im->metadata.bytesPerLine = bytesPerLine;
1488
1489 im->metadata.bitsPerPixel = im->bitsPerPixel * 4/ 3;
1490 im->metadata.samplesPerPixel = 4;
1491 im->metadata.bytesPerPixel =
1492 (4 * im->metadata.bitsPerSample) / 8;
1493 break;
1494 case 4:
1495 bytesPerLine = panoTiffBytesPerLine(tiff);
1496 break;
1497 default:
1498 PrintError("We only support 3 or 4 samples per pixel");
1499 return 0;
1500 }
1501
1502 im->dataSize = bytesPerLine * im->height;
1503
1504 // compute how much space we need
1505 //printf("Data size %d bytesperline %d width %d height %d\n",
1506 //(int)im->dataSize,
1507 // (int)im->bytesPerLine, (int)im->width,(int)im->height
1508 //);
1509
1510 return TRUE;
1511 }
1512
1513
1514 /*
1515 Read a TIFF file and place it in a Image data structure
1516 Read also the metadata including crop information
1517 */
1518
panoTiffRead(Image * im,char * fileName)1519 int panoTiffRead(Image * im, char *fileName)
1520 {
1521 pano_Tiff *tiff = NULL;
1522 int result = FALSE;
1523
1524 SetImageDefaults(im);
1525
1526 //printf("Reading tiff\n");
1527 if ((tiff = panoTiffOpen(fileName)) == NULL) {
1528 PrintError("Could not open tiff-file %s", fileName);
1529 goto end;
1530 }
1531 //printf("to update metadata tiff\n");
1532
1533 // Update metadata in the image
1534 if (!panoUpdateMetadataFromTiff(im, tiff)) {
1535 goto end;
1536 }
1537
1538 //printf("to read data\n");
1539
1540 if (!panoTiffReadData(im, tiff)) {
1541 PrintError("Unable to read data from TIFF file %s", fileName);
1542 goto end;
1543 }
1544
1545 //Store name of TIFF file
1546 snprintf(im->name, MAX_PATH_LENGTH, "%s", fileName);
1547
1548 //printf("after update metadata tiff\n");
1549 result = TRUE;
1550
1551 end:
1552
1553 //printf("ENd of Reading tiff\n");
1554
1555 //panoDumpMetadata(&im->metadata,"Read metadata");
1556
1557 if (tiff != NULL)
1558 panoTiffClose(tiff);
1559 return result;
1560 }
1561
1562
1563 // THis functions clens any memory currently used by the Image
1564 // data structure
panoImageDispose(Image * im)1565 void panoImageDispose(Image *im)
1566 {
1567 if (im != NULL) {
1568
1569 // Release metadata
1570 panoMetadataFree(&(im->metadata));
1571
1572 // Release image data
1573 if (im->data != NULL) {
1574 myfree((void **) im->data);
1575 im->data = NULL;
1576 }
1577 }
1578 }
1579
1580
panoTiffErrorHandler(const char * module,const char * fmt,va_list ap)1581 void panoTiffErrorHandler(const char *module, const char *fmt, va_list ap)
1582 {
1583 PrintError("Error in TIFF file (%s) ", module);
1584 PrintError((char *) fmt, ap);
1585 }
1586
panoTiffSetErrorHandler(void)1587 void panoTiffSetErrorHandler(void)
1588 {
1589 // TODO
1590 // This routines need to be properly implemented. Currently it does nothing
1591
1592 //MRDL: Reluctantly commented these out...the calls to TIFFSetWarningHandler and
1593 //TIFFSetErrorHandler cause to GCC to abort, with a series of errors like this:
1594 //../../../LibTiff/tiff-v3.6.1/libtiff/libtiff.a(tif_unix.o)(.text+0x11a): In function `TIFFOpen':
1595 //../../../libtiff/tiff-v3.6.1/libtiff/../libtiff/tif_unix.c:144: multiple definition of `TIFFOpen'
1596 //../libpano12.a(dyces00121.o)(.text+0x0): first defined here
1597 // Make sure we have a tiff error handler
1598
1599 // Disable warnings in TIFF library
1600
1601 TIFFSetWarningHandler(NULL);
1602
1603 #ifdef TOBEIMPLEMENTED
1604 TIFFSetWarningHandler(panoTiffErrorHandler);
1605 TIFFSetErrorHandler(panoTiffErrorHandler);
1606
1607 #endif
1608 }
1609
1610
1611 /* panotools is only able to operate on images that have the same size and same depth.
1612 if the colour profiles exist they should be the same too
1613
1614 Some checksk are optional
1615
1616 */
panoTiffVerifyAreCompatible(fullPath * tiffFiles,int numberImages,int optionalCheck)1617 int panoTiffVerifyAreCompatible(fullPath * tiffFiles, int numberImages,
1618 int optionalCheck)
1619 {
1620 int currentImage;
1621 pano_Tiff *firstFile;
1622 pano_Tiff *otherFile;
1623
1624 pano_CropInfo *firstCropInfo = NULL;
1625 pano_CropInfo *otherCropInfo = NULL;
1626
1627 assert(tiffFiles != NULL);
1628
1629 assert(numberImages > 1);
1630
1631
1632 panoTiffSetErrorHandler();
1633
1634 // Open TIFFs
1635
1636 firstFile = panoTiffOpen(tiffFiles[0].name);
1637
1638 if (firstFile == NULL) {
1639 PrintError("Unable to read tiff file %s", tiffFiles[0].name);
1640 return FALSE;
1641 }
1642
1643 firstCropInfo = &firstFile->metadata.cropInfo;
1644
1645 // Compare the metadata of the current file with each of the other ones
1646 for (currentImage = 1; currentImage < numberImages; currentImage++) {
1647
1648 otherFile = panoTiffOpen(tiffFiles[currentImage].name);
1649 otherCropInfo = &otherFile->metadata.cropInfo;
1650
1651 if (otherFile == NULL) {
1652 PrintError("Unable to read tiff file %s",
1653 tiffFiles[currentImage].name);
1654 return FALSE;
1655 }
1656
1657
1658 // THey should have the same width
1659 if (panoTiffFullImageWidth(firstFile) !=
1660 panoTiffFullImageWidth(otherFile)) {
1661 PrintError
1662 ("Image 0 and %d do not have the same width: %d vs %d\n",
1663 currentImage, (int) firstCropInfo->fullWidth,
1664 (int) otherCropInfo->fullWidth);
1665 return FALSE;
1666 }
1667
1668 // THey should have the same height
1669 if (panoTiffFullImageHeight(firstFile) !=
1670 panoTiffFullImageHeight(otherFile)) {
1671 PrintError
1672 ("Image 0 and %d do not have the same length: %d vs %d\n",
1673 currentImage, (int) firstCropInfo->fullHeight,
1674 (int) otherCropInfo->fullHeight);
1675 return FALSE;
1676 }
1677
1678 // THey should have the same colour depth
1679 if (panoTiffBytesPerPixel(firstFile) !=
1680 panoTiffBytesPerPixel(otherFile)) {
1681 PrintError("Image 0 and %d do not have the same colour depth\n",
1682 currentImage);
1683 return FALSE;
1684 }
1685 //printf("compatible 1\n");
1686 // THey should have the same number of channels per pixel
1687 if (panoTiffSamplesPerPixel(firstFile) !=
1688 panoTiffSamplesPerPixel(otherFile)) {
1689 PrintError
1690 ("Image 0 and %d do not have the same number of channels per pixel\n",
1691 currentImage);
1692 return FALSE;
1693 }
1694
1695 if (optionalCheck) {
1696
1697 // Compare profiles
1698
1699 if (firstFile->metadata.iccProfile.size > 0) {
1700
1701 // They should be the same size and have the same contents
1702 if (firstFile->metadata.iccProfile.size !=
1703 otherFile->metadata.iccProfile.size
1704 || memcmp(firstFile->metadata.iccProfile.data,
1705 otherFile->metadata.iccProfile.data,
1706 firstFile->metadata.iccProfile.size) != 0) {
1707 PrintError
1708 ("Image 0 and %d have different colour profiles\n",
1709 currentImage);
1710 return FALSE;
1711 }
1712 }
1713 }
1714 panoTiffClose(otherFile);
1715
1716 } // for loop
1717
1718 panoTiffClose(firstFile);
1719 //printf("THe files are compatible\n");
1720
1721 return TRUE;
1722
1723 }
1724
1725 /**
1726 * Reads inputFile and "uncrops" the image by adding black space to pad
1727 * image to its full size, saving the result as outputFile. If an error
1728 * is encountered messageBuffer is filled with the message, and a non-zero
1729 * value is returned. If success, zero is returned
1730 */
panoTiffUnCrop(char * inputFile,char * outputFile,pano_cropping_parms * croppingParms)1731 int panoTiffUnCrop(char *inputFile, char *outputFile, pano_cropping_parms *croppingParms)
1732 {
1733
1734 pano_CropInfo *inputCropInfo = NULL;
1735 char *buffer = NULL;
1736 char *offsetInBuffer;
1737 int inputRow, outputRow;
1738 pano_Tiff *tiffInput = NULL;
1739 pano_Tiff *tiffOutput = NULL;
1740 pano_ImageMetadata *metadata = NULL;
1741
1742 if ((tiffInput = panoTiffOpen(inputFile)) == NULL) {
1743 PrintError("Unable to open input file");
1744 goto error;
1745 }
1746
1747 if (!panoTiffIsCropped(tiffInput)) {
1748 PrintError("Source image is not a cropped tiff");
1749 if (!croppingParms->forceProcessing)
1750 goto error;
1751 PrintError("Forced processing... continuing");
1752 }
1753
1754 inputCropInfo = &tiffInput->metadata.cropInfo;
1755
1756 if ((tiffOutput =
1757 panoTiffCreateUnCropped(outputFile, &tiffInput->metadata)) == NULL) {
1758 PrintError("Unable to create output file [%s]", outputFile);
1759 goto error;
1760 }
1761
1762 metadata = &tiffOutput->metadata;
1763 //printf("***Size of line %d\n", metadata->bytesPerLine);
1764
1765 // Allocate buffer for line
1766 buffer = calloc(metadata->bytesPerLine, 1);
1767
1768 if (buffer == NULL) {
1769 PrintError("Unable to allocate memory for IO buffer");
1770 goto error;
1771 }
1772
1773 inputRow = 0;
1774 // The crop data has to be placed inside the buffer according to the
1775 // cropinfo offset
1776
1777 offsetInBuffer =
1778 buffer + inputCropInfo->xOffset * metadata->bytesPerPixel;
1779
1780 assert(metadata->imageHeight > 0);
1781 // Read one line at a time and transfer to output file
1782 for (outputRow = 0; outputRow < (int) metadata->imageHeight; outputRow++) {
1783
1784 //fill empty buffer with empty space (zeros)
1785 bzero(buffer, metadata->bytesPerLine);
1786
1787 //if inside ROI then read from input file
1788 if (panoROIRowInside(inputCropInfo, outputRow)) {
1789
1790 if (TIFFReadScanline(tiffInput->tiff, offsetInBuffer, inputRow, 0)
1791 != 1) {
1792 PrintError("Unable to read scanline %d", inputRow);
1793 goto error;
1794 }
1795 inputRow++;
1796 }
1797
1798 //write buffer to outputfile
1799 if (TIFFWriteScanline(tiffOutput->tiff, buffer, outputRow, 0) != 1) {
1800 PrintError("Unable to write scanline %d", outputRow);
1801 goto error;
1802 }
1803
1804 }
1805
1806 //printf("Finished\n");
1807
1808 free(buffer);
1809 panoTiffClose(tiffInput);
1810 panoTiffClose(tiffOutput);
1811
1812 return 1;
1813
1814 error:
1815 // Error handler
1816 // Make sure we release any resources we have
1817
1818 if (buffer != NULL)
1819 free(buffer);
1820
1821 if (tiffOutput != NULL)
1822 panoTiffClose(tiffOutput);
1823
1824 if (tiffInput != NULL)
1825 panoTiffClose(tiffInput);
1826
1827
1828 return 0;
1829 }
1830
1831
1832
panoImageBoundingRectangleCompute(unsigned char * data,int width,int height,int bytesPerPixel,pano_CropInfo * cropInfo)1833 int panoImageBoundingRectangleCompute(unsigned char *data, int width, int height, int bytesPerPixel, pano_CropInfo *cropInfo)
1834 {
1835 unsigned char *pixel;
1836 int xLeft, xRight, yTop, yBottom;
1837 int row; int column;
1838 int alphaChannel;
1839
1840 xLeft = width;
1841 yTop = 0;
1842
1843 xRight = 0;
1844 yBottom = 0;
1845
1846 // We can do it all in one pass over the data
1847
1848 pixel = data;
1849 for (row = 0; row < height; row++) {
1850
1851 for (column = 0; column < width; column++) {
1852
1853 alphaChannel = panoStitchPixelChannelGet(pixel, bytesPerPixel/4, 0);
1854
1855 if (alphaChannel != 0) {
1856 // Only set the row the first time
1857 if (yTop == 0)
1858 yTop = row;
1859 // Keep setting it until we find no more data
1860 yBottom = row;
1861
1862 // Columns are trickier...
1863 // We are scanning row by row, so we need to
1864 if (xLeft > column) {
1865 xLeft = column;
1866 }
1867 if (xRight < column) {
1868 xRight = column;
1869 }
1870 }
1871 pixel += bytesPerPixel;
1872 }
1873 }
1874
1875 assert(xRight > xLeft);
1876 assert(yBottom > yTop);
1877
1878
1879 // Fill return struct
1880
1881 cropInfo->fullWidth = width;
1882 cropInfo->fullHeight = height;
1883 cropInfo->xOffset = xLeft;
1884 cropInfo->yOffset = yTop;
1885 cropInfo->croppedWidth = 1 + xRight - xLeft ;
1886 cropInfo->croppedHeight = 1+ yBottom - yTop;
1887
1888 // it should be at most equal to the original size
1889 assert(width >= cropInfo->croppedWidth);
1890 assert(height >= cropInfo->croppedHeight);
1891
1892
1893
1894 // fprintf(stderr, "Finding boudinging box: x %d y %d width %d height %d\n", (int)cropInfo->xOffset, (int)cropInfo->yOffset,
1895 // (int)cropInfo->croppedWidth, (int)cropInfo->croppedHeight);
1896
1897 return 1;
1898 }
1899
1900 /**
1901 * Reads inputFile and crops image
1902 */
panoTiffCrop(char * inputFile,char * outputFile,pano_cropping_parms * croppingParms)1903 int panoTiffCrop(char *inputFile, char *outputFile, pano_cropping_parms *croppingParms)
1904 {
1905
1906 pano_Tiff *tiffOutput = NULL;
1907 pano_ImageMetadata metadata;
1908 pano_CropInfo cropInfo;
1909 Image im;
1910 unsigned char *data = NULL;
1911 int i;
1912 fullPath tempFile;
1913
1914 strcpy(tempFile.name, "");
1915 // Let us do the processing in a different file
1916 if (panoFileMakeTemp(&tempFile) == 0) {
1917 PrintError("Could not make Tempfile");
1918 return -1;
1919 }
1920
1921 if (panoTiffRead(&im, inputFile) ==0 ) {
1922 PrintError("Unable to open input file %s", inputFile);
1923 goto error;
1924 }
1925
1926 // Compute inner rectangle
1927
1928 panoImageBoundingRectangleCompute(*im.data, im.width, im.height, im.bitsPerPixel/8, &cropInfo);
1929
1930 // Cropinfo is with respect to the data of the read image, not with respet to the "uncropped" image
1931 if (cropInfo.croppedWidth == 0 || cropInfo.croppedHeight == 0) {
1932 PrintError("Image is empty, unable to crop. ");
1933 goto error;
1934 }
1935
1936 if (!panoMetadataCopy(&metadata, &(im.metadata))) {
1937 goto error;
1938 }
1939
1940 panoMetadataCropSizeUpdate(&metadata, &cropInfo);
1941
1942 if ((tiffOutput =
1943 panoTiffCreate(tempFile.name, &metadata)) == NULL) {
1944 PrintError("Unable to create output file [%s]", outputFile);
1945 goto error;
1946 }
1947
1948 // Now we need to copy the data.
1949
1950 data = *(im.data);
1951
1952 // We need to advance data the number of lines that this file has more of ofset
1953 data += im.bytesPerLine * cropInfo.yOffset;
1954 for (i =0;i < (int) metadata.imageHeight; i++) {
1955 unsigned char *ptr;
1956
1957 // skip the necessary bytes
1958
1959 ptr = data + im.metadata.bytesPerPixel * cropInfo.xOffset;
1960
1961 // write
1962 ARGBtoRGBA(ptr, metadata.imageWidth, metadata.bitsPerPixel);
1963 if (TIFFWriteScanline(tiffOutput->tiff, ptr, i, 1) != 1) {
1964 PrintError("Error writing to output file");
1965 goto error;
1966 }
1967
1968 data += im.bytesPerLine;
1969
1970 }
1971
1972 //printf("Finished\n");
1973
1974 panoTiffClose(tiffOutput);
1975 remove(outputFile);
1976 if (rename(tempFile.name, outputFile) != 0) {
1977 PrintError("Unable to create output file %s", outputFile);
1978 goto error;
1979 }
1980
1981
1982 return 1;
1983
1984 error:
1985 // Error handler
1986 // Make sure we release any resources we have
1987
1988 if (tiffOutput != NULL) {
1989 panoTiffClose(tiffOutput);
1990 remove(tempFile.name);
1991 }
1992
1993 return 0;
1994 }
1995
1996
panoTiffDisplayInfo(char * fileName)1997 int panoTiffDisplayInfo(char *fileName)
1998 {
1999 pano_Tiff *imageFile;
2000 pano_ImageMetadata *meta;
2001
2002 char *line = NULL;
2003
2004 if ((imageFile = panoTiffOpen(fileName)) == NULL) {
2005 PrintError("Could not open TIFF-file %s", fileName);
2006 return 0;
2007 }
2008 meta = &(imageFile->metadata);
2009 printf("Dimensions: %d,%d\n", meta->imageWidth, meta->imageHeight);
2010 if (meta->isCropped) {
2011 printf("Cropped tiff. Full size: %d,%d Offset: %d,%d\n",
2012 (int)meta->cropInfo.fullWidth, (int)meta->cropInfo.fullHeight,
2013 (int)meta->cropInfo.xOffset, (int)meta->cropInfo.yOffset);
2014 }
2015 printf("Samples per pixel: %d\n", meta->samplesPerPixel);
2016 printf("Bits per sample: %d\n", meta->bitsPerSample);
2017
2018 if (meta->iccProfile.size == 0) {
2019 printf("Contains ICC profile\n");
2020 }
2021 if (meta->copyright != NULL){
2022 printf("Copyright: %s\n", meta->copyright);
2023 }
2024 if (meta->datetime != NULL){
2025 printf("Date created: %s\n", meta->datetime);
2026 }
2027 if (meta->artist != NULL){
2028 printf("Photographer: %s\n", meta->artist);
2029 }
2030 printf("Image: %d out of %d\n", meta->imageNumber, meta->imageTotalNumber);
2031
2032 line = panoParserFindOLine(meta->imageDescription, meta->imageNumber);
2033 if (line != NULL) {
2034 printf("Image Spec: %s\n", line);
2035 free(line);
2036 if (meta->imageDescription) {
2037 printf("Script that created it:\n%s\n", meta->imageDescription);
2038 }
2039 }
2040
2041 return 1;
2042 }
2043