1141cc406Sopenharmony_ci/* Create SANE/tiff headers TIFF interfacing routines for SANE
2141cc406Sopenharmony_ci   Copyright (C) 2000 Peter Kirchgessner
3141cc406Sopenharmony_ci   Copyright (C) 2002 Oliver Rauch: added tiff ICC profile
4141cc406Sopenharmony_ci   Copyright (C) 2017 Aaron Muir Hamilton <aaron@correspondwith.me>
5141cc406Sopenharmony_ci
6141cc406Sopenharmony_ci   This program is free software; you can redistribute it and/or
7141cc406Sopenharmony_ci   modify it under the terms of the GNU General Public License as
8141cc406Sopenharmony_ci   published by the Free Software Foundation; either version 2 of the
9141cc406Sopenharmony_ci   License, or (at your option) any later version.
10141cc406Sopenharmony_ci
11141cc406Sopenharmony_ci   This program is distributed in the hope that it will be useful, but
12141cc406Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
13141cc406Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14141cc406Sopenharmony_ci   General Public License for more details.
15141cc406Sopenharmony_ci
16141cc406Sopenharmony_ci   You should have received a copy of the GNU General Public License
17141cc406Sopenharmony_ci   along with this program.  If not, see <https://www.gnu.org/licenses/>.
18141cc406Sopenharmony_ci*/
19141cc406Sopenharmony_ci
20141cc406Sopenharmony_ci/* Changes:
21141cc406Sopenharmony_ci   2000-11-19, PK: Color TIFF-header: write 3 values for bits per sample
22141cc406Sopenharmony_ci   2001-12-16, PK: Write fill order tag for b/w-images
23141cc406Sopenharmony_ci   2002-08-27, OR: Added tiff tag for ICC profile
24141cc406Sopenharmony_ci   2017-04-16, AMH: Separate ICC profile loading into a separate file
25141cc406Sopenharmony_ci*/
26141cc406Sopenharmony_ci#ifdef _AIX
27141cc406Sopenharmony_ci# include "../include/lalloca.h"	/* MUST come first for AIX! */
28141cc406Sopenharmony_ci#endif
29141cc406Sopenharmony_ci
30141cc406Sopenharmony_ci#include <stdlib.h>
31141cc406Sopenharmony_ci#include <stdio.h>
32141cc406Sopenharmony_ci
33141cc406Sopenharmony_ci#include "../include/sane/config.h"
34141cc406Sopenharmony_ci#include "../include/sane/sane.h"
35141cc406Sopenharmony_ci
36141cc406Sopenharmony_ci#include "sicc.h"
37141cc406Sopenharmony_ci#include "stiff.h"
38141cc406Sopenharmony_ci
39141cc406Sopenharmony_citypedef struct {
40141cc406Sopenharmony_ci    int tag, typ, nvals, val;
41141cc406Sopenharmony_ci} IFD_ENTRY;
42141cc406Sopenharmony_ci
43141cc406Sopenharmony_ci
44141cc406Sopenharmony_citypedef struct {
45141cc406Sopenharmony_ci    int maxtags;
46141cc406Sopenharmony_ci    int ntags;
47141cc406Sopenharmony_ci    IFD_ENTRY *ifde;
48141cc406Sopenharmony_ci} IFD;
49141cc406Sopenharmony_ci
50141cc406Sopenharmony_ci#define IFDE_TYP_BYTE     (1)
51141cc406Sopenharmony_ci#define IFDE_TYP_ASCII    (2)
52141cc406Sopenharmony_ci#define IFDE_TYP_SHORT    (3)
53141cc406Sopenharmony_ci#define IFDE_TYP_LONG     (4)
54141cc406Sopenharmony_ci#define IFDE_TYP_RATIONAL (5)
55141cc406Sopenharmony_ci
56141cc406Sopenharmony_cistatic IFD *
57141cc406Sopenharmony_cicreate_ifd (void)
58141cc406Sopenharmony_ci
59141cc406Sopenharmony_ci{   IFD *ifd;
60141cc406Sopenharmony_ci    int maxtags = 10;
61141cc406Sopenharmony_ci
62141cc406Sopenharmony_ci    ifd = (IFD *)malloc (sizeof (IFD));
63141cc406Sopenharmony_ci    if (ifd == NULL) return NULL;
64141cc406Sopenharmony_ci
65141cc406Sopenharmony_ci    ifd->ifde = (IFD_ENTRY *)malloc (maxtags * sizeof (IFD_ENTRY));
66141cc406Sopenharmony_ci    if (ifd->ifde == NULL)
67141cc406Sopenharmony_ci    {
68141cc406Sopenharmony_ci        free (ifd);
69141cc406Sopenharmony_ci        return NULL;
70141cc406Sopenharmony_ci    }
71141cc406Sopenharmony_ci    ifd->ntags = 0;
72141cc406Sopenharmony_ci    ifd->maxtags = maxtags;
73141cc406Sopenharmony_ci
74141cc406Sopenharmony_ci    return ifd;
75141cc406Sopenharmony_ci}
76141cc406Sopenharmony_ci
77141cc406Sopenharmony_cistatic void
78141cc406Sopenharmony_cifree_ifd (IFD *ifd)
79141cc406Sopenharmony_ci
80141cc406Sopenharmony_ci{
81141cc406Sopenharmony_ci    if (ifd == NULL) return;
82141cc406Sopenharmony_ci    if (ifd->ifde != NULL)
83141cc406Sopenharmony_ci    {
84141cc406Sopenharmony_ci        free (ifd->ifde);
85141cc406Sopenharmony_ci        ifd->ifde = NULL;
86141cc406Sopenharmony_ci    }
87141cc406Sopenharmony_ci    free (ifd);
88141cc406Sopenharmony_ci    ifd = NULL;
89141cc406Sopenharmony_ci}
90141cc406Sopenharmony_ci
91141cc406Sopenharmony_cistatic void
92141cc406Sopenharmony_ciadd_ifd_entry (IFD *ifd, int tag, int typ, int nvals, int val)
93141cc406Sopenharmony_ci
94141cc406Sopenharmony_ci{   IFD_ENTRY *ifde;
95141cc406Sopenharmony_ci    int add_entries = 10;
96141cc406Sopenharmony_ci
97141cc406Sopenharmony_ci    if (ifd == NULL) return;
98141cc406Sopenharmony_ci    if (ifd->ntags == ifd->maxtags)
99141cc406Sopenharmony_ci    {
100141cc406Sopenharmony_ci        ifde = (IFD_ENTRY *)realloc (ifd->ifde,
101141cc406Sopenharmony_ci                                     (ifd->maxtags+add_entries)*sizeof (IFD_ENTRY));
102141cc406Sopenharmony_ci        if (ifde == NULL) return;
103141cc406Sopenharmony_ci        ifd->ifde = ifde;
104141cc406Sopenharmony_ci        ifd->maxtags += add_entries;
105141cc406Sopenharmony_ci    }
106141cc406Sopenharmony_ci    ifde = &(ifd->ifde[ifd->ntags]);
107141cc406Sopenharmony_ci    ifde->tag = tag;
108141cc406Sopenharmony_ci    ifde->typ = typ;
109141cc406Sopenharmony_ci    ifde->nvals = nvals;
110141cc406Sopenharmony_ci    ifde->val = val;
111141cc406Sopenharmony_ci    (ifd->ntags)++;
112141cc406Sopenharmony_ci}
113141cc406Sopenharmony_ci
114141cc406Sopenharmony_cistatic void
115141cc406Sopenharmony_ciwrite_i2 (FILE *fptr, int val, int motorola)
116141cc406Sopenharmony_ci{
117141cc406Sopenharmony_ci    if (motorola)
118141cc406Sopenharmony_ci    {
119141cc406Sopenharmony_ci        putc ((val >> 8) & 0xff, fptr);
120141cc406Sopenharmony_ci        putc (val & 0xff, fptr);
121141cc406Sopenharmony_ci    }
122141cc406Sopenharmony_ci    else
123141cc406Sopenharmony_ci    {
124141cc406Sopenharmony_ci        putc (val & 0xff, fptr);
125141cc406Sopenharmony_ci        putc ((val >> 8) & 0xff, fptr);
126141cc406Sopenharmony_ci    }
127141cc406Sopenharmony_ci}
128141cc406Sopenharmony_ci
129141cc406Sopenharmony_ci
130141cc406Sopenharmony_cistatic void
131141cc406Sopenharmony_ciwrite_i4 (FILE *fptr, int val, int motorola)
132141cc406Sopenharmony_ci{
133141cc406Sopenharmony_ci    if (motorola)
134141cc406Sopenharmony_ci    {
135141cc406Sopenharmony_ci        putc ((val >> 24) & 0xff, fptr);
136141cc406Sopenharmony_ci        putc ((val >> 16) & 0xff, fptr);
137141cc406Sopenharmony_ci        putc ((val >> 8) & 0xff, fptr);
138141cc406Sopenharmony_ci        putc (val & 0xff, fptr);
139141cc406Sopenharmony_ci    }
140141cc406Sopenharmony_ci    else
141141cc406Sopenharmony_ci    {
142141cc406Sopenharmony_ci        putc (val & 0xff, fptr);
143141cc406Sopenharmony_ci        putc ((val >> 8) & 0xff, fptr);
144141cc406Sopenharmony_ci        putc ((val >> 16) & 0xff, fptr);
145141cc406Sopenharmony_ci        putc ((val >> 24) & 0xff, fptr);
146141cc406Sopenharmony_ci    }
147141cc406Sopenharmony_ci}
148141cc406Sopenharmony_ci
149141cc406Sopenharmony_cistatic void
150141cc406Sopenharmony_ciwrite_ifd (FILE *fptr, IFD *ifd, int motorola)
151141cc406Sopenharmony_ci{int k;
152141cc406Sopenharmony_ci    IFD_ENTRY *ifde;
153141cc406Sopenharmony_ci
154141cc406Sopenharmony_ci    if (!ifd) return;
155141cc406Sopenharmony_ci
156141cc406Sopenharmony_ci    if (motorola) putc ('M', fptr), putc ('M', fptr);
157141cc406Sopenharmony_ci    else putc ('I', fptr), putc ('I', fptr);
158141cc406Sopenharmony_ci
159141cc406Sopenharmony_ci    write_i2 (fptr, 42, motorola);  /* Magic */
160141cc406Sopenharmony_ci    write_i4 (fptr, 8, motorola);   /* Offset to first IFD */
161141cc406Sopenharmony_ci    write_i2 (fptr, ifd->ntags, motorola);
162141cc406Sopenharmony_ci
163141cc406Sopenharmony_ci    for (k = 0; k < ifd->ntags; k++)
164141cc406Sopenharmony_ci    {
165141cc406Sopenharmony_ci        ifde = &(ifd->ifde[k]);
166141cc406Sopenharmony_ci        write_i2 (fptr, ifde->tag, motorola);
167141cc406Sopenharmony_ci        write_i2 (fptr, ifde->typ, motorola);
168141cc406Sopenharmony_ci        write_i4 (fptr, ifde->nvals, motorola);
169141cc406Sopenharmony_ci        if ((ifde->typ == IFDE_TYP_SHORT) && (ifde->nvals == 1))
170141cc406Sopenharmony_ci        {
171141cc406Sopenharmony_ci            write_i2 (fptr, ifde->val, motorola);
172141cc406Sopenharmony_ci            write_i2 (fptr, 0, motorola);
173141cc406Sopenharmony_ci        }
174141cc406Sopenharmony_ci        else
175141cc406Sopenharmony_ci        {
176141cc406Sopenharmony_ci            write_i4 (fptr, ifde->val, motorola);
177141cc406Sopenharmony_ci        }
178141cc406Sopenharmony_ci    }
179141cc406Sopenharmony_ci    write_i4 (fptr, 0, motorola); /* End of IFD chain */
180141cc406Sopenharmony_ci}
181141cc406Sopenharmony_ci
182141cc406Sopenharmony_ci
183141cc406Sopenharmony_cistatic void
184141cc406Sopenharmony_ciwrite_tiff_bw_header (FILE *fptr, int width, int height, int resolution)
185141cc406Sopenharmony_ci{IFD *ifd;
186141cc406Sopenharmony_ci    int header_size = 8, ifd_size;
187141cc406Sopenharmony_ci    int strip_offset, data_offset, data_size;
188141cc406Sopenharmony_ci    int strip_bytecount;
189141cc406Sopenharmony_ci    int ntags;
190141cc406Sopenharmony_ci    int motorola;
191141cc406Sopenharmony_ci
192141cc406Sopenharmony_ci    ifd = create_ifd ();
193141cc406Sopenharmony_ci
194141cc406Sopenharmony_ci    strip_bytecount = ((width+7)/8) * height;
195141cc406Sopenharmony_ci
196141cc406Sopenharmony_ci    /* the following values must be known in advance */
197141cc406Sopenharmony_ci    ntags = 12;
198141cc406Sopenharmony_ci    data_size = 0;
199141cc406Sopenharmony_ci    if (resolution > 0)
200141cc406Sopenharmony_ci    {
201141cc406Sopenharmony_ci        ntags += 3;
202141cc406Sopenharmony_ci        data_size += 2*4 + 2*4;
203141cc406Sopenharmony_ci    }
204141cc406Sopenharmony_ci
205141cc406Sopenharmony_ci    ifd_size = 2 + ntags*12 + 4;
206141cc406Sopenharmony_ci    data_offset = header_size + ifd_size;
207141cc406Sopenharmony_ci    strip_offset = data_offset + data_size;
208141cc406Sopenharmony_ci
209141cc406Sopenharmony_ci    /* New subfile type */
210141cc406Sopenharmony_ci    add_ifd_entry (ifd, 254, IFDE_TYP_LONG, 1, 0);
211141cc406Sopenharmony_ci    /* image width */
212141cc406Sopenharmony_ci    add_ifd_entry (ifd, 256, (width > 0xffff) ? IFDE_TYP_LONG : IFDE_TYP_SHORT,
213141cc406Sopenharmony_ci                   1, width);
214141cc406Sopenharmony_ci    /* image length */
215141cc406Sopenharmony_ci    add_ifd_entry (ifd, 257, (height > 0xffff) ? IFDE_TYP_LONG : IFDE_TYP_SHORT,
216141cc406Sopenharmony_ci                   1, height);
217141cc406Sopenharmony_ci    /* bits per sample */
218141cc406Sopenharmony_ci    add_ifd_entry (ifd, 258, IFDE_TYP_SHORT, 1, 1);
219141cc406Sopenharmony_ci    /* compression (uncompressed) */
220141cc406Sopenharmony_ci    add_ifd_entry (ifd, 259, IFDE_TYP_SHORT, 1, 1);
221141cc406Sopenharmony_ci    /* photometric interpretation */
222141cc406Sopenharmony_ci    add_ifd_entry (ifd, 262, IFDE_TYP_SHORT, 1, 0);
223141cc406Sopenharmony_ci    /* fill order */
224141cc406Sopenharmony_ci    add_ifd_entry (ifd, 266, IFDE_TYP_SHORT, 1, 1);
225141cc406Sopenharmony_ci    /* strip offset */
226141cc406Sopenharmony_ci    add_ifd_entry (ifd, 273, IFDE_TYP_LONG, 1, strip_offset);
227141cc406Sopenharmony_ci    /* orientation */
228141cc406Sopenharmony_ci    add_ifd_entry (ifd, 274, IFDE_TYP_SHORT, 1, 1);
229141cc406Sopenharmony_ci    /* samples per pixel */
230141cc406Sopenharmony_ci    add_ifd_entry (ifd, 277, IFDE_TYP_SHORT, 1, 1);
231141cc406Sopenharmony_ci    /* rows per strip */
232141cc406Sopenharmony_ci    add_ifd_entry (ifd, 278, IFDE_TYP_LONG, 1, height);
233141cc406Sopenharmony_ci    /* strip bytecount */
234141cc406Sopenharmony_ci    add_ifd_entry (ifd, 279, IFDE_TYP_LONG, 1, strip_bytecount);
235141cc406Sopenharmony_ci    if (resolution > 0)
236141cc406Sopenharmony_ci    {
237141cc406Sopenharmony_ci        /* x resolution */
238141cc406Sopenharmony_ci        add_ifd_entry (ifd, 282, IFDE_TYP_RATIONAL, 1, data_offset);
239141cc406Sopenharmony_ci        data_offset += 2*4;
240141cc406Sopenharmony_ci        /* y resolution */
241141cc406Sopenharmony_ci        add_ifd_entry (ifd, 283, IFDE_TYP_RATIONAL, 1, data_offset);
242141cc406Sopenharmony_ci        data_offset += 2*4;
243141cc406Sopenharmony_ci    }
244141cc406Sopenharmony_ci    if (resolution > 0)
245141cc406Sopenharmony_ci    {
246141cc406Sopenharmony_ci        /* resolution unit (dpi) */
247141cc406Sopenharmony_ci        add_ifd_entry (ifd, 296, IFDE_TYP_SHORT, 1, 2);
248141cc406Sopenharmony_ci    }
249141cc406Sopenharmony_ci
250141cc406Sopenharmony_ci    /* I prefer motorola format. Its human readable. */
251141cc406Sopenharmony_ci    motorola = 1;
252141cc406Sopenharmony_ci    write_ifd (fptr, ifd, motorola);
253141cc406Sopenharmony_ci
254141cc406Sopenharmony_ci    /* Write x/y resolution */
255141cc406Sopenharmony_ci    if (resolution > 0)
256141cc406Sopenharmony_ci    {
257141cc406Sopenharmony_ci        write_i4 (fptr, resolution, motorola);
258141cc406Sopenharmony_ci        write_i4 (fptr, 1, motorola);
259141cc406Sopenharmony_ci        write_i4 (fptr, resolution, motorola);
260141cc406Sopenharmony_ci        write_i4 (fptr, 1, motorola);
261141cc406Sopenharmony_ci    }
262141cc406Sopenharmony_ci
263141cc406Sopenharmony_ci    free_ifd (ifd);
264141cc406Sopenharmony_ci}
265141cc406Sopenharmony_ci
266141cc406Sopenharmony_cistatic void
267141cc406Sopenharmony_ciwrite_tiff_grey_header (FILE *fptr, int width, int height, int depth,
268141cc406Sopenharmony_ci                        int resolution, const char *icc_profile)
269141cc406Sopenharmony_ci{IFD *ifd;
270141cc406Sopenharmony_ci    int header_size = 8, ifd_size;
271141cc406Sopenharmony_ci    int strip_offset, data_offset, data_size;
272141cc406Sopenharmony_ci    int strip_bytecount;
273141cc406Sopenharmony_ci    int ntags;
274141cc406Sopenharmony_ci    int motorola, bps, maxsamplevalue;
275141cc406Sopenharmony_ci    void *icc_buffer = NULL;
276141cc406Sopenharmony_ci    size_t icc_size = 0;
277141cc406Sopenharmony_ci
278141cc406Sopenharmony_ci    if (icc_profile)
279141cc406Sopenharmony_ci    {
280141cc406Sopenharmony_ci      icc_buffer = sanei_load_icc_profile(icc_profile, &icc_size);
281141cc406Sopenharmony_ci    }
282141cc406Sopenharmony_ci
283141cc406Sopenharmony_ci    ifd = create_ifd ();
284141cc406Sopenharmony_ci
285141cc406Sopenharmony_ci    bps = (depth <= 8) ? 1 : 2;  /* Bytes per sample */
286141cc406Sopenharmony_ci    maxsamplevalue = (depth <= 8) ? 255 : 65535;
287141cc406Sopenharmony_ci    strip_bytecount = width * height * bps;
288141cc406Sopenharmony_ci
289141cc406Sopenharmony_ci    /* the following values must be known in advance */
290141cc406Sopenharmony_ci    ntags = 13;
291141cc406Sopenharmony_ci    data_size = 0;
292141cc406Sopenharmony_ci    if (resolution > 0)
293141cc406Sopenharmony_ci    {
294141cc406Sopenharmony_ci        ntags += 3;
295141cc406Sopenharmony_ci        data_size += 2*4 + 2*4;
296141cc406Sopenharmony_ci    }
297141cc406Sopenharmony_ci
298141cc406Sopenharmony_ci    if (icc_size > 0) /* if icc profile exists add memory for tag */
299141cc406Sopenharmony_ci    {
300141cc406Sopenharmony_ci        ntags += 1;
301141cc406Sopenharmony_ci        data_size += icc_size;
302141cc406Sopenharmony_ci    }
303141cc406Sopenharmony_ci
304141cc406Sopenharmony_ci    ifd_size = 2 + ntags*12 + 4;
305141cc406Sopenharmony_ci    data_offset = header_size + ifd_size;
306141cc406Sopenharmony_ci    strip_offset = data_offset + data_size;
307141cc406Sopenharmony_ci
308141cc406Sopenharmony_ci    /* New subfile type */
309141cc406Sopenharmony_ci    add_ifd_entry (ifd, 254, IFDE_TYP_LONG, 1, 0);
310141cc406Sopenharmony_ci    /* image width */
311141cc406Sopenharmony_ci    add_ifd_entry (ifd, 256, (width > 0xffff) ? IFDE_TYP_LONG : IFDE_TYP_SHORT,
312141cc406Sopenharmony_ci                   1, width);
313141cc406Sopenharmony_ci    /* image length */
314141cc406Sopenharmony_ci    add_ifd_entry (ifd, 257, (height > 0xffff) ? IFDE_TYP_LONG : IFDE_TYP_SHORT,
315141cc406Sopenharmony_ci                   1, height);
316141cc406Sopenharmony_ci    /* bits per sample */
317141cc406Sopenharmony_ci    add_ifd_entry (ifd, 258, IFDE_TYP_SHORT, 1, depth);
318141cc406Sopenharmony_ci    /* compression (uncompressed) */
319141cc406Sopenharmony_ci    add_ifd_entry (ifd, 259, IFDE_TYP_SHORT, 1, 1);
320141cc406Sopenharmony_ci    /* photometric interpretation */
321141cc406Sopenharmony_ci    add_ifd_entry (ifd, 262, IFDE_TYP_SHORT, 1, 1);
322141cc406Sopenharmony_ci    /* strip offset */
323141cc406Sopenharmony_ci    add_ifd_entry (ifd, 273, IFDE_TYP_LONG, 1, strip_offset);
324141cc406Sopenharmony_ci    /* orientation */
325141cc406Sopenharmony_ci    add_ifd_entry (ifd, 274, IFDE_TYP_SHORT, 1, 1);
326141cc406Sopenharmony_ci    /* samples per pixel */
327141cc406Sopenharmony_ci    add_ifd_entry (ifd, 277, IFDE_TYP_SHORT, 1, 1);
328141cc406Sopenharmony_ci    /* rows per strip */
329141cc406Sopenharmony_ci    add_ifd_entry (ifd, 278, IFDE_TYP_LONG, 1, height);
330141cc406Sopenharmony_ci    /* strip bytecount */
331141cc406Sopenharmony_ci    add_ifd_entry (ifd, 279, IFDE_TYP_LONG, 1, strip_bytecount);
332141cc406Sopenharmony_ci    /* min sample value */
333141cc406Sopenharmony_ci    add_ifd_entry (ifd, 280, IFDE_TYP_SHORT, 1, 0);
334141cc406Sopenharmony_ci    /* max sample value */
335141cc406Sopenharmony_ci    add_ifd_entry (ifd, 281, IFDE_TYP_SHORT, 1, maxsamplevalue);
336141cc406Sopenharmony_ci    if (resolution > 0)
337141cc406Sopenharmony_ci    {
338141cc406Sopenharmony_ci        /* x resolution */
339141cc406Sopenharmony_ci        add_ifd_entry (ifd, 282, IFDE_TYP_RATIONAL, 1, data_offset);
340141cc406Sopenharmony_ci        data_offset += 2*4;
341141cc406Sopenharmony_ci        /* y resolution */
342141cc406Sopenharmony_ci        add_ifd_entry (ifd, 283, IFDE_TYP_RATIONAL, 1, data_offset);
343141cc406Sopenharmony_ci        data_offset += 2*4;
344141cc406Sopenharmony_ci    }
345141cc406Sopenharmony_ci    if (resolution > 0)
346141cc406Sopenharmony_ci    {
347141cc406Sopenharmony_ci        /* resolution unit (dpi) */
348141cc406Sopenharmony_ci        add_ifd_entry (ifd, 296, IFDE_TYP_SHORT, 1, 2);
349141cc406Sopenharmony_ci    }
350141cc406Sopenharmony_ci
351141cc406Sopenharmony_ci    if (icc_size > 0) /* add ICC-profile TAG */
352141cc406Sopenharmony_ci    {
353141cc406Sopenharmony_ci      add_ifd_entry(ifd, 34675, 7, (int) icc_size, data_offset);
354141cc406Sopenharmony_ci      data_offset += icc_size;
355141cc406Sopenharmony_ci    }
356141cc406Sopenharmony_ci
357141cc406Sopenharmony_ci    /* I prefer motorola format. Its human readable. But for 16 bit, */
358141cc406Sopenharmony_ci    /* the image format is defined by SANE to be the native byte order */
359141cc406Sopenharmony_ci    if (bps == 1)
360141cc406Sopenharmony_ci    {
361141cc406Sopenharmony_ci        motorola = 1;
362141cc406Sopenharmony_ci    }
363141cc406Sopenharmony_ci    else
364141cc406Sopenharmony_ci    {int check = 1;
365141cc406Sopenharmony_ci        motorola = ((*((char *)&check)) == 0);
366141cc406Sopenharmony_ci    }
367141cc406Sopenharmony_ci
368141cc406Sopenharmony_ci    write_ifd (fptr, ifd, motorola);
369141cc406Sopenharmony_ci
370141cc406Sopenharmony_ci    /* Write x/y resolution */
371141cc406Sopenharmony_ci    if (resolution > 0)
372141cc406Sopenharmony_ci    {
373141cc406Sopenharmony_ci        write_i4 (fptr, resolution, motorola);
374141cc406Sopenharmony_ci        write_i4 (fptr, 1, motorola);
375141cc406Sopenharmony_ci        write_i4 (fptr, resolution, motorola);
376141cc406Sopenharmony_ci        write_i4 (fptr, 1, motorola);
377141cc406Sopenharmony_ci    }
378141cc406Sopenharmony_ci
379141cc406Sopenharmony_ci    if (icc_size > 0)
380141cc406Sopenharmony_ci    {
381141cc406Sopenharmony_ci      fwrite(icc_buffer, icc_size, 1, fptr);
382141cc406Sopenharmony_ci    }
383141cc406Sopenharmony_ci
384141cc406Sopenharmony_ci    free(icc_buffer);
385141cc406Sopenharmony_ci
386141cc406Sopenharmony_ci    free_ifd (ifd);
387141cc406Sopenharmony_ci}
388141cc406Sopenharmony_ci
389141cc406Sopenharmony_cistatic void
390141cc406Sopenharmony_ciwrite_tiff_color_header (FILE *fptr, int width, int height, int depth,
391141cc406Sopenharmony_ci                         int resolution, const char *icc_profile)
392141cc406Sopenharmony_ci{IFD *ifd;
393141cc406Sopenharmony_ci    int header_size = 8, ifd_size;
394141cc406Sopenharmony_ci    int strip_offset, data_offset, data_size;
395141cc406Sopenharmony_ci    int strip_bytecount;
396141cc406Sopenharmony_ci    int ntags;
397141cc406Sopenharmony_ci    int motorola, bps, maxsamplevalue;
398141cc406Sopenharmony_ci    void *icc_buffer = NULL;
399141cc406Sopenharmony_ci    size_t icc_size = 0;
400141cc406Sopenharmony_ci
401141cc406Sopenharmony_ci    if (icc_profile)
402141cc406Sopenharmony_ci    {
403141cc406Sopenharmony_ci      icc_buffer = sanei_load_icc_profile(icc_profile, &icc_size);
404141cc406Sopenharmony_ci    }
405141cc406Sopenharmony_ci
406141cc406Sopenharmony_ci
407141cc406Sopenharmony_ci    ifd = create_ifd ();
408141cc406Sopenharmony_ci
409141cc406Sopenharmony_ci    bps = (depth <= 8) ? 1 : 2;  /* Bytes per sample */
410141cc406Sopenharmony_ci    maxsamplevalue = (depth <= 8) ? 255 : 65535;
411141cc406Sopenharmony_ci    strip_bytecount = width * height * 3 * bps;
412141cc406Sopenharmony_ci
413141cc406Sopenharmony_ci    /* the following values must be known in advance */
414141cc406Sopenharmony_ci    ntags = 13;
415141cc406Sopenharmony_ci    data_size = 3*2 + 3*2 + 3*2;
416141cc406Sopenharmony_ci
417141cc406Sopenharmony_ci    if (resolution > 0)
418141cc406Sopenharmony_ci    {
419141cc406Sopenharmony_ci        ntags += 3;
420141cc406Sopenharmony_ci        data_size += 2*4 + 2*4;
421141cc406Sopenharmony_ci    }
422141cc406Sopenharmony_ci
423141cc406Sopenharmony_ci    if (icc_size > 0) /* if icc profile exists add memory for tag */
424141cc406Sopenharmony_ci    {
425141cc406Sopenharmony_ci        ntags += 1;
426141cc406Sopenharmony_ci        data_size += icc_size;
427141cc406Sopenharmony_ci    }
428141cc406Sopenharmony_ci
429141cc406Sopenharmony_ci
430141cc406Sopenharmony_ci    ifd_size = 2 + ntags*12 + 4;
431141cc406Sopenharmony_ci    data_offset = header_size + ifd_size;
432141cc406Sopenharmony_ci    strip_offset = data_offset + data_size;
433141cc406Sopenharmony_ci
434141cc406Sopenharmony_ci    /* New subfile type */
435141cc406Sopenharmony_ci    add_ifd_entry (ifd, 254, IFDE_TYP_LONG, 1, 0);
436141cc406Sopenharmony_ci    /* image width */
437141cc406Sopenharmony_ci    add_ifd_entry (ifd, 256, (width > 0xffff) ? IFDE_TYP_LONG : IFDE_TYP_SHORT,
438141cc406Sopenharmony_ci                   1, width);
439141cc406Sopenharmony_ci    /* image length */
440141cc406Sopenharmony_ci    add_ifd_entry (ifd, 257, (height > 0xffff) ? IFDE_TYP_LONG : IFDE_TYP_SHORT,
441141cc406Sopenharmony_ci                   1, height);
442141cc406Sopenharmony_ci    /* bits per sample */
443141cc406Sopenharmony_ci    add_ifd_entry (ifd, 258, IFDE_TYP_SHORT, 3, data_offset);
444141cc406Sopenharmony_ci    data_offset += 3*2;
445141cc406Sopenharmony_ci    /* compression (uncompressed) */
446141cc406Sopenharmony_ci    add_ifd_entry (ifd, 259, IFDE_TYP_SHORT, 1, 1);
447141cc406Sopenharmony_ci    /* photometric interpretation */
448141cc406Sopenharmony_ci    add_ifd_entry (ifd, 262, IFDE_TYP_SHORT, 1, 2);
449141cc406Sopenharmony_ci    /* strip offset */
450141cc406Sopenharmony_ci    add_ifd_entry (ifd, 273, IFDE_TYP_LONG, 1, strip_offset);
451141cc406Sopenharmony_ci    /* orientation */
452141cc406Sopenharmony_ci    add_ifd_entry (ifd, 274, IFDE_TYP_SHORT, 1, 1);
453141cc406Sopenharmony_ci    /* samples per pixel */
454141cc406Sopenharmony_ci    add_ifd_entry (ifd, 277, IFDE_TYP_SHORT, 1, 3);
455141cc406Sopenharmony_ci    /* rows per strip */
456141cc406Sopenharmony_ci    add_ifd_entry (ifd, 278, IFDE_TYP_LONG, 1, height);
457141cc406Sopenharmony_ci    /* strip bytecount */
458141cc406Sopenharmony_ci    add_ifd_entry (ifd, 279, IFDE_TYP_LONG, 1, strip_bytecount);
459141cc406Sopenharmony_ci    /* min sample value */
460141cc406Sopenharmony_ci    add_ifd_entry (ifd, 280, IFDE_TYP_SHORT, 3, data_offset);
461141cc406Sopenharmony_ci    data_offset += 3*2;
462141cc406Sopenharmony_ci    /* max sample value */
463141cc406Sopenharmony_ci    add_ifd_entry (ifd, 281, IFDE_TYP_SHORT, 3, data_offset);
464141cc406Sopenharmony_ci    data_offset += 3*2;
465141cc406Sopenharmony_ci
466141cc406Sopenharmony_ci    if (resolution > 0)
467141cc406Sopenharmony_ci    {
468141cc406Sopenharmony_ci        /* x resolution */
469141cc406Sopenharmony_ci        add_ifd_entry (ifd, 282, IFDE_TYP_RATIONAL, 1, data_offset);
470141cc406Sopenharmony_ci        data_offset += 2*4;
471141cc406Sopenharmony_ci        /* y resolution */
472141cc406Sopenharmony_ci        add_ifd_entry (ifd, 283, IFDE_TYP_RATIONAL, 1, data_offset);
473141cc406Sopenharmony_ci        data_offset += 2*4;
474141cc406Sopenharmony_ci    }
475141cc406Sopenharmony_ci
476141cc406Sopenharmony_ci    if (resolution > 0)
477141cc406Sopenharmony_ci    {
478141cc406Sopenharmony_ci        /* resolution unit (dpi) */
479141cc406Sopenharmony_ci        add_ifd_entry (ifd, 296, IFDE_TYP_SHORT, 1, 2);
480141cc406Sopenharmony_ci    }
481141cc406Sopenharmony_ci
482141cc406Sopenharmony_ci    if (icc_size > 0) /* add ICC-profile TAG */
483141cc406Sopenharmony_ci    {
484141cc406Sopenharmony_ci      add_ifd_entry(ifd, 34675, 7, (int) icc_size, data_offset);
485141cc406Sopenharmony_ci      data_offset += icc_size;
486141cc406Sopenharmony_ci    }
487141cc406Sopenharmony_ci
488141cc406Sopenharmony_ci
489141cc406Sopenharmony_ci    /* I prefer motorola format. Its human readable. But for 16 bit, */
490141cc406Sopenharmony_ci    /* the image format is defined by SANE to be the native byte order */
491141cc406Sopenharmony_ci    if (bps == 1)
492141cc406Sopenharmony_ci    {
493141cc406Sopenharmony_ci        motorola = 1;
494141cc406Sopenharmony_ci    }
495141cc406Sopenharmony_ci    else
496141cc406Sopenharmony_ci    {int check = 1;
497141cc406Sopenharmony_ci        motorola = ((*((char *)&check)) == 0);
498141cc406Sopenharmony_ci    }
499141cc406Sopenharmony_ci
500141cc406Sopenharmony_ci    write_ifd (fptr, ifd, motorola);
501141cc406Sopenharmony_ci
502141cc406Sopenharmony_ci    /* Write bits per sample value values */
503141cc406Sopenharmony_ci    write_i2 (fptr, depth, motorola);
504141cc406Sopenharmony_ci    write_i2 (fptr, depth, motorola);
505141cc406Sopenharmony_ci    write_i2 (fptr, depth, motorola);
506141cc406Sopenharmony_ci
507141cc406Sopenharmony_ci    /* Write min sample value values */
508141cc406Sopenharmony_ci    write_i2 (fptr, 0, motorola);
509141cc406Sopenharmony_ci    write_i2 (fptr, 0, motorola);
510141cc406Sopenharmony_ci    write_i2 (fptr, 0, motorola);
511141cc406Sopenharmony_ci
512141cc406Sopenharmony_ci    /* Write max sample value values */
513141cc406Sopenharmony_ci    write_i2 (fptr, maxsamplevalue, motorola);
514141cc406Sopenharmony_ci    write_i2 (fptr, maxsamplevalue, motorola);
515141cc406Sopenharmony_ci    write_i2 (fptr, maxsamplevalue, motorola);
516141cc406Sopenharmony_ci
517141cc406Sopenharmony_ci    /* Write x/y resolution */
518141cc406Sopenharmony_ci    if (resolution > 0)
519141cc406Sopenharmony_ci    {
520141cc406Sopenharmony_ci        write_i4 (fptr, resolution, motorola);
521141cc406Sopenharmony_ci        write_i4 (fptr, 1, motorola);
522141cc406Sopenharmony_ci        write_i4 (fptr, resolution, motorola);
523141cc406Sopenharmony_ci        write_i4 (fptr, 1, motorola);
524141cc406Sopenharmony_ci    }
525141cc406Sopenharmony_ci
526141cc406Sopenharmony_ci    /* Write ICC profile */
527141cc406Sopenharmony_ci    if (icc_size > 0)
528141cc406Sopenharmony_ci    {
529141cc406Sopenharmony_ci      fwrite(icc_buffer, icc_size, 1, fptr);
530141cc406Sopenharmony_ci    }
531141cc406Sopenharmony_ci
532141cc406Sopenharmony_ci    free(icc_buffer);
533141cc406Sopenharmony_ci
534141cc406Sopenharmony_ci    free_ifd (ifd);
535141cc406Sopenharmony_ci}
536141cc406Sopenharmony_ci
537141cc406Sopenharmony_ci
538141cc406Sopenharmony_civoid
539141cc406Sopenharmony_cisanei_write_tiff_header (SANE_Frame format, int width, int height, int depth,
540141cc406Sopenharmony_ci			 int resolution, const char *icc_profile, FILE *ofp)
541141cc406Sopenharmony_ci{
542141cc406Sopenharmony_ci#ifdef __EMX__	/* OS2 - write in binary mode. */
543141cc406Sopenharmony_ci    _fsetmode(ofp, "b");
544141cc406Sopenharmony_ci#endif
545141cc406Sopenharmony_ci    switch (format)
546141cc406Sopenharmony_ci    {
547141cc406Sopenharmony_ci    case SANE_FRAME_RED:
548141cc406Sopenharmony_ci    case SANE_FRAME_GREEN:
549141cc406Sopenharmony_ci    case SANE_FRAME_BLUE:
550141cc406Sopenharmony_ci    case SANE_FRAME_RGB:
551141cc406Sopenharmony_ci        write_tiff_color_header (ofp, width, height, depth, resolution, icc_profile);
552141cc406Sopenharmony_ci        break;
553141cc406Sopenharmony_ci
554141cc406Sopenharmony_ci    default:
555141cc406Sopenharmony_ci        if (depth == 1)
556141cc406Sopenharmony_ci            write_tiff_bw_header (ofp, width, height, resolution);
557141cc406Sopenharmony_ci        else
558141cc406Sopenharmony_ci            write_tiff_grey_header (ofp, width, height, depth, resolution, icc_profile);
559141cc406Sopenharmony_ci        break;
560141cc406Sopenharmony_ci    }
561141cc406Sopenharmony_ci}
562