ciderpress/reformat/ReformatBase.cpp
Andy McFadden 2adbe9591f Add optional MouseText-to-ASCII conversion
The AWP5 MouseText output is okay for some things but not others.
This adds an ASCII conversion, enabled through a preference.
2017-11-13 14:24:58 -08:00

1041 lines
29 KiB
C++

/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Reformatter base class implementation.
*/
#include "StdAfx.h"
#include "ReformatBase.h"
#include <math.h>
/*
* ==========================================================================
* ReformatText
* ==========================================================================
*/
/*
* Set the output format and buffer.
*
* Clears our work buffer pointer so we don't free it.
*/
void ReformatText::SetResultBuffer(ReformatOutput* pOutput, bool multiFont)
{
char* buf;
long len;
fExpBuf.SeizeBuffer(&buf, &len);
pOutput->SetTextBuf(buf, len, true);
if (pOutput->GetTextBuf() == NULL) {
/*
* Force "raw" mode if there's no output. This can happen if we,
* say, try to format an empty file as a hex dump. We never
* produce any output, so no buffer gets allocated.
*
* We set the mode to "raw" so that applications can assume that
* results of type "text" actually have text to look at -- though
* it's possible the length will be zero, we promise that there'll
* be a buffer there. I'm not sure it's important to do this,
* but it does reduce the #of situations in which we have to
* worry about NULL pointers.
*/
pOutput->SetOutputKind(ReformatOutput::kOutputRaw);
LOGI("ReformatText returning a null pointer");
} else {
if (fUseRTF)
pOutput->SetOutputKind(ReformatOutput::kOutputRTF);
else
pOutput->SetOutputKind(ReformatOutput::kOutputText);
}
if (fUseRTF && multiFont)
pOutput->SetMultipleFontsFlag(true);
}
/*
* Output the RTF header.
*
* The color table is the standard MS Word color table, except that entry
* #17 (dark grey) has been lightened from (51,51,51) because it's nearly
* indistinguishable from black on the screen.
*
* The default font is Courier New (\f0) at 10 points (\fs20).
*/
void ReformatText::RTFBegin(int flags)
{
// static const char* rtfHdr =
//"{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033{\\fonttbl{\\f0\\fmodern\\fprq1\\fcharset0 Courier New;}}\r\n"
//"\\viewkind4\\uc1\\pard\\f0\\fs20 ";
static const char* rtfHdrStart =
"{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033\\deflangfe1033{\\fonttbl"
"{\\f0\\fmodern\\fprq1\\fcharset0 Courier New;}"
"{\\f1\\froman\\fprq2\\fcharset0 Times New Roman;}"
"{\\f2\\fswiss\\fprq2\\fcharset0 Arial;}"
"{\\f3\\froman\\fprq2\\fcharset2 Symbol;}"
"}\r\n";
static const char* rtfColorTable =
"{\\colortbl;"
"\\red0\\green0\\blue0;\\red0\\green0\\blue255;\\red0\\green255\\blue255;\\red0\\green255\\blue0;"
"\\red255\\green0\\blue255;\\red255\\green0\\blue0;\\red255\\green255\\blue0;\\red255\\green255\\blue255;"
"\\red0\\green0\\blue128;\\red0\\green128\\blue128;\\red0\\green128\\blue0;\r\n"
"\\red128\\green0\\blue128;\\red128\\green0\\blue0;\\red128\\green128\\blue0;\\red128\\green128\\blue128;"
"\\red192\\green192\\blue192;\\red64\\green64\\blue64;\\red255\\green153\\blue0;}\r\n";
static const char* rtfHdrEnd =
"\\viewkind4\\uc1\\pard\\f0\\fs20 ";
if (fUseRTF) {
BufPrintf("%s", rtfHdrStart);
if ((flags & kRTFFlagColorTable) != 0)
BufPrintf("%s", rtfColorTable);
BufPrintf("%s", rtfHdrEnd);
}
fPointSize = 10;
}
/*
* Output the RTF footer.
*/
void ReformatText::RTFEnd(void)
{
if (fUseRTF) BufPrintf("}\r\n%c", '\0');
}
/*
* Output RTF paragraph definition marker. Do this every time we change some
* aspect of paragraph formatting, such as margins or justification.
*/
void ReformatText::RTFSetPara(void)
{
if (!fUseRTF)
return;
BufPrintf("\\pard\\nowidctlpar");
if (fLeftMargin != 0 || fRightMargin != 0) {
/* looks like RTF thinks we're getting 12 chars per inch? */
if (fLeftMargin != 0)
BufPrintf("\\li%d",
(int) (fLeftMargin * (kRTFUnitsPerInch/12)));
if (fLeftMargin != 0)
BufPrintf("\\ri%d",
(int) (fRightMargin * (kRTFUnitsPerInch/12)));
}
switch (fJustified) {
case kJustifyLeft: break;
case kJustifyRight: BufPrintf("\\qr"); break;
case kJustifyCenter: BufPrintf("\\qc"); break;
case kJustifyFull: BufPrintf("\\qj"); break;
default:
assert(false);
break;
}
// Ideally we'd suppress this if the next thing is an RTF
// formatting command, esp. "\\par".
BufPrintf(" ");
}
/*
* Output a new paragraph marker.
*
* If you're producing RTF output, this is the right way to output an
* end-of-line character.
*/
void ReformatText::RTFNewPara(void)
{
if (fUseRTF)
BufPrintf("\\par\r\n");
else
BufPrintf("\r\n");
}
/*
* Insert a page break. This isn't supported by the Rich Edit control,
* so it won't appear in CiderPress or WordPad, but it will come out in
* Microsoft Word if you extract to a file.
*/
void ReformatText::RTFPageBreak(void)
{
if (fUseRTF)
BufPrintf("\\page ");
}
/*
* RTF tab character.
*/
void ReformatText::RTFTab(void)
{
if (fUseRTF)
BufPrintf("\\tab ");
}
/*
* Minor formatting.
*/
void ReformatText::RTFBoldOn(void)
{
if (fBoldEnabled)
return;
if (fUseRTF) {
BufPrintf("\\b ");
fBoldEnabled = true;
}
}
void ReformatText::RTFBoldOff(void)
{
if (!fBoldEnabled)
return;
if (fUseRTF) {
BufPrintf("\\b0 ");
fBoldEnabled = false;
}
}
void ReformatText::RTFItalicOn(void)
{
if (fItalicEnabled)
return;
if (fUseRTF) {
BufPrintf("\\i ");
fItalicEnabled = true;
}
}
void ReformatText::RTFItalicOff(void)
{
if (!fItalicEnabled)
return;
if (fUseRTF) {
BufPrintf("\\i0 ");
fItalicEnabled = false;
}
}
void ReformatText::RTFUnderlineOn(void)
{
if (fUnderlineEnabled)
return;
if (fUseRTF) {
BufPrintf("\\ul ");
fUnderlineEnabled = true;
}
}
void ReformatText::RTFUnderlineOff(void)
{
if (!fUnderlineEnabled)
return;
if (fUseRTF) {
BufPrintf("\\ulnone ");
fUnderlineEnabled = false;
}
}
void ReformatText::RTFInverseOn(void)
{
if (fInverseEnabled)
return;
if (fUseRTF) {
RTFSetColor(TextColor::kColorWhite);
BufPrintf("\\highlight1 "); // black
fInverseEnabled = true;
}
}
void ReformatText::RTFInverseOff(void)
{
if (!fInverseEnabled)
return;
if (fUseRTF) {
RTFSetColor(TextColor::kColorNone);
BufPrintf("\\highlight0 ");
fInverseEnabled = false;
}
}
void ReformatText::RTFOutlineOn(void)
{
if (fOutlineEnabled)
return;
if (fUseRTF) {
BufPrintf("\\outl ");
fOutlineEnabled = true;
}
}
void ReformatText::RTFOutlineOff(void)
{
if (!fOutlineEnabled)
return;
if (fUseRTF) {
BufPrintf("\\outl0 ");
fOutlineEnabled = false;
}
}
void ReformatText::RTFShadowOn(void)
{
if (fShadowEnabled)
return;
if (fUseRTF) {
BufPrintf("\\shad ");
fShadowEnabled = true;
}
}
void ReformatText::RTFShadowOff(void)
{
if (!fShadowEnabled)
return;
if (fUseRTF) {
BufPrintf("\\shad0 ");
fShadowEnabled = false;
}
}
void ReformatText::RTFSubscriptOn(void)
{
if (fSubscriptEnabled)
return;
if (fUseRTF) {
BufPrintf("\\sub ");
fSubscriptEnabled = true;
}
}
void ReformatText::RTFSubscriptOff(void)
{
if (!fSubscriptEnabled)
return;
if (fUseRTF) {
BufPrintf("\\nosupersub ");
fSubscriptEnabled = false;
}
}
void ReformatText::RTFSuperscriptOn(void)
{
if (fSuperscriptEnabled)
return;
if (fUseRTF) {
BufPrintf("\\super ");
fSuperscriptEnabled = true;
}
}
void ReformatText::RTFSuperscriptOff(void)
{
if (!fSuperscriptEnabled)
return;
if (fUseRTF) {
BufPrintf("\\nosupersub ");
fSuperscriptEnabled = false;
}
}
ReformatText::TextColor ReformatText::RTFSetColor(TextColor color)
{
TextColor oldColor = fTextColor;
if (color != fTextColor && fUseRTF) {
BufPrintf("\\cf%d ", color);
fTextColor = color;
}
return oldColor;
}
/*
* Change paragraph formatting.
*/
void ReformatText::RTFParaLeft(void)
{
if (fJustified != kJustifyLeft) {
fJustified = kJustifyLeft;
RTFSetPara();
}
}
void ReformatText::RTFParaRight(void)
{
if (fJustified != kJustifyRight) {
fJustified = kJustifyRight;
RTFSetPara();
}
}
void ReformatText::RTFParaCenter(void)
{
if (fJustified != kJustifyCenter) {
fJustified = kJustifyCenter;
RTFSetPara();
}
}
void ReformatText::RTFParaJustify(void)
{
if (fJustified != kJustifyFull) {
fJustified = kJustifyFull;
RTFSetPara();
}
}
/*
* Page margins, in 1/10th inches.
*/
void ReformatText::RTFLeftMargin(int margin)
{
//LOGI("+++ Left margin now %d", margin);
fLeftMargin = margin;
RTFSetPara();
}
void ReformatText::RTFRightMargin(int margin)
{
//LOGI("+++ Right margin now %d", margin);
fRightMargin = margin;
RTFSetPara();
}
/*
* Switch to a different font size.
*/
void ReformatText::RTFSetFontSize(int points)
{
if (fUseRTF && fPointSize != points)
BufPrintf("\\fs%d ", points * 2);
fPointSize = points;
}
/*
* Switch to a different font.
*/
void ReformatText::RTFSetFont(RTFFont font)
{
if (fUseRTF)
BufPrintf("\\f%d ", font);
}
/*
* Set the font by specifying a IIgs QuickDraw II font family number.
*/
void ReformatText::RTFSetGSFont(uint16_t family)
{
float newMult;
if (!fUseRTF)
return;
/*
* Apple II fonts seem to be about 1.5x in a WYSIWYG way, except
* for Times, which is about 1:1.
*/
switch (family) {
case kGSFontTimes:
RTFSetFont(kFontTimesRoman);
newMult = 0.9f;
break;
case kGSFontNewYork:
RTFSetFont(kFontTimesRoman);
newMult = 1.1f;
break;
case kGSFontSymbol:
RTFSetFont(kFontSymbol);
newMult = 1.0f;
break;
case kGSFontMonaco:
RTFSetFont(kFontCourierNew);
newMult = 0.80f;
break;
case kGSFontCourier:
case kGSFontPCMonospace:
case kGSFontAppleM:
case kGSFontGenesys:
RTFSetFont(kFontCourierNew);
newMult = 1.5f;
break;
case kGSFontClassical:
case kGSFontGenoa:
case kGSFontWestern:
RTFSetFont(kFontArial);
newMult = 0.80f;
break;
case kGSFontChicago:
case kGSFontVenice:
case kGSFontGeneva:
case kGSFontStarfleet:
case kGSFontUnknown1:
case kGSFontUnknown2:
RTFSetFont(kFontArial);
newMult = 1.0f;
break;
case kGSFontLondon:
case kGSFontAthens:
case kGSFontSanFran:
case kGSFontShaston:
case kGSFontToronto:
case kGSFontCairo:
case kGSFontLosAngeles:
case kGSFontHelvetica:
case kGSFontTaliesin:
RTFSetFont(kFontArial);
newMult = 1.5f;
break;
default:
LOGI("Unrecognized font family 0x%04x, using Arial", family);
RTFSetFont(kFontArial);
newMult = 1.0f;
break;
}
if (newMult != fGSFontSizeMult) {
fGSFontSizeMult = newMult;
RTFSetGSFontSize(fPreMultPointSize);
}
}
/*
* Set the font size of a IIgs font. We factor the size multiplier in.
*
* BUG: we should track the state of the "underline" mode, and turn it
* on and off based on the font size (8-point fonts aren't underlined).
*/
void ReformatText::RTFSetGSFontSize(int points)
{
RTFSetFontSize((int) roundf(points * fGSFontSizeMult));
fPreMultPointSize = points;
}
/*
* Set bold/italic/underline etc.
*
* Note that "Teach" does not show underlining on text that is 8 points
* or smaller. We have to emulate this behavior or some documents, such
* as ModZap's "MZ.Manual", look terrible.
*
* Set the font size before calling here.
*
* Some characters, such as '=' in Shaston 8, look the same in
* bold as they do in plain. This doesn't hold true for Windows
* fonts, so we're going to look different in some circumstances.
*/
void ReformatText::RTFSetGSFontStyle(uint8_t qdStyle)
{
if (!fUseRTF)
return;
if ((qdStyle & kQDStyleBold) != 0) {
RTFBoldOn();
} else {
RTFBoldOff();
}
if ((qdStyle & kQDStyleItalic) != 0) {
RTFItalicOn();
} else {
RTFItalicOff();
}
if ((qdStyle & kQDStyleUnderline) != 0 && fPreMultPointSize > 8) {
RTFUnderlineOn();
} else {
RTFUnderlineOff();
}
if ((qdStyle & kQDStyleOutline) != 0) {
RTFOutlineOn();
} else {
RTFOutlineOff();
}
if ((qdStyle & kQDStyleShadow) != 0) {
RTFShadowOn();
} else {
RTFShadowOff();
}
if ((qdStyle & kQDStyleSuperscript) != 0) {
RTFSuperscriptOn();
} else {
RTFSuperscriptOff();
}
if ((qdStyle & kQDStyleSubscript) != 0) {
RTFSubscriptOn();
} else {
RTFSubscriptOff();
}
}
#if 0
void
ReformatText::RTFProportionalOn(void) {
if (fUseRTF)
BufPrintf("\\f%d ", kFontTimesRoman);
}
void
ReformatText::RTFProportionalOff(void) {
if (fUseRTF)
BufPrintf("\\f%d ", kFontCourierNew);
}
#endif
/*
* Convert the EOL markers in a buffer. The output is written to the work
* buffer. The input buffer may be CR, LF, or CRLF.
*
* If "stripHiBits" is set, the high bit of each character is cleared before
* the value is considered.
*/
void ReformatText::ConvertEOL(const uint8_t* srcBuf, long srcLen,
bool stripHiBits)
{
/* Compatibility - assume we're not stripping nulls */
ConvertEOL(srcBuf, srcLen, stripHiBits, false);
}
/*
* Convert the EOL markers in a buffer. The output is written to the work
* buffer. The input buffer may be CR, LF, or CRLF.
*
* If "stripHiBits" is set, the high bit of each character is cleared before
* the value is considered.
*
* If "stripNulls" is true, no null values will make it through.
*/
void ReformatText::ConvertEOL(const uint8_t* srcBuf, long srcLen,
bool stripHiBits, bool stripNulls)
{
uint8_t ch;
int mask;
assert(!fUseRTF); // else we have to use RTFPrintChar
if (stripHiBits)
mask = 0x7f;
else
mask = 0xff;
/*
* Could probably speed this up by taking things a line at a time,
* but this is fast enough and much more straightforward.
*/
while (srcLen) {
ch = (*srcBuf++) & mask;
srcLen--;
if (ch == '\r') {
/* got CR, check for CRLF */
if (srcLen != 0 && ((*srcBuf) & mask) == '\n') {
srcBuf++;
srcLen--;
}
BufPrintf("\r\n");
} else if (ch == '\n') {
BufPrintf("\r\n");
} else {
/* Strip out null bytes if requested */
if ((stripNulls && ch != 0x00) || !stripNulls)
BufPrintf("%c", ch);
}
}
}
/*
* Write a hex dump into the buffer.
*/
void ReformatText::BufHexDump(const uint8_t* srcBuf, long srcLen)
{
const uint8_t* origSrcBuf = srcBuf;
char chBuf[17];
int i, remLen;
ASSERT(srcBuf != NULL);
ASSERT(srcLen >= 0);
chBuf[16] = '\0';
while (srcLen > 0) {
BufPrintf("%08lx: ", srcBuf - origSrcBuf);
if (srcLen >= 16) {
if (!fUseRTF) {
/* the really easy (and relatively fast) way */
BufPrintf("%02x %02x %02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x %02x %02x %02x %02x ",
srcBuf[0], srcBuf[1], srcBuf[2], srcBuf[3],
srcBuf[4], srcBuf[5], srcBuf[6], srcBuf[7],
srcBuf[8], srcBuf[9], srcBuf[10], srcBuf[11],
srcBuf[12], srcBuf[13], srcBuf[14], srcBuf[15]);
} else {
/* the fairly easy (and fairly fast) way */
RTFBoldOn();
BufPrintf("%02x %02x %02x %02x ",
srcBuf[0], srcBuf[1], srcBuf[2], srcBuf[3]);
RTFBoldOff();
BufPrintf("%02x %02x %02x %02x ",
srcBuf[4], srcBuf[5], srcBuf[6], srcBuf[7]);
RTFBoldOn();
BufPrintf("%02x %02x %02x %02x ",
srcBuf[8], srcBuf[9], srcBuf[10], srcBuf[11]);
RTFBoldOff();
BufPrintf("%02x %02x %02x %02x ",
srcBuf[12], srcBuf[13], srcBuf[14], srcBuf[15]);
}
} else {
/* the not-so-easy (and not-so-fast) way */
remLen = srcLen;
for (i = 0; i < remLen; i++) {
if (i == 0 || i == 8)
RTFBoldOn();
else if (i == 4 || i == 12)
RTFBoldOff();
BufPrintf("%02x ", srcBuf[i]);
}
RTFBoldOff();
for ( ; i < 16; i++)
BufPrintf(" ");
/* blank out the char buf, since we're only filling part in */
for (i = 0; i < 16; i++)
chBuf[i] = ' ';
}
bool hosed = false;
remLen = srcLen;
if (remLen > 16)
remLen = 16;
int i;
for (i = 0; i < remLen; i++) {
chBuf[i] = PrintableChar(srcBuf[i]);
if (fUseRTF &&
(chBuf[i] == '\\' || chBuf[i] == '{' || chBuf[i] == '}'))
{
hosed = true;
break;
}
}
if (!hosed) {
BufPrintf(" %s", chBuf);
} else {
/* escaped chars in RTF mode; have to do this one the hard way */
ASSERT(fUseRTF);
BufPrintf(" ");
for (i = 0; i < remLen; i++) {
RTFPrintChar(srcBuf[i]);
}
}
RTFNewPara();
srcBuf += 16;
srcLen -= 16;
}
}
// Thanks to http://hoop-la.ca/apple2/docs/mousetext/unicode.html for Unicode.
// Thanks to Hugh Hood for extracting the ASCII conversions from SEG.WP.
static const struct {
uint32_t unicode; // UTF-16 single code point or surrogate pair
int8_t ascii; // 7-bit ASCII
} gMouseTextConv[32] = {
{ 0xd83cdf4e, '@' }, // 00 U+1f34e RED APPLE
{ 0xd83cdf4f, '@' }, // 01 U+1f34f GREEN APPLE
//{ 0x00002316, '^' }, // 02 U+2316 POSITION INDICATOR
{ 0x000025c4, '^' }, // 02 U+25c4 BLACK LEFT-POINTING POINTER
{ 0x000023f3, '&' }, // 03 U+23f3 HOURGLASS WITH FLOWING SAND
{ 0x00002713, '\'' }, // 04 U+2713 CHECK MARK
{ 0x00002705, '\'' }, // 05 U+2705 WHITE HEAVY CHECK MARK
{ 0x000023ce, '/' }, // 06 U+23ce RETURN SYMBOL
//{ 0xd83cdf54, ':' }, // 07 U+1f354 HAMBURGER
{ 0x00002630, ':' }, // 07 U+2630 TRIGRAM FOR HEAVEN (actually want 4 lines, not 3)
{ 0x00002190, '<' }, // 08 U+2190 LEFTWARDS ARROW
{ 0x00002026, '_' }, // 09 U+2026 HORIZONTAL ELLIPSIS
{ 0x00002193, 'v' }, // 0a U+2193 DOWNWARDS ARROW
{ 0x00002191, '^' }, // 0b U+2191 UPWARDS ARROW
{ 0x00002594, '-' }, // 0c U+2594 UPPER ONE EIGHTH BLOCK
{ 0x000021b5, '/' }, // 0d U+21b5 DOWNWARDS ARROW WITH CORNER LEFTWARDS
{ 0x00002589, '$' }, // 0e U+2589 LEFT SEVEN EIGHTHS BLOCK
{ 0x000021e4, '{' }, // 0f U+21e4 LEFTWARDS ARROW TO BAR
{ 0x000021e5, '}' }, // 10 U+21e5 RIGHTWARDS ARROW TO BAR
{ 0x00002913, 'v' }, // 11 U+2913 DOWNWARDS ARROW TO BAR
{ 0x00002912, '^' }, // 12 U+2912 UPWARDS ARROW TO BAR
{ 0x00002500, '-' }, // 13 U+2500 BOX DRAWINGS LIGHT HORIZONTAL
{ 0x0000231e, 'L' }, // 14 U+231e BOTTOM LEFT CORNER
{ 0x00002192, '>' }, // 15 U+2192 UPWARDS ARROW TO BAR
//{ 0xd83dde7e, '*' }, // 16 U+1f67e CHECKER BOARD
{ 0x00002591, '*' }, // 16 U+2591 LIGHT SHADE
//{ 0xd83dde7f, '*' }, // 17 U+1f67f REVERSE CHECKER BOARD
{ 0x00002592, '*' }, // 17 U+2592 MEDIUM SHADE
{ 0xd83ddcc1, '[' }, // 18 U+1f4c1 FILE FOLDER
{ 0xd83ddcc2, ']' }, // 19 U+1f4c2 OPEN FILE FOLDER
{ 0x00002595, '|' }, // 1a U+2595 RIGHT ONE EIGHTH BLOCK
{ 0x00002666, '#' }, // 1b U+2666 BLACK DIAMOND SUIT
{ 0x0000203e, '=' }, // 1c U+203e OVERLINE -- wrong, want top/bottom
{ 0x0000256c, '#' }, // 1d U+256c BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
//{ 0xd83ddcbe, 'O' }, // 1e U+1f4be FLOPPY DISK
{ 0x000022a1, 'O' }, // 1e U+22a1 SQUARED DOT OPERATOR (seems better than 25a3)
{ 0x0000258f, '|' }, // 1f U+258f LEFT ONE EIGHTH BLOCK
};
void ReformatText::MouseTextToUTF16(uint8_t mtVal, uint16_t* pLow, uint16_t* pHigh) {
ASSERT(mtVal < 32);
*pLow = gMouseTextConv[mtVal].unicode & 0xffff;
*pHigh = gMouseTextConv[mtVal].unicode >> 16;
}
int8_t ReformatText::MouseTextToASCII(uint8_t mtVal) {
ASSERT(mtVal < 32);
return gMouseTextConv[mtVal].ascii;
}
/*
* ==========================================================================
* ReformatGraphics
* ==========================================================================
*/
/*
* Initialize the Apple II color palette, used for Hi-Res and DHR
* conversions. Could also be used for lo-res mode.
*/
void ReformatGraphics::InitPalette(void)
{
ASSERT(kPaletteSize == 16);
static const RGBQUAD stdPalette[kPaletteSize] = {
/* blue, green, red, reserved */
{ 0x00, 0x00, 0x00 }, // $0 black
{ 0x33, 0x00, 0xdd }, // $1 red (magenta)
{ 0x99, 0x00, 0x00 }, // $2 dark blue
{ 0xdd, 0x22, 0xdd }, // $3 purple (violet)
{ 0x22, 0x77, 0x00 }, // $4 dark green
{ 0x55, 0x55, 0x55 }, // $5 grey1 (dark)
{ 0xff, 0x22, 0x22 }, // $6 medium blue
{ 0xff, 0xaa, 0x66 }, // $7 light blue
{ 0x00, 0x55, 0x88 }, // $8 brown
{ 0x00, 0x66, 0xff }, // $9 orange
{ 0xaa, 0xaa, 0xaa }, // $A grey2 (light)
{ 0x88, 0x99, 0xff }, // $B pink
{ 0x00, 0xdd, 0x11 }, // $C green (a/k/a light green)
{ 0x00, 0xff, 0xff }, // $D yellow
{ 0x99, 0xff, 0x44 }, // $E aqua
{ 0xff, 0xff, 0xff }, // $F white
};
memcpy(fPalette, stdPalette, sizeof(fPalette));
}
/*
* Stuff out DIB into the output fields, and set the appropriate flags.
*/
void ReformatGraphics::SetResultBuffer(ReformatOutput* pOutput, MyDIBitmap* pDib)
{
ASSERT(pOutput != NULL);
ASSERT(pDib != NULL);
pOutput->SetOutputKind(ReformatOutput::kOutputBitmap);
pOutput->SetDIB(pDib);
}
/*
* Unpack the Apple PackBytes format.
*
* Format is:
* <flag><data> ...
*
* Flag values (first 6 bits of flag byte):
* 00xxxxxx: (0-63) 1 to 64 bytes follow, all different
* 01xxxxxx: (0-63) 1 to 64 repeats of next byte
* 10xxxxxx: (0-63) 1 to 64 repeats of next 4 bytes
* 11xxxxxx: (0-63) 1 to 64 repeats of next byte taken as 4 bytes
* (as in 10xxxxxx case)
*
* Pass the destination buffer in "dst", source buffer in "src", source
* length in "srcLen", and expected sizes of output in "dstRem".
*
* Returns the number of bytes unpacked on success, negative if the buffer is
* overfilled.
*/
int ReformatGraphics::UnpackBytes(uint8_t* dst, const uint8_t* src,
long dstRem, long srcLen)
{
const uint8_t* origDst = dst;
while (srcLen > 0) {
uint8_t flag = *src++;
int count = (flag & 0x3f) +1;
uint8_t val;
uint8_t valSet[4];
int i;
srcLen--;
switch (flag & 0xc0) {
case 0x00:
for (i = 0; i < count; i++) {
if (srcLen == 0 || dstRem == 0) {
LOGI(" SHR unpack overrun1 (srcLen=%ld dstRem=%ld)",
srcLen, dstRem);
return -1;
}
*dst++ = *src++;
srcLen--;
dstRem--;
}
break;
case 0x40:
//if (count != 3 || count != 5 || count != 6 || count != 7) {
// LOGI(" SHR unpack funky len %d?", count);
//}
if (srcLen == 0) {
LOGI(" SHR unpack underrun2");
return -1;
}
val = *src++;
srcLen--;
for (i = 0; i < count; i++) {
if (dstRem == 0) {
LOGI(" SHR unpack overrun2 (srcLen=%d, i=%d of %d)",
srcLen, i, count);
return -1;
}
*dst++ = val;
dstRem--;
}
break;
case 0x80:
if (srcLen < 4) {
LOGI(" SHR unpack underrun3");
return -1;
}
valSet[0] = *src++;
valSet[1] = *src++;
valSet[2] = *src++;
valSet[3] = *src++;
srcLen -= 4;
for (i = 0; i < count; i++) {
if (dstRem < 4) {
LOGI(" SHR unpack overrun3 (srcLen=%ld dstRem=%ld)",
srcLen, dstRem);
return -1;
}
*dst++ = valSet[0];
*dst++ = valSet[1];
*dst++ = valSet[2];
*dst++ = valSet[3];
dstRem -= 4;
}
break;
case 0xc0:
if (srcLen == 0) {
LOGI(" SHR unpack underrun4");
return -1;
}
val = *src++;
srcLen--;
for (i = 0; i < count; i++) {
if (dstRem < 4) {
LOGI(" SHR unpack overrun4 (srcLen=%ld dstRem=%ld count=%d)",
srcLen, dstRem, count);
return -1;
}
*dst++ = val;
*dst++ = val;
*dst++ = val;
*dst++ = val;
dstRem -= 4;
}
break;
default:
ASSERT(false);
break;
}
}
ASSERT(srcLen == 0);
if (false) {
/* require that we completely fill the buffer */
if (dstRem != 0) {
LOGI(" SHR unpack dstRem at %d", dstRem);
return -1;
}
}
return dst - origDst;
}
/*
* Unpack Macintosh PackBits format. See Technical Note TN1023.
*
* Read a byte.
* If the high bit is set, count is 2s complement +1 (i.e. count = (-byte)+1).
* Read the next byte, then write that byte 'count' times.
* If the high bit is clear, count is 1+value (i.e. count = byte+1). Read and
* copy that many bytes.
* After "destLen" bytes have been written, return (even if in the middle of
* a run).
*
* NOTE: if the count byte is 0x80, Apple says it's an invalid value and
* should be skipped over. Use the following byte as the count byte. This
* is probably because PackBits is only supposed to crunch 127 bytes, though
* that suggests 0x81 and 0x7f are also impossible.
*
* We have to watch for underruns on the input and overruns on the output.
*/
void ReformatGraphics::UnPackBits(const uint8_t** pSrcBuf, long* pSrcLen,
uint8_t** pOutPtr, long dstLen, uint8_t xorVal)
{
const uint8_t* srcBuf = *pSrcBuf;
long length = *pSrcLen;
uint8_t* outPtr = *pOutPtr;
int pixByte = 0;
while (pixByte < dstLen && length > 0) {
uint8_t countByte;
int count;
countByte = *srcBuf++;
length--;
if (countByte & 0x80) {
/* RLE string */
uint8_t ch;
count = (countByte ^ 0xff)+1 +1;
ch = *srcBuf++;
length--;
while (count-- && pixByte < dstLen) {
*outPtr++ = ch ^ xorVal;
pixByte++;
}
} else {
/* series of bytes */
count = countByte +1;
while (count && pixByte < dstLen && length > 0) {
*outPtr++ = *srcBuf++ ^ xorVal;
count--;
length--;
pixByte++;
}
}
}
if (pixByte != 72) {
/* can happen if we run out of input early */
LOGI(" MP unexpected pixByte=%d", pixByte);
/* keep going */
}
*pSrcBuf = srcBuf;
*pSrcLen = length;
*pOutPtr = outPtr;
}