JPEGView/Source/C/BMP.c
Aaron Giles 92bdb55672 JPEGView 3.3 for Macintosh
These are the sources for the final official release of JPEGView for the
Mac, back in 1994.
2015-02-05 00:18:10 -08:00

1 line
20 KiB
C

/*********************************************************/
/* This source code copyright (c) 1991-2001, Aaron Giles */
/* See the Read Me file for licensing information. */
/* Contact email: mac@aarongiles.com */
/*********************************************************/
//=====================================================================================
// Generic includes for Macintosh headers
//=====================================================================================
#if THINK_C
#include "THINK.Header"
#include <stddef.h>
#elif applec
#pragma load ":Headers:MPW.Header"
#elif __MWERKS__
//#include "MW.Header"
#else
#include "JPEGView.h"
#endif
//=====================================================================================
// Includes specific to this module
//=====================================================================================
#include "BMP.h"
//=====================================================================================
// BMPPrivates: private data structure for BMP images, maintained for each image.
//=====================================================================================
typedef struct BMPPrivates BMPPrivates, *BMPPrivatesPtr, **BMPPrivatesHandle;
typedef (*ExtractProcPtr)(uchar **srcData, uchar *dstData, BMPPrivatesHandle privates);
struct BMPPrivates
{
ushort compression; // compression method used
ushort depth; // calculated depth of the image
ulong height, width; // size of the image
ulong dataStart; // offset of the start of data
CTabHandle decompColors; // color table to be used in decompression
ExtractProcPtr extract; // pointer to extract function
};
//=====================================================================================
// Constants specific to this module
//=====================================================================================
enum {
compRGB = 0,
compRLE8 = 1,
compRLE4 = 2
};
enum {
errAllDone = 1
};
//=====================================================================================
// Prototypes for functions local to this module
//=====================================================================================
static CTabHandle ExtractColorTable(uchar *dataStart, ulong offset, short depth,
short colors, ulong *newOffset);
static OSErr SetUpCompression(BMPPrivatesHandle privates, ImageHandle theImage);
static OSErr DecompressBMPImage(uchar *dataStart, BMPPrivatesHandle privates,
PixMapHandle dstPixMap, ICMProgressProcRecordPtr prog, Size length);
static void CopyPixMapToDestination(PixMapHandle srcPixMap, short startRow);
static OSErr ExtractUncompressed(uchar **srcData, uchar *dstData,
BMPPrivatesHandle privates);
static OSErr ExtractUncompressedRGB(uchar **srcData, uchar *dstData,
BMPPrivatesHandle privates);
static OSErr ExtractRLE4(uchar **srcData, uchar *dstData, BMPPrivatesHandle privates);
static OSErr ExtractRLE8(uchar **srcData, uchar *dstData, BMPPrivatesHandle privates);
//=====================================================================================
// Boolean idBMP(uchar *theData, long theSize)
//=====================================================================================
// Examines the given data and attempts to identify it as a BMP image.
//=====================================================================================
extern Boolean idBMP(uchar *theData, long theSize, short refNum, FSSpec *theSpec)
{
#if applec
#pragma unused(theSize, refNum, theSpec)
#endif
return (theSize > 2 && theData[0] == 'B' && theData[1] == 'M');
}
//=====================================================================================
// OSErr OpenBMP(ImageHandle theImage)
//=====================================================================================
// Initializes the image record for a BMP image; note that since we are a simple
// baseline BMP reader, we only read the first set of tags.
//=====================================================================================
#define GetIntelLong(a, o) \
((ulong)a[o] + \
(ulong)(a[(o) + 1] << 8) + \
(ulong)(a[(o) + 2] << 16) + \
(ulong)(a[(o) + 3] << 24))
#define GetIntelShort(a, o) \
((ushort)a[o] + \
(ushort)(a[(o) + 1] << 8))
extern OSErr OpenBMP(ImageHandle theImage)
{
long theLen = GetHandleSize((*theImage)->data);
char hState = HGetState((*theImage)->data);
BMPPrivatesHandle privates;
ulong offset, headerSize;
OSErr theErr = noErr;
uchar *dataStart;
if (!(privates = (BMPPrivatesHandle)NewHandleClear(sizeof(BMPPrivates))))
return memFullErr;
(*theImage)->privateData = (Handle)privates;
HLock((*theImage)->data);
dataStart = (uchar *)*(*theImage)->data;
(*privates)->dataStart = GetIntelLong(dataStart, 10);
offset = 14;
headerSize = GetIntelLong(dataStart, offset);
if (headerSize <= 12) {
// BITMAPCOREHEADER
(*privates)->width = (*theImage)->grect.right = GetIntelShort(dataStart, offset + 4);
(*privates)->height = (*theImage)->grect.bottom = GetIntelShort(dataStart, offset + 6);
if (GetIntelShort(dataStart, offset + 8) != 1) theErr = codecBadDataErr;
(*privates)->depth = (*theImage)->depth = GetIntelShort(dataStart, offset + 10);
(*privates)->decompColors = ExtractColorTable(dataStart, offset + headerSize,
(*privates)->depth, -1, &offset);
} else {
// BITMAPINFOHEADER
(*privates)->width = (*theImage)->grect.right = GetIntelLong(dataStart, offset + 4);
(*privates)->height = (*theImage)->grect.bottom = GetIntelLong(dataStart, offset + 8);
if (GetIntelShort(dataStart, offset + 12) != 1) theErr = codecBadDataErr;
(*privates)->depth = (*theImage)->depth = GetIntelShort(dataStart, offset + 14);
(*privates)->compression = GetIntelLong(dataStart, offset + 16);
(*privates)->decompColors = ExtractColorTable(dataStart, offset + headerSize,
(*privates)->depth, GetIntelLong(dataStart, offset + 32), &offset);
}
if ((*privates)->depth == 24) (*privates)->depth = 32;
else if ((*privates)->depth < 24 && !(*privates)->decompColors) theErr = codecBadDataErr;
if (theErr == noErr) theErr = SetUpCompression(privates, theImage);
if ((*privates)->decompColors)
(*theImage)->ipalette = NewPalette(256, (*privates)->decompColors, pmTolerant, 0);
(*theImage)->qrect = (*theImage)->crect = (*theImage)->grect;
HSetState((*theImage)->data, hState);
return theErr;
}
//=====================================================================================
// OSErr DrawBMP(Handle theHandle, JVDrawParamsHandle theParams)
//=====================================================================================
// Draws a BMP image into the specified GWorld, from the srcRect into the dstRect,
// clipping as dstRgn specifies.
//=====================================================================================
extern OSErr DrawBMP(Handle theHandle, JVDrawParamsHandle theParams)
{
BMPPrivatesHandle privates = (BMPPrivatesHandle)(*theParams)->privateData;
NestedProgress theProgress = (*theParams)->progress;
char hState = HGetState(theHandle);
OSErr theErr = noErr;
GWorldPtr theGWorld;
uchar *dataStart;
if (theProgress.prog.progressProc) CallICMProgressProc(theProgress.prog.progressProc,
codecProgressUpdatePercent, 0x00000000L, theProgress.prog.progressRefCon);
HLockHi(theHandle);
if (theErr == noErr) {
dataStart = (uchar *)StripAddress(*theHandle);
theGWorld = NewTempGWorld((*privates)->width, (*privates)->height, (*privates)->depth,
(*privates)->decompColors);
if (theGWorld) {
theErr = DecompressBMPImage(dataStart, privates, GetGWorldPixMap(theGWorld),
(ICMProgressProcRecordPtr)&theProgress, GetHandleSize(theHandle));
if ((*theParams)->progress.aborted) theErr = codecAbortErr;
DisposeGWorld(theGWorld);
} else gIntError = errNoDrawMemory, theErr = memFullErr;
}
HSetState(theHandle, hState);
if (theProgress.prog.progressProc) CallICMProgressProc(theProgress.prog.progressProc,
codecProgressUpdatePercent, 0x00010000L, theProgress.prog.progressRefCon);
return theErr;
}
//=====================================================================================
// OSErr CloneBMP(ImageHandle origImage, ImageHandle newImage)
//=====================================================================================
// Called when an image is cloned, to give us a chance to duplicate any secondary
// storage.
//=====================================================================================
extern OSErr CloneBMP(ImageHandle origImage, ImageHandle newImage)
{
BMPPrivatesHandle origPrivates = (BMPPrivatesHandle)(*origImage)->privateData;
BMPPrivatesHandle newPrivates = (BMPPrivatesHandle)(*newImage)->privateData;
CTabHandle theColors;
OSErr theErr;
if (origPrivates && newPrivates) {
if (theColors = (*origPrivates)->decompColors) {
theErr = HandToHand((Handle *)&theColors);
(*newPrivates)->decompColors = theColors;
}
}
return theErr;
}
//=====================================================================================
// OSErr CloseBMP(ImageHandle theImage)
//=====================================================================================
// Called before the image is disposed, to get rid of any secondary storage we
// allocated on open.
//=====================================================================================
extern OSErr CloseBMP(ImageHandle theImage)
{
BMPPrivatesHandle privates = (BMPPrivatesHandle)(*theImage)->privateData;
if (privates)
if ((*privates)->decompColors) DisposeHandle((Handle)(*privates)->decompColors);
return noErr;
}
//=====================================================================================
// CTabHandle ExtractColorTable(uchar *dataStart, ulong offset, short depth,
// short colors, ulong *newOffset)
//=====================================================================================
// Extracts the included BMP color table from the data at the given offset.
//=====================================================================================
static CTabHandle ExtractColorTable(uchar *dataStart, ulong offset, short depth,
short colors, ulong *newOffset)
{
ulong count = (colors <= 0) ? (1L << depth) : colors, size = (1L << depth), i;
uchar *data = dataStart + offset;
Boolean isRGB3 = (colors == -1);
CTabHandle theColors;
if (depth == 24 && colors <= 0) return nil;
if (count > size || count > 256) {
*newOffset = offset + count * ((isRGB3) ? 3 : 4);
return nil;
}
theColors = (CTabHandle)NewHandleClear(sizeof(ColorTable) + (size - 1) * sizeof(ColorSpec));
if (theColors) {
(*theColors)->ctSeed = GetCTSeed();
(*theColors)->ctFlags = 0x8000;
(*theColors)->ctSize = size;
for (i = 0; i < count; i++) {
(*theColors)->ctTable[i].rgb.blue = ((ushort)*data << 8) + *data, data++;
(*theColors)->ctTable[i].rgb.green = ((ushort)*data << 8) + *data, data++;
(*theColors)->ctTable[i].rgb.red = ((ushort)*data << 8) + *data, data++;
if (!isRGB3) data++;
}
}
*newOffset = data - dataStart;
return theColors;
}
//=====================================================================================
// OSErr SetUpCompression(BMPPrivatesHandle privates, ImageHandle theImage)
//=====================================================================================
// Sets up the compression parameters for the BMP image.
//=====================================================================================
static OSErr SetUpCompression(BMPPrivatesHandle privates, ImageHandle theImage)
{
OSErr theErr = noErr;
switch ((*privates)->compression) {
case compRGB:
(*theImage)->compression = 0;
BlockMove(gString[strUncompressed], (*theImage)->compressionDesc, *gString[strUncompressed] + 1);
(*privates)->extract = ((*privates)->depth < 24) ?
(ExtractProcPtr)ExtractUncompressed : (ExtractProcPtr)ExtractUncompressedRGB;
break;
case compRLE8:
(*theImage)->compression = kRLE8Compression;
BlockMove(gString[strRLE8], (*theImage)->compressionDesc, *gString[strRLE8] + 1);
(*privates)->extract = (ExtractProcPtr)ExtractRLE8;
break;
case compRLE4:
(*theImage)->compression = kRLE4Compression;
BlockMove(gString[strRLE4], (*theImage)->compressionDesc, *gString[strRLE4] + 1);
(*privates)->extract = (ExtractProcPtr)ExtractRLE4;
break;
default:
theErr = codecBadDataErr;
break;
}
return theErr;
}
//=====================================================================================
// OSErr DecompressBMPImage(uchar *dataStart, BMPPrivatesHandle privates,
// PixMapHandle dstPixMap, ICMProgressProcRecordPtr prog, Size length)
//=====================================================================================
// Oversees the decompression of a BMP image, handling banding, and calling the
// appropriate function to perform the actual decompression.
//=====================================================================================
static OSErr DecompressBMPImage(uchar *dataStart, BMPPrivatesHandle privates,
PixMapHandle dstPixMap, ICMProgressProcRecordPtr prog, Size length)
{
uchar *ptr, *rowStart, *dataEnd = dataStart + length;
char pState = GetPixelsState(dstPixMap);
RgnHandle dstRgn = qd.thePort->visRgn;
ulong bandRow = 0, row;
char mmuMode = true32b;
OSErr theErr = noErr;
LockPixels(dstPixMap);
rowStart = (uchar *)GetPixBaseAddr(dstPixMap) +
(Height(&(*dstPixMap)->bounds) - 1) * ((*dstPixMap)->rowBytes & 0x3fff);
ptr = dataStart + (*privates)->dataStart;
for (row = 0; row < (*privates)->height; row++) {
if (ptr < dataStart || ptr > dataEnd) {
theErr = codecBadDataErr;
break;
}
SwapMMUMode(&mmuMode);
theErr = (*privates)->extract(&ptr, rowStart, privates);
SwapMMUMode(&mmuMode);
rowStart -= ((*dstPixMap)->rowBytes & 0x3fff);
if (!(row & 63) && prog->progressProc)
theErr = (OSErr)CallICMProgressProc(prog->progressProc,
codecProgressUpdatePercent,
FixRatio(row, (*privates)->height), prog->progressRefCon);
if (++bandRow == Height(&(*dstPixMap)->bounds)) {
CopyPixMapToDestination(dstPixMap, (*privates)->height - row - 1);
bandRow = 0;
rowStart = (uchar *)GetPixBaseAddr(dstPixMap) +
(Height(&(*dstPixMap)->bounds) - 1) * ((*dstPixMap)->rowBytes & 0x3fff);
}
if (theErr != noErr) break;
}
if (theErr == errAllDone) theErr = noErr;
if (theErr == noErr && bandRow)
CopyPixMapToDestination(dstPixMap,
(*privates)->height - (row + Height(&(*dstPixMap)->bounds) - bandRow));
SetPixelsState(dstPixMap, pState);
return theErr;
}
//=====================================================================================
// void CopyPixMapToDestination(PixMapHandle srcPixMap, short startRow)
//=====================================================================================
// Copies the band contained in srcPixMap to the destination, offset from the top by
// startRow.
//=====================================================================================
static void CopyPixMapToDestination(PixMapHandle srcPixMap, short startRow)
{
Rect srcRect = (*srcPixMap)->bounds, dstRect = (*srcPixMap)->bounds;
PixMapHandle dstPixMap = GetGWorldPixMap((CGrafPtr)qd.thePort);
char srcState = HGetState((Handle)srcPixMap);
char dstState = HGetState((Handle)dstPixMap);
OffsetRect(&dstRect, 0, startRow);
HLock((Handle)srcPixMap);
HLock((Handle)dstPixMap);
CopyBits((BitMap *)*srcPixMap, (BitMap *)*dstPixMap, &srcRect, &dstRect,
srcCopy + ditherCopy, qd.thePort->visRgn);
HSetState((Handle)dstPixMap, dstState);
HSetState((Handle)srcPixMap, srcState);
}
//=====================================================================================
// OSErr ExtractUncompressed(uchar **srcData, uchar *dstData,
// BMPPrivatesHandle privates)
//=====================================================================================
// Pulls out a row of uncompressed 4 or 8 bit data.
//=====================================================================================
static OSErr ExtractUncompressed(uchar **srcData, uchar *dstData,
BMPPrivatesHandle privates)
{
ulong bytes = ((*privates)->width * (*privates)->depth + 7) / 8L;
ulong offset = (*privates)->dataStart & 3;
uchar *src = *srcData;
while (bytes--) *dstData++ = *src++;
*srcData = (uchar *)(((long)src - offset + 3) & 0xfffffffc) + offset;
return noErr;
}
//=====================================================================================
// OSErr ExtractUncompressedRGB(uchar **srcData, uchar *dstData,
// BMPPrivatesHandle privates)
//=====================================================================================
// Pulls out a row of uncompressed RGB data, skipping the alpha channel in the
// destination.
//=====================================================================================
static OSErr ExtractUncompressedRGB(uchar **srcData, uchar *dstData,
BMPPrivatesHandle privates)
{
// ulong offset = (*privates)->dataStart & 3;
ulong col = (*privates)->width;
uchar *src = *srcData;
while (col--) {
dstData++, *dstData++ = src[2], *dstData++ = src[1], *dstData++ = src[0];
src += 3;
}
// *srcData = (uchar *)(((long)src - offset + 3) & 0xfffffffc) + offset;
*srcData = src;
return noErr;
}
//=====================================================================================
// OSErr ExtractRLE4(uchar **srcData, uchar *dstData, BMPPrivatesHandle privates)
//=====================================================================================
// Pulls out a row of RLE-compressed 4-bit data.
//=====================================================================================
static OSErr ExtractRLE4(uchar **srcData, uchar *dstData, BMPPrivatesHandle privates)
{
uchar *src = *srcData, val, pixel = 0, shift = 4, sshift, spixel;
short count, col = (*privates)->width;
while (col) {
if (count = *src++) {
val = *src++;
if ((col -= count) < 0) return codecBadDataErr;
while (count--) {
pixel |= (val & 0xf) << shift;
if (shift = 4 - shift) *dstData++ = pixel, pixel = 0;
val = (val << 4) | (val >> 4);
}
} else {
count = *src++;
if (count > 2) {
if ((col -= count) < 0) return codecBadDataErr;
sshift = 4, spixel = *src++;
while (count--) {
pixel |= ((spixel >> sshift) & 0xf) << shift;
if (count && (sshift = 4 - sshift)) spixel = *src++;
if (shift = 4 - shift) *dstData++ = pixel, pixel = 0;
}
src = (uchar *)(((long)src + 1) & 0xfffffffe);
} else if (count == 0) break;
else if (count == 1) return errAllDone;
else {
count = *src++;
if ((col -= count) < 0) return codecBadDataErr;
dstData += count >> 1;
if (count & 1)
if (shift = 4 - shift) dstData++;
src++; // we skip the vertical offset for now
}
}
}
if (!shift) *dstData = pixel;
while (!src[0] && !src[1]) src += 2;
*srcData = src;
return noErr;
}
//=====================================================================================
// OSErr ExtractRLE8(uchar **srcData, uchar *dstData, BMPPrivatesHandle privates)
//=====================================================================================
// Pulls out a row of RLE-compressed 8-bit data.
//=====================================================================================
static OSErr ExtractRLE8(uchar **srcData, uchar *dstData, BMPPrivatesHandle privates)
{
short count, col = (*privates)->width;
uchar *src = *srcData, val;
while (col) {
if (count = *src++) {
val = *src++;
if ((col -= count) < 0) return codecBadDataErr;
while (count--) *dstData++ = val;
} else {
count = *src++;
if (count > 2) {
if ((col -= count) < 0) return codecBadDataErr;
while (count--) *dstData++ = *src++;
src = (uchar *)(((long)src + 1) & 0xfffffffe);
} else if (count == 0) break;
else if (count == 1) return errAllDone;
else {
count = *src++;
if ((col -= count) < 0) return codecBadDataErr;
dstData += count;
src++; // we skip the vertical offset for now
}
}
}
while (!src[0] && !src[1]) src += 2;
*srcData = src;
return noErr;
}