cameron@symcom.math.uiuc.EDU (12/03/87)
While we're on the subject of file formats used by this or that computer, program, or printer, could someone please tell me what the format of a MacPaint document is? I have obtained some MacPaintings from a friend who has a Mac and a modem. I have a program obtained from a PC-oriented BBS which will read these files and display them on my (CGA-compatible) screen, but the aspect ratio is wrong, and anyway, I want to be able to manipulate the images (crop, rotate, scale, &c.), not just look at them. When I view the file as straight text I see some recognizable character strings which I assume are things like file name, creator ID, and such. I want to be able to extract the bitmap and throw the Finder-oriented stuff away. Thanks in advance. Email is OK, or post here if you think it's of general interest -- I'm a regular reader of this newsgroup (notwithstanding the fact that a lot of it is Greek to me). -------- Cameron Smith Symbolic Computation Lab "You knew the job was dangerous Math Dept. when you took it, Fred!" University of Illinois -- SuperChicken Urbana IL 61801 (217) 333-4654 cameron@symcom.math.uiuc.edu
wel@ncsuvx.ncsu.edu (Warren E. Lewis) (12/08/87)
________________________________________________________________________________
Macintosh Technical Notes p
#86: MacPaint Document Format
See also: MacPaint User Manual
ToolBox Utilities
Written by: Bill Atkinson 1983
Modified by: Bryan Johnson August 19, 1986
________________________________________________________________________________
This technical note describes the internal format of a MacPaint document. It
is the same as the description in the Macintosh Miscellaneous section of
early versions of Inside Macintosh. It has been verified to be correct for
version 1.5 of MacPaint.
________________________________________________________________________________
MacPaint documents are easy to read and write and have become a standard
interchange format for full-page images on Macintosh. Their internal format is
described here to help developers generate and read MacPaint documents.
Documents created by MacPaint have a file type of PNTG and creator ID of MPNT.
MacPaint documents use only the data fork; the resource fork is not used and may
ignored. The data fork contains a 512 byte header and then the compressed data
representing a single bit map of 576 pixels wide by 720 pixels tall. At 72 pixe
inch, this bit map occupies the full 8 by 10 inch printable area of the standard
ImageWriter printer page.
Header
The first 512 bytes of the document form a header with a 4 byte version number
(default = 2), then 38*8 = 304 bytes of patterns, then 204 unused bytes reserved
future expansion. As a Pascal record it could look like:
MPHeader = RECORD
Version: LONGINT;
PatArray: ARRAY [1..38] of Pattern;
Future: PACKED ARRAY [1..204] of SignedByte;
END;
If the version number is zero, the rest of the header block is ignored and defau
are used, so programs generating MacPaint documents can simply write out 512 byt
zero as the document header. Most programs which read MacPaint documents can
simply skip over the header when reading.
BitMap
Following the header are 720 compressed scanlines of data which form the 576 pix
wide by 720 pixel tall bit map. Without compression, this bit map would occupy 5
bytes and chew up disk space pretty fast; typical MacPaint documents compress to
10 Kbytes using the PackBits procedure in the Macintosh ROM to compress runs of
equal bytes within each scanline. The bit map part of a MacPaint document is si
output of PackBits called 720 times, with 72 bytes input.
Reading Sample
{ ReadMPFile:
This is a small example program written in TML Pascal that demonstrates how
to read MacPaint files. As a final step, it takes the data that was read
and displays it on the screen to show that it worked. Caveat: This is not
intended to be an example of good programming practice, in that the possible
errors merely cause the program to exit. This is VERY uninformative, and
there should be some sort of error handler to explain what happened. For
simplicity, and thus clarity, those types of things were deliberately not
included. This example will not work on a 128K Macintosh, since memory
allocation is done too simplistically.
}
PROGRAM ReadMPFile;
{$I 'HD:Pascal System:MemTypes.ipas' }
{$I 'HD:Pascal System:QuickDraw.ipas' }
{$I 'HD:Pascal System:OSIntf.ipas' }
{$I 'HD:Pascal System:ToolIntf.ipas' }
CONST DefaultVolume = 0;
MaxFileSize = 51840; { maximum MacPaint file size, 720*72. }
VAR srcPtr: Ptr;
dstPtr: Ptr;
saveDstPtr: Ptr;
srcFileName: Str255;
srcFile: INTEGER;
srcSize: LONGINT;
errCode: INTEGER;
scanLine: INTEGER;
aPort: GrafPort;
theBitMap: BitMap;
BEGIN
{ Initialize QuickDraw. }
InitGraf(@thePort);
{ Make a name of a file to read. }
srcFileName := 'MP TestFile';
{ Make a buffer that is the largest picture we can expect. This
could be done in a more memory efficient manner. }
srcPtr := NewPtr(MaxFileSize);
IF srcPtr = NIL THEN ExitToShell;
{ Open the data file. }
errCode := FSOpen(srcFileName,DefaultVolume,srcFile);
IF errCode <> noErr THEN ExitToShell;
{ Skip the header. }
srcSize := 512;
errCode := FSRead(srcFile,srcSize,srcPtr);
IF errCode <> noErr THEN ExitToShell;
{ Find out how big the file is, and figure out source size. }
errCode := GetEOF(srcFile,srcSize);
IF errCode <> noErr THEN ExitToShell;
srcSize := srcSize - 512; { Remove the header from count. }
{ Read the data into the buffer. The file mark is already past the header. }
errCode := FSRead(srcFile,srcSize,srcPtr);
IF errCode <> noErr THEN ExitToShell;
{ Close the file we just read. }
errCode := FSClose(srcFile);
IF errCode <> noErr THEN ExitToShell;
{ Create a buffer that will be used for the Destination BitMap. This also
has a maximum size possible, the same as the full MacPaint picture size. }
dstPtr := NewPtr(MaxFileSize);
IF dstPtr = NIL THEN ExitToShell;
saveDstPtr := dstPtr;
{ Unpack each scanline into the buffer. Note that 720 scanlines are
guaranteed to be in the file. (They may be blank lines) In the
UnPackBits call, the 72 is the count of bytes done when the file was
created. MacPaint does one scan line at a time when creating the file. }
FOR scanLine := 1 to 720 DO
BEGIN
UnPackBits(srcPtr,dstPtr,72); {bumps both ptrs}
END;
{ The buffer has been fully unpacked. Create a port that we can draw into. }
OpenPort(@aPort);
{ Create a BitMap out of our saveDstPtr that can be copied to the screen. }
theBitMap.baseAddr := saveDstPtr;
theBitMap.rowBytes := 72; { width of MacPaint picture }
SetPt(theBitMap.bounds.topLeft, 0,0);
SetPt(theBitMap.bounds.botRight, 72*8, 720); {maximum rectangle}
{ Now use that BitMap and draw the piece of it to the screen.
Only draw the piece that is full screen size (portRect). }
CopyBits(theBitMap, aPort.portBits, aPort.portRect,
aPort.portRect, srcCopy, NIL);
{ That's it. Now wait for the mouse button to leave. Pause. }
REPEAT
UNTIL Button;
END.
Writing Sample
{ WriteMPFile:
This is a small example program written in TML Pascal that demonstrates how
to write MacPaint files. It will use the screen as a handy BitMap to be
written to a file.
}
PROGRAM WriteMPFile;
{$I 'HD:Pascal System:MemTypes.ipas' }
{$I 'HD:Pascal System:QuickDraw.ipas' }
{$I 'HD:Pascal System:OSIntf.ipas' }
{$I 'HD:Pascal System:ToolIntf.ipas' }
CONST DefaultVolume = 0;
MaxFileSize = 51840; { maximum MacPaint file size, 720*72. }
MaxDiv2 = 25920; { divided by 2 for half buffer size. }
MaxDiv4 = 12960; { divided by 4 for clearing. }
MaxDiv8 = 6480; { divided by 8 for bug avoidance. }
TYPE ByteBuffer = PACKED ARRAY [1..512] of SignedByte;
BigBuffer = PACKED ARRAY [1..MaxDiv4] of LONGINT;
BigPtr = ^BigBuffer;
VAR srcPtr: Ptr;
dstPtr: Ptr;
dstFileName: Str255;
dstFile: INTEGER;
dstSize: LONGINT;
errCode: INTEGER;
scanLine: INTEGER;
aPort: GrafPort;
dstBuffer: ByteBuffer;
I: LONGINT;
picturePtr: BigPtr;
tempPtr: BigPtr;
theBitMap: BitMap;
BEGIN
{ Initialize QuickDraw. }
InitGraf(@thePort);
{ Make a buffer that is the picture size. }
picturePtr := NewPtr(MaxFileSize);
IF picturePtr = NIL THEN ExitToShell;
{ Now clear the buffer since it is a picture, and we want it to
start as a blank page. Clear four bytes at a time. This is done
in two steps since there is a bug with TML that won't allow us
to index an array larger than 32768. This would normally be
done by setting the CLEAR bit for the NewPtr used in assembly,
but that would make this even more complex. }
FOR I := 1 to MaxDiv8 DO
picturePtr^[I] := 0;
{ Create the address half way through the block. This keeps our
index of I smaller than 32768. }
tempPtr := BigPtr (ORD4 (picturePtr) + MaxDiv2);
{ Clear the rest of the block, 4 bytes at a time. }
FOR I := 1 to MaxDiv8 DO
tempPtr^[I] := 0;
{ Open a port so we can get to the screen's BitMap easily. }
OpenPort(@aPort);
{ Create a BitMap out of our dstPtr that can be copied to the screen. }
theBitMap.baseAddr := picturePtr;
theBitMap.rowBytes := 72; { width of MacPaint picture }
SetPt(theBitMap.bounds.topLeft, 0,0);
SetPt(theBitMap.bounds.botRight,72*8,720); {maximum rectangle}
{ Draw the screen over into our picture buffer. }
CopyBits(aPort.portBits, theBitMap, aPort.portRect,
aPort.portRect, srcCopy, NIL);
{ Make a name of a file to write. }
dstFileName := 'MP TestFile';
{ Create the file, giving it the right Creator and File type.}
errCode := Create(dstFileName, DefaultVolume, 'MPNT', 'PNTG');
IF errCode <> noErr THEN ExitToShell;
{ Open the data file to be written. }
errCode := FSOpen(dstFileName,DefaultVolume,dstFile);
IF errCode <> noErr THEN ExitToShell;
{ Write the header as all zeros. }
FOR I := 1 to 512 DO
dstBuffer[I] := 0;
dstSize := 512;
errCode := FSWrite(dstFile,dstSize,@dstBuffer);
IF errCode <> noErr THEN ExitToShell;
{ Now go into a loop where we pack each line of data into the buffer, then
write that data to the file. We are using the line count of 72 in order
to make the file readable by MacPaint. Note that the Pack/UnPackBits can
be used for other purposes. }
srcPtr := theBitMap.baseAddr; { point at our picture BitMap }
FOR scanLine := 1 to 720 DO
BEGIN
dstPtr := @dstBuffer; { reset the pointer to bottom }
PackBits(srcPtr,dstPtr,72); { bumps both ptrs}
dstSize := ORD(dstPtr)-ORD(@dstBuffer);{calc packed size}
errCode := FSWrite(dstFile,dstSize,@dstBuffer);
IF errCode <> noErr THEN ExitToShell;
END;
{ Close the file we just wrote. }
errCode := FSClose(dstFile);
IF errCode <> noErr THEN ExitToShell;
END.wew@naucse.UUCP (Bill Wilson) (12/08/87)
I just picked up a program called "The Graphics Link" that allows the conversion of Graphics files from one format to another. It even works with Mac files. It can be purchased for $99 from PC Quick Art. Call them at 1-800-523-1796.
ks26+@andrew.cmu.edu (Kenneth Sykes) (12/09/87)
The file description posted by Warren E. Lewis (8-Dec-87) is complete, with the exception of what the PackBits/UnpackBits procedures do. The packed information consists of a control byte (8 bit signed integer) followed by a set of 'appropriate' data. If the control byte is greater than zero (n = c), then the next n bytes are to be copied directly to the output bitmap (this is called a MIXED block). If the control byte is less than zero, then negate the control byte and add 1 (n = -c + 1). Then make n copies of the next byte in the output bit map (this is called a REPEAT block). After the control byte has been processed and the output bitmap updated, the next control byte is immediately after the data associated with the current control byte. Hope this helps any non-Mac programmers get Mac pictures up-and-running. -- Ken Sykes
cameron@symcom.math.uiuc.EDU (12/10/87)
/* Written 8:03 pm Dec 7, 1987 by wel@ncsuvx.ncsu.edu */ /* in symcom:comp.graphics */ /* ---------- "Re: MacPaint format" ---------- */ ________________________________________________________________________________ Macintosh Technical Notes p #86: MacPaint Document Format See also: MacPaint User Manual ToolBox Utilities Written by: Bill Atkinson 1983 Modified by: Bryan Johnson August 19, 1986 ________________________________________________________________________________ [...Document deleted...] /* End of text from symcom:comp.graphics */ I was the one who asked for that. Thank you! Thank you! Thank you! -------- Cameron Smith Symbolic Computation Lab "You knew the job was dangerous Math Dept. when you took it, Fred!" University of Illinois -- SuperChicken Urbana IL 61801 (217) 333-4654 cameron@symcom.math.uiuc.edu
ks26+@andrew.cmu.edu (Kenneth Sykes) (12/11/87)
One note on the description of Packbits/Unpackbits (8-Dec-87):
Mixed blocks need to add 1 to the control byte as well as Repeat blocks.
Old line:
>... If the control byte is greater than zero (n = c), then the next n bytes
...
Should read as
"If the control byte is greater than zero (n=c+1), the next n bytes..."