2011-03-05 04:17:57 +00:00
|
|
|
/* ScummVM - Graphic Adventure Engine
|
|
|
|
*
|
|
|
|
* ScummVM is the legal property of its developers, whose names
|
|
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
|
|
* file distributed with this source distribution.
|
|
|
|
*
|
|
|
|
* 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.
|
2014-02-18 01:34:20 +00:00
|
|
|
*
|
2011-03-05 04:17:57 +00:00
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
2014-02-18 01:34:20 +00:00
|
|
|
*
|
2011-03-05 04:17:57 +00:00
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2011-03-07 21:37:12 +00:00
|
|
|
#include "common/ptr.h"
|
2011-03-05 04:17:57 +00:00
|
|
|
#include "common/stream.h"
|
2011-04-24 08:34:27 +00:00
|
|
|
#include "common/textconsole.h"
|
2011-03-05 04:17:57 +00:00
|
|
|
|
|
|
|
#include "graphics/wincursor.h"
|
|
|
|
|
|
|
|
namespace Graphics {
|
|
|
|
|
2012-05-14 04:59:50 +00:00
|
|
|
/** A Windows cursor. */
|
|
|
|
class WinCursor : public Cursor {
|
|
|
|
public:
|
|
|
|
WinCursor();
|
|
|
|
~WinCursor();
|
|
|
|
|
|
|
|
/** Return the cursor's width. */
|
|
|
|
uint16 getWidth() const;
|
|
|
|
/** Return the cursor's height. */
|
|
|
|
uint16 getHeight() const;
|
|
|
|
/** Return the cursor's hotspot's x coordinate. */
|
|
|
|
uint16 getHotspotX() const;
|
|
|
|
/** Return the cursor's hotspot's y coordinate. */
|
|
|
|
uint16 getHotspotY() const;
|
|
|
|
/** Return the cursor's transparent key. */
|
|
|
|
byte getKeyColor() const;
|
|
|
|
|
|
|
|
const byte *getSurface() const { return _surface; }
|
|
|
|
|
|
|
|
const byte *getPalette() const { return _palette; }
|
|
|
|
byte getPaletteStartIndex() const { return 0; }
|
|
|
|
uint16 getPaletteCount() const { return 256; }
|
|
|
|
|
|
|
|
/** Read the cursor's data out of a stream. */
|
|
|
|
bool readFromStream(Common::SeekableReadStream &stream);
|
|
|
|
|
|
|
|
private:
|
|
|
|
byte *_surface;
|
|
|
|
byte _palette[256 * 3];
|
|
|
|
|
|
|
|
uint16 _width; ///< The cursor's width.
|
|
|
|
uint16 _height; ///< The cursor's height.
|
|
|
|
uint16 _hotspotX; ///< The cursor's hotspot's x coordinate.
|
|
|
|
uint16 _hotspotY; ///< The cursor's hotspot's y coordinate.
|
|
|
|
byte _keyColor; ///< The cursor's transparent key
|
|
|
|
|
|
|
|
/** Clear the cursor. */
|
|
|
|
void clear();
|
|
|
|
};
|
|
|
|
|
2011-03-05 04:17:57 +00:00
|
|
|
WinCursor::WinCursor() {
|
|
|
|
_width = 0;
|
|
|
|
_height = 0;
|
|
|
|
_hotspotX = 0;
|
|
|
|
_hotspotY = 0;
|
|
|
|
_surface = 0;
|
|
|
|
_keyColor = 0;
|
|
|
|
memset(_palette, 0, 256 * 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
WinCursor::~WinCursor() {
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16 WinCursor::getWidth() const {
|
|
|
|
return _width;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16 WinCursor::getHeight() const {
|
|
|
|
return _height;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16 WinCursor::getHotspotX() const {
|
|
|
|
return _hotspotX;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16 WinCursor::getHotspotY() const {
|
|
|
|
return _hotspotY;
|
|
|
|
}
|
|
|
|
|
|
|
|
byte WinCursor::getKeyColor() const {
|
|
|
|
return _keyColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WinCursor::readFromStream(Common::SeekableReadStream &stream) {
|
|
|
|
clear();
|
|
|
|
|
|
|
|
_hotspotX = stream.readUint16LE();
|
|
|
|
_hotspotY = stream.readUint16LE();
|
|
|
|
|
|
|
|
// Check header size
|
|
|
|
if (stream.readUint32LE() != 40)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Check dimensions
|
|
|
|
_width = stream.readUint32LE();
|
|
|
|
_height = stream.readUint32LE() / 2;
|
|
|
|
|
2011-03-07 00:53:17 +00:00
|
|
|
if (_width & 3) {
|
|
|
|
// Cursors should always be a power of 2
|
|
|
|
// Of course, it wouldn't be hard to handle but if we have no examples...
|
|
|
|
warning("Non-divisible-by-4 width cursor found");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-03-05 04:17:57 +00:00
|
|
|
// Color planes
|
|
|
|
if (stream.readUint16LE() != 1)
|
|
|
|
return false;
|
2011-03-07 00:53:17 +00:00
|
|
|
|
2020-07-21 19:57:39 +00:00
|
|
|
// Only 1bpp, 4bpp and 8bpp supported
|
2011-03-07 00:53:17 +00:00
|
|
|
uint16 bitsPerPixel = stream.readUint16LE();
|
2020-07-21 19:57:39 +00:00
|
|
|
if (bitsPerPixel != 1 && bitsPerPixel != 4 && bitsPerPixel != 8)
|
2011-03-05 04:17:57 +00:00
|
|
|
return false;
|
2011-03-07 00:53:17 +00:00
|
|
|
|
2011-03-05 04:17:57 +00:00
|
|
|
// Compression
|
|
|
|
if (stream.readUint32LE() != 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Image size + X resolution + Y resolution
|
|
|
|
stream.skip(12);
|
|
|
|
|
|
|
|
uint32 numColors = stream.readUint32LE();
|
|
|
|
|
2011-03-07 00:53:17 +00:00
|
|
|
// If the color count is 0, then it uses up the maximum amount
|
2011-03-05 04:17:57 +00:00
|
|
|
if (numColors == 0)
|
2011-03-07 00:53:17 +00:00
|
|
|
numColors = 1 << bitsPerPixel;
|
2011-03-05 04:17:57 +00:00
|
|
|
|
|
|
|
// Reading the palette
|
|
|
|
stream.seek(40 + 4);
|
|
|
|
for (uint32 i = 0 ; i < numColors; i++) {
|
2011-03-07 00:53:17 +00:00
|
|
|
_palette[i * 3 + 2] = stream.readByte();
|
|
|
|
_palette[i * 3 + 1] = stream.readByte();
|
|
|
|
_palette[i * 3 ] = stream.readByte();
|
2011-03-05 04:17:57 +00:00
|
|
|
stream.readByte();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reading the bitmap data
|
|
|
|
uint32 dataSize = stream.size() - stream.pos();
|
|
|
|
byte *initialSource = new byte[dataSize];
|
|
|
|
stream.read(initialSource, dataSize);
|
|
|
|
|
2011-03-07 00:53:17 +00:00
|
|
|
// Parse the XOR map
|
|
|
|
const byte *src = initialSource;
|
2011-03-05 04:17:57 +00:00
|
|
|
_surface = new byte[_width * _height];
|
|
|
|
byte *dest = _surface + _width * (_height - 1);
|
2011-03-07 00:53:17 +00:00
|
|
|
uint32 imagePitch = _width * bitsPerPixel / 8;
|
2011-03-05 04:17:57 +00:00
|
|
|
|
|
|
|
for (uint32 i = 0; i < _height; i++) {
|
|
|
|
byte *rowDest = dest;
|
|
|
|
|
2011-03-07 00:53:17 +00:00
|
|
|
if (bitsPerPixel == 1) {
|
|
|
|
// 1bpp
|
2011-03-19 00:30:03 +00:00
|
|
|
for (uint16 j = 0; j < (_width / 8); j++) {
|
2011-03-07 00:53:17 +00:00
|
|
|
byte p = src[j];
|
2011-03-05 04:17:57 +00:00
|
|
|
|
2011-03-07 00:53:17 +00:00
|
|
|
for (int k = 0; k < 8; k++, rowDest++, p <<= 1) {
|
2011-03-05 04:17:57 +00:00
|
|
|
if ((p & 0x80) == 0x80)
|
|
|
|
*rowDest = 1;
|
2011-03-07 00:53:17 +00:00
|
|
|
else
|
|
|
|
*rowDest = 0;
|
|
|
|
}
|
2011-03-05 04:17:57 +00:00
|
|
|
}
|
2020-07-21 19:57:39 +00:00
|
|
|
} else if (bitsPerPixel == 4) {
|
|
|
|
// 4bpp
|
|
|
|
for (uint16 j = 0; j < (_width / 2); j++) {
|
|
|
|
byte p = src[j];
|
|
|
|
*rowDest++ = p >> 4;
|
|
|
|
*rowDest++ = p & 0x0f;
|
|
|
|
}
|
2011-03-07 00:53:17 +00:00
|
|
|
} else {
|
|
|
|
// 8bpp
|
|
|
|
memcpy(rowDest, src, _width);
|
2011-03-05 04:17:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dest -= _width;
|
2011-03-07 00:53:17 +00:00
|
|
|
src += imagePitch;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate our key color
|
|
|
|
if (numColors < 256) {
|
|
|
|
// If we're not using the maximum colors in a byte, we can fit it in
|
|
|
|
_keyColor = numColors;
|
|
|
|
} else {
|
2011-03-07 05:53:40 +00:00
|
|
|
// HACK: Try to find a color that's not being used so it can become
|
|
|
|
// our keycolor. It's quite impossible to fit 257 entries into 256...
|
|
|
|
for (uint32 i = 0; i < 256; i++) {
|
|
|
|
for (int j = 0; j < _width * _height; j++) {
|
|
|
|
// TODO: Also check to see if the space is transparent
|
|
|
|
|
|
|
|
if (_surface[j] == i)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (j == _width * _height - 1) {
|
|
|
|
_keyColor = i;
|
|
|
|
i = 256;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-03-07 00:53:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Now go through and apply the AND map to get the transparency
|
|
|
|
uint32 andWidth = (_width + 7) / 8;
|
|
|
|
src += andWidth * (_height - 1);
|
|
|
|
|
|
|
|
for (uint32 y = 0; y < _height; y++) {
|
|
|
|
for (uint32 x = 0; x < _width; x++)
|
|
|
|
if (src[x / 8] & (1 << (7 - x % 8)))
|
|
|
|
_surface[y * _width + x] = _keyColor;
|
|
|
|
|
|
|
|
src -= andWidth;
|
2011-03-05 04:17:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
delete[] initialSource;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WinCursor::clear() {
|
|
|
|
delete[] _surface; _surface = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
WinCursorGroup::WinCursorGroup() {
|
|
|
|
}
|
|
|
|
|
|
|
|
WinCursorGroup::~WinCursorGroup() {
|
|
|
|
for (uint32 i = 0; i < cursors.size(); i++)
|
|
|
|
delete cursors[i].cursor;
|
|
|
|
}
|
|
|
|
|
2020-01-02 22:32:03 +00:00
|
|
|
WinCursorGroup *WinCursorGroup::createCursorGroup(Common::WinResources *exe, const Common::WinResourceID &id) {
|
2019-12-31 19:19:42 +00:00
|
|
|
Common::ScopedPtr<Common::SeekableReadStream> stream(exe->getResource(Common::kWinGroupCursor, id));
|
2011-03-05 18:36:55 +00:00
|
|
|
|
|
|
|
if (!stream || stream->size() <= 6)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
stream->skip(4);
|
|
|
|
uint32 cursorCount = stream->readUint16LE();
|
2011-03-07 00:53:17 +00:00
|
|
|
if ((uint32)stream->size() < (6 + cursorCount * 14))
|
2011-03-05 18:36:55 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
WinCursorGroup *group = new WinCursorGroup();
|
|
|
|
group->cursors.reserve(cursorCount);
|
|
|
|
|
|
|
|
for (uint32 i = 0; i < cursorCount; i++) {
|
|
|
|
stream->readUint16LE(); // width
|
|
|
|
stream->readUint16LE(); // height
|
|
|
|
|
|
|
|
// Plane count
|
|
|
|
if (stream->readUint16LE() != 1) {
|
2018-07-08 17:19:37 +00:00
|
|
|
warning("PlaneCount is not 1.");
|
2011-03-05 18:36:55 +00:00
|
|
|
}
|
|
|
|
|
2011-03-07 00:53:17 +00:00
|
|
|
stream->readUint16LE(); // bits per pixel
|
2011-03-05 18:36:55 +00:00
|
|
|
stream->readUint32LE(); // data size
|
2011-03-07 00:53:17 +00:00
|
|
|
uint32 cursorId = stream->readUint16LE();
|
2011-03-05 18:36:55 +00:00
|
|
|
|
2019-12-31 19:19:42 +00:00
|
|
|
Common::ScopedPtr<Common::SeekableReadStream> cursorStream(exe->getResource(Common::kWinCursor, cursorId));
|
2011-03-05 18:36:55 +00:00
|
|
|
if (!cursorStream) {
|
|
|
|
delete group;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
WinCursor *cursor = new WinCursor();
|
|
|
|
if (!cursor->readFromStream(*cursorStream)) {
|
|
|
|
delete cursor;
|
|
|
|
delete group;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
CursorItem item;
|
|
|
|
item.id = cursorId;
|
|
|
|
item.cursor = cursor;
|
|
|
|
group->cursors.push_back(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
return group;
|
|
|
|
}
|
|
|
|
|
2011-08-16 04:30:12 +00:00
|
|
|
/**
|
|
|
|
* The default Windows cursor
|
|
|
|
*/
|
|
|
|
class DefaultWinCursor : public Cursor {
|
|
|
|
public:
|
|
|
|
DefaultWinCursor() {}
|
|
|
|
~DefaultWinCursor() {}
|
|
|
|
|
|
|
|
uint16 getWidth() const { return 12; }
|
|
|
|
uint16 getHeight() const { return 20; }
|
|
|
|
uint16 getHotspotX() const { return 0; }
|
|
|
|
uint16 getHotspotY() const { return 0; }
|
|
|
|
byte getKeyColor() const { return 0; }
|
|
|
|
|
|
|
|
const byte *getSurface() const {
|
|
|
|
static const byte defaultCursor[] = {
|
|
|
|
1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
1, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
1, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
1, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0,
|
|
|
|
1, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0,
|
|
|
|
1, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0,
|
|
|
|
1, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0,
|
|
|
|
1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0,
|
|
|
|
1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0,
|
|
|
|
1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1,
|
|
|
|
1, 2, 2, 2, 1, 2, 2, 1, 0, 0, 0, 0,
|
|
|
|
1, 2, 2, 1, 1, 2, 2, 1, 0, 0, 0, 0,
|
|
|
|
1, 2, 1, 0, 1, 1, 2, 2, 1, 0, 0, 0,
|
|
|
|
1, 1, 0, 0, 0, 1, 2, 2, 1, 0, 0, 0,
|
|
|
|
1, 0, 0, 0, 0, 0, 1, 2, 2, 1, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 1, 2, 2, 1, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 1, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 1, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0
|
|
|
|
};
|
|
|
|
|
|
|
|
return defaultCursor;
|
|
|
|
}
|
|
|
|
|
|
|
|
const byte *getPalette() const {
|
|
|
|
static const byte bwPalette[] = {
|
|
|
|
0x00, 0x00, 0x00, // Black
|
|
|
|
0xFF, 0xFF, 0xFF // White
|
|
|
|
};
|
|
|
|
|
|
|
|
return bwPalette;
|
|
|
|
}
|
|
|
|
byte getPaletteStartIndex() const { return 1; }
|
|
|
|
uint16 getPaletteCount() const { return 2; }
|
|
|
|
};
|
|
|
|
|
|
|
|
Cursor *makeDefaultWinCursor() {
|
|
|
|
return new DefaultWinCursor();
|
|
|
|
}
|
|
|
|
|
2013-07-28 00:00:26 +00:00
|
|
|
/**
|
|
|
|
* The Windows busy cursor
|
|
|
|
*/
|
|
|
|
class BusyWinCursor : public Cursor {
|
|
|
|
public:
|
|
|
|
BusyWinCursor() {}
|
|
|
|
~BusyWinCursor() {}
|
|
|
|
|
|
|
|
uint16 getWidth() const { return 15; }
|
|
|
|
uint16 getHeight() const { return 27; }
|
|
|
|
uint16 getHotspotX() const { return 7; }
|
|
|
|
uint16 getHotspotY() const { return 13; }
|
|
|
|
byte getKeyColor() const { return 0; }
|
|
|
|
|
|
|
|
const byte *getSurface() const {
|
|
|
|
static const byte busyCursor[] = {
|
|
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
|
|
1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
|
|
|
|
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
|
|
|
|
0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 0,
|
|
|
|
0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 0,
|
|
|
|
0, 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 1, 1, 0,
|
|
|
|
0, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 0,
|
|
|
|
0, 1, 1, 2, 2, 1, 2, 1, 2, 1, 2, 2, 1, 1, 0,
|
|
|
|
0, 0, 1, 1, 2, 2, 1, 2, 1, 2, 2, 1, 1, 0, 0,
|
|
|
|
0, 0, 0, 1, 1, 2, 2, 1, 2, 2, 1, 1, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 1, 1, 2, 1, 1, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 1, 1, 2, 1, 1, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 1, 1, 2, 1, 1, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 1, 1, 0, 0, 0,
|
|
|
|
0, 0, 1, 1, 2, 2, 2, 1, 2, 2, 2, 1, 1, 0, 0,
|
|
|
|
0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 0,
|
|
|
|
0, 1, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 1, 0,
|
|
|
|
0, 1, 1, 2, 2, 2, 1, 2, 1, 2, 2, 2, 1, 1, 0,
|
|
|
|
0, 1, 1, 2, 2, 1, 2, 1, 2, 1, 2, 2, 1, 1, 0,
|
|
|
|
0, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 0,
|
|
|
|
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
|
|
|
|
1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
|
|
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
|
|
|
|
};
|
|
|
|
|
|
|
|
return busyCursor;
|
|
|
|
}
|
|
|
|
|
|
|
|
const byte *getPalette() const {
|
|
|
|
static const byte bwPalette[] = {
|
|
|
|
0x00, 0x00, 0x00, // Black
|
|
|
|
0xFF, 0xFF, 0xFF // White
|
|
|
|
};
|
|
|
|
|
|
|
|
return bwPalette;
|
|
|
|
}
|
|
|
|
byte getPaletteStartIndex() const { return 1; }
|
|
|
|
uint16 getPaletteCount() const { return 2; }
|
|
|
|
};
|
|
|
|
|
|
|
|
Cursor *makeBusyWinCursor() {
|
|
|
|
return new BusyWinCursor();
|
|
|
|
}
|
|
|
|
|
2011-03-05 04:17:57 +00:00
|
|
|
} // End of namespace Graphics
|