/********************************************************************** * File: imgtiff.c (Formerly tiff.c) * Description: Max format image reader/writer. * Author: Ray Smith * Created: Mon Jun 11 14:00:21 BST 1990 * * (C) Copyright 1990, Hewlett-Packard Ltd. ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** http://www.apache.org/licenses/LICENSE-2.0 ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. * **********************************************************************/ #include "mfcpch.h" //precompiled headers #ifdef __MSW32__ #include #else #include #endif /* ** Include automatically generated configuration file if running autoconf */ #ifdef HAVE_CONFIG_H #include "config_auto.h" #if defined(MOTOROLA_BYTE_ORDER) || defined(WORDS_BIGENDIAN) #define __MOTO__ // Big-endian. #endif #endif #include "fileerr.h" #include "imgerrs.h" #include "img.h" #include "bitstrm.h" #include "tprintf.h" #include "serialis.h" #include "imgtiff.h" #define INTEL 0x4949 #define MOTO 0x4d4d /************************************************************************* * NOTE ON BIG-ENDIAN vs LITTLE-ENDIAN * * Intel machines store numbers with LSByte in the left position. * Motorola (and PA_RISC) machines use the opposite byte ordering. * * This code is written so that: * a) it will compile and run on EITHER machine type AND * b) the program (on either machine) will process tiff file written in either * Motorola or Intel format. * * The code is compiled with a __NATIVE__ define which is either MOTO or INTEL. * MOTO and INTEL are defined (above) to be the value of the first two bytes of * a tiff file in either format. (This identifies the filetype). * * Subsequent reads and writes normally just reverse the byte order if the * machine type (__NATIVE__) is not equal to the filetype determined from the * first two bytes of the tiff file. * * A special case is the "value" field of the tag structure. This can contain * EITHER a 16bit or a 32bit value. According to the "type" field. The 4 cases * of machine type / file type combinations need to be treated differently in * the case of 16 bit values *************************************************************************/ #define ENTRIES 19 /*no of entries */ #define START 8 /*start of tag table */ typedef struct { uinT16 tag; //entry tag uinT16 type; uinT32 length; inT32 value; } TIFFENTRY; //tiff tag entry typedef struct myrational { inT32 top; inT32 bottom; } MYRATIONAL; //type 5 //statics for the run length codes #define EOL_CODE 0x800 #define EOL_MASK 0xfff #define EOL_LENGTH 12 //12 bits #define SHORT_CODE_SIZE 64 //no of short codes #define LONG_CODE_SIZE 40 //no of long codes static uinT16 short_white_codes[SHORT_CODE_SIZE] = { 0xac, 0x38, 0xe, 0x1, 0xd, 0x3, 0x7, 0xf, 0x19, 0x5, 0x1c, 0x2, 0x4, 0x30, 0xb, 0x2b, 0x15, 0x35, 0x72, 0x18, 0x8, 0x74, 0x60, 0x10, 0xa, 0x6a, 0x64, 0x12, 0xc, 0x40, 0xc0, 0x58, 0xd8, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x20, 0xa0, 0x50, 0xd0, 0x4a, 0xca, 0x2a, 0xaa, 0x24, 0xa4, 0x1a, 0x9a, 0x5a, 0xda, 0x52, 0xd2, 0x4c, 0xcc, 0x2c }; static uinT8 short_white_lengths[SHORT_CODE_SIZE] = { 8, 6, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }; static uinT16 short_black_codes[SHORT_CODE_SIZE] = { 0x3b0, 0x2, 0x3, 0x1, 0x6, 0xc, 0x4, 0x18, 0x28, 0x8, 0x10, 0x50, 0x70, 0x20, 0xe0, 0x30, 0x3a0, 0x60, 0x40, 0x730, 0xb0, 0x1b0, 0x760, 0xa0, 0x740, 0xc0, 0x530, 0xd30, 0x330, 0xb30, 0x160, 0x960, 0x560, 0xd60, 0x4b0, 0xcb0, 0x2b0, 0xab0, 0x6b0, 0xeb0, 0x360, 0xb60, 0x5b0, 0xdb0, 0x2a0, 0xaa0, 0x6a0, 0xea0, 0x260, 0xa60, 0x4a0, 0xca0, 0x240, 0xec0, 0x1c0, 0xe40, 0x140, 0x1a0, 0x9a0, 0xd40, 0x340, 0x5a0, 0x660, 0xe60 }; static uinT8 short_black_lengths[SHORT_CODE_SIZE] = { 10, 3, 2, 2, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8, 8, 9, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12 }; static uinT16 long_white_codes[LONG_CODE_SIZE] = { 0x1b, 0x9, 0x3a, 0x76, 0x6c, 0xec, 0x26, 0xa6, 0x16, 0xe6, 0x66, 0x166, 0x96, 0x196, 0x56, 0x156, 0xd6, 0x1d6, 0x36, 0x136, 0xb6, 0x1b6, 0x32, 0x132, 0xb2, 0x6, 0x1b2, 0x80, 0x180, 0x580, 0x480, 0xc80, 0x280, 0xa80, 0x680, 0xe80, 0x380, 0xb80, 0x780, 0xf80 }; static uinT8 long_white_lengths[LONG_CODE_SIZE] = { 5, 5, 6, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 6, 9, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12 }; static uinT16 long_black_codes[LONG_CODE_SIZE] = { 0x3c0, 0x130, 0x930, 0xda0, 0xcc0, 0x2c0, 0xac0, 0x6c0, 0x16c0, 0xa40, 0x1a40, 0x640, 0x1640, 0x9c0, 0x19c0, 0x5c0, 0x15c0, 0xdc0, 0x1dc0, 0x940, 0x1940, 0x540, 0x1540, 0xb40, 0x1b40, 0x4c0, 0x14c0, 0x80, 0x180, 0x580, 0x480, 0xc80, 0x280, 0xa80, 0x680, 0xe80, 0x380, 0xb80, 0x780, 0xf80 }; static uinT8 long_black_lengths[LONG_CODE_SIZE] = { 10, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12 }; /********************************************************************** * open_tif_image * * Read the header of a tif format image and prepare to read the rest. **********************************************************************/ inT8 open_tif_image( //read header int fd, //file to read inT32 *xsize, //size of image inT32 *ysize, inT8 *bpp, //bits per pixel inT8 *photo, //interpretation inT32 *res //resolution ) { inT16 filetype; inT32 start; //start of tiff directory inT16 entries; //no of tiff entries inT32 imagestart; //location of image in file inT32 resoffset; //location of res TIFFENTRY tiffentry; //tag table entry BOOL8 compressed; //compression control MYRATIONAL resinfo; //resolution BOOL8 strips = false; //if in strips *xsize = -1; //illegal values *ysize = -1; *bpp = -1; *res = -1; resoffset = -1; if (read (fd, (char *) &filetype, sizeof filetype) != sizeof filetype || (filetype != INTEL && filetype != MOTO)) { BADIMAGEFORMAT.error ("read_tif_image", TESSLOG, "Filetype"); return -1; } lseek (fd, 4L, 0); if (read (fd, (char *) &start, sizeof start) != sizeof start) { READFAILED.error ("read_tif_image", TESSLOG, "Start of tag table"); return -1; } if (filetype != __NATIVE__) start = reverse32 (start); if (start <= 0) { BADIMAGEFORMAT.error ("read_tif_image", TESSLOG, "Start of tag table"); return -1; } lseek (fd, start, 0); if (read (fd, (char *) &entries, sizeof (inT16)) != sizeof (inT16)) { BADIMAGEFORMAT.error ("read_tif_image", TESSLOG, "Size of tag table"); return -1; } if (filetype != __NATIVE__) entries = reverse16 (entries); // printf("No of tiff directory entries=%d\n",entries); imagestart = 0; compressed = FALSE; for (; entries-- > 0;) { if (read (fd, (char *) &tiffentry, sizeof tiffentry) != sizeof tiffentry) { BADIMAGEFORMAT.error ("read_tif_image", TESSLOG, "Tag table entry"); return -1; } if (filetype != __NATIVE__) { tiffentry.type = reverse16 (tiffentry.type); tiffentry.tag = reverse16 (tiffentry.tag); tiffentry.length = reverse32 (tiffentry.length); } if (tiffentry.type != 3) { //Full 32bit value if (filetype != __NATIVE__) tiffentry.value = reverse32 (tiffentry.value); } else { /* A 16bit value in 4 bytes - handle with care. SEE NOTE at start of file */ if (__NATIVE__ == MOTO) { if (filetype == MOTO) //MOTO file on MOTO Machine tiffentry.value = tiffentry.value >> 16; else //INTEL file on MOTO Machine tiffentry.value = reverse32 (tiffentry.value); } else { //INTEL Machine if (filetype == MOTO) //MOTO file on INTEL Machine tiffentry.value = reverse16 ((uinT16) tiffentry.value); //INTEL file on INTEL Machine NO ACTION NEEDED } //Clear top 2 MSBytes tiffentry.value &= 0x0000ffff; } // printf("Tag=%x, Type=%x, Length=%x, value=%x\n", // tiffentry.tag,tiffentry.type,tiffentry.length,tiffentry.value); switch (tiffentry.tag) { case 0x101: *ysize = tiffentry.value; break; case 0x100: *xsize = tiffentry.value; break; case 0x102: if (tiffentry.length == 1) *bpp = (inT8) tiffentry.value; else *bpp = 24; break; case 0x111: imagestart = tiffentry.value; strips = tiffentry.length > 1; break; case 0x103: if (tiffentry.value == 3) { compressed = TRUE; } else if (tiffentry.value != 1) { BADIMAGEFORMAT.error ("read_tif_image", TESSLOG, "Compression"); return -1; } break; case 0x11a: case 0x11b: //resolution resoffset = tiffentry.value; break; case 0x106: *photo = (inT8) tiffentry.value; break; } //endswitch } if (*xsize <= 0 || *ysize <= 0 || *bpp > 24 || imagestart <= 0) { BADIMAGEFORMAT.error ("read_tif_image", TESSLOG, "Vital tag"); return -1; } tprintf ("Image has %d bit%c per pixel and size (%d,%d)\n", *bpp, *bpp == 1 ? ' ' : 's', *xsize, *ysize); if (resoffset >= 0) { lseek (fd, resoffset, 0); if (read (fd, (char *) &resinfo, sizeof (resinfo)) != sizeof (resinfo)) { READFAILED.error ("read_tif_image", TESSLOG, "Resolution"); return -1; } if (filetype != __NATIVE__) { resinfo.top = reverse32 (resinfo.top); resinfo.bottom = reverse32 (resinfo.bottom); } *res = resinfo.top / resinfo.bottom; tprintf ("Resolution=%d\n", *res); } lseek (fd, (long) imagestart, 0); if (strips) { if (read (fd, (char *) &imagestart, sizeof (imagestart)) != sizeof (imagestart)) { READFAILED.error ("read_tif_image", TESSLOG, "Strip offset"); return -1; } if (filetype != __NATIVE__) imagestart = reverse32 (imagestart); //indirection lseek (fd, (long) imagestart, 0); } return compressed ? -2 : 0; } /********************************************************************** * read_tif_image * * Read a whole tif image into memory. **********************************************************************/ inT8 read_tif_image( //read whole image int fd, //file to read uinT8 *pixels, //pixels of image inT32 xsize, //size of image inT32 ysize, inT8 bpp, //bits per pixel inT32 //bytes per line ) { inT32 xindex; //indices in image inT32 yindex; inT32 length; //short length inT32 biglength; //extender uinT8 *lengths; //current lengths uinT16 *codes; //current codes uinT16 codeword; //current code word IMAGELINE imageline; //current line IMAGE image; //dummy image R_BITSTREAM bits; //read bitstream uinT8 colour; //current colour image.capture (pixels, xsize, ysize, bpp); codeword = bits.open (fd); //open bitstream read_eol(&bits, codeword); //find end of line for (yindex = ysize - 1; yindex >= 0; yindex--) { imageline.init (); colour = TRUE; for (xindex = 0; xindex < xsize;) { if (colour) { lengths = long_white_lengths; codes = long_white_codes; } else { lengths = long_black_lengths; codes = long_black_codes; } for (biglength = 0; biglength < LONG_CODE_SIZE && (codeword & bits.masks (*lengths)) != *codes; codes++, lengths++, biglength++); if (biglength < LONG_CODE_SIZE) { codeword = bits.read_code (*lengths); biglength++; biglength *= SHORT_CODE_SIZE; } else biglength = 0; if (colour) { lengths = short_white_lengths; codes = short_white_codes; } else { lengths = short_black_lengths; codes = short_black_codes; } for (length = 0; length < SHORT_CODE_SIZE && (codeword & bits.masks (*lengths)) != *codes; codes++, lengths++, length++); if (length < SHORT_CODE_SIZE) { codeword = bits.read_code (*lengths); for (length += biglength; length > 0; length--, xindex++) imageline.pixels[xindex] = colour; colour = !colour; } else break; } if (xindex < xsize) { tprintf ("%d pixels short on line %d", xsize - xindex, yindex); tprintf (", unknown code=%x\n", codeword); } xindex = read_eol (&bits, codeword); if (xindex > 0) tprintf ("Discarding %d bits on line %d\n", xindex, yindex); image.put_line (0, yindex, xsize, &imageline, 0); } return 0; } /********************************************************************** * read_eol * * Take bits out of the stream until and end-of-line code is hit. **********************************************************************/ inT32 read_eol( //read end of line R_BITSTREAM *bits, //bitstream to read uinT16 &code //current code ) { BOOL8 anyones; //any 1 bits skipped inT32 bitcount; //total bits skipped anyones = FALSE; bitcount = 0; while ((code & EOL_MASK) != EOL_CODE) { if (code & 1) anyones = TRUE; //discarded one bit bitcount++; //total discarded bits code = bits->read_code (1); //take single bits } //extract EOL code code = bits->read_code (EOL_LENGTH); if (!anyones) bitcount = 0; //ignore filler bits return bitcount; } /********************************************************************** * write_moto_tif * * Write a whole tif format image and close the file. **********************************************************************/ inT8 write_moto_tif( //write whole image int fd, //file to write on uinT8 *pixels, //image pixels inT32 xsize, //size of image inT32 ysize, inT8 bpp, //bits per pixel inT8 photo, inT32 res //resolution ) { return write_tif_image (fd, pixels, xsize, ysize, bpp, res, MOTO, photo); //use moto format } /********************************************************************** * write_intel_tif * * Write a whole tif format image and close the file. **********************************************************************/ inT8 write_intel_tif( //write whole image int fd, //file to write on uinT8 *pixels, //image pixels inT32 xsize, //size of image inT32 ysize, inT8 bpp, //bits per pixel inT8 photo, inT32 res //resolution ) { return write_tif_image (fd, pixels, xsize, ysize, bpp, res, INTEL, photo); //use intel format } /********************************************************************** * write_inverse_tif * * Write a whole tif format image and close the file. **********************************************************************/ inT8 write_inverse_tif( //write whole image int fd, //file to write on uinT8 *pixels, //image pixels inT32 xsize, //size of image inT32 ysize, inT8 bpp, //bits per pixel inT8 photo, inT32 res //resolution ) { return write_tif_image (fd, pixels, xsize, ysize, bpp, res, INTEL, 1 - photo); //use intel format } /********************************************************************** * write_tif_image * * Write a whole tif format image and close the file. **********************************************************************/ inT8 write_tif_image( //write whole image int fd, //file to write on uinT8 *pixels, //image pixels inT32 xsize, //size of image inT32 ysize, inT8 bpp, //bits per pixel inT32 res, //resolution inT16 type, //format type inT16 photo //metric interp ) { inT32 size; //line/image size inT16 entries; //no of tiff entries inT32 start; //start of tag table inT32 zero = 0; MYRATIONAL resolution; //resolution TIFFENTRY entry; //current entry static TIFFENTRY tags[ENTRIES] = { {0xfe, 4, 1, 0}, {0x100, 3, 1, 0}, {0x101, 3, 1, 0}, {0x102, 3, 1, 0}, {0x103, 3, 1, 1}, {0x106, 3, 1, 1}, { /*line art */ 0x107, 3, 1, 1 }, {0x10a, 3, 1, 1}, { 0x111, 4, 1, START + ENTRIES * sizeof (TIFFENTRY) + sizeof (inT32) + sizeof (short) + sizeof (MYRATIONAL) * 2 } , {0x112, 3, 1, 1} , {0x115, 3, 1, 1} , {0x116, 4, 1, 0} , {0x117, 4, 1, 0} , {0x118, 3, 1, 0} , {0x119, 3, 1, 1} , { 0x11a, 5, 1, START + ENTRIES * sizeof (TIFFENTRY) + sizeof (inT32) + sizeof (short) }, { 0x11b, 5, 1, START + ENTRIES * sizeof (TIFFENTRY) + sizeof (inT32) + sizeof (short) + sizeof (MYRATIONAL) } , {0x11c, 3, 1, 1} , {0x128, 3, 1, 2} }; resolution.top = res; resolution.bottom = 1; if (write (fd, (char *) &type, sizeof type) != sizeof type || (type != INTEL && type != MOTO)) { WRITEFAILED.error ("write_tif_image", TESSLOG, "Filetype"); return -1; } start = START; entries = 0x002a; if (type != __NATIVE__) entries = reverse16 (entries); if (write (fd, (char *) &entries, sizeof entries) != sizeof entries) { WRITEFAILED.error ("write_tif_image", TESSLOG, "Version"); return -1; } if (type != __NATIVE__) start = reverse32 (start); if (write (fd, (char *) &start, sizeof start) != sizeof start) { WRITEFAILED.error ("write_tif_image", TESSLOG, "Start"); return -1; } lseek (fd, (long) START, 0); entries = ENTRIES; if (type != __NATIVE__) entries = reverse16 (entries); if (write (fd, (char *) &entries, sizeof entries) != sizeof entries) { WRITEFAILED.error ("write_tif_image", TESSLOG, "Entries"); return -1; } //line length size = COMPUTE_IMAGE_XDIM (xsize, bpp); size *= ysize; //total image size // if (photo==0) // { // tags[0].tag=0xfe; // tags[0].type=4; // tags[0].value=0; // } // else // { // tags[0].tag=0xff; // tags[0].type=3; // tags[0].value=1; // } tags[1].value = xsize; tags[2].value = ysize; if (bpp == 24) { tags[3].value = 8; tags[10].value = 3; tags[5].value = 2; } else { tags[3].value = bpp; tags[5].value = photo; } tags[11].value = ysize; tags[14].value = (1 << bpp) - 1; tags[12].value = size; for (entries = 0; entries < ENTRIES; entries++) { entry = tags[entries]; //get an entry /* NB Convert entry.value BEFORE converting entry.type!!! */ if (entry.type != 3) { //Full 32bit value if (type != __NATIVE__) entry.value = reverse32 (entry.value); } else { /* A 16bit value in 4 bytes - handle with care. SEE NOTE at start of file */ entry.value &= 0x0000ffff; //Ensure top 2 MSBytes clear if (__NATIVE__ == MOTO) { if (type == MOTO) //MOTO file on MOTO Machine entry.value = entry.value << 16; else //INTEL file on MOTO Machine entry.value = reverse32 (entry.value); } else { //INTEL Machine if (type == MOTO) //MOTO file on INTEL Machine entry.value = reverse16 ((uinT16) entry.value); //INTEL file on INTEL Machine NO ACTION NEEDED } } if (type != __NATIVE__) { entry.tag = reverse16 (entry.tag); entry.type = reverse16 (entry.type); entry.length = reverse32 (entry.length); } if (write (fd, (char *) &entry, sizeof (TIFFENTRY)) != sizeof (TIFFENTRY)) { WRITEFAILED.error ("write_tif_image", TESSLOG, "Tag Table"); return -1; } } if (write (fd, (char *) &zero, sizeof zero) != sizeof zero) { WRITEFAILED.error ("write_tif_image", TESSLOG, "Tag table Terminator"); return -1; } if (type != __NATIVE__) { resolution.top = reverse32 (resolution.top); resolution.bottom = reverse32 (resolution.bottom); } if (write (fd, (char *) &resolution, sizeof resolution) != sizeof resolution || write (fd, (char *) &resolution, sizeof resolution) != sizeof resolution) { WRITEFAILED.error ("write_tif_image", TESSLOG, "Resolution"); return -1; } if (write (fd, (char *) pixels, (size_t) size) != size) { WRITEFAILED.error ("write_tif_image", TESSLOG, "Image"); return -1; } close(fd); return 0; } /********************************************************************** * reverse32 * * Byte swap the 32 bit number between Motorola & Intel format. **********************************************************************/ //inT32 reverse32( //reverse 32 bit int //uinT32 value //value to reverse //) //{ // return (value>>24) | (value>>8) & 0xff00 // | (value<<8) & 0xff0000 | (value<<24); //} /********************************************************************** * reverse16 * * Byte swap the 16 bit number between Motorola & Intel format. **********************************************************************/ //inT16 reverse16( //reverse 16 bit int //uinT16 value //value to reverse //) //{ // return (value>>8) | (value<<8); //}