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
32 KiB
C
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();
|
|
}
|