mirror of
https://github.com/aaronsgiles/JPEGView.git
synced 2024-06-01 03:41:27 +00:00
92bdb55672
These are the sources for the final official release of JPEGView for the Mac, back in 1994.
1 line
20 KiB
C
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;
|
|
}
|