aboutsummaryrefslogtreecommitdiff
path: root/graphics
diff options
context:
space:
mode:
authorPaul Gilbert2013-11-30 20:44:23 -0500
committerPaul Gilbert2013-11-30 20:44:23 -0500
commitede418b67a0f14e4f17a2b03f5362741badd5532 (patch)
tree07de039fac5c303f1b9fce372afe5fa19854f547 /graphics
parent66d1f7a8de2ff5a21ad013f45924c406f4833e9a (diff)
parent3e859768770a0b385e21c4528cd546b33ed9a55d (diff)
downloadscummvm-rg350-ede418b67a0f14e4f17a2b03f5362741badd5532.tar.gz
scummvm-rg350-ede418b67a0f14e4f17a2b03f5362741badd5532.tar.bz2
scummvm-rg350-ede418b67a0f14e4f17a2b03f5362741badd5532.zip
VOYEUR: Merge of upstream
Diffstat (limited to 'graphics')
-rw-r--r--graphics/VectorRenderer.h18
-rw-r--r--graphics/VectorRendererSpec.cpp874
-rw-r--r--graphics/VectorRendererSpec.h26
-rw-r--r--graphics/cursorman.cpp8
-rw-r--r--graphics/cursorman.h6
-rw-r--r--graphics/decoders/bmp.cpp6
-rw-r--r--graphics/decoders/iff.cpp10
-rw-r--r--graphics/decoders/iff.h4
-rw-r--r--graphics/decoders/image_decoder.h6
-rw-r--r--graphics/decoders/jpeg.cpp861
-rw-r--r--graphics/decoders/jpeg.h124
-rw-r--r--graphics/decoders/pcx.cpp6
-rw-r--r--graphics/decoders/pict.cpp2
-rw-r--r--graphics/decoders/png.cpp14
-rw-r--r--graphics/decoders/tga.cpp6
-rw-r--r--graphics/decoders/tga.h1
-rw-r--r--graphics/font.cpp219
-rw-r--r--graphics/font.h10
-rw-r--r--graphics/fonts/bdf.cpp12
-rw-r--r--graphics/fonts/bdf.h6
-rw-r--r--graphics/fonts/ttf.cpp85
-rw-r--r--graphics/fonts/winfont.cpp6
-rw-r--r--graphics/fonts/winfont.h6
-rw-r--r--graphics/scaler/thumbnail_intern.cpp217
-rw-r--r--graphics/surface.cpp72
-rw-r--r--graphics/surface.h105
-rw-r--r--graphics/thumbnail.cpp118
-rw-r--r--graphics/thumbnail.h18
-rw-r--r--graphics/yuv_to_rgb.cpp18
-rw-r--r--graphics/yuv_to_rgb.h15
30 files changed, 1545 insertions, 1334 deletions
diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h
index 0467cac946..8e1c5e91e1 100644
--- a/graphics/VectorRenderer.h
+++ b/graphics/VectorRenderer.h
@@ -73,6 +73,8 @@ struct DrawStep {
uint8 shadow, stroke, factor, radius, bevel; /**< Misc options... */
uint8 fillMode; /**< active fill mode */
+ uint8 shadowFillMode; /**< fill mode of the shadow used */
+
uint32 extraData; /**< Generic parameter for extra options (orientation/bevel) */
uint32 scale; /**< scale of all the coordinates in FIXED POINT with 16 bits mantissa */
@@ -103,7 +105,7 @@ VectorRenderer *createRenderer(int mode);
*/
class VectorRenderer {
public:
- VectorRenderer() : _activeSurface(NULL), _fillMode(kFillDisabled), _shadowOffset(0),
+ VectorRenderer() : _activeSurface(NULL), _fillMode(kFillDisabled), _shadowOffset(0), _shadowFillMode(kShadowExponential),
_disableShadows(false), _strokeWidth(1), _gradientFactor(1) {
}
@@ -126,6 +128,11 @@ public:
kTriangleRight
};
+ enum ShadowFillMode {
+ kShadowLinear = 0,
+ kShadowExponential = 1
+ };
+
/**
* Draws a line by considering the special cases for optimization.
*
@@ -278,7 +285,7 @@ public:
* Clears the active surface.
*/
virtual void clearSurface() {
- byte *src = (byte *)_activeSurface->pixels;
+ byte *src = (byte *)_activeSurface->getPixels();
memset(src, 0, _activeSurface->pitch * _activeSurface->h);
}
@@ -292,6 +299,10 @@ public:
_fillMode = mode;
}
+ virtual void setShadowFillMode(ShadowFillMode mode) {
+ _shadowFillMode = mode;
+ }
+
/**
* Sets the stroke width. All shapes drawn with a stroke will
* have that width. Pass 0 to disable shape stroking.
@@ -466,7 +477,7 @@ public:
*/
virtual void drawString(const Graphics::Font *font, const Common::String &text,
const Common::Rect &area, Graphics::TextAlign alignH,
- GUI::ThemeEngine::TextAlignVertical alignV, int deltax, bool useEllipsis) = 0;
+ GUI::ThemeEngine::TextAlignVertical alignV, int deltax, bool useEllipsis, const Common::Rect &textDrawableArea) = 0;
/**
* Allows to temporarily enable/disable all shadows drawing.
@@ -485,6 +496,7 @@ protected:
Surface *_activeSurface; /**< Pointer to the surface currently being drawn */
FillMode _fillMode; /**< Defines in which way (if any) are filled the drawn shapes */
+ ShadowFillMode _shadowFillMode;
int _shadowOffset; /**< offset for drawn shadows */
int _bevel; /**< amount of fake bevel */
diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp
index 6a3ee306a5..491a9d7f42 100644
--- a/graphics/VectorRendererSpec.cpp
+++ b/graphics/VectorRendererSpec.cpp
@@ -119,6 +119,38 @@ inline frac_t fp_sqroot(uint32 x) {
*(ptr4 + (y) + (px)) = color2; \
} while (0)
+#define BE_DRAWCIRCLE_BCOLOR_TR_CW(ptr,x,y,px,py,a) do { \
+ this->blendPixelPtr(ptr + (y) - (px), color, a); \
+} while (0)
+
+#define BE_DRAWCIRCLE_BCOLOR_TR_CCW(ptr,x,y,px,py,a) do { \
+ this->blendPixelPtr(ptr + (x) - (py), color, a); \
+} while (0)
+
+#define BE_DRAWCIRCLE_BCOLOR_TL_CW(ptr,x,y,px,py,a) do { \
+ this->blendPixelPtr(ptr - (x) - (py), color, a); \
+} while (0)
+
+#define BE_DRAWCIRCLE_BCOLOR_TL_CCW(ptr,x,y,px,py,a) do { \
+ this->blendPixelPtr(ptr - (y) - (px), color, a); \
+} while (0)
+
+#define BE_DRAWCIRCLE_BCOLOR_BL_CW(ptr,x,y,px,py,a) do { \
+ this->blendPixelPtr(ptr - (y) + (px), color, a); \
+} while (0)
+
+#define BE_DRAWCIRCLE_BCOLOR_BL_CCW(ptr,x,y,px,py,a) do { \
+ this->blendPixelPtr(ptr - (x) + (py), color, a); \
+} while (0)
+
+#define BE_DRAWCIRCLE_BCOLOR_BR_CW(ptr,x,y,px,py,a) do { \
+ this->blendPixelPtr(ptr + (x) + (py), color, a); \
+} while (0)
+
+#define BE_DRAWCIRCLE_BCOLOR_BR_CCW(ptr,x,y,px,py,a) do { \
+ this->blendPixelPtr(ptr + (y) + (px), color, a); \
+} while (0)
+
#define BE_DRAWCIRCLE_XCOLOR_TOP(ptr1,ptr2,x,y,px,py) do { \
*(ptr1 + (y) - (px)) = color1; \
*(ptr1 + (x) - (py)) = color2; \
@@ -222,6 +254,37 @@ inline frac_t fp_sqroot(uint32 x) {
this->blendPixelPtr(ptr4 + (y) + (px), color2, a); \
} while (0)
+#define WU_DRAWCIRCLE_BCOLOR_TR_CW(ptr,x,y,px,py,a) do { \
+ this->blendPixelPtr(ptr + (y) - (px), color, a); \
+} while (0)
+
+#define WU_DRAWCIRCLE_BCOLOR_TR_CCW(ptr,x,y,px,py,a) do { \
+ this->blendPixelPtr(ptr + (x) - (py), color, a); \
+} while (0)
+
+#define WU_DRAWCIRCLE_BCOLOR_TL_CW(ptr,x,y,px,py,a) do { \
+ this->blendPixelPtr(ptr - (x) - (py), color, a); \
+} while (0)
+
+#define WU_DRAWCIRCLE_BCOLOR_TL_CCW(ptr,x,y,px,py,a) do { \
+ this->blendPixelPtr(ptr - (y) - (px), color, a); \
+} while (0)
+
+#define WU_DRAWCIRCLE_BCOLOR_BL_CW(ptr,x,y,px,py,a) do { \
+ this->blendPixelPtr(ptr - (y) + (px), color, a); \
+} while (0)
+
+#define WU_DRAWCIRCLE_BCOLOR_BL_CCW(ptr,x,y,px,py,a) do { \
+ this->blendPixelPtr(ptr - (x) + (py), color, a); \
+} while (0)
+
+#define WU_DRAWCIRCLE_BCOLOR_BR_CW(ptr,x,y,px,py,a) do { \
+ this->blendPixelPtr(ptr + (x) + (py), color, a); \
+} while (0)
+
+#define WU_DRAWCIRCLE_BCOLOR_BR_CCW(ptr,x,y,px,py,a) do { \
+ this->blendPixelPtr(ptr + (y) + (px), color, a); \
+} while (0)
// optimized Wu's algorithm
#define WU_ALGORITHM() do { \
@@ -277,16 +340,24 @@ void colorFill(PixelType *first, PixelType *last, PixelType color) {
VectorRenderer *createRenderer(int mode) {
#ifdef DISABLE_FANCY_THEMES
- assert(mode == GUI::ThemeEngine::kGfxStandard16bit);
+ assert(mode == GUI::ThemeEngine::kGfxStandard);
#endif
PixelFormat format = g_system->getOverlayFormat();
switch (mode) {
- case GUI::ThemeEngine::kGfxStandard16bit:
- return new VectorRendererSpec<OverlayColor>(format);
+ case GUI::ThemeEngine::kGfxStandard:
+ if (g_system->getOverlayFormat().bytesPerPixel == 4)
+ return new VectorRendererSpec<uint32>(format);
+ else if (g_system->getOverlayFormat().bytesPerPixel == 2)
+ return new VectorRendererSpec<uint16>(format);
+ break;
#ifndef DISABLE_FANCY_THEMES
- case GUI::ThemeEngine::kGfxAntialias16bit:
- return new VectorRendererAA<OverlayColor>(format);
+ case GUI::ThemeEngine::kGfxAntialias:
+ if (g_system->getOverlayFormat().bytesPerPixel == 4)
+ return new VectorRendererAA<uint32>(format);
+ else if (g_system->getOverlayFormat().bytesPerPixel == 2)
+ return new VectorRendererAA<uint16>(format);
+ break;
#endif
default:
break;
@@ -317,9 +388,15 @@ setGradientColors(uint8 r1, uint8 g1, uint8 b1, uint8 r2, uint8 g2, uint8 b2) {
_gradientEnd = _format.RGBToColor(r2, g2, b2);
_gradientStart = _format.RGBToColor(r1, g1, b1);
- _gradientBytes[0] = (_gradientEnd & _redMask) - (_gradientStart & _redMask);
- _gradientBytes[1] = (_gradientEnd & _greenMask) - (_gradientStart & _greenMask);
- _gradientBytes[2] = (_gradientEnd & _blueMask) - (_gradientStart & _blueMask);
+ if (sizeof(PixelType) == 4) {
+ _gradientBytes[0] = ((_gradientEnd & _redMask) >> _format.rShift) - ((_gradientStart & _redMask) >> _format.rShift);
+ _gradientBytes[1] = ((_gradientEnd & _greenMask) >> _format.gShift) - ((_gradientStart & _greenMask) >> _format.gShift);
+ _gradientBytes[2] = ((_gradientEnd & _blueMask) >> _format.bShift) - ((_gradientStart & _blueMask) >> _format.bShift);
+ } else {
+ _gradientBytes[0] = (_gradientEnd & _redMask) - (_gradientStart & _redMask);
+ _gradientBytes[1] = (_gradientEnd & _greenMask) - (_gradientStart & _greenMask);
+ _gradientBytes[2] = (_gradientEnd & _blueMask) - (_gradientStart & _blueMask);
+ }
}
template<typename PixelType>
@@ -328,9 +405,15 @@ calcGradient(uint32 pos, uint32 max) {
PixelType output = 0;
pos = (MIN(pos * Base::_gradientFactor, max) << 12) / max;
- output |= ((_gradientStart & _redMask) + ((_gradientBytes[0] * pos) >> 12)) & _redMask;
- output |= ((_gradientStart & _greenMask) + ((_gradientBytes[1] * pos) >> 12)) & _greenMask;
- output |= ((_gradientStart & _blueMask) + ((_gradientBytes[2] * pos) >> 12)) & _blueMask;
+ if (sizeof(PixelType) == 4) {
+ output |= ((_gradientStart & _redMask) + (((_gradientBytes[0] * pos) >> 12) << _format.rShift)) & _redMask;
+ output |= ((_gradientStart & _greenMask) + (((_gradientBytes[1] * pos) >> 12) << _format.gShift)) & _greenMask;
+ output |= ((_gradientStart & _blueMask) + (((_gradientBytes[2] * pos) >> 12) << _format.bShift)) & _blueMask;
+ } else {
+ output |= ((_gradientStart & _redMask) + ((_gradientBytes[0] * pos) >> 12)) & _redMask;
+ output |= ((_gradientStart & _greenMask) + ((_gradientBytes[1] * pos) >> 12)) & _greenMask;
+ output |= ((_gradientStart & _blueMask) + ((_gradientBytes[2] * pos) >> 12)) & _blueMask;
+ }
output |= _alphaMask;
return output;
@@ -397,7 +480,7 @@ gradientFill(PixelType *ptr, int width, int x, int y) {
template<typename PixelType>
void VectorRendererSpec<PixelType>::
fillSurface() {
- byte *ptr = (byte *)_activeSurface->getBasePtr(0, 0);
+ byte *ptr = (byte *)_activeSurface->getPixels();
int h = _activeSurface->h;
int pitch = _activeSurface->pitch;
@@ -453,7 +536,7 @@ template<typename PixelType>
void VectorRendererSpec<PixelType>::
blitSubSurface(const Graphics::Surface *source, const Common::Rect &r) {
byte *dst_ptr = (byte *)_activeSurface->getBasePtr(r.left, r.top);
- const byte *src_ptr = (const byte *)source->getBasePtr(0, 0);
+ const byte *src_ptr = (const byte *)source->getPixels();
const int dst_pitch = _activeSurface->pitch;
const int src_pitch = source->pitch;
@@ -481,7 +564,7 @@ blitAlphaBitmap(const Graphics::Surface *source, const Common::Rect &r) {
y = y + (r.height() >> 1) - (source->h >> 1);
PixelType *dst_ptr = (PixelType *)_activeSurface->getBasePtr(x, y);
- const PixelType *src_ptr = (const PixelType *)source->getBasePtr(0, 0);
+ const PixelType *src_ptr = (const PixelType *)source->getPixels();
int dst_pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
int src_pitch = source->pitch / source->format.bytesPerPixel;
@@ -508,7 +591,7 @@ template<typename PixelType>
void VectorRendererSpec<PixelType>::
applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle) {
int pixels = _activeSurface->w * _activeSurface->h;
- PixelType *ptr = (PixelType *)_activeSurface->getBasePtr(0, 0);
+ PixelType *ptr = (PixelType *)_activeSurface->getPixels();
uint8 r, g, b;
uint lum;
@@ -537,20 +620,41 @@ applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle) {
template<typename PixelType>
inline void VectorRendererSpec<PixelType>::
blendPixelPtr(PixelType *ptr, PixelType color, uint8 alpha) {
- int idst = *ptr;
- int isrc = color;
-
- *ptr = (PixelType)(
- (_redMask & ((idst & _redMask) +
- ((int)(((int)(isrc & _redMask) -
- (int)(idst & _redMask)) * alpha) >> 8))) |
- (_greenMask & ((idst & _greenMask) +
- ((int)(((int)(isrc & _greenMask) -
- (int)(idst & _greenMask)) * alpha) >> 8))) |
- (_blueMask & ((idst & _blueMask) +
- ((int)(((int)(isrc & _blueMask) -
- (int)(idst & _blueMask)) * alpha) >> 8))) |
- (idst & _alphaMask));
+ if (sizeof(PixelType) == 4) {
+ const byte sR = (color & _redMask) >> _format.rShift;
+ const byte sG = (color & _greenMask) >> _format.gShift;
+ const byte sB = (color & _blueMask) >> _format.bShift;
+
+ byte dR = (*ptr & _redMask) >> _format.rShift;
+ byte dG = (*ptr & _greenMask) >> _format.gShift;
+ byte dB = (*ptr & _blueMask) >> _format.bShift;
+
+ dR += ((sR - dR) * alpha) >> 8;
+ dG += ((sG - dG) * alpha) >> 8;
+ dB += ((sB - dB) * alpha) >> 8;
+
+ *ptr = ((dR << _format.rShift) & _redMask)
+ | ((dG << _format.gShift) & _greenMask)
+ | ((dB << _format.bShift) & _blueMask)
+ | (*ptr & _alphaMask);
+ } else if (sizeof(PixelType) == 2) {
+ int idst = *ptr;
+ int isrc = color;
+
+ *ptr = (PixelType)(
+ (_redMask & ((idst & _redMask) +
+ ((int)(((int)(isrc & _redMask) -
+ (int)(idst & _redMask)) * alpha) >> 8))) |
+ (_greenMask & ((idst & _greenMask) +
+ ((int)(((int)(isrc & _greenMask) -
+ (int)(idst & _greenMask)) * alpha) >> 8))) |
+ (_blueMask & ((idst & _blueMask) +
+ ((int)(((int)(isrc & _blueMask) -
+ (int)(idst & _blueMask)) * alpha) >> 8))) |
+ (idst & _alphaMask));
+ } else {
+ error("Unsupported BPP format: %u", (uint)sizeof(PixelType));
+ }
}
template<typename PixelType>
@@ -607,24 +711,45 @@ darkenFill(PixelType *ptr, PixelType *end) {
template<typename PixelType>
void VectorRendererSpec<PixelType>::
drawString(const Graphics::Font *font, const Common::String &text, const Common::Rect &area,
- Graphics::TextAlign alignH, GUI::ThemeEngine::TextAlignVertical alignV, int deltax, bool ellipsis) {
+ Graphics::TextAlign alignH, GUI::ThemeEngine::TextAlignVertical alignV, int deltax, bool ellipsis, const Common::Rect &textDrawableArea) {
int offset = area.top;
if (font->getFontHeight() < area.height()) {
switch (alignV) {
- case GUI::ThemeEngine::kTextAlignVCenter:
- offset = area.top + ((area.height() - font->getFontHeight()) >> 1);
- break;
- case GUI::ThemeEngine::kTextAlignVBottom:
- offset = area.bottom - font->getFontHeight();
- break;
- default:
- break;
+ case GUI::ThemeEngine::kTextAlignVCenter:
+ offset = area.top + ((area.height() - font->getFontHeight()) >> 1);
+ break;
+ case GUI::ThemeEngine::kTextAlignVBottom:
+ offset = area.bottom - font->getFontHeight();
+ break;
+ default:
+ break;
}
}
- font->drawString(_activeSurface, text, area.left, offset, area.width() - deltax, _fgColor, alignH, deltax, ellipsis);
+ Common::Rect drawArea;
+ if (textDrawableArea.isEmpty()) {
+ // In case no special area to draw to is given we only draw in the
+ // area specified by the user.
+ drawArea = area;
+ // warning("there is no text drawable area. Please set this area for clipping");
+ } else {
+ // The area we can draw to is the intersection between the allowed
+ // drawing area (textDrawableArea) and the area where we try to draw
+ // the text (area).
+ drawArea = textDrawableArea.findIntersectingRect(area);
+ }
+
+ // Better safe than sorry. We intersect with the actual surface boundaries
+ // to avoid any ugly clipping in _activeSurface->getSubArea which messes
+ // up the calculation of the x and y coordinates where to draw the string.
+ drawArea = drawArea.findIntersectingRect(Common::Rect(0, 0, _activeSurface->w, _activeSurface->h));
+
+ if (!drawArea.isEmpty()) {
+ Surface textAreaSurface = _activeSurface->getSubArea(drawArea);
+ font->drawString(&textAreaSurface, text, area.left - drawArea.left, offset - drawArea.top, area.width() - deltax, _fgColor, alignH, deltax, ellipsis);
+ }
}
/** LINES **/
@@ -778,7 +903,8 @@ drawRoundedSquare(int x, int y, int r, int w, int h) {
if (Base::_fillMode != kFillDisabled && Base::_shadowOffset
&& x + w + Base::_shadowOffset + 1 < Base::_activeSurface->w
- && y + h + Base::_shadowOffset + 1 < Base::_activeSurface->h) {
+ && y + h + Base::_shadowOffset + 1 < Base::_activeSurface->h
+ && h > (Base::_shadowOffset + 1) * 2) {
drawRoundedSquareShadow(x, y, r, w, h, Base::_shadowOffset);
}
@@ -809,13 +935,14 @@ drawTab(int x, int y, int r, int w, int h) {
// FIXME: This is broken for the AA renderer.
// See the rounded rect alg for how to fix it. (The border should
// be drawn before the interior, both inside drawTabAlg.)
- drawTabAlg(x, y, w, h, r, (Base::_fillMode == kFillBackground) ? _bgColor : _fgColor, Base::_fillMode);
+ drawTabShadow(x, y, w - 2, h, r);
+ drawTabAlg(x, y, w - 2, h, r, _bgColor, Base::_fillMode);
if (Base::_strokeWidth)
drawTabAlg(x, y, w, h, r, _fgColor, kFillDisabled, (Base::_dynamicData >> 16), (Base::_dynamicData & 0xFFFF));
break;
case kFillForeground:
- drawTabAlg(x, y, w, h, r, (Base::_fillMode == kFillBackground) ? _bgColor : _fgColor, Base::_fillMode);
+ drawTabAlg(x, y, w, h, r, _fgColor, Base::_fillMode);
break;
}
}
@@ -996,6 +1123,67 @@ drawTabAlg(int x1, int y1, int w, int h, int r, PixelType color, VectorRenderer:
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawTabShadow(int x1, int y1, int w, int h, int r) {
+ int offset = 3;
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+
+ // "Harder" shadows when having lower BPP, since we will have artifacts (greenish tint on the modern theme)
+ uint8 expFactor = 3;
+ uint16 alpha = (_activeSurface->format.bytesPerPixel > 2) ? 4 : 8;
+
+ int xstart = x1;
+ int ystart = y1;
+ int width = w;
+ int height = h + offset + 1;
+
+ for (int i = offset; i >= 0; i--) {
+ int f, ddF_x, ddF_y;
+ int x, y, px, py;
+
+ PixelType *ptr_tl = (PixelType *)Base::_activeSurface->getBasePtr(xstart + r, ystart + r);
+ PixelType *ptr_tr = (PixelType *)Base::_activeSurface->getBasePtr(xstart + width - r, ystart + r);
+ PixelType *ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(xstart, ystart);
+
+ int short_h = height - (2 * r) + 2;
+ PixelType color = _format.RGBToColor(0, 0, 0);
+
+ BE_RESET();
+
+ // HACK: As we are drawing circles exploting 8-axis symmetry,
+ // there are 4 pixels on each circle which are drawn twice.
+ // this is ok on filled circles, but when blending on surfaces,
+ // we cannot let it blend twice. awful.
+ uint32 hb = 0;
+
+ while (x++ < y) {
+ BE_ALGORITHM();
+
+ if (((1 << x) & hb) == 0) {
+ blendFill(ptr_tl - y - px, ptr_tr + y - px, color, (uint8)alpha);
+ hb |= (1 << x);
+ }
+
+ if (((1 << y) & hb) == 0) {
+ blendFill(ptr_tl - x - py, ptr_tr + x - py, color, (uint8)alpha);
+ hb |= (1 << y);
+ }
+ }
+
+ ptr_fill += pitch * r;
+ while (short_h--) {
+ blendFill(ptr_fill, ptr_fill + width + 1, color, (uint8)alpha);
+ ptr_fill += pitch;
+ }
+
+ // Move shadow one pixel upward each iteration
+ xstart += 1;
+ // Multiply with expfactor
+ alpha = (alpha * (expFactor << 8)) >> 9;
+ }
+}
+
/** BEVELED TABS FOR CLASSIC THEME **/
template<typename PixelType>
void VectorRendererSpec<PixelType>::
@@ -1429,118 +1617,160 @@ drawTriangleFast(int x1, int y1, int size, bool inverted, PixelType color, Vecto
/** ROUNDED SQUARE ALGORITHM **/
template<typename PixelType>
void VectorRendererSpec<PixelType>::
-drawRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color, VectorRenderer::FillMode fill_m) {
+drawBorderRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color, VectorRenderer::FillMode fill_m, uint8 alpha_t, uint8 alpha_r, uint8 alpha_b, uint8 alpha_l) {
int f, ddF_x, ddF_y;
int x, y, px, py;
int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int sw = 0, sp = 0, hp = h * pitch;
- // TODO: Split this up into border, bevel and interior functions
+ PixelType *ptr_tl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + r);
+ PixelType *ptr_tr = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + r);
+ PixelType *ptr_bl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + h - r);
+ PixelType *ptr_br = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + h - r);
+ PixelType *ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(x1, y1);
- if (fill_m != kFillDisabled) {
- PixelType *ptr_tl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + r);
- PixelType *ptr_tr = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + r);
- PixelType *ptr_bl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + h - r);
- PixelType *ptr_br = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + h - r);
- PixelType *ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(x1, y1);
-
- int real_radius = r;
- int short_h = h - (2 * r) + 2;
- int long_h = h;
+ int real_radius = r;
+ int short_h = h - (2 * r) + 2;
- BE_RESET();
+ PixelType color1 = color;
+ PixelType color2 = color;
- PixelType color1 = color;
- if (fill_m == kFillBackground)
- color1 = _bgColor;
+ while (sw++ < Base::_strokeWidth) {
+ blendFill(ptr_fill + sp + r, ptr_fill + w + 1 + sp - r, color1, alpha_t); // top
+ blendFill(ptr_fill + hp - sp + r, ptr_fill + w + hp + 1 - sp - r, color2, alpha_b); // bottom
+ sp += pitch;
- if (fill_m == kFillGradient) {
- PixelType color2, color3, color4;
- precalcGradient(long_h);
+ BE_RESET();
+ r--;
+
+ int alphaStep_tr = ((alpha_t - alpha_r)/(y+1));
+ int alphaStep_br = ((alpha_r - alpha_b)/(y+1));
+ int alphaStep_bl = ((alpha_b - alpha_l)/(y+1));
+ int alphaStep_tl = ((alpha_l - alpha_t)/(y+1));
+
+ // Avoid blending the last pixels twice, since we have an alpha
+ while (x++ < (y - 2)) {
+ BE_ALGORITHM();
- while (x++ < y) {
- BE_ALGORITHM();
+ BE_DRAWCIRCLE_BCOLOR_TR_CW(ptr_tr, x, y, px, py, (uint8)(alpha_r + (alphaStep_tr * x)));
+ BE_DRAWCIRCLE_BCOLOR_BR_CW(ptr_br, x, y, px, py, (uint8)(alpha_b + (alphaStep_br * x)));
+ BE_DRAWCIRCLE_BCOLOR_BL_CW(ptr_bl, x, y, px, py, (uint8)(alpha_l + (alphaStep_bl * x)));
+ BE_DRAWCIRCLE_BCOLOR_TL_CW(ptr_tl, x, y, px, py, (uint8)(alpha_t + (alphaStep_tl * x)));
+
+ BE_DRAWCIRCLE_BCOLOR_TR_CCW(ptr_tr, x, y, px, py, (uint8)(alpha_t - (alphaStep_tr * x)));
+ BE_DRAWCIRCLE_BCOLOR_BR_CCW(ptr_br, x, y, px, py, (uint8)(alpha_r - (alphaStep_br * x)));
+ BE_DRAWCIRCLE_BCOLOR_BL_CCW(ptr_bl, x, y, px, py, (uint8)(alpha_b - (alphaStep_bl * x)));
+ BE_DRAWCIRCLE_BCOLOR_TL_CCW(ptr_tl, x, y, px, py, (uint8)(alpha_l - (alphaStep_tl * x)));
+
+ if (Base::_strokeWidth > 1) {
+ BE_DRAWCIRCLE_BCOLOR(ptr_tr, ptr_tl, ptr_bl, ptr_br, x - 1, y, px, py);
+ BE_DRAWCIRCLE_BCOLOR(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px - pitch, py);
+ }
+ }
+ }
- color1 = calcGradient(real_radius - x, long_h);
- color2 = calcGradient(real_radius - y, long_h);
- color3 = calcGradient(long_h - r + x, long_h);
- color4 = calcGradient(long_h - r + y, long_h);
+ ptr_fill += pitch * real_radius;
+ while (short_h--) {
+ blendFill(ptr_fill, ptr_fill + Base::_strokeWidth, color1, alpha_l); // left
+ blendFill(ptr_fill + w - Base::_strokeWidth + 1, ptr_fill + w + 1, color2, alpha_r); // right
+ ptr_fill += pitch;
+ }
+}
- gradientFill(ptr_tl - x - py, w - 2 * r + 2 * x, x1 + r - x - y, real_radius - y);
- gradientFill(ptr_tl - y - px, w - 2 * r + 2 * y, x1 + r - y - x, real_radius - x);
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawInteriorRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color, VectorRenderer::FillMode fill_m) {
+ int f, ddF_x, ddF_y;
+ int x, y, px, py;
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
- gradientFill(ptr_bl - x + py, w - 2 * r + 2 * x, x1 + r - x - y, long_h - r + y);
- gradientFill(ptr_bl - y + px, w - 2 * r + 2 * y, x1 + r - y - x, long_h - r + x);
+ PixelType *ptr_tl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + r);
+ PixelType *ptr_tr = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + r);
+ PixelType *ptr_bl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + h - r);
+ PixelType *ptr_br = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + h - r);
+ PixelType *ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(x1, y1);
- BE_DRAWCIRCLE_XCOLOR(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px, py);
- }
- } else {
- while (x++ < y) {
- BE_ALGORITHM();
+ int real_radius = r;
+ int short_h = h - (2 * r) + 2;
+ int long_h = h;
- colorFill<PixelType>(ptr_tl - x - py, ptr_tr + x - py, color1);
- colorFill<PixelType>(ptr_tl - y - px, ptr_tr + y - px, color1);
+ BE_RESET();
- colorFill<PixelType>(ptr_bl - x + py, ptr_br + x + py, color1);
- colorFill<PixelType>(ptr_bl - y + px, ptr_br + y + px, color1);
+ PixelType color1 = color;
- // do not remove - messes up the drawing at lower resolutions
- BE_DRAWCIRCLE(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px, py);
- }
- }
+ if (fill_m == kFillGradient) {
+ PixelType color2, color3, color4;
+ precalcGradient(long_h);
- ptr_fill += pitch * r;
- while (short_h--) {
- if (fill_m == kFillGradient) {
- gradientFill(ptr_fill, w + 1, x1, real_radius++);
- } else {
- colorFill<PixelType>(ptr_fill, ptr_fill + w + 1, color1);
- }
- ptr_fill += pitch;
- }
- }
+ while (x++ < y) {
+ BE_ALGORITHM();
+ color1 = calcGradient(real_radius - x, long_h);
+ color2 = calcGradient(real_radius - y, long_h);
+ color3 = calcGradient(long_h - r + x, long_h);
+ color4 = calcGradient(long_h - r + y, long_h);
- if (Base::_strokeWidth) {
- int sw = 0, sp = 0, hp = h * pitch;
+ gradientFill(ptr_tl - x - py, w - 2 * r + 2 * x, x1 + r - x - y, real_radius - y);
+ gradientFill(ptr_tl - y - px, w - 2 * r + 2 * y, x1 + r - y - x, real_radius - x);
- PixelType *ptr_tl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + r);
- PixelType *ptr_tr = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + r);
- PixelType *ptr_bl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + h - r);
- PixelType *ptr_br = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + h - r);
- PixelType *ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(x1, y1);
+ gradientFill(ptr_bl - x + py, w - 2 * r + 2 * x, x1 + r - x - y, long_h - r + y);
+ gradientFill(ptr_bl - y + px, w - 2 * r + 2 * y, x1 + r - y - x, long_h - r + x);
- int real_radius = r;
- int short_h = h - (2 * r) + 2;
+ BE_DRAWCIRCLE_XCOLOR(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px, py);
+ }
+ } else {
+ while (x++ < y) {
+ BE_ALGORITHM();
- // TODO: A gradient effect on the bevel
- PixelType color1, color2;
- color1 = Base::_bevel ? _bevelColor : color;
- color2 = color;
+ colorFill<PixelType>(ptr_tl - x - py, ptr_tr + x - py, color1);
+ colorFill<PixelType>(ptr_tl - y - px, ptr_tr + y - px, color1);
- while (sw++ < Base::_strokeWidth) {
- colorFill<PixelType>(ptr_fill + sp + r, ptr_fill + w + 1 + sp - r, color1);
- colorFill<PixelType>(ptr_fill + hp - sp + r, ptr_fill + w + hp + 1 - sp - r, color2);
- sp += pitch;
+ colorFill<PixelType>(ptr_bl - x + py, ptr_br + x + py, color1);
+ colorFill<PixelType>(ptr_bl - y + px, ptr_br + y + px, color1);
- BE_RESET();
- r--;
-
- while (x++ < y) {
- BE_ALGORITHM();
- BE_DRAWCIRCLE_BCOLOR(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px, py);
+ // do not remove - messes up the drawing at lower resolutions
+ BE_DRAWCIRCLE(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px, py);
+ }
+ }
- if (Base::_strokeWidth > 1) {
- BE_DRAWCIRCLE_BCOLOR(ptr_tr, ptr_tl, ptr_bl, ptr_br, x - 1, y, px, py);
- BE_DRAWCIRCLE_BCOLOR(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px - pitch, py);
- }
- }
+ ptr_fill += pitch * r;
+ while (short_h--) {
+ if (fill_m == kFillGradient) {
+ gradientFill(ptr_fill, w + 1, x1, real_radius++);
+ } else {
+ colorFill<PixelType>(ptr_fill, ptr_fill + w + 1, color1);
}
+ ptr_fill += pitch;
+ }
+}
- ptr_fill += pitch * real_radius;
- while (short_h--) {
- colorFill<PixelType>(ptr_fill, ptr_fill + Base::_strokeWidth, color1);
- colorFill<PixelType>(ptr_fill + w - Base::_strokeWidth + 1, ptr_fill + w + 1, color2);
- ptr_fill += pitch;
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color, VectorRenderer::FillMode fill_m) {
+ const uint8 borderAlpha_t = 0;
+ const uint8 borderAlpha_r = 127;
+ const uint8 borderAlpha_b = 255;
+ const uint8 borderAlpha_l = 63;
+
+ const uint8 bevelAlpha_t = 255;
+ const uint8 bevelAlpha_r = 31;
+ const uint8 bevelAlpha_b = 0;
+ const uint8 bevelAlpha_l = 127;
+
+ // If only border is visible
+ if ((!(w <= 0 || h <= 0)) && (fill_m != Base::kFillDisabled)) {
+ if (fill_m == Base::kFillBackground)
+ drawInteriorRoundedSquareAlg(x1, y1, r, w, h, _bgColor, fill_m);
+ else
+ drawInteriorRoundedSquareAlg(x1, y1, r, w, h, color, fill_m);
+ }
+
+ if (Base::_strokeWidth) {
+ if (r != 0 && _bevel > 0) {
+ drawBorderRoundedSquareAlg(x1, y1, r, w, h, color, fill_m, borderAlpha_t, borderAlpha_r, borderAlpha_b, borderAlpha_l);
+ drawBorderRoundedSquareAlg(x1, y1, r, w, h, _bevelColor, fill_m, bevelAlpha_t, bevelAlpha_r, bevelAlpha_b, bevelAlpha_l);
+ } else {
+ drawBorderRoundedSquareAlg(x1, y1, r, w, h, color, fill_m, 255, 255, 255, 255);
}
}
}
@@ -1599,85 +1829,112 @@ drawCircleAlg(int x1, int y1, int r, PixelType color, VectorRenderer::FillMode f
********************************************************************/
template<typename PixelType>
void VectorRendererSpec<PixelType>::
-drawSquareShadow(int x, int y, int w, int h, int blur) {
- PixelType *ptr = (PixelType *)_activeSurface->getBasePtr(x + w - 1, y + blur);
+drawSquareShadow(int x, int y, int w, int h, int offset) {
+ PixelType *ptr = (PixelType *)_activeSurface->getBasePtr(x + w - 1, y + offset);
int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
int i, j;
- i = h - blur;
+ i = h - offset;
while (i--) {
- j = blur;
+ j = offset;
while (j--)
- blendPixelPtr(ptr + j, 0, ((blur - j) << 8) / blur);
+ blendPixelPtr(ptr + j, 0, ((offset - j) << 8) / offset);
ptr += pitch;
}
- ptr = (PixelType *)_activeSurface->getBasePtr(x + blur, y + h - 1);
+ ptr = (PixelType *)_activeSurface->getBasePtr(x + offset, y + h - 1);
- while (i++ < blur) {
- j = w - blur;
+ while (i++ < offset) {
+ j = w - offset;
while (j--)
- blendPixelPtr(ptr + j, 0, ((blur - i) << 8) / blur);
+ blendPixelPtr(ptr + j, 0, ((offset - i) << 8) / offset);
ptr += pitch;
}
ptr = (PixelType *)_activeSurface->getBasePtr(x + w, y + h);
i = 0;
- while (i++ < blur) {
- j = blur - 1;
+ while (i++ < offset) {
+ j = offset - 1;
while (j--)
- blendPixelPtr(ptr + j, 0, (((blur - j) * (blur - i)) << 8) / (blur * blur));
+ blendPixelPtr(ptr + j, 0, (((offset - j) * (offset - i)) << 8) / (offset * offset));
ptr += pitch;
}
}
template<typename PixelType>
void VectorRendererSpec<PixelType>::
-drawRoundedSquareShadow(int x1, int y1, int r, int w, int h, int blur) {
- int f, ddF_x, ddF_y;
- int x, y, px, py;
+drawRoundedSquareShadow(int x1, int y1, int r, int w, int h, int offset) {
int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
- int alpha = 102;
- x1 += blur;
- y1 += blur;
+ // "Harder" shadows when having lower BPP, since we will have artifacts (greenish tint on the modern theme)
+ uint8 expFactor = 3;
+ uint16 alpha = (_activeSurface->format.bytesPerPixel > 2) ? 4 : 8;
+
+ // These constants ensure a border of 2px on the left and of each rounded square
+ int xstart = (x1 > 2) ? x1 - 2 : x1;
+ int ystart = y1;
+ int width = w + offset + 2;
+ int height = h + offset + 1;
+
+ for (int i = offset; i >= 0; i--) {
+ int f, ddF_x, ddF_y;
+ int x, y, px, py;
+
+ PixelType *ptr_tl = (PixelType *)Base::_activeSurface->getBasePtr(xstart + r, ystart + r);
+ PixelType *ptr_tr = (PixelType *)Base::_activeSurface->getBasePtr(xstart + width - r, ystart + r);
+ PixelType *ptr_bl = (PixelType *)Base::_activeSurface->getBasePtr(xstart + r, ystart + height - r);
+ PixelType *ptr_br = (PixelType *)Base::_activeSurface->getBasePtr(xstart + width - r, ystart + height - r);
+ PixelType *ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(xstart, ystart);
+
+ int short_h = height - (2 * r) + 2;
+ PixelType color = _format.RGBToColor(0, 0, 0);
- PixelType *ptr_tr = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + r);
- PixelType *ptr_bl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + h - r);
- PixelType *ptr_br = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + h - r);
- PixelType *ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - blur, y1 + r);
-
- int short_h = h - (2 * r) + 1;
+ BE_RESET();
- BE_RESET();
+ // HACK: As we are drawing circles exploting 8-axis symmetry,
+ // there are 4 pixels on each circle which are drawn twice.
+ // this is ok on filled circles, but when blending on surfaces,
+ // we cannot let it blend twice. awful.
+ uint32 hb = 0;
+
+ while (x++ < y) {
+ BE_ALGORITHM();
- // HACK: As we are drawing circles exploting 8-axis symmetry,
- // there are 4 pixels on each circle which are drawn twice.
- // this is ok on filled circles, but when blending on surfaces,
- // we cannot let it blend twice. awful.
- uint32 hb = 0;
- while (x++ < y) {
- BE_ALGORITHM();
+ if (((1 << x) & hb) == 0) {
+ blendFill(ptr_tl - y - px, ptr_tr + y - px, color, (uint8)alpha);
+
+ // Will create a dark line of pixles if left out
+ if (hb > 0) {
+ blendFill(ptr_bl - y + px, ptr_br + y + px, color, (uint8)alpha);
+ }
+ hb |= (1 << x);
+ }
- if (((1 << x) & hb) == 0) {
- blendFill(ptr_tr - px - r, ptr_tr + y - px, 0, alpha);
- blendFill(ptr_bl - y + px, ptr_br + y + px, 0, alpha);
- hb |= (1 << x);
+ if (((1 << y) & hb) == 0) {
+ blendFill(ptr_tl - x - py, ptr_tr + x - py, color, (uint8)alpha);
+ blendFill(ptr_bl - x + py, ptr_br + x + py, color, (uint8)alpha);
+ hb |= (1 << y);
+ }
}
-
- if (((1 << y) & hb) == 0) {
- blendFill(ptr_tr - r - py, ptr_tr + x - py, 0, alpha);
- blendFill(ptr_bl - x + py, ptr_br + x + py, 0, alpha);
- hb |= (1 << y);
+
+ ptr_fill += pitch * r;
+ while (short_h--) {
+ blendFill(ptr_fill, ptr_fill + width + 1, color, (uint8)alpha);
+ ptr_fill += pitch;
}
- }
- while (short_h--) {
- blendFill(ptr_fill - r, ptr_fill + blur, 0, alpha);
- ptr_fill += pitch;
+ // Make shadow smaller each iteration, and move it one pixel inward
+ xstart += 1;
+ ystart += 1;
+ width -= 2;
+ height -= 2;
+
+ if (_shadowFillMode == kShadowExponential)
+ // Multiply with expfactor
+ alpha = (alpha * (expFactor << 8)) >> 9;
}
}
@@ -1870,7 +2127,7 @@ drawTabAlg(int x1, int y1, int w, int h, int r, PixelType color, VectorRenderer:
/** ROUNDED SQUARES **/
template<typename PixelType>
void VectorRendererAA<PixelType>::
-drawRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color, VectorRenderer::FillMode fill_m) {
+drawBorderRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color, VectorRenderer::FillMode fill_m, uint8 alpha_t, uint8 alpha_r, uint8 alpha_b, uint8 alpha_l) {
int x, y;
const int pitch = Base::_activeSurface->pitch / Base::_activeSurface->format.bytesPerPixel;
int px, py;
@@ -1879,65 +2136,89 @@ drawRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color, Vecto
frac_t T = 0, oldT;
uint8 a1, a2;
- // TODO: Split this up into border, bevel and interior functions
+ PixelType *ptr_tl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + r);
+ PixelType *ptr_tr = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + r);
+ PixelType *ptr_bl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + h - r);
+ PixelType *ptr_br = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + h - r);
+ PixelType *ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(x1, y1);
- if (Base::_strokeWidth) {
- PixelType *ptr_tl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + r);
- PixelType *ptr_tr = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + r);
- PixelType *ptr_bl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + h - r);
- PixelType *ptr_br = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + h - r);
- PixelType *ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(x1, y1);
-
- int sw = 0, sp = 0;
- int short_h = h - 2 * r;
- int hp = h * pitch;
-
- int strokeWidth = Base::_strokeWidth;
- // If we're going to fill the inside, draw a slightly thicker border
- // so we can blend the inside on top of it.
- if (fill_m != Base::kFillDisabled) strokeWidth++;
-
- // TODO: A gradient effect on the bevel
- PixelType color1, color2;
- color1 = Base::_bevel ? Base::_bevelColor : color;
- color2 = color;
+ int sw = 0, sp = 0;
+ int short_h = h - 2 * r;
+ int hp = h * pitch;
+ int strokeWidth = Base::_strokeWidth;
- while (sw++ < strokeWidth) {
- colorFill<PixelType>(ptr_fill + sp + r, ptr_fill + w + 1 + sp - r, color1);
- colorFill<PixelType>(ptr_fill + hp - sp + r, ptr_fill + w + hp + 1 - sp - r, color2);
- sp += pitch;
+ while (sw++ < strokeWidth) {
+ this->blendFill(ptr_fill + hp - sp + r, ptr_fill + w + hp + 1 - sp - r, color, alpha_b); // bottom
+ this->blendFill(ptr_fill + sp + r, ptr_fill + w + 1 + sp - r, color, alpha_t); // top
- x = r - (sw - 1);
- y = 0;
- T = 0;
- px = pitch * x;
- py = 0;
+ sp += pitch;
- while (x > y++) {
- WU_ALGORITHM();
+ x = r - (sw - 1);
+ y = 0;
+ T = 0;
+ px = pitch * x;
+ py = 0;
- // sw == 1: outside, sw = _strokeWidth: inside
- // We always draw the outer edge AAed, but the inner edge
- // only when the inside isn't filled
- if (sw != strokeWidth || fill_m != Base::kFillDisabled)
- a2 = 255;
+ int alphaStep_tr = ((alpha_t - alpha_r)/(x+1));
+ int alphaStep_br = ((alpha_r - alpha_b)/(x+1));
+ int alphaStep_bl = ((alpha_b - alpha_l)/(x+1));
+ int alphaStep_tl = ((alpha_l - alpha_t)/(x+1));
- // inner arc
- WU_DRAWCIRCLE_BCOLOR(ptr_tr, ptr_tl, ptr_bl, ptr_br, (x - 1), y, (px - pitch), py, a2);
+ while (x > y++) {
+ WU_ALGORITHM();
- if (sw == 1) // outer arc
- WU_DRAWCIRCLE_BCOLOR(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px, py, a1);
+ // sw == 1: outside, sw = _strokeWidth: inside
+ // We always draw the outer edge AAed, but the inner edge
+ // only when the inside isn't filled
+ if (sw != strokeWidth || fill_m != Base::kFillDisabled)
+ a2 = 255;
+
+ // inner arc
+ WU_DRAWCIRCLE_BCOLOR_TR_CW(ptr_tr, (x - 1), y, (px - pitch), py, (uint8)((uint32)(((alpha_t - (alphaStep_tr * y)) << 8) * a2) >> 16));
+ WU_DRAWCIRCLE_BCOLOR_BR_CW(ptr_br, (x - 1), y, (px - pitch), py, (uint8)((uint32)(((alpha_r - (alphaStep_br * y)) << 8) * a2) >> 16));
+ WU_DRAWCIRCLE_BCOLOR_BL_CW(ptr_bl, (x - 1), y, (px - pitch), py, (uint8)((uint32)(((alpha_b - (alphaStep_bl * y)) << 8) * a2) >> 16));
+ WU_DRAWCIRCLE_BCOLOR_TL_CW(ptr_tl, (x - 1), y, (px - pitch), py, (uint8)((uint32)(((alpha_l - (alphaStep_tl * y)) << 8) * a2) >> 16));
+
+ WU_DRAWCIRCLE_BCOLOR_TR_CCW(ptr_tr, (x - 1), y, (px - pitch), py, (uint8)((uint32)(((alpha_r + (alphaStep_tr * y)) << 8) * a2) >> 16));
+ WU_DRAWCIRCLE_BCOLOR_BR_CCW(ptr_br, (x - 1), y, (px - pitch), py, (uint8)((uint32)(((alpha_b + (alphaStep_br * y)) << 8) * a2) >> 16));
+ WU_DRAWCIRCLE_BCOLOR_BL_CCW(ptr_bl, (x - 1), y, (px - pitch), py, (uint8)((uint32)(((alpha_l + (alphaStep_bl * y)) << 8) * a2) >> 16));
+ WU_DRAWCIRCLE_BCOLOR_TL_CCW(ptr_tl, (x - 1), y, (px - pitch), py, (uint8)((uint32)(((alpha_t + (alphaStep_tl * y)) << 8) * a2) >> 16));
+
+ // outer arc
+ if (sw == 1) {
+ WU_DRAWCIRCLE_BCOLOR_TR_CW(ptr_tr, x, y, px, py, (uint8)((uint32)(((alpha_t - (alphaStep_tr * y)) << 8) * a1) >> 16));
+ WU_DRAWCIRCLE_BCOLOR_BR_CW(ptr_br, x, y, px, py, (uint8)((uint32)(((alpha_r - (alphaStep_br * y)) << 8) * a1) >> 16));
+ WU_DRAWCIRCLE_BCOLOR_BL_CW(ptr_bl, x, y, px, py, (uint8)((uint32)(((alpha_b - (alphaStep_bl * y)) << 8) * a1) >> 16));
+ WU_DRAWCIRCLE_BCOLOR_TL_CW(ptr_tl, x, y, px, py, (uint8)((uint32)(((alpha_l - (alphaStep_tl * y)) << 8) * a1) >> 16));
+
+ WU_DRAWCIRCLE_BCOLOR_TR_CCW(ptr_tr, x, y, px, py, (uint8)((uint32)(((alpha_r + (alphaStep_tr * y)) << 8) * a1) >> 16));
+ WU_DRAWCIRCLE_BCOLOR_BR_CCW(ptr_br, x, y, px, py, (uint8)((uint32)(((alpha_b + (alphaStep_br * y)) << 8) * a1) >> 16));
+ WU_DRAWCIRCLE_BCOLOR_BL_CCW(ptr_bl, x, y, px, py, (uint8)((uint32)(((alpha_l + (alphaStep_bl * y)) << 8) * a1) >> 16));
+ WU_DRAWCIRCLE_BCOLOR_TL_CCW(ptr_tl, x, y, px, py, (uint8)((uint32)(((alpha_t + (alphaStep_tl * y)) << 8) * a1) >> 16));
+ }
}
- }
ptr_fill += pitch * r;
- while (short_h-- >= 0) {
- colorFill<PixelType>(ptr_fill, ptr_fill + Base::_strokeWidth, color1);
- colorFill<PixelType>(ptr_fill + w - Base::_strokeWidth + 1, ptr_fill + w + 1, color2);
+
+ while (short_h-- >= -2) {
+ this->blendFill(ptr_fill, ptr_fill + Base::_strokeWidth, color, alpha_l); // left
+ this->blendFill(ptr_fill + w - Base::_strokeWidth + 1, ptr_fill + w + 1, color, alpha_r); // right
ptr_fill += pitch;
}
}
+}
+
+template<typename PixelType>
+void VectorRendererAA<PixelType>::
+drawInteriorRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color, VectorRenderer::FillMode fill_m) {
+ int x, y;
+ const int pitch = Base::_activeSurface->pitch / Base::_activeSurface->format.bytesPerPixel;
+ int px, py;
+
+ uint32 rsq = r*r;
+ frac_t T = 0, oldT;
+ uint8 a1, a2;
r -= Base::_strokeWidth;
x1 += Base::_strokeWidth;
@@ -1946,93 +2227,116 @@ drawRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color, Vecto
h -= 2*Base::_strokeWidth;
rsq = r*r;
- if (w <= 0 || h <= 0)
- return; // Only border is visible
+ PixelType *ptr_tl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + r);
+ PixelType *ptr_tr = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + r);
+ PixelType *ptr_bl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + h - r);
+ PixelType *ptr_br = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + h - r);
+ PixelType *ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(x1, y1);
- if (fill_m != Base::kFillDisabled) {
- if (fill_m == Base::kFillBackground)
- color = Base::_bgColor;
+ int short_h = h - 2 * r;
+ x = r;
+ y = 0;
+ T = 0;
+ px = pitch * x;
+ py = 0;
- PixelType *ptr_tl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + r);
- PixelType *ptr_tr = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + r);
- PixelType *ptr_bl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + h - r);
- PixelType *ptr_br = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w - r, y1 + h - r);
- PixelType *ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(x1, y1);
+ if (fill_m == Base::kFillGradient) {
- int short_h = h - 2 * r;
- x = r;
- y = 0;
- T = 0;
- px = pitch * x;
- py = 0;
+ Base::precalcGradient(h);
- if (fill_m == Base::kFillGradient) {
+ PixelType color1, color2, color3, color4;
+ while (x > y++) {
+ WU_ALGORITHM();
- Base::precalcGradient(h);
+ color1 = Base::calcGradient(r - x, h);
+ color2 = Base::calcGradient(r - y, h);
+ color3 = Base::calcGradient(h - r + x, h);
+ color4 = Base::calcGradient(h - r + y, h);
- PixelType color1, color2, color3, color4;
- while (x > y++) {
- WU_ALGORITHM();
+ Base::gradientFill(ptr_tl - x - py + 1, w - 2 * r + 2 * x - 1, x1 + r - x - y + 1, r - y);
- color1 = Base::calcGradient(r - x, h);
- color2 = Base::calcGradient(r - y, h);
- color3 = Base::calcGradient(h - r + x, h);
- color4 = Base::calcGradient(h - r + y, h);
+ // Only fill each horizontal line once (or we destroy
+ // the gradient effect at the edges)
+ if (T < oldT || y == 1)
+ Base::gradientFill(ptr_tl - y - px + 1, w - 2 * r + 2 * y - 1, x1 + r - y - x + 1, r - x);
- Base::gradientFill(ptr_tl - x - py + 1, w - 2 * r + 2 * x - 1, x1 + r - x - y + 1, r - y);
+ Base::gradientFill(ptr_bl - x + py + 1, w - 2 * r + 2 * x - 1, x1 + r - x - y + 1, h - r + y);
- // Only fill each horizontal line once (or we destroy
- // the gradient effect at the edges)
- if (T < oldT || y == 1)
- Base::gradientFill(ptr_tl - y - px + 1, w - 2 * r + 2 * y - 1, x1 + r - y - x + 1, r - x);
+ // Only fill each horizontal line once (or we destroy
+ // the gradient effect at the edges)
+ if (T < oldT || y == 1)
+ Base::gradientFill(ptr_bl - y + px + 1, w - 2 * r + 2 * y - 1, x1 + r - y - x + 1, h - r + x);
- Base::gradientFill(ptr_bl - x + py + 1, w - 2 * r + 2 * x - 1, x1 + r - x - y + 1, h - r + y);
+ // This shape is used for dialog backgrounds.
+ // If we're drawing on top of an empty overlay background,
+ // and the overlay supports alpha, we have to do AA by
+ // setting the dest alpha channel, instead of blending with
+ // dest color channels.
+ if (!g_system->hasFeature(OSystem::kFeatureOverlaySupportsAlpha))
+ WU_DRAWCIRCLE_XCOLOR(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px, py, a1, blendPixelPtr);
+ else
+ WU_DRAWCIRCLE_XCOLOR(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px, py, a1, blendPixelDestAlphaPtr);
+ }
- // Only fill each horizontal line once (or we destroy
- // the gradient effect at the edges)
- if (T < oldT || y == 1)
- Base::gradientFill(ptr_bl - y + px + 1, w - 2 * r + 2 * y - 1, x1 + r - y - x + 1, h - r + x);
-
- // This shape is used for dialog backgrounds.
- // If we're drawing on top of an empty overlay background,
- // and the overlay supports alpha, we have to do AA by
- // setting the dest alpha channel, instead of blending with
- // dest color channels.
- if (!g_system->hasFeature(OSystem::kFeatureOverlaySupportsAlpha))
- WU_DRAWCIRCLE_XCOLOR(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px, py, a1, blendPixelPtr);
- else
- WU_DRAWCIRCLE_XCOLOR(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px, py, a1, blendPixelDestAlphaPtr);
- }
+ ptr_fill += pitch * r;
+ while (short_h-- >= 0) {
+ Base::gradientFill(ptr_fill, w + 1, x1, r++);
+ ptr_fill += pitch;
+ }
- ptr_fill += pitch * r;
- while (short_h-- >= 0) {
- Base::gradientFill(ptr_fill, w + 1, x1, r++);
- ptr_fill += pitch;
- }
+ } else {
- } else {
+ while (x > 1 + y++) {
+ WU_ALGORITHM();
- while (x > 1 + y++) {
- WU_ALGORITHM();
+ colorFill<PixelType>(ptr_tl - x - py + 1, ptr_tr + x - py, color);
+ if (T < oldT || y == 1)
+ colorFill<PixelType>(ptr_tl - y - px + 1, ptr_tr + y - px, color);
- colorFill<PixelType>(ptr_tl - x - py + 1, ptr_tr + x - py, color);
- if (T < oldT || y == 1)
- colorFill<PixelType>(ptr_tl - y - px + 1, ptr_tr + y - px, color);
+ colorFill<PixelType>(ptr_bl - x + py + 1, ptr_br + x + py, color);
+ if (T < oldT || y == 1)
+ colorFill<PixelType>(ptr_bl - y + px + 1, ptr_br + y + px, color);
- colorFill<PixelType>(ptr_bl - x + py + 1, ptr_br + x + py, color);
- if (T < oldT || y == 1)
- colorFill<PixelType>(ptr_bl - y + px + 1, ptr_br + y + px, color);
+ WU_DRAWCIRCLE(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px, py, a1);
+ }
- WU_DRAWCIRCLE(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px, py, a1);
- }
+ ptr_fill += pitch * r;
+ while (short_h-- >= 0) {
+ colorFill<PixelType>(ptr_fill, ptr_fill + w + 1, color);
+ ptr_fill += pitch;
+ }
+ }
+}
- ptr_fill += pitch * r;
- while (short_h-- >= 0) {
- colorFill<PixelType>(ptr_fill, ptr_fill + w + 1, color);
- ptr_fill += pitch;
- }
+template<typename PixelType>
+void VectorRendererAA<PixelType>::
+drawRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color, VectorRenderer::FillMode fill_m) {
+ const uint8 borderAlpha_t = 0;
+ const uint8 borderAlpha_r = 127;
+ const uint8 borderAlpha_b = 255;
+ const uint8 borderAlpha_l = 63;
+
+ const uint8 bevelAlpha_t = 255;
+ const uint8 bevelAlpha_r = 31;
+ const uint8 bevelAlpha_b = 0;
+ const uint8 bevelAlpha_l = 127;
+
+ if (Base::_strokeWidth) {
+ if (r != 0 && Base::_bevel > 0) {
+ drawBorderRoundedSquareAlg(x1, y1, r, w, h, color, fill_m, borderAlpha_t, borderAlpha_r, borderAlpha_b, borderAlpha_l);
+ drawBorderRoundedSquareAlg(x1, y1, r, w, h, Base::_bevelColor, fill_m, bevelAlpha_t, bevelAlpha_r, bevelAlpha_b, bevelAlpha_l);
+ } else {
+ drawBorderRoundedSquareAlg(x1, y1, r, w, h, color, fill_m, 255, 255, 255, 255);
}
}
+
+ // If only border is visible
+ if ((!(w <= 0 || h <= 0)) && (fill_m != Base::kFillDisabled)) {
+ if (fill_m == Base::kFillBackground)
+ drawInteriorRoundedSquareAlg(x1, y1, r, w, h, Base::_bgColor, fill_m);
+ else
+ drawInteriorRoundedSquareAlg(x1, y1, r, w, h, color, fill_m);
+ }
}
/** CIRCLES **/
diff --git a/graphics/VectorRendererSpec.h b/graphics/VectorRendererSpec.h
index 4ed80cb55f..c035ca0e19 100644
--- a/graphics/VectorRendererSpec.h
+++ b/graphics/VectorRendererSpec.h
@@ -61,7 +61,7 @@ public:
}
void drawString(const Graphics::Font *font, const Common::String &text,
const Common::Rect &area, Graphics::TextAlign alignH,
- GUI::ThemeEngine::TextAlignVertical alignV, int deltax, bool elipsis);
+ GUI::ThemeEngine::TextAlignVertical alignV, int deltax, bool elipsis, const Common::Rect &textDrawableArea = Common::Rect(0, 0, 0, 0));
void setFgColor(uint8 r, uint8 g, uint8 b) { _fgColor = _format.RGBToColor(r, g, b); }
void setBgColor(uint8 r, uint8 g, uint8 b) { _bgColor = _format.RGBToColor(r, g, b); }
@@ -158,6 +158,12 @@ protected:
virtual void drawRoundedSquareAlg(int x1, int y1, int r, int w, int h,
PixelType color, FillMode fill_m);
+ virtual void drawBorderRoundedSquareAlg(int x1, int y1, int r, int w, int h,
+ PixelType color, FillMode fill_m, uint8 alpha_t, uint8 alpha_r, uint8 alpha_b, uint8 alpha_l);
+
+ virtual void drawInteriorRoundedSquareAlg(int x1, int y1, int r, int w, int h,
+ PixelType color, FillMode fill_m);
+
virtual void drawSquareAlg(int x, int y, int w, int h,
PixelType color, FillMode fill_m);
@@ -174,6 +180,8 @@ protected:
PixelType color, VectorRenderer::FillMode fill_m,
int baseLeft = 0, int baseRight = 0);
+ virtual void drawTabShadow(int x, int y, int w, int h, int r);
+
virtual void drawBevelTabAlg(int x, int y, int w, int h,
int bevel, PixelType topColor, PixelType bottomColor,
int baseLeft = 0, int baseRight = 0);
@@ -186,10 +194,10 @@ protected:
* There functions may be overloaded in inheriting classes to improve performance
* in the slowest platforms where pixel alpha blending just doesn't cut it.
*
- * @param blur Intensity/size of the shadow.
+ * @param offset Intensity/size of the shadow.
*/
- virtual void drawSquareShadow(int x, int y, int w, int h, int blur);
- virtual void drawRoundedSquareShadow(int x, int y, int r, int w, int h, int blur);
+ virtual void drawSquareShadow(int x, int y, int w, int h, int offset);
+ virtual void drawRoundedSquareShadow(int x, int y, int r, int w, int h, int offset);
/**
* Calculates the color gradient on a given point.
@@ -292,10 +300,12 @@ protected:
*/
virtual void drawRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color, VectorRenderer::FillMode fill_m);
- virtual void drawRoundedSquareShadow(int x, int y, int r, int w, int h, int blur) {
- Base::drawRoundedSquareShadow(x, y, r, w, h, blur);
-// VectorRenderer::applyConvolutionMatrix(VectorRenderer::kConvolutionHardBlur,
-// Common::Rect(x, y, x + w + blur * 2, y + h + blur * 2));
+ virtual void drawBorderRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color, VectorRenderer::FillMode fill_m, uint8 alpha_t, uint8 alpha_l, uint8 alpha_r, uint8 alpha_b);
+
+ virtual void drawInteriorRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color, VectorRenderer::FillMode fill_m);
+
+ virtual void drawRoundedSquareShadow(int x, int y, int r, int w, int h, int offset) {
+ Base::drawRoundedSquareShadow(x, y, r, w, h, offset);
}
virtual void drawTabAlg(int x, int y, int w, int h, int r,
diff --git a/graphics/cursorman.cpp b/graphics/cursorman.cpp
index c818101645..133ee5fd71 100644
--- a/graphics/cursorman.cpp
+++ b/graphics/cursorman.cpp
@@ -48,6 +48,9 @@ bool CursorManager::isVisible() {
bool CursorManager::showMouse(bool visible) {
if (_cursorStack.empty())
return false;
+ if (_locked) {
+ return false;
+ }
_cursorStack.top()->_visible = visible;
@@ -225,6 +228,10 @@ void CursorManager::replaceCursorPalette(const byte *colors, uint start, uint nu
}
}
+void CursorManager::lock(bool locked) {
+ _locked = locked;
+}
+
CursorManager::Cursor::Cursor(const void *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) {
#ifdef USE_RGB_COLOR
if (!format)
@@ -246,6 +253,7 @@ CursorManager::Cursor::Cursor(const void *data, uint w, uint h, int hotspotX, in
_hotspotX = hotspotX;
_hotspotY = hotspotY;
_dontScale = dontScale;
+ _visible = false;
}
CursorManager::Cursor::~Cursor() {
diff --git a/graphics/cursorman.h b/graphics/cursorman.h
index 66e8d1ba56..b4d8ad94ce 100644
--- a/graphics/cursorman.h
+++ b/graphics/cursorman.h
@@ -160,12 +160,15 @@ public:
*/
void replaceCursorPalette(const byte *colors, uint start, uint num);
+ void lock(bool locked);
private:
friend class Common::Singleton<SingletonBaseType>;
// Even though this is basically the default constructor we implement it
// ourselves, so it is private and thus there is no way to create this class
// except from the Singleton code.
- CursorManager() {}
+ CursorManager() {
+ _locked = false;
+ }
~CursorManager();
struct Cursor {
@@ -198,6 +201,7 @@ private:
};
Common::Stack<Cursor *> _cursorStack;
Common::Stack<Palette *> _cursorPaletteStack;
+ bool _locked;
};
} // End of namespace Graphics
diff --git a/graphics/decoders/bmp.cpp b/graphics/decoders/bmp.cpp
index bcfd0abbda..2eabbb7631 100644
--- a/graphics/decoders/bmp.cpp
+++ b/graphics/decoders/bmp.cpp
@@ -130,14 +130,14 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) {
const int extraDataLength = (srcPitch % 4) ? 4 - (srcPitch % 4) : 0;
if (bitsPerPixel == 8) {
- byte *dst = (byte *)_surface->pixels;
+ byte *dst = (byte *)_surface->getPixels();
for (int32 i = 0; i < height; i++) {
stream.read(dst + (height - i - 1) * width, width);
stream.skip(extraDataLength);
}
} else if (bitsPerPixel == 24) {
- byte *dst = (byte *)_surface->pixels + (height - 1) * _surface->pitch;
+ byte *dst = (byte *)_surface->getBasePtr(0, height - 1);
for (int32 i = 0; i < height; i++) {
for (uint32 j = 0; j < width; j++) {
@@ -154,7 +154,7 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) {
dst -= _surface->pitch * 2;
}
} else { // 32 bpp
- byte *dst = (byte *)_surface->pixels + (height - 1) * _surface->pitch;
+ byte *dst = (byte *)_surface->getBasePtr(0, height - 1);
for (int32 i = 0; i < height; i++) {
for (uint32 j = 0; j < width; j++) {
diff --git a/graphics/decoders/iff.cpp b/graphics/decoders/iff.cpp
index 50c7b4f7de..fe27258d28 100644
--- a/graphics/decoders/iff.cpp
+++ b/graphics/decoders/iff.cpp
@@ -30,6 +30,10 @@ namespace Graphics {
IFFDecoder::IFFDecoder() {
_surface = 0;
_palette = 0;
+
+ // these 2 properties are not reset by destroy(), so the default is set here.
+ _numRelevantPlanes = 8;
+ _pixelPacking = false;
destroy();
}
@@ -54,8 +58,6 @@ void IFFDecoder::destroy() {
_paletteRanges.clear();
_type = TYPE_UNKNOWN;
_paletteColorCount = 0;
- _numRelevantPlanes = 8;
- _pixelPacking = false;
}
bool IFFDecoder::loadStream(Common::SeekableReadStream &stream) {
@@ -170,7 +172,7 @@ void IFFDecoder::loadBitmap(Common::SeekableReadStream &stream) {
if (_type == TYPE_ILBM) {
uint32 scanlinePitch = ((_header.width + 15) >> 4) << 1;
byte *scanlines = new byte[scanlinePitch * _header.numPlanes];
- byte *data = (byte *)_surface->pixels;
+ byte *data = (byte *)_surface->getPixels();
for (uint16 i = 0; i < _header.height; ++i) {
byte *scanline = scanlines;
@@ -194,7 +196,7 @@ void IFFDecoder::loadBitmap(Common::SeekableReadStream &stream) {
delete[] scanlines;
} else if (_type == TYPE_PBM) {
- byte *data = (byte *)_surface->pixels;
+ byte *data = (byte *)_surface->getPixels();
uint32 outSize = _header.width * _header.height;
if (_header.compression) {
diff --git a/graphics/decoders/iff.h b/graphics/decoders/iff.h
index beac62e519..37cb4b38c1 100644
--- a/graphics/decoders/iff.h
+++ b/graphics/decoders/iff.h
@@ -85,6 +85,8 @@ public:
/**
* The number of planes to decode, also determines the pixel packing if _packPixels is true.
* 8 == decode all planes, map 1 pixel in 1 byte. (default, no packing even if _packPixels is true)
+ *
+ * NOTE: this property must be reset manually, and is not reset by a call to destroy().
*/
void setNumRelevantPlanes(const uint8 numRelevantPlanes) { _numRelevantPlanes = numRelevantPlanes; }
@@ -94,6 +96,8 @@ public:
* 2 == decode first 2 planes, pack 4 pixels in 1 byte. This makes _surface->w 1/4th of _header.width
* 4 == decode first 4 planes, pack 2 pixels in 1 byte. This makes _surface->w half of _header.width
* Packed bitmaps won't have a proper surface format since there is no way to tell it to use 1, 2 or 4 bits per pixel
+ *
+ * NOTE: this property must be reset manually, and is not reset by a call to destroy().
*/
void setPixelPacking(const bool pixelPacking) { _pixelPacking = pixelPacking; }
private:
diff --git a/graphics/decoders/image_decoder.h b/graphics/decoders/image_decoder.h
index 49e31c6e3a..a39a9a1493 100644
--- a/graphics/decoders/image_decoder.h
+++ b/graphics/decoders/image_decoder.h
@@ -44,6 +44,9 @@ public:
/**
* Load an image from the specified stream
+ *
+ * loadStream() should implicitly call destroy() to free the memory
+ * of the last loadStream() call.
*
* @param stream the input stream
* @return whether loading the file succeeded
@@ -54,6 +57,9 @@ public:
/**
* Destroy this decoder's surface and palette
+ *
+ * This should be called by a loadStream() implementation as well
+ * as the destructor.
*/
virtual void destroy() = 0;
diff --git a/graphics/decoders/jpeg.cpp b/graphics/decoders/jpeg.cpp
index 75fdcd6e5a..c858884095 100644
--- a/graphics/decoders/jpeg.cpp
+++ b/graphics/decoders/jpeg.cpp
@@ -20,8 +20,11 @@
*
*/
+// libjpeg uses forbidden symbols in its header. Thus, we need to allow them
+// here.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "graphics/pixelformat.h"
-#include "graphics/yuv_to_rgb.h"
#include "graphics/decoders/jpeg.h"
#include "common/debug.h"
@@ -29,35 +32,19 @@
#include "common/stream.h"
#include "common/textconsole.h"
-namespace Graphics {
-
-// Order used to traverse the quantization tables
-static const uint8 _zigZagOrder[64] = {
- 0, 1, 8, 16, 9, 2, 3, 10,
- 17, 24, 32, 25, 18, 11, 4, 5,
- 12, 19, 26, 33, 40, 48, 41, 34,
- 27, 20, 13, 6, 7, 14, 21, 28,
- 35, 42, 49, 56, 57, 50, 43, 36,
- 29, 22, 15, 23, 30, 37, 44, 51,
- 58, 59, 52, 45, 38, 31, 39, 46,
- 53, 60, 61, 54, 47, 55, 62, 63
-};
-
-JPEGDecoder::JPEGDecoder() : ImageDecoder(),
- _stream(NULL), _w(0), _h(0), _numComp(0), _components(NULL), _numScanComp(0),
- _scanComp(NULL), _currentComp(NULL), _rgbSurface(0) {
+#ifdef USE_JPEG
+// The original release of libjpeg v6b did not contain any extern "C" in case
+// its header files are included in a C++ environment. To avoid any linking
+// issues we need to add it on our own.
+extern "C" {
+#include <jpeglib.h>
+#include <jerror.h>
+}
+#endif
- // Initialize the quantization tables
- for (int i = 0; i < JPEG_MAX_QUANT_TABLES; i++)
- _quant[i] = NULL;
+namespace Graphics {
- // Initialize the Huffman tables
- for (int i = 0; i < 2 * JPEG_MAX_HUFF_TABLES; i++) {
- _huff[i].count = 0;
- _huff[i].values = NULL;
- _huff[i].sizes = NULL;
- _huff[i].codes = NULL;
- }
+JPEGDecoder::JPEGDecoder() : ImageDecoder(), _surface(), _colorSpace(kColorSpaceRGBA) {
}
JPEGDecoder::~JPEGDecoder() {
@@ -65,723 +52,215 @@ JPEGDecoder::~JPEGDecoder() {
}
const Surface *JPEGDecoder::getSurface() const {
- // Make sure we have loaded data
- if (!isLoaded())
- return 0;
-
- if (_rgbSurface)
- return _rgbSurface;
-
- // Create an RGBA8888 surface
- _rgbSurface = new Graphics::Surface();
- _rgbSurface->create(_w, _h, Graphics::PixelFormat(4, 8, 8, 8, 0, 24, 16, 8, 0));
-
- // Get our component surfaces
- const Graphics::Surface *yComponent = getComponent(1);
- const Graphics::Surface *uComponent = getComponent(2);
- const Graphics::Surface *vComponent = getComponent(3);
-
- YUVToRGBMan.convert444(_rgbSurface, Graphics::YUVToRGBManager::kScaleFull, (byte *)yComponent->pixels, (byte *)uComponent->pixels, (byte *)vComponent->pixels, yComponent->w, yComponent->h, yComponent->pitch, uComponent->pitch);
-
- return _rgbSurface;
+ return &_surface;
}
void JPEGDecoder::destroy() {
- // Reset member variables
- _stream = NULL;
- _w = _h = 0;
- _restartInterval = 0;
-
- // Free the components
- for (int c = 0; c < _numComp; c++)
- _components[c].surface.free();
- delete[] _components; _components = NULL;
- _numComp = 0;
-
- // Free the scan components
- delete[] _scanComp; _scanComp = NULL;
- _numScanComp = 0;
- _currentComp = NULL;
-
- // Free the quantization tables
- for (int i = 0; i < JPEG_MAX_QUANT_TABLES; i++) {
- delete[] _quant[i];
- _quant[i] = NULL;
- }
-
- // Free the Huffman tables
- for (int i = 0; i < 2 * JPEG_MAX_HUFF_TABLES; i++) {
- _huff[i].count = 0;
- delete[] _huff[i].values; _huff[i].values = NULL;
- delete[] _huff[i].sizes; _huff[i].sizes = NULL;
- delete[] _huff[i].codes; _huff[i].codes = NULL;
- }
-
- if (_rgbSurface) {
- _rgbSurface->free();
- delete _rgbSurface;
- }
+ _surface.free();
}
-bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) {
- // Reset member variables and tables from previous reads
- destroy();
-
- // Save the input stream
- _stream = &stream;
-
- bool ok = true;
- bool done = false;
- while (!_stream->eos() && ok && !done) {
- // Read the marker
-
- // WORKAROUND: While each and every JPEG file should end with
- // an EOI (end of image) tag, in reality this may not be the
- // case. For instance, at least one image in the Masterpiece
- // edition of Myst doesn't, yet other programs are able to read
- // the image without complaining.
- //
- // Apparently, the customary workaround is to insert a fake
- // EOI tag.
-
- uint16 marker = _stream->readByte();
- bool fakeEOI = false;
-
- if (_stream->eos()) {
- fakeEOI = true;
- marker = 0xFF;
- }
-
- if (marker != 0xFF) {
- error("JPEG: Invalid marker[0]: 0x%02X", marker);
- ok = false;
- break;
- }
-
- while (marker == 0xFF && !_stream->eos())
- marker = _stream->readByte();
-
- if (_stream->eos()) {
- fakeEOI = true;
- marker = 0xD9;
- }
-
- if (fakeEOI)
- warning("JPEG: Inserted fake EOI");
+#ifdef USE_JPEG
+namespace {
- // Process the marker data
- switch (marker) {
- case 0xC0: // Start Of Frame
- ok = readSOF0();
- break;
- case 0xC4: // Define Huffman Tables
- ok = readDHT();
- break;
- case 0xD8: // Start Of Image
- break;
- case 0xD9: // End Of Image
- done = true;
- break;
- case 0xDA: // Start Of Scan
- ok = readSOS();
- break;
- case 0xDB: // Define Quantization Tables
- ok = readDQT();
- break;
- case 0xE0: // JFIF/JFXX segment
- ok = readJFIF();
- break;
- case 0xDD: // Define Restart Interval
- ok = readDRI();
- break;
- case 0xFE: // Comment
- _stream->seek(_stream->readUint16BE() - 2, SEEK_CUR);
- break;
- default: { // Unknown marker
- uint16 size = _stream->readUint16BE();
+#define JPEG_BUFFER_SIZE 4096
- if ((marker & 0xE0) != 0xE0)
- warning("JPEG: Unknown marker %02X, skipping %d bytes", marker, size - 2);
-
- _stream->seek(size - 2, SEEK_CUR);
- }
- }
- }
+struct StreamSource : public jpeg_source_mgr {
+ Common::SeekableReadStream *stream;
+ bool startOfFile;
+ JOCTET buffer[JPEG_BUFFER_SIZE];
+};
- _stream = 0;
- return ok;
+void initSource(j_decompress_ptr cinfo) {
+ StreamSource *source = (StreamSource *)cinfo->src;
+ source->startOfFile = true;
}
-bool JPEGDecoder::readJFIF() {
- uint16 length = _stream->readUint16BE();
- uint32 tag = _stream->readUint32BE();
+boolean fillInputBuffer(j_decompress_ptr cinfo) {
+ StreamSource *source = (StreamSource *)cinfo->src;
- if (tag != MKTAG('J', 'F', 'I', 'F')) {
- warning("JPEGDecoder::readJFIF() tag mismatch");
- return false;
- }
-
- if (_stream->readByte() != 0) { // NULL
- warning("JPEGDecoder::readJFIF() NULL mismatch");
- return false;
+ uint32 bufferSize = source->stream->read((byte *)source->buffer, sizeof(source->buffer));
+ if (bufferSize == 0) {
+ if (source->startOfFile) {
+ // An empty file is a fatal error
+ ERREXIT(cinfo, JERR_INPUT_EMPTY);
+ } else {
+ // Otherwise we insert an EOF marker
+ WARNMS(cinfo, JWRN_JPEG_EOF);
+ source->buffer[0] = (JOCTET)0xFF;
+ source->buffer[1] = (JOCTET)JPEG_EOI;
+ bufferSize = 2;
+ }
}
- byte majorVersion = _stream->readByte();
- byte minorVersion = _stream->readByte();
- if (majorVersion != 1 || minorVersion > 2)
- warning("JPEGDecoder::readJFIF(): v%d.%02d JPEGs may not be handled correctly", majorVersion, minorVersion);
-
- /* byte densityUnits = */_stream->readByte();
- /* uint16 xDensity = */_stream->readUint16BE();
- /* uint16 yDensity = */_stream->readUint16BE();
- byte thumbW = _stream->readByte();
- byte thumbH = _stream->readByte();
-
- _stream->seek(thumbW * thumbH * 3, SEEK_CUR); // Ignore thumbnail
- if (length != (thumbW * thumbH * 3) + 16) {
- warning("JPEGDecoder::readJFIF() length mismatch");
- return false;
- }
+ source->next_input_byte = source->buffer;
+ source->bytes_in_buffer = bufferSize;
+ source->startOfFile = false;
- return true;
+ return TRUE;
}
-// Marker 0xC0 (Start Of Frame, Baseline DCT)
-bool JPEGDecoder::readSOF0() {
- debug(5, "JPEG: readSOF0");
- uint16 size = _stream->readUint16BE();
-
- // Read the sample precision
- uint8 precision = _stream->readByte();
- if (precision != 8) {
- warning("JPEG: Just 8 bit precision supported at the moment");
- return false;
- }
+void skipInputData(j_decompress_ptr cinfo, long numBytes) {
+ StreamSource *source = (StreamSource *)cinfo->src;
- // Image size
- _h = _stream->readUint16BE();
- _w = _stream->readUint16BE();
-
- // Number of components
- _numComp = _stream->readByte();
- if (size != 8 + 3 * _numComp) {
- warning("JPEG: Invalid number of components");
- return false;
- }
+ if (numBytes > 0) {
+ if (numBytes > (long)source->bytes_in_buffer) {
+ // In case we need to skip more bytes than there are in the buffer
+ // we will skip the remaining data and fill the buffer again
+ numBytes -= (long)source->bytes_in_buffer;
- // Allocate the new components
- delete[] _components;
- _components = new Component[_numComp];
-
- // Read the components details
- for (int c = 0; c < _numComp; c++) {
- _components[c].id = _stream->readByte();
- _components[c].factorH = _stream->readByte();
- _components[c].factorV = _components[c].factorH & 0xF;
- _components[c].factorH >>= 4;
- _components[c].quantTableSelector = _stream->readByte();
- }
-
- return true;
-}
+ // Skip the remaining bytes
+ source->stream->skip(numBytes);
-// Marker 0xC4 (Define Huffman Tables)
-bool JPEGDecoder::readDHT() {
- debug(5, "JPEG: readDHT");
- uint16 size = _stream->readUint16BE() - 2;
- uint32 pos = _stream->pos();
-
- while ((uint32)_stream->pos() < (size + pos)) {
- // Read the table type and id
- uint8 tableId = _stream->readByte();
- uint8 tableType = tableId >> 4; // type 0: DC, 1: AC
- tableId &= 0xF;
- uint8 tableNum = (tableId << 1) + tableType;
-
- // Free the Huffman table
- delete[] _huff[tableNum].values; _huff[tableNum].values = NULL;
- delete[] _huff[tableNum].sizes; _huff[tableNum].sizes = NULL;
- delete[] _huff[tableNum].codes; _huff[tableNum].codes = NULL;
-
- // Read the number of values for each length
- uint8 numValues[16];
- _huff[tableNum].count = 0;
- for (int len = 0; len < 16; len++) {
- numValues[len] = _stream->readByte();
- _huff[tableNum].count += numValues[len];
- }
-
- // Allocate memory for the current table
- _huff[tableNum].values = new uint8[_huff[tableNum].count];
- _huff[tableNum].sizes = new uint8[_huff[tableNum].count];
- _huff[tableNum].codes = new uint16[_huff[tableNum].count];
-
- // Read the table contents
- int cur = 0;
- for (int len = 0; len < 16; len++) {
- for (int i = 0; i < numValues[len]; i++) {
- _huff[tableNum].values[cur] = _stream->readByte();
- _huff[tableNum].sizes[cur] = len + 1;
- cur++;
- }
+ // Fill up the buffer again
+ (*source->fill_input_buffer)(cinfo);
+ } else {
+ source->next_input_byte += (size_t)numBytes;
+ source->bytes_in_buffer -= (size_t)numBytes;
}
- // Fill the table of Huffman codes
- cur = 0;
- uint16 curCode = 0;
- uint8 curCodeSize = _huff[tableNum].sizes[0];
- while (cur < _huff[tableNum].count) {
- // Increase the code size to fit the request
- while (_huff[tableNum].sizes[cur] != curCodeSize) {
- curCode <<= 1;
- curCodeSize++;
- }
-
- // Assign the current code
- _huff[tableNum].codes[cur] = curCode;
- curCode++;
- cur++;
- }
}
-
- return true;
}
-// Marker 0xDA (Start Of Scan)
-bool JPEGDecoder::readSOS() {
- debug(5, "JPEG: readSOS");
- uint16 size = _stream->readUint16BE();
-
- // Number of scan components
- _numScanComp = _stream->readByte();
- if (size != 6 + 2 * _numScanComp) {
- warning("JPEG: Invalid number of components");
- return false;
- }
-
- // Allocate the new scan components
- delete[] _scanComp;
- _scanComp = new Component *[_numScanComp];
-
- // Reset the maximum sampling factors
- _maxFactorV = 0;
- _maxFactorH = 0;
-
- // Component-specification parameters
- for (int c = 0; c < _numScanComp; c++) {
- // Read the desired component id
- uint8 id = _stream->readByte();
-
- // Search the component with the specified id
- bool found = false;
- for (int i = 0; !found && i < _numComp; i++) {
- if (_components[i].id == id) {
- // We found the desired component
- found = true;
-
- // Assign the found component to the c'th scan component
- _scanComp[c] = &_components[i];
- }
- }
-
- if (!found) {
- warning("JPEG: Invalid component");
- return false;
- }
-
- // Read the entropy table selectors
- _scanComp[c]->DCentropyTableSelector = _stream->readByte();
- _scanComp[c]->ACentropyTableSelector = _scanComp[c]->DCentropyTableSelector & 0xF;
- _scanComp[c]->DCentropyTableSelector >>= 4;
-
- // Calculate the maximum sampling factors
- if (_scanComp[c]->factorV > _maxFactorV)
- _maxFactorV = _scanComp[c]->factorV;
-
- if (_scanComp[c]->factorH > _maxFactorH)
- _maxFactorH = _scanComp[c]->factorH;
-
- // Initialize the DC predictor
- _scanComp[c]->DCpredictor = 0;
- }
-
- // Start of spectral selection
- if (_stream->readByte() != 0) {
- warning("JPEG: Progressive scanning not supported");
- return false;
- }
-
- // End of spectral selection
- if (_stream->readByte() != 63) {
- warning("JPEG: Progressive scanning not supported");
- return false;
- }
-
- // Successive approximation parameters
- if (_stream->readByte() != 0) {
- warning("JPEG: Progressive scanning not supported");
- return false;
- }
-
- // Entropy coded sequence starts, initialize Huffman decoder
- _bitsNumber = 0;
-
- // Read all the scan MCUs
- uint16 xMCU = _w / (_maxFactorH * 8);
- uint16 yMCU = _h / (_maxFactorV * 8);
-
- // Check for non- multiple-of-8 dimensions
- if (_w % (_maxFactorH * 8) != 0)
- xMCU++;
- if (_h % (_maxFactorV * 8) != 0)
- yMCU++;
-
- // Initialize the scan surfaces
- for (uint16 c = 0; c < _numScanComp; c++) {
- _scanComp[c]->surface.create(xMCU * _maxFactorH * 8, yMCU * _maxFactorV * 8, PixelFormat::createFormatCLUT8());
- }
-
- bool ok = true;
- uint16 interval = _restartInterval;
-
- for (int y = 0; ok && (y < yMCU); y++) {
- for (int x = 0; ok && (x < xMCU); x++) {
- ok = readMCU(x, y);
-
- // If we have a restart interval, we'll need to reset a couple
- // variables
- if (_restartInterval != 0) {
- interval--;
-
- if (interval == 0) {
- interval = _restartInterval;
- _bitsNumber = 0;
-
- for (byte i = 0; i < _numScanComp; i++)
- _scanComp[i]->DCpredictor = 0;
- }
- }
- }
- }
-
- // Trim Component surfaces back to image height and width
- // Note: Code using jpeg must use surface.pitch correctly...
- for (uint16 c = 0; c < _numScanComp; c++) {
- _scanComp[c]->surface.w = _w;
- _scanComp[c]->surface.h = _h;
- }
-
- return ok;
+void termSource(j_decompress_ptr cinfo) {
}
-// Marker 0xDB (Define Quantization Tables)
-bool JPEGDecoder::readDQT() {
- debug(5, "JPEG: readDQT");
- uint16 size = _stream->readUint16BE() - 2;
- uint32 pos = _stream->pos();
-
- while ((uint32)_stream->pos() < (pos + size)) {
- // Read the table precision and id
- uint8 tableId = _stream->readByte();
- bool highPrecision = (tableId & 0xF0) != 0;
-
- // Validate the table id
- tableId &= 0xF;
- if (tableId >= JPEG_MAX_QUANT_TABLES) {
- warning("JPEG: Invalid quantization table");
- return false;
- }
-
- // Create the new table if necessary
- if (!_quant[tableId])
- _quant[tableId] = new uint16[64];
+void jpeg_scummvm_src(j_decompress_ptr cinfo, Common::SeekableReadStream *stream) {
+ StreamSource *source;
- // Read the table (stored in Zig-Zag order)
- for (int i = 0; i < 64; i++)
- _quant[tableId][i] = highPrecision ? _stream->readUint16BE() : _stream->readByte();
+ // Initialize the source in case it has not been done yet.
+ if (cinfo->src == NULL) {
+ cinfo->src = (jpeg_source_mgr *)(*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(StreamSource));
}
- return true;
-}
+ source = (StreamSource *)cinfo->src;
+ source->init_source = &initSource;
+ source->fill_input_buffer = &fillInputBuffer;
+ source->skip_input_data = &skipInputData;
+ source->resync_to_restart = &jpeg_resync_to_restart;
+ source->term_source = &termSource;
+ source->bytes_in_buffer = 0;
+ source->next_input_byte = NULL;
-// Marker 0xDD (Define Restart Interval)
-bool JPEGDecoder::readDRI() {
- debug(5, "JPEG: readDRI");
- uint16 size = _stream->readUint16BE() - 2;
-
- if (size != 2) {
- warning("JPEG: Invalid DRI size %d", size);
- return false;
- }
-
- _restartInterval = _stream->readUint16BE();
- debug(5, "Restart interval: %d", _restartInterval);
- return true;
+ source->stream = stream;
}
-bool JPEGDecoder::readMCU(uint16 xMCU, uint16 yMCU) {
- bool ok = true;
- for (int c = 0; ok && (c < _numComp); c++) {
- // Set the current component
- _currentComp = _scanComp[c];
-
- // Read the data units of the current component
- for (int y = 0; ok && (y < _scanComp[c]->factorV); y++)
- for (int x = 0; ok && (x < _scanComp[c]->factorH); x++)
- ok = readDataUnit(xMCU * _scanComp[c]->factorH + x, yMCU * _scanComp[c]->factorV + y);
- }
-
- return ok;
+void errorExit(j_common_ptr cinfo) {
+ char buffer[JMSG_LENGTH_MAX];
+ (*cinfo->err->format_message)(cinfo, buffer);
+ // This function is not allowed to return to the caller, thus we simply
+ // error out with our error handling here.
+ error("%s", buffer);
}
-// triple-butterfly-add (and possible rounding)
-#define xadd3(xa, xb, xc, xd, h) \
- p = xa + xb; \
- n = xa - xb; \
- xa = p + xc + h; \
- xb = n + xd + h; \
- xc = p - xc + h; \
- xd = n - xd + h;
-
-// butterfly-mul
-#define xmul(xa, xb, k1, k2, sh) \
- n = k1 * (xa + xb); \
- p = xa; \
- xa = (n + (k2 - k1) * xb) >> sh; \
- xb = (n - (k2 + k1) * p) >> sh;
-
-// IDCT based on public domain code from http://halicery.com/jpeg/idct.html
-void JPEGDecoder::idct1D8x8(int32 src[8], int32 dest[64], int32 ps, int32 half) {
- int p, n;
-
- src[0] <<= 9;
- src[1] <<= 7;
- src[3] *= 181;
- src[4] <<= 9;
- src[5] *= 181;
- src[7] <<= 7;
-
- // Even part
- xmul(src[6], src[2], 277, 669, 0)
- xadd3(src[0], src[4], src[6], src[2], half)
-
- // Odd part
- xadd3(src[1], src[7], src[3], src[5], 0)
- xmul(src[5], src[3], 251, 50, 6)
- xmul(src[1], src[7], 213, 142, 6)
-
- dest[0 * 8] = (src[0] + src[1]) >> ps;
- dest[1 * 8] = (src[4] + src[5]) >> ps;
- dest[2 * 8] = (src[2] + src[3]) >> ps;
- dest[3 * 8] = (src[6] + src[7]) >> ps;
- dest[4 * 8] = (src[6] - src[7]) >> ps;
- dest[5 * 8] = (src[2] - src[3]) >> ps;
- dest[6 * 8] = (src[4] - src[5]) >> ps;
- dest[7 * 8] = (src[0] - src[1]) >> ps;
+void outputMessage(j_common_ptr cinfo) {
+ char buffer[JMSG_LENGTH_MAX];
+ (*cinfo->err->format_message)(cinfo, buffer);
+ // Is using debug here a good idea? Or do we want to ignore all libjpeg
+ // messages?
+ debug(3, "libjpeg: %s", buffer);
}
-void JPEGDecoder::idct2D8x8(int32 block[64]) {
- int32 tmp[64];
-
- // Apply 1D IDCT to rows
- for (int i = 0; i < 8; i++)
- idct1D8x8(&block[i * 8], &tmp[i], 9, 1 << 8);
-
- // Apply 1D IDCT to columns
- for (int i = 0; i < 8; i++)
- idct1D8x8(&tmp[i * 8], &block[i], 12, 1 << 11);
- }
-
-bool JPEGDecoder::readDataUnit(uint16 x, uint16 y) {
- // Prepare an empty data array
- int16 readData[64];
- for (int i = 1; i < 64; i++)
- readData[i] = 0;
-
- // Read the DC component
- readData[0] = _currentComp->DCpredictor + readDC();
- _currentComp->DCpredictor = readData[0];
-
- // Read the AC components (stored in Zig-Zag)
- readAC(readData);
-
- // Calculate the DCT coefficients from the input sequence
- int32 block[64];
- for (uint8 i = 0; i < 64; i++) {
- // Dequantize
- int32 val = readData[i];
- int16 quant = _quant[_currentComp->quantTableSelector][i];
- val *= quant;
-
- // Store the normalized coefficients, undoing the Zig-Zag
- block[_zigZagOrder[i]] = val;
- }
+} // End of anonymous namespace
+#endif
- // Apply the IDCT
- idct2D8x8(block);
+bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) {
+#ifdef USE_JPEG
+ // Reset member variables from previous decodings
+ destroy();
- // Level shift to make the values unsigned
- for (int i = 0; i < 64; i++) {
- block[i] = block[i] + 128;
+ jpeg_decompress_struct cinfo;
+ jpeg_error_mgr jerr;
- if (block[i] < 0)
- block[i] = 0;
+ // Initialize error handling callbacks
+ cinfo.err = jpeg_std_error(&jerr);
+ cinfo.err->error_exit = &errorExit;
+ cinfo.err->output_message = &outputMessage;
- if (block[i] > 255)
- block[i] = 255;
- }
+ // Initialize the decompression structure
+ jpeg_create_decompress(&cinfo);
- // Paint the component surface
- uint8 scalingV = _maxFactorV / _currentComp->factorV;
- uint8 scalingH = _maxFactorH / _currentComp->factorH;
+ // Initialize our buffer handling
+ jpeg_scummvm_src(&cinfo, &stream);
- // Convert coordinates from MCU blocks to pixels
- x <<= 3;
- y <<= 3;
+ // Read the file header
+ jpeg_read_header(&cinfo, TRUE);
- for (uint8 j = 0; j < 8; j++) {
- for (uint16 sV = 0; sV < scalingV; sV++) {
- // Get the beginning of the block line
- byte *ptr = (byte *)_currentComp->surface.getBasePtr(x * scalingH, (y + j) * scalingV + sV);
+ // We can request YUV output because Groovie requires it
+ switch (_colorSpace) {
+ case kColorSpaceRGBA:
+ cinfo.out_color_space = JCS_RGB;
+ break;
- for (uint8 i = 0; i < 8; i++) {
- for (uint16 sH = 0; sH < scalingH; sH++) {
- *ptr = (byte)(block[j * 8 + i]);
- ptr++;
- }
- }
- }
+ case kColorSpaceYUV:
+ cinfo.out_color_space = JCS_YCbCr;
+ break;
}
- return true;
-}
-
-int16 JPEGDecoder::readDC() {
- // DC is type 0
- uint8 tableNum = _currentComp->DCentropyTableSelector << 1;
-
- // Get the number of bits to read
- uint8 numBits = readHuff(tableNum);
-
- // Read the requested bits
- return readSignedBits(numBits);
-}
-
-void JPEGDecoder::readAC(int16 *out) {
- // AC is type 1
- uint8 tableNum = (_currentComp->ACentropyTableSelector << 1) + 1;
-
- // Start reading AC element 1
- uint8 cur = 1;
- while (cur < 64) {
- uint8 s = readHuff(tableNum);
- uint8 r = s >> 4;
- s &= 0xF;
-
- if (s == 0) {
- if (r == 15) {
- // Skip 16 values
- cur += 16;
- } else {
- // EOB: end of block
- cur = 64;
- }
- } else {
- // Skip r values
- cur += r;
-
- // Read the next value
- out[cur] = readSignedBits(s);
- cur++;
- }
+ // Actually start decompressing the image
+ jpeg_start_decompress(&cinfo);
+
+ // Allocate buffers for the output data
+ switch (_colorSpace) {
+ case kColorSpaceRGBA:
+ // We use RGBA8888 in this scenario
+ _surface.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat(4, 8, 8, 8, 0, 24, 16, 8, 0));
+ break;
+
+ case kColorSpaceYUV:
+ // We use YUV with 3 bytes per pixel otherwise.
+ // This is pretty ugly since our PixelFormat cannot express YUV...
+ _surface.create(cinfo.output_width, cinfo.output_height, Graphics::PixelFormat(3, 0, 0, 0, 0, 0, 0, 0, 0));
+ break;
}
-}
-
-int16 JPEGDecoder::readSignedBits(uint8 numBits) {
- uint16 ret = 0;
- if (numBits > 16)
- error("requested %d bits", numBits); //XXX
-
- // MSB=0 for negatives, 1 for positives
- for (int i = 0; i < numBits; i++)
- ret = (ret << 1) + readBit();
-
- // Extend sign bits (PAG109)
- if (!(ret >> (numBits - 1))) {
- uint16 tmp = ((uint16)-1 << numBits) + 1;
- ret = ret + tmp;
- }
- return ret;
-}
-
-// TODO: optimize?
-uint8 JPEGDecoder::readHuff(uint8 table) {
- bool foundCode = false;
- uint8 val = 0;
-
- uint8 cur = 0;
- uint8 codeSize = 1;
- uint16 code = readBit();
- while (!foundCode) {
- // Prepare a code of the current size
- while (codeSize < _huff[table].sizes[cur]) {
- code = (code << 1) + readBit();
- codeSize++;
- }
- // Compare the codes of the current size
- while (!foundCode && (codeSize == _huff[table].sizes[cur])) {
- if (code == _huff[table].codes[cur]) {
- // Found the code
- val = _huff[table].values[cur];
- foundCode = true;
- } else {
- // Continue reading
- cur++;
+ // Allocate buffer for one scanline
+ assert(cinfo.output_components == 3);
+ JDIMENSION pitch = cinfo.output_width * cinfo.output_components;
+ assert(_surface.pitch >= pitch);
+ JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, pitch, 1);
+
+ // Go through the image data scanline by scanline
+ while (cinfo.output_scanline < cinfo.output_height) {
+ byte *dst = (byte *)_surface.getBasePtr(0, cinfo.output_scanline);
+
+ jpeg_read_scanlines(&cinfo, buffer, 1);
+
+ const byte *src = buffer[0];
+ switch (_colorSpace) {
+ case kColorSpaceRGBA: {
+ for (int remaining = cinfo.output_width; remaining > 0; --remaining) {
+ byte r = *src++;
+ byte g = *src++;
+ byte b = *src++;
+ // We need to insert a alpha value of 255 (opaque) here.
+#ifdef SCUMM_BIG_ENDIAN
+ *dst++ = r;
+ *dst++ = g;
+ *dst++ = b;
+ *dst++ = 0xFF;
+#else
+ *dst++ = 0xFF;
+ *dst++ = b;
+ *dst++ = g;
+ *dst++ = r;
+#endif
}
- }
- }
+ } break;
- return val;
-}
-
-uint8 JPEGDecoder::readBit() {
- // Read a whole byte if necessary
- if (_bitsNumber == 0) {
- _bitsData = _stream->readByte();
- _bitsNumber = 8;
-
- // Detect markers
- if (_bitsData == 0xFF) {
- uint8 byte2 = _stream->readByte();
-
- // A stuffed 0 validates the previous byte
- if (byte2 != 0) {
- if (byte2 == 0xDC) {
- // DNL marker: Define Number of Lines
- // TODO: terminate scan
- warning("DNL marker detected: terminate scan");
- } else if (byte2 >= 0xD0 && byte2 <= 0xD7) {
- debug(7, "RST%d marker detected", byte2 & 7);
- _bitsData = _stream->readByte();
- } else {
- warning("Error: marker 0x%02X read in entropy data", byte2);
- }
- }
+ case kColorSpaceYUV:
+ memcpy(dst, src, pitch);
+ break;
}
}
- _bitsNumber--;
-
- return (_bitsData & (1 << _bitsNumber)) ? 1 : 0;
-}
-const Surface *JPEGDecoder::getComponent(uint c) const {
- for (int i = 0; i < _numComp; i++)
- if (_components[i].id == c) // We found the desired component
- return &_components[i].surface;
+ // We are done with decompressing, thus free all the data
+ jpeg_finish_decompress(&cinfo);
+ jpeg_destroy_decompress(&cinfo);
- error("JPEGDecoder::getComponent: No component %d present", c);
- return NULL;
+ return true;
+#else
+ return false;
+#endif
}
} // End of Graphics namespace
diff --git a/graphics/decoders/jpeg.h b/graphics/decoders/jpeg.h
index d59b72adf4..8460bc2698 100644
--- a/graphics/decoders/jpeg.h
+++ b/graphics/decoders/jpeg.h
@@ -40,100 +40,54 @@ class SeekableReadStream;
namespace Graphics {
-struct PixelFormat;
-
-#define JPEG_MAX_QUANT_TABLES 4
-#define JPEG_MAX_HUFF_TABLES 2
-
class JPEGDecoder : public ImageDecoder {
public:
JPEGDecoder();
~JPEGDecoder();
// ImageDecoder API
- void destroy();
- bool loadStream(Common::SeekableReadStream &str);
- const Surface *getSurface() const;
-
- bool isLoaded() const { return _numComp && _w && _h; }
- uint16 getWidth() const { return _w; }
- uint16 getHeight() const { return _h; }
- const Surface *getComponent(uint c) const;
-
-private:
- Common::SeekableReadStream *_stream;
- uint16 _w, _h;
- uint16 _restartInterval;
-
- // mutable so that we can convert to RGB only during
- // a getSurface() call while still upholding the
- // const requirement in other ImageDecoders
- mutable Graphics::Surface *_rgbSurface;
-
- // Image components
- uint8 _numComp;
- struct Component {
- // Global values
- uint8 id;
- uint8 factorH;
- uint8 factorV;
- uint8 quantTableSelector;
-
- // Scan specific values
- uint8 DCentropyTableSelector;
- uint8 ACentropyTableSelector;
- int16 DCpredictor;
-
- // Result image for this component
- Surface surface;
+ virtual void destroy();
+ virtual bool loadStream(Common::SeekableReadStream &str);
+ virtual const Surface *getSurface() const;
+
+ // Special API for JPEG
+ enum ColorSpace {
+ /**
+ * Output 32bit RGBA data.
+ *
+ * This is the default output.
+ */
+ kColorSpaceRGBA,
+
+ /**
+ * Output (interleaved) YUV data.
+ *
+ * Be aware that some images cannot be output in YUV mode.
+ * These are (non-standard) JPEG images which are in RGB colorspace.
+ *
+ * The resulting Surface will have a PixelFormat with 3 bytes per
+ * pixel and the remaining entries are completely zeroed. This works
+ * around the fact that PixelFormat can only describe RGB formats.
+ *
+ * You should only use this when you are really aware of what you are
+ * doing!
+ */
+ kColorSpaceYUV
};
- Component *_components;
+ /**
+ * Request the output color space. This can be used to obtain raw YUV
+ * data from the JPEG file. But this might not work for all files!
+ *
+ * The decoder itself defaults to RGBA.
+ *
+ * @param outSpace The color space to output.
+ */
+ void setOutputColorSpace(ColorSpace outSpace) { _colorSpace = outSpace; }
- // Scan components
- uint8 _numScanComp;
- Component **_scanComp;
- Component *_currentComp;
-
- // Maximum sampling factors, used to calculate the interleaving of the MCU
- uint8 _maxFactorV;
- uint8 _maxFactorH;
-
- // Quantization tables
- uint16 *_quant[JPEG_MAX_QUANT_TABLES];
-
- // Huffman tables
- struct HuffmanTable {
- uint8 count;
- uint8 *values;
- uint8 *sizes;
- uint16 *codes;
- } _huff[2 * JPEG_MAX_HUFF_TABLES];
-
- // Marker read functions
- bool readJFIF();
- bool readSOF0();
- bool readDHT();
- bool readSOS();
- bool readDQT();
- bool readDRI();
-
- // Helper functions
- bool readMCU(uint16 xMCU, uint16 yMCU);
- bool readDataUnit(uint16 x, uint16 y);
- int16 readDC();
- void readAC(int16 *out);
- int16 readSignedBits(uint8 numBits);
-
- // Huffman decoding
- uint8 readHuff(uint8 table);
- uint8 readBit();
- uint8 _bitsData;
- uint8 _bitsNumber;
-
- // Inverse Discrete Cosine Transformation
- static void idct1D8x8(int32 src[8], int32 dest[64], int32 ps, int32 half);
- static void idct2D8x8(int32 block[64]);
+private:
+ Graphics::Surface _surface;
+ ColorSpace _colorSpace;
};
} // End of Graphics namespace
diff --git a/graphics/decoders/pcx.cpp b/graphics/decoders/pcx.cpp
index 1250398c73..eb9b4c997d 100644
--- a/graphics/decoders/pcx.cpp
+++ b/graphics/decoders/pcx.cpp
@@ -117,7 +117,7 @@ bool PCXDecoder::loadStream(Common::SeekableReadStream &stream) {
if (nPlanes == 3 && bitsPerPixel == 8) { // 24bpp
Graphics::PixelFormat format = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
_surface->create(width, height, format);
- dst = (byte *)_surface->pixels;
+ dst = (byte *)_surface->getPixels();
_paletteColorCount = 0;
for (y = 0; y < height; y++) {
@@ -135,7 +135,7 @@ bool PCXDecoder::loadStream(Common::SeekableReadStream &stream) {
}
} else if (nPlanes == 1 && bitsPerPixel == 8) { // 8bpp indexed
_surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
- dst = (byte *)_surface->pixels;
+ dst = (byte *)_surface->getPixels();
_paletteColorCount = 16;
for (y = 0; y < height; y++, dst += _surface->pitch) {
@@ -163,7 +163,7 @@ bool PCXDecoder::loadStream(Common::SeekableReadStream &stream) {
}
} else if ((nPlanes == 2 || nPlanes == 3 || nPlanes == 4) && bitsPerPixel == 1) { // planar, 4, 8 or 16 colors
_surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
- dst = (byte *)_surface->pixels;
+ dst = (byte *)_surface->getPixels();
_paletteColorCount = 16;
for (y = 0; y < height; y++, dst += _surface->pitch) {
diff --git a/graphics/decoders/pict.cpp b/graphics/decoders/pict.cpp
index b1d408ebc3..f3e17b33e2 100644
--- a/graphics/decoders/pict.cpp
+++ b/graphics/decoders/pict.cpp
@@ -364,7 +364,7 @@ void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool withPa
case 1:
// Just copy to the image
_outputSurface->create(width, height, PixelFormat::createFormatCLUT8());
- memcpy(_outputSurface->pixels, buffer, _outputSurface->w * _outputSurface->h);
+ memcpy(_outputSurface->getPixels(), buffer, _outputSurface->w * _outputSurface->h);
break;
case 2:
// We have a 16-bit surface
diff --git a/graphics/decoders/png.cpp b/graphics/decoders/png.cpp
index 11e26162eb..c4fee46fec 100644
--- a/graphics/decoders/png.cpp
+++ b/graphics/decoders/png.cpp
@@ -38,7 +38,7 @@
namespace Graphics {
-PNGDecoder::PNGDecoder() : _outputSurface(0), _palette(0), _paletteColorCount(0) {
+PNGDecoder::PNGDecoder() : _outputSurface(0), _palette(0), _paletteColorCount(0), _stream(0) {
}
PNGDecoder::~PNGDecoder() {
@@ -163,16 +163,20 @@ bool PNGDecoder::loadStream(Common::SeekableReadStream &stream) {
_outputSurface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
png_set_packing(pngPtr);
} else {
- _outputSurface->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
- if (!_outputSurface->pixels) {
+ bool isAlpha = (colorType & PNG_COLOR_MASK_ALPHA);
+ if (png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS)) {
+ isAlpha = true;
+ png_set_expand(pngPtr);
+ }
+ _outputSurface->create(width, height, Graphics::PixelFormat(4,
+ 8, 8, 8, isAlpha ? 8 : 0, 24, 16, 8, 0));
+ if (!_outputSurface->getPixels()) {
error("Could not allocate memory for output image.");
}
if (bitDepth == 16)
png_set_strip_16(pngPtr);
if (bitDepth < 8)
png_set_expand(pngPtr);
- if (png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS))
- png_set_expand(pngPtr);
if (colorType == PNG_COLOR_TYPE_GRAY ||
colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(pngPtr);
diff --git a/graphics/decoders/tga.cpp b/graphics/decoders/tga.cpp
index c3b9d84055..bc27f18a49 100644
--- a/graphics/decoders/tga.cpp
+++ b/graphics/decoders/tga.cpp
@@ -50,6 +50,8 @@ void TGADecoder::destroy() {
}
bool TGADecoder::loadStream(Common::SeekableReadStream &tga) {
+ destroy();
+
byte imageType, pixelDepth;
bool success;
success = readHeader(tga, imageType, pixelDepth);
@@ -272,7 +274,7 @@ bool TGADecoder::readData(Common::SeekableReadStream &tga, byte imageType, byte
} else if (imageType == TYPE_BW) {
_surface.create(_surface.w, _surface.h, _format);
- byte *data = (byte *)_surface.pixels;
+ byte *data = (byte *)_surface.getPixels();
uint32 count = _surface.w * _surface.h;
while (count-- > 0) {
@@ -318,7 +320,7 @@ bool TGADecoder::readDataRLE(Common::SeekableReadStream &tga, byte imageType, by
if (imageType == TYPE_RLE_TRUECOLOR || imageType == TYPE_RLE_BW || imageType == TYPE_RLE_CMAP) {
_surface.create(_surface.w, _surface.h, _format);
uint32 count = _surface.w * _surface.h;
- byte *data = (byte *)_surface.pixels;
+ byte *data = (byte *)_surface.getPixels();
while (count > 0) {
uint32 header = tga.readByte();
diff --git a/graphics/decoders/tga.h b/graphics/decoders/tga.h
index d8ccf8f766..5b1e87d4c5 100644
--- a/graphics/decoders/tga.h
+++ b/graphics/decoders/tga.h
@@ -26,6 +26,7 @@
/*
* TGA decoder used in engines:
* - wintermute
+ * - zvision
*/
#ifndef GRAPHICS_DECODERS_TGA_H
diff --git a/graphics/font.cpp b/graphics/font.cpp
index 3b00cd8568..e9f139c478 100644
--- a/graphics/font.cpp
+++ b/graphics/font.cpp
@@ -26,93 +26,32 @@
namespace Graphics {
-int Font::getKerningOffset(byte left, byte right) const {
+int Font::getKerningOffset(uint32 left, uint32 right) const {
return 0;
}
-int Font::getStringWidth(const Common::String &str) const {
+namespace {
+
+template<class StringType>
+int getStringWidthImpl(const Font &font, const StringType &str) {
int space = 0;
- uint last = 0;
+ typename StringType::unsigned_type last = 0;
for (uint i = 0; i < str.size(); ++i) {
- const uint cur = str[i];
- space += getCharWidth(cur) + getKerningOffset(last, cur);
+ const typename StringType::unsigned_type cur = str[i];
+ space += font.getCharWidth(cur) + font.getKerningOffset(last, cur);
last = cur;
}
return space;
}
-void Font::drawString(Surface *dst, const Common::String &sOld, int x, int y, int w, uint32 color, TextAlign align, int deltax, bool useEllipsis) const {
+template<class StringType>
+void drawStringImpl(const Font &font, Surface *dst, const StringType &str, int x, int y, int w, uint32 color, TextAlign align, int deltax) {
assert(dst != 0);
- const int leftX = x, rightX = x + w;
- uint i;
- Common::String s = sOld;
- int width = getStringWidth(s);
- Common::String str;
-
- if (useEllipsis && width > w && s.hasSuffix("...")) {
- // String is too wide. Check whether it ends in an ellipsis
- // ("..."). If so, remove that and try again!
- s.deleteLastChar();
- s.deleteLastChar();
- s.deleteLastChar();
- width = getStringWidth(s);
- }
-
- if (useEllipsis && width > w) {
- // String is too wide. So we shorten it "intelligently" by
- // replacing parts of the string 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 seems to be a good compromise.
-
- const int ellipsisWidth = 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;
- uint last = 0;
-
- for (i = 0; i < s.size(); ++i) {
- const uint cur = s[i];
- int charWidth = getCharWidth(cur) + getKerningOffset(last, cur);
- if (w2 + charWidth > halfWidth)
- break;
- last = cur;
- w2 += charWidth;
- str += cur;
- }
-
- // 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 += "...";
- last = '.';
- // 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 accommodate 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) {
- const uint cur = s[i];
- skip -= getCharWidth(cur) + getKerningOffset(last, cur);
- last = cur;
- }
-
- // Append the remaining chars, if any
- for (; i < s.size(); ++i) {
- str += s[i];
- }
-
- width = getStringWidth(str);
- } else {
- str = s;
- }
+ const int leftX = x, rightX = x + w;
+ int width = font.getStringWidth(str);
if (align == kTextAlignCenter)
x = x + (w - width)/2;
@@ -120,29 +59,29 @@ void Font::drawString(Surface *dst, const Common::String &sOld, int x, int y, in
x = x + w - width;
x += deltax;
- uint last = 0;
- for (i = 0; i < str.size(); ++i) {
- const uint cur = str[i];
- x += getKerningOffset(last, cur);
+ typename StringType::unsigned_type last = 0;
+ for (typename StringType::const_iterator i = str.begin(), end = str.end(); i != end; ++i) {
+ const typename StringType::unsigned_type cur = *i;
+ x += font.getKerningOffset(last, cur);
last = cur;
- w = getCharWidth(cur);
+ w = font.getCharWidth(cur);
if (x+w > rightX)
break;
- if (x >= leftX)
- drawChar(dst, str[i], x, y, color);
+ if (x+w >= leftX)
+ font.drawChar(dst, cur, x, y, color);
x += w;
}
}
-
+template<class StringType>
struct WordWrapper {
- Common::Array<Common::String> &lines;
+ Common::Array<StringType> &lines;
int actualMaxLineWidth;
- WordWrapper(Common::Array<Common::String> &l) : lines(l), actualMaxLineWidth(0) {
+ WordWrapper(Common::Array<StringType> &l) : lines(l), actualMaxLineWidth(0) {
}
- void add(Common::String &line, int &w) {
+ void add(StringType &line, int &w) {
if (actualMaxLineWidth < w)
actualMaxLineWidth = w;
@@ -153,10 +92,11 @@ struct WordWrapper {
}
};
-int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array<Common::String> &lines) const {
- WordWrapper wrapper(lines);
- Common::String line;
- Common::String tmpStr;
+template<class StringType>
+int wordWrapTextImpl(const Font &font, const StringType &str, int maxWidth, Common::Array<StringType> &lines) {
+ WordWrapper<StringType> wrapper(lines);
+ StringType line;
+ StringType tmpStr;
int lineWidth = 0;
int tmpWidth = 0;
@@ -173,10 +113,10 @@ int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array<Co
// of a line. If we encounter such a word, we have to wrap it over multiple
// lines.
- uint last = 0;
- for (Common::String::const_iterator x = str.begin(); x != str.end(); ++x) {
- const byte c = *x;
- const int w = getCharWidth(c) + getKerningOffset(last, c);
+ typename StringType::unsigned_type last = 0;
+ for (typename StringType::const_iterator x = str.begin(); x != str.end(); ++x) {
+ const typename StringType::unsigned_type c = *x;
+ const int w = font.getCharWidth(c) + font.getKerningOffset(last, c);
last = c;
const bool wouldExceedWidth = (lineWidth + tmpWidth + w > maxWidth);
@@ -212,7 +152,7 @@ int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array<Co
tmpStr.deleteChar(0);
// This is not very fast, but it is the simplest way to
// assure we do not mess something up because of kerning.
- tmpWidth = getStringWidth(tmpStr);
+ tmpWidth = font.getStringWidth(tmpStr);
}
} else {
wrapper.add(tmpStr, tmpWidth);
@@ -232,5 +172,98 @@ int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array<Co
return wrapper.actualMaxLineWidth;
}
+} // End of anonymous namespace
+
+int Font::getStringWidth(const Common::String &str) const {
+ return getStringWidthImpl(*this, str);
+}
+
+int Font::getStringWidth(const Common::U32String &str) const {
+ return getStringWidthImpl(*this, str);
+}
+
+void Font::drawString(Surface *dst, const Common::String &sOld, int x, int y, int w, uint32 color, TextAlign align, int deltax, bool useEllipsis) const {
+ Common::String s = sOld;
+ int width = getStringWidth(s);
+ Common::String str;
+
+ if (useEllipsis && width > w && s.hasSuffix("...")) {
+ // String is too wide. Check whether it ends in an ellipsis
+ // ("..."). If so, remove that and try again!
+ s.deleteLastChar();
+ s.deleteLastChar();
+ s.deleteLastChar();
+ width = getStringWidth(s);
+ }
+
+ if (useEllipsis && width > w) {
+ // String is too wide. So we shorten it "intelligently" by
+ // replacing parts of the string 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 seems to be a good compromise.
+
+ const int ellipsisWidth = 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;
+ Common::String::unsigned_type last = 0;
+ uint i;
+
+ for (i = 0; i < s.size(); ++i) {
+ const Common::String::unsigned_type cur = s[i];
+ int charWidth = getCharWidth(cur) + getKerningOffset(last, cur);
+ if (w2 + charWidth > halfWidth)
+ break;
+ last = cur;
+ w2 += charWidth;
+ str += cur;
+ }
+
+ // 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 += "...";
+ last = '.';
+
+ // 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 accommodate 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) {
+ const Common::String::unsigned_type cur = s[i];
+ skip -= getCharWidth(cur) + getKerningOffset(last, cur);
+ last = cur;
+ }
+
+ // Append the remaining chars, if any
+ for (; i < s.size(); ++i) {
+ str += s[i];
+ }
+
+ width = getStringWidth(str);
+ } else {
+ str = s;
+ }
+
+ drawStringImpl(*this, dst, str, x, y, w, color, align, deltax);
+}
+
+void Font::drawString(Surface *dst, const Common::U32String &str, int x, int y, int w, uint32 color, TextAlign align) const {
+ drawStringImpl(*this, dst, str, x, y, w, color, align, 0);
+}
+
+int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array<Common::String> &lines) const {
+ return wordWrapTextImpl(*this, str, maxWidth, lines);
+}
+
+int Font::wordWrapText(const Common::U32String &str, int maxWidth, Common::Array<Common::U32String> &lines) const {
+ return wordWrapTextImpl(*this, str, maxWidth, lines);
+}
} // End of namespace Graphics
diff --git a/graphics/font.h b/graphics/font.h
index 6819b42f52..77b7623a85 100644
--- a/graphics/font.h
+++ b/graphics/font.h
@@ -23,6 +23,7 @@
#define GRAPHICS_FONT_H
#include "common/str.h"
+#include "common/ustr.h"
namespace Common {
template<class T> class Array;
@@ -70,7 +71,7 @@ public:
* @param chr The character to query the width of.
* @return The character's width.
*/
- virtual int getCharWidth(byte chr) const = 0;
+ virtual int getCharWidth(uint32 chr) const = 0;
/**
* Query the kerning offset between two characters.
@@ -79,7 +80,7 @@ public:
* @param right The right character. May be 0.
* @return The horizontal displacement.
*/
- virtual int getKerningOffset(byte left, byte right) const;
+ virtual int getKerningOffset(uint32 left, uint32 right) const;
/**
* Draw a character at a specific point on a surface.
@@ -96,15 +97,17 @@ public:
* @param y The y coordinate where to draw the character.
* @param color The color of the character.
*/
- virtual void drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const = 0;
+ virtual void drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const = 0;
// TODO: Add doxygen comments to this
void drawString(Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft, int deltax = 0, bool useEllipsis = true) const;
+ void drawString(Surface *dst, const Common::U32String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft) const;
/**
* Compute and return the width the string str has when rendered using this font.
*/
int getStringWidth(const Common::String &str) const;
+ int getStringWidth(const Common::U32String &str) const;
/**
* Take a text (which may contain newline characters) and word wrap it so that
@@ -120,6 +123,7 @@ public:
* @return the maximal width of any of the lines added to lines
*/
int wordWrapText(const Common::String &str, int maxWidth, Common::Array<Common::String> &lines) const;
+ int wordWrapText(const Common::U32String &str, int maxWidth, Common::Array<Common::U32String> &lines) const;
};
} // End of namespace Graphics
diff --git a/graphics/fonts/bdf.cpp b/graphics/fonts/bdf.cpp
index 6d4befa37c..7b2290cc3a 100644
--- a/graphics/fonts/bdf.cpp
+++ b/graphics/fonts/bdf.cpp
@@ -51,7 +51,7 @@ int BdfFont::getMaxCharWidth() const {
return _data.maxAdvance;
}
-int BdfFont::getCharWidth(byte chr) const {
+int BdfFont::getCharWidth(uint32 chr) const {
// In case all font have the same advance value, we use the maximum.
if (!_data.advances)
return _data.maxAdvance;
@@ -85,9 +85,9 @@ void drawCharIntern(byte *ptr, uint pitch, const byte *src, int h, int width, in
}
}
-int BdfFont::mapToIndex(byte ch) const {
+int BdfFont::mapToIndex(uint32 ch) const {
// Check whether the character is included
- if (_data.firstCharacter <= ch && ch <= _data.firstCharacter + _data.numCharacters) {
+ if (_data.firstCharacter <= (int)ch && (int)ch <= _data.firstCharacter + _data.numCharacters) {
if (_data.bitmaps[ch - _data.firstCharacter])
return ch - _data.firstCharacter;
}
@@ -95,14 +95,14 @@ int BdfFont::mapToIndex(byte ch) const {
return _data.defaultCharacter - _data.firstCharacter;
}
-void BdfFont::drawChar(Surface *dst, byte chr, const int tx, const int ty, const uint32 color) const {
+void BdfFont::drawChar(Surface *dst, uint32 chr, const int tx, const int ty, const uint32 color) const {
assert(dst != 0);
// TODO: Where is the relation between the max advance being smaller or
// equal to 50 and the decision of the theme designer?
// asserting _data.maxAdvance <= 50: let the theme designer decide what looks best
assert(_data.maxAdvance <= 50);
- assert(dst->format.bytesPerPixel == 1 || dst->format.bytesPerPixel == 2);
+ assert(dst->format.bytesPerPixel == 1 || dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
const int idx = mapToIndex(chr);
if (idx < 0)
@@ -165,6 +165,8 @@ void BdfFont::drawChar(Surface *dst, byte chr, const int tx, const int ty, const
drawCharIntern<byte>(ptr, dst->pitch, src, height, originalWidth, xStart, xEnd, color);
else if (dst->format.bytesPerPixel == 2)
drawCharIntern<uint16>(ptr, dst->pitch, src, height, originalWidth, xStart, xEnd, color);
+ else if (dst->format.bytesPerPixel == 4)
+ drawCharIntern<uint32>(ptr, dst->pitch, src, height, originalWidth, xStart, xEnd, color);
}
namespace {
diff --git a/graphics/fonts/bdf.h b/graphics/fonts/bdf.h
index b0166a2095..842e54f851 100644
--- a/graphics/fonts/bdf.h
+++ b/graphics/fonts/bdf.h
@@ -61,14 +61,14 @@ public:
virtual int getFontHeight() const;
virtual int getMaxCharWidth() const;
- virtual int getCharWidth(byte chr) const;
- virtual void drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const;
+ virtual int getCharWidth(uint32 chr) const;
+ virtual void drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const;
static BdfFont *loadFont(Common::SeekableReadStream &stream);
static bool cacheFontData(const BdfFont &font, const Common::String &filename);
static BdfFont *loadFromCache(Common::SeekableReadStream &stream);
private:
- int mapToIndex(byte ch) const;
+ int mapToIndex(uint32 ch) const;
const BdfFontData _data;
const DisposeAfterUse::Flag _dispose;
diff --git a/graphics/fonts/ttf.cpp b/graphics/fonts/ttf.cpp
index 2b1dca1eae..93f119f028 100644
--- a/graphics/fonts/ttf.cpp
+++ b/graphics/fonts/ttf.cpp
@@ -107,11 +107,11 @@ public:
virtual int getMaxCharWidth() const;
- virtual int getCharWidth(byte chr) const;
+ virtual int getCharWidth(uint32 chr) const;
- virtual int getKerningOffset(byte left, byte right) const;
+ virtual int getKerningOffset(uint32 left, uint32 right) const;
- virtual void drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const;
+ virtual void drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const;
private:
bool _initialized;
FT_Face _face;
@@ -126,13 +126,14 @@ private:
Surface image;
int xOffset, yOffset;
int advance;
+ FT_UInt slot;
};
- bool cacheGlyph(Glyph &glyph, FT_UInt &slot, uint chr);
- typedef Common::HashMap<byte, Glyph> GlyphCache;
- GlyphCache _glyphs;
-
- FT_UInt _glyphSlots[256];
+ bool cacheGlyph(Glyph &glyph, uint32 chr) const;
+ typedef Common::HashMap<uint32, Glyph> GlyphCache;
+ mutable GlyphCache _glyphs;
+ bool _allowLateCaching;
+ void assureCached(uint32 chr) const;
bool _monochrome;
bool _hasKerning;
@@ -140,7 +141,7 @@ private:
TTFFont::TTFFont()
: _initialized(false), _face(), _ttfFile(0), _size(0), _width(0), _height(0), _ascent(0),
- _descent(0), _glyphs(), _glyphSlots(), _monochrome(false), _hasKerning(false) {
+ _descent(0), _glyphs(), _monochrome(false), _hasKerning(false), _allowLateCaching(false) {
}
TTFFont::~TTFFont() {
@@ -212,19 +213,26 @@ bool TTFFont::load(Common::SeekableReadStream &stream, int size, uint dpi, bool
_height = _ascent - _descent + 1;
if (!mapping) {
+ // Allow loading of all unicode characters.
+ _allowLateCaching = true;
+
// Load all ISO-8859-1 characters.
for (uint i = 0; i < 256; ++i) {
- if (!cacheGlyph(_glyphs[i], _glyphSlots[i], i))
- _glyphSlots[i] = 0;
+ if (!cacheGlyph(_glyphs[i], i)) {
+ _glyphs.erase(i);
+ }
}
} else {
+ // We have a fixed map of characters do not load more later.
+ _allowLateCaching = false;
+
for (uint i = 0; i < 256; ++i) {
const uint32 unicode = mapping[i] & 0x7FFFFFFF;
const bool isRequired = (mapping[i] & 0x80000000) != 0;
// Check whether loading an important glyph fails and error out if
// that is the case.
- if (!cacheGlyph(_glyphs[i], _glyphSlots[i], unicode)) {
- _glyphSlots[i] = 0;
+ if (!cacheGlyph(_glyphs[i], unicode)) {
+ _glyphs.erase(i);
if (isRequired)
return false;
}
@@ -243,7 +251,8 @@ int TTFFont::getMaxCharWidth() const {
return _width;
}
-int TTFFont::getCharWidth(byte chr) const {
+int TTFFont::getCharWidth(uint32 chr) const {
+ assureCached(chr);
GlyphCache::const_iterator glyphEntry = _glyphs.find(chr);
if (glyphEntry == _glyphs.end())
return 0;
@@ -251,12 +260,29 @@ int TTFFont::getCharWidth(byte chr) const {
return glyphEntry->_value.advance;
}
-int TTFFont::getKerningOffset(byte left, byte right) const {
+int TTFFont::getKerningOffset(uint32 left, uint32 right) const {
if (!_hasKerning)
return 0;
- FT_UInt leftGlyph = _glyphSlots[left];
- FT_UInt rightGlyph = _glyphSlots[right];
+ assureCached(left);
+ assureCached(right);
+
+ FT_UInt leftGlyph, rightGlyph;
+ GlyphCache::const_iterator glyphEntry;
+
+ glyphEntry = _glyphs.find(left);
+ if (glyphEntry != _glyphs.end()) {
+ leftGlyph = glyphEntry->_value.slot;
+ } else {
+ return 0;
+ }
+
+ glyphEntry = _glyphs.find(right);
+ if (glyphEntry != _glyphs.end()) {
+ rightGlyph = glyphEntry->_value.slot;
+ } else {
+ return 0;
+ }
if (!leftGlyph || !rightGlyph)
return 0;
@@ -304,7 +330,8 @@ void renderGlyph(uint8 *dstPos, const int dstPitch, const uint8 *srcPos, const i
} // End of anonymous namespace
-void TTFFont::drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const {
+void TTFFont::drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const {
+ assureCached(chr);
GlyphCache::const_iterator glyphEntry = _glyphs.find(chr);
if (glyphEntry == _glyphs.end())
return;
@@ -322,7 +349,7 @@ void TTFFont::drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const
int w = glyph.image.w;
int h = glyph.image.h;
- const uint8 *srcPos = (const uint8 *)glyph.image.getBasePtr(0, 0);
+ const uint8 *srcPos = (const uint8 *)glyph.image.getPixels();
// Make sure we are not drawing outside the screen bounds
if (x < 0) {
@@ -376,11 +403,13 @@ void TTFFont::drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const
}
}
-bool TTFFont::cacheGlyph(Glyph &glyph, FT_UInt &slot, uint chr) {
- slot = FT_Get_Char_Index(_face, chr);
+bool TTFFont::cacheGlyph(Glyph &glyph, uint32 chr) const {
+ FT_UInt slot = FT_Get_Char_Index(_face, chr);
if (!slot)
return false;
+ glyph.slot = slot;
+
// We use the light target and render mode to improve the looks of the
// glyphs. It is most noticable in FreeSansBold.ttf, where otherwise the
// 't' glyph looks like it is cut off on the right side.
@@ -422,7 +451,7 @@ bool TTFFont::cacheGlyph(Glyph &glyph, FT_UInt &slot, uint chr) {
srcPitch = -srcPitch;
}
- uint8 *dst = (uint8 *)glyph.image.getBasePtr(0, 0);
+ uint8 *dst = (uint8 *)glyph.image.getPixels();
memset(dst, 0, glyph.image.h * glyph.image.pitch);
switch (bitmap.pixel_mode) {
@@ -456,12 +485,24 @@ bool TTFFont::cacheGlyph(Glyph &glyph, FT_UInt &slot, uint chr) {
default:
warning("TTFFont::cacheGlyph: Unsupported pixel mode %d", bitmap.pixel_mode);
+ glyph.image.free();
return false;
}
return true;
}
+void TTFFont::assureCached(uint32 chr) const {
+ if (!chr || !_allowLateCaching || _glyphs.contains(chr)) {
+ return;
+ }
+
+ Glyph newGlyph;
+ if (cacheGlyph(newGlyph, chr)) {
+ _glyphs[chr] = newGlyph;
+ }
+}
+
Font *loadTTFFont(Common::SeekableReadStream &stream, int size, uint dpi, bool monochrome, const uint32 *mapping) {
TTFFont *font = new TTFFont();
diff --git a/graphics/fonts/winfont.cpp b/graphics/fonts/winfont.cpp
index 3bad92236d..c0ebab19ba 100644
--- a/graphics/fonts/winfont.cpp
+++ b/graphics/fonts/winfont.cpp
@@ -200,7 +200,7 @@ char WinFont::indexToCharacter(uint16 index) const {
return index + _firstChar;
}
-uint16 WinFont::characterToIndex(byte character) const {
+uint16 WinFont::characterToIndex(uint32 character) const {
// Go to the default character if we didn't find a mapping
if (character < _firstChar || character > _lastChar)
character = _defaultChar;
@@ -208,7 +208,7 @@ uint16 WinFont::characterToIndex(byte character) const {
return character - _firstChar;
}
-int WinFont::getCharWidth(byte chr) const {
+int WinFont::getCharWidth(uint32 chr) const {
return _glyphs[characterToIndex(chr)].charWidth;
}
@@ -324,7 +324,7 @@ bool WinFont::loadFromFNT(Common::SeekableReadStream &stream) {
return true;
}
-void WinFont::drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const {
+void WinFont::drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const {
assert(dst);
assert(dst->format.bytesPerPixel == 1 || dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
assert(_glyphs);
diff --git a/graphics/fonts/winfont.h b/graphics/fonts/winfont.h
index 4382d7ed6b..2c7ba07b41 100644
--- a/graphics/fonts/winfont.h
+++ b/graphics/fonts/winfont.h
@@ -62,8 +62,8 @@ public:
// Font API
int getFontHeight() const { return _pixHeight; }
int getMaxCharWidth() const { return _maxWidth; }
- int getCharWidth(byte chr) const;
- void drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const;
+ int getCharWidth(uint32 chr) const;
+ void drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const;
private:
bool loadFromPE(const Common::String &fileName, const WinFontDirEntry &dirEntry);
@@ -72,7 +72,7 @@ private:
uint32 getFontIndex(Common::SeekableReadStream &stream, const WinFontDirEntry &dirEntry);
bool loadFromFNT(Common::SeekableReadStream &stream);
char indexToCharacter(uint16 index) const;
- uint16 characterToIndex(byte character) const;
+ uint16 characterToIndex(uint32 character) const;
uint16 _pixHeight;
uint16 _maxWidth;
diff --git a/graphics/scaler/thumbnail_intern.cpp b/graphics/scaler/thumbnail_intern.cpp
index 88f3cc2077..c30fc3b6fd 100644
--- a/graphics/scaler/thumbnail_intern.cpp
+++ b/graphics/scaler/thumbnail_intern.cpp
@@ -42,8 +42,10 @@ uint16 quadBlockInterpolate(const uint8 *src, uint32 srcPitch) {
template<int bitFormat>
void createThumbnail_2(const uint8 *src, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, int width, int height) {
- assert(width % 2 == 0);
- assert(height % 2 == 0);
+ // Make sure the width and height is a multiple of 2.
+ width &= ~1;
+ height &= ~1;
+
for (int y = 0; y < height; y += 2) {
for (int x = 0; x < width; x += 2, dstPtr += 2) {
*((uint16 *)dstPtr) = quadBlockInterpolate<bitFormat>(src + 2 * x, srcPitch);
@@ -55,8 +57,10 @@ void createThumbnail_2(const uint8 *src, uint32 srcPitch, uint8 *dstPtr, uint32
template<int bitFormat>
void createThumbnail_4(const uint8 *src, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, int width, int height) {
- assert(width % 4 == 0);
- assert(height % 4 == 0);
+ // Make sure the width and height is a multiple of 4
+ width &= ~3;
+ height &= ~3;
+
for (int y = 0; y < height; y += 4) {
for (int x = 0; x < width; x += 4, dstPtr += 2) {
uint16 upleft = quadBlockInterpolate<bitFormat>(src + 2 * x, srcPitch);
@@ -71,17 +75,87 @@ void createThumbnail_4(const uint8 *src, uint32 srcPitch, uint8 *dstPtr, uint32
}
}
-static void createThumbnail(const uint8 *src, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, int width, int height) {
- // only 1/2 and 1/4 downscale supported
- if (width != 320 && width != 640)
- return;
+static void scaleThumbnail(Graphics::Surface &in, Graphics::Surface &out) {
+ while (in.w / out.w >= 4 || in.h / out.h >= 4) {
+ createThumbnail_4<565>((const uint8 *)in.getPixels(), in.pitch, (uint8 *)in.getPixels(), in.pitch, in.w, in.h);
+ in.w /= 4;
+ in.h /= 4;
+ }
+
+ while (in.w / out.w >= 2 || in.h / out.h >= 2) {
+ createThumbnail_2<565>((const uint8 *)in.getPixels(), in.pitch, (uint8 *)in.getPixels(), in.pitch, in.w, in.h);
+ in.w /= 2;
+ in.h /= 2;
+ }
+
+ if ((in.w == out.w && in.h < out.h) || (in.w < out.w && in.h == out.h)) {
+ // In this case we simply center the input surface in the output
+ uint8 *dst = (uint8 *)out.getBasePtr((out.w - in.w) / 2, (out.h - in.h) / 2);
+ const uint8 *src = (const uint8 *)in.getPixels();
- int downScaleMode = (width == 320) ? 2 : 4;
+ for (int y = 0; y < in.h; ++y) {
+ memcpy(dst, src, in.w * in.format.bytesPerPixel);
+ src += in.pitch;
+ dst += out.pitch;
+ }
+ } else {
+ // Assure the aspect of the scaled image still matches the original.
+ int targetWidth = out.w, targetHeight = out.h;
- if (downScaleMode == 2) {
- createThumbnail_2<565>(src, srcPitch, dstPtr, dstPitch, width, height);
- } else if (downScaleMode == 4) {
- createThumbnail_4<565>(src, srcPitch, dstPtr, dstPitch, width, height);
+ const float inputAspect = (float)in.w / in.h;
+ const float outputAspect = (float)out.w / out.h;
+
+ if (inputAspect > outputAspect) {
+ targetHeight = int(targetWidth / inputAspect);
+ } else if (inputAspect < outputAspect) {
+ targetWidth = int(targetHeight * inputAspect);
+ }
+
+ // Make sure we are still in the bounds of the output
+ assert(targetWidth <= out.w);
+ assert(targetHeight <= out.h);
+
+ // Center the image on the output surface
+ byte *dst = (byte *)out.getBasePtr((out.w - targetWidth) / 2, (out.h - targetHeight) / 2);
+ const uint dstLineIncrease = out.pitch - targetWidth * out.format.bytesPerPixel;
+
+ const float scaleFactorX = (float)targetWidth / in.w;
+ const float scaleFactorY = (float)targetHeight / in.h;
+
+ for (int y = 0; y < targetHeight; ++y) {
+ const float yFrac = (y / scaleFactorY);
+ const int y1 = (int)yFrac;
+ const int y2 = (y1 + 1 < in.h) ? (y1 + 1) : (in.h - 1);
+
+ for (int x = 0; x < targetWidth; ++x) {
+ const float xFrac = (x / scaleFactorX);
+ const int x1 = (int)xFrac;
+ const int x2 = (x1 + 1 < in.w) ? (x1 + 1) : (in.w - 1);
+
+ // Look up colors at the points
+ uint8 p1R, p1G, p1B;
+ Graphics::colorToRGB<Graphics::ColorMasks<565> >(READ_UINT16(in.getBasePtr(x1, y1)), p1R, p1G, p1B);
+ uint8 p2R, p2G, p2B;
+ Graphics::colorToRGB<Graphics::ColorMasks<565> >(READ_UINT16(in.getBasePtr(x2, y1)), p2R, p2G, p2B);
+ uint8 p3R, p3G, p3B;
+ Graphics::colorToRGB<Graphics::ColorMasks<565> >(READ_UINT16(in.getBasePtr(x1, y2)), p3R, p3G, p3B);
+ uint8 p4R, p4G, p4B;
+ Graphics::colorToRGB<Graphics::ColorMasks<565> >(READ_UINT16(in.getBasePtr(x2, y2)), p4R, p4G, p4B);
+
+ const float xDiff = xFrac - x1;
+ const float yDiff = yFrac - y1;
+
+ uint8 pR = (uint8)((1 - yDiff) * ((1 - xDiff) * p1R + xDiff * p2R) + yDiff * ((1 - xDiff) * p3R + xDiff * p4R));
+ uint8 pG = (uint8)((1 - yDiff) * ((1 - xDiff) * p1G + xDiff * p2G) + yDiff * ((1 - xDiff) * p3G + xDiff * p4G));
+ uint8 pB = (uint8)((1 - yDiff) * ((1 - xDiff) * p1B + xDiff * p2B) + yDiff * ((1 - xDiff) * p3B + xDiff * p4B));
+
+ WRITE_UINT16(dst, Graphics::RGBToColor<Graphics::ColorMasks<565> >(pR, pG, pB));
+ dst += 2;
+ }
+
+ // Move to the next line
+ dst = (byte *)dst + dstLineIncrease;
+ }
}
}
@@ -90,7 +164,7 @@ static void createThumbnail(const uint8 *src, uint32 srcPitch, uint8 *dstPtr, ui
* Copies the current screen contents to a new surface, using RGB565 format.
* WARNING: surf->free() must be called by the user to avoid leaking.
*
- * @param surf the surface to store the data in it
+ * @param surf the surface to store the data in it
*/
static bool grabScreen565(Graphics::Surface *surf) {
Graphics::Surface *screen = g_system->lockScreen();
@@ -98,7 +172,7 @@ static bool grabScreen565(Graphics::Surface *surf) {
return false;
assert(screen->format.bytesPerPixel == 1 || screen->format.bytesPerPixel == 2);
- assert(screen->pixels != 0);
+ assert(screen->getPixels() != 0);
Graphics::PixelFormat screenFormat = g_system->getScreenFormat();
@@ -116,15 +190,16 @@ static bool grabScreen565(Graphics::Surface *surf) {
byte r = 0, g = 0, b = 0;
if (screenFormat.bytesPerPixel == 1) {
- r = palette[((uint8 *)screen->pixels)[y * screen->pitch + x] * 3];
- g = palette[((uint8 *)screen->pixels)[y * screen->pitch + x] * 3 + 1];
- b = palette[((uint8 *)screen->pixels)[y * screen->pitch + x] * 3 + 2];
+ uint8 pixel = *(uint8 *)screen->getBasePtr(x, y);
+ r = palette[pixel * 3 + 0];
+ g = palette[pixel * 3 + 1];
+ b = palette[pixel * 3 + 2];
} else if (screenFormat.bytesPerPixel == 2) {
uint16 col = READ_UINT16(screen->getBasePtr(x, y));
screenFormat.colorToRGB(col, r, g, b);
}
- ((uint16 *)surf->pixels)[y * surf->w + x] = Graphics::RGBToColor<Graphics::ColorMasks<565> >(r, g, b);
+ *((uint16 *)surf->getBasePtr(x, y)) = Graphics::RGBToColor<Graphics::ColorMasks<565> >(r, g, b);
}
}
@@ -135,78 +210,20 @@ static bool grabScreen565(Graphics::Surface *surf) {
}
static bool createThumbnail(Graphics::Surface &out, Graphics::Surface &in) {
- uint16 width = in.w;
- uint16 inHeight = in.h;
-
- if (width < 320) {
- // Special case to handle MM NES (uses a screen width of 256)
- width = 320;
-
- // center MM NES screen
- Graphics::Surface newscreen;
- newscreen.create(width, in.h, in.format);
-
- uint8 *dst = (uint8 *)newscreen.getBasePtr((320 - in.w) / 2, 0);
- const uint8 *src = (const uint8 *)in.getBasePtr(0, 0);
- uint16 height = in.h;
-
- while (height--) {
- memcpy(dst, src, in.pitch);
- dst += newscreen.pitch;
- src += in.pitch;
- }
-
- in.free();
- in = newscreen;
- } else if (width == 720) {
- // Special case to handle Hercules mode
- //
- // NOTE: This code is pretty SCUMM specific.
- // For other games this code might cut off
- // not only the menu, but also other graphics.
- width = 640;
- inHeight = 400;
-
- // cut off menu and so on..
- Graphics::Surface newscreen;
- newscreen.create(width, 400, in.format);
-
- uint8 *dst = (uint8 *)newscreen.getBasePtr(0, (400 - 240) / 2);
- const uint8 *src = (const uint8 *)in.getBasePtr(41, 28);
-
- for (int y = 0; y < 240; ++y) {
- memcpy(dst, src, 640 * in.format.bytesPerPixel);
- dst += newscreen.pitch;
- src += in.pitch;
- }
-
- in.free();
- in = newscreen;
- } else if (width == 640 && inHeight == 440) {
- // Special case to handle KQ6 Windows: resize the screen to 640x480,
- // adding a black band in the bottom.
- inHeight = 480;
-
- Graphics::Surface newscreen;
- newscreen.create(width, 480, in.format);
-
- memcpy(newscreen.getBasePtr(0, 0), in.getBasePtr(0, 0), width * 440 * in.format.bytesPerPixel);
-
- in.free();
- in = newscreen;
+ int height;
+ if ((in.w == 320 && in.h == 200) || (in.w == 640 && in.h == 400)) {
+ height = kThumbnailHeight1;
+ } else {
+ height = kThumbnailHeight2;
}
- uint16 newHeight = !(inHeight % 240) ? kThumbnailHeight2 : kThumbnailHeight1;
-
- out.create(kThumbnailWidth, newHeight, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
- createThumbnail((const uint8 *)in.pixels, width * sizeof(uint16), (uint8 *)out.pixels, out.pitch, width, inHeight);
-
+ out.create(kThumbnailWidth, height, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
+ scaleThumbnail(in, out);
in.free();
-
return true;
}
-bool createThumbnailFromScreen(Graphics::Surface* surf) {
+bool createThumbnailFromScreen(Graphics::Surface *surf) {
assert(surf);
Graphics::Surface screen;
@@ -230,9 +247,37 @@ bool createThumbnail(Graphics::Surface *surf, const uint8 *pixels, int w, int h,
g = palette[pixels[y * w + x] * 3 + 1];
b = palette[pixels[y * w + x] * 3 + 2];
- ((uint16 *)screen.pixels)[y * screen.w + x] = Graphics::RGBToColor<Graphics::ColorMasks<565> >(r, g, b);
+ *((uint16 *)screen.getBasePtr(x, y)) = Graphics::RGBToColor<Graphics::ColorMasks<565> >(r, g, b);
}
}
return createThumbnail(*surf, screen);
}
+
+// this is somewhat awkward, but createScreenShot should logically be in graphics,
+// but moving other functions in this file into that namespace breaks several engines
+namespace Graphics {
+bool createScreenShot(Graphics::Surface &surf) {
+ Graphics::PixelFormat screenFormat = g_system->getScreenFormat();
+ //convert surface to 2 bytes pixel format to avoid problems with palette saving and loading
+ if ((screenFormat.bytesPerPixel == 1) || (screenFormat.bytesPerPixel == 2)) {
+ return grabScreen565(&surf);
+ } else {
+ Graphics::Surface *screen = g_system->lockScreen();
+ if (!screen) {
+ return false;
+ }
+ surf.create(screen->w, screen->h, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
+ for (uint y = 0; y < screen->h; ++y) {
+ for (uint x = 0; x < screen->w; ++x) {
+ byte r = 0, g = 0, b = 0, a = 0;
+ uint32 col = READ_UINT32(screen->getBasePtr(x, y));
+ screenFormat.colorToARGB(col, a, r, g, b);
+ *((uint32 *)surf.getBasePtr(x, y)) = Graphics::ARGBToColor<Graphics::ColorMasks<8888> >(a, r, g, b);
+ }
+ }
+ g_system->unlockScreen();
+ return true;
+ }
+}
+} // End of namespace Graphics
diff --git a/graphics/surface.cpp b/graphics/surface.cpp
index 41ae8dcebb..777c1058fb 100644
--- a/graphics/surface.cpp
+++ b/graphics/surface.cpp
@@ -82,9 +82,79 @@ void Surface::free() {
format = PixelFormat();
}
+void Surface::init(uint16 width, uint16 height, uint16 newPitch, void *newPixels, const PixelFormat &f) {
+ w = width;
+ h = height;
+ pitch = newPitch;
+ pixels = newPixels;
+ format = f;
+}
+
void Surface::copyFrom(const Surface &surf) {
create(surf.w, surf.h, surf.format);
- memcpy(pixels, surf.pixels, h * pitch);
+ if (surf.pitch == pitch) {
+ memcpy(pixels, surf.pixels, h * pitch);
+ } else {
+ const byte *src = (const byte *)surf.pixels;
+ byte *dst = (byte *)pixels;
+ for (int y = h; y > 0; --y) {
+ memcpy(dst, src, w * format.bytesPerPixel);
+ src += surf.pitch;
+ dst += pitch;
+ }
+ }
+}
+
+Surface Surface::getSubArea(const Common::Rect &area) {
+ Common::Rect effectiveArea(area);
+ effectiveArea.clip(w, h);
+
+ Surface subSurface;
+ subSurface.w = effectiveArea.width();
+ subSurface.h = effectiveArea.height();
+ subSurface.pitch = pitch;
+ subSurface.pixels = getBasePtr(area.left, area.top);
+ subSurface.format = format;
+ return subSurface;
+}
+
+const Surface Surface::getSubArea(const Common::Rect &area) const {
+ Common::Rect effectiveArea(area);
+ effectiveArea.clip(w, h);
+
+ Surface subSurface;
+ subSurface.w = effectiveArea.width();
+ subSurface.h = effectiveArea.height();
+ subSurface.pitch = pitch;
+ // We need to cast the const away here because a Surface always has a
+ // pointer to modifiable pixel data.
+ subSurface.pixels = const_cast<void *>(getBasePtr(area.left, area.top));
+ subSurface.format = format;
+ return subSurface;
+}
+
+void Surface::copyRectToSurface(const void *buffer, int srcPitch, int destX, int destY, int width, int height) {
+ assert(buffer);
+
+ assert(destX >= 0 && destX < w);
+ assert(destY >= 0 && destY < h);
+ assert(height > 0 && destY + height <= h);
+ assert(width > 0 && destX + width <= w);
+
+ // Copy buffer data to internal buffer
+ const byte *src = (const byte *)buffer;
+ byte *dst = (byte *)getBasePtr(destX, destY);
+ for (int i = 0; i < height; i++) {
+ memcpy(dst, src, width * format.bytesPerPixel);
+ src += srcPitch;
+ dst += pitch;
+ }
+}
+
+void Surface::copyRectToSurface(const Graphics::Surface &srcSurface, int destX, int destY, const Common::Rect subRect) {
+ assert(srcSurface.format == format);
+
+ copyRectToSurface(srcSurface.getBasePtr(subRect.left, subRect.top), srcSurface.pitch, destX, destY, subRect.width(), subRect.height());
}
void Surface::hLine(int x, int y, int x2, uint32 color) {
diff --git a/graphics/surface.h b/graphics/surface.h
index 6c9e464657..f1b2aa64ab 100644
--- a/graphics/surface.h
+++ b/graphics/surface.h
@@ -61,11 +61,13 @@ struct Surface {
*/
uint16 pitch;
+protected:
/**
* The surface's pixel data.
*/
void *pixels;
+public:
/**
* The pixel format of the surface.
*/
@@ -78,6 +80,33 @@ struct Surface {
}
/**
+ * Return a pointer to the pixel data.
+ *
+ * @return Pointer to the pixel data.
+ */
+ inline const void *getPixels() const {
+ return pixels;
+ }
+
+ /**
+ * Return a pointer to the pixel data.
+ *
+ * @return Pointer to the pixel data.
+ */
+ inline void *getPixels() {
+ return pixels;
+ }
+
+ /**
+ * Sets the pixel data.
+ *
+ * Note that this is a simply a setter. Be careful what you are doing!
+ *
+ * @param newPixels The new pixel data.
+ */
+ void setPixels(void *newPixels) { pixels = newPixels; }
+
+ /**
* Return a pointer to the pixel at the specified point.
*
* @param x The x coordinate of the pixel.
@@ -122,6 +151,20 @@ struct Surface {
void free();
/**
+ * Set up the Surface with user specified data.
+ *
+ * Note that this simply sets the 'internal' attributes of the Surface. It
+ * will not take care of freeing old data via free or similar!
+ *
+ * @param width Width of the pixel data.
+ * @param height Height of the pixel data.
+ * @param pitch The pitch of the pixel data.
+ * @param pixels The pixel data itself.
+ * @param format The pixel format of the pixel data.
+ */
+ void init(uint16 width, uint16 height, uint16 pitch, void *pixels, const PixelFormat &format);
+
+ /**
* Copy the data from another Surface.
*
* Note that this calls free on the current surface, to assure it being
@@ -135,10 +178,66 @@ struct Surface {
void copyFrom(const Surface &surf);
/**
+ * Creates a Surface which represents a sub-area of this Surface object.
+ *
+ * The pixel (0, 0) of the returned Surface will be the same as Pixel
+ * (area.x, area.y) of this Surface. Changes to any of the Surface objects
+ * will change the shared pixel data.
+ *
+ * Note that the Surface returned is only valid as long as this Surface
+ * object is still alive (i.e. its pixel data is not destroyed or
+ * reallocated). Do *never* try to free the returned Surface.
+ *
+ * @param area The area which should be represented. Note that the area
+ * will get clipped in case it does not fit!
+ */
+ Surface getSubArea(const Common::Rect &area);
+
+ /**
+ * Creates a Surface which represents a sub-area of this Surface object.
+ *
+ * The pixel (0, 0) of the returned Surface will be the same as Pixel
+ * (area.x, area.y) of this Surface.
+ *
+ * Note that the Surface returned is only valid as long as this Surface
+ * object is still alive (i.e. its pixel data is not destroyed or
+ * reallocated). Do *never* try to free the returned Surface.
+ *
+ * @param area The area which should be represented. Note that the area
+ * will get clipped in case it does not fit!
+ */
+ const Surface getSubArea(const Common::Rect &area) const;
+
+ /**
+ * Copies a bitmap to the Surface internal buffer. The pixel format
+ * of buffer must match the pixel format of the Surface.
+ *
+ * @param buffer The buffer containing the graphics data source
+ * @param pitch The pitch of the buffer (number of bytes in a scanline)
+ * @param destX The x coordinate of the destination rectangle
+ * @param destY The y coordinate of the destination rectangle
+ * @param width The width of the destination rectangle
+ * @param height The height of the destination rectangle
+ */
+ void copyRectToSurface(const void *buffer, int srcPitch, int destX, int destY, int width, int height);
+ /**
+ * Copies a bitmap to the Surface internal buffer. The pixel format
+ * of buffer must match the pixel format of the Surface.
+ *
+ * @param srcSurface The source of the bitmap data
+ * @param destX The x coordinate of the destination rectangle
+ * @param destY The y coordinate of the destination rectangle
+ * @param subRect The subRect of surface to be blitted
+ * @return
+ */
+ void copyRectToSurface(const Graphics::Surface &srcSurface, int destX, int destY, const Common::Rect subRect);
+
+ /**
* Convert the data to another pixel format.
*
* This works in-place. This means it will not create an additional buffer
- * for the conversion process. The value of pixels might change though.
+ * for the conversion process. The value of 'pixels' might change though
+ * (that means it might realloc the pixel data).
*
* Note that you should only use this, when you created the Surface data via
* create! Otherwise this function has undefined behavior.
@@ -235,7 +334,9 @@ struct Surface {
*/
struct SharedPtrSurfaceDeleter {
void operator()(Surface *ptr) {
- ptr->free();
+ if (ptr) {
+ ptr->free();
+ }
delete ptr;
}
};
diff --git a/graphics/thumbnail.cpp b/graphics/thumbnail.cpp
index ddb377306d..e3f368dffa 100644
--- a/graphics/thumbnail.cpp
+++ b/graphics/thumbnail.cpp
@@ -23,6 +23,7 @@
#include "graphics/scaler.h"
#include "graphics/colormasks.h"
#include "common/endian.h"
+#include "common/algorithm.h"
#include "common/system.h"
#include "common/stream.h"
#include "common/textconsole.h"
@@ -42,7 +43,16 @@ struct ThumbnailHeader {
#define ThumbnailHeaderSize (4+4+1+2+2+(1+4+4))
-bool loadHeader(Common::SeekableReadStream &in, ThumbnailHeader &header, bool outputWarnings) {
+enum HeaderState {
+ /// There is no header present
+ kHeaderNone,
+ /// The header present only has reliable values for version and size
+ kHeaderUnsupported,
+ /// The header is present and the version is supported
+ kHeaderPresent
+};
+
+HeaderState loadHeader(Common::SeekableReadStream &in, ThumbnailHeader &header, bool outputWarnings) {
header.type = in.readUint32BE();
// We also accept the bad 'BMHT' header here, for the sake of compatibility
// with some older savegames which were written incorrectly due to a bug in
@@ -50,16 +60,28 @@ bool loadHeader(Common::SeekableReadStream &in, ThumbnailHeader &header, bool ou
if (header.type != MKTAG('T','H','M','B') && header.type != MKTAG('B','M','H','T')) {
if (outputWarnings)
warning("couldn't find thumbnail header type");
- return false;
+ return kHeaderNone;
}
header.size = in.readUint32BE();
header.version = in.readByte();
+ // Do a check whether any read errors had occured. If so we cannot use the
+ // values obtained for size and version because they might be bad.
+ if (in.err() || in.eos()) {
+ // TODO: We fake that there is no header. This is actually not quite
+ // correct since we found the start of the header and then things
+ // started to break. Right no we leave detection of this to the client.
+ // Since this case is caused by broken files, the client code should
+ // catch it anyway... If there is a nicer solution here, we should
+ // implement it.
+ return kHeaderNone;
+ }
+
if (header.version > THMB_VERSION) {
if (outputWarnings)
warning("trying to load a newer thumbnail version: %d instead of %d", header.version, THMB_VERSION);
- return false;
+ return kHeaderUnsupported;
}
header.width = in.readUint16BE();
@@ -81,7 +103,15 @@ bool loadHeader(Common::SeekableReadStream &in, ThumbnailHeader &header, bool ou
header.format = createPixelFormat<565>();
}
- return true;
+ if (in.err() || in.eos()) {
+ // When we reached this point we know that at least the size and
+ // version field was loaded successfully, thus we tell this header
+ // is not supported and silently hope that the client code is
+ // prepared to handle read errors.
+ return kHeaderUnsupported;
+ } else {
+ return kHeaderPresent;
+ }
}
} // end of anonymous namespace
@@ -89,7 +119,12 @@ bool checkThumbnailHeader(Common::SeekableReadStream &in) {
uint32 position = in.pos();
ThumbnailHeader header;
- bool hasHeader = loadHeader(in, header, false);
+ // TODO: It is not clear whether this is the best semantics. Now
+ // checkThumbnailHeader will return true even when the thumbnail header
+ // found is actually not usable. However, most engines seem to use this
+ // to detect the presence of any header and if there is none it wont even
+ // try to skip it. Thus, this looks like the best solution for now...
+ bool hasHeader = (loadHeader(in, header, false) != kHeaderNone);
in.seek(position, SEEK_SET);
@@ -100,7 +135,9 @@ bool skipThumbnail(Common::SeekableReadStream &in) {
uint32 position = in.pos();
ThumbnailHeader header;
- if (!loadHeader(in, header, false)) {
+ // We can skip unsupported and supported headers. So we only seek back
+ // to the old position in case there is no header at all.
+ if (loadHeader(in, header, false) == kHeaderNone) {
in.seek(position, SEEK_SET);
return false;
}
@@ -110,10 +147,23 @@ bool skipThumbnail(Common::SeekableReadStream &in) {
}
Graphics::Surface *loadThumbnail(Common::SeekableReadStream &in) {
+ const uint32 position = in.pos();
ThumbnailHeader header;
-
- if (!loadHeader(in, header, true))
+ HeaderState headerState = loadHeader(in, header, true);
+
+ // Try to handle unsupported/broken headers gracefully. If there is no
+ // header at all, we seek back and return at this point. If there is an
+ // unsupported/broken header, we skip the actual data and return. The
+ // downside is that we might reset the end of stream flag with this and
+ // the client code would not be able to notice a read past the end of the
+ // stream at this point then.
+ if (headerState == kHeaderNone) {
+ in.seek(position, SEEK_SET);
+ return 0;
+ } else if (headerState == kHeaderUnsupported) {
+ in.seek(header.size - (in.pos() - position), SEEK_CUR);
return 0;
+ }
if (header.format.bytesPerPixel != 2 && header.format.bytesPerPixel != 4) {
warning("trying to load thumbnail with unsupported bit depth %d", header.format.bytesPerPixel);
@@ -143,7 +193,6 @@ Graphics::Surface *loadThumbnail(Common::SeekableReadStream &in) {
assert(0);
}
}
-
return to;
}
@@ -216,4 +265,55 @@ bool saveThumbnail(Common::WriteStream &out, const Graphics::Surface &thumb) {
return true;
}
+
+/**
+ * Returns an array indicating which pixels of a source image horizontally or vertically get
+ * included in a scaled image
+ */
+int *scaleLine(int size, int srcSize) {
+ int scale = 100 * size / srcSize;
+ assert(scale > 0);
+ int *v = new int[size];
+ Common::fill(v, &v[size], 0);
+
+ int distCtr = 0;
+ int *destP = v;
+ for (int distIndex = 0; distIndex < srcSize; ++distIndex) {
+ distCtr += scale;
+ while (distCtr >= 100) {
+ assert(destP < &v[size]);
+ *destP++ = distIndex;
+ distCtr -= 100;
+ }
+ }
+
+ return v;
+}
+
+Graphics::Surface *scale(const Graphics::Surface &srcImage, int xSize, int ySize) {
+ Graphics::Surface *s = new Graphics::Surface();
+ s->create(xSize, ySize, srcImage.format);
+
+ int *horizUsage = scaleLine(xSize, srcImage.w);
+ int *vertUsage = scaleLine(ySize, srcImage.h);
+
+ // Loop to create scaled version
+ for (int yp = 0; yp < ySize; ++yp) {
+ const byte *srcP = (const byte *)srcImage.getBasePtr(0, vertUsage[yp]);
+ byte *destP = (byte *)s->getBasePtr(0, yp);
+
+ for (int xp = 0; xp < xSize; ++xp) {
+ const byte *tempSrcP = srcP + (horizUsage[xp] * srcImage.format.bytesPerPixel);
+ for (int byteCtr = 0; byteCtr < srcImage.format.bytesPerPixel; ++byteCtr) {
+ *destP++ = *tempSrcP++;
+ }
+ }
+ }
+
+ // Delete arrays and return surface
+ delete[] horizUsage;
+ delete[] vertUsage;
+ return s;
+}
+
} // End of namespace Graphics
diff --git a/graphics/thumbnail.h b/graphics/thumbnail.h
index 45a0fdbf07..c857809c91 100644
--- a/graphics/thumbnail.h
+++ b/graphics/thumbnail.h
@@ -64,6 +64,24 @@ bool saveThumbnail(Common::WriteStream &out);
*/
bool saveThumbnail(Common::WriteStream &out, const Graphics::Surface &thumb);
+/**
+ * Grabs framebuffer into surface
+ *
+ * @param surf a surface
+ * @return false if a error occurred
+ */
+bool createScreenShot(Graphics::Surface &surf);
+
+/**
+ * Scales a passed surface, creating a new surface with the result
+ * @param srcImage Source image to scale
+ * @param xSize New surface width
+ * @param ySize New surface height
+ * @remarks Caller is responsible for freeing the returned surface
+ */
+Graphics::Surface *scale(const Graphics::Surface &srcImage, int xSize, int ySize);
+
+
} // End of namespace Graphics
#endif
diff --git a/graphics/yuv_to_rgb.cpp b/graphics/yuv_to_rgb.cpp
index 6043315a13..2a485fa664 100644
--- a/graphics/yuv_to_rgb.cpp
+++ b/graphics/yuv_to_rgb.cpp
@@ -229,7 +229,7 @@ void convertYUV444ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup
void YUVToRGBManager::convert444(Graphics::Surface *dst, YUVToRGBManager::LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
// Sanity checks
- assert(dst && dst->pixels);
+ assert(dst && dst->getPixels());
assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
assert(ySrc && uSrc && vSrc);
@@ -237,9 +237,9 @@ void YUVToRGBManager::convert444(Graphics::Surface *dst, YUVToRGBManager::Lumina
// Use a templated function to avoid an if check on every pixel
if (dst->format.bytesPerPixel == 2)
- convertYUV444ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+ convertYUV444ToRGB<uint16>((byte *)dst->getPixels(), dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
else
- convertYUV444ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+ convertYUV444ToRGB<uint32>((byte *)dst->getPixels(), dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
}
template<typename PixelInt>
@@ -283,7 +283,7 @@ void convertYUV420ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup
void YUVToRGBManager::convert420(Graphics::Surface *dst, YUVToRGBManager::LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
// Sanity checks
- assert(dst && dst->pixels);
+ assert(dst && dst->getPixels());
assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
assert(ySrc && uSrc && vSrc);
assert((yWidth & 1) == 0);
@@ -293,9 +293,9 @@ void YUVToRGBManager::convert420(Graphics::Surface *dst, YUVToRGBManager::Lumina
// Use a templated function to avoid an if check on every pixel
if (dst->format.bytesPerPixel == 2)
- convertYUV420ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+ convertYUV420ToRGB<uint16>((byte *)dst->getPixels(), dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
else
- convertYUV420ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+ convertYUV420ToRGB<uint32>((byte *)dst->getPixels(), dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
}
#define READ_QUAD(ptr, prefix) \
@@ -368,7 +368,7 @@ void convertYUV410ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup
void YUVToRGBManager::convert410(Graphics::Surface *dst, YUVToRGBManager::LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
// Sanity checks
- assert(dst && dst->pixels);
+ assert(dst && dst->getPixels());
assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
assert(ySrc && uSrc && vSrc);
assert((yWidth & 3) == 0);
@@ -378,9 +378,9 @@ void YUVToRGBManager::convert410(Graphics::Surface *dst, YUVToRGBManager::Lumina
// Use a templated function to avoid an if check on every pixel
if (dst->format.bytesPerPixel == 2)
- convertYUV410ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+ convertYUV410ToRGB<uint16>((byte *)dst->getPixels(), dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
else
- convertYUV410ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+ convertYUV410ToRGB<uint32>((byte *)dst->getPixels(), dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
}
} // End of namespace Graphics
diff --git a/graphics/yuv_to_rgb.h b/graphics/yuv_to_rgb.h
index f785422c5a..a1e61ec705 100644
--- a/graphics/yuv_to_rgb.h
+++ b/graphics/yuv_to_rgb.h
@@ -22,10 +22,17 @@
/**
* @file
- * YUV to RGB conversion used in engines:
- * - mohawk
- * - scumm (he)
- * - sword25
+ * YUV to RGB conversion.
+ *
+ * Used in graphics:
+ * - JPEGDecoder
+ *
+ * Used in video:
+ * - BinkDecoder
+ * - Indeo3Decoder
+ * - PSXStreamDecoder
+ * - TheoraDecoder
+ * - SVQ1Decoder
*/
#ifndef GRAPHICS_YUV_TO_RGB_H