ciderpress/reformat/MacPaint.cpp
Andy McFadden 8f61f84585 Use types with explicit sizes
Much of what the "reformat" code does involves processing data that is
8, 16, or 32 bits.  We want to use size-specific types from stdint.h
(e.g. uint16_t) rather than "unsigned short".

This was a quick pass to replace the various "unsigned" declarations.
More can be done here and elsewhere.
2014-11-20 18:10:18 -08:00

184 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;
}