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..."