520 lines
11 KiB
C
520 lines
11 KiB
C
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cpsi2tif.c
|
|
|
|
Abstract:
|
|
|
|
converting the bitmap from CPSI to a TIFF file
|
|
usage: cpsi2tif input-file output-file
|
|
|
|
Revision History:
|
|
|
|
12/30/96 -davidx-
|
|
Created it.
|
|
|
|
--*/
|
|
|
|
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
|
|
#define LITTLE_ENDIAN 1
|
|
|
|
#define PAGE_HEADER 0x00000101
|
|
#define PAGE_TRAILER 0x00000004
|
|
#define BAND_HEADER 0x00000003
|
|
|
|
// we assume 32-bit integer values are stored in
|
|
// big-endian format in the input file.
|
|
|
|
#if LITTLE_ENDIAN
|
|
|
|
#define swap_integer(n) \
|
|
((((n) >> 24) & 0x00ff) | \
|
|
(((n) >> 8) & 0xff00) | \
|
|
(((n) & 0xff00) << 8) | \
|
|
(((n) & 0x00ff) << 24))
|
|
|
|
#else //!LITTLE_ENDIAN
|
|
|
|
#define swap_integer(n) (n)
|
|
|
|
#endif //!LITTLE_ENDIAN
|
|
|
|
#define ASSERT(cond) { \
|
|
if (! (cond)) \
|
|
error("assertion failed on line %d\n", __LINE__); \
|
|
}
|
|
|
|
|
|
// bitmap header structure at the beginning of each page
|
|
|
|
typedef long INT32;
|
|
typedef unsigned long UINT32;
|
|
typedef short INT16;
|
|
typedef unsigned short UINT16;
|
|
typedef unsigned char UINT8;
|
|
|
|
typedef struct {
|
|
|
|
INT32 byteWidth;
|
|
INT32 height;
|
|
INT32 topMargin;
|
|
INT32 leftMargin;
|
|
INT32 bandHeight;
|
|
INT32 xResolution;
|
|
INT32 yResolution;
|
|
INT32 imagingOrder;
|
|
INT32 planeOrder;
|
|
INT32 depth;
|
|
INT32 dcc;
|
|
INT32 firstColor;
|
|
INT32 redFirst;
|
|
INT32 redN;
|
|
INT32 redDelta;
|
|
INT32 greenFirst;
|
|
INT32 greenN;
|
|
INT32 greenDelta;
|
|
INT32 blueFirst;
|
|
INT32 blueN;
|
|
INT32 blueDelta;
|
|
INT32 grayFirst;
|
|
INT32 grayN;
|
|
INT32 grayDelta;
|
|
INT32 isPlanar;
|
|
INT32 scanUnit;
|
|
INT32 swapBits;
|
|
|
|
} BITMAP_HEADER, *PBITMAP_HEADER;
|
|
|
|
|
|
char* progname;
|
|
FILE* fin;
|
|
FILE* fout;
|
|
INT32 outputByteCnt;
|
|
int useStrips = 1;
|
|
|
|
|
|
void
|
|
error(
|
|
char *fmtstr,
|
|
...
|
|
)
|
|
|
|
{
|
|
va_list arglist;
|
|
|
|
fprintf(stderr, "%s: ", progname);
|
|
|
|
va_start(arglist, fmtstr);
|
|
vfprintf(stderr, fmtstr, arglist);
|
|
va_end(arglist);
|
|
|
|
exit(-1);
|
|
}
|
|
|
|
|
|
void*
|
|
alloc_memory(
|
|
int size
|
|
)
|
|
|
|
{
|
|
void* p;
|
|
|
|
if ((p = malloc(size)) == NULL)
|
|
error("out of memory\n");
|
|
|
|
memset(p, 0, size);
|
|
return p;
|
|
}
|
|
|
|
|
|
INT32
|
|
read_integer(
|
|
void
|
|
)
|
|
|
|
{
|
|
INT32 n;
|
|
|
|
if (fread(&n, sizeof(n), 1, fin) != 1)
|
|
error("error reading input file\n");
|
|
|
|
return swap_integer(n);
|
|
}
|
|
|
|
|
|
void
|
|
write_output_data(
|
|
void* p,
|
|
int size
|
|
)
|
|
|
|
{
|
|
if (fwrite(p, size, 1, fout) != 1)
|
|
error("error writing output file\n");
|
|
|
|
outputByteCnt += size;
|
|
}
|
|
|
|
|
|
#include "tiffcomp.c"
|
|
|
|
|
|
int
|
|
write_tiff_fileheader(
|
|
void
|
|
)
|
|
|
|
{
|
|
write_output_data(&tiffFileHeader, sizeof(tiffFileHeader));
|
|
return offsetof(TIFF_FILEHEADER, firstIFDOffset);
|
|
}
|
|
|
|
|
|
void
|
|
align_output_data(
|
|
void
|
|
)
|
|
|
|
{
|
|
if (outputByteCnt & 1)
|
|
{
|
|
char ch = 0;
|
|
write_output_data(&ch, 1);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
write_nextifd_offset(
|
|
int offset,
|
|
INT32 value
|
|
)
|
|
|
|
{
|
|
// update the next IFD offset and move to end of file
|
|
|
|
if (fseek(fout, offset, SEEK_SET) != 0 ||
|
|
fwrite(&value, sizeof(value), 1, fout) != 1 ||
|
|
fseek(fout, 0, SEEK_END) != 0)
|
|
{
|
|
error("couldn't write IFD offset\n");
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
process_page(
|
|
int prevIFDOffset
|
|
)
|
|
|
|
{
|
|
BITMAP_HEADER bitmapHeader;
|
|
int ifd_offset;
|
|
int strip_count, strip_index;
|
|
INT16 ifd_count;
|
|
IFD ifd;
|
|
UINT8* cur_line;
|
|
UINT8* ref_line;
|
|
int totalLines, bandLines, lineBytes, lineBits;
|
|
static int pageNumber = 0;
|
|
|
|
// read in bitmap header
|
|
|
|
if (fread(&bitmapHeader, sizeof(bitmapHeader), 1, fin) != 1)
|
|
error("couldn't read bitmap header\n");
|
|
|
|
if (LITTLE_ENDIAN)
|
|
{
|
|
INT32* p;
|
|
int n;
|
|
|
|
p = (INT32*) &bitmapHeader;
|
|
n = sizeof(bitmapHeader) / sizeof(INT32);
|
|
ASSERT(sizeof(bitmapHeader) % sizeof(INT32) == 0)
|
|
|
|
while (n--)
|
|
{
|
|
*p = swap_integer(*p);
|
|
p++;
|
|
}
|
|
}
|
|
|
|
// valid bitmap header fields
|
|
// we're only able to deal with 1-bpp monochrome images for now
|
|
|
|
if (bitmapHeader.byteWidth <= 0 ||
|
|
bitmapHeader.height <= 0 ||
|
|
bitmapHeader.topMargin != 0 ||
|
|
bitmapHeader.leftMargin != 0 ||
|
|
bitmapHeader.bandHeight <= 0 ||
|
|
bitmapHeader.bandHeight > bitmapHeader.height ||
|
|
bitmapHeader.xResolution <= 0 ||
|
|
bitmapHeader.yResolution <= 0 ||
|
|
bitmapHeader.imagingOrder != 0 ||
|
|
bitmapHeader.planeOrder != 0 ||
|
|
bitmapHeader.depth != 1 ||
|
|
bitmapHeader.dcc != 0 ||
|
|
bitmapHeader.firstColor != 0 ||
|
|
bitmapHeader.redFirst != 0 ||
|
|
bitmapHeader.redN != 0 ||
|
|
bitmapHeader.redDelta != 0 ||
|
|
bitmapHeader.greenFirst != 0 ||
|
|
bitmapHeader.greenN != 0 ||
|
|
bitmapHeader.greenDelta != 0 ||
|
|
bitmapHeader.blueFirst != 0 ||
|
|
bitmapHeader.blueN != 0 ||
|
|
bitmapHeader.blueDelta != 0 ||
|
|
bitmapHeader.grayFirst != 0 ||
|
|
bitmapHeader.grayN != 2 ||
|
|
bitmapHeader.grayDelta != 1 ||
|
|
bitmapHeader.isPlanar != 0 ||
|
|
bitmapHeader.scanUnit != 4 ||
|
|
bitmapHeader.swapBits != 0)
|
|
{
|
|
error("invalid bitmap header\n");
|
|
}
|
|
|
|
totalLines = bitmapHeader.height;
|
|
lineBytes = bitmapHeader.byteWidth;
|
|
lineBits = lineBytes * 8;
|
|
|
|
// allocate memory for various data structures:
|
|
// reference line
|
|
// current line
|
|
// strip offsets
|
|
// strip bytecounts
|
|
|
|
memcpy(&ifd, &ifdTemplate, sizeof(ifd));
|
|
|
|
strip_count = (totalLines + bitmapHeader.bandHeight - 1) / bitmapHeader.bandHeight;
|
|
ref_line = alloc_memory(lineBytes);
|
|
cur_line = alloc_memory(lineBytes);
|
|
|
|
if (useStrips)
|
|
{
|
|
INT32* strip_offsets;
|
|
INT32* strip_bytecounts;
|
|
|
|
strip_offsets = alloc_memory(sizeof(INT32) * strip_count);
|
|
strip_bytecounts = alloc_memory(sizeof(INT32) * strip_count);
|
|
|
|
// process one band at a time
|
|
|
|
for (strip_index=0; strip_index < strip_count; strip_index++)
|
|
{
|
|
strip_offsets[strip_index] = outputByteCnt;
|
|
|
|
// read band header
|
|
|
|
if (read_integer() != BAND_HEADER ||
|
|
(bandLines = read_integer()) > bitmapHeader.bandHeight ||
|
|
bandLines <= 0)
|
|
{
|
|
error("invalid band header\n");
|
|
}
|
|
|
|
init_tiff_encoder();
|
|
memset(ref_line, 0, lineBytes);
|
|
|
|
while (bandLines--)
|
|
{
|
|
UINT8* p;
|
|
|
|
// read input scanline
|
|
|
|
if (fread(cur_line, lineBytes, 1, fin) != 1)
|
|
error("error reading scanline\n");
|
|
|
|
// compress it
|
|
|
|
compress_tiff_line(cur_line, ref_line, lineBits);
|
|
|
|
// switch current and reference line buffer
|
|
|
|
p = ref_line;
|
|
ref_line = cur_line;
|
|
cur_line = p;
|
|
}
|
|
|
|
cleanup_tiff_encoder();
|
|
|
|
// calculate the size of compressed data for the current strip
|
|
|
|
strip_bytecounts[strip_index] = outputByteCnt - strip_offsets[strip_index];
|
|
align_output_data();
|
|
}
|
|
|
|
// output strip offsets and strip bytecounts
|
|
|
|
ifd.ifdRowsPerStrip.value = bitmapHeader.bandHeight;
|
|
ifd.ifdStripOffsets.count = strip_count;
|
|
ifd.ifdStripOffsets.value = outputByteCnt;
|
|
write_output_data(strip_offsets, strip_count * sizeof(INT32));
|
|
|
|
ifd.ifdStripByteCounts.count = strip_count;
|
|
ifd.ifdStripByteCounts.value = outputByteCnt;
|
|
write_output_data(strip_bytecounts, strip_count * sizeof(INT32));
|
|
|
|
free(strip_offsets);
|
|
free(strip_bytecounts);
|
|
}
|
|
else
|
|
{
|
|
ifd.ifdStripOffsets.count = 1;
|
|
ifd.ifdStripOffsets.value = outputByteCnt;
|
|
ifd.ifdRowsPerStrip.value = totalLines;
|
|
|
|
init_tiff_encoder();
|
|
memset(ref_line, 0, lineBytes);
|
|
|
|
// process one band at a time
|
|
|
|
for (strip_index=0; strip_index < strip_count; strip_index++)
|
|
{
|
|
// read band header
|
|
|
|
if (read_integer() != BAND_HEADER ||
|
|
(bandLines = read_integer()) > bitmapHeader.bandHeight ||
|
|
bandLines <= 0)
|
|
{
|
|
error("invalid band header\n");
|
|
}
|
|
|
|
while (bandLines--)
|
|
{
|
|
UINT8* p;
|
|
|
|
// read input scanline
|
|
|
|
if (fread(cur_line, lineBytes, 1, fin) != 1)
|
|
error("error reading scanline\n");
|
|
|
|
// compress it
|
|
|
|
compress_tiff_line(cur_line, ref_line, lineBits);
|
|
|
|
// switch current and reference line buffer
|
|
|
|
p = ref_line;
|
|
ref_line = cur_line;
|
|
cur_line = p;
|
|
}
|
|
}
|
|
|
|
cleanup_tiff_encoder();
|
|
|
|
ifd.ifdStripByteCounts.count = 1;
|
|
ifd.ifdStripByteCounts.value = outputByteCnt - ifd.ifdStripOffsets.value;
|
|
align_output_data();
|
|
}
|
|
|
|
// update the nextIFDOffset field in the previous IFD
|
|
|
|
write_nextifd_offset(prevIFDOffset, outputByteCnt);
|
|
|
|
// write out IFD
|
|
|
|
ASSERT(sizeof(ifd_count) == 2);
|
|
ifd_count = IFD_COUNT;
|
|
write_output_data(&ifd_count, 2);
|
|
ifd_offset = outputByteCnt;
|
|
|
|
ifd.ifdImageWidth.value = lineBits;
|
|
ifd.ifdImageHeight.value = totalLines;
|
|
ifd.ifdXResolution.value = ifd_offset + offsetof(IFD, xresNum);
|
|
ifd.xresNum = bitmapHeader.xResolution;
|
|
ifd.ifdYResolution.value = ifd_offset + offsetof(IFD, yresNum);
|
|
ifd.yresNum = bitmapHeader.yResolution;
|
|
ifd.ifdPageNumber.value = pageNumber++;
|
|
ifd.ifdSoftware.value = ifd_offset + offsetof(IFD, software);
|
|
|
|
write_output_data(&ifd, sizeof(ifd));
|
|
|
|
free(ref_line);
|
|
free(cur_line);
|
|
|
|
return ifd_offset + offsetof(IFD, nextIFDOffset);
|
|
}
|
|
|
|
|
|
int
|
|
end_of_file(
|
|
FILE* fin
|
|
)
|
|
|
|
{
|
|
int ch;
|
|
|
|
if ((ch = fgetc(fin)) == EOF)
|
|
return 1;
|
|
|
|
ungetc(ch, fin);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int __cdecl
|
|
main(
|
|
int argc,
|
|
char **argv
|
|
)
|
|
|
|
{
|
|
int offset;
|
|
|
|
// check command line arguments
|
|
|
|
progname = *argv;
|
|
|
|
if (argc != 3)
|
|
{
|
|
fprintf(stderr, "usage: %s input-file output-file\n", progname);
|
|
return 1;
|
|
}
|
|
|
|
// open input and output file
|
|
|
|
if ((fin = fopen(argv[1], "r")) == NULL)
|
|
error("couldn't open input file '%s'\n", argv[1]);
|
|
|
|
if ((fout = fopen(argv[2], "w")) == NULL)
|
|
error("couldn't open output file '%s'\n", argv[2]);
|
|
|
|
// output tiff file header
|
|
|
|
offset = write_tiff_fileheader();
|
|
|
|
while (! end_of_file(fin))
|
|
{
|
|
// look for page header
|
|
|
|
if (read_integer() != PAGE_HEADER)
|
|
error("invalid page header\n");
|
|
|
|
// process a single page
|
|
|
|
offset = process_page(offset);
|
|
|
|
// look for page trailer
|
|
|
|
if (read_integer() != PAGE_TRAILER)
|
|
error("invalid page trailer\n");
|
|
}
|
|
|
|
write_nextifd_offset(offset, 0);
|
|
|
|
fclose(fin);
|
|
fclose(fout);
|
|
return 0;
|
|
}
|
|
|