[comp.sys.mac.programmer] Reading PICT's revisited.

minow@mountn.dec.com (Martin Minow) (07/08/90)

Thanks for all the good replies to my question on reading a 300 dpi
PICT into a (72 dpi) window.  Here's the code I use to read and write
PICTs into an offscreen GrafPort.

Martin Minow
minow@bolt.enet.dec.com

/*                    ReadPicture.c                    */
/*
 * Picture I/O
 *
 * For some reason, the PICT dimensions do not correctly
 * describe the picture: it loses its 300 dpi resolution
 * when read in. MAGIC scales the PICT by a factor of 4.
 * We should get the "real" scale factor by inserting
 * our function into the StdBits bottleneck.
 */
#include "CleanPICT.h"
#include <SysErr.h>
#define MAGIC(size)  ((((long) size) * (4 * 72)) / 72)

/*
 * See TechNote 27 (original format), and the Essential
 * MacTutor, vol 3, p 417.  (This is now unused.)
 */
typedef struct {
  OSType  fType;          /* 'DRWG' for MacPaint files  */
  short    hdrID;          /* 'MD' for MacPaint format    */
  short    version;        /* File version                */
  short    prRec[60];      /* 120 byte print record      */
  Fixed    xOrigin;        /* Drawing origin              */
  Fixed    yOrigin;        /* Drawing origin              */
  Fixed    xScale;          /* Screen resolution          */
  Fixed    yScale;          /* Screen resolution          */    
  short    atrState[31];    /* Drawing attribute state    */
  short    lCnt;            /* Top-level objects          */
  short    lTot;            /* Total number of objects    */
  long    lSiz;            /* Total size of list          */
  Long    top;            /* Box enclosing all objects  */
  Long    left;
  Long    bottom;
  Long    right;
  short    filler1[141];    /* 282 bytes unused            */
} MacDrawHdrRec;

static int      PICTfile;  /* The file ioRefNum          */

/*
 * Use this to peek into the bitmap as it is read in.
 * The peeking is done by jumping into the Debugger.
 */
pascal void      myStdBits(
    BitMap *, Rect *, Rect *, int, RgnHandle);
pascal void      read_picture_data(Ptr, int);
pascal void      write_picture_data(Ptr, int);

/*
 * read_picture() reads the picture from the PICT file,
 * constructing the window at the proper size.
 * "window" is really a document structure:
 *    typedef struct {
 *	WindowRecord	window;
 *	GrafPtr		pictPort; -- offscreen grafport
 *	Rect		pictSize; -- true PICT size
 *    } DocumentRecord;
 * The DOC macro handles type casting and dereferencing.
 */
OSErr
read_picture(window, theFile)
WindowPtr      window;    /* Read into this document  */
int            theFile;   /* From this open PICT file */
{
    PicHandle        handle;
    QDProcs          procedures;
    OSErr            status;
    long            size;
    long            place;
    Rect            box;
    GrafPtr          oldPort;
    MacDrawHdrRec  header;
    
    PICTfile = theFile;
    handle = (PicHandle) NewHandle(sizeof (Picture));
    if (handle == NIL) {
      DebugStr("\pCan't get memory for picture");
      return (MemError());
    }
    /*
     * Read the MacDraw header record -- that was a
     * good idea, but it didn't work as the headers
     * are garbage in the PICT I'm using.
     */
    if (sizeof header != 512)
      DebugStr("\pMacDrawHdrRec wrong size!");
    read_picture_data((Ptr) &header, sizeof header);
    HLock(handle);
    read_picture_data((Ptr) *handle, sizeof (Picture));
    HUnlock(handle);
    box = (**handle).picFrame;
    DOC.pictSize = box;
    box.right = MAGIC(box.right);
    box.bottom = MAGIC(box.bottom);
    GetPort(&oldPort);
    /*
     * Create an offscreen GrafPort.  See TechNote 41.
     */
    DOC.pictPort = CreateOSGrafPort(box);
    if (DOC.pictPort == NIL) {
      DebugStr("\pNo memory for picture");
      SetPort(oldPort);
      return (MemError());
    }
    SetStdProcs(&procedures);
    DOC.pictPort->grafProcs = &procedures;
    procedures.getPicProc = (Ptr) read_picture_data;
/*  procedures.bitsProc = (Ptr) myStdBits;    -- unused  */
    DrawPicture(handle, &box);
    DOC.pictPort->grafProcs = NIL;
    DisposHandle((Handle) handle);
    SetPort(oldPort);
    /*
     * Check for errors by getting the file position and
     * checking that it is at the end of file.
     */
    if ((status = GetEOF(PICTfile, &size)) != noErr
     || (status = GetFPos(PICTfile, &place)) != noErr) {
       DebugStr("\pCan't get EOF or file position");
       return (status);
    }
    if (size != place) {
      DebugStr("\pDidn't read entire picture");
      return (dsSysErr);
    }
    /*
     * Ok so far.  Now, change the window size so the
     * picture fills the window -- but keep the proportions
     * as close to the original as possible.
     */
    SetRect(
      &box,
      2,
      GetMBarHeight() * 2,
      width(DOC.pictSize) + 2,
      GetMBarHeight() * 2 + height(DOC.pictSize)
    );
    if (box.bottom > (screenBits.bounds.bottom - 2)) {
      box.bottom = screenBits.bounds.bottom - 2;
      size = height(box);
      box.right = box.left
        + ((long) width(box) * size) / height(DOC.pictSize);
    }
    if (box.right > (screenBits.bounds.right - 2)) {
      box.right = screenBits.bounds.right - 2;
      size = width(box);
      box.bottom = box.top
        + ((long) height(box) * size) / width(DOC.pictSize);
    }
    SizeWindow(window, width(box), height(box), TRUE);
    InvalRect(&window->portRect);
    ShowWindow(window);
    return (MemError());
}

/*
 * This should implement a "vanilla" StdBits -- for some
 * reason, though, it "bombs" when it runs to completion:
 * probably because its called with a NULL argument at
 * eof.  It was only used to check that the created
 * "MAGIC" bitmap is the same size as the bitmap inside
 * of the PICT -- by running the function under the
 * debugger.
 */
pascal void
myStdBits(srcBits, srcRect, dstRect, mode, maskRgn)
BitMap    *srcBits;
Rect      *srcRect;
Rect      *dstRect;
short      mode;
RgnHandle  maskRgn;
{
    CopyBits(
      srcBits,
      &thePort->portBits, 
      srcRect, dstRect,
      mode,
      maskRgn
    );
}

/*
 * Called indirectly to read a chunk of picture data.
 */
pascal void
read_picture_data(data_ptr, byte_count)
Ptr        data_ptr;
int        byte_count;
{
    OSErr        status;
    long        count;
    
    count = byte_count;
    status = FSRead(PICTfile, &count, data_ptr);
    if (status != noErr)
      DebugStr("\pReading picture");
}

/*
 * write_picture() writes the current picture to a
 * specified (open) file.  It should be redone to
 * add error handling.
 */
void
write_picture(window, theFile)
WindowPtr      window;
int            theFile;
{
    PicHandle    picHandle;
    QDProcs      procedures;
    int          i;
    long        temp;
    Picture      header;
    GrafPtr      tempPort;
    GrafPtr      oldPort;

    GetPort(&oldPort);
    PICTfile = theFile;
    /*
     * Write a dummy MacPaint header
     */
    temp = 0L;
    for (i = 0; i < 512; i += sizeof temp)
      write_picture_data((Ptr) &temp, (int) sizeof temp);
    header.picSize = 0;
    header.picFrame = DOC.pictSize;
    write_picture_data((Ptr) &header, (int) sizeof header);
    /*
     * Write the picture by creating a GrafPort with the
     * same dimensions as the original, then drawing the
     * cleaned up picture.  Can this be done without
     * creating a new GrafPort?
     */
    tempPort = CreateOSGrafPort(DOC.pictSize);
    if (tempPort == NIL) {
      DebugStr("\pNo space for temp port");
      return;
    }
    SetStdProcs(&procedures);
    tempPort->grafProcs = &procedures;
    procedures.putPicProc = (Ptr) write_picture_data;
    picHandle = OpenPicture(&tempPort->portRect);
    CopyOSGrafPort(DOC.pictPort, tempPort);
    ClosePicture();
    KillPicture(picHandle);
    DeleteOSGrafPort(tempPort);
    DOC.pictPort->grafProcs = NIL;
    SetPort(oldPort);
}

/*
 * Called indirectly to write a chunk of picture data.
 */
pascal void
write_picture_data(data_ptr, byte_count)
Ptr        data_ptr;
int        byte_count;
{
    OSErr        status;
    long        count;
    
    count = byte_count;
    status = FSWrite(PICTfile, &count, data_ptr);
    if (status != noErr)
      DebugStr("\pWriting picture");
}