ciderpress/reformat/MacPaint.cpp

181 lines
5.3 KiB
C++

/*
* 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;
}