MacPaint files
Volume Number: | | 1
|
Issue Number: | | 7
|
Column Tag: | | C Workshop
|
MacPaint file format
By Keith McGreggor
The MacPaint file format is rapidly becoming the standard for transferring graphic information from one application to another. This article will explain that format and show you how to transfer your own images into MacPaint files.
A Little Bit of QuickDraw
The Macintoshs screen is a bit image. That is, what appears on the screen is actually a collection of consecutive bits in memory that the hardware in the Mac interprets as screen dots (or pixels). If the value of one of these bits is 1, then the pixel corresponding to it will be black. If the bit is 0, then the pixel will be white.
Figure #1. Bits represent pixels
The Mac screen is 512 pixels wide by 342 pixels tall. The bit image in memory takes up 175,104 bits (or 21,888 bytes with 8 bits per byte). We can consider the screen bit image as being 342 rows with 64 bytes per row. Due to the 68000 microprocessor in the Macintosh, every bit image must have an even number of bytes per row, and the rows must begin and end on word boundaries.
In C, the easiest way to define a bit image is with an array of characters. For example, if we wanted to define the Macintosh screens bit image, the definition would be:

Figure #2. C definition of a bit image
Bit images are manipulated by Quick- Draw through the use of bitmaps. A bit- map is a structure which points to a bit image and associates a coordinate system with it. The definition of a bitmap is:

Figure #3. C definition of a bitmap
The baseAddr field points to the first byte (character) in the bit image. The rowBytes field contains the number of bytes per row in the bit image. The rectangle bounds provides the coordinate system for the bits. The top left corner of the rectangle is aligned with the first bit of the bit image.
Since we can treat bit images in C as arrays of characters, we dont always have to display them immediately. You can easily create a picture in a bit image off-screen, and later in your program stamp it onto the Macs screen.
The Macintoshs ROM contains several QuickDraw calls that allow you to manipulate bitmaps (on- or off-screen). Well look at two of these later in the program.
MacPaint images are kept in the data fork of a MacPaint file. The file is essentially a 512 byte header followed by a bit image that is 576 pixels wide by 720 pixels tall (or 72 bytes by 720 rows).

Figure #4. The MacPaint File Format
The 512 bytes of header contain version information, pattern definitions, and an unused area for future expansion. If you change the contents of this section with care, you can set up your own patterns. If the version number is zero, the default patterns are used. If the number is not zero, then the patterns in the header are used.

Fig.#5.The Paint File Header Format
The MacPaint file bit image is considerably larger than the Macintosh screens bit image. In fact, if you were to store a bit image 72 bytes by 720 rows directly onto disk, each MacPaint file would take a minimum of 52K bytes! To make these files smaller, each row of pixels has been compressed using the PackBits routine in the Macintosh ROM. Using PackBits, the typical MacPaint file compresses down to around 10K bytes.
Dipping in the Bit Bucket
To make your program transfer an image into a MacPaint file, you need only to copy the bitmap containing the image into the compressed MacPaint file format. To be able to do this, you need to use two ROM routines, CopyBits and PackBits.
CopyBits transfers the contents of one bitmap into another. If you want, you can specify a mask region in the destination bitmap to prevent CopyBits from destroying the entire image. CopyBits will shrink or expand the contents of the source bitmap to fit the destination bitmaps rectangle. To call CopyBits from C, use this format:

Figure #6. Calling CopyBits from C
where copymode is one of the source transfer modes:

Figure #7. Source Transfer Modes
PackBits compacts a string of bytes by compressing runs of equal bytes. You call PackBits from C like so:

Figure #8. Calling PackBits from C
where numbytes is the number of bytes pointed to by srcPtr.
Initially, dstPtr should point to the first destination byte. The PackBits routine will move dstPtr to point to the next available byte. To find out how much space the original bytes were compacted into, you need to subtract the original location of dstPtr from its current location.

Fig. #9 How PackBits moves dstPtr
About the Program
This program will demonstrate how to cut a part of an image into a MacPaint file. It is written for the Consulair Mac C system and Toolkit. Youll probably need to change things a bit to make it work with other versions of C.
The program opens a single window and draws a few inverted rectangles in it. Then, a gray flashing rectangle will appear and follow the position of the cursor. Use the mouse to position this rectangle where you want, and then press the mouse button. The image inside the rectangle will be cut from the screen (using CopyBits) and transferred into a MacPaint file called DummyFileName (using repeated calls to PackBits).
The key routine, MakePaintFile, takes as input a C string containing a filename, and a pointer to a bitmap of arbitrary size. It then creates that file (if possible) on the active disk drive, sets the file signature and type to a MacPaint file type, and compresses that bitmap into the file using PackBits. If the bitmap is smaller than 576 by 720 (as will usually be the case), the bitmap is padded with white space. If the bitmap is larger than 576 by 720, the bitmap is clipped to 576 by 720.
/*
* MakePaint.h
* a C routine to create a MacPaint file
* from a given bitmap
*
* (c) 1985 by Keith McGreggor for MacTutor
*
*/
#define WRITEONLY 7
MakePaintFile( myfilename, mybitmap )
char myfilename[63];
struct ABitMap *mybitmap;
{
char dstbuf[511],srcbuf[511];
char *srcPtr;
char *dstPtr;
char *mypointer;
short i,j,dstBytes;
FILE myfile;
short t,b,vsize,hsize;
// attempt to create the file
myfile = creat( myfilename, WRITEONLY );
if (myfile != 0) {
// turn the file into a MacPaint file
// and write out a 512 byte header
// full of zeros (were not using any of
// our own patterns)
SetFileSignature(myfilename,MPNT);
SetFileType(myfilename,PNTG);
for (i = 0; i < 512; i++) fputc(0,myfile);
// figure out how big the bitmap is and set up
// a general pointer to the bit image
mypointer = mybitmap->baseAddr;
hsize = mybitmap->rowBytes;
vsize = (mybitmap->bounds.bottom)
- (mybitmap->bounds.top);
// now, write out 720 rows of bytes
for (j = 1; j <= 720; j++) {
// reinitialize source and destination pointers
srcPtr = &srcbuf[0];
dstPtr = &dstbuf[0];
// copy the next row of bytes into srcbuf[],
// clipping or expanding where necessary
for (i = 0; i < 72; i++) {
if ((i < hsize) && (j <= vsize)) {
srcbuf[i] = *mypointer;
mypointer++;
}
else srcbuf[i] = 0; // 8 white bits
}
// compress srcbuf[] into dstbuf[]
#PackBits(&srcPtr,&dstPtr,72);
// figure out how much compression occurred
// and write those bytes out to the file
dstBytes= (short)(dstPtr-&dstbuf[0]);
for (i = 0; i < dstBytes; i++)
fputc(dstbuf[i],myfile);
}
// all 720 lines have been written, so
// close up everything and return
close(myfile);
}
}
/* CutToPaint.C
* A sample program to illustrate
* the use of PackBits and CopyBits
*
* (c) 1985 by Keith McGreggor for MacTutor
*/
#Options -N
#include Stdio.h
#include MacCdefs.h
#include Window.h
#define TRUE 0xFF
_#define BUTTONNOTPRESSED !#Button()
#define GRAY &(QD->gray)
// allocate the applications window
WindowPtr mywindow;
Rect mywindowrect = { 40, 5, 300, 507 };
// allocate the tracking variables
Rect trackingrect{0,0,100,100};
short globx,globy,incx,incy;
// allocate a bitmap and bit image to
// hold the cut bits
struct ABitMap{
char *baseAddr;
short rowbytes;
Rect bounds;
};
struct ABitMap targetmap;
char theactualbits[14][100];
#include MakePaint.h
/* ------------------------------------*/
// draw some inverted rectangles
// just to have something to cut
DrawSomeStuff()
{
Rect temprect;
short i;
#MoveTo(10,240);
#DrawString(\020Press the button!);
for (i=0;i<200;i += 10) {
#SetRect(&temprect,i+20,i+20,i+100,i+40);
#InvertRect(&temprect);
}
}
/*------------------------------------*/
// Open a window for drawing
// and do general housekeeping
InitializeOurSystem()
{
#InitDialogs(0);
mywindow = (WindowPtr)#NewWindow(0, &mywindowrect,\021A Window for
bits, TRUE,0,-1,TRUE,0);
#SetPort(mywindow);
DrawSomeStuff();
#FlushEvents(-1,0);
#InitCursor();
#PenSize(2,2); // set up pen for
#PenMode(patXor); // drawing the
#PenPat(GRAY); // ghost rectange
}
/*------------------------------------*/
// Draw the ghost rectangle
DrawCurrentPosition()
{
#FrameRect(&trackingrect);
}
/*--------------------------------------*/
// Find out where the mouse is and
// update global variables
GetNewMouse()
{
Point mypoint;
#GetMouse(&mypoint);
#GlobalToLocal(&mypoint);
incx = mypoint.h-globx;
incy = mypoint.v-globy;
globx = mypoint.h;
globy = mypoint.v;
}
/*------------------------------------*/
// move the ghost rectange to track
// the motion of the mouse
UpdatePostion()
{
#OffsetRect(&trackingrect,incx,incy);
}
/*------------------------------------*/
// First, move around a ghost rectange
// until the button is pressed.
// Then, create a bitmap and copy
// whatever is inside the rectange to
// the bitmap.
CutARectangle()
{
globx = 0;
globy = 0;
while (BUTTONNOTPRESSED) {
DrawCurrentPosition();
GetNewMouse();
DrawCurrentPosition();
UpdatePosition();
}
#FlushEvents(-1,0);
#LocalToGlobal(&trackingrect.topleft);
#LocalToGlobal(&trackingrect.botRight);
targetmap.baseAddr = &theactualbits[0][0];
targetmap.rowBytes = 14;
targetmap.bounds.left = 0;
targetmap.bounds.top = 0;
targetmap.bounds.right = trackingrect.right-trackingrect.left;
targetmap.bounds.bottom = trackingrect.bottom- trackingrect.top;
// mywindow -> portBits is the Macintosh screen bit image
#CopyBits(&(mywindow->portBits), &targetmap,
&trackingrect,
&(targetmap.bounds),
srcCopy,
0);
}
/*------------------------------------*/
// call makepaintfile to save the
// bitmap away as a macpaint file
// named dummyfilename
// (note: see MakePaint.h listing)
SaveItAway()
{
MakePaintFile(DummyFileName,&targetmap);
}
/*----------------------------------*/
// Our main program
main()
{
InitializeOurSystem();
CutARectangle();
SaveItAway();
#ExitToShell();
}