2014-04-30 16:53:21 +00:00
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
// SSSS tt lll lll
|
|
|
|
// SS SS tt ll ll
|
|
|
|
// SS tttttt eeee ll ll aaaa
|
|
|
|
// SSSS tt ee ee ll ll aa
|
|
|
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
|
|
|
// SS SS tt ee ll ll aa aa
|
|
|
|
// SSSS ttt eeeee llll llll aaaaa
|
|
|
|
//
|
2015-01-01 03:49:32 +00:00
|
|
|
// Copyright (c) 1995-2015 by Bradford W. Mott, Stephen Anthony
|
2014-04-30 16:53:21 +00:00
|
|
|
// and the Stella Team
|
|
|
|
//
|
|
|
|
// See the file "License.txt" for information on usage and redistribution of
|
|
|
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|
|
|
//
|
|
|
|
// $Id$
|
|
|
|
//============================================================================
|
|
|
|
|
2014-05-02 13:17:24 +00:00
|
|
|
#include "FrameBuffer.hxx"
|
2014-04-30 16:53:21 +00:00
|
|
|
#include "FBSurface.hxx"
|
|
|
|
|
2014-05-02 13:17:24 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2014-05-12 23:34:25 +00:00
|
|
|
FBSurface::FBSurface()
|
2014-11-09 03:05:19 +00:00
|
|
|
: myPixels(nullptr),
|
2014-05-04 16:59:11 +00:00
|
|
|
myPitch(0)
|
2014-05-02 13:17:24 +00:00
|
|
|
{
|
2014-05-04 16:59:11 +00:00
|
|
|
// NOTE: myPixels and myPitch MUST be set in child classes that inherit
|
2014-05-02 15:58:57 +00:00
|
|
|
// from this class
|
2014-05-14 22:29:53 +00:00
|
|
|
|
|
|
|
// Set default attributes
|
|
|
|
myAttributes.smoothing = false;
|
|
|
|
myAttributes.blending = false;
|
|
|
|
myAttributes.blendalpha = 100;
|
2014-05-02 13:17:24 +00:00
|
|
|
}
|
|
|
|
|
2014-06-19 16:45:07 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void FBSurface::readPixels(uInt8* buffer, uInt32 pitch, const GUI::Rect& rect) const
|
|
|
|
{
|
|
|
|
uInt8* src = (uInt8*) myPixels + rect.y() * myPitch + rect.x();
|
|
|
|
|
2014-11-07 23:28:40 +00:00
|
|
|
if(rect.empty())
|
2014-06-19 16:45:07 +00:00
|
|
|
memcpy(buffer, src, width() * height() * 4);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
uInt32 w = BSPF_min(rect.width(), width());
|
|
|
|
uInt32 h = BSPF_min(rect.height(), height());
|
|
|
|
|
|
|
|
// Copy 'height' lines of width 'pitch' (in bytes for both)
|
|
|
|
uInt8* dst = buffer;
|
|
|
|
while(h--)
|
|
|
|
{
|
|
|
|
memcpy(dst, src, w * 4);
|
|
|
|
src += myPitch * 4;
|
|
|
|
dst += pitch * 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-02 13:17:24 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void FBSurface::hLine(uInt32 x, uInt32 y, uInt32 x2, uInt32 color)
|
|
|
|
{
|
|
|
|
uInt32* buffer = myPixels + y * myPitch + x;
|
|
|
|
while(x++ <= x2)
|
|
|
|
*buffer++ = (uInt32) myPalette[color];
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void FBSurface::vLine(uInt32 x, uInt32 y, uInt32 y2, uInt32 color)
|
|
|
|
{
|
|
|
|
uInt32* buffer = (uInt32*) myPixels + y * myPitch + x;
|
|
|
|
while(y++ <= y2)
|
|
|
|
{
|
|
|
|
*buffer = (uInt32) myPalette[color];
|
|
|
|
buffer += myPitch;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-04 16:59:11 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void FBSurface::fillRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, uInt32 color)
|
|
|
|
{
|
|
|
|
while(h--)
|
|
|
|
hLine(x, y+h, x+w-1, color);
|
|
|
|
}
|
|
|
|
|
2014-05-02 13:17:24 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void FBSurface::drawChar(const GUI::Font& font, uInt8 chr,
|
|
|
|
uInt32 tx, uInt32 ty, uInt32 color)
|
|
|
|
{
|
|
|
|
const FontDesc& desc = font.desc();
|
|
|
|
|
|
|
|
// If this character is not included in the font, use the default char.
|
|
|
|
if(chr < desc.firstchar || chr >= desc.firstchar + desc.size)
|
|
|
|
{
|
|
|
|
if (chr == ' ') return;
|
|
|
|
chr = desc.defaultchar;
|
|
|
|
}
|
|
|
|
chr -= desc.firstchar;
|
|
|
|
|
|
|
|
// Get the bounding box of the character
|
|
|
|
int bbw, bbh, bbx, bby;
|
|
|
|
if(!desc.bbx)
|
|
|
|
{
|
|
|
|
bbw = desc.fbbw;
|
|
|
|
bbh = desc.fbbh;
|
|
|
|
bbx = desc.fbbx;
|
|
|
|
bby = desc.fbby;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bbw = desc.bbx[chr].w;
|
|
|
|
bbh = desc.bbx[chr].h;
|
|
|
|
bbx = desc.bbx[chr].x;
|
|
|
|
bby = desc.bbx[chr].y;
|
|
|
|
}
|
|
|
|
|
|
|
|
const uInt16* tmp = desc.bits + (desc.offset ? desc.offset[chr] : (chr * desc.fbbh));
|
|
|
|
uInt32* buffer = myPixels + (ty + desc.ascent - bby - bbh) * myPitch + tx + bbx;
|
|
|
|
|
|
|
|
for(int y = 0; y < bbh; y++)
|
|
|
|
{
|
|
|
|
const uInt16 ptr = *tmp++;
|
|
|
|
uInt16 mask = 0x8000;
|
|
|
|
|
|
|
|
for(int x = 0; x < bbw; x++, mask >>= 1)
|
|
|
|
if(ptr & mask)
|
|
|
|
buffer[x] = (uInt32) myPalette[color];
|
|
|
|
|
|
|
|
buffer += myPitch;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void FBSurface::drawBitmap(uInt32* bitmap, uInt32 tx, uInt32 ty,
|
|
|
|
uInt32 color, uInt32 h)
|
|
|
|
{
|
|
|
|
uInt32* buffer = myPixels + ty * myPitch + tx;
|
|
|
|
|
|
|
|
for(uInt32 y = 0; y < h; ++y)
|
|
|
|
{
|
|
|
|
uInt32 mask = 0xF0000000;
|
|
|
|
for(uInt32 x = 0; x < 8; ++x, mask >>= 4)
|
|
|
|
if(bitmap[y] & mask)
|
|
|
|
buffer[x] = (uInt32) myPalette[color];
|
|
|
|
|
|
|
|
buffer += myPitch;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void FBSurface::drawPixels(uInt32* data, uInt32 tx, uInt32 ty, uInt32 numpixels)
|
|
|
|
{
|
|
|
|
uInt32* buffer = myPixels + ty * myPitch + tx;
|
|
|
|
|
|
|
|
for(uInt32 i = 0; i < numpixels; ++i)
|
|
|
|
*buffer++ = (uInt32) data[i];
|
|
|
|
}
|
|
|
|
|
2014-04-30 16:53:21 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void FBSurface::box(uInt32 x, uInt32 y, uInt32 w, uInt32 h,
|
|
|
|
uInt32 colorA, uInt32 colorB)
|
|
|
|
{
|
|
|
|
hLine(x + 1, y, x + w - 2, colorA);
|
|
|
|
hLine(x, y + 1, x + w - 1, colorA);
|
|
|
|
vLine(x, y + 1, y + h - 2, colorA);
|
|
|
|
vLine(x + 1, y, y + h - 1, colorA);
|
|
|
|
|
|
|
|
hLine(x + 1, y + h - 2, x + w - 1, colorB);
|
|
|
|
hLine(x + 1, y + h - 1, x + w - 2, colorB);
|
|
|
|
vLine(x + w - 1, y + 1, y + h - 2, colorB);
|
|
|
|
vLine(x + w - 2, y + 1, y + h - 1, colorB);
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void FBSurface::frameRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h,
|
|
|
|
uInt32 color, FrameStyle style)
|
|
|
|
{
|
|
|
|
switch(style)
|
|
|
|
{
|
|
|
|
case kSolidLine:
|
|
|
|
hLine(x, y, x + w - 1, color);
|
|
|
|
hLine(x, y + h - 1, x + w - 1, color);
|
|
|
|
vLine(x, y, y + h - 1, color);
|
|
|
|
vLine(x + w - 1, y, y + h - 1, color);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kDashLine:
|
2014-11-17 14:17:19 +00:00
|
|
|
uInt32 i, skip, lwidth = 1;
|
2014-04-30 16:53:21 +00:00
|
|
|
|
|
|
|
for(i = x, skip = 1; i < x+w-1; i=i+lwidth+1, ++skip)
|
|
|
|
{
|
|
|
|
if(skip % 2)
|
|
|
|
{
|
|
|
|
hLine(i, y, i + lwidth, color);
|
|
|
|
hLine(i, y + h - 1, i + lwidth, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for(i = y, skip = 1; i < y+h-1; i=i+lwidth+1, ++skip)
|
|
|
|
{
|
|
|
|
if(skip % 2)
|
|
|
|
{
|
|
|
|
vLine(x, i, i + lwidth, color);
|
|
|
|
vLine(x + w - 1, i, i + lwidth, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void FBSurface::drawString(const GUI::Font& font, const string& s,
|
|
|
|
int x, int y, int w,
|
|
|
|
uInt32 color, TextAlignment align,
|
|
|
|
int deltax, bool useEllipsis)
|
|
|
|
{
|
|
|
|
const int leftX = x, rightX = x + w;
|
2014-11-17 14:17:19 +00:00
|
|
|
uInt32 i;
|
2014-04-30 16:53:21 +00:00
|
|
|
int width = font.getStringWidth(s);
|
|
|
|
string str;
|
|
|
|
|
|
|
|
if(useEllipsis && width > w)
|
|
|
|
{
|
|
|
|
// String is too wide. So we shorten it "intelligently", by replacing
|
|
|
|
// parts of it by an ellipsis ("..."). There are three possibilities
|
|
|
|
// for this: replace the start, the end, or the middle of the string.
|
|
|
|
// What is best really depends on the context; but unless we want to
|
|
|
|
// make this configurable, replacing the middle probably is a good
|
|
|
|
// compromise.
|
|
|
|
const int ellipsisWidth = font.getStringWidth("...");
|
|
|
|
|
|
|
|
// SLOW algorithm to remove enough of the middle. But it is good enough for now.
|
|
|
|
const int halfWidth = (w - ellipsisWidth) / 2;
|
|
|
|
int w2 = 0;
|
|
|
|
|
|
|
|
for(i = 0; i < s.size(); ++i)
|
|
|
|
{
|
|
|
|
int charWidth = font.getCharWidth(s[i]);
|
|
|
|
if(w2 + charWidth > halfWidth)
|
|
|
|
break;
|
|
|
|
|
|
|
|
w2 += charWidth;
|
|
|
|
str += s[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
// At this point we know that the first 'i' chars are together 'w2'
|
|
|
|
// pixels wide. We took the first i-1, and add "..." to them.
|
|
|
|
str += "...";
|
|
|
|
|
|
|
|
// The original string is width wide. Of those we already skipped past
|
|
|
|
// w2 pixels, which means (width - w2) remain.
|
|
|
|
// The new str is (w2+ellipsisWidth) wide, so we can accomodate about
|
|
|
|
// (w - (w2+ellipsisWidth)) more pixels.
|
|
|
|
// Thus we skip ((width - w2) - (w - (w2+ellipsisWidth))) =
|
|
|
|
// (width + ellipsisWidth - w)
|
|
|
|
int skip = width + ellipsisWidth - w;
|
|
|
|
for(; i < s.size() && skip > 0; ++i)
|
|
|
|
skip -= font.getCharWidth(s[i]);
|
|
|
|
|
|
|
|
// Append the remaining chars, if any
|
|
|
|
for(; i < s.size(); ++i)
|
|
|
|
str += s[i];
|
|
|
|
|
|
|
|
width = font.getStringWidth(str);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
str = s;
|
|
|
|
|
|
|
|
if(align == kTextAlignCenter)
|
|
|
|
x = x + (w - width - 1)/2;
|
|
|
|
else if(align == kTextAlignRight)
|
|
|
|
x = x + w - width;
|
|
|
|
|
|
|
|
x += deltax;
|
|
|
|
for(i = 0; i < str.size(); ++i)
|
|
|
|
{
|
|
|
|
w = font.getCharWidth(str[i]);
|
|
|
|
if(x+w > rightX)
|
|
|
|
break;
|
|
|
|
if(x >= leftX)
|
|
|
|
drawChar(font, str[i], x, y, color);
|
|
|
|
|
|
|
|
x += w;
|
|
|
|
}
|
|
|
|
}
|
2014-05-09 17:44:04 +00:00
|
|
|
|
2014-05-12 23:34:25 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2014-11-09 03:05:19 +00:00
|
|
|
const uInt32* FBSurface::myPalette = nullptr;
|