home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / POV-Ray 3.0.2 / src / MacSource / ImageWindow.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-01-28  |  47.6 KB  |  1,832 lines  |  [TEXT/CWIE]

  1. /*==============================================================================
  2. Project:    POV-Ray
  3.  
  4. Version:    3
  5.  
  6. File:    ImageWindow.c
  7.  
  8. Description:
  9.     This file contains the Macintosh Image window routines for POV-Ray.
  10. ------------------------------------------------------------------------------
  11. Authors:
  12.     Jim Nitchals, David Harr, Eduard [esp] Schwan
  13. ------------------------------------------------------------------------------
  14.     from Persistence of Vision(tm) Ray Tracer
  15.     Copyright 1996 Persistence of Vision Team
  16. ------------------------------------------------------------------------------
  17.     NOTICE: This source code file is provided so that users may experiment
  18.     with enhancements to POV-Ray and to port the software to platforms other 
  19.     than those supported by the POV-Ray Team.  There are strict rules under
  20.     which you are permitted to use this file.  The rules are in the file
  21.     named POVLEGAL.DOC which should be distributed with this file. If 
  22.     POVLEGAL.DOC is not available or for more info please contact the POV-Ray
  23.     Team Coordinator by leaving a message in CompuServe's Graphics Developer's
  24.     Forum.  The latest version of POV-Ray may be found there as well.
  25.  
  26.     This program is based on the popular DKB raytracer version 2.12.
  27.     DKBTrace was originally written by David K. Buck.
  28.     DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
  29. ------------------------------------------------------------------------------
  30. Change History:
  31.     930610    [esp]    Created
  32.     930610    [esp]    Added Size2Window code
  33.     930620    [esp]    Changed brighten/darken image slightly to fix saturation bug
  34.     930710    [esp]    Added WindowValid check
  35.     930903    [esp]    Major bug fixes to Virtual Image Buffer code
  36.     931001    [esp]    version 2.0 finished (Released on 10/4/93)
  37.     931119    [djh]    2.0.1 source conditionally compiles for PPC machines, keyword __powerc
  38.     940416    [PFS]    2.2.1 greatly reworked to clean up PPC support and provide CodeWarrior projects
  39.     960704    [esp]    3.0b7k redid VIB buffer to be a whole # of scanlines to fix a bug
  40.     960706    [esp]    Changed offscreen from old pixmaps to new GWorlds, now images can be bigger than 4Kx4K!
  41. ==============================================================================*/
  42.  
  43. /*==== our header ====*/
  44. #include "ImageWindow.h"
  45.  
  46.  
  47. /*==== POV Mac Library routines =====*/
  48. #include "UtilLib.h"
  49.  
  50.  
  51. /*==== Compress PICT routines ====*/
  52. #include "SaveCmpPict.h"
  53.  
  54. #include "AppPrefs.h"        // APGet_AddCustomIcons()
  55. #include "FilePrefs.h"
  56. #include "ProgressDialog.h"
  57.  
  58. #include <finder.h>            // FInfo
  59. #include <strings.h>        // c2pstr
  60.  
  61. /*==== defs ====*/
  62.  
  63. // temp virtual image file file
  64. #define    POVRAY_IMAGETEMP_FNAME    "POV-Ray.vib"
  65.  
  66. // Windows
  67. #define    kWindID_Image            1001    // image window resource ID
  68.  
  69. #define    PICTF_HEADER_SIZE        512        // old MacDraw PICTF header size in bytes
  70.  
  71. /* Pict file header structure (after the 512 byte header) */
  72. typedef struct
  73. {
  74.     short        picSize;        // low word of size
  75.     Rect        picFrame;        // picture bounds
  76. } PictFHeader, *PictFHeaderPtr;
  77.  
  78.  
  79. /*==== globals ====*/
  80.  
  81. WindowPtr        gImageWindowPtr = NULL;        // the image window
  82. Boolean            gImageWindIsValid = false;    // true if valid stuff in window
  83. Boolean            gUsingCustomPalette = false;    // does the user desire to use color q?
  84. int                gColorQuantMethod = -1;        // what kind of color quantization?
  85. Boolean            gTooBigForPICT = false;    // true if X size too big to save/display as PICT
  86. Boolean            gDoingVirtualFile = false;    // true if doing virtual image buffering
  87.  
  88.  
  89. /*==== globals (local) ====*/
  90.  
  91. /* image window stuff */
  92. static    GWorldPtr        gOffScreenGWPtr    = NULL;
  93.  
  94. static PaletteHandle    gCustomPalette = NULL;
  95. static PaletteHandle    gSystemPalette = NULL;
  96.  
  97. // used by Paint_to_Picture() and MyPutPicProc()
  98. static PicHandle    gMyPicHandle;
  99. static long            gMyPicSize;
  100.  
  101. static FILE            *gImagePictFile = NULL; // used to save image to PICT
  102. static Rect            gImageBounds,        // source (offscreen image) bounds
  103.                     gDisplayBounds;        // destination image bounds
  104. static long            gImageWidth,        // user-requested image size
  105.                     gImageHeight;
  106. static long            gLastYPos = 0;        // last vertical position during previous refresh
  107. static long            gCurrYPos;
  108. static long            gPixMapBuffSize;
  109. static long            gDitherTicks;        // # of ticks to wait between image refresh
  110. static long            gRefreshTick = 0;    // Time of last Quickdraw dither of graphic port
  111. static Boolean        gBusySavingImage = FALSE; // true while in the save code, prevent reentrancy
  112.  
  113. // Undo stuff
  114. // static Ptr            gImageUndoBuffer = NULL;
  115. // static Ptr            gImageUndoBuffer2 = NULL;
  116. // static Ptr            gImageRevertBuffer = NULL;
  117.  
  118. // virtual image buffer globals
  119. static FILE            *gVirtualImageFile = NULL;
  120. static long            gVIBPixHeight;
  121. static Boolean        gVIBIsDirty;
  122. static long            gVIBCurrSegmentIndex = -1;
  123. static long            gVIBMinY;
  124. static long            gVIBMaxY;
  125.  
  126.  
  127. static void CalcVIBSegmentIndexes(short y);
  128.  
  129.  
  130. // ==============================================
  131. void InitImageWindow(void)
  132. {
  133.     // allocate the image window (it is shown/hidden later)
  134.     gImageWindowPtr = GetNewCWindow(kWindID_Image, NULL, (WindowPtr)NULL);
  135. } // InitImageWindow
  136.  
  137.  
  138. // ==============================================
  139. void KillImageWindow(void)
  140. {
  141.     if (gImageWindowPtr)
  142.         DisposeWindow(gImageWindowPtr);
  143.     gImageWindowPtr = NULL;
  144. } // KillImageWindow
  145.  
  146.  
  147.  
  148. // ==============================================
  149. // Create system/custom palette variables for use by image window
  150. void SetupPalettes(void)
  151. {
  152.     int        k;
  153.  
  154.     // Get default System palette for non-custom mode, (IM VI, Pg. 20-17)
  155.     gSystemPalette = NewPalette(256, NULL, pmTolerant, 0x0000);
  156.     if (gSystemPalette)
  157.     {
  158.         CopyPalette(GetPalette((WindowPtr)-1), gSystemPalette, 0, 0, 256);
  159.         for (k=0; k<256; k++)
  160.             SetEntryUsage(gSystemPalette, k, pmTolerant, 0x0000);
  161.     }
  162.  
  163.     // allocate a palette for the Image window, fill to default system palette
  164.     gCustomPalette = NewPalette(256, NULL, pmTolerant, 0x0000); // exact match only
  165.     if (gCustomPalette)
  166.     {
  167.         CopyPalette(gSystemPalette, gCustomPalette, 0, 0, 256);
  168.         for (k=0; k<256; k++)
  169.             SetEntryUsage(gCustomPalette, k, pmTolerant, 0x0000);
  170.     }
  171. } // SetupPalettes
  172.  
  173. // ==============================================
  174. // Calculate # of bytes used by a particular size of offscreen grafport
  175. long CalcPixMapSize(long imageWidth, short imageHeight)
  176. {
  177.     // do rowbytes-like rounding calculation
  178.     return (((imageWidth+4L)>>2)<<2) * imageHeight * 4;
  179. }
  180.  
  181. // ==============================================
  182. // Build offscreen image buffer
  183. void SetupOffscreen(void)
  184. {
  185.     OSErr    anError    = noErr;
  186.     GWorldPtr    saveGWorld;
  187.     GDHandle    saveGD;
  188.  
  189.     // don't allow static buildup!
  190.     if (gOffScreenGWPtr)
  191.         KillOffscreen();
  192.  
  193.     GetGWorld(&saveGWorld, &saveGD);
  194.  
  195.     if (gDoingVirtualFile)
  196.         {
  197.         // set up VIB stuff
  198.         gVIBPixHeight        = 16;    // Just do 16 scanlines at a time
  199.         gCurrYPos            = 0;
  200.         gLastYPos            = -1;
  201.         gVIBIsDirty            = false;
  202.         CalcVIBSegmentIndexes(gCurrYPos);
  203.         gPixMapBuffSize        = CalcPixMapSize(gImageWidth, gVIBPixHeight);
  204.         SetRect(&gImageBounds, 0, 0, gImageWidth, gVIBPixHeight);
  205.         }
  206.     else
  207.         {
  208.         gPixMapBuffSize        = CalcPixMapSize(gImageWidth, gImageHeight);
  209.         SetRect(&gImageBounds, 0, 0, gImageWidth, gImageHeight);
  210.         }
  211.  
  212.     // create offscreen pixmap to write into
  213.     anError = NewGWorld(&gOffScreenGWPtr, 32, &gImageBounds, NULL, NULL, 0);
  214.  
  215.     if (!anError)
  216.         {
  217.         PixMapHandle    aPixMapH;
  218.         SetGWorld(gOffScreenGWPtr, NULL);
  219.         // Here's a really skanky thing to do to allow an alpha channel in PICTs
  220.         // Let's hope it works! [esp]
  221.         aPixMapH = GetGWorldPixMap(gOffScreenGWPtr);
  222.         if ((**gPrefs2Use_h).doAlphaChannel)
  223.             {
  224.             (**aPixMapH).cmpCount = 4; // from RGB to RGBA!
  225.             PortChanged(qd.thePort);    // tell QD to notice the cmpCount change (3.0.1)
  226.             }
  227.         // Clear it
  228.         EraseRect(&gImageBounds);
  229.         SetGWorld((CGrafPtr)saveGWorld, saveGD);
  230.         }
  231.     else
  232.         {
  233.         // fatal error
  234.         DisplayModalDialog(kdlog_GenericFatalErr, ok, 0,
  235.                     "Cannot allocate memory for offscreen image buffer",
  236.                     anError, ewcDoCentering, eMainDevice);
  237.         exit_handler();
  238.         }
  239. } // SetupOffscreen
  240.  
  241.  
  242. // ==============================================
  243. // Delete offscreen image buffer
  244. void KillOffscreen(void)
  245. {
  246.     if (gOffScreenGWPtr)
  247.         DisposeGWorld(gOffScreenGWPtr);
  248.     gOffScreenGWPtr = NULL;
  249. } // KillOffscreen
  250.  
  251.  
  252. // ==============================================
  253. // Return Pixmap within the gworld passed, and lock it down for use
  254. static PixMapHandle LockAndLoadPixMap(GWorldPtr aGWorldPtr)
  255. {
  256.     PixMapHandle    aPixMapH = NULL;
  257.     if (gOffScreenGWPtr)
  258.         {
  259.         aPixMapH = GetGWorldPixMap(aGWorldPtr);
  260.         if (aPixMapH)
  261.             LockPixels(aPixMapH);
  262.         }
  263.     return aPixMapH;
  264. }
  265.  
  266.  
  267. // ==============================================
  268. // Unlock the passed Pixmap
  269. static void UnlockPixMap(PixMapHandle aPixMapH)
  270. {
  271.     if (aPixMapH)
  272.         UnlockPixels(aPixMapH);
  273. }
  274.  
  275.  
  276. // ==============================================
  277. // Set the image window name
  278. static void SetImageWindowTitle(StringPtr theFileName)
  279. {
  280.     if (gImageWindowPtr)
  281.         SetWTitle(gImageWindowPtr, theFileName);
  282. } // SetImageWindowTitle
  283.  
  284.  
  285. // ==============================================
  286. // Close the image window
  287. void CloseImageWindow()
  288. {
  289.     if (gImageWindowPtr)
  290.         HideWindow(gImageWindowPtr);
  291.     (**gFilePrefs_h).imageMagFactor = viewmn_hidden;
  292. } // CloseImageWindow
  293.  
  294.  
  295. // ==============================================
  296. // Force the entire image window to be updated
  297. void InvalRect_ImageWindow(Boolean DoWholeWindow)
  298. {
  299.     int            magFactor;
  300.     Rect        myInvalRect;
  301.     RgnHandle    theClip;
  302.  
  303.     if (gImageWindowPtr && ((WindowPeek)gImageWindowPtr)->visible)
  304.     {
  305.         SetPort(gImageWindowPtr);
  306.  
  307.         theClip = NewRgn();
  308.         GetClip(theClip); // save old clip region
  309.         myInvalRect = gImageWindowPtr->portRect;
  310.         ClipRect(&myInvalRect); // change clip region
  311.  
  312.         // if doing partial window in a regular magnification mode...
  313.         if (!DoWholeWindow && ((**gPrefs2Use_h).imageMagFactor != viewmn_Size2Window))
  314.         {
  315.             // no magnification allowed in VIB mode!
  316.             if (gDoingVirtualFile)
  317.                 magFactor = 1;
  318.             else
  319.                 magFactor = (**gPrefs2Use_h).imageMagFactor-viewmn_normal+1; // 1,2,3,4
  320.             myInvalRect.top = magFactor * gLastYPos;
  321.             myInvalRect.bottom = magFactor * (gCurrYPos + 1);
  322.         }
  323.         InvalRect(&myInvalRect);
  324.  
  325.         SetClip(theClip); // restore old clip region
  326.         DisposeRgn(theClip);
  327.  
  328.     }
  329. } // InvalRect_ImageWindow
  330.  
  331.  
  332. // ==============================================
  333. // Display the image window grow box
  334. static void DrawImageGrowBox(void)
  335. {
  336.     int            looper;
  337.     Rect        box;
  338. //    PenState    pstate;
  339.  
  340.     // Save current port state
  341. //    SetPort(gImageWindowPtr);
  342. //    GetPenState(&pstate);
  343.  
  344.     // find window size
  345.     box = gImageWindowPtr->portRect;
  346.  
  347.     // make boxes in lower left corner
  348.     box.top = box.bottom-7;
  349.     box.left = box.right-7;
  350.     OffsetRect(&box, -1, -1);
  351.  
  352.     // do two overlapping boxes
  353.     for (looper=0; looper<2; looper++)
  354.     {
  355.         // draw white box
  356.         ForeColor(whiteColor);
  357.         FrameRect(&box);
  358.  
  359.         // draw black box
  360.         OffsetRect(&box, -1, -1);
  361.         ForeColor(blackColor);
  362.         FrameRect(&box);
  363.  
  364.         // move up & draw 2nd smaller box
  365.         OffsetRect(&box, -2, -2);
  366.         box.right    -= 1;
  367.         box.bottom    -= 1;
  368.     }
  369.  
  370.     // restore world
  371. //    SetPenState(&pstate);
  372. } // DrawImageGrowBox
  373.  
  374.  
  375. // ==============================================
  376. // Display the image to the window (assumes port set to window)
  377. static void DrawImageWindow(Boolean DoWholeWindow)
  378. {
  379.     static short counter = 0;
  380.     RGBColor    myRGBWhite = {-1,-1,-1};
  381.     RGBColor    myRGBBlack = {0,0,0};
  382.  
  383.     ShowWatchCursor(); // could take a little while..
  384.  
  385.     if ((**gPrefs2Use_h).imageMagFactor != viewmn_hidden)
  386.     { // it is visible
  387.  
  388.         // set these to defaults so copybits behaves predictably
  389.         RGBBackColor(&myRGBWhite);
  390.         RGBForeColor(&myRGBBlack);
  391.  
  392.         if (DoWholeWindow)
  393.             {
  394.             // clear whole window
  395.             EraseRect(&gImageWindowPtr->portRect);
  396.             }
  397.  
  398.         // something to display?
  399.         if (gOffScreenGWPtr)
  400.         {
  401.             short             myMode;
  402.             PixMapHandle    aPixMapH = NULL;
  403.  
  404. //            if (gHasPictUtils)
  405. //                SetCustomPalette(false);
  406.  
  407.             if ((**gPrefs2Use_h).doDither)
  408.                 myMode = ditherCopy;
  409.             else
  410.                 myMode = srcCopy;
  411.  
  412.             if (gDoingVirtualFile)
  413.             {    // VIB - display each segment to the window
  414.                 int    k;
  415.                 Rect        vibRect,windRect;
  416.                 // set up source (VIB) rect... use window width for copybits width
  417.                 vibRect            = gImageWindowPtr->portRect;
  418.                 vibRect.top        = 0;
  419.                 vibRect.bottom    = gVIBPixHeight;
  420.                 // set up destination rect sides
  421.                 windRect        = gImageWindowPtr->portRect;
  422.                 for (k=0; k < gImageWindowPtr->portRect.bottom; k+=gVIBPixHeight)
  423.                     {
  424.                     swap_virtual_segment (k);
  425.                     // set up destination rect top/bottom
  426.                     windRect.top = k;
  427.                     windRect.bottom = k + gVIBPixHeight;
  428.                     // Get a handle to the offscreen pixmap area
  429.                     aPixMapH = LockAndLoadPixMap(gOffScreenGWPtr);
  430.                     CopyBits((BitMap*)*aPixMapH, &gImageWindowPtr->portBits,
  431.                             &vibRect, &windRect,
  432.                             myMode, NULL);
  433.                     // all done with the offscreen pixmap area
  434.                     UnlockPixMap(aPixMapH);
  435.                     }
  436.                 }
  437.             else
  438.                 {    // blast whole thing to the window
  439.                 // Get a handle to the offscreen pixmap area
  440.                 aPixMapH = LockAndLoadPixMap(gOffScreenGWPtr);
  441.                 // display it
  442.                 CopyBits((BitMap*)*aPixMapH, &gImageWindowPtr->portBits,
  443.                         &gImageBounds, &gDisplayBounds,
  444.                         myMode, NULL);
  445.                 // all done with the offscreen pixmap area
  446.                 UnlockPixMap(aPixMapH);
  447.                 }
  448.  
  449.             // Draw our custom size box if in Size-2-Window mode
  450.             if ((**gPrefs2Use_h).imageMagFactor == viewmn_Size2Window)
  451.                 DrawImageGrowBox();
  452.  
  453.  
  454.         } // offscreen exists
  455.  
  456.      } // it is visible
  457.  
  458.     ShowArrowCursor();
  459.  
  460. } // DrawImageWindow
  461.  
  462.  
  463. // ==============================================
  464. void DoResizeImageWindow(WindowPtr w, short h, short v)
  465. {
  466.     Rect     r;
  467.     
  468.     SetPort(w);
  469.  
  470.     SizeWindow(w, h, v, false);
  471.  
  472.     GetGlobalWindowRect(w, &r);
  473.     (**gFilePrefs_h).imageWind_pos = r;
  474.     if ((**gPrefs2Use_h).imageMagFactor == viewmn_Size2Window)
  475.         gDisplayBounds = w->portRect;
  476.  
  477. } // DoResizeImageWindow
  478.  
  479.  
  480. // ==============================================
  481. void DoGrowImageWindow(WindowPtr w, Point p)
  482. {
  483.     GrafPtr        savePort;
  484.     long        theResult;
  485.     Rect        r;
  486.     
  487.     GetPort(&savePort);
  488.     SetPort(w);
  489.  
  490.     GetMaxGrowRect(w, &r);    
  491.     theResult = GrowWindow(w, p, &r);
  492.     if (theResult != 0)
  493.     {
  494.         DoResizeImageWindow(w, LoWord(theResult), HiWord(theResult));
  495.         InvalRect_ImageWindow(true);
  496.     }
  497.  
  498.     SetPort(savePort);
  499. } // DoGrowImageWindow
  500.  
  501.  
  502. // ==============================================
  503. void UpdateImageWindow(void)
  504. {
  505.     if (gImageWindowPtr)
  506.     {
  507.         SetPort(gImageWindowPtr);
  508.         BeginUpdate(gImageWindowPtr);
  509.         DrawImageWindow(false);
  510.         EndUpdate(gImageWindowPtr);
  511.     }
  512. } // UpdateImageWindow
  513.  
  514.  
  515. // ==============================================
  516. void SetImageWindowMag(short magMenuItem)
  517. {
  518.     int            magFactor;
  519.     Rect        r,screenRect;
  520.     GDHandle    theGD;
  521.  
  522.     if (magMenuItem == viewmn_hidden)
  523.         HideWindow(gImageWindowPtr);
  524.     else
  525.     {
  526.         if (magMenuItem == viewmn_Size2Window)
  527.         { // set image buffer to 1xsize, leave window size alone
  528.             gDisplayBounds = gImageWindowPtr->portRect;
  529.         }
  530.         else
  531.         { // set image buffer rect to N*Size, resize window
  532.             // no magnification allowed in VIB mode!
  533.             if (gDoingVirtualFile)
  534.                 magFactor = 1; // only 1x
  535.             else
  536.                 magFactor = magMenuItem-viewmn_normal+1; // 1,2,3,4
  537.             // get new zero-based rect
  538.             SetRect(&gDisplayBounds, 0, 0,
  539.                         gImageWidth * magFactor,
  540.                         gImageHeight * magFactor);
  541.             // convert to potential global window rect
  542.             GetGlobalWindowRect(gImageWindowPtr, &r);
  543.             r.right = r.left + gDisplayBounds.right;
  544.             r.bottom = r.top + gDisplayBounds.bottom;
  545.             // move it to appropriate screen if needed
  546.             ForceRectOnScreen(&r);
  547. // [esp] hmm, shouldn't this next useful chunk of code be moved into ScreenUtils.c?
  548.             // clip any overhang off bottom/right
  549.             theGD = GetClosestGDevice(&r);
  550.             screenRect = (**theGD).gdRect;
  551.             if (r.right > screenRect.right)
  552.                 r.right = screenRect.right;
  553.             if (r.bottom > screenRect.bottom)
  554.                 r.bottom = screenRect.bottom;
  555.             MoveWindow(gImageWindowPtr, r.left, r.top, false);
  556.             DoResizeImageWindow(gImageWindowPtr,
  557.                         r.right-r.left,
  558.                         r.bottom-r.top);
  559. // [esp] need code here to handle scroll bars... oh yah, we need scroll bars first!
  560.         }
  561.  
  562.         // if window is valid, show it now.  This check lets the user
  563.         // set the window size even when there's no valid window content,
  564.         // and doesn't actually show the window until it is valid.
  565.         if (gImageWindIsValid)
  566.         {
  567.             ShowWindow(gImageWindowPtr);
  568.             SelectWindow(gImageWindowPtr);
  569.             InvalRect_ImageWindow(true);
  570.         }
  571.     } // else visible
  572. } // SetImageWindowMag
  573.  
  574. // ==============================================
  575. // ==============================================
  576. #ifdef PHASE_OUT
  577. // These were retouching tools put into 1.0 and 2.0 by Jim Nitchals for small images,
  578. // but it is dangerous when we switch to VIBs (and GWorlds after 3.0), so they're
  579. // being phased out... but the source code remains in 3.0 for the curious.
  580.  
  581.  
  582. // ==============================================
  583. // Create undo buffers for Image window changes
  584. void make_undo(void)
  585. {
  586.     if (gImageUndoBuffer == 0)
  587.     {
  588.         gImageUndoBuffer = NewPtr (gPixMapBuffSize);    /* room for image */
  589.         gImageUndoBuffer2 = NewPtr (gPixMapBuffSize);    /* room for image */
  590.         gImageRevertBuffer = NewPtr (gPixMapBuffSize);
  591.         if ( (gImageRevertBuffer) && (gImagePixmapPtr))
  592.             memcpy(gImageRevertBuffer, gImagePixmapPtr, gPixMapBuffSize);
  593.     }
  594.  
  595.     /* if any of the memory allocations failed, or available mem is low, */
  596.     /* fail the whole undo system */
  597.     if ( (gImageUndoBuffer == NULL) || (gImageUndoBuffer2 == NULL) || (gImageRevertBuffer == NULL)
  598.     || (FreeMem() < 50000L) )
  599.     {
  600.         if (gImageUndoBuffer)
  601.             DisposePtr (gImageUndoBuffer);
  602.         if (gImageUndoBuffer2)
  603.             DisposePtr (gImageUndoBuffer2);
  604.         if (gImageRevertBuffer)
  605.             DisposePtr (gImageRevertBuffer);
  606.         gImageUndoBuffer = NULL;
  607.         gImageUndoBuffer2 = NULL;
  608.         gImageRevertBuffer = NULL;
  609.     }
  610.  
  611.     if ((gImageUndoBuffer) && (gImagePixmapPtr))
  612.     {
  613.         memcpy(gImageUndoBuffer, gImagePixmapPtr, gPixMapBuffSize);
  614.         gCanUndo = TRUE;
  615.     }
  616. } // make_undo
  617.  
  618.  
  619. // ==============================================
  620. // Actually do the undo operation on the image window
  621. void undo_image(void)
  622. {
  623.     if ((gImageUndoBuffer) && (gCanUndo) && (gImagePixmapPtr))
  624.     {
  625.         memcpy(gImageUndoBuffer2, gImagePixmapPtr, gPixMapBuffSize);
  626.         memcpy(gImagePixmapPtr, gImageUndoBuffer, gPixMapBuffSize);
  627.         memcpy(gImageUndoBuffer, gImageUndoBuffer2, gPixMapBuffSize);
  628.         InvalRect_ImageWindow(true);
  629.     }
  630. } // undo_image
  631.  
  632.  
  633. // ==============================================
  634. // restore the original image to the image window
  635. void revert_image(void)
  636. {
  637.     if ((gImageRevertBuffer) && (gImagePixmapPtr))
  638.     {
  639.         memcpy(gImageUndoBuffer, gImagePixmapPtr, gPixMapBuffSize);
  640.         memcpy(gImagePixmapPtr, gImageRevertBuffer, gPixMapBuffSize);
  641.         gCanUndo = TRUE;
  642.         InvalRect_ImageWindow(true);
  643.     }
  644. } // revert_image
  645.  
  646.  
  647.  
  648. // ==============================================
  649. // return the value "v", insuring it is between 0 and 255
  650. static short ClipToByteRange(short v)
  651. {
  652.     if ( v < 0)
  653.         return (0);
  654.     if (v > 255)
  655.         return (255);
  656.     return (v);
  657. } // ClipToByteRange
  658.  
  659.  
  660. // ==============================================
  661. // Darken every pixel value in image window by 7/8ths original
  662. void darken_image(void)
  663. {
  664.     unsigned char *pptr;
  665.     short        v;
  666.     long        i,j;
  667.  
  668.     make_undo();
  669.     if (gImagePixmapPtr == 0) return;
  670.  
  671.     for (i=0; i < gImageHeight; i++)
  672.     {
  673.         for (j=0; j < gImageWidth; j++)
  674.         {
  675.             pptr = (unsigned char *) 
  676.                     (gImagePixmapPtr + 4L * ((gImageWidth * i) + j));
  677.             *pptr = 0;
  678.             pptr++;
  679.             v = (*pptr * 7 / 8)-1;
  680.             *pptr = ClipToByteRange (v);
  681.             pptr++;
  682.             v = (*pptr * 7 / 8)-1;
  683.             *pptr = ClipToByteRange (v);
  684.             pptr++;
  685.             v = (*pptr * 7 / 8)-1;
  686.             *pptr = ClipToByteRange (v);
  687.             pptr++;
  688.         }
  689.     }
  690.     InvalRect_ImageWindow(true);
  691.  
  692. } // darken_image
  693.  
  694.  
  695. // ==============================================
  696. // Lighten every pixel value in image window by 8/7ths original
  697. void lighten_image(void)
  698. {
  699.     unsigned char *pptr;
  700.     long i,j;
  701.     short v;
  702.  
  703.     make_undo();
  704.     if (gImagePixmapPtr == 0) return;
  705.  
  706.     for (i=0; i < gImageHeight; i++)
  707.     {
  708.         for (j=0; j < gImageWidth; j++)
  709.         {
  710.             pptr = (unsigned char *) 
  711.                     (gImagePixmapPtr + 4L * ((gImageWidth * i) + j));
  712.             *pptr = 0;
  713.             pptr++;
  714.             v = (*pptr * 8 / 7)+1;
  715.             *pptr = ClipToByteRange (v);
  716.             pptr++;
  717.             v = (*pptr * 8 / 7)+1;
  718.             *pptr = ClipToByteRange (v);
  719.             pptr++;
  720.             v = (*pptr * 8 / 7)+1;
  721.             *pptr = ClipToByteRange (v);
  722.             pptr++;
  723.         }
  724.     }
  725.     InvalRect_ImageWindow(true);
  726.  
  727. } // lighten_image
  728.  
  729.  
  730. // ==============================================
  731. // Invert every pixel value in image window
  732. void invert_image(void)
  733. {
  734.  
  735.     unsigned char *pptr;
  736.     long i,j;
  737.     
  738.     make_undo();
  739.     if (gImagePixmapPtr == 0) return;
  740.  
  741.     for (i=0; i < gImageHeight; i++)
  742.     {
  743.         for (j=0; j < gImageWidth; j++)
  744.         {
  745.             pptr = (unsigned char *) 
  746.                     (gImagePixmapPtr + 4L * ((gImageWidth * i) + j));
  747.             *pptr = 0;
  748.             pptr++;
  749.             *pptr = 255 - *pptr;
  750.             pptr++;
  751.             *pptr = 255 - *pptr;
  752.             pptr++;
  753.             *pptr = 255 - *pptr;
  754.             pptr++;
  755.         }
  756.     }
  757.     InvalRect_ImageWindow(true);
  758.  
  759. } // invert_image
  760.  
  761.  
  762. // ==============================================
  763. // Reduce the contrast of every pixel value in image window a bit
  764. void reduce_contrast(void)
  765. {
  766.     unsigned char *pptr;
  767.     long i,j;
  768.     short v;
  769.     
  770.     make_undo();
  771.     if (gImagePixmapPtr == 0) return;
  772.  
  773.     for (i=0; i < gImageHeight; i++)
  774.     {
  775.         for (j=0; j < gImageWidth; j++)
  776.         {
  777.             pptr = (unsigned char *) 
  778.                     (gImagePixmapPtr + 4L * ((gImageWidth * i) + j));
  779.             *pptr++ = 0;
  780.  
  781.             v = *pptr;
  782.             v = v - ((v - 128) >> 2);
  783.             *pptr++ = v;
  784.  
  785.             v = *pptr;
  786.             v = v - ((v - 128) >> 2);
  787.             *pptr++ = v;
  788.  
  789.             v = *pptr;
  790.             v = v - ((v - 128) >> 2);
  791.             *pptr++ = v;
  792.         }
  793.     }
  794.     InvalRect_ImageWindow(true);
  795.  
  796. } // reduce_contrast
  797.  
  798.  
  799. // ==============================================
  800. // Increase the contrast of every pixel value in image window a bit
  801. void increase_contrast(void)
  802. {
  803.     unsigned char *pptr;
  804.     long i,j;
  805.     short v;
  806.  
  807.     make_undo();
  808.     if (gImagePixmapPtr == 0) return;
  809.  
  810.     for (i=0; i < gImageHeight; i++)
  811.     {
  812.         for (j=0; j < gImageWidth; j++)
  813.         {
  814.             pptr = (unsigned char *) 
  815.                     (gImagePixmapPtr + 4L * ((gImageWidth * i) + j));
  816.             *pptr++ = 0;
  817.  
  818.             v = *pptr;
  819.             v = v + ((v - 128) >> 2);
  820.             *pptr++ = ClipToByteRange(v);
  821.  
  822.             v = *pptr;
  823.             v = v + ((v - 128) >> 2);
  824.             *pptr++ = ClipToByteRange(v);
  825.  
  826.             v = *pptr;
  827.             v = v + ((v - 128) >> 2);
  828.             *pptr++ = ClipToByteRange(v);
  829.         }
  830.     }
  831.     InvalRect_ImageWindow(true);
  832.  
  833. } // increase_contrast
  834.  
  835.  
  836. // ==============================================
  837. // draw a 1 pixel black border around the edge of the image
  838. void draw_border(void)
  839. {
  840.     unsigned char *pptr;
  841.     long i;
  842.     
  843.     make_undo();
  844.     if (gImagePixmapPtr == 0) return;
  845.  
  846.     for (i=0; i < gImageHeight; i++)
  847.     {
  848. /* left border edge */
  849.             pptr = (unsigned char *) 
  850.                     (gImagePixmapPtr + (gImageWidth * 4 * i) );
  851.             *pptr++ = 0;
  852.             *pptr++ = 0;
  853.             *pptr++ = 0;
  854.             *pptr++ = 0;
  855.  
  856. /* right border edge */
  857.             pptr = (unsigned char *) 
  858.                     (gImagePixmapPtr + (gImageWidth * 4 * i) + ((gImageWidth-1) * 4) );
  859.             *pptr++ = 0;
  860.             *pptr++ = 0;
  861.             *pptr++ = 0;
  862.             *pptr++ = 0;
  863.     }
  864.     for (i=0; i < gImageWidth; i++)
  865.     {
  866. /* top border edge */
  867.             pptr = (unsigned char *) 
  868.                     (gImagePixmapPtr + (4 * i));
  869.             *pptr++ = 0;
  870.             *pptr++ = 0;
  871.             *pptr++ = 0;
  872.             *pptr++ = 0;
  873.  
  874. /* bottom border edge */
  875.             pptr = (unsigned char *) 
  876.                     ( gImagePixmapPtr + 4L*gImageWidth*(gImageHeight-1) + 4*i );
  877.             *pptr++ = 0;
  878.             *pptr++ = 0;
  879.             *pptr++ = 0;
  880.             *pptr++ = 0;
  881.  
  882.     }
  883.     InvalRect_ImageWindow(true);
  884.  
  885. } // draw_border
  886.  
  887. #endif // PHASE_OUT
  888.  
  889.  
  890. // ==============================================
  891. // Create and open the virtual image buffer file for large images
  892. void open_virtual(void)
  893. {
  894.     int err;
  895.  
  896.     delete_virtual();
  897.  
  898.     if (!gDoingVirtualFile)
  899.     {
  900.         gVirtualImageFile = fopen(POVRAY_IMAGETEMP_FNAME,"wb+");
  901.         err = ferror(gVirtualImageFile);
  902.     }
  903.  
  904.     if (!gVirtualImageFile)
  905.         {
  906.         printf("## Error #%d opening file '%s'!\n", err, POVRAY_IMAGETEMP_FNAME);
  907.         dispose_virtual();
  908.         // no display memory
  909.         DisplayModalDialog(133, ok, 0, NULL, 0, ewcDoCentering, eSameAsPassedWindow);
  910.         }
  911.     else
  912.         {
  913.         gDoingVirtualFile    = true;
  914.         }
  915. } // open_virtual
  916.  
  917.  
  918. // ==============================================
  919. // Close and delete the virtual image buffer file
  920. void delete_virtual(void)
  921. {
  922.     int k;
  923.     if (gDoingVirtualFile)
  924.     {
  925.         fclose (gVirtualImageFile);
  926.         gVirtualImageFile = NULL;
  927.     }
  928.     k = remove(POVRAY_IMAGETEMP_FNAME);
  929.     gDoingVirtualFile = false;
  930. } // delete_virtual
  931.  
  932.  
  933. // ==============================================
  934. // Dispose of virtual image buffer memory, and close/delete the file
  935. void dispose_virtual(void)
  936. {
  937.     delete_virtual();
  938.     gVIBMinY = 32767;
  939. } // dispose_virtual
  940.  
  941.  
  942. // ==============================================
  943. // Calculate VIB indexes from current Y position passed in
  944. static void CalcVIBSegmentIndexes(short y)
  945. {
  946.     gVIBCurrSegmentIndex = (int) (y / gVIBPixHeight);
  947.     gVIBMinY = gVIBCurrSegmentIndex * gVIBPixHeight;
  948.     gVIBMaxY = ((gVIBCurrSegmentIndex+1L)*gVIBPixHeight) - 1L;
  949. }
  950.  
  951.  
  952. // ==============================================
  953. // write out the current virtual image buffer to the file
  954. void write_virtual_segment (void)
  955. {
  956.     int                err;
  957.     PixMapHandle    aPixMapH = NULL;
  958.     Ptr                aPixPtr;
  959.  
  960.     if (gDoingVirtualFile)
  961.     {
  962.         // Get a handle to the offscreen pixmap struct
  963.         aPixMapH = LockAndLoadPixMap(gOffScreenGWPtr);
  964.         // now get a pointer to the pixmap data
  965.         aPixPtr = GetPixBaseAddr(aPixMapH);
  966.  
  967.         fseek(gVirtualImageFile, (gVIBCurrSegmentIndex * gPixMapBuffSize), SEEK_SET);
  968.         if ( fwrite(aPixPtr, gPixMapBuffSize, 1, gVirtualImageFile) != 1)
  969.         {    // error
  970.             err = ferror(gVirtualImageFile);
  971.             if (err)
  972.                 printf("## Error #%d writing position #%ld in VIB file '%s'!\n",
  973.                         err, (gVIBCurrSegmentIndex * gPixMapBuffSize),
  974.                         POVRAY_IMAGETEMP_FNAME);
  975.             dispose_virtual();
  976.         }
  977.         gVIBIsDirty = false;
  978.         // all done with the offscreen pixmap area
  979.         UnlockPixMap(aPixMapH);
  980.     }
  981. }
  982.  
  983.  
  984. // ==============================================
  985. // swap in the "y"th virtual image buffer segment from disk that has "y" scanline
  986. // (write any old segment first.)
  987. void swap_virtual_segment (short y)
  988. {
  989.     int                i;
  990.     int                err;
  991.     PixMapHandle    aPixMapH = NULL;
  992.     Ptr                aPixPtr;
  993.         
  994.     // is Y outside currently loaded segment? need to load new one
  995.     if (gDoingVirtualFile && ((y < gVIBMinY) || (y > gVIBMaxY)) )
  996.     {
  997.         // is current segment dirty? save to disk first if so
  998.         if (gVIBIsDirty)
  999.         {
  1000.             write_virtual_segment();
  1001.         }
  1002.  
  1003.         // Now calculate and read needed segment into memory
  1004.         if (gDoingVirtualFile)
  1005.         {
  1006.             // calculate new current segment stuff
  1007.             CalcVIBSegmentIndexes(y);
  1008.  
  1009.             // Get a handle to the offscreen pixmap struct
  1010.             aPixMapH = LockAndLoadPixMap(gOffScreenGWPtr);
  1011.             // now get a pointer to the pixmap data
  1012.             aPixPtr = GetPixBaseAddr(aPixMapH);
  1013.  
  1014.             // read it in off disk
  1015.             fseek(gVirtualImageFile, (gVIBCurrSegmentIndex * gPixMapBuffSize), SEEK_SET);
  1016.             i = fread(aPixPtr, gPixMapBuffSize, 1, gVirtualImageFile);
  1017.             err = ferror(gVirtualImageFile);
  1018.             if (err)
  1019.                 printf("## Error #%d reading position #%ld in VIB file '%s'!\n",
  1020.                         err, (gVIBCurrSegmentIndex * gPixMapBuffSize),
  1021.                         POVRAY_IMAGETEMP_FNAME);
  1022.             // if freshly loaded from disk, it is not dirty yet
  1023.             gVIBIsDirty = false;
  1024.  
  1025.             // all done with the offscreen pixmap area
  1026.             UnlockPixMap(aPixMapH);
  1027.         }
  1028.  
  1029.         // error.. couldn't keep swap file open
  1030.         if (!gDoingVirtualFile)
  1031.             (void)DisplayModalDialog(139, ok, 0, NULL, 0, ewcDoCentering, eSameAsPassedWindow);
  1032.     }
  1033.  
  1034. } // swap_virtual_segment
  1035.  
  1036.  
  1037. // ==============================================
  1038. // Initialize the display, set up buffers (VIB if needed)
  1039. void POV_Mac_Display_Init(int width, int height)
  1040. {
  1041.     long        freeMemory;
  1042.     DialogPtr    progressDialogPtr = NULL;
  1043.  
  1044.     freeMemory = FreeMem();
  1045.     if ((**gPrefs2Use_h).progress >= eProgMac)
  1046.     {
  1047.         printf("-- [Memory]  Avail. Memory=%ldKBytes\n", freeMemory/1024L);
  1048.     }
  1049.  
  1050.     // This could take a little while..
  1051.     ShowWatchCursor();
  1052.  
  1053.     gImageWidth        = width;
  1054.     gImageHeight    = height;
  1055.     gPixMapBuffSize    = CalcPixMapSize(gImageWidth, gImageHeight);
  1056.     SetRect(&gImageBounds, 0, 0, width, height);
  1057.  
  1058.     // NOTE: Due to a limitation of QuickDraw/Pixmaps, a 24-bit color image cannot
  1059.     // be bigger than 2047 pixels wide (The pixmap's ROWBYTES field can go up to
  1060.     // 8191 bytes, and a 24-bit pixel uses 4 bytes, so 8191/4 = 2047 pixels.)
  1061.  
  1062.     // So, for really big images, we don't output PICTs, just Targa or PNG.
  1063.     // Note that when we rewrite this VIB code to do small squares (banding the
  1064.     // image vertically) this will no longer be a problem.
  1065.     gTooBigForPICT = (gImageWidth > 2047);
  1066.     if (gTooBigForPICT)
  1067.         (**gPrefs2Use_h).imageMagFactor = viewmn_hidden;
  1068.  
  1069.     // if we cannot do an image, or if it is small enough to all fit in RAM...
  1070.     if    (gTooBigForPICT || (freeMemory > gPixMapBuffSize+100L*1024L))
  1071.         {    // do the whole image in one offscreen buffer
  1072.         gDitherTicks = 20*60;    /* 20 seconds between re-dithers */
  1073.         }
  1074.     else
  1075.         { // do the image in segments (VIB mode)
  1076.         gDitherTicks = 2L*60L*60L;    /* 2 minutes between re-dithers */
  1077.  
  1078.         // modal dialog up, turn off menus...
  1079.         DisableMenus();
  1080.  
  1081.         // open the VIB file
  1082.         open_virtual();
  1083.  
  1084.         // set up progress dialog
  1085.         progressDialogPtr = GetNewProgressDialog(153, 3);
  1086.         if (progressDialogPtr)
  1087.             {
  1088.             PositionWindow(progressDialogPtr, ewcDoCentering, eSameAsPassedWindow, (WindowPtr)gp2wWindow);
  1089.             ShowWindow(progressDialogPtr);
  1090.             SelectWindow(progressDialogPtr);
  1091.             DrawDialog(progressDialogPtr);
  1092.             // make sure events get through so dialog is updated
  1093.             Cooperate(true);
  1094.             Cooperate(true);
  1095.             Cooperate(true);
  1096.             }
  1097.         }
  1098.  
  1099.     // Allocate the offscreen buffer
  1100.     if (!gTooBigForPICT)
  1101.         SetupOffscreen();
  1102.  
  1103.     // let user know some info about the offscreen buffer size
  1104.     if ((**gPrefs2Use_h).progress >= eProgMac)
  1105.         {
  1106.         if (gTooBigForPICT)
  1107.             {
  1108.             printf("!! [Image] WARNING!  This image is too big to display or save as PICT...\n");
  1109.             printf("!!         it can save as Targa or PNG only.\n");
  1110.             }
  1111.         else
  1112.             {
  1113.             // VIB info...
  1114.             if (gDoingVirtualFile)
  1115.                 printf("-- [VIB] VIBNumSegments=%ld, VIBSegmentSize=%ld Kbytes\n",
  1116.                         1L+(gImageHeight / gVIBPixHeight), gPixMapBuffSize/1024L);
  1117.             // Regular image info
  1118.             printf("-- [Image] ImageBufferPixWidth=%ld, ImageBufferPixHeight=%ld, ImageBufferSize=%ld Kbytes\n",
  1119.                 gImageWidth, gImageHeight, gPixMapBuffSize/1024L);
  1120.             }
  1121.         }
  1122.     
  1123.     if (gDoingVirtualFile)
  1124.         { // fill the VIB file (pre-swap it)
  1125.         if (progressDialogPtr)
  1126.             updateProgressDialog(progressDialogPtr, 0, gImageHeight-1, 1);
  1127.  
  1128.         DrawDialog(progressDialogPtr); // redraw just to be redundant again
  1129.  
  1130.         // Go through the loop forcing each empty VIB segment to be written to disk,
  1131.         // to pre-fill the file.
  1132.         {
  1133.         int y;
  1134.         for (y=0; (y<gImageHeight)&&gDoingVirtualFile; y+=gVIBPixHeight)
  1135.             {
  1136.             Cooperate(true);
  1137.             // Update the progress bar
  1138.             if (progressDialogPtr)
  1139.                 {
  1140.                 updateProgressDialog(progressDialogPtr, 0, gImageHeight-1, y);
  1141.                 }
  1142.             // force it to write each segment to disk to build the file
  1143.             CalcVIBSegmentIndexes(y);
  1144.             write_virtual_segment();
  1145.             }
  1146.         }
  1147.  
  1148.         // swap back in segment 0
  1149.         if (gDoingVirtualFile)
  1150.             {
  1151.             // force progress bar to go to the end
  1152.             if (progressDialogPtr)
  1153.                 updateProgressDialog(progressDialogPtr, 0, gImageHeight-1, gImageHeight);
  1154.             swap_virtual_segment(0);
  1155.             }
  1156.  
  1157.         // get rid of progress dialog
  1158.         if (progressDialogPtr)
  1159.             disposeProgressDialog(progressDialogPtr);
  1160.  
  1161.         if (!gDoingVirtualFile) // any errors turned off VIB mode?
  1162.             {
  1163.             // no disk space for swap file
  1164.             (void)DisplayModalDialog(139, ok, 0, NULL, 0, ewcDoCentering, eSameAsPassedWindow);
  1165.             dispose_virtual();
  1166.             }
  1167.  
  1168.         // all done being modal...
  1169.         EnableMenus(); // re-enable menus
  1170.  
  1171.         } // VIB mode
  1172.  
  1173.     if (!gTooBigForPICT)
  1174.         {
  1175.         // image window now has content
  1176.         // turn on now, so SetImageWindMag will show window
  1177.         gImageWindIsValid = true;
  1178.  
  1179.         // Set image window's title
  1180.         SetImageWindowTitle(gPictFname);
  1181.  
  1182.         if (gHasPictUtils)
  1183.             SetCustomPalette(false);
  1184.         }
  1185.  
  1186.     // set up next time to repaint
  1187.     gRefreshTick = MAC_TICKS;
  1188.  
  1189.     // size and show the window
  1190.     SetImageWindowMag((**gPrefs2Use_h).imageMagFactor);
  1191.  
  1192.     ShowArrowCursor(); // all done initializing..
  1193.  
  1194. } // POV_Mac_Display_Init
  1195.  
  1196.  
  1197. // ==============================================
  1198. // Plot a single pixel at (x,y)
  1199. void POV_Mac_Display_Plot(int x, int y, 
  1200.                      unsigned int r,
  1201.                      unsigned int g,
  1202.                      unsigned int b,
  1203.                      unsigned int a)
  1204. {
  1205.     RGBColor    aColor;
  1206.     Ptr            pixPtr;
  1207.     signed char    mmuMode;
  1208.     Rect        magniRect;
  1209.     long        pport_y;
  1210.     int            mr;
  1211.     int            magFactor;
  1212.     PixMapHandle aPixMapH = NULL;
  1213.  
  1214.     // out of range?
  1215.     if ((x>=gImageWidth) || (y>=gImageHeight))
  1216.          return;
  1217.  
  1218.     gCurrYPos = y;
  1219.  
  1220.     if (gOffScreenGWPtr)
  1221.     {
  1222.         if (gDoingVirtualFile)
  1223.         {
  1224.             // make sure we've loaded the right segment in
  1225.             swap_virtual_segment(y);
  1226.             pport_y = y - gVIBMinY;    // calculate Y offset within current virtual segment
  1227.             gVIBIsDirty = true; // we're about to add to the buffer
  1228.         }
  1229.         else
  1230.             pport_y = y;
  1231.  
  1232. #ifdef USE_QUICKDRAW    // the way your mother wants you to do it...
  1233.         SetPort(gOffScreenGWPtr);
  1234.         aColor.red = ((short)r)<<8;
  1235.         aColor.green = ((short) g)<<8;
  1236.         aColor.blue = ((short) b)<<8;
  1237.         if (magFactor == viewmn_normal)
  1238.             SetCPixel(x, y, &aColor); // slow, but safe
  1239. #else    // the way Chernobog wants you to do it...
  1240.         // Get a handle to the offscreen pixmap area
  1241.         aPixMapH = LockAndLoadPixMap(gOffScreenGWPtr);
  1242.  
  1243.         // Calculate a pointer into the Pixmap
  1244.         pixPtr = GetPixBaseAddr(aPixMapH);                    // start at base address
  1245.         pixPtr += pport_y*(((**aPixMapH).rowBytes)&0x3fff)    // + Y scanline offset down
  1246.                 + (x*4L);                                    // + X offset over (4 bytes per pixel)
  1247.  
  1248.         // slam the RGBA values into the pixmap at (x,y)
  1249.         mmuMode = true32b;
  1250.         SwapMMUMode(&mmuMode);
  1251.         *pixPtr    = a;    pixPtr++;
  1252.         *pixPtr    = r;    pixPtr++;
  1253.         *pixPtr    = g;    pixPtr++;
  1254.         *pixPtr    = b;
  1255.         SwapMMUMode(&mmuMode);
  1256.  
  1257.         // all done with the offscreen pixmap area
  1258.         UnlockPixMap(aPixMapH);
  1259. #endif
  1260.     }
  1261.     magFactor = (**gPrefs2Use_h).imageMagFactor;
  1262.     if (magFactor != viewmn_hidden)
  1263.     { // it is visible, display to screen
  1264.         // no magnification allowed in VIB mode!
  1265.         if (gDoingVirtualFile)
  1266.             magFactor = viewmn_normal;
  1267.         SetPort(gImageWindowPtr);
  1268.         aColor.red = ((short)r)<<8;
  1269.         aColor.green = ((short) g)<<8;
  1270.         aColor.blue = ((short) b)<<8;
  1271.         if (magFactor == viewmn_normal)
  1272.             SetCPixel(x, y, &aColor); // slow, but safe
  1273.         else
  1274.         { // some magnification at play here
  1275.             if (magFactor != viewmn_Size2Window)
  1276.             { // go to straight magnification N (don't display
  1277.                 magFactor            = magFactor-viewmn_normal+1; // 2,3,4
  1278.                 mr                    = gCurrYPos * magFactor;
  1279.                 magniRect.top        = mr;
  1280.                 magniRect.bottom    = mr + magFactor;
  1281.                 magniRect.left        = x * magFactor;
  1282.                 magniRect.right        = (x+1) * magFactor;
  1283.                 RGBForeColor(&aColor);
  1284.                 PaintRect(&magniRect);
  1285.             }
  1286.         }
  1287.  
  1288.         // time to update the image?
  1289.         if (MAC_TICKS > (gRefreshTick + gDitherTicks))
  1290.         {
  1291.             InvalRect_ImageWindow(false);
  1292.             Cooperate(true);
  1293.             gRefreshTick = MAC_TICKS;
  1294.         }
  1295.  
  1296.     }
  1297. } // POV_Mac_Display_Plot
  1298.  
  1299.  
  1300. // ==============================================
  1301. // Plot a rectangle of pixels at (x1,x2,y1,y2)
  1302. void POV_Mac_Display_Plot_Rect (int x1, int x2, int y1, int y2,
  1303.                             unsigned int r,
  1304.                             unsigned int g,
  1305.                             unsigned int b,
  1306.                             unsigned int a)
  1307. {
  1308.     RGBColor    aColor;
  1309.     Ptr            pixPtr;
  1310.     signed char    mmuMode;
  1311.     Rect        magniRect;
  1312.     int            magFactor;
  1313.     PixMapHandle aPixMapH = NULL;
  1314.  
  1315.     // out of range?
  1316.     if ((x1>=gImageWidth) || (y1>=gImageHeight))
  1317.          return;
  1318.  
  1319.     gCurrYPos = y1;
  1320.  
  1321.     magFactor = (**gPrefs2Use_h).imageMagFactor - viewmn_normal + 1; // 1,2,3,4;
  1322.     // no magnification allowed in VIB mode!
  1323.     if (gDoingVirtualFile)
  1324.         magFactor = viewmn_normal;
  1325.  
  1326.     // make a color record
  1327.     aColor.red = ((short)r)<<8;
  1328.     aColor.green = ((short) g)<<8;
  1329.     aColor.blue = ((short) b)<<8;
  1330.  
  1331.     // calculate the rect to paint
  1332.     x2++; // cheat, stretch out a pixel
  1333.     y2++;
  1334.     magniRect.top        = y1 * magFactor;
  1335.     magniRect.bottom    = y2 * magFactor;
  1336.     magniRect.left        = x1 * magFactor;
  1337.     magniRect.right        = x2 * magFactor;
  1338.  
  1339.     if (((**gPrefs2Use_h).imageMagFactor != viewmn_hidden)
  1340.     && ((**gPrefs2Use_h).imageMagFactor != viewmn_Size2Window))
  1341.     { // it is visible, display to screen
  1342.         SetPort(gImageWindowPtr);
  1343.         RGBForeColor(&aColor);
  1344.         PaintRect(&magniRect);
  1345.  
  1346.         // time to update the entire image?
  1347.         if (MAC_TICKS > (gRefreshTick + gDitherTicks))
  1348.         {
  1349.             InvalRect_ImageWindow(false);
  1350.             gRefreshTick = MAC_TICKS;
  1351.         }
  1352.     }
  1353.  
  1354.     // Don't write to offscreen in mosaic mode if doing VIB, this is broken and useless,
  1355.     // wait till final pass.  Who's doing giant images in mosaic mode anyway??? :-)
  1356.     if (gDoingVirtualFile)
  1357.          return;
  1358.  
  1359.     if (gOffScreenGWPtr)
  1360.     {
  1361. #ifdef NOT_WORKING
  1362. // original 3.0 code, doesn't really work on alpha-channelized gworld!
  1363.         SetRect(&magniRect,x1,y1,x2,y2);
  1364.         SetPort((GrafPtr)gOffScreenGWPtr);
  1365.         RGBForeColor(&aColor);
  1366.         PaintRect(&magniRect);
  1367.         SetPort(gImageWindowPtr); // exit with image window as curr port
  1368. #else
  1369. /*
  1370. // old 3.0 beta code, worked but slow
  1371.         int    x,y;
  1372.         for (x=x1;x<=x2;x++)
  1373.             for (y=y1;y<=y2;y++)
  1374.                 POV_Mac_Display_Plot(x,y,r,g,b,a);
  1375. */
  1376.         int    x,y,xm,ym;
  1377.         // Get a handle to the offscreen pixmap area
  1378.         aPixMapH = LockAndLoadPixMap(gOffScreenGWPtr);
  1379.         // loop down scanlines
  1380.         ym=(y2>=gImageHeight)?gImageHeight-1:y2;    // pin at edge
  1381.         for (y=y1;y<ym;y++)
  1382.             {
  1383.             // Calculate a pointer into the Pixmap
  1384.             pixPtr = GetPixBaseAddr(aPixMapH);                    // start at base address
  1385.             pixPtr += y*(((**aPixMapH).rowBytes)&0x3fff)        // + Y scanline offset down
  1386.                     + (x1*4L);                                    // + X offset over (4 bytes per pixel)
  1387.             mmuMode = true32b;
  1388.             SwapMMUMode(&mmuMode);
  1389.             // loop across pixels on this line
  1390.             xm=(x2>=gImageWidth)?gImageWidth-1:x2;    // pin at edge
  1391.             for (x=x1;x<xm;x++)
  1392.                 {
  1393.                 // slam the RGBA values into the pixmap at (x,y)
  1394.                 *pixPtr    = a;    pixPtr++;
  1395.                 *pixPtr    = r;    pixPtr++;
  1396.                 *pixPtr    = g;    pixPtr++;
  1397.                 *pixPtr    = b;    pixPtr++;
  1398.                 }
  1399.             SwapMMUMode(&mmuMode);
  1400.             }
  1401.  
  1402.         // all done with the offscreen pixmap area
  1403.         UnlockPixMap(aPixMapH);
  1404.  
  1405. #endif
  1406.     }
  1407.  
  1408. } // POV_Mac_Display_Plot_Rect
  1409.  
  1410.  
  1411.  
  1412. // ==============================================
  1413. // Do any cleanup needed when the display is complete
  1414. void POV_Mac_Display_Finished(void)
  1415. {
  1416.     // Reset last Y pos so that screen refreshes do the whole screen
  1417.     gLastYPos = -1;
  1418. } // POV_Mac_Display_Finished
  1419.  
  1420.  
  1421. // ==============================================
  1422. // Close any files used after display is complete
  1423. void POV_Mac_Display_Close(void)
  1424. {
  1425. } // POV_Mac_Display_Close
  1426.  
  1427.  
  1428. // ==============================================
  1429. // Find and assign the appropriate custom palette to the image window
  1430. void SetCustomPalette(Boolean doScreenUpdate)
  1431. {
  1432.     PictInfo        plot_info;
  1433.     short            k;
  1434.     OSErr            anError = noErr;
  1435.     PaletteHandle    thePalette = NULL;
  1436.  
  1437.     if (gImageWindowPtr)
  1438.         SetPort(gImageWindowPtr);
  1439.  
  1440.     if (gHasPictUtils && gUsingCustomPalette && gOffScreenGWPtr)
  1441.     {
  1442.         switch(gColorQuantMethod)
  1443.         {
  1444.             case systemMethod:
  1445.             case popularMethod:
  1446.             case medianMethod:
  1447.                 {
  1448.                 PixMapHandle aPixMapH = NULL;
  1449.                 // Get a handle to the offscreen pixmap area
  1450.                 aPixMapH = LockAndLoadPixMap(gOffScreenGWPtr);
  1451.                 anError = GetPixMapInfo(aPixMapH,
  1452.                                 &plot_info,
  1453.                                 returnPalette + suppressBlackAndWhite,
  1454.                                 256,
  1455.                                 gColorQuantMethod,
  1456.                                 0);
  1457.                 // all done with the offscreen pixmap area
  1458.                 UnlockPixMap(aPixMapH);
  1459.  
  1460.                 // copy the new palette entries into custom palette & use it
  1461.                 if (plot_info.thePalette && gCustomPalette)
  1462.                 {
  1463.                     CopyPalette(plot_info.thePalette, gCustomPalette, 0, 0, 256);
  1464.                     for (k=0; k<256; k++)
  1465.                         SetEntryUsage(gCustomPalette, k, pmTolerant, 0x0000);
  1466.                     thePalette = gCustomPalette;
  1467.                 }
  1468.                 // done with retrieved palette. toolbox created it, so we must destroy it!
  1469.                 if (plot_info.thePalette)
  1470.                     DisposePalette(plot_info.thePalette);
  1471.                 break;
  1472.                 }
  1473.             otherwise:
  1474.                 SysBeep(4);
  1475.                 break;
  1476.         }
  1477.     }
  1478.     else
  1479.     {
  1480.         // Revert to system palette
  1481.         thePalette = gSystemPalette;
  1482.     }
  1483.  
  1484.     if (gImageWindowPtr)
  1485.     {
  1486.         // reset all screens, this is a little severe though!
  1487.         if (doScreenUpdate)
  1488.             RestoreDeviceClut(NULL);
  1489.         // tell window to use the new palette
  1490.         if (thePalette)
  1491.             NSetPalette(gImageWindowPtr, thePalette, pmAllUpdates);
  1492.         ActivatePalette(gImageWindowPtr);
  1493.     }
  1494.  
  1495. } // SetCustomPalette
  1496.  
  1497.  
  1498.  
  1499.  
  1500. // ==============================================
  1501. // Write Picture bottleneck routine for paint_to_picture()
  1502. static pascal void MyPutPicProc(char *dataPtr, short byteCount)
  1503. {
  1504.     size_t    myByteCount;
  1505.  
  1506.     myByteCount = byteCount;
  1507.     gMyPicSize += byteCount;
  1508.     if (gImagePictFile)
  1509.     {
  1510.         if (fwrite( dataPtr, 1, myByteCount, gImagePictFile) != myByteCount)
  1511.         {
  1512.             fclose (gImagePictFile);
  1513.             gImagePictFile = NULL;
  1514.             SysBeep(3);
  1515.         }
  1516.     }
  1517.     // we have to keep the picture handle header updated for Quickdraw!
  1518.     if (gMyPicHandle)
  1519.         (**gMyPicHandle).picSize = gMyPicSize;
  1520. } // MyPutPicProc
  1521.  
  1522.  
  1523.  
  1524. // ==============================================
  1525. // Write the Pixmap out to either an open disk file, or the Clipboard
  1526. void paint_to_picture(short do_disk_buffer)
  1527. {
  1528.     short         myMode, i;
  1529.     OSErr        anError=noErr;
  1530.     Rect        my_bounds, myPicRect;    
  1531.     CGrafPort    myCGrafPort;
  1532.     GrafPtr        oldGrafPtr;
  1533.     PictFHeader    myPictFHeader;
  1534.     CQDProcsPtr    oldQDProcs;
  1535.     CQDProcs    myQDProcs;
  1536.     QDPutPicUPP    myDrawProcUPP;
  1537.     PixMapHandle aPixMapH = NULL;
  1538.  
  1539.     if (gOffScreenGWPtr)
  1540.     {
  1541.         ShowWatchCursor(); // could take a little while..
  1542.  
  1543.         GetPort(&oldGrafPtr);
  1544.         OpenCPort (&myCGrafPort);
  1545.         SetPort ((GrafPtr)&myCGrafPort);
  1546.  
  1547.         PortSize (gImageWidth, gImageHeight);
  1548.         myPicRect.top = 0;
  1549.         myPicRect.bottom = gImageHeight;
  1550.         myPicRect.left = 0;
  1551.         myPicRect.right = gImageWidth;
  1552.  
  1553.         if (do_disk_buffer)
  1554.         {
  1555.             // remember old bottlenecks for restoring later
  1556.             oldQDProcs = myCGrafPort.grafProcs;
  1557.             // Fill our custom rec with standard Color bottleneck procs
  1558.             SetStdCProcs ( &myQDProcs);
  1559.             // create ProcPtr of our custom proc
  1560.             myDrawProcUPP = NewQDPutPicProc(MyPutPicProc);
  1561.             // stick our new PutPic bottleneck proc in
  1562.             myQDProcs.putPicProc = myDrawProcUPP;
  1563.             // stick new methods back into QD Grafport
  1564.             myCGrafPort.grafProcs = &myQDProcs;
  1565.             // write the PICT header to the file
  1566.             gMyPicSize = sizeof(myPictFHeader);
  1567.             // set up header fields
  1568.             myPictFHeader.picSize = gMyPicSize; // incremented later in MyPutPicProc()..
  1569.             myPictFHeader.picFrame = myPicRect;
  1570.             // write header
  1571.             i = fwrite (&myPictFHeader, 1, sizeof(myPictFHeader), gImagePictFile);
  1572.             anError = ferror(gImagePictFile);
  1573.         }
  1574.  
  1575.         if (!anError)
  1576.         {
  1577.             /* insure gMyPicHandle null, 'cause MyPutPicProc will be called in OpenPicture! */
  1578.             gMyPicHandle = NULL;
  1579.             gMyPicHandle = OpenPicture (&myPicRect);
  1580.             anError = QDError();
  1581.         }
  1582.  
  1583.         if ((**gPrefs2Use_h).doDither)
  1584.             myMode = ditherCopy;
  1585.         else
  1586.             myMode = srcCopy;
  1587.  
  1588.         if (!anError)
  1589.         {
  1590.             RGBColor    myRGBWhite = {-1,-1,-1};
  1591.             RGBColor    myRGBBlack = {0,0,0};
  1592.     
  1593.             ClipRect(&myPicRect);
  1594.             // set these to defaults so copybits behaves predictably
  1595.             RGBBackColor(&myRGBWhite);
  1596.             RGBForeColor(&myRGBBlack);
  1597.  
  1598.             if (gDoingVirtualFile)
  1599.                 {
  1600.                 for (i=0; (i <= gImageHeight) && !anError; i+= gVIBPixHeight)
  1601.                     {
  1602.                     swap_virtual_segment (i);
  1603.                     // set left/right
  1604.                     my_bounds = gImageBounds;
  1605.                     // set top/bottom
  1606.                     my_bounds.top = i;
  1607. // NOTE, JIM! (next source line)
  1608. // By doing a CopyBits of the last rect in full, the whole rect of pixels _does_
  1609. // get written to the PICT file, even though not all the scan lines are valid..
  1610. // this doesn't hurt anything, just makes the file a little bigger than it needs to be,
  1611. // since the extra lines will get clipped on playback.  However, since gImageBounds is
  1612. // global, it would be icky to temporarily change its bottom along with my_bounds so
  1613. // as to only write the necessary lines.. maybe later. [esp] :-)
  1614.                     my_bounds.bottom = my_bounds.top + gVIBPixHeight;
  1615.                     // Get a handle to the offscreen pixmap area
  1616.                     aPixMapH = LockAndLoadPixMap(gOffScreenGWPtr);
  1617.                     CopyBits((BitMap*)*aPixMapH, (BitMap*)&myCGrafPort.portPixMap,
  1618.                             &gImageBounds, &my_bounds,
  1619.                             myMode, NULL);
  1620.                     anError = QDError();
  1621.                     // all done with the offscreen pixmap area
  1622.                     UnlockPixMap(aPixMapH);
  1623.                     }
  1624.                 }
  1625.             else
  1626.                 {
  1627.                 // Get a handle to the offscreen pixmap area
  1628.                 aPixMapH = LockAndLoadPixMap(gOffScreenGWPtr);
  1629.                 CopyBits((BitMap*)*aPixMapH, (BitMap*)&myCGrafPort.portPixMap,
  1630.                         &gImageBounds, &gImageBounds,
  1631.                         myMode, NULL);
  1632.                 anError = QDError();
  1633.                 // all done with the offscreen pixmap area
  1634.                 UnlockPixMap(aPixMapH);
  1635.                 }
  1636.  
  1637.  
  1638.         }
  1639.  
  1640.         /* write end-of-picture */
  1641.         if (!anError)
  1642.         {
  1643.             ClosePicture();
  1644.             anError = QDError();
  1645.         }
  1646.  
  1647.         if (!anError)
  1648.         {
  1649.             if (do_disk_buffer)
  1650.             {
  1651.                 // restore old bottlenecks
  1652.                 myCGrafPort.grafProcs = oldQDProcs;
  1653.                 DisposeRoutineDescriptor(myDrawProcUPP);
  1654.  
  1655.                 /* move back to pic header again */
  1656.                 if (gImagePictFile)
  1657.                     {
  1658.                     fflush(gImagePictFile);
  1659.                     fseek(gImagePictFile, PICTF_HEADER_SIZE, SEEK_SET);
  1660.                     anError = ferror(gImagePictFile);
  1661.                     }
  1662.                 /* update the PICT header in the file (with now-true picSize field..) */
  1663.                 if (!anError && gImagePictFile)
  1664.                 {
  1665.                     myPictFHeader.picSize = gMyPicSize;
  1666.                     i = fwrite (&myPictFHeader, 1, sizeof(myPictFHeader), gImagePictFile);
  1667.                     anError = ferror(gImagePictFile);
  1668.                 }
  1669.             }
  1670.             else
  1671.             {
  1672.                 ZeroScrap();
  1673.                 HLock((Handle) gMyPicHandle);
  1674.                 gMyPicSize = GetHandleSize((Handle) gMyPicHandle);
  1675.                 PutScrap (gMyPicSize, 'PICT', (Ptr) *gMyPicHandle);
  1676.                 gMyPicSize = UnloadScrap();
  1677.                 HUnlock((Handle) gMyPicHandle);
  1678.             }
  1679.         } // if !error
  1680.  
  1681.         /* close up shop */
  1682.         CloseCPort(&myCGrafPort);
  1683.         SetPort(oldGrafPtr);
  1684.         if (gMyPicHandle)
  1685.             DisposeHandle((Handle) gMyPicHandle);
  1686.  
  1687.         /* oh yeah, check for errors */
  1688.         if (anError)
  1689.         {
  1690.             if (gImagePictFile)
  1691.             {
  1692.                 fclose (gImagePictFile);
  1693.                 gImagePictFile = NULL;
  1694.             }
  1695.             SysBeep(4);
  1696.         }
  1697.  
  1698.         ShowArrowCursor();
  1699.  
  1700.     } // if gOffScreenGWPtr..
  1701.  
  1702. } // paint_to_picture
  1703.  
  1704.  
  1705. // ==============================================
  1706. // Write the Pixmap as a PICT file
  1707. // getName - if true, prompt for output file name
  1708. // ci - QuickTime StdCompression component if image is to be compressed
  1709. void SaveOutputFile(Boolean getName, ComponentInstance ci)
  1710. {
  1711.     int         k;
  1712.     Rect        myPicRect;    
  1713.     Point         where;
  1714.     SFReply        reply;
  1715.     FInfo        myFileInfo;
  1716.     FSSpec        fsFile;        
  1717.     long        filler;
  1718.  
  1719.     // It is possible (via Cooperate) that we could be in here saving an image,
  1720.     // and the silly user could hit SAVE again, getting us in here again on top
  1721.     // of the previous save.  This causes a file save error.  Instead, we check
  1722.     // here to see if we're already in here, and we beep and return if so.
  1723.     // (Thanks Adam for finding this one!)
  1724.     if (gBusySavingImage)
  1725.     {
  1726.         SysBeep(1);
  1727.         return;
  1728.     }
  1729.     gBusySavingImage = TRUE;
  1730.  
  1731.     //
  1732.     // Get the name of the file, or just get existing name as is
  1733.     //
  1734.     if (getName)
  1735.     {
  1736.         GetBestDialogPos(&where, (WindowPtr)&gp2wWindow);
  1737.         SFPutFile (where, "\pSave Pict file as…", gPictFname, NULL, &reply);
  1738.     }
  1739.     else
  1740.     {
  1741.         // copy the existing name into SF Reply..
  1742.         BlockMoveData(gPictFname, (char *) reply.fName, 1+gPictFname[0]);
  1743.         reply.vRefNum = gSrcWind_VRefNum;
  1744.         reply.good = TRUE;
  1745.     }
  1746.  
  1747.     //
  1748.     // Now save the file
  1749.     //
  1750.     if (reply.good)
  1751.     {
  1752.         ShowWatchCursor();
  1753.  
  1754.         // build an FSSpec
  1755.         fsFile.vRefNum = reply.vRefNum;
  1756.         fsFile.parID = 0;
  1757.         BlockMove(reply.fName, fsFile.name, 1+reply.fName[0]);
  1758.  
  1759.         // delete any previous file first
  1760.         FSpDelete(&fsFile);
  1761.  
  1762.         // prepare for C-style file I/O, set current directory
  1763.         p2cstr(reply.fName);
  1764.         SetVol(NULL, reply.vRefNum);
  1765.  
  1766.         // create new file
  1767.         gImagePictFile = fopen((char *) reply.fName,"wb");
  1768.         if (gImagePictFile)
  1769.         {
  1770.             // prepare a little buffer of zeroes to write out header
  1771.             filler = 0;
  1772.             // write 512 bytes of zeroes to fill the header
  1773.             for (k=0; k<PICTF_HEADER_SIZE/4; k++)
  1774.                 fwrite (&filler, 1, 4, gImagePictFile);
  1775.  
  1776.             // if not doing a virtual screen (which could take awhile) then
  1777.             // let the window update event get through to repaint the image
  1778.             // in case the dialog stomped on it.
  1779.             if (!gDoingVirtualFile)
  1780.             {
  1781.                 Cooperate(true);
  1782.             }
  1783.  
  1784.             paint_to_picture(true);
  1785.  
  1786.             if (gImagePictFile)
  1787.                 {
  1788.                     fclose (gImagePictFile);
  1789.                     gImagePictFile = NULL;
  1790.                     c2pstr((char *)reply.fName);
  1791.  
  1792.                     /* If QuickTime is around & user wants to squish the picture, ask user how to squish it */
  1793.                     k = FSMakeFSSpec(reply.vRefNum, 0, reply.fName, &fsFile);
  1794.                     if (gHasImageCompressionMgr && (**gPrefs2Use_h).doPictCompression)
  1795.                     {
  1796.                         myPicRect.top = 0;
  1797.                         myPicRect.bottom = gImageHeight;
  1798.                         myPicRect.left = 0;
  1799.                         myPicRect.right = gImageWidth;
  1800.                         k=CompressPictF(ci, &fsFile);
  1801.                     }
  1802.  
  1803.                     // set image file to type PICT
  1804.                     k = FSpGetFInfo(&fsFile, &myFileInfo);
  1805.                     if (k==noErr)
  1806.                     {
  1807.                         myFileInfo.fdType = 'PICT';
  1808.                         myFileInfo.fdCreator = (**gPrefs2Use_h).pictFileCreator;
  1809.                         // turn OFF inited flag, so Finder refreshes file information
  1810.                         myFileInfo.fdFlags &= ~kHasBeenInited;
  1811.                         k = FSpSetFInfo(&fsFile, &myFileInfo);
  1812.                     }
  1813.  
  1814. // not yet tested and working, but leave in for later..
  1815. //                    // stick a preview resource on it
  1816. //                    if (wanted..)
  1817. //                        k = AppendFilePreview2PictF(&fsFile);
  1818.  
  1819.                     // Add System 7 custom icons of the image itself to the file
  1820.                     if (gHasImageCompressionMgr && APGet_AddCustomIcons())
  1821.                         AppendFinderIcons2PictF(&fsFile, &myPicRect, eAFI_ShrinkWholeImage);
  1822.                 }
  1823.             else
  1824.                 // couldn't write PICT file
  1825.                 (void)DisplayModalDialog(140, ok, 0, NULL, 0, ewcDoCentering, eSameAsPassedWindow);
  1826.         }
  1827.         ShowArrowCursor();
  1828.     }
  1829.     gBusySavingImage = FALSE;
  1830. } // SaveOutputFile
  1831.  
  1832.