summaryrefslogtreecommitdiff
path: root/frontend/stiff.c
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/stiff.c')
-rw-r--r--frontend/stiff.c610
1 files changed, 610 insertions, 0 deletions
diff --git a/frontend/stiff.c b/frontend/stiff.c
new file mode 100644
index 0000000..1107e09
--- /dev/null
+++ b/frontend/stiff.c
@@ -0,0 +1,610 @@
+/* Create SANE/tiff headers TIFF interfacing routines for SANE
+ Copyright (C) 2000 Peter Kirchgessner
+ Copyright (C) 2002 Oliver Rauch: added tiff ICC profile
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Changes:
+ 2000-11-19, PK: Color TIFF-header: write 3 values for bits per sample
+ 2001-12-16, PK: Write fill order tag for b/w-images
+ 2002-08-27, OR: Added tiff tag for ICC profile
+*/
+#ifdef _AIX
+# include "../include/lalloca.h" /* MUST come first for AIX! */
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "../include/sane/config.h"
+#include "../include/sane/sane.h"
+
+#include "stiff.h"
+
+typedef struct {
+ int tag, typ, nvals, val;
+} IFD_ENTRY;
+
+
+typedef struct {
+ int maxtags;
+ int ntags;
+ IFD_ENTRY *ifde;
+} IFD;
+
+#define IFDE_TYP_BYTE (1)
+#define IFDE_TYP_ASCII (2)
+#define IFDE_TYP_SHORT (3)
+#define IFDE_TYP_LONG (4)
+#define IFDE_TYP_RATIONAL (5)
+
+static IFD *
+create_ifd (void)
+
+{ IFD *ifd;
+ int maxtags = 10;
+
+ ifd = (IFD *)malloc (sizeof (IFD));
+ if (ifd == NULL) return NULL;
+
+ ifd->ifde = (IFD_ENTRY *)malloc (maxtags * sizeof (IFD_ENTRY));
+ if (ifd->ifde == NULL)
+ {
+ free (ifd);
+ return NULL;
+ }
+ ifd->ntags = 0;
+ ifd->maxtags = maxtags;
+
+ return ifd;
+}
+
+static void
+free_ifd (IFD *ifd)
+
+{
+ if (ifd == NULL) return;
+ if (ifd->ifde != NULL)
+ {
+ free (ifd->ifde);
+ ifd->ifde = NULL;
+ }
+ free (ifd);
+ ifd = NULL;
+}
+
+static void
+add_ifd_entry (IFD *ifd, int tag, int typ, int nvals, int val)
+
+{ IFD_ENTRY *ifde;
+ int add_entries = 10;
+
+ if (ifd == NULL) return;
+ if (ifd->ntags == ifd->maxtags)
+ {
+ ifde = (IFD_ENTRY *)realloc (ifd->ifde,
+ (ifd->maxtags+add_entries)*sizeof (IFD_ENTRY));
+ if (ifde == NULL) return;
+ ifd->ifde = ifde;
+ ifd->maxtags += add_entries;
+ }
+ ifde = &(ifd->ifde[ifd->ntags]);
+ ifde->tag = tag;
+ ifde->typ = typ;
+ ifde->nvals = nvals;
+ ifde->val = val;
+ (ifd->ntags)++;
+}
+
+static void
+write_i2 (FILE *fptr, int val, int motorola)
+{
+ if (motorola)
+ {
+ putc ((val >> 8) & 0xff, fptr);
+ putc (val & 0xff, fptr);
+ }
+ else
+ {
+ putc (val & 0xff, fptr);
+ putc ((val >> 8) & 0xff, fptr);
+ }
+}
+
+
+static void
+write_i4 (FILE *fptr, int val, int motorola)
+{
+ if (motorola)
+ {
+ putc ((val >> 24) & 0xff, fptr);
+ putc ((val >> 16) & 0xff, fptr);
+ putc ((val >> 8) & 0xff, fptr);
+ putc (val & 0xff, fptr);
+ }
+ else
+ {
+ putc (val & 0xff, fptr);
+ putc ((val >> 8) & 0xff, fptr);
+ putc ((val >> 16) & 0xff, fptr);
+ putc ((val >> 24) & 0xff, fptr);
+ }
+}
+
+static void
+write_ifd (FILE *fptr, IFD *ifd, int motorola)
+{int k;
+ IFD_ENTRY *ifde;
+
+ if (!ifd) return;
+
+ if (motorola) putc ('M', fptr), putc ('M', fptr);
+ else putc ('I', fptr), putc ('I', fptr);
+
+ write_i2 (fptr, 42, motorola); /* Magic */
+ write_i4 (fptr, 8, motorola); /* Offset to first IFD */
+ write_i2 (fptr, ifd->ntags, motorola);
+
+ for (k = 0; k < ifd->ntags; k++)
+ {
+ ifde = &(ifd->ifde[k]);
+ write_i2 (fptr, ifde->tag, motorola);
+ write_i2 (fptr, ifde->typ, motorola);
+ write_i4 (fptr, ifde->nvals, motorola);
+ if ((ifde->typ == IFDE_TYP_SHORT) && (ifde->nvals == 1))
+ {
+ write_i2 (fptr, ifde->val, motorola);
+ write_i2 (fptr, 0, motorola);
+ }
+ else
+ {
+ write_i4 (fptr, ifde->val, motorola);
+ }
+ }
+ write_i4 (fptr, 0, motorola); /* End of IFD chain */
+}
+
+
+static void
+write_tiff_bw_header (FILE *fptr, int width, int height, int resolution)
+{IFD *ifd;
+ int header_size = 8, ifd_size;
+ int strip_offset, data_offset, data_size;
+ int strip_bytecount;
+ int ntags;
+ int motorola;
+
+ ifd = create_ifd ();
+
+ strip_bytecount = ((width+7)/8) * height;
+
+ /* the following values must be known in advance */
+ ntags = 12;
+ data_size = 0;
+ if (resolution > 0)
+ {
+ ntags += 3;
+ data_size += 2*4 + 2*4;
+ }
+
+ ifd_size = 2 + ntags*12 + 4;
+ data_offset = header_size + ifd_size;
+ strip_offset = data_offset + data_size;
+
+ /* New subfile type */
+ add_ifd_entry (ifd, 254, IFDE_TYP_LONG, 1, 0);
+ /* image width */
+ add_ifd_entry (ifd, 256, (width > 0xffff) ? IFDE_TYP_LONG : IFDE_TYP_SHORT,
+ 1, width);
+ /* image length */
+ add_ifd_entry (ifd, 257, (height > 0xffff) ? IFDE_TYP_LONG : IFDE_TYP_SHORT,
+ 1, height);
+ /* bits per sample */
+ add_ifd_entry (ifd, 258, IFDE_TYP_SHORT, 1, 1);
+ /* compression (uncompressed) */
+ add_ifd_entry (ifd, 259, IFDE_TYP_SHORT, 1, 1);
+ /* photometric interpretation */
+ add_ifd_entry (ifd, 262, IFDE_TYP_SHORT, 1, 0);
+ /* fill order */
+ add_ifd_entry (ifd, 266, IFDE_TYP_SHORT, 1, 1);
+ /* strip offset */
+ add_ifd_entry (ifd, 273, IFDE_TYP_LONG, 1, strip_offset);
+ /* orientation */
+ add_ifd_entry (ifd, 274, IFDE_TYP_SHORT, 1, 1);
+ /* samples per pixel */
+ add_ifd_entry (ifd, 277, IFDE_TYP_SHORT, 1, 1);
+ /* rows per strip */
+ add_ifd_entry (ifd, 278, IFDE_TYP_LONG, 1, height);
+ /* strip bytecount */
+ add_ifd_entry (ifd, 279, IFDE_TYP_LONG, 1, strip_bytecount);
+ if (resolution > 0)
+ {
+ /* x resolution */
+ add_ifd_entry (ifd, 282, IFDE_TYP_RATIONAL, 1, data_offset);
+ data_offset += 2*4;
+ /* y resolution */
+ add_ifd_entry (ifd, 283, IFDE_TYP_RATIONAL, 1, data_offset);
+ data_offset += 2*4;
+ }
+ if (resolution > 0)
+ {
+ /* resolution unit (dpi) */
+ add_ifd_entry (ifd, 296, IFDE_TYP_SHORT, 1, 2);
+ }
+
+ /* I prefer motorola format. Its human readable. */
+ motorola = 1;
+ write_ifd (fptr, ifd, motorola);
+
+ /* Write x/y resolution */
+ if (resolution > 0)
+ {
+ write_i4 (fptr, resolution, motorola);
+ write_i4 (fptr, 1, motorola);
+ write_i4 (fptr, resolution, motorola);
+ write_i4 (fptr, 1, motorola);
+ }
+
+ free_ifd (ifd);
+}
+
+static void
+write_tiff_grey_header (FILE *fptr, int width, int height, int depth,
+ int resolution, const char *icc_profile)
+{IFD *ifd;
+ int header_size = 8, ifd_size;
+ int strip_offset, data_offset, data_size;
+ int strip_bytecount;
+ int ntags;
+ int motorola, bps, maxsamplevalue;
+ FILE *icc_file = 0;
+ int icc_len = -1;
+
+ if (icc_profile)
+ {
+ icc_file = fopen(icc_profile, "r");
+
+ if (!icc_file)
+ {
+ fprintf(stderr, "Could not open ICC profile %s\n", icc_profile);
+ }
+ else
+ {
+ icc_len = 16777216 * fgetc(icc_file) + 65536 * fgetc(icc_file) + 256 * fgetc(icc_file) + fgetc(icc_file);
+ rewind(icc_file);
+ }
+ }
+
+ ifd = create_ifd ();
+
+ bps = (depth <= 8) ? 1 : 2; /* Bytes per sample */
+ maxsamplevalue = (depth <= 8) ? 255 : 65535;
+ strip_bytecount = width * height * bps;
+
+ /* the following values must be known in advance */
+ ntags = 13;
+ data_size = 0;
+ if (resolution > 0)
+ {
+ ntags += 3;
+ data_size += 2*4 + 2*4;
+ }
+
+ if (icc_len > 0) /* if icc profile exists add memory for tag */
+ {
+ ntags += 1;
+ data_size += icc_len;
+ }
+
+ ifd_size = 2 + ntags*12 + 4;
+ data_offset = header_size + ifd_size;
+ strip_offset = data_offset + data_size;
+
+ /* New subfile type */
+ add_ifd_entry (ifd, 254, IFDE_TYP_LONG, 1, 0);
+ /* image width */
+ add_ifd_entry (ifd, 256, (width > 0xffff) ? IFDE_TYP_LONG : IFDE_TYP_SHORT,
+ 1, width);
+ /* image length */
+ add_ifd_entry (ifd, 257, (height > 0xffff) ? IFDE_TYP_LONG : IFDE_TYP_SHORT,
+ 1, height);
+ /* bits per sample */
+ add_ifd_entry (ifd, 258, IFDE_TYP_SHORT, 1, depth);
+ /* compression (uncompressed) */
+ add_ifd_entry (ifd, 259, IFDE_TYP_SHORT, 1, 1);
+ /* photometric interpretation */
+ add_ifd_entry (ifd, 262, IFDE_TYP_SHORT, 1, 1);
+ /* strip offset */
+ add_ifd_entry (ifd, 273, IFDE_TYP_LONG, 1, strip_offset);
+ /* orientation */
+ add_ifd_entry (ifd, 274, IFDE_TYP_SHORT, 1, 1);
+ /* samples per pixel */
+ add_ifd_entry (ifd, 277, IFDE_TYP_SHORT, 1, 1);
+ /* rows per strip */
+ add_ifd_entry (ifd, 278, IFDE_TYP_LONG, 1, height);
+ /* strip bytecount */
+ add_ifd_entry (ifd, 279, IFDE_TYP_LONG, 1, strip_bytecount);
+ /* min sample value */
+ add_ifd_entry (ifd, 280, IFDE_TYP_SHORT, 1, 0);
+ /* max sample value */
+ add_ifd_entry (ifd, 281, IFDE_TYP_SHORT, 1, maxsamplevalue);
+ if (resolution > 0)
+ {
+ /* x resolution */
+ add_ifd_entry (ifd, 282, IFDE_TYP_RATIONAL, 1, data_offset);
+ data_offset += 2*4;
+ /* y resolution */
+ add_ifd_entry (ifd, 283, IFDE_TYP_RATIONAL, 1, data_offset);
+ data_offset += 2*4;
+ }
+ if (resolution > 0)
+ {
+ /* resolution unit (dpi) */
+ add_ifd_entry (ifd, 296, IFDE_TYP_SHORT, 1, 2);
+ }
+
+ if (icc_len > 0) /* add ICC-profile TAG */
+ {
+ add_ifd_entry(ifd, 34675, 7, icc_len, data_offset);
+ data_offset += icc_len;
+ }
+
+ /* I prefer motorola format. Its human readable. But for 16 bit, */
+ /* the image format is defined by SANE to be the native byte order */
+ if (bps == 1)
+ {
+ motorola = 1;
+ }
+ else
+ {int check = 1;
+ motorola = ((*((char *)&check)) == 0);
+ }
+
+ write_ifd (fptr, ifd, motorola);
+
+ /* Write x/y resolution */
+ if (resolution > 0)
+ {
+ write_i4 (fptr, resolution, motorola);
+ write_i4 (fptr, 1, motorola);
+ write_i4 (fptr, resolution, motorola);
+ write_i4 (fptr, 1, motorola);
+ }
+
+ /* Write ICC profile */
+ if (icc_len > 0)
+ {
+ int i;
+ for (i=0; i<icc_len; i++)
+ {
+ if (!feof(icc_file))
+ {
+ fputc(fgetc(icc_file), fptr);
+ }
+ else
+ {
+ fprintf(stderr, "ICC profile %s is too short\n", icc_profile);
+ break;
+ }
+ }
+ }
+
+ if (icc_file)
+ {
+ fclose(icc_file);
+ }
+
+ free_ifd (ifd);
+}
+
+
+static void
+write_tiff_color_header (FILE *fptr, int width, int height, int depth,
+ int resolution, const char *icc_profile)
+{IFD *ifd;
+ int header_size = 8, ifd_size;
+ int strip_offset, data_offset, data_size;
+ int strip_bytecount;
+ int ntags;
+ int motorola, bps, maxsamplevalue;
+ FILE *icc_file = 0;
+ int icc_len = -1;
+
+ if (icc_profile)
+ {
+ icc_file = fopen(icc_profile, "r");
+
+ if (!icc_file)
+ {
+ fprintf(stderr, "Could not open ICC profile %s\n", icc_profile);
+ }
+ else
+ {
+ icc_len = 16777216 * fgetc(icc_file) + 65536 * fgetc(icc_file) + 256 * fgetc(icc_file) + fgetc(icc_file);
+ rewind(icc_file);
+ }
+ }
+
+
+ ifd = create_ifd ();
+
+ bps = (depth <= 8) ? 1 : 2; /* Bytes per sample */
+ maxsamplevalue = (depth <= 8) ? 255 : 65535;
+ strip_bytecount = width * height * 3 * bps;
+
+ /* the following values must be known in advance */
+ ntags = 13;
+ data_size = 3*2 + 3*2 + 3*2;
+
+ if (resolution > 0)
+ {
+ ntags += 3;
+ data_size += 2*4 + 2*4;
+ }
+
+ if (icc_len > 0) /* if icc profile exists add memory for tag */
+ {
+ ntags += 1;
+ data_size += icc_len;
+ }
+
+
+ ifd_size = 2 + ntags*12 + 4;
+ data_offset = header_size + ifd_size;
+ strip_offset = data_offset + data_size;
+
+ /* New subfile type */
+ add_ifd_entry (ifd, 254, IFDE_TYP_LONG, 1, 0);
+ /* image width */
+ add_ifd_entry (ifd, 256, (width > 0xffff) ? IFDE_TYP_LONG : IFDE_TYP_SHORT,
+ 1, width);
+ /* image length */
+ add_ifd_entry (ifd, 257, (height > 0xffff) ? IFDE_TYP_LONG : IFDE_TYP_SHORT,
+ 1, height);
+ /* bits per sample */
+ add_ifd_entry (ifd, 258, IFDE_TYP_SHORT, 3, data_offset);
+ data_offset += 3*2;
+ /* compression (uncompressed) */
+ add_ifd_entry (ifd, 259, IFDE_TYP_SHORT, 1, 1);
+ /* photometric interpretation */
+ add_ifd_entry (ifd, 262, IFDE_TYP_SHORT, 1, 2);
+ /* strip offset */
+ add_ifd_entry (ifd, 273, IFDE_TYP_LONG, 1, strip_offset);
+ /* orientation */
+ add_ifd_entry (ifd, 274, IFDE_TYP_SHORT, 1, 1);
+ /* samples per pixel */
+ add_ifd_entry (ifd, 277, IFDE_TYP_SHORT, 1, 3);
+ /* rows per strip */
+ add_ifd_entry (ifd, 278, IFDE_TYP_LONG, 1, height);
+ /* strip bytecount */
+ add_ifd_entry (ifd, 279, IFDE_TYP_LONG, 1, strip_bytecount);
+ /* min sample value */
+ add_ifd_entry (ifd, 280, IFDE_TYP_SHORT, 3, data_offset);
+ data_offset += 3*2;
+ /* max sample value */
+ add_ifd_entry (ifd, 281, IFDE_TYP_SHORT, 3, data_offset);
+ data_offset += 3*2;
+
+ if (resolution > 0)
+ {
+ /* x resolution */
+ add_ifd_entry (ifd, 282, IFDE_TYP_RATIONAL, 1, data_offset);
+ data_offset += 2*4;
+ /* y resolution */
+ add_ifd_entry (ifd, 283, IFDE_TYP_RATIONAL, 1, data_offset);
+ data_offset += 2*4;
+ }
+
+ if (resolution > 0)
+ {
+ /* resolution unit (dpi) */
+ add_ifd_entry (ifd, 296, IFDE_TYP_SHORT, 1, 2);
+ }
+
+ if (icc_len > 0) /* add ICC-profile TAG */
+ {
+ add_ifd_entry(ifd, 34675, 7, icc_len, data_offset);
+ data_offset += icc_len;
+ }
+
+
+ /* I prefer motorola format. Its human readable. But for 16 bit, */
+ /* the image format is defined by SANE to be the native byte order */
+ if (bps == 1)
+ {
+ motorola = 1;
+ }
+ else
+ {int check = 1;
+ motorola = ((*((char *)&check)) == 0);
+ }
+
+ write_ifd (fptr, ifd, motorola);
+
+ /* Write bits per sample value values */
+ write_i2 (fptr, depth, motorola);
+ write_i2 (fptr, depth, motorola);
+ write_i2 (fptr, depth, motorola);
+
+ /* Write min sample value values */
+ write_i2 (fptr, 0, motorola);
+ write_i2 (fptr, 0, motorola);
+ write_i2 (fptr, 0, motorola);
+
+ /* Write max sample value values */
+ write_i2 (fptr, maxsamplevalue, motorola);
+ write_i2 (fptr, maxsamplevalue, motorola);
+ write_i2 (fptr, maxsamplevalue, motorola);
+
+ /* Write x/y resolution */
+ if (resolution > 0)
+ {
+ write_i4 (fptr, resolution, motorola);
+ write_i4 (fptr, 1, motorola);
+ write_i4 (fptr, resolution, motorola);
+ write_i4 (fptr, 1, motorola);
+ }
+
+ /* Write ICC profile */
+ if (icc_len > 0)
+ {
+ int i;
+ for (i=0; i<icc_len; i++)
+ {
+ if (!feof(icc_file))
+ {
+ fputc(fgetc(icc_file), fptr);
+ }
+ else
+ {
+ fprintf(stderr, "ICC profile %s is too short\n", icc_profile);
+ break;
+ }
+ }
+ }
+
+ if (icc_file)
+ {
+ fclose(icc_file);
+ }
+
+ free_ifd (ifd);
+}
+
+
+void
+sanei_write_tiff_header (SANE_Frame format, int width, int height, int depth,
+ int resolution, const char *icc_profile)
+{
+#ifdef __EMX__ /* OS2 - write in binary mode. */
+ _fsetmode(stdout, "b");
+#endif
+ switch (format)
+ {
+ case SANE_FRAME_RED:
+ case SANE_FRAME_GREEN:
+ case SANE_FRAME_BLUE:
+ case SANE_FRAME_RGB:
+ write_tiff_color_header (stdout, width, height, depth, resolution, icc_profile);
+ break;
+
+ default:
+ if (depth == 1)
+ write_tiff_bw_header (stdout, width, height, resolution);
+ else
+ write_tiff_grey_header (stdout, width, height, depth, resolution, icc_profile);
+ break;
+ }
+}