JPEGView/Source/C/ImageUtils.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
32 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 void HandleUpdateError(ImageHandle theImage, OSErr theErr);
static void DoDragSelection(ImageHandle theImage, EventRecord *theEvent);
static pascal OSErr SendItemData(FlavorType theType, void *dragSendRefCon,
ItemReference theItem, DragReference theDrag);
extern void DeferImageUpdate(ImageHandle theImage)
{
Pattern whitePat = { 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00 };
Pattern blackPat = { 0xff, 0xfd, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff };
RgnHandle theRgn;
Rect theRect;
PushPort();
MySetPort((CGrafPtr)(*theImage)->window);
InvalRgn((*theImage)->update);
BeginUpdate((*theImage)->window);
PenNormal();
RGBForeColor(&gBlack);
RGBBackColor(&gWhite);
theRect = (*theImage)->wrect;
if (theRgn = NewRgn()) {
RectRgn(theRgn, &theRect);
if (gDrawing && theImage == gDrawing)
DiffRgn(theRgn, (*theImage)->update, theRgn);
SetClip(theRgn);
DisposeRgn(theRgn);
} else ClipRect(&theRect);
HideAnts((*theImage)->ants);
FillRgn((*theImage)->window->visRgn, NeedsWhiteBG(theImage) ? &whitePat : &blackPat);
ShowAnts((*theImage)->ants);
ClipRect(&(*theImage)->window->portRect);
UnionRgn((*theImage)->update, (*theImage)->window->visRgn, (*theImage)->update);
EndUpdate((*theImage)->window);
(*theImage)->flags &= ~ifAborted;
PopPort();
}
extern OSErr UpdateImage(ImageHandle theImage)
{
Point thePoint = { 0, 0 };
OSErr theErr = noErr;
RgnHandle oldVisRgn;
StartSpinning();
PushPort();
MySetPort((CGrafPtr)(*theImage)->window);
if (oldVisRgn = NewRgn()) CopyRgn((*theImage)->window->visRgn, oldVisRgn);
ClipRect(&(*theImage)->window->portRect);
InvalRgn((*theImage)->update);
BeginUpdate((*theImage)->window);
SetClip((*theImage)->window->visRgn);
EndUpdate((*theImage)->window);
CopyRgn((*theImage)->window->clipRgn, (*theImage)->window->visRgn);
GetMinMaxMonitor(theImage);
gIntError = 0;
if (!EmptyRgn(oldVisRgn)) {
HideAnts((*theImage)->ants);
theErr = DrawImage(theImage, bfOnscreen, nil);
ShowAnts((*theImage)->ants);
}
CalcVis((WindowPeek)(*theImage)->window);
if (IsAboutBox(theImage)) {
Rect theRect = (*theImage)->wrect;
FontInfo theFontInfo;
Str63 theText;
TextFont(geneva);
TextSize(9);
TextMode(srcOr);
ClipRect(&theRect);
GetFontInfo(&theFontInfo);
#if defined(powerc) || defined(__powerc)
BlockMove(gString[strVersionPPC], theText, *gString[strVersionPPC] + 1);
#else
BlockMove(gString[strVersion68k], theText, *gString[strVersion68k] + 1);
#endif
StuffNumber(theText, 1, (kVersion >> 8) & 0xf);
StuffNumber(theText, 2, (kVersion >> 4) & 0xf);
MoveTo((*theImage)->wrect.right - StringWidth(theText) - 3,
(*theImage)->wrect.top + theFontInfo.ascent + theFontInfo.leading + 3);
DrawString(theText);
}
if (oldVisRgn && EqualRgn(oldVisRgn, (*theImage)->window->visRgn) && !Aborted(theImage)) {
SetEmptyRgn((*theImage)->update);
DisposeRgn(oldVisRgn);
}
if (theErr) HandleUpdateError(theImage, theErr);
SetStatistics();
PopPort();
StopSpinning(&qd.arrow);
return theErr;
}
static void HandleUpdateError(ImageHandle theImage, OSErr theErr)
{
if ((theErr == codecBadDataErr) || (gIntError == errCorruptImage)) {
if (Corrupt(theImage)) return;
(*theImage)->flags |= ifCorrupt;
}
HandleAEError(theErr, errNoDrawMemory, theImage ? FileName(theImage) : nil);
if (theErr == memFullErr) DoCloseWindow((*theImage)->window, gThePrefs.restoreColors);
}
extern void HandleImageActivate(ImageHandle theImage, Boolean nowActive)
{
if (theImage) {
if (nowActive) ActivateAnts((*theImage)->ants);
else DeactivateAnts((*theImage)->ants);
}
}
extern void HandleImageClick(ImageHandle theImage, EventRecord *theEvent)
{
Boolean inFront = (!gInBackground && (*theImage)->window == FWFrontWindow());
Rect theRect = { 0, 0, 0, 0 }, selRect;
Point where = theEvent->where;
GetAntsSelection((*theImage)->ants, &selRect);
PushPort();
MySetPort((CGrafPtr)(*theImage)->window);
// hidden feature: cmd-click forces redraw of window
if (theEvent->modifiers & cmdKey) {
KillGWorld(theImage);
theRect = (*theImage)->wrect;
InvalRect(&theRect);
inFront = true;
goto done;
}
// check to see if we're even inside the drawing area of the window
GlobalToLocal(&where);
if (!gSlideShow && !IsAboutBox(theImage) && PtInRect(where, &(*theImage)->wrect)) {
// if we're in the size box area, resize the window
if (inFront && (((*theImage)->wrect.right - where.h) < 16) && (((*theImage)->wrect.bottom - where.v) < 16)) {
Boolean modified = (theEvent->modifiers & (optionKey + cmdKey + shiftKey)) != 0;
if (TrackGrowWindow(where, theImage, !modified, &theRect)) {
SendSetDrawBounds((*theImage)->window, &theRect);
goto done;
}
} else if (!Full(theImage) || (*theImage)->dmon != gMainMonitor || !gMenuVisible) {
// if we're selected, either drag or cancel the selection
if (AntsHaveSelection((*theImage)->ants)) {
if (PtInRect(where, &selRect) && !EqualRect(&selRect, &(*theImage)->wrect)) {
if ((inFront && TrackDragRect(where, theImage) && gDragMgrPresent) ||
(!inFront && gDragMgrPresent && WaitMouseMoved(theEvent->where))) {
DoDragSelection(theImage, theEvent);
inFront = true;
}
GetAntsSelection((*theImage)->ants, &theRect);
SendSetSelection(theImage, &theRect);
goto done;
}
}
// if we fall through here, we need to track a new selection
if (inFront) {
SendSetSelection(theImage, &theRect);
TrackAntsSelection((*theImage)->ants, where);
GetAntsSelection((*theImage)->ants, &theRect);
SendSetSelection(theImage, &theRect);
if (AntsHaveSelection((*theImage)->ants)) goto done;
}
// special case: if full screen image overlapped by menubar, treat as window in background
} else if (AntsHaveSelection((*theImage)->ants) && PtInRect(where, &selRect) && gDragMgrPresent && WaitMouseMoved(theEvent->where)) {
DoDragSelection(theImage, theEvent);
inFront = true;
}
}
// if we fall all the way through here, we need to flip the menu bar
AdjustMenus();
if (Full(theImage) && (*theImage)->dmon == gMainMonitor && gSlideShow != kPaused) {
if (gMenuVisible) {
RehideMenuBar();
ActivateAnts((*theImage)->ants);
} else {
DeactivateAnts((*theImage)->ants);
UnhideMenuBar();
}
}
done:
if (!inFront) ChangeActive((*theImage)->window);
AdjustMenus();
PopPort();
}
static void DoDragSelection(ImageHandle theImage, EventRecord *theEvent)
{
static DragSendDataProc gSendItemData = nil;
RgnHandle theRgn = NewRgn(), tempRgn = NewRgn();
DragReference theDrag;
Rect startRect;
OSErr theErr;
GetAntsSelection((*theImage)->ants, &startRect);
if (!gSendItemData) gSendItemData = NewDragSendDataProc((ProcPtr)SendItemData);
if (theRgn && tempRgn) {
theErr = NewDrag(&theDrag);
if (theErr == noErr) {
AddDragItemFlavor(theDrag, (ItemReference)theImage, 'PICT', nil, 0, 0);
GlobalRect(&startRect, (*theImage)->window);
RectRgn(theRgn, &startRect);
RectRgn(tempRgn, &startRect);
InsetRgn(tempRgn, 1, 1);
DiffRgn(theRgn, tempRgn, theRgn);
SetDragItemBounds(theDrag, (ItemReference)theImage,
&(*theRgn)->rgnBBox);
SetDragSendProc(theDrag, gSendItemData, nil);
SetCursor(&qd.arrow);
theErr = TrackDrag(theDrag, theEvent, theRgn);
DisposeDrag(theDrag);
}
}
if (theRgn) DisposeRgn(theRgn);
if (tempRgn) DisposeRgn(tempRgn);
}
static pascal OSErr SendItemData(FlavorType theType, void *dragSendRefCon,
ItemReference theItem, DragReference theDrag)
{
#if applec
#pragma unused(dragSendRefCon)
#endif
Boolean dontDispose = false;
PicHandle thePicture;
OSErr theErr;
switch (theType) {
case 'PICT':
theErr = GetSelectedPICT((ImageHandle)theItem, &thePicture);
if (theErr == noErr) {
if (!thePicture) {
thePicture = (PicHandle)(*(ImageHandle)theItem)->data;
dontDispose = true;
}
HLock((Handle)thePicture);
theErr = SetDragItemFlavorData(theDrag, theItem, 'PICT',
*thePicture, GetHandleSize((Handle)thePicture), 0);
if (!dontDispose) DisposeHandle((Handle)thePicture);
}
break;
default:
theErr = badDragFlavorErr;
break;
}
return theErr;
}
/*
* NewImage()
*
* Purpose: Adds a new image to the linked list
* Inputs: none
* Returns: a pointer to the new image
*
*/
ImageHandle NewImage(void)
{
ImageHandle theImage;
if (gImageRoot) {
theImage = LastImage();
theImage = (*theImage)->next = (ImageHandle)NewHandleClear(sizeof(ImageItem));
} else
theImage = gImageRoot = (ImageHandle)NewHandleClear(sizeof(ImageItem));
(*theImage)->id = kImageID;
(*theImage)->update = NewRgn();
(*theImage)->ants = NewAnts();
return theImage;
}
/*
* DisposeImage(theImage)
*
* Purpose: Removes the given image from the linked list
* Inputs: theImage = the image record to be removed
* Returns: nothing
*
*/
void DisposeImage(ImageHandle theImage)
{
Boolean foundData = false, foundGWorld = false;
ImageHandle tempImage;
PushPort();
MySetPort(nil);
(*theImage)->id = 0;
for (tempImage = gImageRoot; tempImage; tempImage = (*tempImage)->next)
if (tempImage != theImage) {
if ((*tempImage)->data == (*theImage)->data) foundData = true;
if ((*tempImage)->gworld == (*theImage)->gworld) foundGWorld = true;
}
if (!foundData && (*theImage)->data) DisposeHandle((*theImage)->data);
if (!foundGWorld) KillGWorld(theImage);
if ((*theImage)->ipalette) DisposePalette((*theImage)->ipalette);
if ((*theImage)->qpalette) DisposePalette((*theImage)->qpalette);
if ((*theImage)->palette) DisposePalette((*theImage)->palette);
if ((*theImage)->comments) DisposeHandle((*theImage)->comments);
if ((*theImage)->window) {
FWCloseWindow((*theImage)->window);
DeallocateWindow((*theImage)->window);
}
if ((*theImage)->update) DisposeRgn((*theImage)->update);
if ((*theImage)->format && (*theImage)->format->close)
(*theImage)->format->close(theImage);
if ((*theImage)->privateData) DisposeHandle((*theImage)->privateData);
if (theImage != gImageRoot) (*PreviousImage(theImage))->next = (*theImage)->next;
else gImageRoot = (*gImageRoot)->next;
if ((*theImage)->ants) DisposeAnts((*theImage)->ants);
DisposeHandle((Handle)theImage);
PopPort();
}
/*
* CopyImage(theImage)
*
* Purpose: Makes a copy of the given image
* Inputs: theImage = the image record to be copied
* Returns: a pointer to a new copy of the image
*
*/
ImageHandle CopyImage(ImageHandle srcImage)
{
ImageHandle dstImage, theImage;
PaletteHandle newPalette;
AntsReference oldAnts;
RgnHandle oldUpdate;
Handle tmpHandle;
if (dstImage = NewImage()) {
oldUpdate = (*dstImage)->update;
oldAnts = (*dstImage)->ants;
BlockMove(*srcImage, *dstImage, sizeof(ImageItem));
(*dstImage)->update = oldUpdate;
(*dstImage)->ants = oldAnts;
(*dstImage)->next = nil;
(*dstImage)->window = nil;
if ((*srcImage)->palette) {
newPalette = NewPalette((*(*srcImage)->palette)->pmEntries, nil, pmTolerant, 0);
if (newPalette) {
CopyPalette((*srcImage)->palette, newPalette, 0, 0,
(*(*srcImage)->palette)->pmEntries);
(*dstImage)->palette = newPalette;
}
}
if ((*srcImage)->qpalette) {
newPalette = NewPalette((*(*srcImage)->qpalette)->pmEntries, nil, pmTolerant, 0);
if (newPalette) {
CopyPalette((*srcImage)->qpalette, newPalette, 0, 0,
(*(*srcImage)->qpalette)->pmEntries);
(*dstImage)->qpalette = newPalette;
}
}
if ((*srcImage)->ipalette) {
newPalette = NewPalette((*(*srcImage)->ipalette)->pmEntries, nil, pmTolerant, 0);
if (newPalette) {
CopyPalette((*srcImage)->ipalette, newPalette, 0, 0,
(*(*srcImage)->ipalette)->pmEntries);
(*dstImage)->ipalette = newPalette;
}
}
tmpHandle = (*dstImage)->comments;
if (tmpHandle) {
HandToHand(&tmpHandle);
(*dstImage)->comments = tmpHandle;
}
tmpHandle = (*dstImage)->privateData;
if (tmpHandle) {
HandToHand(&tmpHandle);
(*dstImage)->privateData = tmpHandle;
}
if ((*srcImage)->format->clone) (*srcImage)->format->clone(srcImage, dstImage);
(*dstImage)->flags &= ~(ifSelected + ifSelVisible + ifAborted + ifScreenSize +
ifFilename);
for (theImage = gImageRoot; theImage; theImage = (*theImage)->next)
if ((theImage != dstImage) && ((*theImage)->num >= (*dstImage)->num))
(*dstImage)->num = (*theImage)->num + 1;
}
return dstImage;
}
/*
* LastImage()
*
* Purpose: Returns a pointer to the last image in the linked list
* Inputs: none
* Returns: a pointer to the last image, or nil if no images exist
*
*/
ImageHandle LastImage(void)
{
ImageHandle theImage;
if (!gImageRoot) return nil;
for (theImage = gImageRoot; (*theImage)->next; theImage = (*theImage)->next);
return theImage;
}
/*
* PreviousImage(theImage)
*
* Purpose: Returns a pointer to the image preceding theImage in the linked list
* Inputs: none
* Returns: a pointer to the preceding image, or nil if theImage is gImageRoot
*
*/
ImageHandle PreviousImage(ImageHandle theImage)
{
ImageHandle curImage;
if (theImage == gImageRoot) return nil;
for (curImage = gImageRoot; curImage && ((*curImage)->next != theImage);
curImage = (*curImage)->next);
return curImage;
}
/*
* AnotherFull(theImage)
*
* Purpose: Determines if there is another full-screen image on the same monitor as
* theImage
* Inputs: theImage = a handle to the image
* Returns: true if there is another full-screen image; false otherwise
*
*/
Boolean AnotherFull(ImageHandle theImage)
{
MonitorHandle imageMon = theImage ? (*theImage)->dmon : gMainMonitor;
ImageHandle fullImage;
if (imageMon == gMainMonitor && GetSlideBackWindow()) return true;
for (fullImage = gImageRoot; fullImage; fullImage = (*fullImage)->next)
if ((fullImage != theImage) && Full(fullImage) && (*fullImage)->dmon == imageMon) break;
return (fullImage != nil);
}
/*
* FindImage(theWindow)
*
* Purpose: Scans the list of images for one who owns the given window
* Inputs: theWindow = a pointer to the window record
* Returns: a pointer to the associated image, or nil if none found
*
*/
ImageHandle FindImage(WindowPtr theWindow)
{
ImageHandle theImage;
if (theWindow)
for (theImage = gImageRoot; theImage; theImage = (*theImage)->next)
if ((*theImage)->window == theWindow) return theImage;
return nil;
}
/*
* UpdateImages(theMonitor)
*
* Purpose: Updates any images whose deepest monitor happens to be theMonitor
* Inputs: theMonitor = the monitor whose state has changed
* Returns: nothing
*
*/
void UpdateImages(MonitorHandle theMonitor)
{
#if applec
#pragma unused(theMonitor)
#endif
short depth, palette;
ImageHandle theImage;
Boolean grey;
for (theImage = gImageRoot; theImage; theImage = (*theImage)->next) {
GetMinMaxMonitor(theImage);
depth = (*(*theImage)->dmon)->depth;
grey = !(*(*theImage)->dmon)->color;
if (grey || (depth < 4)) palette = plGrey;
else if (depth > 8) palette = plSys;
else palette = (*theImage)->npalette;
switch (palette) {
case plSys:
DoSetPalette((*theImage)->window, kAEPSystem);
break;
case plGrey:
DoSetPalette((*theImage)->window, kAEPGrayscale);
break;
case plImage:
if ((*(*theImage)->ipalette)->pmEntries <= (1L << depth))
DoSetPalette((*theImage)->window, kAEPImage);
else DoSetPalette((*theImage)->window, kAEPSystem);
break;
case plQuant:
if ((*theImage)->qpalette &&
((1L << depth) != ((*(*theImage)->qpalette)->pmEntries))) {
DisposePalette((*theImage)->qpalette);
(*theImage)->qpalette = nil;
DoSetPalette((*theImage)->window, kAEPSystem);
} else DoSetPalette((*theImage)->window, kAEPQuantized);
break;
}
if (!gThePrefs.ditherQuant && ((*theImage)->npalette == plQuant ||
((*theImage)->npalette == plImage && (*theImage)->depth >= 16)))
(*theImage)->flags &= ~ifDithered;
else (*theImage)->flags |= ifDithered;
}
}
/*
* DrawImage(theImage)
*
* Purpose: Draws the given image in its window
* Inputs: theImage = the image record to be drawn
* bitsFlags = the initial set of bitsFlags
* Returns: nothing
*
*/
OSErr DrawImage(ImageHandle theImage, short bitsFlags, NestedProgressPtr progProc)
{
Rect theRect = (*theImage)->wrect;
OSErr theErr = noErr;
RgnHandle tmpRgn;
PushPort();
MySetPort((CGrafPtr)(*theImage)->window);
/* If we're drawing onscreen, erase the region to be updated, draw a border around
* full screen images, reset the clipping rectangle, and limit the visRgn to the
* clipped area (since we assume we're doing this from within an update event */
if (bitsFlags & bfOnscreen) {
CopyRgn((*theImage)->window->visRgn, (*theImage)->update);
if (Full(theImage) && (tmpRgn = NewRgn())) {
ClipRect(&(*theImage)->window->portRect);
RectRgn(tmpRgn, &theRect);
DiffRgn((*theImage)->window->clipRgn, tmpRgn, (*theImage)->window->clipRgn);
if (NeedsWhiteBG(theImage) && !Full(theImage)) EraseRgn((*theImage)->window->visRgn);
else PaintRgn((*theImage)->window->visRgn);
DrawFullBorder(theImage);
DisposeRgn(tmpRgn);
DiffRgn((*theImage)->update, (*theImage)->window->clipRgn, (*theImage)->update);
DiffRgn((*theImage)->window->visRgn, (*theImage)->window->clipRgn,
(*theImage)->window->visRgn);
}
}
ClipRect(&theRect);
/* If we have anything to draw, then set up the global bitsFlags, point to our
* special drawing procedures, and do the appropriate method of drawing */
if (bitsFlags != bfOnscreen || !EmptyRgn((*theImage)->window->visRgn)) {
if ((*theImage)->gworld) LockPixels(GetGWorldPixMap((*theImage)->gworld));
if ((*theImage)->gworld && !(bitsFlags & bfForceFull))
theErr = DrawImageGWorld(theImage, bitsFlags, progProc);
else theErr = DrawImageFull(theImage, bitsFlags, progProc);
}
if ((theErr == noErr) && Full(theImage) && (bitsFlags & bfOnscreen) &&
ShowFilename(theImage)) DrawFileName(theImage);
if (bitsFlags & bfOnscreen) {
if (theErr == codecAbortErr && !(bitsFlags & bfQuantize)) {
if (NeedToRedraw(theImage)) (*theImage)->flags &= ~(ifNeedToRedraw + ifAborted);
else {
Pattern whitePat = { 0x82, 0x44, 0x28, 0x10, 0x28, 0x44, 0x82, 0x00 };
Pattern blackPat = { 0x7d, 0xbb, 0xd7, 0xef, 0xd7, 0xbb, 0x7d, 0xff };
RGBForeColor(&gBlack);
RGBBackColor(&gWhite);
SetClip((*theImage)->update);
FillRgn((*theImage)->update, NeedsWhiteBG(theImage) ? &whitePat : &blackPat);
(*theImage)->flags |= ifAborted;
}
} else (*theImage)->flags &= ~ifAborted;
}
if ((*theImage)->gworld) UnlockPixels(GetGWorldPixMap((*theImage)->gworld));
PopPort();
return theErr;
}
/*
* DrawImageGWorld(theImage, bitsFlags)
*
* Purpose: Handles drawing from the offscreen GWorld
* Inputs: theImage = the image record to be drawn
* bitsFlags = set of flags to be passed on to the custom copybits
* Returns: nothing
*
*/
OSErr DrawImageGWorld(ImageHandle theImage, short bitsFlags, NestedProgressPtr progProc)
{
PixMapHandle src = GetGWorldPixMap((*theImage)->gworld);
Boolean deepGWorld = ((*(*theImage)->dmon)->depth < PixMapDepth(src)) &&
!gThePrefs.origSize;
Rect srcRect = GWOrigSize(theImage) ? (*theImage)->crect : (*theImage)->gworld->portRect;
GWorldPtr oldGWorld = (*theImage)->gworld;
Rect theRect = (*theImage)->wrect;
JVDrawParamsHandle theParams;
OSErr theErr = noErr;
KeepSpinning();
if (theParams = NewDrawParams(&(*theImage)->gworld->portRect, (*theImage)->quality,
(bitsFlags & bfQuantize) != 0, progProc, (*theImage)->privateData)) {
SetUpDrawPort(theParams, kOnscreenPort, (CGrafPtr)(*theImage)->window, &srcRect,
&theRect, Dithered(theImage));
(*theParams)->newupdates = (*theImage)->update;
// if the GWorld is too deep, we need to delete it and make a new one
if (deepGWorld) {
(*theImage)->gworld = nil;
(*theImage)->flags &= ~ifGWOrigSize;
if (!MakeImageGWorld(theImage, false)) {
DisposeGWorld(oldGWorld);
DisposeHandle((Handle)theParams);
if (gThePrefs.useBitmaps == ubAlways && !gSlideShow)
return gIntError = errNoOffscreenMemory, memFullErr;
else return DrawImageFull(theImage, bitsFlags, progProc);
}
SetUpDrawPort(theParams, kOffscreenPort1, (*theImage)->gworld, &srcRect,
&(*theImage)->gworld->portRect, true);
LockPixels(GetGWorldPixMap((*theImage)->gworld));
}
// save the port, lock the handles, and initialize the drawing
theErr = PreflightDrawing(theParams);
// If everything's okay, CopyBits to the dummy port and clean up
if (theErr == noErr) {
SpinIndef();
CopyGWorldToGWorld(oldGWorld, (*theParams)->dummy, &srcRect, &srcRect,
srcCopy, nil);
theErr = PostflightDrawing(theParams);
}
KeepSpinning();
if (deepGWorld) {
UnlockPixels(GetGWorldPixMap((*theImage)->gworld));
DisposeGWorld(oldGWorld);
}
if (progProc) *progProc = (*theParams)->progress;
DisposeHandle((Handle)theParams);
} else theErr = MemError();
if (theErr == codecAbortErr && (bitsFlags & bfOffscreen)) KillGWorld(theImage);
return theErr;
}
/*
* DrawImageFull(theImage, bitsFlags)
*
* Purpose: Handles drawing from scratch
* Inputs: theImage = the image record to be drawn
* bitsFlags = set of flags to be passed on to the custom copybits
* progProc = pointer to the progress procedure, or nil to use the default
* Returns: nothing
*
*/
OSErr DrawImageFull(ImageHandle theImage, short bitsFlags, NestedProgressPtr progProc)
{
NestedProgress localProg = { { nil, 0L }, 0, 0L, 0x10000L, false };
Rect crect = (*theImage)->crect, theRect = (*theImage)->wrect;
JVDrawParamsHandle theParams;
OSErr theErr;
long dtime;
KeepSpinning();
// erase the area we're drawing into before doing anything else
if (bitsFlags & bfOnscreen) {
if (NeedsWhiteBG(theImage)) EraseRgn((*theImage)->window->visRgn);
else PaintRgn((*theImage)->window->visRgn);
}
// set up the progress procedure, according to flags & current situation & such
if (progProc) localProg = *progProc;
else {
if (bitsFlags & bfOnscreen) {
if (NoOnscreenProg(theImage))
localProg.prog.progressProc = gDummyProg.prog.progressProc;
else localProg.prog.progressProc = gDrawProgress;
} else if (bitsFlags & bfUseSlideProgress)
localProg.prog.progressProc = gSlideProgress;
else localProg.prog.progressProc = gGenericProgress;
}
// allocate and initialize the drawing parameters
if (theParams = NewDrawParams(&(*theImage)->grect, (*theImage)->quality,
(bitsFlags & bfQuantize) != 0, &localProg, (*theImage)->privateData)) {
if (localProg.prog.progressProc == gDrawProgress)
(*theParams)->drawProgressRect = (*theImage)->wrect;
// set up the onscreen port
if (bitsFlags & bfOnscreen) {
SetUpDrawPort(theParams, kOnscreenPort, (CGrafPtr)(*theImage)->window,
&crect, &theRect, Dithered(theImage));
(*theParams)->newupdates = (*theImage)->update;
}
// try to make a GWorld for this image; if successful, set the bitsFlags
// and ClipRgn appropriately; otherwise, if we've required bitmaps, close the
// image and return an error
if (!(*theImage)->gworld) {
if (MakeImageGWorld(theImage, bitsFlags & bfQuantize)) {
LockPixels(GetGWorldPixMap((*theImage)->gworld));
bitsFlags |= bfOffscreen;
} else if (gThePrefs.useBitmaps == ubAlways && !gSlideShow &&
((*theImage)->compression || !gThePrefs.noUncomp))
return gIntError = errNoOffscreenMemory, memFullErr;
}
// set up the offscreen port
if ((bitsFlags & bfOffscreen) && (*theImage)->gworld)
SetUpDrawPort(theParams, kOffscreenPort1, (*theImage)->gworld,
GWOrigSize(theImage) ? &(*theImage)->gworld->portRect : &crect,
&(*theImage)->gworld->portRect, !GWOrigSize(theImage));
// spin the cursor, start the timer, and preflight our drawing
KeepSpinning();
StartTimer();
theErr = PreflightDrawing(theParams);
KeepSpinning();
PushPort();
if (theErr == noErr) {
// indicate globally that we are drawing, and actually call the function
gDrawing = theImage;
MySetPort((*theParams)->dummy);
CallICMProgressProc((*theParams)->progress.prog.progressProc,
codecProgressOpen, 0L,
(*theParams)->progress.prog.progressRefCon);
theErr = (*theImage)->format->draw((*theImage)->data, theParams);
CallICMProgressProc((*theParams)->progress.prog.progressProc,
codecProgressClose, 0L,
(*theParams)->progress.prog.progressRefCon);
// reset everything now and stop the timer
gDrawing = nil;
KeepSpinning();
dtime = StopTimer();
// if we were drawing a full-size image, save the time from the timer
if (EqualSizeRect(&theRect, &(*(*theImage)->window->visRgn)->rgnBBox) &&
theErr == noErr && (bitsFlags & (bfOnscreen + bfOffscreen)))
(*theImage)->dtime = dtime;
// postflight the drawing, saving the error
if (theErr == noErr) theErr = PostflightDrawing(theParams);
else PostflightDrawing(theParams);
}
PopPort();
if (progProc) *progProc = (*theParams)->progress;
DisposeHandle((Handle)theParams);
} else theErr = memFullErr;
if (theErr == memFullErr) gIntError = errNoDrawMemory;
else if (theErr == codecAbortErr && (bitsFlags & bfOffscreen)) KillGWorld(theImage);
return theErr;
}
/*
* DrawFullBorder(theImage)
*
* Purpose: Draws the border for a full-screen window
* Inputs: theImage = the image record to be drawn
* Returns: nothing
*
*/
void DrawFullBorder(ImageHandle theImage)
{
Rect outerRect = (*theImage)->wrect;
InsetRect(&outerRect, -1, -1);
RGBForeColor(&gFGrey);
RGBBackColor(&gWhite);
FrameRect(&outerRect);
MoveTo(outerRect.left + 1, outerRect.bottom);
LineTo(outerRect.right, outerRect.bottom);
LineTo(outerRect.right, outerRect.top + 1);
RGBForeColor(&gBlack);
}
/*
* DrawFileName(theImage)
*
* Purpose: Draws a filename on a full-screen window for the slide show
* Inputs: theImage = the image record to be drawn
* Returns: nothing
*
*/
void DrawFileName(ImageHandle theImage)
{
short height, width, i, j;
FontInfo theInfo;
Str255 theText;
Point start;
if (!theImage || !(*theImage)->window) return;
PushPort();
MySetPort((CGrafPtr)(*theImage)->window);
ClipRect(&(*theImage)->window->portRect);
CalcVis((WindowPeek)(*theImage)->window);
// Do this to get the real visRgn of the window
TextFont(systemFont);
TextFace(normal);
TextSize(12);
TextMode(srcOr);
GetFontInfo(&theInfo);
BlockMove((*theImage)->file.name, theText, *(*theImage)->file.name + 1);
height = theInfo.ascent + theInfo.descent + theInfo.leading;
width = StringWidth(theText);
/* If there's enough room, simply draw the file name below the image's rect */
if (((*theImage)->wrect.bottom + height) < (*(*theImage)->dmon)->rect.bottom) {
start.h = (*theImage)->wrect.right - width - theInfo.leading;
start.v = (*theImage)->wrect.bottom + height - theInfo.leading;
RGBForeColor(&gWhite);
MoveTo(start.h, start.v);
DrawString(theText);
/* Or we could draw it to the right of the image's rect */
} else if (((*theImage)->wrect.right + width + 4) < (*(*theImage)->dmon)->rect.right) {
start.h = (*theImage)->wrect.right + 4;
start.v = (*theImage)->wrect.bottom - theInfo.descent - theInfo.leading;
RGBForeColor(&gWhite);
MoveTo(start.h, start.v);
DrawString(theText);
/* If there's not enough room, we draw the filename on top of the lower-right corner */
} else {
start.h = (*theImage)->wrect.right - width - theInfo.leading;
start.v = (*theImage)->wrect.bottom - theInfo.descent - theInfo.leading;
RGBForeColor(&gBlack);
for (i = -1; i <= 1; i++)
for (j = -1; j <= 1; j++) {
MoveTo(start.h + i, start.v + j);
DrawString(theText);
}
RGBForeColor(&gWhite);
MoveTo(start.h, start.v);
DrawString(theText);
}
RGBForeColor(&gBlack);
PopPort();
}
/*
* MakeImageGWorld(theImage)
*
* Purpose: Attempts to create a new offscreen bitmap for the given image
* Inputs: theImage = the image record
* quantizing = flag: true if we're quantizing
* Returns: true if it worked; false otherwise
*
*/
Boolean MakeImageGWorld(ImageHandle theImage, Boolean quantizing)
{
short imageDepth = ((*theImage)->depth < 8) ? 8 : (*theImage)->depth;
short theDepth = (*(*theImage)->dmon)->depth;
Rect theRect = (*theImage)->wrect;
CTabHandle theColors = nil;
if (gThePrefs.noUncomp && !(*theImage)->compression) return false;
if (gThePrefs.useBitmaps == ubNever && (!gSlideShow || !(*gSlideOptions)->offscreen))
return false;
if (gThePrefs.origSize && (!Banded(theImage) || !Cropped(theImage)))
theRect = (*theImage)->grect;
if (theDepth > imageDepth || gThePrefs.origSize || quantizing) theDepth = imageDepth;
if (theDepth == 24) theDepth = 32;
else if (theDepth <= 8) theColors = GetGWorldColors(theImage, theDepth);
OffsetRect(&theRect, (*theImage)->grect.left - theRect.left,
(*theImage)->grect.top - theRect.top);
(*theImage)->gworld = nil;
if (IsMemAvailable(EstGWorldSize(&theRect, theDepth)))
(*theImage)->gworld = MyNewGWorld(&theRect, theDepth, theColors,
nil, NeedsWhiteBG(theImage), false);
if (!(*theImage)->gworld && gSlideShow && (*gSlideOptions)->offscreen && !HasVirtualMemory()) {
(*theImage)->gworld = MyNewGWorld(&theRect, theDepth, theColors,
nil, NeedsWhiteBG(theImage), true);
if ((*theImage)->gworld) (*theImage)->flags |= ifGWTempMem;
}
if ((*theImage)->gworld) {
(*theImage)->flags &= ~ifGWOrigSize;
if (EqualSizeRect(&theRect, &(*theImage)->grect) &&
theDepth >= (*theImage)->depth &&
(gThePrefs.origSize || theDepth >= 16)) (*theImage)->flags |= ifGWOrigSize;
if (theColors) DisposeHandle((Handle)theColors);
return true;
}
if (theColors) DisposeHandle((Handle)theColors);
return false;
}
/*
* KillGWorld(theImage)
*
* Purpose: Kills the given image's GWorld and checks for others who might share it
* Inputs: theImage = the image record whose GWorld we are about to kill
* Returns: nothing
*
*/
void KillGWorld(ImageHandle theImage)
{
GWorldPtr theGWorld, curPort = (GWorldPtr)qd.thePort;
Boolean foundOthers = false;
ImageHandle image;
if (theImage && (theGWorld = (*theImage)->gworld)) {
if (curPort == theGWorld) MySetPort(nil);
for (image = gImageRoot; image; image = (*image)->next)
if (image != theImage && (*image)->gworld == theGWorld) foundOthers = true;
if (!foundOthers) DisposeGWorld(theGWorld);
(*theImage)->gworld = nil;
}
}
/*
* GetGWorldColors(theImage, gwDepth)
*
* Purpose: Creates an appropriate color table for the given image's GWorld at the
* given depth
* Inputs: theImage = the image record
* gwDepth = the depth of the GWorld we'd like to create
* Returns: a CTabHandle containing the color table
*
*/
CTabHandle GetGWorldColors(ImageHandle theImage, short gwDepth)
{
short imDepth = (*theImage)->depth;
PaletteHandle thePalette = nil;
CTabHandle theColors = nil;
if (!gThePrefs.origSize) { // can only copy from screen if not original size
if ((theImage == FrontImage()) && !gInBackground && (*theImage)->window &&
WindowVisible((*theImage)->window) &&
(gwDepth == (*(*theImage)->dmon)->depth) && (gwDepth <= 8)) {
theColors = (*(*(*(*theImage)->dmon)->device)->gdPMap)->pmTable;
if (HandToHand((Handle *)&theColors) != noErr) theColors = nil;
return theColors;
}
}
if (gwDepth >= imDepth) {
if (imDepth <= 8) {
if (gwDepth <= 8) thePalette = (*theImage)->ipalette ? (*theImage)->ipalette :
(*theImage)->palette;
else thePalette = nil;
} else thePalette = nil;
} else {
if (imDepth <= 8) thePalette = (*theImage)->palette;
else {
if (gwDepth <= 8) thePalette = (*theImage)->palette;
else thePalette = nil;
}
}
if (thePalette && ((1L << gwDepth) == (*thePalette)->pmEntries)) {
if (theColors = (CTabHandle)NewHandle(4)) {
Palette2CTab(thePalette, theColors);
(*theColors)->ctSeed = GetCTSeed();
}
}
return theColors;
}
/*
* DoImageHelp(theWindow, globalPt)
*
* Purpose: Pops up help for an image window; needed because they can be called anything
* Inputs: theWindow = the window whose help we're getting
* globalPt = point, in global coordinates, where the mouse is
* Returns: nothing
*
*/
void DoImageHelp(WindowPtr theWindow, Point globalPt)
{
ImageHandle theImage = FindImage(theWindow);
HMMessageRecord theRecord;
Point tip = { 0, 0 };
Rect aRect;
if (HMIsBalloon() || gInBackground) return;
PushPort();
SetPort(theWindow);
if (HMExtractHelpMsg(kHMRectListResType, rImageWindow, 1, kHMEnabledItem, &theRecord) == noErr) {
tip = globalPt;
aRect = theWindow->portRect;
GlobalRect(&aRect, theWindow);
HMShowBalloon(&theRecord, tip, &aRect, nil, 0, 0, kHMRegularWindow);
}
PopPort();
}