//	Zinc Application Framework - W_GDIDSP.CPP
//	COPYRIGHT (C) 1990-1999.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA

#include <math.h>
#include <w_app.hpp>
#include <w_gdidsp.hpp>
#include <z_cset.hpp>
#include <z_string.hpp>
#include <z_utils.hpp>

#if !defined(MAKEWORD)
#	define MAKEWORD(a, b)      ((WORD)(((BYTE)(a)) | (((WORD)((BYTE)(b))) << 8)))
#endif


// Cache used for cacheing GDI pens and brushes. 
class ZafCache : public ZafList
{
public:
	ZafCache(int size,
		long (*CreateFunction)(long type, long value),
		void (*DestroyFunction)(long type, long value, long handle));
	virtual ~ZafCache(void);

	long Get(long type, long value);
	int Release(long handle);

private:
	int count;
	int size;
	long (*CreateFunction)(long type, long value);
	void (*DestroyFunction)(long type, long value, long handle);

	class CacheElement : public ZafElement
	{
	public:
		long handle;
		long type;
		long value;
		int useCount;

		CacheElement(long type, long value, long handle);
	};
};

ZafCache::ZafCache(int setSize,
	long (*setCreateFunction)(long type, long value),
	void (*setDestroyFunction)(long type, long value, long handle)) :
	ZafList(), size(setSize), CreateFunction(setCreateFunction),
	DestroyFunction(setDestroyFunction)
{
}

ZafCache::~ZafCache(void)
{
	for (CacheElement *element = (CacheElement *)First(); element; element = (CacheElement *)element->Next())
		DestroyFunction(element->type, element->value, element->handle);
}

long ZafCache::Get(long type, long value)
{
	// Find a matching cache element.
	CacheElement *element; 
	for (element = (CacheElement *)First(); element; element = (CacheElement *)element->Next())
		if (element->type == type && element->value == value)
		{
			// Move the element to the front of the list because it is
			// most recently used.
			if (element != first)
			{
				ZafList::Subtract(element);
				ZafList::Add(element, first);
			}

			element->useCount++;
			return element->handle;
		}

	// Create a cache element if one didn't already exist.
	long handle = CreateFunction(type, value);
	element = new CacheElement(type, value, handle);
	ZafList::Add(element, first);

	// Remove a cache element if the cache is full.
	if (++count >= size)
	{
		element = (CacheElement *)last;
		if (element->useCount <= 0)
		{
			ZafList::Subtract(element);
			DestroyFunction(element->type, element->value, element->handle);
			delete element;
			count--;
		}
	}

	return handle;
}

int ZafCache::Release(long handle)
{
	for (CacheElement *element = (CacheElement *)First(); element; element = (CacheElement *)element->Next())
		if (element->handle == handle)
		{
			element->useCount--;
			if (element->useCount == 0)
			{
				// Move unused elements to the end of the cache.
				CacheElement *positionElement = (CacheElement *)element->Next();
				while (positionElement && positionElement->useCount > 0)
					positionElement = (CacheElement *)positionElement->Next();

				if (positionElement && positionElement != element->Next())
				{
					ZafList::Subtract(element);
					ZafList::Add(element, positionElement);
				}
			}
			return (element->useCount);
		}

	return -1;
}

ZafCache::CacheElement::CacheElement(long setType, long setValue, long setHandle) :
	type(setType), value(setValue), handle(setHandle), useCount(1)
{
}

// GDI cache types.
const int ZAF_GDI_PEN		= 1;
const int ZAF_GDI_BRUSH		= 2;

long ZafGDICreate(long type, long value)
{
	int gdiType = LOBYTE(LOWORD(type));

	HGDIOBJ handle = 0;
	if (gdiType == ZAF_GDI_PEN)
	{
		int gdiWidth = (ZafInt16)HIWORD(type);
		if (gdiWidth == -1)
			gdiWidth = 0;
		else if (gdiWidth == 0)
			gdiWidth = -1;
		int gdiStyle = (gdiWidth > -1) ? HIBYTE(LOWORD(type)) : PS_NULL;
		COLORREF gdiColor = (COLORREF)value;

/* START BLOCK COMMENT
**	#if defined(ZAF_WIN32)
**			if (gdiWidth > 1)
**			{
**				gdiStyle |= PS_GEOMETRIC | PS_ENDCAP_FLAT | PS_JOIN_MITER;
**				LOGBRUSH gdiBrushStyle;
**				gdiBrushStyle.lbStyle = BS_SOLID;
**				gdiBrushStyle.lbColor = gdiColor;
**				gdiBrushStyle.lbHatch = 0;
**				handle = ExtCreatePen(gdiStyle, gdiWidth, &gdiBrushStyle, 0, NULL);
**			}
**			else
**	#endif
END BLOCK COMMENT */
		handle = CreatePen(gdiStyle, gdiWidth, gdiColor);
	}
	else if (gdiType == ZAF_GDI_BRUSH)
	{
		ZafLogicalFillPattern fillPattern = (ZafLogicalFillPattern)HIWORD(type);
		if (fillPattern == ZAF_PTN_SOLID_FILL)
		{
			COLORREF colorRef = (COLORREF)value;
			handle = CreateSolidBrush(colorRef);
		}
		else
		{
			HBITMAP hBitmap = (HBITMAP)value;
			handle = CreatePatternBrush(hBitmap);
		}
	}

	return (long)handle;
}

void ZafGDIDestroy(long, long, long handle)
{
	DeleteObject((HGDIOBJ)handle);
}

static ZafCache zafGdiCache(32, ZafGDICreate, ZafGDIDestroy);

HBRUSH ZafGdiDisplay::GetGdiCacheBrush(ZafPaletteStruct palette)
{
	if (palette.fillPattern == ZAF_PTN_SOLID_FILL)
	{
		COLORREF colorRef = (palette.colorBackground == ZAF_CLR_DEFAULT) ?
			palette.osPalette.colorBackground : colorTable[palette.colorBackground];
		return (HBRUSH)zafGdiCache.Get(MAKELONG(ZAF_GDI_BRUSH, palette.fillPattern), (long)colorRef);
	}
	else
		return (HBRUSH)zafGdiCache.Get(MAKELONG(ZAF_GDI_BRUSH, palette.fillPattern), (long)patternTable[palette.fillPattern]);
}

HPEN ZafGdiDisplay::GetGdiCachePen(ZafPaletteStruct palette, int width)
{
	COLORREF colorRef = (palette.colorForeground == ZAF_CLR_DEFAULT) ?
		palette.osPalette.colorForeground : colorTable[palette.colorForeground];
	OSLineStyle lineStyle = lineTable[palette.lineStyle];
	return (HPEN)zafGdiCache.Get(MAKELONG(MAKEWORD(ZAF_GDI_PEN, lineStyle), width), colorRef);
}

int ZafGdiDisplay::ReleaseGdiCacheObject(HGDIOBJ hGdiObject)
{
	return (zafGdiCache.Release((long)hGdiObject));
}

// ----- ZafGdiDisplay -----------------------------------------------

static ZafPaletteStruct ZAF_FARDATA defaultPalette =
{
	ZAF_LINE_SOLID,
	ZAF_PTN_SOLID_FILL,
	ZAF_CLR_BLACK,
	ZAF_CLR_WHITE,
	ZAF_MONO_BLACK,
	ZAF_MONO_WHITE,
	ZAF_FNT_DIALOG
};

ZafGdiDisplay::ZafGdiDisplay(void) :
	ZafDisplay(), hDC(0), hWnd(0), hPen(0), hBrush(0), logicalPalette(0),
	virtualCount(0)
{
//??? Can't be done here because tables haven't been initialized yet.
//???	hPen = GetGdiCachePen(palette, 1);
//???	hBrush = GetGdiCacheBrush(palette);
}

ZafGdiDisplay::~ZafGdiDisplay(void)
{
}

ZafLogicalColor ZafGdiDisplay::AddColor(ZafLogicalColor logicalColor, ZafUInt8 red, ZafUInt8 green, ZafUInt8 blue)
{
	if (logicalColor != ZAF_CLR_NULL && logicalColor >= ZAF_MAXCOLORS)
		return (ZAF_CLR_NULL);

	if (logicalColor == ZAF_CLR_NULL)
	{
		logicalColor = ZAF_CLR_USER_FIRST;
		while (logicalColor < ZAF_MAXCOLORS && colorTable[logicalColor] != 0xFFFFFFFFL)
			logicalColor++;

		if (logicalColor == ZAF_MAXCOLORS)
			return (ZAF_CLR_NULL);
	}

	colorTable[logicalColor] = RGB(red, green, blue);

	CreateLogicalPalette();

  	return (logicalColor);
}

ZafError ZafGdiDisplay::DestroyColor(ZafLogicalColor color)
{
	if (color >= ZAF_MAXCOLORS)
		return (ZAF_ERROR_INVALID_INDEX);

	colorTable[color] = 0xFFFFFFFFL;
//	CreateLogicalPalette();
	return (ZAF_ERROR_NONE);
}

ZafError ZafGdiDisplay::ColorInfo(ZafLogicalColor index, ZafUInt8 &red, ZafUInt8 &green, ZafUInt8 &blue)
{
	if (index >= ZAF_MAXCOLORS || colorTable[index] == 0xFFFFFFFFL)
		return (ZAF_ERROR_INVALID_INDEX);

	// Return the color information.
	if (paletteDevice)
	{
		PALETTEENTRY paletteEntry;
		GetPaletteEntries(logicalPalette, (int)(colorTable[index] & 0x0000FFFFL), 1, &paletteEntry);
		red = paletteEntry.peRed;
		green = paletteEntry.peGreen;
		blue = paletteEntry.peBlue;
	}
	else
	{
		red = (ZafUInt8)GetRValue(colorTable[index]);
		green = (ZafUInt8)GetGValue(colorTable[index]);
		blue = (ZafUInt8)GetBValue(colorTable[index]);
	}

	return (ZAF_ERROR_NONE);
}

ZafLogicalFont ZafGdiDisplay::AddFont(ZafLogicalFont logicalFont, char *fontFamily,
	int pointSize, ZafFontWeight weight, ZafFontSlant slant, ZafFontPitch pitch)
{
	if (logicalFont != ZAF_FNT_NULL &&
		(logicalFont < 0 || logicalFont >= ZAF_MAXFONTS))
		return (ZAF_FNT_NULL);

	if (logicalFont == ZAF_FNT_NULL)
	{
		logicalFont = 0;
		while (logicalFont < ZAF_MAXFONTS && fontTable[logicalFont])
			logicalFont++;

		if (logicalFont == ZAF_MAXFONTS)
			return (ZAF_FNT_NULL);
	}
	else if (fontTable[logicalFont])
	{
		DeleteObject(fontTable[logicalFont]);
		fontTable[logicalFont] = ZAF_NULLH(OSFont);
	}

	int fnWeight;
	switch (weight)
	{
	case ZAF_FNT_WEIGHT_BOLD:
		fnWeight = FW_BOLD;
		break;

	default:
		fnWeight = FW_DONTCARE;
		break;
	}

	// Convert from pointSize to pixel size (with rounding).
	int emHeight = -(int)(((long)pointSize * (long)pixelsPerInchY + 36) / 72);
	bool fbItalic = (slant == ZAF_FNT_SLANT_ITALIC) ? true : false;
#if defined(ZAF_WIN32)
	DWORD fdwPitchAndFamily = FF_DONTCARE;
#else
	BYTE fdwPitchAndFamily = FF_DONTCARE;
#endif
	if (pitch == ZAF_FNT_PITCH_FIXED)
		fdwPitchAndFamily |= FIXED_PITCH;
	else if (pitch == ZAF_FNT_PITCH_VARIABLE)
		fdwPitchAndFamily |= VARIABLE_PITCH;
	else
		fdwPitchAndFamily |= DEFAULT_PITCH;

#if defined(ZAF_WIN32)
	HFONT hFont = CreateFontA(emHeight, 0, 0, 0, fnWeight, fbItalic,
		0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
		DEFAULT_QUALITY, fdwPitchAndFamily, fontFamily);
#else
	HFONT hFont = CreateFont(emHeight, 0, 0, 0, fnWeight, fbItalic,
		0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
		DEFAULT_QUALITY, fdwPitchAndFamily, fontFamily);
#endif

	if (hFont)
	{
		fontTable[logicalFont] = hFont;
		return (logicalFont);
	}

	return (ZAF_FNT_NULL);
}

ZafError ZafGdiDisplay::DestroyFont(ZafLogicalFont font)
{
	if (font < 0 || font >= ZAF_MAXFONTS)
		return (ZAF_ERROR_INVALID_INDEX);

	DeleteObject(fontTable[font]);
	fontTable[font] = ZAF_NULLH(OSFont);
	return (ZAF_ERROR_NONE);
}

ZafError ZafGdiDisplay::FontInfo(ZafLogicalFont index, char *fontFamily, int bufferSize,
	int *pointSize, ZafFontWeight *weight, ZafFontSlant *slant,
		ZafFontPitch *pitch, ZafCoordinate *averageCharWidth,
		ZafCoordinate *height, ZafCoordinate *leading)
{
	if (index < 0 || index >= ZAF_MAXFONTS || !fontTable[index])
		return (ZAF_ERROR_INVALID_INDEX);

	// Return the font information.
	HDC fontDC = hDC ? hDC : GetDC(0);
	HFONT oldFont = (HFONT)SelectObject(fontDC, fontTable[index]);
#if defined(ZAF_WIN32) && defined(ZAF_UNICODE)
	TEXTMETRICA textMetrics;
	GetTextMetricsA(fontDC, &textMetrics);
	if (fontFamily)
		GetTextFaceA(fontDC, bufferSize, fontFamily);
#else
	TEXTMETRIC textMetrics;
	GetTextMetrics(fontDC, &textMetrics);
	if (fontFamily)
		GetTextFace(fontDC, bufferSize, fontFamily);
#endif

  	SelectObject(fontDC, oldFont);

	if (!hDC)
		ReleaseDC(0, fontDC);

	// Convert from pixel size to pointSize (with rounding).
	if (pointSize)
		*pointSize = (int)((((long)textMetrics.tmHeight -
			(long)textMetrics.tmInternalLeading) * 72 +
			(pixelsPerInchY / 2)) / pixelsPerInchY);
	if (weight)
		*weight = textMetrics.tmWeight == FW_BOLD ? ZAF_FNT_WEIGHT_BOLD : ZAF_FNT_WEIGHT_NORMAL;
	if (slant)
		*slant = textMetrics.tmItalic ? ZAF_FNT_SLANT_ITALIC : ZAF_FNT_SLANT_NORMAL;
	if (pitch)
	{
		if (textMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)
			*pitch = ZAF_FNT_PITCH_VARIABLE;
		else
			*pitch = ZAF_FNT_PITCH_FIXED;
	}
	if (averageCharWidth)
		*averageCharWidth = ConvertXValue(textMetrics.tmAveCharWidth, ZAF_PIXEL, coordinateType);
	if (height)
		*height = ConvertYValue(textMetrics.tmHeight, ZAF_PIXEL, coordinateType);
	if (leading)
		*leading = ConvertYValue(textMetrics.tmExternalLeading, ZAF_PIXEL, coordinateType);

	return (ZAF_ERROR_NONE);
}

ZafError ZafGdiDisplay::BeginDraw(OSDisplayContext tHDC, OSDrawContext tHWnd,
	const ZafRegionStruct &, const ZafRegionStruct &)
{
	if (virtualCount++ == 0)
	{
		if (tHDC)
			SetDisplayContext(tHDC);
		else if (tHWnd == ID_ZAF_SCREEN)
		{
			hWnd = tHWnd;
			SetDisplayContext(GetDC(0));
		}
		else if (tHWnd)
		{
			hWnd = tHWnd;
			SetDisplayContext(GetDC(hWnd));
		}
		SetBkMode(hDC, TRANSPARENT);

		// Initialize pen and brush if they haven't been.
		// This only needs to be done at startup, but has to be done after
		// the constructor has completely finished (the tables set up in
		// derived classes are required).
		if (!hPen)
			hPen = GetGdiCachePen(palette, 1);
		if (!hBrush)
			hBrush = GetGdiCacheBrush(palette);

		// Cause pens, etc. to be selected.
		SetPalette(palette);
	}

	return (ZAF_ERROR_NONE);
}

void ZafGdiDisplay::CreateLogicalPalette(void)
{
	// Count the colors.
	int numPaletteEntries = 0;
	int index;
	for (index = 0; index < ZAF_MAXCOLORS; index++)
	{
		if (colorTable[index] != 0xFFFFFFFFL)
			numPaletteEntries++;
	}

	// Allocate the LOGPALETTE structure.
	LOGPALETTE *logPalette = (LOGPALETTE *)new BYTE[sizeof(LOGPALETTE)
		+ (numPaletteEntries - 1) * sizeof(PALETTEENTRY)];
	logPalette->palVersion = 0x300;
	logPalette->palNumEntries = (WORD)numPaletteEntries;

	// Fill the LOGPALETTE structure.
	int paletteIndex = 0;
	for (index = 0; index < ZAF_MAXCOLORS; index++)
	{
		if (colorTable[index] != 0xFFFFFFFFL)
		{
			if ((colorTable[index] & 0xFF000000L) == 0x01000000L)
			{
				// The entry is an index into the logical palette.
				PALETTEENTRY paletteEntry;
			 	GetPaletteEntries(logicalPalette, (int)(colorTable[index] & 0x0000FFFFL), 1, &paletteEntry);
				logPalette->palPalEntry[paletteIndex] = paletteEntry;

				if (paletteDevice)
					colorTable[index] = PALETTEINDEX(paletteIndex);
				else
					colorTable[index] = RGB(paletteEntry.peRed, paletteEntry.peGreen, paletteEntry.peBlue);
			}
			else
			{
				// The entry is an RGB value.
				logPalette->palPalEntry[paletteIndex].peRed = GetRValue(colorTable[index]);
				logPalette->palPalEntry[paletteIndex].peGreen = GetGValue(colorTable[index]);
				logPalette->palPalEntry[paletteIndex].peBlue = GetBValue(colorTable[index]);
				logPalette->palPalEntry[paletteIndex].peFlags = 0;

				if (paletteDevice)
					colorTable[index] = PALETTEINDEX(paletteIndex);
                else
					colorTable[index] = RGB(logPalette->palPalEntry[paletteIndex].peRed,
					  logPalette->palPalEntry[paletteIndex].peGreen, logPalette->palPalEntry[paletteIndex].peBlue);
			}

			paletteIndex++;
		}
	}

	// Destroy the old logical palette.
	if (logicalPalette)
		DeleteObject(logicalPalette);

	// Create the new logical palette.
	logicalPalette = CreatePalette(logPalette);

	// Clean up.
	delete []logPalette;
}

ZafError ZafGdiDisplay::EndDraw(void)
{
	if (--virtualCount == 0)
	{
		HDC oldDC = RestoreDisplayContext();

		if (hWnd)
		{
			if (hWnd == ID_ZAF_SCREEN)
				ReleaseDC(0, oldDC);
			else
				ReleaseDC(hWnd, oldDC);
		}
		hWnd = 0;

		palette = defaultPalette;
		mode = ZAF_MODE_COPY;
		coordinateType = ZAF_PIXEL;
		originX = originY = 0;
		scaleNumerator = scaleDenominator = 1;
	}
	else
	{
		// Restore clipRegion in case a child set one from a nested Draw().
		ZafRegionStruct defaultClipRegion;
		defaultClipRegion.coordinateType = ZAF_PIXEL;
		defaultClipRegion.Assign(0, 0, columns - 1, lines - 1);
		if (clipRegion != defaultClipRegion)
			SetClipRegion(defaultClipRegion);
	}

	return (ZAF_ERROR_NONE);
}

// --- Bitmap ---------------------------------------------------------------

ZafError ZafGdiDisplay::Bitmap(ZafCoordinate column, ZafCoordinate line, ZafBitmapStruct &bitmap)
{
	int pixelColumn = (int)(ConvertXValue(column, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelLine = (int)(ConvertYValue(line, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	int width = (int)((long)bitmap.width * scaleNumerator / scaleDenominator);
	int height = (int)((long)bitmap.height * scaleNumerator / scaleDenominator);

	// Create the bitmap handle(s) if necessary.
	if (!bitmap.handle && bitmap.array)
		ConvertToOSBitmap(bitmap);

	if (mode == ZAF_MODE_COPY && bitmap.mask)
	{
		// Transparancy is achieved using the "Black Source" masking method.
		// Drawing is performed flicker free by copying the screen contents
		// where the bitmap is to be drawn into a memory (buffer) bitmap,
		// perforiming the "bitblt" operations on this buffer bitmap, and
		// then copying the buffer bitmap back to the screen.

		// Create memory DC's.
		HDC bufferDC = CreateCompatibleDC(hDC);
		HDC bitmapDC = CreateCompatibleDC(hDC);

		// Create the buffer bitmap and select it into the buffer DC.
		HBITMAP bufferBitmap = CreateCompatibleBitmap(hDC, bitmap.width, bitmap.height);
		SelectObject(bufferDC, bufferBitmap);

		// Copy screen contents into the buffer bitmap.
		BitBlt(bufferDC, 0, 0, bitmap.width, bitmap.height, hDC, pixelColumn, pixelLine, SRCCOPY);

		// Select the mask bitmap into the bitmap DC.
		SelectObject(bitmapDC, bitmap.mask);

		// And the mask bitmap onto the buffer bitmap.
		SetTextColor(bufferDC, RGB(0,0,0)); // Mask bitmap 'on' color.
		SetBkColor(bufferDC, RGB(0xFF,0xFF,0xFF)); // Mask bitmap 'off' color.
		BitBlt(bufferDC, 0, 0, bitmap.width, bitmap.height, bitmapDC, 0, 0, SRCAND);

		// Select the color bitmap into the bitmap DC.
		SelectObject(bitmapDC, bitmap.handle);

		// XOR the color bitmap onto  the buffer bitmap.
		BitBlt(bufferDC, 0, 0, bitmap.width, bitmap.height, bitmapDC, 0, 0, SRCINVERT);

		// Copy the buffer bitmap back to the screen.
		if (Scale() == 100)
			BitBlt(hDC, pixelColumn, pixelLine, bitmap.width, bitmap.height, bufferDC, 0, 0, SRCCOPY);
		else
			StretchBlt(hDC, pixelColumn, pixelLine, width, height, bufferDC, 0, 0, bitmap.width, bitmap.height, modeTable[mode].ternary);

		// Clean up.
		DeleteDC(bitmapDC);
		DeleteDC(bufferDC);
		DeleteObject(bufferBitmap);
	}
	else // Bitmap does not contain transparency.
	{
		// Create a memory DC.
		HDC memDC = CreateCompatibleDC(hDC);

		// Select the bitmap into the memory DC.
		SelectObject(memDC, bitmap.handle);

		// Copy the bitmap onto the screen.
		if (Scale() == 100)
			BitBlt(hDC, pixelColumn, pixelLine, bitmap.width, bitmap.height, memDC, 0, 0, modeTable[mode].ternary);
		else
			StretchBlt(hDC, pixelColumn, pixelLine, width, height, memDC, 0, 0, bitmap.width, bitmap.height, modeTable[mode].ternary);

		// Clean up.
		DeleteDC(memDC);
	}

	return (ZAF_ERROR_NONE);
}

ZafError ZafGdiDisplay::ConvertToOSBitmap(ZafBitmapStruct &bitmap)
{
	// Allocate local data to optimize speed.
	int width = bitmap.width, height = bitmap.height;
	ZafLogicalColor *array = bitmap.array;

	// Compute scan line size.
	int bitmapBytesPerLine = width;
	int maskBytesPerLine = (width + 7) / 8;

	// Color bitmap scan lines must be padded to end on "LONG" boundaries.
	if (bitmapBytesPerLine % sizeof(LONG))
		bitmapBytesPerLine += sizeof(LONG) - bitmapBytesPerLine % sizeof(LONG);

	// Monochrome bitmap scan lines must be padded to end on "word" boundaries.
	if (maskBytesPerLine % sizeof(WORD))
		maskBytesPerLine += sizeof(WORD) - maskBytesPerLine % sizeof(WORD);

	// Allocate bitmap initialization data.
	BYTE *bitmapData = new BYTE[bitmapBytesPerLine * height];
	BYTE *maskData = new BYTE[maskBytesPerLine * height];

	// Initialize bitmap data.
	memset(maskData, 0, maskBytesPerLine * height);
	bool transparent = false;
	int zafIndex = 0, colors = 2;
	for (int y = 0; y < height; y++)
	{
		// Compute vertical index/mask values.
		int bitmapYIndex = (height - y - 1) * bitmapBytesPerLine;
		int maskYIndex = y * maskBytesPerLine;
		BYTE maskBit = 0x80;

		for (int x = 0; x < width; x++)
		{
			if (array[zafIndex] == ZAF_CLR_BACKGROUND)
			{
				// Initialize "Black Source" and "transparent" mask values.
				transparent = true;
				bitmapData[bitmapYIndex + x] = ZAF_CLR_BLACK;
				maskData[maskYIndex + x / 8] |= maskBit;
			}
			else
			{
				// Initialize "opaque" color values.				
				bitmapData[bitmapYIndex + x] = array[zafIndex];
				if (array[zafIndex] >= colors)
					colors = array[zafIndex] + 1;
			}

			// Update index/mask values.
			zafIndex++;
			maskBit >>= 1;
			if (!maskBit)
				maskBit = 0x80;
		}
	}

	// Allocate BITMAPINFO structure.
	BITMAPINFO *bitmapInfo = (BITMAPINFO *)new char[sizeof(BITMAPINFO)
		+ (colors - 1) * (paletteDevice ? sizeof(WORD) : sizeof(RGBQUAD))];

	// Initialize HEADER information.
	bitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bitmapInfo->bmiHeader.biWidth = width;
	bitmapInfo->bmiHeader.biHeight = height;
	bitmapInfo->bmiHeader.biPlanes = 1;
	bitmapInfo->bmiHeader.biBitCount = 8;
	bitmapInfo->bmiHeader.biCompression = BI_RGB;
	bitmapInfo->bmiHeader.biSizeImage = 0;
	bitmapInfo->bmiHeader.biXPelsPerMeter = 0;
	bitmapInfo->bmiHeader.biYPelsPerMeter = 0;
	bitmapInfo->bmiHeader.biClrUsed = colors;
	bitmapInfo->bmiHeader.biClrImportant = colors;

	// Initialize the color table.
	if (paletteDevice)
		for (int i = 0; i < colors; i++)
			((WORD *)bitmapInfo->bmiColors)[i] = LOWORD(colorTable[i]);
	else
		for (int i = 0; i < colors; i++)
		{
			bitmapInfo->bmiColors[i].rgbRed = GetRValue(colorTable[i]);
			bitmapInfo->bmiColors[i].rgbGreen = GetGValue(colorTable[i]);
			bitmapInfo->bmiColors[i].rgbBlue = GetBValue(colorTable[i]);
		}

	// Get a device context for the convertsion.
	HDC convertDC;
	HPALETTE savePalette = 0;
	if (hDC)
		convertDC = hDC;
	else
	{
		// Get the device context of the screen.
		convertDC = GetDC(0);
		savePalette = SelectPalette(convertDC, logicalPalette, FALSE);
	}

	// Realize logicalPalette.
	RealizePalette(convertDC);


	// Create the Bitmap.
	bitmap.handle = CreateDIBitmap(convertDC, &bitmapInfo->bmiHeader,
		CBM_INIT, bitmapData, bitmapInfo, paletteDevice ? DIB_PAL_COLORS : DIB_RGB_COLORS);

	// Release the conversion device context.
	if (!hDC)
	{
		SelectPalette(convertDC, savePalette, FALSE);
		ReleaseDC(0, convertDC);
	}

	// Create the "transpanency" mask.
	if (transparent)
		bitmap.mask = CreateBitmap(width, height, 1, 1, maskData);

	// Clean up.
	delete []bitmapInfo;
	delete []bitmapData;
	delete []maskData;

//???	// Free up the ZAF bitmap information.
//???	if (!bitmap.staticArray)
//???	{
//???		delete []bitmap.array;
//???		bitmap.array = ZAF_NULLP(ZafLogicalColor);
//???	}

	return (ZAF_ERROR_NONE);
}

ZafError ZafGdiDisplay::ConvertToZafBitmap(ZafBitmapStruct &bitmap)
{
	// Make sure the OS bitmap exists.
	if (!bitmap.handle)
		return (ZAF_ERROR_INVALID_SOURCE);

	// Get width, height, and color format of the bitmap.
	BITMAP logBitmap;
	GetObject(bitmap.handle, sizeof(logBitmap), &logBitmap);
	bitmap.width = logBitmap.bmWidth;
	bitmap.height = logBitmap.bmHeight;

	// Initialize BITMAPINFO structure.
	BITMAPINFO *bitmapInfo = (BITMAPINFO *)new BYTE[sizeof(BITMAPINFO) +
		255 * sizeof(WORD)];
	bitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bitmapInfo->bmiHeader.biWidth = bitmap.width;
	bitmapInfo->bmiHeader.biHeight = bitmap.height;
	bitmapInfo->bmiHeader.biPlanes = 1;
	bitmapInfo->bmiHeader.biBitCount = 8;
	bitmapInfo->bmiHeader.biCompression = BI_RGB;
	bitmapInfo->bmiHeader.biSizeImage = 0;
	bitmapInfo->bmiHeader.biXPelsPerMeter = 0;
	bitmapInfo->bmiHeader.biYPelsPerMeter = 0;
	bitmapInfo->bmiHeader.biClrUsed = 0;
	bitmapInfo->bmiHeader.biClrImportant = 0;

	// Get a device context for the convertsion.
	HDC convertDC;
	HPALETTE savePalette = 0;
	if (hDC)
		convertDC = hDC;
	else
	{
		// Get the device context of the screen.
		convertDC = GetDC(0);
		savePalette = SelectPalette(convertDC, logicalPalette, FALSE);
	}

	// Realize logicalPalette.
	RealizePalette(convertDC);

	// Get mask data.
	int maskBytesPerLine;
	BYTE *maskData = ZAF_NULLP(BYTE);
	if (bitmap.mask)
	{
		// Allocate mask bitmap pixel data.
		maskBytesPerLine = (bitmap.width + 7) / 8;
		if (maskBytesPerLine % sizeof(LONG))
			maskBytesPerLine += sizeof(LONG) - maskBytesPerLine % sizeof(LONG);
		maskData = new BYTE[maskBytesPerLine * bitmap.height];

		// Retrieve mask bitmap pixel data.
		bitmapInfo->bmiHeader.biBitCount = 1;
		GetDIBits(convertDC, bitmap.mask, 0, bitmap.height, maskData, bitmapInfo,
			DIB_RGB_COLORS);
	}

	// Allocate color bitmap pixel data.
	int bitmapBytesPerLine = bitmap.width;
	if (bitmapBytesPerLine % sizeof(LONG))
		bitmapBytesPerLine += sizeof(LONG) - bitmapBytesPerLine % sizeof(LONG);
	BYTE *bitmapData = new BYTE[bitmapBytesPerLine * bitmap.height];

	// Get the color bitmap pixel data and RGB color table.
	bitmapInfo->bmiHeader.biBitCount = 8;
	GetDIBits(convertDC, bitmap.handle, 0, bitmap.height, bitmapData, bitmapInfo,
		DIB_PAL_COLORS);

	// Release the conversion device context.
	if (!hDC)
	{
		SelectPalette(convertDC, savePalette, FALSE);
		ReleaseDC(0, convertDC);
	}

	// Allocate the ZAF array.
	if (bitmap.array && !bitmap.staticArray)
		delete []bitmap.array;
	bitmap.array = new ZafLogicalColor[bitmap.width * bitmap.height];
	bitmap.staticArray = false;

	// Fill the ZAF array.
	// (Optimized for speed.)
	if (bitmap.mask)
	{
		int zafIndex = 0;
		for (int y = bitmap.height - 1; y >= 0; y--)
		{
			// Initialize mask value.
			BYTE maskBit = 0x80;
			for (int x = 0; x < bitmap.width; x++)
			{
				// Test for transparency.
				if (maskData[y * maskBytesPerLine + x / 8] & maskBit)
					bitmap.array[zafIndex] = ZAF_CLR_BACKGROUND;
				else
					bitmap.array[zafIndex] = (ZafLogicalColor)((WORD *)bitmapInfo->bmiColors)[bitmapData[y * bitmapBytesPerLine + x]];

				// Update index/mask values.
				zafIndex++;
				maskBit >>= 1;
				if (!maskBit)
					maskBit = 0x80;
			}
		}
	}
	else
	{
		int zafIndex = 0;
		for (int y = bitmap.height - 1; y >= 0; y--)
			for (int x = 0; x < bitmap.width; x++)
				bitmap.array[zafIndex++] = (ZafLogicalColor)((WORD *)bitmapInfo->bmiColors)[bitmapData[y * bitmapBytesPerLine + x]];
	}

	// Clean up.
	delete []bitmapInfo;
	delete []bitmapData;
	if (maskData)
		delete []maskData;

	return (ZAF_ERROR_NONE);
}

ZafError ZafGdiDisplay::DestroyOSBitmap(ZafBitmapStruct &bitmap)
{
	// Destroy the bitmap handle and mask.
	if (!bitmap.StaticHandle())
	{
		if (bitmap.handle)
			DeleteObject(bitmap.handle);
		if (bitmap.mask)
			DeleteObject(bitmap.mask);
	}
	bitmap.handle = bitmap.mask = 0;
	return (ZAF_ERROR_NONE);
}

ZafError ZafGdiDisplay::DestroyZafBitmap(ZafBitmapStruct &bitmap)
{
	// Destroy the bitmap array.
	if (bitmap.array && !bitmap.StaticArray())
		delete []bitmap.array;
	return (ZAF_ERROR_NONE);
}

// --- Icon -----------------------------------------------------------------

ZafError ZafGdiDisplay::Icon(ZafCoordinate column, ZafCoordinate line, ZafIconStruct &icon)
{
	int pixelColumn = (int)(ConvertXValue(column, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelLine = (int)(ConvertYValue(line, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);

	// Create an icon handle if necessary.
	if (!icon.handle && icon.array)
		ConvertToOSIcon(icon);

	// Draw the icon.
	if (mode == ZAF_MODE_COPY)
		DrawIcon(hDC, pixelColumn, pixelLine, icon.handle);
	else
	{
		HDC bufferDC = CreateCompatibleDC(hDC);
		HBITMAP bufferBitmap = CreateCompatibleBitmap(hDC, icon.width, icon.height);
		SelectObject(bufferDC, bufferBitmap);
		BitBlt(bufferDC, 0, 0, icon.width, icon.height, NULL, 0, 0, BLACKNESS);
		DrawIcon(bufferDC, 0, 0, icon.handle);
		BitBlt(hDC, pixelColumn, pixelLine, icon.width, icon.height, bufferDC, 0, 0, SRCINVERT);
		DeleteDC(bufferDC);
		DeleteObject(bufferBitmap);
	}
	return (ZAF_ERROR_NONE);
}

ZafError ZafGdiDisplay::ConvertToOSIcon(ZafIconStruct &icon)
{
	// Create and convert a bitmap struct for use in the icon conversion.
	ZafBitmapStruct bitmap;
	bitmap.width = icon.width;
	bitmap.height = icon.height;
	bitmap.array = icon.array;
	bitmap.staticArray = true;
	bitmap.handle = 0;
	bitmap.mask = 0;
	ConvertToOSBitmap(bitmap);

#if defined(ZAF_WIN32)
	// Initialize an ICONINFO structure.
	ICONINFO iconInfo;
	iconInfo.fIcon = TRUE;
	iconInfo.xHotspot = iconInfo.yHotspot = 0;
	if (!bitmap.mask)
	{
		// Compute scan line size.
		int maskBytesPerLine = (icon.width + 7) / 8;
		maskBytesPerLine += maskBytesPerLine % sizeof(WORD);

		// Create a mask bitmap with no transparancy.
		BYTE *maskData = new BYTE[maskBytesPerLine * icon.height];
		memset(maskData, 0, maskBytesPerLine * icon.height);
		iconInfo.hbmMask = bitmap.mask = CreateBitmap(icon.width, icon.height, 1, 1, maskData);
		delete []maskData;
	}
	else
		iconInfo.hbmMask = bitmap.mask;
	iconInfo.hbmColor = bitmap.handle;

	// Create Icon.
	icon.handle = CreateIconIndirect(&iconInfo);
#else
	// Get color data from the color bitmap.
	BITMAP colorBitmap;
	GetObject(bitmap.handle, sizeof(colorBitmap), &colorBitmap);
	int colorDataSize = colorBitmap.bmWidthBytes * colorBitmap.bmHeight;
	BYTE *colorData = new BYTE[colorDataSize];
	GetBitmapBits(bitmap.handle, colorDataSize, colorData);

	// Get the mask data.
	BYTE *maskData;
	if (bitmap.mask)
	{
		// Get mask data from the mask bitmap.
		BITMAP maskBitmap;
		GetObject(bitmap.mask, sizeof(maskBitmap), &maskBitmap);
		int maskDataSize = maskBitmap.bmWidthBytes * maskBitmap.bmHeight;
		maskData = new BYTE[maskDataSize];
		GetBitmapBits(bitmap.mask, maskDataSize, maskData);
	}
	else
	{
		// Initialize mask data with no transparancy.
		int maskBytesPerLine = (icon.width + 7) / 8;
		maskBytesPerLine += maskBytesPerLine % sizeof(WORD);
		int maskDataSize = maskBytesPerLine * icon.height;
		maskData = new BYTE[maskDataSize];
		memset(maskData, 0, maskDataSize);
	}

	// Create the icon handle.
	icon.handle = CreateIcon(ZafMSWindowsApp::hInstance, icon.width, icon.height,
		1, colorBitmap.bmBitsPixel, maskData, colorData);

	// Clean up.
	delete []colorData;
	delete []maskData;
#endif

	// Clean up bitmap handles.
	if (bitmap.handle)
		DeleteObject(bitmap.handle);
	if (bitmap.mask)
		DeleteObject(bitmap.mask);

//???	// Free up the ZAF icon information.
//???	if (!icon.staticArray)
//???	{
//???		delete []icon.array;
//???		icon.array = ZAF_NULLP(ZafLogicalColor);
//???	}

	return (ZAF_ERROR_NONE);
}

ZafError ZafGdiDisplay::ConvertToZafIcon(ZafIconStruct &icon)
{
	if (!icon.handle)
		return (ZAF_ERROR_INVALID_SOURCE);

	// Create and intialize a bitmap struct for use in the icon conversion.
	ZafBitmapStruct bitmap;
	bitmap.array = ZAF_NULLP(ZafLogicalColor);
	bitmap.staticArray = false;
	bitmap.staticHandle = true;

#if defined(ZAF_WIN32)
	// Get ICONINFO from the icon handle.
	ICONINFO iconInfo;
	GetIconInfo(icon.handle, &iconInfo);

	// Get bitmap handles from the icon info.
	bitmap.handle = iconInfo.hbmColor;
	bitmap.mask = iconInfo.hbmMask;

	// Convert the ZAF bitap.
	ConvertToZafBitmap(bitmap);
#else
	// Create color bitmap and mask bitmap handles from the icon handle.
	icon.width = GetSystemMetrics(SM_CXICON);
	icon.height = GetSystemMetrics(SM_CYICON);

	// Get a device context for the convertsion.
	HDC convertDC;
	HPALETTE savePalette = 0;
	if (hDC)
		convertDC = hDC;
	else
	{
		// Get the device context of the screen.
		convertDC = GetDC(0);
		savePalette = SelectPalette(convertDC, logicalPalette, FALSE);
	}

	// Realize logicalPalette.
	RealizePalette(convertDC);

	// Create 2 memory device contexts.
	HDC memDC1 = CreateCompatibleDC(convertDC);
	HDC memDC2 = CreateCompatibleDC(convertDC);

	// Create a color bitmap.
	bitmap.handle = CreateCompatibleBitmap(convertDC, icon.width, icon.height);

	// Release the conversion device context.
	if (!hDC)
	{
		SelectPalette(convertDC, savePalette, FALSE);
		ReleaseDC(0, convertDC);
	}

	// Select the color bitmap into memDC1;
	SelectObject(memDC1, bitmap.handle);

	// Draw the icon on to the color bitmap with black in transparent areas.
	BitBlt(memDC1, 0, 0, icon.width, icon.height, 0, 0, 0, BLACKNESS);
	DrawIcon(memDC1, 0, 0, icon.handle);

	// Create two mono (mask) bitmaps.
	bitmap.mask = CreateBitmap(icon.width, icon.height, 1, 1, 0);
	HBITMAP tempBitmap = CreateBitmap(icon.width, icon.height, 1, 1, 0);

	// Select the mask bitmaps into memDC1, and memDC2.
	SelectObject(memDC1, bitmap.mask);
	SelectObject(memDC2, tempBitmap);

	// Draw the icon on to the mask bitmaps so that when finished
	// 'bitmap.mask' has pixels set only in transparent areas.
	BitBlt(memDC1, 0, 0, icon.width, icon.height, 0, 0, 0, BLACKNESS);
	DrawIcon(memDC1, 0, 0, icon.handle);
	BitBlt(memDC2, 0, 0, icon.width, icon.height, 0, 0, 0, WHITENESS);
	DrawIcon(memDC2, 0, 0, icon.handle);
	BitBlt(memDC1, 0, 0, icon.width, icon.height, memDC2, 0, 0, SRCERASE);

	// Convert the bitmap.
	ConvertToZafBitmap(bitmap);

	// Clean up.
	DeleteDC(memDC1);
	DeleteDC(memDC2);
	DeleteObject(bitmap.handle);
	DeleteObject(bitmap.mask);
	DeleteObject(tempBitmap);
#endif

	// Copy the bitmap information.
	icon.width = bitmap.width;
	icon.height = bitmap.height;
	if (icon.array && !icon.staticArray)
		delete []icon.array;
	icon.array = bitmap.array;
	icon.staticArray = false;

	return (ZAF_ERROR_NONE);
}

ZafError ZafGdiDisplay::DestroyOSIcon(ZafIconStruct &icon)
{
	// Destroy the handle.
	if (icon.handle && !icon.StaticHandle())
		DestroyIcon(icon.handle);
	icon.handle = 0;
	return (ZAF_ERROR_NONE);
}

ZafError ZafGdiDisplay::DestroyZafIcon(ZafIconStruct &icon)
{
	// Destroy the icon array.
	if (icon.array && !icon.StaticArray())
		delete []icon.array;
	icon.array = 0;
	return (ZAF_ERROR_NONE);
}

// --- Mouse -----------------------------------------------------------------

//??? Not implemented yet.
ZafError ZafGdiDisplay::Mouse(ZafCoordinate, ZafCoordinate, ZafMouseStruct &)
{
	return (ZAF_ERROR_NONE);
}

ZafError ZafGdiDisplay::ConvertToOSMouse(ZafMouseStruct &mouse)
{
//???#if defined(ZAF_WIN32)
//???	// Create and convert a bitmap struct for use in the icon conversion.
//???	ZafBitmapStruct bitmap;
//???	bitmap.width = mouse.width;
//???	bitmap.height = mouse.height;
//???	bitmap.array = mouse.array;
//???	bitmap.staticArray = true;
//???	bitmap.handle = 0;
//???	bitmap.mask = 0;
//???	ConvertToOSBitmap(bitmap);
//???
//???	// Initialize an ICONINFO structure.
//???	ICONINFO iconInfo;
//???	iconInfo.fIcon = FALSE;
//???	iconInfo.xHotspot =	mouse.hotSpotX;
//???	iconInfo.yHotspot = mouse.hotSpotY;
//???	if (!bitmap.mask)
//???	{
//???		// Compute scan line size.
//???		int maskBytesPerLine = (mouse.width + 7) / 8;
//???		maskBytesPerLine += maskBytesPerLine % sizeof(WORD);
//???
//???		// Create a mask bitmap with no transparancy.
//???		BYTE *maskData = new BYTE[maskBytesPerLine * mouse.height];
//???		memset(maskData, 0, maskBytesPerLine * mouse.height);
//???		iconInfo.hbmMask = CreateBitmap(mouse.width, mouse.height, 1, 1, maskData);
//???		delete []maskData;
//???	}
//???	else
//???		iconInfo.hbmMask = bitmap.mask;
//???	iconInfo.hbmColor = bitmap.handle;
//???
//???	// Create Icon.
//???	mouse.cursor = CreateIconIndirect(&iconInfo);
//???#else
	int startRow = (32 - mouse.height) / 2;
	int endRow = startRow + mouse.height - 1;
	int startColumn = (32 - mouse.width) / 2;
	int endColumn = startColumn + mouse.width - 1;

	ZafUInt8 andData[128], xorData[128];
	int index = 0;
	for (int row = 0; row < 32; row++)
	{
		if (startRow <= row && row <= endRow)
		{
			int column = 0;
			for (int byte = 0; byte < 4; byte++)
			{
				andData[row * 4 + byte] = xorData[row * 4 + byte] = 0;
				ZafUInt8 bitmask = 0x80;
				for (int bit = 0; bit < 8; bit++)
				{
					if (startColumn <= column && column <= endColumn)
					{
						ZafLogicalColor color = mouse.array[index++];
						if (color == ZAF_CLR_BACKGROUND)
							andData[row * 4 + byte] |= bitmask;
						else if (color != ZAF_CLR_BLACK)
							xorData[row * 4 + byte] |= bitmask;
					}
					else
						andData[row * 4 + byte] |= bitmask;

					column++;
					bitmask = (ZafUInt8)(bitmask >> 1);
				}
			}
		}
		else
		{
			*((ZafUInt32 *)&andData[row * 4]) = 0xFFFFFFFFL;
			*((ZafUInt32 *)&xorData[row * 4]) = 0L;
		}
	}

	mouse.handle = CreateCursor(ZafMSWindowsApp::hInstance,
		startColumn + mouse.hotSpotX, startRow + mouse.hotSpotY,
		32, 32, andData, xorData);
//???#endif
	return (ZAF_ERROR_NONE);
}

//??? Not implemented yet.
ZafError ZafGdiDisplay::ConvertToZafMouse(ZafMouseStruct &)
{
	return (ZAF_ERROR_NONE);
}

//??? Not implemented yet.
ZafError ZafGdiDisplay::DestroyOSMouse(ZafMouseStruct &mouseStruct)
{
	if (mouseStruct.handle && !mouseStruct.staticHandle)
		DestroyCursor(mouseStruct.handle);
	return (ZAF_ERROR_NONE);
}

//??? Not implemented yet.
ZafError ZafGdiDisplay::DestroyZafMouse(ZafMouseStruct &)
{
	return (ZAF_ERROR_NONE);
}

// --- Text -----------------------------------------------------------------

ZafError ZafGdiDisplay::Text(ZafCoordinate left, ZafCoordinate top, ZafCoordinate right, ZafCoordinate bottom,
	const ZafIChar *text, int length,
	ZafHzJustify hzJustify,
	ZafVtJustify vtJustify,
	int hotKeyIndex, bool fill)
{
	if (!text || !*text)
		return (ZAF_ERROR_NONE);

	int pixelLeft = (int)ConvertXValue(left, coordinateType, ZAF_PIXEL);
	int pixelTop = (int)ConvertYValue(top, coordinateType, ZAF_PIXEL);
	int pixelRight = (int)ConvertXValue(right, coordinateType, ZAF_PIXEL);
	int pixelBottom = (int)ConvertYValue(bottom, coordinateType, ZAF_PIXEL);
	int nonScaledWidth = pixelRight - pixelLeft + 1;
	int nonScaledHeight = pixelBottom - pixelTop + 1;
	pixelLeft = (int)((long)pixelLeft * scaleNumerator / scaleDenominator + originX);
	pixelTop = (int)((long)pixelTop * scaleNumerator / scaleDenominator + originY);
	pixelRight = (int)((long)pixelRight * scaleNumerator / scaleDenominator + originX);
	pixelBottom = (int)((long)pixelBottom * scaleNumerator / scaleDenominator + originY);

	// Set background mode.
	if (fill)
		SetBkMode(hDC, OPAQUE);

	// Set format flags for Windows API.
	const ZafIChar *drawText;
	ZafIChar *hotKeyText = ZAF_NULLP(ZafIChar);
	UINT wFormat = DT_SINGLELINE | DT_NOCLIP;
	if (hotKeyIndex > -1 && hotKeyIndex < (int)strlen(text))
	{
		hotKeyText = ZafMSWindowsApp::HotKeyText(text, hotKeyIndex);
		drawText = hotKeyText;
	}
	else
	{
		drawText = text;
		wFormat |= DT_NOPREFIX;
	}

	// Set horizontal justification flags.
	if (hzJustify == ZAF_HZ_CENTER)
		wFormat |= DT_CENTER;
	else if (hzJustify == ZAF_HZ_RIGHT)
		wFormat |= DT_RIGHT;
	else
		wFormat |= DT_LEFT;

	// Set vertical justification flags.
	if (vtJustify == ZAF_VT_TOP)
		wFormat |= DT_TOP;
	else if (vtJustify == ZAF_VT_BOTTOM)
		wFormat |= DT_BOTTOM;
	else
		wFormat |= DT_VCENTER;

	if (mode != ZAF_MODE_COPY || Scale() != 100)
	{
		// Create a memory device context (DC) with an associated bitmap.
		HDC memDC = CreateCompatibleDC(hDC);
		HBITMAP textBitmap = CreateCompatibleBitmap(hDC, nonScaledWidth, nonScaledHeight);
		SelectObject(memDC, textBitmap);

		// Match the memory DC attributes with the current DC.
		SetTextColor(memDC, GetTextColor(hDC));
		SetBkColor(memDC, GetBkColor(hDC));
		SetBkMode(memDC, GetBkMode(hDC));
		SelectObject(memDC, fontTable[palette.font]);

		if (mode != ZAF_MODE_COPY)
			// Initialize the memory bitmap with black.  (Black has no effect in
			// XOR operations.)
			BitBlt(memDC, 0, 0, pixelRight - pixelLeft + 1, pixelBottom - pixelTop + 1, 0, 0, 0, BLACKNESS);
		else if (!fill)
			StretchBlt(memDC, 0, 0, nonScaledWidth, nonScaledHeight, hDC, pixelLeft, pixelTop, pixelRight - pixelLeft + 1, pixelBottom - pixelTop + 1, SRCCOPY);

		// Draw the text to the memory bitmap.
		RECT rect = { 0, 0, nonScaledWidth, nonScaledHeight };
		if (ZafMSWindowsApp::convertText)
		{
			int tLength = length;
			if (tLength < 0)
				tLength = strlen(drawText);
			ZafIChar *tDrawText = new ZafIChar[tLength + 1];
			tDrawText[tLength] = 0;
			strncpy(tDrawText, drawText, tLength);
			char *osText = ZafConvertToOSString(tDrawText);
			delete []tDrawText;
#if defined(ZAF_WIN32) && defined(ZAF_UNICODE)
			length = strlen(osText);
			DrawTextA(memDC, osText, length, &rect, wFormat);
#else
			DrawText(memDC, osText, length, &rect, wFormat);
#endif
			delete []osText;
		}
#if defined(ZAF_WIN32) || !defined(ZAF_UNICODE)
		else
			DrawText(memDC, drawText, length, &rect, wFormat);
#endif

		// XOR the memory bitmap onto the screen.
		if (Scale() == 100)
			BitBlt(hDC, pixelLeft, pixelTop, pixelRight - pixelLeft + 1, pixelBottom - pixelTop + 1, memDC, 0, 0, modeTable[mode].ternary);
		else
			StretchBlt(hDC, pixelLeft, pixelTop, pixelRight - pixelLeft + 1, pixelBottom - pixelTop + 1, memDC, 0, 0, nonScaledWidth, nonScaledHeight, modeTable[mode].ternary);

		// Clean up.
		DeleteDC(memDC);
		DeleteObject(textBitmap);
	}
	else
	{
		// Draw the text.
		RECT rect = { pixelLeft, pixelTop, pixelRight + 1, pixelBottom + 1 };
		if (ZafMSWindowsApp::convertText)
		{
			int tLength = length;
			if (tLength < 0)
				tLength = strlen(drawText);
			ZafIChar *tDrawText = new ZafIChar[tLength + 1];
			tDrawText[tLength] = 0;
			strncpy(tDrawText, drawText, tLength);
			char *osText = ZafConvertToOSString(tDrawText);
			delete []tDrawText;
#if defined(ZAF_WIN32) && defined(ZAF_UNICODE)
			length = strlen(osText);
			DrawTextA(hDC, osText, length, &rect, wFormat);
#else
			DrawText(hDC, osText, length, &rect, wFormat);
#endif
			delete []osText;
		}
#if defined(ZAF_WIN32) || !defined(ZAF_UNICODE)
		else
			DrawText(hDC, drawText, length, &rect, wFormat);
#endif
	}

	if (hotKeyText)
		delete []hotKeyText;

	// Restore the background mode.
	if (fill)
		SetBkMode(hDC, TRANSPARENT);

	return (ZAF_ERROR_NONE);
}

ZafError ZafGdiDisplay::Text(ZafCoordinate left, ZafCoordinate top, const ZafIChar *text, int length,
	int hotKeyIndex, bool fill)
{
	// Get the text size.
	ZafRegionStruct textSize = TextSize(text, length);

	// Draw the text (textSize.right and textSize.bottom are width & height).
	return (Text(left, top,
		left + textSize.right - 1, top + textSize.bottom - 1, text, length,
		ZAF_HZ_LEFT, ZAF_VT_TOP, hotKeyIndex, fill));
}

ZafRegionStruct ZafGdiDisplay::TextSize(const ZafIChar *text, int length)
{
//???Regions?
	ZafRegionStruct textRegion;
	textRegion.Assign(1, 1, 0, 0, ZAF_PIXEL);
	if (!text || !*text)
		return (textRegion);

	// Get the text size.
	SIZE size;
	if (ZafMSWindowsApp::convertText)
	{
		char *osSizeText = ZafConvertToOSString(text);
#if defined(ZAF_WIN32)
		if (ZafMSWindowsApp::windowsPlatform == ZAF_WIN32S)
			GetTextExtentPointA(hDC, osSizeText, length >= 0 ? length : strlen(osSizeText), &size);
		else
#	if defined(ZAF_UNICODE)
			GetTextExtentPoint32A(hDC, osSizeText, length >= 0 ? length : strlen(osSizeText), &size);
#	else
			GetTextExtentPoint32(hDC, osSizeText, length >= 0 ? length : strlen(osSizeText), &size);
#	endif
#else
		GetTextExtentPoint(hDC, osSizeText, length >= 0 ? length : strlen(osSizeText), &size);
#endif
		delete []osSizeText;
	}
#if defined(ZAF_WIN32) || !defined(ZAF_UNICODE)
	else
#	if defined(ZAF_WIN32)
		if (ZafMSWindowsApp::windowsPlatform == ZAF_WIN32S)
			GetTextExtentPoint(hDC, text, length >= 0 ? length : strlen(text), &size);
		else
			GetTextExtentPoint32(hDC, text, length >= 0 ? length : strlen(text), &size);
#	else
		GetTextExtentPoint(hDC, text, length >= 0 ? length : strlen(text), &size);
#	endif
#endif

	// Return a bounding rectangle for the text.
	// left and top stay 1 so that right and bottom contain the width & height.
	textRegion.right = (int)ConvertXValue(size.cx, ZAF_PIXEL, coordinateType);
	textRegion.bottom = (int)ConvertYValue(size.cy, ZAF_PIXEL, coordinateType);
	textRegion.coordinateType = coordinateType;
	return (textRegion);
}


// --- Primitives -----------------------------------------------------------

ZafError ZafGdiDisplay::Ellipse(ZafCoordinate left, ZafCoordinate top, ZafCoordinate right, ZafCoordinate bottom,
	float startAngle, float endAngle, int width, bool fill)
{
	int pixelLeft = (int)(ConvertXValue(left, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelTop = (int)(ConvertYValue(top, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	int pixelRight = (int)(ConvertXValue(right, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelBottom = (int)(ConvertYValue(bottom, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	int pixelWidth = (width > 0) ? ZafMax((int)(ConvertXValue(width, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator), 1) : width;
	if (pixelWidth == 1)
		pixelWidth = ZAF_HAIR_LINE;

	// Normalize the start and end angles of the ellipse.
	while (startAngle > 360.0)
		startAngle -= (float)360.0;
	while (startAngle < 0.0)
		startAngle += (float)360.0;
	while (endAngle > 360.0)
		endAngle -= (float)360.0;
	while (endAngle < 0.0)
		endAngle += (float)360.0;
	if ((endAngle - startAngle) == 360.0)
		endAngle = startAngle;

	// Convert degrees to radians.
	const double pieOver180 = 0.01745329278;
	double startRad = startAngle * pieOver180;
	double endRad = endAngle * pieOver180;

	// Define starting and ending radial lines for use in the windows API.
	// 10000 is an arbitrary "r" value for the radial lines' length.
	double centerX = (pixelLeft + pixelRight) / 2.0;
	double centerY = (pixelTop + pixelBottom) / 2.0;
	int startX = (int)(centerX + 10000.0 * cos(startRad));
	int startY = (int)(centerY - 10000.0 * sin(startRad));
	int endX = (int)(centerX + 10000.0 * cos(endRad));
	int endY = (int)(centerY - 10000.0 * sin(endRad));

	// Set the pen width.
	if (pixelWidth != 1)
	{
		HPEN newPen = GetGdiCachePen(palette, pixelWidth);
		SelectObject(hDC, newPen);
	}

	if (fill)
	{
		if (startAngle == endAngle || (endAngle - startAngle) == 360.0)
		{
			// Draw a complete ellipse.
			::Ellipse(hDC, pixelLeft, pixelTop, pixelRight, pixelBottom);
		}
		else
		{
			// Draw a pie slice.
			Pie(hDC, pixelLeft, pixelTop, pixelRight, pixelBottom, startX - 1, startY - 1,
				endX - 1, endY - 1);
		}
	}
	else
	{
		// Draw a non-filled ellipse or arc.
		Arc(hDC, pixelLeft, pixelTop, pixelRight, pixelBottom, startX, startY, endX, endY);
	}

	// Restore the pen width.
	if (pixelWidth != 1)
	{
		HPEN hOldPen = (HPEN)SelectObject(hDC, hPen);
		ReleaseGdiCacheObject(hOldPen);
	}

	return (ZAF_ERROR_NONE);
}

ZafError ZafGdiDisplay::Line(ZafCoordinate column1, ZafCoordinate line1, ZafCoordinate column2, ZafCoordinate line2,
	int width)
{
	int pixelColumn1 = (int)(ConvertXValue(column1, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelLine1 = (int)(ConvertYValue(line1, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	int pixelColumn2 = (int)(ConvertXValue(column2, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelLine2 = (int)(ConvertYValue(line2, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	int pixelWidth = (width > 0) ? ZafMax((int)(ConvertXValue(width, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator), 1) : width;
	if (pixelWidth == 1)
		pixelWidth = ZAF_HAIR_LINE;

	int endPointX = pixelColumn2, endPointY = pixelLine2;

	if (pixelWidth == ZAF_HAIR_LINE)
	{
		// Calculate a new end point that is one pixel beyond the specified
		// endpoint because the Window's LineTo() API doesnt include the
		// end point when it draws.
		if (pixelColumn1 == pixelColumn2)
			// The specified line is vertical, extend the "y" coordinate
			// of the end point.
			(pixelLine1 < pixelLine2) ? endPointY++ : endPointY--;
		else if (pixelLine1 == pixelLine2)
			// The specified line is horizontal, extend the "x" coordinate
			// of the end point.
			(pixelColumn1 < pixelColumn2) ? endPointX++ : endPointX--;
		else
		{
			// Calculate the slope of the line.  (Divide by zero errors are
			// avoided by the code above.)
			long slope = (pixelLine2 - pixelLine1) * 1000L / (pixelColumn2 - pixelColumn1);
			if (slope < 1000 && slope > -1000)
			{
				// This specified line is less than 45 degrees from horizontal.
				// The "x" coordinate of the enpoint can be extended, and the
				// "y" coordinate can be calculated using the slope-intercept
				// formula.
				int deltaX = pixelColumn2 - pixelColumn1;
				(deltaX > 0) ? deltaX++ : deltaX--;
				endPointX = pixelColumn1 + deltaX;
				endPointY = (int)(pixelLine1 + deltaX * slope / 1000L);
			}
			else
			{
				// This specified line is more than 45 degrees from horizontal.
				// The "y" coordinate of the enpoint can be extended, and the
				// "x" coordinate can be calculated using the slope-intercept
				// formula.
				int deltaY = pixelLine2 - pixelLine1;
				(deltaY > 0) ? deltaY++ : deltaY--;
				endPointX = (int)(pixelColumn1 + deltaY * 1000L / slope);
				endPointY = pixelLine1 + deltaY;
			}
		}
	}

	// Set the pen width.
	if (pixelWidth != 1)
	{
		HPEN newPen = GetGdiCachePen(palette, pixelWidth);
		SelectObject(hDC, newPen);
	}

	// Draw the line.
	MoveToEx(hDC, pixelColumn1, pixelLine1, NULL);
	LineTo(hDC, endPointX, endPointY);

	// Restore the pen width.
	if (pixelWidth != 1)
	{
		HPEN hOldPen = (HPEN)SelectObject(hDC, hPen);
		ReleaseGdiCacheObject(hOldPen);
	}

	return (ZAF_ERROR_NONE);
}

ZafError ZafGdiDisplay::Pixel(ZafCoordinate column, ZafCoordinate line, ZafLogicalColor color, ZafLogicalColor)
{
	int pixelColumn = (int)(ConvertXValue(column, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelLine = (int)(ConvertYValue(line, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);

	// Get the color.
	ZafLogicalColor drawColor = (color == ZAF_CLR_DEFAULT) ? palette.colorForeground : color;
	COLORREF colorRef = (drawColor == ZAF_CLR_DEFAULT) ?
		palette.osPalette.colorForeground : colorTable[drawColor];

	// Draw the pixel.
	SetPixel(hDC, pixelColumn, pixelLine, colorRef);

	return (ZAF_ERROR_NONE);
}

ZafError ZafGdiDisplay::Polygon(int numPoints, const ZafCoordinate *polygonPoints,
	int width, bool fill, bool close)
{
	int pixelWidth = (width > 0) ? ZafMax((int)(ConvertXValue(width, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator), 1) : width;
	if (pixelWidth == 1)
		pixelWidth = ZAF_HAIR_LINE;

	// Allocate Windows polygon data with space to "close" polygon if necessary.
	POINT *polygon = new POINT[numPoints + 1];

	// Initialize the polygon points.
	for (int i = 0; i < numPoints; i++)
	{
		polygon[i].x = (int)(ConvertXValue(polygonPoints[i*2], coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
		polygon[i].y = (int)(ConvertYValue(polygonPoints[i*2+1], coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	}

	// Close the polygon if specified.
	if (close && (polygon[0].x != polygon[numPoints - 1].x ||
		polygon[0].y != polygon[numPoints - 1].y))
	{
		polygon[numPoints].x = polygon[0].x;
		polygon[numPoints].y = polygon[0].y;
		numPoints++;
	}

	// Set the pen width.
	if (pixelWidth != 1)
	{
		HPEN newPen = GetGdiCachePen(palette, pixelWidth);
		SelectObject(hDC, newPen);
	}

	// Draw the polygon.
	if (fill)
		::Polygon(hDC, polygon, numPoints);
	else
		Polyline(hDC, polygon, numPoints);

	// Restore the pen width.
	if (pixelWidth != 1)
	{
		HPEN hOldPen = (HPEN)SelectObject(hDC, hPen);
		ReleaseGdiCacheObject(hOldPen);
	}

	// Clean up.
	delete []polygon;

	return (ZAF_ERROR_NONE);
}

ZafError ZafGdiDisplay::Rectangle(ZafCoordinate left, ZafCoordinate top, ZafCoordinate right, ZafCoordinate bottom,
	int width, bool fill)
{
	int pixelLeft = (int)(ConvertXValue(left, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelTop = (int)(ConvertYValue(top, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	int pixelRight = (int)(ConvertXValue(right, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelBottom = (int)(ConvertYValue(bottom, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	int pixelWidth = (width > 0) ? ZafMax((int)(ConvertXValue(width, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator), 1) : width;
	if (pixelWidth == 1)
		pixelWidth = ZAF_HAIR_LINE;

	// Set the pen width.
	if (pixelWidth > 1)
	{
		HPEN newPen = GetGdiCachePen(palette, pixelWidth);
		SelectObject(hDC, newPen);
	}

	// Set background mode.
	HBRUSH oldBrush;
	if (!fill)
		oldBrush = (HBRUSH)SelectObject(hDC, GetStockObject(NULL_BRUSH));

	// Draws outline with current pen and fills with current brush.
	if (pixelWidth == 0)
	{
		RECT rect;
		rect.left = pixelLeft;
		rect.top = pixelTop;
		rect.right = pixelRight + 1;
		rect.bottom = pixelBottom + 1;
		FillRect(hDC, &rect, hBrush);
	}
	else
		::Rectangle(hDC, pixelLeft, pixelTop, pixelRight + 1, pixelBottom + 1);

	// Restore the brush.
	if (!fill)
		SelectObject(hDC, oldBrush);

	// Restore the pen.
	if (pixelWidth > 1)
	{
		HPEN hOldPen = (HPEN)SelectObject(hDC, hPen);
		ReleaseGdiCacheObject(hOldPen);
	}

	return (ZAF_ERROR_NONE);
}

ZafError ZafGdiDisplay::RectangleXORDiff(const ZafRegionStruct *oldRegion,
	const ZafRegionStruct *newRegion)
{
	RECT rect;
	if (newRegion)
	{
		// Convert to Windows region.
		rect.left = newRegion->left;
		rect.top = newRegion->top;
		rect.right = newRegion->right + 1;
		rect.bottom = newRegion->bottom + 1;

		// Draw new rectagle.
		DrawFocusRect(hDC, &rect);
	}

	if (oldRegion)
	{
		// Convert to Windows region.
		rect.left = oldRegion->left;
		rect.top = oldRegion->top;
		rect.right = oldRegion->right + 1;
		rect.bottom = oldRegion->bottom + 1;

		// Erase old rectagle.
		DrawFocusRect(hDC, &rect);
	}

	return (ZAF_ERROR_NONE);
}


// --- Drawing support ------------------------------------------------------

ZafError ZafGdiDisplay::RegionCopy(const ZafRegionStruct &region,
	int newColumn, int newLine)
{
	// Get a screen device context if the current context is NULL.
	HDC moveDC = hDC ? hDC : GetDC(0);

	// Move the region by performing a "bitblt" operation.
	BitBlt(moveDC, newColumn, newLine, region.Width(), region.Height(),
		moveDC, region.left, region.top, SRCCOPY);

	// Release the screen device context.
	if (!hDC)
		ReleaseDC(0, moveDC);

	return (ZAF_ERROR_NONE);
}

// --- Get -----------------------------------------------------------------

ZafRegionStruct ZafGdiDisplay::ClipRegion(void) const
{
	// Get the current clipping region.
	RECT clip;
	GetClipBox(hDC, &clip);

	ZafRegionStruct region;
	region.ImportPixel(clip);
	return (region);
}

OSDisplayContext ZafGdiDisplay::DisplayContext(void) const
{
	// Return a handle to the current device contect.
	return (hDC);
}

OSDrawContext ZafGdiDisplay::DrawContext(void) const
{
	// Return a handle to the drawing window.
	return (hWnd);
}

// --- Set -----------------------------------------------------------------

ZafLogicalColor ZafGdiDisplay::SetBackground(ZafLogicalColor color)
{
	ZafLogicalColor oldColor = palette.colorBackground;
	palette.colorBackground = color;

	// Get the Windows color reference.
	COLORREF colorRef;
	if (color == ZAF_CLR_DEFAULT)
	{
		if (palette.osPalette.colorBackground != 0xFFFFFFFFL)
			colorRef = palette.osPalette.colorBackground;
		else
			colorRef = colorTable[ZAF_CLR_BLACK];
	}
	else
		colorRef = colorTable[color];

	// Set the background color.
	SetBkColor(hDC, colorRef);

	// Get a new background brush.
	HBRUSH newBrush = GetGdiCacheBrush(palette);

	// Select the new background brush into the DC.
	SelectObject(hDC, newBrush);

	// Release the old backgroud brush.
	if (hBrush)
		ReleaseGdiCacheObject(hBrush);

	hBrush = newBrush;

	return (oldColor);
}

ZafRegionStruct ZafGdiDisplay::SetClipRegion(const ZafRegionStruct &region)
{
	int pixelLeft = (int)(ConvertXValue(region.left, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelTop = (int)(ConvertYValue(region.top, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);
	int pixelRight = (int)(ConvertXValue(region.right, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originX);
	int pixelBottom = (int)(ConvertYValue(region.bottom, coordinateType, ZAF_PIXEL) * scaleNumerator / scaleDenominator + originY);

	ZafRegionStruct oldClipRegion = clipRegion;

	// Create a Windows region.
	HRGN hRegion = CreateRectRgn(pixelLeft, pixelTop, pixelRight + 1, pixelBottom + 1);

	// Copy the Windows region into the DC clipping region.  
	SelectClipRgn(hDC, hRegion);

	// Delete the Windows region.
	DeleteObject(hRegion);

	// Set the member data.
	clipRegion = region;

	return (oldClipRegion);
}

OSDisplayContext ZafGdiDisplay::SetDisplayContext(OSDisplayContext displayContext)
{
	HDC oldDC = hDC;

	hDC = displayContext;

	// Save the current DC attributes so that changes can be restored.
	SaveDC(hDC);

	// Select the logical color palette into the DC.
	SelectPalette(hDC, logicalPalette, FALSE);

	return (oldDC);
}

OSDrawContext ZafGdiDisplay::SetDrawContext(OSDrawContext)
{
	// Unimplemented.
	return (ZAF_NULLH(OSDrawContext));
}

ZafLogicalFont ZafGdiDisplay::SetFont(ZafLogicalFont newFont)
{
	ZafLogicalFont oldFont = palette.font;

	palette.font = (newFont == ZAF_FNT_DEFAULT) ? ZAF_FNT_DIALOG : newFont;

	// Select the new font into the DC.
	SelectObject(hDC, fontTable[palette.font]);

	return (oldFont);
}

ZafLogicalColor ZafGdiDisplay::SetForeground(ZafLogicalColor color)
{
	ZafLogicalColor oldColor = palette.colorForeground;
	palette.colorForeground = color;

	// Get the Windows color reference.
	COLORREF colorRef;
	if (color == ZAF_CLR_DEFAULT)
	{
		if (palette.osPalette.colorForeground != 0xFFFFFFFFL)
			colorRef = palette.osPalette.colorForeground;
		else
			colorRef = colorTable[ZAF_CLR_BLACK];
	}
	else
		colorRef = colorTable[color];


	// Set the text color.
	SetTextColor(hDC, colorRef);

	// Get a new pen.
	HPEN newPen = GetGdiCachePen(palette, 1);

	// Select the new pen into the DC.
	SelectObject(hDC, newPen);

	// Release the old pen.
	if (hPen)
		ReleaseGdiCacheObject(hPen);

	hPen = newPen;

	return (oldColor);
}

ZafLogicalLineStyle ZafGdiDisplay::SetLineStyle(ZafLogicalLineStyle newLineStyle)
{
	ZafLogicalLineStyle oldLineStyle = palette.lineStyle;

	if (newLineStyle != oldLineStyle)
	{
		palette.lineStyle = (newLineStyle == ZAF_LINE_DEFAULT) ?
			ZAF_LINE_SOLID : newLineStyle;

		// Get a new pen.
		HPEN newPen = GetGdiCachePen(palette, 1);

		// Select the new pen into the DC.
		SelectObject(hDC, newPen);

		// Release the old pen.
		ReleaseGdiCacheObject(hPen);

		hPen = newPen;
	}

	return (oldLineStyle);
}

ZafPaletteStruct ZafGdiDisplay::SetPalette(ZafPaletteStruct setPalette)
{
	ZafPaletteStruct oldPalette = palette;

	// Copy the OS specific palette information.
	palette.osPalette = setPalette.osPalette;

	// Set each of the palette elements.
	SetLineStyle(setPalette.lineStyle);
	SetFillPattern(setPalette.fillPattern);
	SetForeground(setPalette.colorForeground);
	SetBackground(setPalette.colorBackground);
	SetFont(setPalette.font);

	return (oldPalette);
}

ZafLogicalFillPattern ZafGdiDisplay::SetFillPattern(ZafLogicalFillPattern newFillPattern)
{
	ZafLogicalFillPattern oldFillPattern = palette.fillPattern;

	if (newFillPattern != oldFillPattern)
	{
		palette.fillPattern = (newFillPattern == ZAF_PTN_DEFAULT) ?
			ZAF_PTN_SOLID_FILL : newFillPattern;

		// Get a new brush.
		HBRUSH newBrush = GetGdiCacheBrush(palette);

		// Select the new brush into the DC.
		SelectObject(hDC, newBrush);

		// Release the old brush.
		if (hBrush)
			ReleaseGdiCacheObject(hBrush);

		hBrush = newBrush;
	}

	return (oldFillPattern);
}

ZafLogicalMode ZafGdiDisplay::SetMode(ZafLogicalMode setMode)
{
	ZafLogicalMode oldMode = mode;
	mode = setMode;

	// Set the raster operation (ROP) mode.
	SetROP2(hDC, modeTable[mode].binary);

	return (oldMode);
}

// --- Restore --------------------------------------------------------------

OSDisplayContext ZafGdiDisplay::RestoreDisplayContext(void)
{
	// Preserve DC handle to be returned.
	OSDisplayContext displayContext = hDC;

	if (hDC)
	{
		// Restore any changes made to the DC, and reset member variables.
		// This call does not release the DC.
		RestoreDC(hDC, -1);
		hDC = 0;

		// Clean up new pen and brush (if any).  (The original (default)
		// pen and brush were selected into the DC by the RestoreDC()
		// call above.)
		if (hPen)
		{
			ReleaseGdiCacheObject(hPen);
			hPen = 0;
		}
		if (hBrush)
		{
			ReleaseGdiCacheObject(hBrush);
			hBrush = 0;
		}
	}

	// Return the UNRELEASED device context.
	return (displayContext);
}

OSDrawContext ZafGdiDisplay::RestoreDrawContext(void)
{
	return (ZAF_NULLH(OSDrawContext));
}
