diff options
Diffstat (limited to 'SrcShared/EmPixMap.cpp')
-rw-r--r-- | SrcShared/EmPixMap.cpp | 2500 |
1 files changed, 2500 insertions, 0 deletions
diff --git a/SrcShared/EmPixMap.cpp b/SrcShared/EmPixMap.cpp new file mode 100644 index 0000000..5b73c9f --- /dev/null +++ b/SrcShared/EmPixMap.cpp @@ -0,0 +1,2500 @@ +/* -*- mode: C++; tab-width: 4 -*- */ +/* ===================================================================== *\ + Copyright (c) 2000-2001 Palm, Inc. or its subsidiaries. + All rights reserved. + + This file is part of the Palm OS Emulator. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. +\* ===================================================================== */ + +#include "EmCommon.h" +#include "EmPixMap.h" + +#include "Platform.h" // Platform::AllocateMemory + +/* + EmPixMap is a simple class for managing pixmaps (multi-bpp bitmap) + in a cross-platform fashion. + + Poser makes use of pixmaps in the following ways: + + - Converting the LCD frame buffer to something that can be + displayed by the host. + - Converting the JPEG skin images to something that can be + displayed by the host. + - Executing the "Save Screen..." command. + + As such, EmPixMap does not try to be everything to everybody. In + particular, it does not support all possible bitdepths and pixel + formats. In general, it supports 1-, 8-, 24-, and 32-bit buffers, + where the 32-bit buffer is always in ARGB format. While there are + routines that manage the conversion of 1-, 2-, and 4-bpp buffers into + one of the above formats, these are in place solely for converting + the LCD framebuffer into one of the standard EmPixMap formats. + + EmPixMap also has some utility routines that are useful for carrying + some of Poser's facilities. It can flip all of the scanlines in order + to help interoperate with Windows BMPs; it can create an outline region + that can be used to create a host window's border; etc. +*/ + + +static uint32* gConvert1To8; // Used to convert a 4-bit nybble consisting of 4 1-bit pixels + // to a 32-bit value containing 4 8-bit pixels. + +static uint32* gConvert2To8; // Used to convert an 8-bit byte consisting of 4 2-bit pixels + // to a 32-bit value containing 4 8-bit pixels. + +static uint16* gConvert4To8; // Used to convert an 8-bit byte consisting of 2 4-bit pixels + // to a 16-bit value containing 4 8-bit pixels. + +struct ScanlineParms +{ + uint8* fDestScanline; + const uint8* fSrcScanline; + const RGBList* fDestColors; + const RGBList* fSrcColors; + EmCoord fLeft; + EmCoord fRight; +}; + + +// We have scanline converters that can go from bitdepth M to bitdepth N. +// The appropriate converter is chosen and stored in a variable of the +// following type. + +typedef void (*ScanlineConverter) (const ScanlineParms&); + + +static void PrvMakeMask (void* dstPtr, void* srcPtr, long rowBytes, long width, long height); +static void PrvAddToRegion (EmRegion& region, int top, int left, int right); +static EmPixMapDepth PrvGetDepth (EmPixMapFormat); + +#define DECLARE_CONVERTER(src_format, dest_format) \ + static void PrvConvert##src_format##To##dest_format (const ScanlineParms&); + + +// Define FOR_EACH_FORMAT, a macro which iterates over all of +// pixmap formats we support. +// +// !!! Science project...how can I use this macro to iterate +// over *pairs* of formats in a generic way? That is, I want +// be able to say something like: +// +// #define FOR_EACH_FORMAT_PAIR(DO_TO_PAIR) ... +// +// #define DECLARE_CONVERTER(format1, format2) ... +// +// FOR_EACH_FORMAT_PAIR(DECLARE_CONVERTER) +// +// One restriction of this exercise is that I don't want to +// define another macro that explicitly lists the formats. +// That should be limited to FOR_EACH_FORMAT. + +#define FOR_EACH_FORMAT(DO_TO_FORMAT) \ + DO_TO_FORMAT (1) \ + DO_TO_FORMAT (2) \ + DO_TO_FORMAT (4) \ + DO_TO_FORMAT (8) \ + DO_TO_FORMAT (24RGB) \ + DO_TO_FORMAT (24BGR) \ + DO_TO_FORMAT (32ARGB) \ + DO_TO_FORMAT (32ABGR) \ + DO_TO_FORMAT (32RGBA) \ + DO_TO_FORMAT (32BGRA) + + + +// Define FOR_EACH_FORMAT_PAIR, which iterates over all of the src/dest +// format pairings. We do this with a helper macro (FOR_EACH_FORMAT_PAIR_0) +// that allows us to multiply all the pairings together. + +#define FOR_EACH_FORMAT_PAIR_0(DO_TO_PAIR, fmt) \ + DO_TO_PAIR (fmt, 1) \ + DO_TO_PAIR (fmt, 2) \ + DO_TO_PAIR (fmt, 4) \ + DO_TO_PAIR (fmt, 8) \ + DO_TO_PAIR (fmt, 24RGB) \ + DO_TO_PAIR (fmt, 24BGR) \ + DO_TO_PAIR (fmt, 32ARGB) \ + DO_TO_PAIR (fmt, 32ABGR) \ + DO_TO_PAIR (fmt, 32RGBA) \ + DO_TO_PAIR (fmt, 32BGRA) + + +#define FOR_EACH_FORMAT_PAIR(DO_TO_PAIR) \ + FOR_EACH_FORMAT_PAIR_0 (DO_TO_PAIR, 1) \ + FOR_EACH_FORMAT_PAIR_0 (DO_TO_PAIR, 2) \ + FOR_EACH_FORMAT_PAIR_0 (DO_TO_PAIR, 4) \ + FOR_EACH_FORMAT_PAIR_0 (DO_TO_PAIR, 8) \ + FOR_EACH_FORMAT_PAIR_0 (DO_TO_PAIR, 24RGB) \ + FOR_EACH_FORMAT_PAIR_0 (DO_TO_PAIR, 24BGR) \ + FOR_EACH_FORMAT_PAIR_0 (DO_TO_PAIR, 32ARGB) \ + FOR_EACH_FORMAT_PAIR_0 (DO_TO_PAIR, 32ABGR) \ + FOR_EACH_FORMAT_PAIR_0 (DO_TO_PAIR, 32RGBA) \ + FOR_EACH_FORMAT_PAIR_0 (DO_TO_PAIR, 32BGRA) + + +FOR_EACH_FORMAT_PAIR (DECLARE_CONVERTER) + + +// Macros used for accessing and converting/copying pixels. + +#define GET_24RGB(srcPtr, r, g, b, a) \ + r = *srcPtr++; \ + g = *srcPtr++; \ + b = *srcPtr++; \ + a = 0; + + +#define GET_24BGR(srcPtr, r, g, b, a) \ + b = *srcPtr++; \ + g = *srcPtr++; \ + r = *srcPtr++; \ + a = 0; + + +#define GET_32ARGB(srcPtr, r, g, b, a) \ + a = *srcPtr++; \ + r = *srcPtr++; \ + g = *srcPtr++; \ + b = *srcPtr++; + + +#define GET_32ABGR(srcPtr, r, g, b, a) \ + a = *srcPtr++; \ + b = *srcPtr++; \ + g = *srcPtr++; \ + r = *srcPtr++; + + +#define GET_32RGBA(srcPtr, r, g, b, a) \ + r = *srcPtr++; \ + g = *srcPtr++; \ + b = *srcPtr++; \ + a = *srcPtr++; + + +#define GET_32BGRA(srcPtr, r, g, b, a) \ + b = *srcPtr++; \ + g = *srcPtr++; \ + r = *srcPtr++; \ + a = *srcPtr++; + + +#define PUT_24RGB(destPtr, r, g, b, a) \ + *destPtr++ = r; \ + *destPtr++ = g; \ + *destPtr++ = b; + + +#define PUT_24BGR(destPtr, r, g, b, a) \ + *destPtr++ = b; \ + *destPtr++ = g; \ + *destPtr++ = r; + + +#define PUT_32ARGB(destPtr, r, g, b, a) \ + *destPtr++ = a; \ + *destPtr++ = r; \ + *destPtr++ = g; \ + *destPtr++ = b; + + +#define PUT_32ABGR(destPtr, r, g, b, a) \ + *destPtr++ = a; \ + *destPtr++ = b; \ + *destPtr++ = g; \ + *destPtr++ = r; + + +#define PUT_32RGBA(destPtr, r, g, b, a) \ + *destPtr++ = r; \ + *destPtr++ = g; \ + *destPtr++ = b; \ + *destPtr++ = a; + + +#define PUT_32BGRA(destPtr, r, g, b, a) \ + *destPtr++ = b; \ + *destPtr++ = g; \ + *destPtr++ = r; \ + *destPtr++ = a; + + +#define CONVERT_PACKED_PIXEL(shift, mask, dest_format) \ + if (xx++ >= right) \ + break; \ + { \ + const RGBType& rgb = (*srcColors)[(bits >> (shift)) & (mask)]; \ + PUT_##dest_format(destPtr, rgb.fRed, rgb.fGreen, rgb.fBlue, 0) \ + } + + +#define CONVERT_PACKED_BYTE_1(dest_format) \ + CONVERT_PACKED_PIXEL(7, 0x01, dest_format) \ + CONVERT_PACKED_PIXEL(6, 0x01, dest_format) \ + CONVERT_PACKED_PIXEL(5, 0x01, dest_format) \ + CONVERT_PACKED_PIXEL(4, 0x01, dest_format) \ + CONVERT_PACKED_PIXEL(3, 0x01, dest_format) \ + CONVERT_PACKED_PIXEL(2, 0x01, dest_format) \ + CONVERT_PACKED_PIXEL(1, 0x01, dest_format) \ + CONVERT_PACKED_PIXEL(0, 0x01, dest_format) + + +#define CONVERT_PACKED_BYTE_2(dest_format) \ + CONVERT_PACKED_PIXEL(6, 0x03, dest_format) \ + CONVERT_PACKED_PIXEL(4, 0x03, dest_format) \ + CONVERT_PACKED_PIXEL(2, 0x03, dest_format) \ + CONVERT_PACKED_PIXEL(0, 0x03, dest_format) + + +#define CONVERT_PACKED_BYTE_4(dest_format) \ + CONVERT_PACKED_PIXEL(4, 0x0F, dest_format) \ + CONVERT_PACKED_PIXEL(0, 0x0F, dest_format) + + +#define CONVERT_PACKED_BYTE_8(dest_format) \ + if (xx++ >= right) \ + break; \ + \ + const RGBType& rgb = (*srcColors)[bits]; \ + PUT_##dest_format(destPtr, rgb.fRed, rgb.fGreen, rgb.fBlue, 0) + + +#define STD_NO_CONVERT(BPP) \ + long numBytes = ((parms.fRight * BPP) + 7) / 8; \ + memcpy (parms.fDestScanline, parms.fSrcScanline, numBytes); + + +#define STD_DIRECT_CONVERT(src_format, dest_format) \ + EmCoord right = parms.fRight; \ + uint8* destPtr = parms.fDestScanline; \ + const uint8* srcPtr = parms.fSrcScanline; \ + \ + for (EmCoord xx = 0; xx < right; ++xx) \ + { \ + uint8 r, g, b, a; \ + GET_##src_format(srcPtr, r, g, b, a) \ + PUT_##dest_format(destPtr, r, g, b, a) \ + } + + +#define STD_INDEX_TO_DIRECT_CONVERT(src_format, dest_format) \ + EmCoord right = parms.fRight; \ + uint8* destPtr = parms.fDestScanline; \ + const uint8* srcPtr = parms.fSrcScanline; \ + const RGBList* srcColors = parms.fSrcColors; \ + \ + EmAssert (srcColors); \ + \ + EmCoord xx = 0; \ + while (1) \ + { \ + uint8 bits = *srcPtr++; \ + CONVERT_PACKED_BYTE_##src_format(dest_format) \ + } + + +#define STD_DIRECT_TO_1_CONVERT(src_format) \ + EmCoord right = parms.fRight; \ + uint8* destPtr = parms.fDestScanline; \ + const uint8* srcPtr = parms.fSrcScanline; \ + \ + uint8 bitMask = 0x80; \ + uint8 aByte = 0; \ + \ + for (EmCoord xx = 0; xx < right; ++xx) \ + { \ + uint8 r, g, b, a; \ + GET_##src_format(srcPtr, r, g, b, a) \ + \ + Bool isDark1 = r < 0xC0; \ + Bool isDark2 = g < 0xC0; \ + Bool isDark3 = b < 0xC0; \ + \ + if (isDark1 || isDark2 || isDark3) \ + { \ + /* Assumes white/black color table! */ \ + aByte |= bitMask; \ + } \ + \ + bitMask >>= 1; \ + if (bitMask == 0) \ + { \ + *destPtr++ = aByte; \ + bitMask = 0x80; \ + aByte = 0; \ + } \ + } \ + \ + /* Write out any partially filled out byte. */ \ + \ + if (bitMask != 0) \ + { \ + *destPtr++ = aByte; \ + } + + +/*********************************************************************** + * + * FUNCTION: EmPixMap constructor + * + * DESCRIPTION: Intialize data members. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +EmPixMap::EmPixMap (void) : + fSize (0, 0), + fFormat (kPixMapFormat1), + fRowBytes (0), + fColors (), + fPixels (NULL), + fPixelsOwned (true) +{ +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap copy constructor + * + * DESCRIPTION: Create a new EmPixMap that is a clone of "other". + * + * PARAMETERS: other - pixmap to make a copy of. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +EmPixMap::EmPixMap (const EmPixMap& other) : + fSize (other.fSize), + fFormat (other.fFormat), + fRowBytes (other.fRowBytes), + fColors (other.fColors), + fPixels (NULL), + fPixelsOwned (true) +{ + this->CopyPixelBuffer (other); +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap destructor + * + * DESCRIPTION: Reclaim resources. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +EmPixMap::~EmPixMap (void) +{ + if (fPixelsOwned) + { + Platform::DisposeMemory (fPixels); + } +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap assignment operator + * + * DESCRIPTION: Make "*this" look like "other". + * + * PARAMETERS: other - source pixmap. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +EmPixMap& +EmPixMap::operator= (const EmPixMap& other) +{ + if (&other != this) + { + fSize = other.fSize; + fFormat = other.fFormat; + fRowBytes = other.fRowBytes; + fColors = other.fColors; + + this->CopyPixelBuffer (other); + } + + return *this; +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::GetSize + * + * DESCRIPTION: Return the size of the image. + * + * PARAMETERS: None. + * + * RETURNED: EmPoint containing the image size. + * + ***********************************************************************/ + +EmPoint +EmPixMap::GetSize (void) const +{ + return fSize; +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::SetSize + * + * DESCRIPTION: Set the size of the image. Any old image is discarded + * and a new buffer is allocated. + * + * PARAMETERS: p - new image size. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void +EmPixMap::SetSize (const EmPoint& p) +{ + if (fSize != p) + { + fSize = p; + fRowBytes = this->DetermineRowBytes (); + this->InvalidateBuffer (); + } +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::GetDepth + * + * DESCRIPTION: Return the image depth (bits per pixel). + * + * PARAMETERS: None. + * + * RETURNED: The image depth. + * + ***********************************************************************/ + +EmPixMapDepth +EmPixMap::GetDepth (void) const +{ + return ::PrvGetDepth (fFormat); +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::GetFormat + * + * DESCRIPTION: Return the format of the pixels. + * + * PARAMETERS: None. + * + * RETURNED: The pixel format. + * + ***********************************************************************/ + +EmPixMapFormat +EmPixMap::GetFormat (void) const +{ + return fFormat; +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::SetFormat + * + * DESCRIPTION: Set the bitdepth of the image. Any old image is + * discarded and a new buffer is allocated. + * + * PARAMETERS: d - new bith depth. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void +EmPixMap::SetFormat (EmPixMapFormat f) +{ + EmPixMapDepth oldDepth = this->GetDepth (); + + fFormat = f; + + if (oldDepth != this->GetDepth ()) + { + fRowBytes = this->DetermineRowBytes (); + this->InvalidateBuffer (); + } +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::GetRowBytes + * + * DESCRIPTION: Return the number of bytes in a scanline. + * + * PARAMETERS: None. + * + * RETURNED: The number of bytes in a scanline. + * + ***********************************************************************/ + +EmPixMapRowBytes +EmPixMap::GetRowBytes (void) const +{ + return fRowBytes; +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::SetRowBytes + * + * DESCRIPTION: Set the number of bytes in a scanline. Any old image is + * discarded and a new buffer is allocated. In general, + * you should not have to call this function; a rowbytes + * value is determined based on the width and depth of the + * image. You should call SetRowBytes only under + * exceptional circumstances to override the default. + * + * PARAMETERS: b - new rowbytes value. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void +EmPixMap::SetRowBytes (EmPixMapRowBytes b) +{ + if (fRowBytes != b) + { + fRowBytes = b; + this->InvalidateBuffer (); + } +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::GetColorTable + * + * DESCRIPTION: Return a *CONST REFERENCE* to the color table used by + * the image. The color table will be empty for direct + * pixmaps (where the depth is > 8). In general, the + * number of entries in the table will be (1 << depth), + * but nothing in EmPixMap itself relies on this. + * + * PARAMETERS: None. + * + * RETURNED: Reference to the color table. + * + ***********************************************************************/ + +const RGBList& +EmPixMap::GetColorTable (void) const +{ + return fColors; +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::SetColorTable + * + * DESCRIPTION: Set the color table to be used for indexed pixmaps + * (where the depth is <= 8). It is up to the caller to + * make sure enough entries are supplied; EmPixMap assumes + * that there are. Also, no color mapping is performed + * (that is, no effort is made to change the pixels in the + * buffer such that they end up pointing to the same or + * similar RGB values). + * + * PARAMETERS: c - the new color table. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void +EmPixMap::SetColorTable (const RGBList& c) +{ + fColors = c; +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::GetBits + * + * DESCRIPTION: Return a pointer to the pixel buffer. + * + * PARAMETERS: None. + * + * RETURNED: Pointer to the pixel buffer. + * + ***********************************************************************/ + +const void* +EmPixMap::GetBits (void) const +{ + this->UpdateBuffer (); + return &fPixels[0]; +} + +void* +EmPixMap::GetBits (void) +{ + this->UpdateBuffer (); + return &fPixels[0]; +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::SetBits + * + * DESCRIPTION: Set the pixels to be used by the pixmap. Note that the + * pixels are NOT OWNED, nor are they copied! This + * function is used to wrap up EXISTING pixels from some + * other source (such as a host pixmap). + * + * PARAMETERS: bits - bits to adopt. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void +EmPixMap::SetBits (void* bits) +{ + this->InvalidateBuffer (); + fPixels = (uint8*) bits; + fPixelsOwned = false; +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::CreateMask + * + * DESCRIPTION: Change dest to be a 1-bpp pixmap containing a mask for + * the "this" pixmap. The mask will contain black pixels + * for the parts inside the mask, and white pixels for the + * parts outside the mask. The mask created is similar to + * that used by the Macintosh Finder to define the boundary + * of an icon. + * + * PARAMETERS: dest - pixmap that will receive the mask. The caller + * does not need to prepare this pixmap in any way; it + * gets completely reinitialized by this function. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void EmPixMap::CreateMask (EmPixMap& dest) const +{ + // Create a simple black & white color table. + + RGBList colors; + colors.push_back (RGBType (255, 255, 255)); // 0 = white + colors.push_back (RGBType (0, 0, 0)); // 1 = black + + // Convert (*this) into a 1-bpp bitmap, putting the result in src. + + EmPixMap src; + src.SetSize (this->GetSize ()); + src.SetFormat (kPixMapFormat1); + src.SetColorTable (colors); + + EmRect rect (EmPoint (0, 0), this->GetSize()); + + EmPixMap::CopyRect (src, *this, rect, rect); + + // Initialize the destination bitmap. + + dest.SetSize (this->GetSize ()); + dest.SetFormat (kPixMapFormat1); + dest.SetColorTable (colors); + + // Call low-level routine to create the mask. + + ::PrvMakeMask (dest.GetBits (), src.GetBits (), src.GetRowBytes (), + src.GetSize().fX, src.GetSize().fY); +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::CreateRegion + * + * DESCRIPTION: Scan-convert a 1-bpp bitmap into a region. Zero bits + * are outside the region and one bits are inside it. + * + * PARAMETERS: None. + * + * RETURNED: The bitmap converted into a region. + * + ***********************************************************************/ + +EmRegion +EmPixMap::CreateRegion (void) const +{ + EmRegion region; + + uint8* bits = (uint8*) this->GetBits (); + EmCoord width = this->GetSize().fX; + EmCoord height = this->GetSize().fY; + EmPixMapRowBytes rowBytes = this->GetRowBytes (); + + for (EmCoord yy = 0; yy < height; ++yy) + { + Bool wasInRegion = false; + long xStart = 0; + long xEnd = 0; + + uint8* cBits = bits + yy * rowBytes; + + uint8 aByte = 0; + uint8 bitMask = 0; + + for (long xx = 0; xx < width; ++xx, bitMask >>= 1) + { + if (bitMask == 0) + { + bitMask = 0x80; + aByte = *cBits++; + } + + Bool nowInRegion = (aByte & bitMask) != 0; + + // Entering a region? + + if (nowInRegion && !wasInRegion) + { + // Start new "in region" scan + + xStart = xx; + } + + // Exiting a region? + + else if (!nowInRegion && wasInRegion) + { + // Close off "in region" scan. + + xEnd = xx; + ::PrvAddToRegion (region, yy, xStart, xEnd); + } + + wasInRegion = nowInRegion; + } + + // End of scan line. Check to see if the region scan is still + // open. If so, close it. + + if (wasInRegion) + { + xEnd = width; + ::PrvAddToRegion (region, yy, xStart, xEnd); + } + } + + return region; +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::ChangeTone + * + * DESCRIPTION: Adjust the pixmap in the given line range by the given + * percentage. + * + * PARAMETERS: amount - amount by which the image should be adjusted. + * 100 = White + * 1...99 = Lighten + * 0 = No change + * -1...-99 = Darken + * -100 = Black + * + * firstLine - first scanline in the image to alter. + * + * lastLine - last scanline in the image to alter plus 1. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +inline void PrvAdjustPixel_ChangeTone (uint8& r, uint8& g, uint8& b, int32 amount) +{ + if (amount >= 0) + { + // Lighten the value. + // + // If amount == 0, rhs == 0, leaving value unchanged. + // + // If amount == 255, rhs == 255 - (value * 255 / 255), + // == 255 - value, leaving value at 255. + + r += (amount - (r * amount / 255)); + g += (amount - (g * amount / 255)); + b += (amount - (b * amount / 255)); + } + else + { + // Darken the value. + // + // If amount == 0, rhs == 0, leaving value unchanged. + // + // If amount == -255, rhs == value * -255 / 255 == -value, leaving value at zero. + + r += (r * amount / 255); + g += (g * amount / 255); + b += (b * amount / 255); + } +} + + +void +EmPixMap::ChangeTone (int32 percent, EmCoord firstLine, EmCoord lastLine) +{ + EmAssert (percent >= -100); + EmAssert (percent <= 100); + + EmPixMapDepth depth = this->GetDepth (); + EmPixMapFormat format = this->GetFormat (); + int32 amount = (percent * 255) / 100; + + if (firstLine == -1) + firstLine = 0; + + if (lastLine == -1) + lastLine = this->GetSize ().fY; + + // If the image has a color table, alter that. + + if (depth <= 8) + { + RGBList::iterator iter = fColors.begin (); + while (iter != fColors.end ()) + { + // Munge the RGB values directly. You might be tempted to + // convert the RGB values to HSV values, alter the V + // parameter, and then convert back. However, that gives + // pretty much the same results. + + RGBType& rgb = *iter; + + ::PrvAdjustPixel_ChangeTone (rgb.fRed, rgb.fGreen, rgb.fBlue, amount); + + ++iter; + } + } + + // If the image is direct, alter that. + + else + { + uint8* image = (uint8*) this->GetBits (); + EmPixMapRowBytes rowBytes = this->GetRowBytes (); + EmCoord width = this->GetSize ().fX; + + for (int yy = firstLine; yy < lastLine; ++yy) + { + uint8* scanline = image + rowBytes * yy; + uint8* pixPtr = scanline; + + for (int xx = 0; xx < width; ++xx) + { + uint8 r, g, b, a; + + switch (format) + { + case kPixMapFormat24RGB: GET_24RGB (pixPtr, r, g, b, a); break; + case kPixMapFormat24BGR: GET_24BGR (pixPtr, r, g, b, a); break; + case kPixMapFormat32ARGB: GET_32ARGB (pixPtr, r, g, b, a); break; + case kPixMapFormat32ABGR: GET_32ABGR (pixPtr, r, g, b, a); break; + case kPixMapFormat32RGBA: GET_32RGBA (pixPtr, r, g, b, a); break; + case kPixMapFormat32BGRA: GET_32BGRA (pixPtr, r, g, b, a); break; + default: EmAssert (false); r = g = b = a = 0; + } + + // Munge the RGB values directly. You might be tempted to + // convert the RGB values to HSV values, alter the V + // parameter, and then convert back. However, that gives + // pretty much the same results. + + ::PrvAdjustPixel_ChangeTone (r, g, b, amount); + + switch (format) + { + case kPixMapFormat24RGB: pixPtr -= 3; PUT_24RGB (pixPtr, r, g, b, a); break; + case kPixMapFormat24BGR: pixPtr -= 3; PUT_24BGR (pixPtr, r, g, b, a); break; + case kPixMapFormat32ARGB: pixPtr -= 4; PUT_32ARGB (pixPtr, r, g, b, a); break; + case kPixMapFormat32ABGR: pixPtr -= 4; PUT_32ABGR (pixPtr, r, g, b, a); break; + case kPixMapFormat32RGBA: pixPtr -= 4; PUT_32RGBA (pixPtr, r, g, b, a); break; + case kPixMapFormat32BGRA: pixPtr -= 4; PUT_32BGRA (pixPtr, r, g, b, a); break; + default: EmAssert (false); + } + } + } + } +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::ConvertToColor + * + * DESCRIPTION: Adjust the pixmap in the given line range by the given + * percentage. + * + * PARAMETERS: type - color to which the pixmap should be converted. + * 100 = White + * 1...99 = Lighten + * 0 = No change + * -1...-99 = Darken + * -100 = Black + * + * firstLine - first scanline in the image to alter. + * + * lastLine - last scanline in the image to alter plus 1. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +inline void PrvAdjustPixel_ConvertToColor (uint8& r, uint8& g, uint8& b, int type) +{ + // First convert to grayscale. + + uint8 gray = (uint8) ((r * 0.2990) + (g * 0.5870) + (b * 0.1140)); + + // The assign the 8-bit grayscale value accordingly. + + switch (type) + { + case 0: + r = gray; + g = gray; + b = gray; + break; + + case 1: + r = gray; + g = 0; + b = 0; + break; + + case 2: + r = 0; + g = gray; + b = 0; + break; + + case 3: + r = 0; + g = 0; + b = gray; + break; + } +} + + +void +EmPixMap::ConvertToColor (int type, EmCoord firstLine, EmCoord lastLine) +{ + EmAssert (type >= 0); + EmAssert (type <= 3); + + EmPixMapDepth depth = this->GetDepth (); + EmPixMapFormat format = this->GetFormat (); + + if (firstLine == -1) + firstLine = 0; + + if (lastLine == -1) + lastLine = this->GetSize ().fY; + + // If the image has a color table, alter that. + + if (depth <= 8) + { + RGBList::iterator iter = fColors.begin (); + while (iter != fColors.end ()) + { + // Munge the RGB values directly. + + RGBType& rgb = *iter; + + ::PrvAdjustPixel_ConvertToColor (rgb.fRed, rgb.fGreen, rgb.fBlue, type); + + ++iter; + } + } + + // If the image is direct, alter that. + + else + { + uint8* image = (uint8*) this->GetBits (); + EmPixMapRowBytes rowBytes = this->GetRowBytes (); + EmCoord width = this->GetSize ().fX; + + for (int yy = firstLine; yy < lastLine; ++yy) + { + uint8* scanline = image + rowBytes * yy; + uint8* pixPtr = scanline; + + for (int xx = 0; xx < width; ++xx) + { + uint8 r, g, b, a; + + switch (format) + { + case kPixMapFormat24RGB: GET_24RGB (pixPtr, r, g, b, a); break; + case kPixMapFormat24BGR: GET_24BGR (pixPtr, r, g, b, a); break; + case kPixMapFormat32ARGB: GET_32ARGB (pixPtr, r, g, b, a); break; + case kPixMapFormat32ABGR: GET_32ABGR (pixPtr, r, g, b, a); break; + case kPixMapFormat32RGBA: GET_32RGBA (pixPtr, r, g, b, a); break; + case kPixMapFormat32BGRA: GET_32BGRA (pixPtr, r, g, b, a); break; + default: EmAssert (false); r = g = b = a = 0; + } + + // Munge the RGB values directly. + + ::PrvAdjustPixel_ConvertToColor (r, g, b, type); + + switch (format) + { + case kPixMapFormat24RGB: pixPtr -= 3; PUT_24RGB (pixPtr, r, g, b, a); break; + case kPixMapFormat24BGR: pixPtr -= 3; PUT_24BGR (pixPtr, r, g, b, a); break; + case kPixMapFormat32ARGB: pixPtr -= 4; PUT_32ARGB (pixPtr, r, g, b, a); break; + case kPixMapFormat32ABGR: pixPtr -= 4; PUT_32ABGR (pixPtr, r, g, b, a); break; + case kPixMapFormat32RGBA: pixPtr -= 4; PUT_32RGBA (pixPtr, r, g, b, a); break; + case kPixMapFormat32BGRA: pixPtr -= 4; PUT_32BGRA (pixPtr, r, g, b, a); break; + default: EmAssert (false); + } + } + } + } +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::FlipScanlines + * + * DESCRIPTION: Invert the image in the pixel buffer, swapping all the + * pixels around the x-axis horizontally bisecting the image. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void +EmPixMap::FlipScanlines (void) +{ + long height = fSize.fY; + long rowBytes = fRowBytes; + uint8* topLine = (uint8*) this->GetBits (); + uint8* bottomLine = topLine + (height - 1) * rowBytes; + + void* tempBuffer = Platform::AllocateMemory (rowBytes); + + while (bottomLine > topLine) + { + memcpy (tempBuffer, topLine, rowBytes); + memcpy (topLine, bottomLine, rowBytes); + memcpy (bottomLine, tempBuffer, rowBytes); + + topLine += rowBytes; + bottomLine -= rowBytes; + } + + Platform::DisposeMemory (tempBuffer); +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::ConvertToFormat + * + * DESCRIPTION: Convert "*this" to be a pixmap of the given depth, + * preserving the pixels in the current buffer. + * + * PARAMETERS: destDepth - the new depth of the pixmap. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void +EmPixMap::ConvertToFormat (EmPixMapFormat destFormat) +{ + // Copy our pixels to a new pixmap, converting along the way. + + EmPixMap dest; + dest.SetSize (this->GetSize ()); + dest.SetFormat (destFormat); + dest.SetColorTable (this->GetColorTable ()); + + EmRect rect (EmPoint (0, 0), this->GetSize()); + + EmPixMap::CopyRect (dest, *this, rect, rect); + + // Now copy those newly-converted pixels back to us. + + this->SetFormat (destFormat); + this->CopyPixelBuffer (dest); +} + + +/*********************************************************************** + * + * FUNCTION: CopyRect + * + * DESCRIPTION: Copy pixels from src pixmap to dest pixmap, copying + * from srcRect to destRect, converting and scaling along + * the way. + * + * RESTRICTIONS: Currently, no translation of pixels can + * occur. Additionally, the only scaling allowed + * is scaling up by a factor of two. Finally, no + * depth conversions will take place if the process + * requires an inverse table lookup. + * + * PARAMETERS: dest - pixmap to receive the pixels. + * + * src - pixmap to provide the pixels. + * + * destRect - area within dest to where the pixels will be + * copied. + * + * srcRect - area within src fro where the pixels will be + * copied. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void +EmPixMap::CopyRect (EmPixMap& dest, const EmPixMap& src, + const EmRect& destRect, const EmRect& srcRect) +{ + // Validate the parameters; we don't support everything just yet. + + EmAssert (destRect.fLeft == 0); + EmAssert (srcRect.fLeft == 0); + + EmAssert (destRect.fRight == dest.GetSize().fX); + EmAssert (srcRect.fRight == src.GetSize().fX); + + EmAssert (srcRect.fRight == destRect.fRight || srcRect.fRight * 2 == destRect.fRight); + EmAssert (srcRect.fTop == destRect.fTop || srcRect.fTop * 2 == destRect.fTop); + EmAssert (srcRect.fBottom == destRect.fBottom || srcRect.fBottom * 2 == destRect.fBottom); + + // Gather data common to all conversions. + + EmPixMapRowBytes destRowBytes = dest.GetRowBytes (); + EmPixMapRowBytes srcRowBytes = src.GetRowBytes (); + + EmCoord top = srcRect.fTop; + EmCoord bottom = srcRect.fBottom; + + ScanlineParms parms; + + parms.fDestScanline = ((uint8*) dest.GetBits ()) + top * destRowBytes; + parms.fSrcScanline = ((uint8*) src.GetBits ()) + top * srcRowBytes; + + parms.fDestColors = dest.GetDepth () <= 8 ? &dest.GetColorTable () : NULL; + parms.fSrcColors = src.GetDepth () <= 8 ? &src.GetColorTable () : NULL; + + parms.fLeft = srcRect.fLeft; + parms.fRight = srcRect.fRight; + + + // Determine what scanline converter to use. + + ScanlineConverter conv = NULL; + long srcDestFormat = (((uint32) src.GetFormat ()) << 16) | (uint32) dest.GetFormat (); + + switch (srcDestFormat) + { + +#define CONVERTER_CASE(src_format, dest_format) \ + case (((uint32) kPixMapFormat##src_format) << 16) | (uint32) kPixMapFormat##dest_format: \ + conv = &::PrvConvert##src_format##To##dest_format; \ + break; + + FOR_EACH_FORMAT_PAIR (CONVERTER_CASE) + } + + EmAssert (conv); + + // Create any tables we (might) need. + + ::Get1To8Table (); + ::Get2To8Table (); + ::Get4To8Table (); + + // Do the conversion. + + for (EmCoord yy = top; yy < bottom; ++yy) + { + conv (parms); + + parms.fDestScanline += destRowBytes; + parms.fSrcScanline += srcRowBytes; + } + + if (destRect != srcRect) + { + // Now scale up. The pixels have been copied in 8-, 24- or + // 32-bit format to the upper-left of the dest pixmap. Expand + // that guy both horizontally and vertically. In order to not + // smash the pixels already there (we're converting the dest + // pixmap "in place"), scale from bottom to top and right to left. + + EmPixMapDepth destDepth = dest.GetDepth (); + uint8* destBasePtr = (uint8*) dest.GetBits (); + long halfDestRowBytes = destRowBytes / 2; + uint8* srcLinePtr = destBasePtr + bottom * destRowBytes + halfDestRowBytes; + uint8* destLinePtr = destBasePtr + bottom * destRowBytes * 2 + destRowBytes; + + for (EmCoord yy = top; yy < bottom; ++yy) + { + srcLinePtr -= destRowBytes; + destLinePtr -= destRowBytes + destRowBytes; + + uint8* srcPtr = srcLinePtr; + uint8* destPtr1 = destLinePtr; + uint8* destPtr2 = destPtr1 + destRowBytes; + int xx = halfDestRowBytes; + + if (destDepth == 8) + { + while ((xx -= 1) >= 0) + { + uint8 c1 = *--srcPtr; + + *--destPtr1 = *--destPtr2 = c1; + *--destPtr1 = *--destPtr2 = c1; + } + } + else if (destDepth == 24) + { + while ((xx -= 3) >= 0) + { + uint8 c1 = *--srcPtr; + uint8 c2 = *--srcPtr; + uint8 c3 = *--srcPtr; + + *--destPtr1 = *--destPtr2 = c1; + *--destPtr1 = *--destPtr2 = c2; + *--destPtr1 = *--destPtr2 = c3; + + *--destPtr1 = *--destPtr2 = c1; + *--destPtr1 = *--destPtr2 = c2; + *--destPtr1 = *--destPtr2 = c3; + } + } + else if (destDepth == 32) + { + while ((xx -= 4) >= 0) + { + uint8 c1 = *--srcPtr; + uint8 c2 = *--srcPtr; + uint8 c3 = *--srcPtr; + uint8 c4 = *--srcPtr; + + *--destPtr1 = *--destPtr2 = c1; + *--destPtr1 = *--destPtr2 = c2; + *--destPtr1 = *--destPtr2 = c3; + *--destPtr1 = *--destPtr2 = c4; + + *--destPtr1 = *--destPtr2 = c1; + *--destPtr1 = *--destPtr2 = c2; + *--destPtr1 = *--destPtr2 = c3; + *--destPtr1 = *--destPtr2 = c4; + } + } + } + } +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::DetermineRowBytes + * + * DESCRIPTION: Determine a rowbytes value appropriate for the current + * pixel depth and image width. Currently, the value + * returned is the minimum number of bytes required, + * rounded up to a multiple of four. + * + * PARAMETERS: None. + * + * RETURNED: Suggested row bytes value. + * + ***********************************************************************/ + +EmPixMapRowBytes +EmPixMap::DetermineRowBytes (void) const +{ + // RowBytes rounded up to 32-bit boundaries. + + long width = fSize.fX; + EmPixMapDepth depth = this->GetDepth (); + + return ((width * depth + 31) & ~31) / 8; +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::InvalidateBuffer + * + * DESCRIPTION: Mark the current pixel buffer as invalid. The next time + * the buffer is accessed, it will be regenerated. This + * function is used after SetBounds, SetDepth, or SetRowBytes + * is called. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void +EmPixMap::InvalidateBuffer (void) +{ + if (fPixelsOwned) + { + Platform::DisposeMemory (fPixels); + } +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::UpdateBuffer + * + * DESCRIPTION: Allocate a new image buffer if the old one has been + * marked invalid by InvalidateBuffer. Called by GetBits + * before returning the address of the buffer. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void +EmPixMap::UpdateBuffer (void) const +{ + if (!fPixels) + { + long neededSize = fSize.fY * fRowBytes; + const_cast <EmPixMap*> (this)->fPixels = + (uint8*) Platform::AllocateMemory (neededSize); + } +} + + +/*********************************************************************** + * + * FUNCTION: EmPixMap::CopyPixelBuffer + * + * DESCRIPTION: Utility function to copy the pixels of one pixmap to + * another. Used as a helper function in the copy + * constructor and assignment operator. + * + * PARAMETERS: other - source pixmap. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void +EmPixMap::CopyPixelBuffer (const EmPixMap& other) +{ + this->InvalidateBuffer (); + + memcpy (this->GetBits (), other.GetBits (), fSize.fY * fRowBytes); +} + + +/*********************************************************************** + * + * FUNCTION: Get1To8Table + * + * DESCRIPTION: Return a table that can be used to convert 1-bpp pixels + * into 8-bpp pixels. The table is 16 entries long, and + * so is indexed with a full nybble. The result is a + * 32-bit value, where each of the 4 1-bpp pixels have been + * converted to 4 8-bpp pixels. + * + * Nybbles of the form 0b0000wxyz are converted to: + * + * 0b0000000w0000000x0000000y0000000z + * + * Note that the 32-bit value returned is already + * byteswapped so that it appears correctly in memory when + * stored on Little-Endian architectures. + * + * PARAMETERS: None. + * + * RETURNED: Pointer to the table. + * + ***********************************************************************/ + +uint32* Get1To8Table (void) +{ + if (!gConvert1To8) + { + const int kTableSize = 16; + gConvert1To8 = (uint32*) Platform::AllocateMemory (sizeof (*gConvert1To8) * kTableSize); + + for (int ii = 0; ii < kTableSize; ++ii) + { + uint32 pixels = ((ii & 0x08) << (24 - 3)) | + ((ii & 0x04) << (16 - 2)) | + ((ii & 0x02) << (8 - 1)) | + ((ii & 0x01) << (0 - 0)); + + gConvert1To8[ii] = pixels; + } + } + + return gConvert1To8; +} + + +/*********************************************************************** + * + * FUNCTION: Get2To8Table + * + * DESCRIPTION: Return a table that can be used to convert 1-bpp pixels + * into 8-bpp pixels. The table is 256 entries long, and + * so is indexed with a full byte. The result is a + * 32-bit value, where each of the 4 2-bpp pixels have been + * converted to 4 8-bpp pixels. + * + * Bytes of the form 0bwwxxyyzz are converted to: + * + * 0b000000ww000000xx000000yy000000zz + * + * Note that the 32-bit value returned is already + * byteswapped so that it appears correctly in memory when + * stored on Little-Endian architectures. + * + * PARAMETERS: None. + * + * RETURNED: Pointer to the table. + * + ***********************************************************************/ + +uint32* Get2To8Table (void) +{ + if (!gConvert2To8) + { + const int kTableSize = 256; + gConvert2To8 = (uint32*) Platform::AllocateMemory (sizeof (*gConvert2To8) * kTableSize); + + for (int ii = 0; ii < kTableSize; ++ii) + { + uint32 pixels = ((ii & 0xC0) << (24 - 6)) | + ((ii & 0x30) << (16 - 4)) | + ((ii & 0x0C) << (8 - 2)) | + ((ii & 0x03) << (0 - 0)); + + gConvert2To8[ii] = pixels; + } + } + + return gConvert2To8; +} + + +/*********************************************************************** + * + * FUNCTION: Get4To8Table + * + * DESCRIPTION: Return a table that can be used to convert 4-bpp pixels + * into 8-bpp pixels. The table is 256 entries long, and + * so is indexed with a full byte. The result is a + * 16-bit value, where each of the 2 4-bpp pixels have been + * converted to 2 8-bpp pixels. + * + * Bytes of the form 0bxxxxyyyy are converted to: + * + * 0b000xxxx0000yyyy + * + * Note that the 16-bit value returned is already + * byteswapped so that it appears correctly in memory when + * stored on Little-Endian architectures. + * + * PARAMETERS: None. + * + * RETURNED: Pointer to the table. + * + ***********************************************************************/ + +uint16* Get4To8Table (void) +{ + if (!gConvert4To8) + { + const int kTableSize = 256; + gConvert4To8 = (uint16*) Platform::AllocateMemory (sizeof (*gConvert4To8) * kTableSize); + + for (int ii = 0; ii < kTableSize; ++ii) + { + uint16 pixels = ((ii & 0xF0) << (8 - 4)) | + ((ii & 0x0F) << (0 - 0)); + + gConvert4To8[ii] = pixels; + } + } + + return gConvert4To8; +} + + +/*********************************************************************** + * + * FUNCTION: PrvMakeMask + * + * DESCRIPTION: Calculate a mask for the given 1-bpp image. The mask + * contains 1's inside the image, and 0's outside the + * image (where the image is defined as the closed outer + * boundary of 1 pixels in the source). Thus: + * + * src dest + * ........... ........... + * .....*..... .....*..... + * ....*.*.... ....***.... + * ...*...*... => ...*****... + * ....*.*.... ....***.... + * .....*..... .....*..... + * ........... ........... + * + * ...where .'s are 0's, and *'s are 1's. + * + * The algorithm is interesting. It starts by initializing + * the dest bitmap to all black. It then creates white + * sentinel pixels all around the src bitmap. These + * sentinels are not actually inserted into the bitmap, + * but are logically and effectively there. The sentinels + * are then used to start a process of "smearing", where + * white pixels are smeared left, right, up, and down + * until they reach black pixels in the src. This process + * continues until no changes are made in the destination. + * + * I can't find any reference to this algorithm on the + * net. This implementation is based on an algorithm + * described to me by Jerry(?) Harris while I was at + * Taligent. + * + * PARAMETERS: dstPtr - buffer to hold the generated mask. Caller is + * responsible for making the buffer is large enough. + * + * srcPtr - pointer to the input 1-bpp image. + * + * rowBytes - number of bytes in a row (scanline). + * + * width - width of the source image, in pixels. + * + * height - height of the source image, in pixels. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +static void PrvMakeMask (void* dstPtr, void* srcPtr, long rowBytes, + long /*width*/, long height) +{ + uint8* src = (uint8*) srcPtr; + uint8* dest = (uint8*) dstPtr; + uint8* prev; + uint8* prev0; + uint8 srcVal, destVal, prevDestVal, newDestVal; + long absRowBytes = rowBytes; + long xx, yy; + long valChanged, anyChange; + long pass = 0; + uint8 edge = 0; // Sentry for the left and right edges. + + // Set the destination bitmap to black (1's) + + memset (dest, ~0, rowBytes * height); + + // Allocate a scanline buffer on the stack. This scanline + // acts as a sentry at the top and bottom of the dest buffer. + + prev0 = (uint8*) Platform::AllocateMemory (rowBytes); + memset (prev0, edge, rowBytes); + + do { // Loop while we're still effectively smearing. + ++pass; + prev = prev0; + anyChange = false; + yy = height; + + do { // Loop through all the scanlines. + + // Smear down and to the right. + + prevDestVal = edge; + xx = absRowBytes; + + do { // Loop through all the bytes in this line. + destVal = *dest; + + if (destVal != 0) // If it's not already all-white, let's see if we can mess with it. + { + srcVal = *src; + + // The following code effectively pushes down any 0's from + // the previous line, then adds any 1's from the source. + + destVal = (destVal & *prev) | srcVal; + + do { + // Shift destVal to the right. The upper bit of the + // new value gets filled in with the lower bit of the + // previous destVal. + + newDestVal = (destVal >> 1) | (prevDestVal << 7); + + // AND the shifted destVal with the pre-shifted destVal. + // This has the effect of smearing any 0's to the right. + + newDestVal &= destVal; + + // OR in the corresponding byte from the source. This + // has the effect of stopping the smear when it hits a 1. + + newDestVal |= srcVal; + + // See if the destVal we're working on has changed. If + // so, keep smearing to the right. + + valChanged = (newDestVal != destVal); + if (valChanged) + { + destVal = newDestVal; + } + } while (valChanged); + + // If this destVal has changed, record the fact and write + // out the new value. + + if (*dest != destVal) + { + anyChange = true; + *dest = destVal; + } + } + + // Move to the next bytes in this line. + + ++src; + ++prev; + ++dest; + prevDestVal = destVal; + } while (--xx != 0); + + // Smear down and to the left. Same logic as before. + + prevDestVal = edge; + xx = absRowBytes; + do { + --src; + --prev; + --dest; + destVal = *dest; + + if (destVal != 0) + { + srcVal = *src; + destVal = (destVal & *prev) | srcVal; + + do { + newDestVal = (destVal << 1) | (prevDestVal >> 7); + newDestVal &= destVal; + newDestVal |= srcVal; + valChanged = (newDestVal != destVal); + if (valChanged) + destVal = newDestVal; + } while (valChanged); + + if (*dest != destVal) + { + anyChange = true; + *dest = destVal; + } + } + + prevDestVal = destVal; + } while (--xx != 0); + + // Move to the next scanlines. Our movement is either + // up or down, depending on whether rowBytes is negative + // or not. + + src += rowBytes; + prev = dest; + dest += rowBytes; + } while (--yy != 0); + + // Switch direction, adjust ptrs, and loop until no change. + // Make sure we do at least one up and one down pass, though. + + rowBytes = -rowBytes; + src += rowBytes; + dest += rowBytes; + } while (anyChange || (pass < 2)); + + Platform::DisposeMemory (prev0); +} + + +/*********************************************************************** + * + * FUNCTION: PrvAddToRegion + * + * DESCRIPTION: Utility function that adds a rectangle with the given + * bounds to the given region. If the given region is + * empty, the region is *set* to the rectangle. + * + * PARAMETERS: region - region to update (or initialize). + * + * top, left, right - coordinates of the rectangle (where + * the bottom is top + 1). + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void PrvAddToRegion (EmRegion& region, int top, int left, int right) +{ + if (region.IsEmpty ()) + { + region = EmRegion (EmRect (left, top, right, top + 1)); + } + else + { + EmRegion tempRegion (EmRect (left, top, right, top + 1)); + region.UnionWith (tempRegion); + } +} + + +/*********************************************************************** + * + * FUNCTION: PrvGetDepth + * + * DESCRIPTION: Return the depth implied by the given format + * + * PARAMETERS: f - format determining the depth. + * + * RETURNED: The depth implied by the format. + * + ***********************************************************************/ + +EmPixMapDepth PrvGetDepth (EmPixMapFormat f) +{ + static const uint8 kDepthArray[] = + { 1, 2, 4, 8, 16, 16, 16, 16, 24, 24, 32, 32, 32, 32 }; + + EmAssert (f >= 0 && f < kPixMapFormatLast); + COMPILE_TIME_ASSERT (countof (kDepthArray) == kPixMapFormatLast); + + return kDepthArray [f]; +} + + +/*********************************************************************** + * + * FUNCTION: PrvConvertMToN + * + * DESCRIPTION: Scanline converters. + * + * PARAMETERS: . + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +#pragma mark - + +void PrvConvert1To1 (const ScanlineParms& parms) +{ + STD_NO_CONVERT(1) +} + +void PrvConvert1To2 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert1To4 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert1To8 (const ScanlineParms& parms) +{ + // Note, this conversion assumes that the first two entries of + // the destination color table contains the same colors as the + // first two entries in the source color table. + + EmCoord right = parms.fRight; + uint8* destPtr = parms.fDestScanline; + const uint8* srcPtr = parms.fSrcScanline; + + for (EmCoord xx = 0; xx < right; ) + { + // Get 8 pixels. + + uint8 bits = *srcPtr++; + + // Convert each nybble (which contains four 1-bit pixels) to + // a 32-bit value containing four 8-bit pixels. + + uint32 p1 = gConvert1To8[(bits >> 4) & 0x0F]; + uint32 p2 = gConvert1To8[(bits >> 0) & 0x0F]; + + if (xx++ < right) + *destPtr++ = p1 >> 24; + if (xx++ < right) + *destPtr++ = p1 >> 16; + if (xx++ < right) + *destPtr++ = p1 >> 8; + if (xx++ < right) + *destPtr++ = p1 >> 0; + + if (xx++ < right) + *destPtr++ = p2 >> 24; + if (xx++ < right) + *destPtr++ = p2 >> 16; + if (xx++ < right) + *destPtr++ = p2 >> 8; + if (xx++ < right) + *destPtr++ = p2 >> 0; + } +} + +void PrvConvert1To24RGB (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(1, 24RGB) +} + +void PrvConvert1To24BGR (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(1, 24BGR) +} + +void PrvConvert1To32ARGB (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(1, 32ARGB) +} + +void PrvConvert1To32ABGR (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(1, 32ABGR) +} + +void PrvConvert1To32RGBA (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(1, 32RGBA) +} + +void PrvConvert1To32BGRA (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(1, 32BGRA) +} + +#pragma mark - + +void PrvConvert2To1 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert2To2 (const ScanlineParms& parms) +{ + STD_NO_CONVERT(2) +} + +void PrvConvert2To4 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert2To8 (const ScanlineParms& parms) +{ + // Note, this conversion assumes that the first four entries of + // the destination color table contains the same colors as the + // first four entries in the source color table. + + EmCoord right = parms.fRight; + uint8* destPtr = parms.fDestScanline; + const uint8* srcPtr = parms.fSrcScanline; + + for (EmCoord xx = 0; xx < right; ) + { + // Get 4 pixels. + + uint8 bits = *srcPtr++; + + // Convert the byte (which contains four 2-bit pixels) to + // a 32-bit value containing four 8-bit pixels. + + uint32 p = gConvert2To8[bits]; + + if (xx++ < right) + *destPtr++ = p >> 24; + if (xx++ < right) + *destPtr++ = p >> 16; + if (xx++ < right) + *destPtr++ = p >> 8; + if (xx++ < right) + *destPtr++ = p >> 0; + } +} + +void PrvConvert2To24RGB (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(2, 24RGB) +} + +void PrvConvert2To24BGR (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(2, 24BGR) +} + +void PrvConvert2To32ARGB (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(2, 32ARGB) +} + +void PrvConvert2To32ABGR (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(2, 32ABGR) +} + +void PrvConvert2To32RGBA (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(2, 32RGBA) +} + +void PrvConvert2To32BGRA (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(2, 32BGRA) +} + +#pragma mark - + +void PrvConvert4To1 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert4To2 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert4To4 (const ScanlineParms& parms) +{ + STD_NO_CONVERT(4) +} + +void PrvConvert4To8 (const ScanlineParms& parms) +{ + // Note, this conversion assumes that the first 16 entries of + // the destination color table contains the same colors as the + // first 16 entries in the source color table. + + EmCoord right = parms.fRight; + uint8* destPtr = parms.fDestScanline; + const uint8* srcPtr = parms.fSrcScanline; + + for (EmCoord xx = 0; xx < right; ) + { + // Get 2 pixels. + + uint8 bits = *srcPtr++; + + // Convert the byte (which contains two 4-bit pixels) to + // a 16-bit value containing two 8-bit pixels. + + uint16 p = gConvert4To8[bits]; + + if (xx++ < right) + *destPtr++ = p >> 8; + if (xx++ < right) + *destPtr++ = p >> 0; + } +} + +void PrvConvert4To24RGB (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(4, 24RGB) +} + +void PrvConvert4To24BGR (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(4, 24BGR) +} + +void PrvConvert4To32ARGB (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(4, 32ARGB) +} + +void PrvConvert4To32ABGR (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(4, 32ABGR) +} + +void PrvConvert4To32RGBA (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(4, 32RGBA) +} + +void PrvConvert4To32BGRA (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(4, 32BGRA) +} + +#pragma mark - + +void PrvConvert8To1 (const ScanlineParms& parms) +{ + EmCoord right = parms.fRight; + uint8* destPtr = parms.fDestScanline; + const uint8* srcPtr = parms.fSrcScanline; + const RGBList* srcColors = parms.fSrcColors; + + EmAssert (srcColors); + + uint8 bitMask = 0x80; + uint8 aByte = 0; + + for (EmCoord xx = 0; xx < right; ++xx) + { + // Get the RGB value. + + uint8 pixel = *srcPtr++; + const RGBType& rgb = (*srcColors)[pixel]; + + // See if the current pixel is dark. + + Bool isDark1 = rgb.fRed < 0xC0; + Bool isDark2 = rgb.fGreen < 0xC0; + Bool isDark3 = rgb.fBlue < 0xC0; + + if (isDark1 || isDark2 || isDark3) + { + aByte |= bitMask; // !!! Assumes white/black color table + } + + bitMask >>= 1; + if (bitMask == 0) + { + *destPtr++ = aByte; + bitMask = 0x80; + aByte = 0; + } + } + + // Write out any partially filled out byte. + + if (bitMask != 0) + { + *destPtr++ = aByte; + } +} + +void PrvConvert8To2 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert8To4 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert8To8 (const ScanlineParms& parms) +{ + STD_NO_CONVERT(8) +} + +void PrvConvert8To24RGB (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(8, 24RGB) +} + +void PrvConvert8To24BGR (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(8, 24BGR) +} + +void PrvConvert8To32ARGB (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(8, 32ARGB) +} + +void PrvConvert8To32ABGR (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(8, 32ABGR) +} + +void PrvConvert8To32RGBA (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(8, 32RGBA) +} + +void PrvConvert8To32BGRA (const ScanlineParms& parms) +{ + STD_INDEX_TO_DIRECT_CONVERT(8, 32BGRA) +} + +#pragma mark - + +void PrvConvert24RGBTo1 (const ScanlineParms& parms) +{ + STD_DIRECT_TO_1_CONVERT(24RGB) +} + +void PrvConvert24RGBTo2 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert24RGBTo4 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert24RGBTo8 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert24RGBTo24RGB (const ScanlineParms& parms) +{ + STD_NO_CONVERT(24) +} + +void PrvConvert24RGBTo24BGR (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(24RGB, 24BGR) +} + +void PrvConvert24RGBTo32ARGB (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(24RGB, 32ARGB) +} + +void PrvConvert24RGBTo32ABGR (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(24RGB, 32ABGR ) +} + +void PrvConvert24RGBTo32RGBA (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(24RGB, 32RGBA) +} + +void PrvConvert24RGBTo32BGRA (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(24RGB, 32BGRA) +} + +#pragma mark - + +void PrvConvert24BGRTo1 (const ScanlineParms& parms) +{ + STD_DIRECT_TO_1_CONVERT(24BGR) +} + +void PrvConvert24BGRTo2 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert24BGRTo4 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert24BGRTo8 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert24BGRTo24RGB (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(24BGR, 24RGB) +} + +void PrvConvert24BGRTo24BGR (const ScanlineParms& parms) +{ + STD_NO_CONVERT(24) +} + +void PrvConvert24BGRTo32ARGB (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(24BGR, 32ARGB) +} + +void PrvConvert24BGRTo32ABGR (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(24BGR, 32ABGR) +} + +void PrvConvert24BGRTo32RGBA (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(24BGR, 32RGBA ) +} + +void PrvConvert24BGRTo32BGRA (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(24BGR, 32BGRA) +} + +#pragma mark - + +void PrvConvert32ARGBTo1 (const ScanlineParms& parms) +{ + STD_DIRECT_TO_1_CONVERT(32ARGB) +} + +void PrvConvert32ARGBTo2 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert32ARGBTo4 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert32ARGBTo8 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert32ARGBTo24RGB (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32ARGB, 24RGB) +} + +void PrvConvert32ARGBTo24BGR (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32ARGB, 24BGR) +} + +void PrvConvert32ARGBTo32ARGB (const ScanlineParms& parms) +{ + STD_NO_CONVERT(32) +} + +void PrvConvert32ARGBTo32ABGR (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32ARGB, 32ABGR) +} + +void PrvConvert32ARGBTo32RGBA (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32ARGB, 32RGBA) +} + +void PrvConvert32ARGBTo32BGRA (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32ARGB, 32BGRA) +} + +#pragma mark - + +void PrvConvert32ABGRTo1 (const ScanlineParms& parms) +{ + STD_DIRECT_TO_1_CONVERT(32ABGR) +} + +void PrvConvert32ABGRTo2 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert32ABGRTo4 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert32ABGRTo8 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert32ABGRTo24RGB (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32ABGR, 24RGB ) +} + +void PrvConvert32ABGRTo24BGR (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32ABGR, 24BGR) +} + +void PrvConvert32ABGRTo32ARGB (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32ABGR, 32ARGB) +} + +void PrvConvert32ABGRTo32ABGR (const ScanlineParms& parms) +{ + STD_NO_CONVERT(32) +} + +void PrvConvert32ABGRTo32RGBA (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32ABGR, 32RGBA) +} + +void PrvConvert32ABGRTo32BGRA (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32ABGR, 32BGRA) +} + +#pragma mark - + +void PrvConvert32RGBATo1 (const ScanlineParms& parms) +{ + STD_DIRECT_TO_1_CONVERT(32RGBA) +} + +void PrvConvert32RGBATo2 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert32RGBATo4 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert32RGBATo8 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert32RGBATo24RGB (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32RGBA, 24RGB) +} + +void PrvConvert32RGBATo24BGR (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32RGBA, 24BGR) +} + +void PrvConvert32RGBATo32ARGB (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32RGBA, 32ARGB) +} + +void PrvConvert32RGBATo32ABGR (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32RGBA, 32ABGR) +} + +void PrvConvert32RGBATo32RGBA (const ScanlineParms& parms) +{ + STD_NO_CONVERT(32) +} + +void PrvConvert32RGBATo32BGRA (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32RGBA, 32BGRA) +} + +#pragma mark - + +void PrvConvert32BGRATo1 (const ScanlineParms& parms) +{ + STD_DIRECT_TO_1_CONVERT(32BGRA) +} + +void PrvConvert32BGRATo2 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert32BGRATo4 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert32BGRATo8 (const ScanlineParms& /*parms*/) +{ + EmAssert (false); +} + +void PrvConvert32BGRATo24RGB (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32BGRA, 24RGB) +} + +void PrvConvert32BGRATo24BGR (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32BGRA, 24BGR) +} + +void PrvConvert32BGRATo32ARGB (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32BGRA, 32ARGB) +} + +void PrvConvert32BGRATo32ABGR (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32BGRA, 32ABGR) +} + +void PrvConvert32BGRATo32RGBA (const ScanlineParms& parms) +{ + STD_DIRECT_CONVERT(32BGRA, 32RGBA) +} + +void PrvConvert32BGRATo32BGRA (const ScanlineParms& parms) +{ + STD_NO_CONVERT(32) +} |