2025-04-27 07:49:33 -04:00

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;
}