/* * CiderPress * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. * See the file LICENSE for distribution terms. */ /* * Convert MacPaint 'PNTG' images. */ #include "StdAfx.h" #include "MacPaint.h" /* * The files are always 576x720 monochrome. * * This format often starts with a MacBinary header, especially for files * posted to BBSs. The FileType should be 'PNTG', and the CreatorType will * usually be 'MPNT'. * * Following the 128-byte header (or at the very start of the data fork for * a file on the Mac) is: * * +000-003: 4-byte big-endian version number (0 or 2) * +004-307: pattern data (38 patterns * 8 bytes), not needed to decode image * +308-511: 240 bytes of zeroes, used for padding * +512-nnn: PackBits-encoded scan lines (72 bytes of output each) * * The files seem to be stored in 512-byte chunks, but it's probably unwise to * expect anything other than some extra bytes at the end. * * The bits are in the same order as Windows BMP, but white/black are reversed * (i.e. a '1' bit indicates a black pixel). This can be accommodated by * changing the palette colors or by inverting the bits. * * Corrupted MacPaint files seem to be popular. Handling corruption * gracefully is important. */ /* * If the file ends in ".mac", we accept it if it has a MacBinary header or * if it begins with 00000002 and exceeds the minimum size required. */ void ReformatMacPaint::Examine(ReformatHolder* pHolder) { ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot; const uint8_t* ptr = pHolder->GetSourceBuf(ReformatHolder::kPartData); long fileLen = pHolder->GetSourceLen(ReformatHolder::kPartData); const char* nameExt = pHolder->GetNameExt(); long version; if (fileLen < kMinSize) { LOGI(" MP: not long enough to be MacPaint (%d vs min %d)", fileLen, kMinSize); goto done; } version = Get32BE(ptr); if (stricmp(nameExt, ".mac") == 0 && version >= 0 && version <= 3) { LOGI(" MP: found w/o MacBinary header"); applies = ReformatHolder::kApplicProbably; goto done; } version = Get32BE(ptr + 128); if (version >= 0 && version <= 3 && ptr[65] == 'P' && ptr[66] == 'N' && ptr[67] == 'T' && ptr[68] == 'G') { LOGI(" MP: found inside MacBinary header"); applies = ReformatHolder::kApplicProbably; goto done; } done: pHolder->SetApplic(ReformatHolder::kReformatMacPaint, applies, ReformatHolder::kApplicNot, ReformatHolder::kApplicNot); } /* * Convert the image to a monochrome bitmap. */ int ReformatMacPaint::Process(const ReformatHolder* pHolder, ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part, ReformatOutput* pOutput) { MyDIBitmap* pDib; const uint8_t* srcBuf = pHolder->GetSourceBuf(part); long srcLen = pHolder->GetSourceLen(part); int retval = -1; if (srcLen < kMinSize || srcLen > kMaxSize) { LOGI(" MacPaint file is only %d bytes long", srcLen); goto bail; } pDib = ConvertMacPaint(srcBuf, srcLen); if (pDib == NULL) goto bail; SetResultBuffer(pOutput, pDib); retval = 0; bail: return retval; } /* * Handle the actual conversion. * * 576 pixels per line == 72 bytes per line, which is a multiple of 4 bytes * (required for windows BMP). */ MyDIBitmap* ReformatMacPaint::ConvertMacPaint(const uint8_t* srcBuf, long length) { MyDIBitmap* pDib = NULL; uint8_t* outBuf; static const RGBQUAD colorConv[2] = { /* blue, green, red, reserved */ { 0x00, 0x00, 0x00 }, // black { 0xff, 0xff, 0xff }, // white }; long version1, version2; int offset; version1 = Get32BE(srcBuf); version2 = Get32BE(srcBuf+128); if (version1 >= 0 && version1 <= 3) { offset = 0; } else if (version2 >= 0 && version2 <= 3 && srcBuf[65] == 'P' && srcBuf[66] == 'N' && srcBuf[67] == 'T' && srcBuf[68] == 'G') { offset = 128; } else { LOGI(" MP couldn't determine picture offset!"); goto bail; } pDib = new MyDIBitmap; if (pDib == NULL) goto bail; srcBuf += offset; srcBuf += kLeadingJunkCount; length -= offset; length -= kLeadingJunkCount; LOGI("Adjusted len is %d", length); outBuf = (uint8_t*) pDib->Create(kOutputWidth, kOutputHeight, 1, kNumColors); if (outBuf == NULL) { delete pDib; pDib = NULL; goto bail; } pDib->SetColorTable(colorConv); uint8_t* outPtr; int line; /* top row goes at the bottom of the buffer */ outPtr = outBuf + (kOutputHeight-1) * (kOutputWidth / 8); /* * Loop through all lines. When we've output 72 bytes, stop immediately * even if we're in a run. Chances are good that the run was corrupted. */ for (line = 0; line < kOutputHeight; line++) { UnPackBits(&srcBuf, &length, &outPtr, 72, 0xff); /* back up to start of next line */ outPtr -= 2*(kOutputWidth / 8); if (length < 0) break; } if (length != 0) { LOGI(" MP found %d unused bytes at end", length); } bail: return pDib; }