home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / xloadimg.zip / xloadimage.4.1 / tiff.c < prev    next >
C/C++ Source or Header  |  1993-11-08  |  18KB  |  630 lines

  1. /* tiff.c:
  2.  *
  3.  * interface file for TIFF image format
  4.  *
  5.  * jim frost 09.05.93
  6.  */
  7.  
  8. #ifdef HAS_TIFF
  9.  
  10. #include "image.h"
  11. #include "tiff/tiffio.h"
  12.  
  13. /* this structure contains all the information we care about WRT a TIFF
  14.  * image.
  15.  */
  16. struct tiff_info {
  17.   unsigned short type; /* little- or big-endian */
  18.   int width;
  19.   int height;
  20.   int depth;
  21.   unsigned short planarconfiguration;
  22.   unsigned short photometric;
  23.   unsigned short compression;
  24.   unsigned short bitspersample;
  25.   unsigned short samplesperpixel;
  26.   unsigned short bytesperrow;
  27.   char *title;
  28. };
  29.  
  30. static TIFF *is_tiff(fullname, name, info)
  31.      char *fullname;
  32.      char *name;
  33.      struct tiff_info *info;
  34. {
  35.   ZFILE *zf;
  36.   TIFFHeader th;
  37.   TIFF *tiff;
  38.  
  39.   zf = zopen(fullname);
  40.  
  41.   /* read TIFF header and see if it looks right
  42.    */
  43.   if ((zread(zf, (byte *)&th, sizeof(TIFFHeader)) == sizeof(TIFFHeader)) &&
  44.       ((th.tiff_magic == TIFF_BIGENDIAN) ||
  45.        (th.tiff_magic == TIFF_LITTLEENDIAN))) {
  46.  
  47.     /* definitely a tiff file
  48.      */
  49.     if (zf->type != ZSTANDARD) {
  50.       printf("%s is a TIFF file, but TIFF files can't be read through pipes or filters, sorry.\n", name);
  51.       return((TIFF *)-1);
  52.     }
  53.  
  54.     info->type= th.tiff_magic;
  55.  
  56.     tiff = TIFFOpen(fullname, "r");
  57.     if (!tiff)
  58.       return(0);
  59.  
  60.     /* ok, start sucking in information about this file
  61.      */
  62.     /* find out gross information about this image
  63.      */
  64.     TIFFGetFieldDefaulted(tiff, TIFFTAG_IMAGEWIDTH, &info->width);
  65.     TIFFGetFieldDefaulted(tiff, TIFFTAG_IMAGELENGTH, &info->height);
  66.     TIFFGetFieldDefaulted(tiff, TIFFTAG_PLANARCONFIG, &info->planarconfiguration);
  67.     TIFFGetFieldDefaulted(tiff, TIFFTAG_PHOTOMETRIC, &info->photometric);
  68.     TIFFGetFieldDefaulted(tiff, TIFFTAG_COMPRESSION, &info->compression);
  69.     TIFFGetFieldDefaulted(tiff, TIFFTAG_BITSPERSAMPLE, &info->bitspersample);
  70.     TIFFGetFieldDefaulted(tiff, TIFFTAG_SAMPLESPERPIXEL, &info->samplesperpixel);
  71.     info->bytesperrow= TIFFScanlineSize(tiff);
  72.  
  73.     /* get the "title" of the image
  74.      */
  75.     if (!TIFFGetField(tiff, TIFFTAG_DOCUMENTNAME, &info->title))
  76.       if (!TIFFGetField(tiff, TIFFTAG_IMAGEDESCRIPTION, &info->title))
  77.     info->title = NULL;
  78.     /* make our own copy just to be safe
  79.      */
  80.     if (info->title)
  81.       info->title = dupString(info->title);
  82.  
  83. #if 0 /* maybe it does */
  84.     /* convert byte orders.  why the hell doesn't the TIFF stuff do this
  85.      * for you?
  86.      */
  87.     switch (info->type) {
  88.     case TIFF_BIGENDIAN:
  89. #define convert(V) (V) = memToVal(&(V), sizeof(V))
  90.       convert(info->width);
  91.       convert(info->height);
  92.       convert(info->planarconfiguration);
  93.       convert(info->photometric);
  94.       convert(info->compression);
  95.       convert(info->bitspersample);
  96.       convert(info->samplesperpixel);
  97. #undef convert
  98.       break;
  99.     case TIFF_LITTLEENDIAN:
  100. #define convert(V) (V) = memToValLSB(&(V), sizeof(V))
  101.       convert(info->width);
  102.       convert(info->height);
  103.       convert(info->planarconfiguration);
  104.       convert(info->photometric);
  105.       convert(info->compression);
  106.       convert(info->bitspersample);
  107.       convert(info->samplesperpixel);
  108. #undef convert
  109.       break;
  110.     }
  111. #endif
  112.     return(tiff);
  113.   }
  114.  
  115.   zclose(zf);
  116.   return(0);
  117. }
  118.  
  119. static char *photometricName(info)
  120.      struct tiff_info *info;
  121. {
  122.   static char buf[32];
  123.  
  124.   switch (info->photometric) {
  125.   case PHOTOMETRIC_MINISBLACK:
  126.     if (info->bitspersample > 1) {
  127.       sprintf(buf, "%d-bit greyscale ", info->bitspersample);
  128.       return(buf);
  129.     }
  130.     else
  131.       return "white-on-black ";
  132.   case PHOTOMETRIC_MINISWHITE:
  133.     if (info->bitspersample > 1) {
  134.       sprintf(buf, "%d-bit greyscale ", info->bitspersample);
  135.       return(buf);
  136.     }
  137.     else
  138.       return "black-on-white ";
  139.   case PHOTOMETRIC_RGB:
  140.     return "RGB ";
  141.   case PHOTOMETRIC_PALETTE:
  142.     return "colormap ";
  143.   case PHOTOMETRIC_MASK:
  144.     return "masked ";
  145.   case PHOTOMETRIC_SEPARATED:
  146.     return "color-separated ";
  147.   case PHOTOMETRIC_YCBCR:
  148.     return "YCBCR ";
  149.   case PHOTOMETRIC_CIELAB:
  150.     return "CIE L*a*b* ";
  151.   default:
  152.     return "";
  153.   }
  154. }
  155.  
  156. static char *compressionName(compression)
  157.      int compression;
  158. {
  159.   switch (compression) {
  160.   case COMPRESSION_NONE:
  161.     return "standard ";
  162.   case COMPRESSION_CCITTRLE:
  163.     return "RLE ";
  164.   case COMPRESSION_CCITTFAX3:
  165.     return "G3FAX ";
  166.   case COMPRESSION_CCITTFAX4:
  167.     return "G4FAX ";
  168.   case COMPRESSION_LZW:
  169.     return "LZW ";
  170.   case COMPRESSION_JPEG:
  171.     return "JPEG ";
  172.   case COMPRESSION_NEXT:
  173.     return "NeXT ";
  174.   case COMPRESSION_CCITTRLEW:
  175.     return "RLEW ";
  176.   case COMPRESSION_PACKBITS:
  177.     return "Macintosh ";
  178.   case COMPRESSION_THUNDERSCAN:
  179.     return "Thunderscan ";
  180.   default:
  181.     return "";
  182.   }
  183. }
  184.  
  185. static char *planarConfigName(config)
  186.      int config;
  187. {
  188.   switch (config) {
  189.   case PLANARCONFIG_CONTIG:
  190.     return "single-plane ";
  191.   case PLANARCONFIG_SEPARATE:
  192.     return "separate-plane ";
  193.   default:
  194.     return "";
  195.   }
  196. }
  197.  
  198. static void babble(name, info)
  199.      char *name;
  200.      struct tiff_info *info;
  201. {
  202.   switch (info->photometric) {
  203.   case PHOTOMETRIC_MINISWHITE:
  204.   case PHOTOMETRIC_MINISBLACK:
  205.     printf("%s is a %dx%d %s%s%sTIFF image",
  206.        name, info->width, info->height,
  207.        planarConfigName(info->planarconfiguration),
  208.        photometricName(info),
  209.        compressionName(info->compression));
  210.     break;
  211.   default:
  212.     printf("%s is a %dx%d %d-bit %s%s%sTIFF image",
  213.        name, info->width, info->height,
  214.        (info->bitspersample * info->samplesperpixel),
  215.        planarConfigName(info->planarconfiguration),
  216.        photometricName(info),
  217.        compressionName(info->compression));
  218.   }
  219.   if (info->title)
  220.     printf("Titled \"%s\"");
  221.   printf("\n");
  222. }
  223.  
  224. int tiffIdent(fullname, name)
  225. {
  226.   TIFF *tiff;
  227.   struct tiff_info info;
  228.  
  229.   tiff = is_tiff(fullname, name, &info);
  230.   babble(name, info);
  231.   if (tiff == NULL)
  232.     return(0);
  233.   if (tiff == (TIFF *)-1) /* is TIFF, but can't open it */
  234.     return(1);
  235.   TIFFClose(tiff);
  236.  
  237.   babble(fullname, name, info);
  238.   return(1);
  239. }
  240.  
  241. Image *tiffLoad(fullname, name, verbose)
  242.      char *fullname;
  243.      char *name;
  244.      int verbose;
  245. {
  246.   TIFF *tiff;
  247.   struct tiff_info info;
  248.   Image *image;
  249.   byte *row, *dest_line;
  250.   int dest_line_len;
  251.   register int pixlen;
  252.   register byte *dest;
  253.   register int x, y;
  254.  
  255.   tiff = is_tiff(fullname, name, &info);
  256.   if ((tiff == NULL) || (tiff == (TIFF *)-1))
  257.     return(NULL);
  258.   if (verbose)
  259.     babble(name, &info);
  260.  
  261.   row = lmalloc(info.bytesperrow);
  262.  
  263.   /* decide which type of image to allocate based on photometric
  264.    * information and set up the colormap as necessary
  265.    */
  266.   switch (info.photometric) {
  267.   case PHOTOMETRIC_MINISWHITE: /* bitmap and greyscale image types */
  268.   case PHOTOMETRIC_MINISBLACK:
  269.     /* monochrome image
  270.      */
  271.     if (info.bitspersample == 1) {
  272.       dest_line_len = (info.width / 8) + (info.width % 8 ? 1 : 0);
  273.       if (dest_line_len > info.bytesperrow) /* just in case */
  274.     dest_line_len = info.bytesperrow;
  275.  
  276.       image= newBitImage(info.width, info.height);
  277.  
  278.       /* default bit image colormap matches PHOTOMETRIC_MINISWHITE, so we
  279.        * need to change it if it's the other type.
  280.        */
  281.       if (info.photometric == PHOTOMETRIC_MINISBLACK) {
  282.     image->rgb.red[0]= image->rgb.green[0]= image->rgb.blue[0]= 0;
  283.     image->rgb.red[1]= image->rgb.green[1]= image->rgb.blue[1]= 65535;
  284.       }
  285.  
  286.       /* read the image data and set the pixels
  287.        */
  288.       dest = image->data;
  289.       if (info.bitspersample == 1) {
  290.     for (y = 0; y < info.height; y++) {
  291.       if (TIFFReadScanline(tiff, row, y, 0) < 0) {
  292.         fprintf(stderr, "%s: Short read in image data!\n", fullname);
  293.         break;
  294.       }
  295.  
  296.       /* isn't it nice when it's in the same data format we use?
  297.        */
  298.       bcopy(row, dest, dest_line_len);
  299.       dest += dest_line_len;
  300.     }
  301.       }
  302.       break;
  303.     }
  304.     else {
  305.       /* need to build the scale for greyscale images
  306.        */
  307.       image = newRGBImage(info.width, info.height, info.bitspersample);
  308.       for (x = 0; x < image->rgb.size; x++) {
  309.     int value = (65535 * x) / (unsigned int)(image->rgb.size);
  310.     if (info.photometric == PHOTOMETRIC_MINISWHITE)
  311.       value = 65535 - value;
  312.     image->rgb.red[x]= image->rgb.green[x]= image->rgb.blue[x]= value;
  313.       }
  314.       image->rgb.used = image->rgb.size;
  315.       goto read_pallette_data; /* ugly but expedient */
  316.     }
  317.     /* NOTREACHED */
  318.  
  319.   case PHOTOMETRIC_PALETTE:
  320.     /* this is a close match with the IRGB-style Image.
  321.      */
  322.     image = newRGBImage(info.width, info.height, info.bitspersample);
  323.     {
  324.       unsigned short *red, *green, *blue;
  325.  
  326.       if (!TIFFGetField(tiff, TIFFTAG_COLORMAP, &red, &green, &blue)) {
  327.     fprintf(stderr, "%s: Image has no colormap!\n", fullname);
  328.     freeImage(image);
  329.     image = NULL;
  330.     break;
  331.       }
  332.  
  333.       /* fill in our colormap with this data
  334.        */
  335.       switch (info.type) {
  336.       case TIFF_BIGENDIAN:
  337.     for (x = 0; x < image->rgb.size; x++) {
  338.       image->rgb.red[x] = memToVal(&red[x], 2);
  339.       image->rgb.green[x] = memToVal(&green[x], 2);
  340.       image->rgb.blue[x] = memToVal(&blue[x], 2);
  341.     }
  342.     break;
  343.       case TIFF_LITTLEENDIAN:
  344.     for (x = 0; x < image->rgb.size; x++) {
  345.       image->rgb.red[x] = memToValLSB(&red[x], 2);
  346.       image->rgb.green[x] = memToValLSB(&green[x], 2);
  347.       image->rgb.blue[x] = memToValLSB(&blue[x], 2);
  348.     }
  349.     break;
  350.       }
  351.       image->rgb.used = image->rgb.size; /* all filled up */
  352.     }
  353.  
  354.   read_pallette_data:
  355.     /* start reading image data
  356.      */
  357.     dest_line_len = info.width * image->pixlen;
  358.     if (dest_line_len > info.bytesperrow) /* just in case */
  359.       dest_line_len = info.bytesperrow;
  360.     pixlen = image->pixlen;
  361.     dest_line = image->data;
  362.  
  363.     switch (info.type) {
  364.     case TIFF_LITTLEENDIAN:
  365.       if (info.bitspersample > 8) {
  366.  
  367.     /* bummer, need to byte-swap
  368.      */
  369.     for (y = 0; y < info.height; y++) {
  370.       if (TIFFReadScanline(tiff, row, y, 0) < 0) {
  371.         fprintf(stderr, "%s: Short read in image data!\n", fullname);
  372.         break;
  373.       }
  374.       dest = dest_line;
  375.       for (x = 0; x < info.width; x++) {
  376.         valToMem(memToValLSB(dest, image->pixlen), dest, pixlen);
  377.         dest += pixlen;
  378.       }
  379.       dest_line += dest_line_len;
  380.     }
  381.       }
  382.       /* if the sample fits in 1 byte we don't need to byte swap so
  383.        * we fall through into the big-endian reader which is much faster.
  384.        */
  385.       /* FALLTHRU */
  386.     case TIFF_BIGENDIAN:
  387.       /* bigendian is a nice match for our internal data format
  388.        */
  389.       for (y = 0; y < info.height; y++) {
  390.     if (TIFFReadScanline(tiff, row, y, 0) < 0) {
  391.       fprintf(stderr, "%s: Short read in image data!\n", fullname);
  392.       break;
  393.     }
  394.     bcopy(row, dest_line, dest_line_len);
  395.     dest_line += dest_line_len;
  396.       }
  397.       break;
  398.     }
  399.     break;
  400.  
  401.   case PHOTOMETRIC_RGB:
  402.     /* this is a close match with the ITRUE-style Image.
  403.      */
  404.     if (info.samplesperpixel != 3) {
  405.       fprintf(stderr,
  406.           "%s: Can't handle TIFF RGB images with %d samples per pixel, sorry\n",
  407.           info.samplesperpixel);
  408.       image = NULL;
  409.       break;
  410.     }
  411.     image = newTrueImage(info.width, info.height);
  412.     dest_line_len = info.width * image->pixlen;
  413.     if (dest_line_len > info.bytesperrow) /* just in case */
  414.       dest_line_len = info.bytesperrow;
  415.     dest_line = image->data;
  416.  
  417.     /* quick-and-dirty copy if it's just what we're looking for
  418.      */
  419.     switch (info.planarconfiguration) {
  420.     case PLANARCONFIG_CONTIG:
  421.       if (info.bitspersample == 8) {
  422.     for (y = 0; y < info.height; y++) {
  423.       if (TIFFReadScanline(tiff, row, y, 0) < 0) {
  424.         fprintf(stderr, "%s: Short read in image data!\n", fullname);
  425.         break;
  426.       }
  427.       bcopy(row, dest_line, dest_line_len);
  428.       dest_line += dest_line_len;
  429.     }
  430.       }
  431.       else {
  432.     case PLANARCONFIG_SEPARATE:
  433.     fprintf(stderr, "%s: %s is an unsupported planar configuration.\n",
  434.         fullname, planarConfigName(info.planarconfiguration));
  435.     freeImage(image);
  436.     image = NULL;
  437.       }
  438.     }
  439.     break;
  440.  
  441.   case PHOTOMETRIC_MASK:
  442.   case PHOTOMETRIC_SEPARATED:
  443.   case PHOTOMETRIC_YCBCR:
  444.   case PHOTOMETRIC_CIELAB:
  445.   default:
  446.     fprintf(stderr, "%s: %s is an unsupported TIFF photometric style, sorry.\n",
  447.         fullname, photometricName(&info));
  448.     image = NULL;
  449.   }
  450.  
  451.   if (image) {
  452.     if (info.title)
  453.       image->title = info.title;
  454.     else
  455.       image->title = dupString(name);
  456.   }
  457.  
  458.   TIFFClose(tiff);
  459.   lfree(row);
  460.   return(image);
  461. }
  462.  
  463. /* this is not what I'd call a well-designed TIFF dumping function but it
  464.  * does do the job.
  465.  */
  466. void tiffDump(image, options, file, verbose)
  467.      Image *image;
  468.      char *options;
  469.      char *file;
  470.      int verbose;
  471. {
  472.   TIFF *out;
  473.   char *name, *value;
  474.   int compression = COMPRESSION_LZW;
  475.   int y;
  476.   int srclinelen;
  477.   byte *srcptr;
  478.  
  479.   out = TIFFOpen(file, "w");
  480.   TIFFSetField(out, TIFFTAG_IMAGEWIDTH, (unsigned long) image->width);
  481.   TIFFSetField(out, TIFFTAG_IMAGELENGTH, (unsigned long) image->height);
  482.   TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
  483.   TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
  484.   TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, 1);
  485.  
  486.   /* this sets the TIFF resolution, necessary for some packages.  since
  487.    * we have no concept of resolution (most input files don't retain that
  488.    * information) we set it for 1:1 pixel resolution.
  489.    */
  490.   TIFFSetField(out, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE);
  491.   TIFFSetField(out, TIFFTAG_XRESOLUTION, (float)1.0);
  492.   TIFFSetField(out, TIFFTAG_YRESOLUTION, (float)1.0);
  493.  
  494.   /* process image options
  495.    */
  496.   while (getNextTypeOption(&options, &name, &value) > 0) {
  497.     if (!strncmp("compression", name, strlen(name))) {
  498.       if (!strncmp("none", value, strlen(value)))
  499.     compression = COMPRESSION_NONE;
  500.       else if (!strncmp("rle", value, strlen(value)))
  501.     compression = COMPRESSION_CCITTRLE;
  502.       else if (!strncmp("g3fax", value, strlen(value)))
  503.     compression = COMPRESSION_CCITTFAX3;
  504.       else if (!strncmp("g4fax", value, strlen(value)))
  505.     compression = COMPRESSION_CCITTFAX4;
  506.       else if (!strncmp("lzw", value, strlen(value)))
  507.     compression = COMPRESSION_LZW; /* default */
  508.       else if (!strncmp("jpeg", value, strlen(value)))
  509.     compression = COMPRESSION_JPEG;
  510.       else if (!strncmp("next", value, strlen(value)))
  511.     compression = COMPRESSION_NEXT;
  512.       else if (!strncmp("rlew", value, strlen(value)))
  513.     compression = COMPRESSION_CCITTRLEW;
  514.       else if (!strncmp("packbits", value, strlen(value)) ||
  515.            !strncmp("mac", value, strlen(value)))
  516.     compression = COMPRESSION_PACKBITS;
  517.       else if (!strncmp("thunderscan", value, strlen(value)))
  518.     compression = COMPRESSION_THUNDERSCAN;
  519.       else
  520.     fprintf(stderr, "tiffDump: Unknown compression type '%s'.\n",
  521.         value);
  522.     }
  523.     else
  524.       fprintf(stderr, "tiffDump: Unknown option '%s'\n", name);
  525.   }
  526.     
  527.   TIFFSetField(out, TIFFTAG_COMPRESSION, compression);
  528.  
  529.   switch (image->type) {
  530.   case IBITMAP:
  531.     /* should use pixel intensity but this will usually work */
  532.     if (image->rgb.red[0] > image->rgb.red[1]) {
  533.       if (verbose) {
  534.     printf("Dumping black-on-white ");
  535.     if (compression != COMPRESSION_NONE)
  536.       printf("%s", compressionName(compression));
  537.     printf("TIFF image to %s.\n", file);
  538.       }
  539.       TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
  540.     }
  541.     else {
  542.       if (verbose) {
  543.     printf("Dumping white-on-black ");
  544.     if (compression != COMPRESSION_NONE)
  545.       printf("%s", compressionName(compression));
  546.     printf("TIFF image to %s.\n", file);
  547.       }
  548.       TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
  549.     }
  550.     TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 1);
  551.     TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, 1);
  552.  
  553.     srclinelen = (image->width / 8) + (image->width % 8 ? 1 : 0);
  554.     srcptr = image->data;
  555.     for (y = 0; y < image->height; y++) {
  556.       TIFFWriteScanline(out, srcptr, y, 0);
  557.       srcptr += srclinelen;
  558.     }
  559.     break;
  560.  
  561.   case IRGB:
  562.     TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 1);
  563.     TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, 8 * image->pixlen);
  564.     TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
  565.  
  566.     if (verbose) {
  567.       printf("Dumping colormap ");
  568.       if (compression != COMPRESSION_NONE)
  569.     printf("%s", compressionName(compression));
  570.       printf("TIFF image to %s.\n", file);
  571.     }
  572.  
  573.     /* save the colormap
  574.      */
  575.     {
  576.       unsigned short *red, *green, *blue;
  577.  
  578.       red = (unsigned short *)lmalloc(image->rgb.size * sizeof(unsigned short));
  579.       green = (unsigned short *)lmalloc(image->rgb.size * sizeof(unsigned short));
  580.       blue = (unsigned short *)lmalloc(image->rgb.size * sizeof(unsigned short));
  581.  
  582.       for (y = 0; y < image->rgb.used; y++) {
  583.     valToMem(image->rgb.red[y], &red[y], 2);
  584.     valToMem(image->rgb.green[y], &green[y], 2);
  585.     valToMem(image->rgb.blue[y], &blue[y], 2);
  586.       }
  587.       while (y < image->rgb.size) {
  588.     valToMem(0, &red[y], 2);
  589.     valToMem(0, &green[y], 2);
  590.     valToMem(0, &blue[y], 2);
  591.     y++;
  592.       }
  593.       TIFFSetField(out, TIFFTAG_COLORMAP, red, green, blue);
  594.       lfree((byte *)red);
  595.       lfree((byte *)green);
  596.       lfree((byte *)blue);
  597.     }
  598.     srclinelen = image->width * image->pixlen;
  599.     srcptr = image->data;
  600.     for (y = 0; y < image->height; y++) {
  601.       TIFFWriteScanline(out, srcptr, y, 0);
  602.       srcptr += srclinelen;
  603.     }
  604.     break;
  605.  
  606.   case ITRUE:
  607.     if (verbose) {
  608.       printf("Dumping RGB ");
  609.       if (compression != COMPRESSION_NONE)
  610.     printf("%s", compressionName(compression));
  611.       printf("TIFF image to %s.\n", file);
  612.     }
  613.     TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 3);
  614.     TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, 8);
  615.     TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
  616.     srclinelen = image->width * image->pixlen;
  617.     srcptr = image->data;
  618.     for (y = 0; y < image->height; y++) {
  619.       TIFFWriteScanline(out, srcptr, y, 0);
  620.       srcptr += srclinelen;
  621.     }
  622.     break;
  623.   }
  624.   TIFFClose(out);
  625. }
  626.  
  627. #else /* !HAS_TIFF */
  628. static int unused;
  629. #endif /* !HAS_TIFF */
  630.