mirror of
https://github.com/aaronsgiles/JPEGView.git
synced 2024-06-13 21:29:28 +00:00
92bdb55672
These are the sources for the final official release of JPEGView for the Mac, back in 1994.
1 line
33 KiB
C
1 line
33 KiB
C
/*********************************************************/
|
|
/* This source code copyright (c) 1991-2001, Aaron Giles */
|
|
/* See the Read Me file for licensing information. */
|
|
/* Contact email: mac@aarongiles.com */
|
|
/*********************************************************/
|
|
|
|
#if THINK_C
|
|
#include "THINK.Header"
|
|
#elif applec
|
|
#pragma load ":Headers:MPW.Header"
|
|
#elif __MWERKS__
|
|
//#include "MW.Header"
|
|
#else
|
|
#include "JPEGView.h"
|
|
#endif
|
|
|
|
static OSErr MakePreviewAndIcons(Handle theHandle, ImageHandle theImage, Boolean preview,
|
|
Boolean icons, NestedProgressPtr progProc, short cropWhat);
|
|
static OSErr AddQuickTimePreview(FSSpec *theFile, GWorldPtr thePreview, Rect *theRect);
|
|
static OSErr MakeIcons(FSSpec *theSpec, ImageHandle theImage);
|
|
static OSErr MakeOneIcon(short theFile, OSType theType, GWorldPtr src, GWorldPtr dst,
|
|
Rect *srcRect, Rect *dstRect);
|
|
static void TrimIcon(Rect *theRect);
|
|
static OSErr PixMapToHandle(PixMapHandle theMap, Rect *theRect, Rect *maskRect, Handle *theHandle);
|
|
|
|
static Rect gPreviewRect, gIconRect;
|
|
|
|
/*
|
|
* DoSaveDocument(theImage, theSpec, theType, preview, palette)
|
|
*
|
|
* Purpose: Oversees the saving of a JPEG image file
|
|
* Inputs: theImage = the image to be saved
|
|
* theSpec = pointer to the destination file spec
|
|
* theType = the destination type
|
|
* preview = flag: true to create and save a preview image
|
|
* palette = flag: true to save the palette with the image
|
|
* Returns: an OSErr describing the result
|
|
*
|
|
*/
|
|
|
|
OSErr DoSaveDocument(ImageHandle theImage, FSSpec *theSpec, OSType theType, Boolean preview,
|
|
Boolean palette, Boolean icons, short cropWhat)
|
|
{
|
|
short total = (preview || icons) ? 1 : 0, i, theFile;
|
|
NestedProgress theProc = { { nil, 0 }, 0, 0L, 0L };
|
|
FSSpec tempSpec, oldSpec = (*theImage)->file;
|
|
Handle finalData = (*theImage)->data;
|
|
ImageFormatPtr theAction = nil;
|
|
OSErr theErr = noErr;
|
|
|
|
// see if we can find the destination type in the type list
|
|
for (i = 0; i < kFileFormats; i++)
|
|
if (theType == gFormat[i]->outType) {
|
|
if (((*theImage)->compression == kJPEGCompression && !gFormat[i]->fix) ||
|
|
((*theImage)->compression != kJPEGCompression && gFormat[i]->save != (*theImage)->format->save)) continue;
|
|
theAction = gFormat[i];
|
|
break;
|
|
}
|
|
if (!theAction) return errAETypeError;
|
|
|
|
// ensure that we can actually save the current image in the requested format
|
|
if (Banded(theImage) && !theAction->doesBanded) return errAETypeError;
|
|
|
|
// adjust parameters to match the output format's limitations
|
|
palette = palette && theAction->doesColors && (*theImage)->depth > 8;
|
|
|
|
// initialize the progress proc, and open it right away
|
|
theProc.prog.progressProc = gGenericProgress;
|
|
theProc.prog.progressRefCon = (long)&theProc;
|
|
UpdateParamText(gString[strPreparing], gNullString, gNullString, (*theImage)->file.name);
|
|
CallICMProgressProc(theProc.prog.progressProc, codecProgressOpen, 0L,
|
|
theProc.prog.progressRefCon);
|
|
|
|
// determine the temporary file name
|
|
(*theImage)->file = *theSpec;
|
|
FSMakeFSSpec(theSpec->vRefNum, theSpec->parID, gString[strJPEGViewTempFile], &tempSpec);
|
|
|
|
// if we want to save a palette, and we haven't calculated it yet, do it now
|
|
if (palette && !(*theImage)->qpalette) {
|
|
short depth = (*(*theImage)->dmon)->depth;
|
|
PaletteHandle thePalette;
|
|
if (depth < 4 || depth > 8) depth = 8;
|
|
total++;
|
|
theProc.end = 0x0000F000 / total;
|
|
theErr = DoQuantize(theImage, 1L << depth, &thePalette, &theProc);
|
|
theProc.prog.progressRefCon = (long)&theProc;
|
|
if (theErr == noErr) (*theImage)->qpalette = thePalette;
|
|
}
|
|
|
|
if (theErr == noErr) {
|
|
// update the progress, and do any internal JPEG conversions that are necessary
|
|
theProc.begin = theProc.end;
|
|
theProc.end += total ? (0x0000F000 / total) : 0L;
|
|
KeepSpinning();
|
|
if ((*theImage)->compression == kJPEGCompression && theAction->fix)
|
|
theErr = theAction->fix(theImage, &finalData, palette);
|
|
|
|
// if everything's ok, create previews and icons
|
|
if (theErr == noErr) theErr = MakePreviewAndIcons((*theImage)->data, theImage,
|
|
preview, icons, &theProc, cropWhat);
|
|
theProc.prog.progressRefCon = (long)&theProc;
|
|
KeepSpinning();
|
|
|
|
// now, do the actual saving of the image
|
|
if (theErr == noErr)
|
|
UpdateParamText(gString[strSaving], gNullString, gNullString, (*theImage)->file.name);
|
|
if (theErr == noErr)
|
|
if (theAction->save) theErr = theAction->save(&tempSpec, finalData,
|
|
preview ? gPreviewGWorld : nil, &gPreviewRect, (*theImage)->privateData);
|
|
else theErr = DefaultSave(&tempSpec, finalData, nil, nil, theAction->outType);
|
|
if (theErr == noErr && preview && !theAction->customPreviews)
|
|
theErr = AddQuickTimePreview(&tempSpec, gPreviewGWorld, &gPreviewRect);
|
|
KeepSpinning();
|
|
|
|
// if everything's still ok, we actually add the icons to the new file
|
|
if (theErr == noErr && icons) {
|
|
FSpCreateResFile(&tempSpec, kCreator, theType, 0);
|
|
theErr = MakeIcons(&tempSpec, theImage);
|
|
}
|
|
|
|
// if things are still going well, we save the cropping rectangle as well
|
|
if (theErr == noErr && (Cropped(theImage) || cropWhat == kCropImage)) {
|
|
Rect **theRect = (Rect **)NewHandle(sizeof(Rect));
|
|
if (theRect) {
|
|
if (cropWhat == kCropImage) SelRectToCropRect(theImage, *theRect);
|
|
else **theRect = (*theImage)->crect;
|
|
FSpCreateResFile(&tempSpec, kCreator, theType, 0);
|
|
if (theFile = FSpOpenResFile(&tempSpec, fsRdWrPerm)) {
|
|
AddResource((Handle)theRect, 'RECT', 0, gNullString);
|
|
if ((theErr = ResError()) == noErr) WriteResource((Handle)theRect);
|
|
else gIntError = errCantWriteFile;
|
|
CloseResFile(theFile);
|
|
} else theErr = ResError(), gIntError = errCantWriteFile;
|
|
if (theErr != noErr) DisposeHandle((Handle)theRect);
|
|
} else theErr = memFullErr, gIntError = errNoSaveMemory;
|
|
}
|
|
if (finalData && finalData != (*theImage)->data) DisposeHandle(finalData);
|
|
KeepSpinning();
|
|
|
|
// finally, if nothing bad happened, exchange the temp file for the real file
|
|
if (theErr == noErr) {
|
|
theErr = FSpExchangeFiles(theSpec, &tempSpec);
|
|
if (theErr == fnfErr) theErr = FSpRename(&tempSpec, theSpec->name);
|
|
if (theErr == noErr) {
|
|
FInfo theInfo;
|
|
theErr = FSpGetFInfo(theSpec, &theInfo);
|
|
if (theErr == noErr) {
|
|
if (icons) theInfo.fdFlags |= kHasCustomIcon;
|
|
theInfo.fdFlags &= ~kHasBeenInited;
|
|
theInfo.fdCreator = kCreator;
|
|
theInfo.fdType = theType;
|
|
theErr = FSpSetFInfo(theSpec, &theInfo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// clean up and get outta here
|
|
FSpDelete(&tempSpec);
|
|
KeepSpinning();
|
|
ForceFolderUpdate(theSpec->parID, theSpec->vRefNum);
|
|
theProc.end = 0x00010000;
|
|
CallICMProgressProc(theProc.prog.progressProc, codecProgressClose, 0L,
|
|
theProc.prog.progressRefCon);
|
|
(*theImage)->file = oldSpec;
|
|
return theErr;
|
|
}
|
|
|
|
extern OSErr DefaultSave(FSSpec *theSpec, Handle theHandle, GWorldPtr thePreview,
|
|
Rect *theRect, OSType theType)
|
|
{
|
|
#if applec
|
|
#pragma unused(thePreview, theRect)
|
|
#endif
|
|
long theSize = GetHandleSize(theHandle);
|
|
char hState = HGetState(theHandle);
|
|
short theFile;
|
|
OSErr theErr;
|
|
|
|
KeepSpinning();
|
|
theErr = FSpCreate(theSpec, kCreator, theType, 0);
|
|
if (theErr == noErr) {
|
|
KeepSpinning();
|
|
theErr = FSpOpenDF(theSpec, fsWrPerm, &theFile);
|
|
if (theErr == noErr) {
|
|
HLock(theHandle);
|
|
SpinIndef();
|
|
theErr = FSWrite(theFile, &theSize, *theHandle);
|
|
KeepSpinning();
|
|
if (theErr == noErr) {
|
|
HSetState(theHandle, hState);
|
|
if (theErr == noErr) {
|
|
FSClose(theFile);
|
|
return theErr;
|
|
}
|
|
} else gIntError = errCantWriteFile;
|
|
HSetState(theHandle, hState);
|
|
FSClose(theFile);
|
|
} else gIntError = errCantOpenFile;
|
|
FSpDelete(theSpec);
|
|
} else gIntError = errCantCreateFile;
|
|
return theErr;
|
|
}
|
|
|
|
static OSErr MakePreviewAndIcons(Handle theHandle, ImageHandle theImage, Boolean preview,
|
|
Boolean icons, NestedProgressPtr progProc, short cropWhat)
|
|
{
|
|
#if applec
|
|
#pragma unused(theHandle)
|
|
#endif
|
|
Rect prevSrcRect, iconSrcRect, prevDstRect, iconDstRect = { 0, 0, 32, 32 }, tmpRect;
|
|
PixMapHandle dstPixMap = GetGWorldPixMap(gPreviewGWorld);
|
|
short prevPort = -1, iconPort = -1;
|
|
JVDrawParamsHandle theParams;
|
|
StringPtr progressString;
|
|
OSErr theErr = noErr;
|
|
|
|
if (!preview && !icons) return noErr;
|
|
|
|
// calculate the appropriate source/destination rectangles
|
|
if (cropWhat != kCropImage) prevSrcRect = (*theImage)->crect;
|
|
else SelRectToCropRect(theImage, &prevSrcRect);
|
|
if (gThePrefs.iconStyle == isProportional || gThePrefs.iconStyle == isProportionalDog) {
|
|
if (cropWhat == kCropNothing) iconSrcRect = (*theImage)->crect;
|
|
else SelRectToCropRect(theImage, &iconSrcRect);
|
|
MaxRect(&iconSrcRect, &gIconGWorld->portRect, &iconDstRect);
|
|
} else {
|
|
if (cropWhat == kCropNothing) MaxSquare(&prevSrcRect, &iconSrcRect);
|
|
else {
|
|
SelRectToCropRect(theImage, &tmpRect);
|
|
MaxSquare(&tmpRect, &iconSrcRect);
|
|
}
|
|
}
|
|
MaxRect(&prevSrcRect, &gPreviewGWorld->portRect, &prevDstRect);
|
|
InsetRect(&iconDstRect, 1, 1);
|
|
|
|
// set up the progress procedure
|
|
if (preview && icons) progressString = gString[strCreatingPreviewIcons];
|
|
else if (preview) progressString = gString[strCreatingPreview];
|
|
else progressString = gString[strCreatingIcons];
|
|
UpdateParamText(progressString, gNullString, gNullString, (*theImage)->file.name);
|
|
|
|
// allocate and initialize the drawing parameters
|
|
tmpRect = (*theImage)->grect;
|
|
if (theParams = NewDrawParams(&tmpRect, iqVHigh, false, progProc, (*theImage)->privateData)) {
|
|
if (preview) SetUpDrawPort(theParams, kOffscreenPort1, gPreviewGWorld,
|
|
&prevSrcRect, &prevDstRect, true);
|
|
if (icons) SetUpDrawPort(theParams, preview ? kOffscreenPort2 : kOffscreenPort1,
|
|
gIconGWorld, &iconSrcRect, &iconDstRect, true);
|
|
|
|
// create the icons and preview
|
|
KeepSpinning();
|
|
theErr = PreflightDrawing(theParams);
|
|
PushPort();
|
|
if (theErr == noErr) {
|
|
|
|
// lock down the pixels and erase the GWorlds
|
|
LockPixels(GetGWorldPixMap(gPreviewGWorld));
|
|
MySetPort(gPreviewGWorld);
|
|
ClipRect(&gPreviewGWorld->portRect);
|
|
RGBForeColor(&gBlack);
|
|
RGBBackColor(&gWhite);
|
|
if (NeedsWhiteBG(theImage)) EraseRect(&gPreviewGWorld->portRect);
|
|
else PaintRect(&gPreviewGWorld->portRect);
|
|
LockPixels(GetGWorldPixMap(gIconGWorld));
|
|
MySetPort(gIconGWorld);
|
|
ClipRect(&gPreviewGWorld->portRect);
|
|
RGBForeColor(&gBlack);
|
|
RGBBackColor(&gWhite);
|
|
if (NeedsWhiteBG(theImage)) EraseRect(&gIconGWorld->portRect);
|
|
else PaintRect(&gIconGWorld->portRect);
|
|
|
|
// now get the ball rolling
|
|
MySetPort((*theParams)->dummy);
|
|
CallICMProgressProc((*theParams)->progress.prog.progressProc,
|
|
codecProgressOpen, 0L,
|
|
(*theParams)->progress.prog.progressRefCon);
|
|
|
|
// Either from the offscreen GWorld ...
|
|
if ((*theImage)->gworld && GWOrigSize(theImage))
|
|
CopyGWorldToGWorld((*theImage)->gworld, (*theParams)->dummy,
|
|
&(*theImage)->gworld->portRect, &(*theImage)->gworld->portRect,
|
|
srcCopy, nil);
|
|
|
|
// or from the image directly!
|
|
else theErr = (*theImage)->format->draw((*theImage)->data, theParams);
|
|
|
|
CallICMProgressProc((*theParams)->progress.prog.progressProc,
|
|
codecProgressClose, 0L,
|
|
(*theParams)->progress.prog.progressRefCon);
|
|
UnlockPixels(GetGWorldPixMap(gPreviewGWorld));
|
|
UnlockPixels(GetGWorldPixMap(gIconGWorld));
|
|
KeepSpinning();
|
|
if (theErr == noErr) theErr = PostflightDrawing(theParams);
|
|
else PostflightDrawing(theParams);
|
|
}
|
|
PopPort();
|
|
DisposeHandle((Handle)theParams);
|
|
}
|
|
gPreviewRect = prevDstRect;
|
|
gIconRect = iconDstRect;
|
|
return theErr;
|
|
}
|
|
|
|
/*
|
|
* AddQuickTimePreview(theFile)
|
|
*
|
|
* Purpose: Adds the preview PICT to the newly-created PICT file
|
|
* Inputs: theFile = pointer to a file spec
|
|
* Returns: an OSErr describing what went wrong
|
|
*
|
|
*/
|
|
|
|
static OSErr AddQuickTimePreview(FSSpec *theFile, GWorldPtr thePreview, Rect *theRect)
|
|
{
|
|
PixMapHandle dstPixMap = GetGWorldPixMap(thePreview);
|
|
PicHandle previewData;
|
|
short resRef;
|
|
OSErr theErr;
|
|
|
|
if (previewData = (PicHandle)NewHandle(4)) {
|
|
KeepSpinning();
|
|
LockPixels(dstPixMap);
|
|
theErr = MakeThumbnailFromPixMap(dstPixMap, theRect, 32, (PicHandle)previewData,
|
|
(ICMProgressProcRecordPtr)&gDummyProg);
|
|
UnlockPixels(dstPixMap);
|
|
KeepSpinning();
|
|
if (theErr == noErr) {
|
|
FSpCreateResFile(theFile, kCreator, kPICTType, 0);
|
|
KeepSpinning();
|
|
if (resRef = FSpOpenResFile(theFile, fsRdWrPerm)) {
|
|
KeepSpinning();
|
|
theErr = AddFilePreview(resRef, kPICTType, (Handle)previewData);
|
|
KeepSpinning();
|
|
CloseResFile(resRef);
|
|
} else gIntError = errCantWriteFile, theErr = fnfErr;
|
|
}
|
|
} else gIntError = errCantMakePreview, theErr = memFullErr;
|
|
return theErr;
|
|
}
|
|
|
|
/*
|
|
* MakeIcons(theSpec, theImage)
|
|
*
|
|
* Purpose: Handles the creation of icons for theImage
|
|
* Inputs: theSpec = pointer to the destination file spec
|
|
* theImage = handle to the image to make icons for
|
|
* Returns: an OSErr describing the result
|
|
*
|
|
*/
|
|
|
|
static OSErr MakeIcons(FSSpec *theSpec, ImageHandle theImage)
|
|
{
|
|
#if applec
|
|
#pragma unused(theImage)
|
|
#endif
|
|
Rect bigRect = { 0, 0, 32, 32 }, smallRect = { 0, 0, 16, 16 };
|
|
OSErr theErr = noErr;
|
|
GWorldPtr theGWorld;
|
|
short theFile;
|
|
|
|
if (!(theFile = FSpOpenResFile(theSpec, fsRdWrPerm)))
|
|
return gIntError = errCantCreateIcons, ResError();
|
|
while (true) {
|
|
if (!(theGWorld = MyNewGWorld(&bigRect, 8, nil, nil, true, false)))
|
|
if (!(theGWorld = MyNewGWorld(&bigRect, 8, nil, nil, true, true))) {
|
|
theErr = memFullErr;
|
|
break;
|
|
}
|
|
theErr = MakeOneIcon(theFile, large8BitData, gIconGWorld, theGWorld, &gIconRect, &bigRect);
|
|
if (theErr != noErr) break;
|
|
theErr = MakeOneIcon(theFile, small8BitData, gIconGWorld, theGWorld, &gIconRect, &smallRect);
|
|
if (theErr != noErr) break;
|
|
DisposeGWorld(theGWorld);
|
|
theGWorld = nil;
|
|
if (!(theGWorld = MyNewGWorld(&bigRect, 4, nil, nil, true, false)))
|
|
if (!(theGWorld = MyNewGWorld(&bigRect, 4, nil, nil, true, true))) {
|
|
theErr = memFullErr;
|
|
break;
|
|
}
|
|
theErr = MakeOneIcon(theFile, large4BitData, gIconGWorld, theGWorld, &gIconRect, &bigRect);
|
|
if (theErr != noErr) break;
|
|
theErr = MakeOneIcon(theFile, small4BitData, gIconGWorld, theGWorld, &gIconRect, &smallRect);
|
|
if (theErr != noErr) break;
|
|
DisposeGWorld(theGWorld);
|
|
theGWorld = nil;
|
|
if (!(theGWorld = MyNewGWorld(&bigRect, 1, nil, nil, true, false)))
|
|
if (!(theGWorld = MyNewGWorld(&bigRect, 1, nil, nil, true, true))) {
|
|
theErr = memFullErr;
|
|
break;
|
|
}
|
|
theErr = MakeOneIcon(theFile, large1BitMask, gIconGWorld, theGWorld, &gIconRect, &bigRect);
|
|
if (theErr != noErr) break;
|
|
theErr = MakeOneIcon(theFile, small1BitMask, gIconGWorld, theGWorld, &gIconRect, &smallRect);
|
|
break;
|
|
}
|
|
if (theGWorld) DisposeGWorld(theGWorld);
|
|
if (theFile) CloseResFile(theFile);
|
|
if (theErr != noErr) gIntError = errCantCreateIcons;
|
|
return theErr;
|
|
}
|
|
|
|
/*
|
|
* MakeOneIcon(theFile, theType, src, dst, srcRect, dstRect)
|
|
*
|
|
* Purpose: Creates and adds a single icon resource to theFile
|
|
* Inputs: theFile = path number of the destination file's resource fork
|
|
* theType = the type of icon to create
|
|
* src = pointer to the source GWorld
|
|
* dst = pointer to a properly-sized destination GWorld
|
|
* srcRect = pointer to the source image's rectangle
|
|
* dstRect = pointer to the destination icon's rectangle
|
|
* Returns: an OSErr describing the result
|
|
*
|
|
*/
|
|
|
|
static OSErr MakeOneIcon(short theFile, OSType theType, GWorldPtr src, GWorldPtr dst,
|
|
Rect *srcRect, Rect *dstRect)
|
|
{
|
|
#if applec
|
|
#pragma unused(theFile)
|
|
#endif
|
|
PixMapHandle srcMap = GetGWorldPixMap(src), dstMap = GetGWorldPixMap(dst);
|
|
Rect realDstRect = *srcRect;
|
|
Handle theHandle;
|
|
short offset;
|
|
OSErr theErr;
|
|
|
|
MapRect(&realDstRect, &src->portRect, dstRect);
|
|
if (Width(&realDstRect) < Height(&realDstRect)) {
|
|
offset = (Height(&realDstRect) - Width(&realDstRect)) >> 1;
|
|
realDstRect.left += offset;
|
|
realDstRect.right += offset;
|
|
} else {
|
|
offset = (Width(&realDstRect) - Height(&realDstRect)) >> 1;
|
|
realDstRect.top += offset;
|
|
realDstRect.bottom += offset;
|
|
}
|
|
PushPort();
|
|
MySetPort(dst);
|
|
HLock((Handle)srcMap);
|
|
HLock((Handle)dstMap);
|
|
LockPixels(srcMap);
|
|
LockPixels(dstMap);
|
|
EraseRect(dstRect);
|
|
CopyBits((BitMap *)*srcMap, (BitMap *)*dstMap, srcRect, &realDstRect, srcCopy + ditherCopy, nil);
|
|
UnlockPixels(srcMap);
|
|
HUnlock((Handle)srcMap);
|
|
TrimIcon(&realDstRect);
|
|
if ((theErr = PixMapToHandle(dstMap, dstRect, &realDstRect, &theHandle)) != noErr) {
|
|
PopPort();
|
|
return theErr;
|
|
}
|
|
UnlockPixels(dstMap);
|
|
HUnlock((Handle)dstMap);
|
|
PopPort();
|
|
AddResource(theHandle, theType, kCustomIconResource, gNullString);
|
|
if ((theErr = ResError()) == noErr) {
|
|
WriteResource(theHandle);
|
|
KeepSpinning();
|
|
ReleaseResource(theHandle);
|
|
return noErr;
|
|
};
|
|
DisposeHandle(theHandle);
|
|
return theErr;
|
|
}
|
|
|
|
/*
|
|
* TrimIcon(theRect)
|
|
*
|
|
* Purpose: Trims the appropriate icon by folding over the upper-right corner
|
|
* Inputs: theRect = pointer to the icon's rectangle
|
|
* Returns: nothing
|
|
*
|
|
*/
|
|
|
|
static void TrimIcon(Rect *theRect)
|
|
{
|
|
short offset = (Height(theRect) > 16 || Width(theRect) > 16) ? 6 : 4;
|
|
Rect cornerRect;
|
|
|
|
RGBForeColor(&gBlack);
|
|
RGBBackColor(&gWhite);
|
|
FrameRect(theRect);
|
|
if (gThePrefs.iconStyle != isSquareDog && gThePrefs.iconStyle != isProportionalDog) return;
|
|
MySetRect(&cornerRect, theRect->right - offset - 1, theRect->top, theRect->right, theRect->top + offset);
|
|
EraseRect(&cornerRect);
|
|
cornerRect.right--;
|
|
MoveTo(cornerRect.left, cornerRect.top);
|
|
LineTo(cornerRect.right, cornerRect.bottom);
|
|
LineTo(cornerRect.left, cornerRect.bottom);
|
|
LineTo(cornerRect.left, cornerRect.top);
|
|
}
|
|
|
|
/*
|
|
* PixMapToHandle(theMap, theRect, theHandle)
|
|
*
|
|
* Purpose: Takes the icon image in theMap and makes an icon handle from it
|
|
* Inputs: theMap = handle to the icon's pixmap
|
|
* theRect = pointer to the icon's rectangle
|
|
* theHandle = pointer to a place to receive the new handle
|
|
* Returns: an OSErr describing the result
|
|
*
|
|
*/
|
|
|
|
static OSErr PixMapToHandle(PixMapHandle theMap, Rect *theRect, Rect *maskRect, Handle *theHandle)
|
|
{
|
|
long theSize = (Height(theRect) * Width(theRect) * (*theMap)->pixelSize) >> 3;
|
|
long r, c, words, rowBytes = (*theMap)->rowBytes & 0x3fff;
|
|
char mode = true32b;
|
|
ushort *s, *d;
|
|
|
|
if ((*theMap)->pixelSize == 1) theSize <<= 1;
|
|
if (*theHandle = NewHandle(theSize)) {
|
|
words = (Width(theRect) * (*theMap)->pixelSize) >> 4;
|
|
s = (ushort *)GetPixBaseAddr(theMap);
|
|
d = (ushort *)StripAddress(**theHandle);
|
|
SwapMMUMode(&mode);
|
|
for (r = 0; r < Height(theRect); r++) {
|
|
for (c = 0; c < words; c++) *d++ = s[c];
|
|
s += rowBytes >> 1;
|
|
}
|
|
SwapMMUMode(&mode);
|
|
} else return memFullErr;
|
|
if ((*theMap)->pixelSize == 1) {
|
|
short offset = (Height(theRect) > 16 || Width(theRect) > 16) ? 6 : 4;
|
|
Rect cornerRect;
|
|
EraseRect(theRect);
|
|
PaintRect(maskRect);
|
|
if (gThePrefs.iconStyle == isSquareDog || gThePrefs.iconStyle == isProportionalDog) {
|
|
MySetRect(&cornerRect, maskRect->right - offset - 1, maskRect->top, maskRect->right, maskRect->top + offset);
|
|
EraseRect(&cornerRect);
|
|
cornerRect.right--;
|
|
MoveTo(cornerRect.left, cornerRect.top);
|
|
LineTo(cornerRect.right, cornerRect.bottom);
|
|
while (++cornerRect.top != cornerRect.bottom) {
|
|
MoveTo(cornerRect.left, cornerRect.top);
|
|
LineTo(cornerRect.left + cornerRect.top - maskRect->top, cornerRect.top);
|
|
}
|
|
}
|
|
s = (ushort *)GetPixBaseAddr(theMap);
|
|
SwapMMUMode(&mode);
|
|
for (r = 0; r < Height(theRect); r++) {
|
|
for (c = 0; c < words; c++) *d++ = s[c];
|
|
s += rowBytes >> 1;
|
|
}
|
|
SwapMMUMode(&mode);
|
|
}
|
|
return noErr;
|
|
}
|
|
|
|
/*
|
|
* DoOpenDocument(theSpec, first)
|
|
*
|
|
* Purpose: Calls the appropriate routines to open an new image
|
|
* Inputs: theSpec = the file specification for the new input file
|
|
* first = flag: true if this is the first image in a series
|
|
* Returns: an OSErr describing the result
|
|
*
|
|
*/
|
|
|
|
OSErr DoOpenDocument(FSSpec *theFile, Boolean first, DescType openScreen, DescType openFull,
|
|
Boolean autoExpand, Boolean openVisible, DescType openPalette, DescType drawQuality,
|
|
Boolean openBitmaps, Boolean autoComments, Boolean fixTypes, Boolean fixCreator)
|
|
{
|
|
OSErr theErr = noErr;
|
|
ImageHandle theImage;
|
|
|
|
LMSetSFSaveDisk(-theFile->vRefNum);
|
|
LMSetCurDirStore(theFile->parID);
|
|
if (theImage = NewImage()) {
|
|
(*theImage)->file = *theFile;
|
|
if ((theErr = ReadFile(theImage, fixTypes, fixCreator)) == noErr) {
|
|
if ((theErr = InitImage(theImage, openScreen, openFull, drawQuality, openVisible)) == noErr) {
|
|
if ((theErr = MakeNewWindow(theImage, first, autoExpand)) == noErr) {
|
|
MySetPort((CGrafPtr)(*theImage)->window);
|
|
if (gSlideShow) SlideProgress(codecProgressRealOpen, 0, 0);
|
|
AddWindowItem(theImage);
|
|
if ((theErr = PickFirstPalette(theImage, openPalette)) == noErr) {
|
|
if ((theErr = RenderOffscreen(theImage, openBitmaps)) == noErr) {
|
|
if (gSlideShow) SlideProgress(codecProgressRealClose, 0, 0);
|
|
if (openVisible) {
|
|
if (!gSlideShow && first) ChangeActive((*theImage)->window);
|
|
else FWShowWindow((*theImage)->window);
|
|
if (autoComments && (*theImage)->comments &&
|
|
((*theImage)->window == FWFrontWindow())) {
|
|
if (OpenComments() == noErr)
|
|
ChangeActive(GetCommentsWindow());
|
|
}
|
|
SetStatistics();
|
|
AdjustMenus();
|
|
}
|
|
return noErr;
|
|
}
|
|
}
|
|
if (gSlideShow) SlideProgress(codecProgressRealClose, 0, 0);
|
|
} else gIntError = errCantOpenWindow;
|
|
}
|
|
}
|
|
} else gIntError = errNoLoadMemory, theErr = memFullErr;
|
|
if ((theErr != codecAbortErr || gSlideShow) && theImage) {
|
|
if ((*theImage)->window) DoCloseWindow((*theImage)->window, gThePrefs.restoreColors);
|
|
else DisposeImage(theImage);
|
|
}
|
|
if (theErr == errBadJPEG) theErr = codecBadDataErr;
|
|
return theErr;
|
|
}
|
|
|
|
/*
|
|
* ReadFile(theImage)
|
|
*
|
|
* Purpose: Opens the input file, figures its type, and dispatches the read operation
|
|
* to the appropriate routine
|
|
* Inputs: theImage = the destination image record for the new image
|
|
* Returns: an OSErr describing the problem
|
|
*
|
|
*/
|
|
|
|
OSErr ReadFile(ImageHandle theImage, Boolean fixTypes, Boolean fixCreator)
|
|
{
|
|
FSSpec theSpec = (*theImage)->file;
|
|
short theFile, theType;
|
|
OSErr theErr = noErr;
|
|
|
|
if (gGetAbout) {
|
|
(*theImage)->format = &gPICTFormat;
|
|
(*theImage)->data = GetResource(kPICTType, rAboutPict);
|
|
if (!(*theImage)->data) theErr = ResError();
|
|
else DetachResource((*theImage)->data);
|
|
(*theImage)->flags |= ifIsAboutBox;
|
|
gGetAbout = false;
|
|
return theErr;
|
|
}
|
|
if ((theErr = FSpOpenDF(&theSpec, fsRdPerm, &theFile)) != noErr)
|
|
theFile = -1;
|
|
if ((theType = IDImage(theFile, &theSpec)) != kInvalidType) {
|
|
if (fixTypes) {
|
|
FInfo theInfo;
|
|
FSpGetFInfo(&theSpec, &theInfo);
|
|
if (theInfo.fdType != gFormat[theType]->inType &&
|
|
theInfo.fdType != gFormat[theType]->altInType) {
|
|
theInfo.fdType = gFormat[theType]->inType;
|
|
if (fixCreator) theInfo.fdCreator = kCreator;
|
|
FSpSetFInfo(&theSpec, &theInfo);
|
|
FSClose(theFile);
|
|
theErr = FSpOpenDF(&theSpec, fsRdPerm, &theFile);
|
|
}
|
|
}
|
|
if (theErr == noErr) {
|
|
(*theImage)->format = gFormat[theType];
|
|
if ((*theImage)->format->load)
|
|
theErr = (*theImage)->format->load(theFile, theImage);
|
|
else theErr = DefaultLoad(theFile, theImage);
|
|
}
|
|
} else {
|
|
if (gIntError != errHSIFile) gIntError = errInvalidFile;
|
|
theErr = errAETypeError;
|
|
}
|
|
if (theFile != -1) FSClose(theFile);
|
|
return theErr;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// OSErr DefaultLoad(short theFile, ImageHandle theImage)
|
|
//=====================================================================================
|
|
// Performs a generic load of an image into memory, with no special processing.
|
|
//=====================================================================================
|
|
|
|
extern OSErr DefaultLoad(short theFile, ImageHandle theImage)
|
|
{
|
|
OSErr theErr = noErr;
|
|
long theSize;
|
|
|
|
KeepSpinning();
|
|
GetEOF(theFile, &theSize);
|
|
if (!MakeMemAvailable(theSize)) return gIntError = errNoLoadMemory, memFullErr;
|
|
if ((*theImage)->data = NewHandle(theSize)) {
|
|
HLock((*theImage)->data);
|
|
SpinIndef();
|
|
theErr = FSRead(theFile, &theSize, *(*theImage)->data);
|
|
KeepSpinning();
|
|
HUnlock((*theImage)->data);
|
|
if (theErr == noErr) return theErr;
|
|
else DisposeHandle((*theImage)->data);
|
|
gIntError = errCantReadFile;
|
|
} else gIntError = errNoLoadMemory, theErr = memFullErr;
|
|
return theErr;
|
|
}
|
|
|
|
/*
|
|
* IDImage(theFile, theSpec)
|
|
*
|
|
* Purpose: Determines the image type by loading in a little and examining the header
|
|
* Inputs: theFile = a pointer to the file number
|
|
* theSpec = the original file specification
|
|
* Returns: the index of the file type, or -1 if not a valid type
|
|
*
|
|
*/
|
|
|
|
short IDImage(short theFile, FSSpec *theSpec)
|
|
{
|
|
uchar theBuffer[kHeaderSize + 64];
|
|
long theSize = kHeaderSize + 64;
|
|
OSErr theErr = noErr;
|
|
FInfo theInfo;
|
|
short i;
|
|
|
|
if (theFile != -1) {
|
|
SetFPos(theFile, fsFromStart, 0);
|
|
theErr = FSRead(theFile, &theSize, theBuffer);
|
|
if (theErr != noErr && theErr != eofErr) theSize = 0;
|
|
SetFPos(theFile, fsFromStart, 0);
|
|
} else theSize = 0;
|
|
if (FSpGetFInfo(theSpec, &theInfo) == noErr) {
|
|
for (i = 0; i < kFileFormats; i++)
|
|
if (gFormat[i]->id) {
|
|
Boolean valid = gFormat[i]->id(theBuffer, theSize, theFile, theSpec);
|
|
if (valid && (gFormat[i]->positiveID ||
|
|
gFormat[i]->inType == theInfo.fdType ||
|
|
gFormat[i]->altInType == theInfo.fdType)) return i;
|
|
}
|
|
}
|
|
if (theBuffer[0] == 'h' && theBuffer[1] == 's' && theBuffer[2] == 'i' && theBuffer[3] == '1') gIntError = errHSIFile;
|
|
/*
|
|
#define errBinHexData 26
|
|
#define errUUEncodeData 27
|
|
*/
|
|
return kInvalidType;
|
|
}
|
|
|
|
/*
|
|
* InitImage(theImage, openScreen, openFull, drawQuality)
|
|
*
|
|
* Purpose: Initializes the fields in the image record
|
|
* Inputs: theImage = the destination image record
|
|
* openScreen = AE enum describing which screen to open up on
|
|
* openFull = AE enum describing how to use full screen windows
|
|
* drawQuality = AE enum describing the default drawing quality
|
|
* visible = flag: true if the image is to be made visible
|
|
* Returns: an OSErr describing what went wrong
|
|
*
|
|
*/
|
|
|
|
OSErr InitImage(ImageHandle theImage, DescType openScreen, DescType openFull,
|
|
DescType drawQuality, Boolean visible)
|
|
{
|
|
FSSpec theSpec = (*theImage)->file;
|
|
short choice, refNum;
|
|
OSErr theErr, AEErr;
|
|
|
|
switch (openScreen) {
|
|
case kAEOSDeepestColor: (*theImage)->dmon = GetDeepMonitor(true); break;
|
|
case kAEOSDeepestGray: (*theImage)->dmon = GetDeepMonitor(false); break;
|
|
case kAEOSLargestColor: (*theImage)->dmon = GetBigMonitor(true); break;
|
|
case kAEOSLargestGray: (*theImage)->dmon = GetBigMonitor(false); break;
|
|
case kAEOSMain: (*theImage)->dmon = gMainMonitor; break;
|
|
}
|
|
(*theImage)->smon = (*theImage)->dmon;
|
|
theErr = (*theImage)->format->open(theImage);
|
|
if (Height(&(*theImage)->grect) <= 0 || Width(&(*theImage)->grect) <= 0)
|
|
gIntError = errCorruptImage, theErr = codecBadDataErr;
|
|
if (theErr == errBadJPEG && (!gSlideShow || !(*gSlideOptions)->noErrors) &&
|
|
(gThePrefs.useQuickTime && gQTVersion)) {
|
|
if (gInAppleEvent) {
|
|
AEErr = AEInteractWithUser(kAEDefaultTimeout, &gTheNotification,
|
|
gAEIdleProc);
|
|
if (AEErr == errAENoUserInteraction) return codecBadDataErr;
|
|
}
|
|
StopSpinning(&qd.arrow);
|
|
ParamText(gNullString, (*theImage)->file.name, gNullString, gNullString);
|
|
choice = FWCautionAlert(CenterAlert(rBadJPEGAlert), gGenericFilter);
|
|
if (choice == 3 || choice == kDialogCancel) return theErr;
|
|
else gIntError = noErr, theErr = noErr;
|
|
(*theImage)->flags |= ifCorrupt;
|
|
StartSpinning();
|
|
}
|
|
if (UseFullScreen(theImage, openFull, visible)) (*theImage)->flags |= ifFull;
|
|
if (gSlideShow && (*gSlideOptions)->fileNames) (*theImage)->flags |= ifFilename;
|
|
switch (drawQuality) {
|
|
case kAEDQVeryHigh: (*theImage)->quality = iqVHigh; break;
|
|
case kAEDQHigh: (*theImage)->quality = iqHigh; break;
|
|
case kAEDQNormal: (*theImage)->quality = iqMedium; break;
|
|
}
|
|
if ((*theImage)->comments) {
|
|
Size oldLength = GetHandleSize((*theImage)->comments), length = 0, i;
|
|
uchar *src = (uchar *)*(*theImage)->comments, *dst = src;
|
|
|
|
for (i = 0; i < oldLength; i++)
|
|
if (*src >= 0x20) break;
|
|
else src++;
|
|
for ( ; i < oldLength; i++)
|
|
if ((*src == 0x0d) || (*src >= 0x20)) *dst++ = *src++, length++;
|
|
else src++;
|
|
while ((length > 0) && (*--dst < 0x20)) length--;
|
|
if (length > 0) SetHandleSize((*theImage)->comments, length);
|
|
else DisposeHandle((*theImage)->comments), (*theImage)->comments = nil;
|
|
}
|
|
if (refNum = FSpOpenResFile(&theSpec, fsRdPerm)) {
|
|
Rect **theRect;
|
|
if (theRect = (Rect **)Get1Resource('RECT', 0)) {
|
|
(*theImage)->crect = **theRect;
|
|
(*theImage)->flags |= ifCropped;
|
|
}
|
|
CloseResFile(refNum);
|
|
}
|
|
return theErr;
|
|
}
|
|
|
|
/*
|
|
* UseFullScreen(theImage, openFull, visible)
|
|
*
|
|
* Purpose: Determines whether the new image should be opened in full screen mode
|
|
* Inputs: theImage = the image record for the image in question
|
|
* openFull = AE enum describing how to use full screen windows
|
|
* visible = flag: true if the image is to be made visible
|
|
* Returns: nothing
|
|
*
|
|
*/
|
|
|
|
Boolean UseFullScreen(ImageHandle theImage, DescType openFull, Boolean visible)
|
|
{
|
|
Rect screenRect, theRect;
|
|
Boolean result;
|
|
short choice;
|
|
OSErr AEErr;
|
|
|
|
if (openFull == kAEOFAlways) return true;
|
|
else if (openFull == kAEOFNever) return false;
|
|
GetActiveRect((*theImage)->dmon, &screenRect);
|
|
screenRect.top += gTitleBarHeight;
|
|
theRect = (*theImage)->crect;
|
|
OffsetRect(&theRect, -theRect.left, -theRect.top);
|
|
if (Width(&theRect) > Width(&screenRect))
|
|
theRect.bottom = (long)theRect.bottom * (long)Width(&screenRect) / (long)Width(&theRect);
|
|
if (Height(&theRect) <= Height(&screenRect)) return false;
|
|
if (openFull == kAEOFAlwaysIfLarge) return true;
|
|
if (!visible) return false;
|
|
if (gInAppleEvent) {
|
|
AEErr = AEInteractWithUser(kAEDefaultTimeout, &gTheNotification,
|
|
gAEIdleProc);
|
|
if (AEErr == errAENoUserInteraction) return false;
|
|
}
|
|
StopSpinning(&qd.arrow);
|
|
ParamText(gNullString, (*theImage)->file.name, gNullString, gNullString);
|
|
choice = FWAlert(CenterAlert(rTooBigAlert), gGenericFilter);
|
|
result = (choice == 1 || choice == kDialogOK);
|
|
StartSpinning();
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* PickFirstPalette(theImage, openPalette)
|
|
*
|
|
* Purpose: Determines which palette we should use for the given image
|
|
* Inputs: theImage = the image record for the image in question
|
|
* openFull = AE enum describing palette preferences
|
|
* Returns: an OSErr describing what went wrong
|
|
*
|
|
*/
|
|
|
|
OSErr PickFirstPalette(ImageHandle theImage, DescType openPalette)
|
|
{
|
|
short depth = (*(*theImage)->dmon)->depth;
|
|
Boolean image = (*theImage)->ipalette && ((*(*theImage)->ipalette)->pmEntries <= (1L << depth));
|
|
Boolean quantize = ((*theImage)->depth >= 16);
|
|
short palette = plNone;
|
|
OSErr theErr = noErr;
|
|
DescType thePalette;
|
|
|
|
while (true) {
|
|
if (!(*(*theImage)->dmon)->color && (depth <= 8)) {
|
|
palette = plGrey;
|
|
break;
|
|
} else if (depth <= 2) {
|
|
palette = plGrey;
|
|
break;
|
|
} else if (depth > 8) {
|
|
palette = plSys;
|
|
break;
|
|
}
|
|
switch (openPalette) {
|
|
case kAEOPQuantImageScrn:
|
|
case kAEOPQuantScrn:
|
|
if (quantize) palette = plQuant;
|
|
if ((openPalette != kAEOPQuantScrn) && image && (palette == plNone))
|
|
palette = plImage;
|
|
if (palette == plNone) palette = plSys;
|
|
break;
|
|
case kAEOPImageQuantScrn:
|
|
case kAEOPImageScrn:
|
|
if (image) palette = plImage;
|
|
if ((openPalette != kAEOPImageScrn) && quantize && (palette == plNone))
|
|
palette = plQuant;
|
|
if (palette == plNone) palette = plSys;
|
|
break;
|
|
case kAEOPScrn:
|
|
palette = plSys;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
switch (palette) {
|
|
case plGrey: thePalette = kAEPGrayscale; break;
|
|
case plImage: thePalette = kAEPImage; break;
|
|
case plQuant: thePalette = kAEPQuantized; break;
|
|
case plSys:
|
|
default: thePalette = kAEPSystem; break;
|
|
}
|
|
if (!gThePrefs.ditherQuant && (palette == plQuant ||
|
|
(palette == plImage && (*theImage)->depth >= 16)))
|
|
(*theImage)->flags &= ~ifDithered;
|
|
else (*theImage)->flags |= ifDithered;
|
|
if (theErr == noErr) theErr = DoSetPalette((*theImage)->window, thePalette);
|
|
if (palette == plQuant && (*theImage)->npalette != plQuant) (*theImage)->flags |= ifDithered;
|
|
if (theErr == codecAbortErr && !gSlideShow) {
|
|
theErr = noErr;
|
|
gIntError = 0;
|
|
}
|
|
return theErr;
|
|
}
|
|
|
|
/*
|
|
* RenderOffscreen(theImage, openBitmaps)
|
|
*
|
|
* Purpose: Renders the given image into an offscreen buffer, if appropriate
|
|
* Inputs: theImage = a pointer to the image in question
|
|
* openBitmaps = flag: true if we should force an offscreen bitmap
|
|
* Returns: an OSErr describing the problem, if any
|
|
*
|
|
*/
|
|
|
|
OSErr RenderOffscreen(ImageHandle theImage, Boolean openBitmaps)
|
|
{
|
|
short flags = bfOffscreen + bfForceFull;
|
|
OSErr theErr = noErr;
|
|
ImageHandle image;
|
|
Boolean itWorked;
|
|
|
|
if ((*theImage)->gworld) return noErr;
|
|
if (openBitmaps) {
|
|
itWorked = MakeImageGWorld(theImage, false);
|
|
if (!itWorked && gSlideShow)
|
|
for (image = gImageRoot; image; image = (*image)->next) KillGWorld(image);
|
|
if (itWorked || MakeImageGWorld(theImage, false)) {
|
|
if (!gSlideShow) {
|
|
ChangeActive((*theImage)->window);
|
|
flags |= bfOnscreen;
|
|
}
|
|
if (!(flags & bfOnscreen) && gSlideShow) flags |= bfUseSlideProgress;
|
|
SetSlideControlsText(gString[strSlideRendering], (*theImage)->file.name, gNullString, gNullString);
|
|
theErr = DrawImage(theImage, flags, nil);
|
|
} else if (!gSlideShow && ((*theImage)->compression || !gThePrefs.noUncomp))
|
|
gIntError = errNoOffscreenMemory, theErr = memFullErr;
|
|
}
|
|
return theErr;
|
|
}
|