aboutsummaryrefslogtreecommitdiff
path: root/graphics
diff options
context:
space:
mode:
Diffstat (limited to 'graphics')
-rw-r--r--graphics/VectorRenderer.cpp29
-rw-r--r--graphics/VectorRenderer.h82
-rw-r--r--graphics/VectorRendererSpec.cpp1825
-rw-r--r--graphics/VectorRendererSpec.h84
-rw-r--r--graphics/font.cpp23
-rw-r--r--graphics/font.h4
-rw-r--r--graphics/fonts/ttf.cpp150
-rw-r--r--graphics/fonts/ttf.h24
-rw-r--r--graphics/macgui/macmenu.cpp583
-rw-r--r--graphics/macgui/macmenu.h123
-rw-r--r--graphics/macgui/macwindow.cpp496
-rw-r--r--graphics/macgui/macwindow.h350
-rw-r--r--graphics/macgui/macwindowborder.cpp117
-rw-r--r--graphics/macgui/macwindowborder.h149
-rw-r--r--graphics/macgui/macwindowmanager.cpp478
-rw-r--r--graphics/macgui/macwindowmanager.h226
-rw-r--r--graphics/managed_surface.cpp260
-rw-r--r--graphics/managed_surface.h376
-rw-r--r--graphics/module.mk8
-rw-r--r--graphics/nine_patch.cpp359
-rw-r--r--graphics/nine_patch.h114
-rw-r--r--graphics/pixelformat.cpp64
-rw-r--r--graphics/pixelformat.h3
-rw-r--r--graphics/primitives.cpp347
-rw-r--r--graphics/primitives.h11
-rw-r--r--graphics/scaler/thumbnail_intern.cpp6
-rw-r--r--graphics/screen.cpp129
-rw-r--r--graphics/screen.h118
-rw-r--r--graphics/surface.cpp105
-rw-r--r--graphics/surface.h68
-rw-r--r--graphics/thumbnail.cpp2
-rw-r--r--graphics/transparent_surface.cpp219
-rw-r--r--graphics/transparent_surface.h56
33 files changed, 6915 insertions, 73 deletions
diff --git a/graphics/VectorRenderer.cpp b/graphics/VectorRenderer.cpp
index f426dd8c41..73dc6309b2 100644
--- a/graphics/VectorRenderer.cpp
+++ b/graphics/VectorRenderer.cpp
@@ -55,7 +55,34 @@ void VectorRenderer::drawStep(const Common::Rect &area, const DrawStep &step, ui
_dynamicData = extra;
- (this->*(step.drawingCall))(area, step);
+ Common::Rect noClip = Common::Rect(0, 0, 0, 0);
+ (this->*(step.drawingCall))(area, step, noClip);
+}
+
+void VectorRenderer::drawStepClip(const Common::Rect &area, const Common::Rect &clip, const DrawStep &step, uint32 extra) {
+
+ if (step.bgColor.set)
+ setBgColor(step.bgColor.r, step.bgColor.g, step.bgColor.b);
+
+ if (step.fgColor.set)
+ setFgColor(step.fgColor.r, step.fgColor.g, step.fgColor.b);
+
+ if (step.bevelColor.set)
+ setBevelColor(step.bevelColor.r, step.bevelColor.g, step.bevelColor.b);
+
+ if (step.gradColor1.set && step.gradColor2.set)
+ setGradientColors(step.gradColor1.r, step.gradColor1.g, step.gradColor1.b,
+ step.gradColor2.r, step.gradColor2.g, step.gradColor2.b);
+
+ setShadowOffset(_disableShadows ? 0 : step.shadow);
+ setBevel(step.bevel);
+ setGradientFactor(step.factor);
+ setStrokeWidth(step.stroke);
+ setFillMode((FillMode)step.fillMode);
+
+ _dynamicData = extra;
+
+ (this->*(step.drawingCall))(area, step, clip);
}
int VectorRenderer::stepGetRadius(const DrawStep &step, const Common::Rect &area) {
diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h
index 6b657f758d..5f7b6e60d3 100644
--- a/graphics/VectorRenderer.h
+++ b/graphics/VectorRenderer.h
@@ -28,6 +28,7 @@
#include "common/str.h"
#include "graphics/surface.h"
+#include "graphics/transparent_surface.h"
#include "gui/ThemeEngine.h"
@@ -38,7 +39,7 @@ class VectorRenderer;
struct DrawStep;
-typedef void (VectorRenderer::*DrawingFunctionCallback)(const Common::Rect &, const Graphics::DrawStep &);
+typedef void (VectorRenderer::*DrawingFunctionCallback)(const Common::Rect &, const Graphics::DrawStep &, const Common::Rect &);
struct DrawStep {
@@ -79,8 +80,11 @@ struct DrawStep {
uint32 scale; /**< scale of all the coordinates in FIXED POINT with 16 bits mantissa */
+ GUI::ThemeEngine::AutoScaleMode autoscale; /**< scale alphaimage if present */
+
DrawingFunctionCallback drawingCall; /**< Pointer to drawing function */
Graphics::Surface *blitSrc;
+ Graphics::TransparentSurface *blitAlphaSrc;
};
VectorRenderer *createRenderer(int mode);
@@ -142,6 +146,7 @@ public:
* @param y2 Vertical (Y) coordinate for the line end
*/
virtual void drawLine(int x1, int y1, int x2, int y2) = 0;
+ virtual void drawLineClip(int x1, int y1, int x2, int y2, Common::Rect clipping) = 0;
/**
* Draws a circle centered at (x,y) with radius r.
@@ -151,6 +156,7 @@ public:
* @param r Radius of the circle.
*/
virtual void drawCircle(int x, int y, int r) = 0;
+ virtual void drawCircleClip(int x, int y, int r, Common::Rect clipping) = 0;
/**
* Draws a square starting at (x,y) with the given width and height.
@@ -161,6 +167,7 @@ public:
* @param h Height of the square
*/
virtual void drawSquare(int x, int y, int w, int h) = 0;
+ virtual void drawSquareClip(int x, int y, int w, int h, Common::Rect clipping) = 0;
/**
* Draws a rounded square starting at (x,y) with the given width and height.
@@ -173,6 +180,7 @@ public:
* @param r Radius of the corners.
*/
virtual void drawRoundedSquare(int x, int y, int r, int w, int h) = 0;
+ virtual void drawRoundedSquareClip(int x, int y, int r, int w, int h, Common::Rect clipping) = 0;
/**
* Draws a triangle starting at (x,y) with the given base and height.
@@ -186,6 +194,7 @@ public:
* @param orient Orientation of the triangle.
*/
virtual void drawTriangle(int x, int y, int base, int height, TriangleOrientation orient) = 0;
+ virtual void drawTriangleClip(int x, int y, int base, int height, TriangleOrientation orient, Common::Rect clipping) = 0;
/**
* Draws a beveled square like the ones in the Classic GUI themes.
@@ -199,6 +208,7 @@ public:
* @param bevel Amount of bevel. Must be positive.
*/
virtual void drawBeveledSquare(int x, int y, int w, int h, int bevel) = 0;
+ virtual void drawBeveledSquareClip(int x, int y, int w, int h, int bevel, Common::Rect clipping) = 0;
/**
* Draws a tab-like shape, specially thought for the Tab widget.
@@ -212,6 +222,7 @@ public:
* @param r Radius of the corners of the tab (0 for squared tabs).
*/
virtual void drawTab(int x, int y, int r, int w, int h) = 0;
+ virtual void drawTabClip(int x, int y, int r, int w, int h, Common::Rect clipping) = 0;
/**
@@ -222,6 +233,11 @@ public:
drawLine(x + w, y, x, y + h);
}
+ virtual void drawCrossClip(int x, int y, int w, int h, Common::Rect clipping) {
+ drawLineClip(x, y, x + w, y + w, clipping);
+ drawLineClip(x + w, y, x, y + h, clipping);
+ }
+
/**
* Set the active foreground painting color for the renderer.
* All the foreground drawing from then on will be done with that color, unless
@@ -269,7 +285,7 @@ public:
*
* @param surface Pointer to a Surface object.
*/
- virtual void setSurface(Surface *surface) {
+ virtual void setSurface(TransparentSurface *surface) {
_activeSurface = surface;
}
@@ -278,6 +294,7 @@ public:
* Defaults to using the active Foreground color for filling.
*/
virtual void fillSurface() = 0;
+ virtual void fillSurfaceClip(Common::Rect clipping) = 0;
/**
* Clears the active surface.
@@ -355,68 +372,74 @@ public:
/**
* DrawStep callback functions for each drawing feature
*/
- void drawCallback_CIRCLE(const Common::Rect &area, const DrawStep &step) {
+ void drawCallback_CIRCLE(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
uint16 x, y, w, h, radius;
radius = stepGetRadius(step, area);
stepGetPositions(step, area, x, y, w, h);
- drawCircle(x + radius, y + radius, radius);
+ drawCircleClip(x + radius, y + radius, radius, clip);
}
- void drawCallback_SQUARE(const Common::Rect &area, const DrawStep &step) {
+ void drawCallback_SQUARE(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
uint16 x, y, w, h;
stepGetPositions(step, area, x, y, w, h);
- drawSquare(x, y, w, h);
+ drawSquareClip(x, y, w, h, clip);
}
- void drawCallback_LINE(const Common::Rect &area, const DrawStep &step) {
+ void drawCallback_LINE(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
uint16 x, y, w, h;
stepGetPositions(step, area, x, y, w, h);
- drawLine(x, y, x + w, y + w);
+ drawLineClip(x, y, x + w, y + w, clip);
}
- void drawCallback_ROUNDSQ(const Common::Rect &area, const DrawStep &step) {
+ void drawCallback_ROUNDSQ(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
uint16 x, y, w, h;
stepGetPositions(step, area, x, y, w, h);
- drawRoundedSquare(x, y, stepGetRadius(step, area), w, h);
+ drawRoundedSquareClip(x, y, stepGetRadius(step, area), w, h, clip);
}
- void drawCallback_FILLSURFACE(const Common::Rect &area, const DrawStep &step) {
- fillSurface();
+ void drawCallback_FILLSURFACE(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
+ fillSurfaceClip(clip);
}
- void drawCallback_TRIANGLE(const Common::Rect &area, const DrawStep &step) {
+ void drawCallback_TRIANGLE(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
uint16 x, y, w, h;
stepGetPositions(step, area, x, y, w, h);
- drawTriangle(x, y, w, h, (TriangleOrientation)step.extraData);
+ drawTriangleClip(x, y, w, h, (TriangleOrientation)step.extraData, clip);
}
- void drawCallback_BEVELSQ(const Common::Rect &area, const DrawStep &step) {
+ void drawCallback_BEVELSQ(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
uint16 x, y, w, h;
stepGetPositions(step, area, x, y, w, h);
- drawBeveledSquare(x, y, w, h, _bevel);
+ drawBeveledSquareClip(x, y, w, h, _bevel, clip);
}
- void drawCallback_TAB(const Common::Rect &area, const DrawStep &step) {
+ void drawCallback_TAB(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
uint16 x, y, w, h;
stepGetPositions(step, area, x, y, w, h);
- drawTab(x, y, stepGetRadius(step, area), w, h);
+ drawTabClip(x, y, stepGetRadius(step, area), w, h, clip);
}
- void drawCallback_BITMAP(const Common::Rect &area, const DrawStep &step) {
+ void drawCallback_BITMAP(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
uint16 x, y, w, h;
stepGetPositions(step, area, x, y, w, h);
- blitAlphaBitmap(step.blitSrc, Common::Rect(x, y, x + w, y + h));
+ blitKeyBitmapClip(step.blitSrc, Common::Rect(x, y, x + w, y + h), clip);
}
- void drawCallback_CROSS(const Common::Rect &area, const DrawStep &step) {
+ void drawCallback_ALPHABITMAP(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
uint16 x, y, w, h;
stepGetPositions(step, area, x, y, w, h);
- drawCross(x, y, w, h);
+ blitAlphaBitmap(step.blitAlphaSrc, Common::Rect(x, y, x + w, y + h), step.autoscale, step.xAlign, step.yAlign); //TODO
}
- void drawCallback_VOID(const Common::Rect &area, const DrawStep &step) {}
+ void drawCallback_CROSS(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
+ uint16 x, y, w, h;
+ stepGetPositions(step, area, x, y, w, h);
+ drawCrossClip(x, y, w, h, clip);
+ }
+
+ void drawCallback_VOID(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {}
/**
* Draws the specified draw step on the screen.
@@ -426,6 +449,7 @@ public:
* @param step Pointer to a DrawStep struct.
*/
virtual void drawStep(const Common::Rect &area, const DrawStep &step, uint32 extra = 0);
+ virtual void drawStepClip(const Common::Rect &area, const Common::Rect &clip, const DrawStep &step, uint32 extra = 0);
/**
* Copies the part of the current frame to the system overlay.
@@ -466,8 +490,16 @@ public:
* blitted into the active surface, at the position specified by "r".
*/
virtual void blitSubSurface(const Graphics::Surface *source, const Common::Rect &r) = 0;
+ virtual void blitSubSurfaceClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) = 0;
+
+ virtual void blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r) = 0;
+ virtual void blitKeyBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) = 0;
- virtual void blitAlphaBitmap(const Graphics::Surface *source, const Common::Rect &r) = 0;
+ virtual void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r,
+ GUI::ThemeEngine::AutoScaleMode autoscale = GUI::ThemeEngine::kAutoScaleNone,
+ Graphics::DrawStep::VectorAlignment xAlign = Graphics::DrawStep::kVectorAlignManual,
+ Graphics::DrawStep::VectorAlignment yAlign = Graphics::DrawStep::kVectorAlignManual,
+ int alpha = 255) = 0;
/**
* Draws a string into the screen. Wrapper for the Graphics::Font string drawing
@@ -491,7 +523,7 @@ public:
virtual void applyScreenShading(GUI::ThemeEngine::ShadingStyle) = 0;
protected:
- Surface *_activeSurface; /**< Pointer to the surface currently being drawn */
+ TransparentSurface *_activeSurface; /**< Pointer to the surface currently being drawn */
FillMode _fillMode; /**< Defines in which way (if any) are filled the drawn shapes */
ShadowFillMode _shadowFillMode;
diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp
index 81a0c04441..9aed3301fa 100644
--- a/graphics/VectorRendererSpec.cpp
+++ b/graphics/VectorRendererSpec.cpp
@@ -25,6 +25,8 @@
#include "common/frac.h"
#include "graphics/surface.h"
+#include "graphics/transparent_surface.h"
+#include "graphics/nine_patch.h"
#include "graphics/colormasks.h"
#include "gui/ThemeEngine.h"
@@ -108,6 +110,33 @@ inline frac_t fp_sqroot(uint32 x) {
BE_DRAWCIRCLE_BOTTOM(ptr3,ptr4,x,y,px,py); \
} while (0)
+#define BE_DRAWCIRCLE_TOP_CLIP(ptr1,ptr2,x,y,px,py,realX1,realY1,realX2,realY2) do { \
+ if (IS_IN_CLIP((realX1) + (y), (realY1) - (x))) \
+ *(ptr1 + (y) - (px)) = color; \
+ if (IS_IN_CLIP((realX1) + (x), (realY1) - (y))) \
+ *(ptr1 + (x) - (py)) = color; \
+ if (IS_IN_CLIP((realX2) - (x), (realY2) - (y))) \
+ *(ptr2 - (x) - (py)) = color; \
+ if (IS_IN_CLIP((realX2) - (y), (realY2) - (x))) \
+ *(ptr2 - (y) - (px)) = color; \
+} while (0)
+
+#define BE_DRAWCIRCLE_BOTTOM_CLIP(ptr3,ptr4,x,y,px,py,realX3,realY3,realX4,realY4) do { \
+ if (IS_IN_CLIP((realX3) - (y), (realY3) + (x))) \
+ *(ptr3 - (y) + (px)) = color; \
+ if (IS_IN_CLIP((realX3) - (x), (realY3) + (y))) \
+ *(ptr3 - (x) + (py)) = color; \
+ if (IS_IN_CLIP((realX4) + (x), (realY4) + (y))) \
+ *(ptr4 + (x) + (py)) = color; \
+ if (IS_IN_CLIP((realX4) + (y), (realY4) + (x))) \
+ *(ptr4 + (y) + (px)) = color; \
+} while (0)
+
+#define BE_DRAWCIRCLE_CLIP(ptr1,ptr2,ptr3,ptr4,x,y,px,py,realX1,realY1,realX2,realY2,realX3,realY3,realX4,realY4) do { \
+ BE_DRAWCIRCLE_TOP_CLIP(ptr1,ptr2,x,y,px,py,realX1,realY1,realX2,realY2); \
+ BE_DRAWCIRCLE_BOTTOM_CLIP(ptr3,ptr4,x,y,px,py,realX3,realY3,realX4,realY4); \
+} while (0)
+
#define BE_DRAWCIRCLE_BCOLOR(ptr1,ptr2,ptr3,ptr4,x,y,px,py) do { \
*(ptr1 + (y) - (px)) = color1; \
*(ptr1 + (x) - (py)) = color1; \
@@ -119,6 +148,25 @@ inline frac_t fp_sqroot(uint32 x) {
*(ptr4 + (y) + (px)) = color2; \
} while (0)
+#define BE_DRAWCIRCLE_BCOLOR_CLIP(ptr1,ptr2,ptr3,ptr4,x,y,px,py,realX1,realY1,realX2,realY2,realX3,realY3,realX4,realY4) do { \
+ if (IS_IN_CLIP((realX1) + (y), (realY1) - (x))) \
+ *(ptr1 + (y) - (px)) = color1; \
+ if (IS_IN_CLIP((realX1) + (x), (realY1) - (y))) \
+ *(ptr1 + (x) - (py)) = color1; \
+ if (IS_IN_CLIP((realX2) - (x), (realY2) - (y))) \
+ *(ptr2 - (x) - (py)) = color1; \
+ if (IS_IN_CLIP((realX2) - (y), (realY2) - (x))) \
+ *(ptr2 - (y) - (px)) = color1; \
+ if (IS_IN_CLIP((realX3) - (y), (realY3) + (x))) \
+ *(ptr3 - (y) + (px)) = color1; \
+ if (IS_IN_CLIP((realX3) - (x), (realY3) + (y))) \
+ *(ptr3 - (x) + (py)) = color1; \
+ if (IS_IN_CLIP((realX4) + (x), (realY4) + (y))) \
+ *(ptr4 + (x) + (py)) = color2; \
+ if (IS_IN_CLIP((realX4) + (y), (realY4) + (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)
@@ -127,6 +175,16 @@ inline frac_t fp_sqroot(uint32 x) {
this->blendPixelPtr(ptr + (x) - (py), color, a); \
} while (0)
+#define BE_DRAWCIRCLE_BCOLOR_TR_CW_CLIP(ptr,x,y,px,py,a,realX,realY) do { \
+ if (IS_IN_CLIP((realX) + (y), (realY) - (x))) \
+ this->blendPixelPtr(ptr + (y) - (px), color, a); \
+} while (0)
+
+#define BE_DRAWCIRCLE_BCOLOR_TR_CCW_CLIP(ptr,x,y,px,py,a,realX,realY) do { \
+ if (IS_IN_CLIP((realX) + (x), (realY) - (y))) \
+ 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)
@@ -135,6 +193,16 @@ inline frac_t fp_sqroot(uint32 x) {
this->blendPixelPtr(ptr - (y) - (px), color, a); \
} while (0)
+#define BE_DRAWCIRCLE_BCOLOR_TL_CW_CLIP(ptr,x,y,px,py,a,realX,realY) do { \
+ if (IS_IN_CLIP((realX) - (x), (realY) - (y))) \
+ this->blendPixelPtr(ptr - (x) - (py), color, a); \
+} while (0)
+
+#define BE_DRAWCIRCLE_BCOLOR_TL_CCW_CLIP(ptr,x,y,px,py,a,realX,realY) do { \
+ if (IS_IN_CLIP((realX) - (y), (realY) - (x))) \
+ 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)
@@ -143,6 +211,16 @@ inline frac_t fp_sqroot(uint32 x) {
this->blendPixelPtr(ptr - (x) + (py), color, a); \
} while (0)
+#define BE_DRAWCIRCLE_BCOLOR_BL_CW_CLIP(ptr,x,y,px,py,a,realX,realY) do { \
+ if (IS_IN_CLIP((realX) - (y), (realY) + (x))) \
+ this->blendPixelPtr(ptr - (y) + (px), color, a); \
+} while (0)
+
+#define BE_DRAWCIRCLE_BCOLOR_BL_CCW_CLIP(ptr,x,y,px,py,a,realX,realY) do { \
+ if (IS_IN_CLIP((realX) - (x), (realY) + (y))) \
+ 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)
@@ -151,6 +229,16 @@ inline frac_t fp_sqroot(uint32 x) {
this->blendPixelPtr(ptr + (y) + (px), color, a); \
} while (0)
+#define BE_DRAWCIRCLE_BCOLOR_BR_CW_CLIP(ptr,x,y,px,py,a,realX,realY) do { \
+ if (IS_IN_CLIP((realX) + (x), (realY) + (y))) \
+ this->blendPixelPtr(ptr + (x) + (py), color, a); \
+} while (0)
+
+#define BE_DRAWCIRCLE_BCOLOR_BR_CCW_CLIP(ptr,x,y,px,py,a,realX,realY) do { \
+ if (IS_IN_CLIP((realX) + (y), (realY) + (x))) \
+ 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; \
@@ -170,6 +258,42 @@ inline frac_t fp_sqroot(uint32 x) {
BE_DRAWCIRCLE_XCOLOR_BOTTOM(ptr3,ptr4,x,y,px,py); \
} while (0)
+#define IS_IN_CLIP(x,y) (_clippingArea.left <= (x) && (x) < _clippingArea.right \
+ && _clippingArea.top <= (y) && (y) < _clippingArea.bottom)
+
+#define BE_DRAWCIRCLE_XCOLOR_TOP_CLIP(ptr1,ptr2,x,y,px,py,realX1,realY1,realX2,realY2) do { \
+ if (IS_IN_CLIP((realX1) + (y), (realY1) - (x))) \
+ *(ptr1 + (y) - (px)) = color1; \
+\
+ if (IS_IN_CLIP((realX1) + (x), (realY1) - (y))) \
+ *(ptr1 + (x) - (py)) = color2; \
+\
+ if (IS_IN_CLIP((realX2) - (x), (realY2) - (y))) \
+ *(ptr2 - (x) - (py)) = color2; \
+\
+ if (IS_IN_CLIP((realX2) - (y), (realY2) - (x))) \
+ *(ptr2 - (y) - (px)) = color1; \
+} while (0)
+
+#define BE_DRAWCIRCLE_XCOLOR_BOTTOM_CLIP(ptr3,ptr4,x,y,px,py,realX3,realY3,realX4,realY4) do { \
+ if (IS_IN_CLIP((realX3) - (y), (realY3) + (x))) \
+ *(ptr3 - (y) + (px)) = color3; \
+\
+ if (IS_IN_CLIP((realX3) - (x), (realY3) + (y))) \
+ *(ptr3 - (x) + (py)) = color4; \
+\
+ if (IS_IN_CLIP((realX4) + (x), (realY4) + (y))) \
+ *(ptr4 + (x) + (py)) = color4; \
+\
+ if (IS_IN_CLIP((realX4) + (y), (realY4) + (x))) \
+ *(ptr4 + (y) + (px)) = color3; \
+} while (0)
+
+#define BE_DRAWCIRCLE_XCOLOR_CLIP(ptr1,ptr2,ptr3,ptr4,x,y,px,py,realX1,realY1,realX2,realY2,realX3,realY3,realX4,realY4) do { \
+ BE_DRAWCIRCLE_XCOLOR_TOP_CLIP(ptr1,ptr2,x,y,px,py,realX1,realY1,realX2,realY2); \
+ BE_DRAWCIRCLE_XCOLOR_BOTTOM_CLIP(ptr3,ptr4,x,y,px,py,realX3,realY3,realX4,realY4); \
+} while (0)
+
#define BE_RESET() do { \
f = 1 - r; \
@@ -337,6 +461,45 @@ void colorFill(PixelType *first, PixelType *last, PixelType color) {
}
}
+template<typename PixelType>
+void colorFillClip(PixelType *first, PixelType *last, PixelType color, int realX, int realY, Common::Rect &clippingArea) {
+ if (realY < clippingArea.top || realY >= clippingArea.bottom)
+ return;
+
+ register int count = (last - first);
+
+ if (realX > clippingArea.right || realX + count < clippingArea.left)
+ return;
+
+ if (realX < clippingArea.left) {
+ register int diff = (clippingArea.left - realX);
+ realX += diff;
+ count -= diff;
+ }
+
+ if (clippingArea.right <= realX + count) {
+ register int diff = (realX + count - clippingArea.right);
+ count -= diff;
+ }
+
+ if (!count)
+ return;
+
+ register int n = (count + 7) >> 3;
+ switch (count % 8) {
+ case 0: do {
+ *first++ = color;
+ case 7: *first++ = color;
+ case 6: *first++ = color;
+ case 5: *first++ = color;
+ case 4: *first++ = color;
+ case 3: *first++ = color;
+ case 2: *first++ = color;
+ case 1: *first++ = color;
+ } while (--n > 0);
+ }
+}
+
VectorRenderer *createRenderer(int mode) {
#ifdef DISABLE_FANCY_THEMES
@@ -376,6 +539,7 @@ VectorRendererSpec(PixelFormat format) :
_alphaMask((0xFF >> format.aLoss) << format.aShift) {
_bitmapAlphaColor = _format.RGBToColor(255, 0, 255);
+ _clippingArea = Common::Rect(0, 0, 32767, 32767);
}
/****************************
@@ -441,13 +605,56 @@ template<typename PixelType>
void VectorRendererSpec<PixelType>::
gradientFill(PixelType *ptr, int width, int x, int y) {
bool ox = ((y & 1) == 1);
- int stripSize;
int curGrad = 0;
while (_gradIndexes[curGrad + 1] <= y)
curGrad++;
- stripSize = _gradIndexes[curGrad + 1] - _gradIndexes[curGrad];
+ // precalcGradient assures that _gradIndexes entries always differ in
+ // their value. This assures stripSize is always different from zero.
+ int stripSize = _gradIndexes[curGrad + 1] - _gradIndexes[curGrad];
+
+ int grad = (((y - _gradIndexes[curGrad]) % stripSize) << 2) / stripSize;
+
+ // Dithering:
+ // +--+ +--+ +--+ +--+
+ // | | | | | *| | *|
+ // | | | *| |* | |**|
+ // +--+ +--+ +--+ +--+
+ // 0 1 2 3
+ if (grad == 0 ||
+ _gradCache[curGrad] == _gradCache[curGrad + 1] || // no color change
+ stripSize < 2) { // the stip is small
+ colorFill<PixelType>(ptr, ptr + width, _gradCache[curGrad]);
+ } else if (grad == 3 && ox) {
+ colorFill<PixelType>(ptr, ptr + width, _gradCache[curGrad + 1]);
+ } else {
+ for (int j = x; j < x + width; j++, ptr++) {
+ bool oy = ((j & 1) == 1);
+
+ if ((ox && oy) ||
+ ((grad == 2 || grad == 3) && ox && !oy) ||
+ (grad == 3 && oy))
+ *ptr = _gradCache[curGrad + 1];
+ else
+ *ptr = _gradCache[curGrad];
+ }
+ }
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+gradientFillClip(PixelType *ptr, int width, int x, int y, int realX, int realY) {
+ if (realY < _clippingArea.top || realY >= _clippingArea.bottom) return;
+ bool ox = ((y & 1) == 1);
+ int curGrad = 0;
+
+ while (_gradIndexes[curGrad + 1] <= y)
+ curGrad++;
+
+ // precalcGradient assures that _gradIndexes entries always differ in
+ // their value. This assures stripSize is always different from zero.
+ int stripSize = _gradIndexes[curGrad + 1] - _gradIndexes[curGrad];
int grad = (((y - _gradIndexes[curGrad]) % stripSize) << 2) / stripSize;
@@ -465,6 +672,7 @@ gradientFill(PixelType *ptr, int width, int x, int y) {
colorFill<PixelType>(ptr, ptr + width, _gradCache[curGrad + 1]);
} else {
for (int j = x; j < x + width; j++, ptr++) {
+ if (realX + j - x < _clippingArea.left || realX + j - x >= _clippingArea.right) continue;
bool oy = ((j & 1) == 1);
if ((ox && oy) ||
@@ -502,6 +710,44 @@ fillSurface() {
template<typename PixelType>
void VectorRendererSpec<PixelType>::
+fillSurfaceClip(Common::Rect clipping) {
+ int w = _activeSurface->w;
+ int h = _activeSurface->h;
+ if (clipping.isEmpty() || (clipping.left == 0 && clipping.top == 0 && clipping.right == w && clipping.bottom == h)) {
+ fillSurface();
+ return;
+ }
+
+ byte *ptr = (byte *)_activeSurface->getPixels();
+ int pitch = _activeSurface->pitch;
+
+ if (Base::_fillMode == kFillBackground || Base::_fillMode == kFillForeground) {
+ PixelType color = (Base::_fillMode == kFillBackground ? _bgColor : _fgColor);
+ byte *ptrLeft = (ptr + _clippingArea.left), *ptrRight = ptr + _clippingArea.right;
+ for (int i = 0; i < h; i++) {
+ if (_clippingArea.top <= i && i < _clippingArea.bottom) {
+ colorFill<PixelType>((PixelType *)ptrLeft, (PixelType *)ptrRight, color);
+ }
+
+ ptrLeft += pitch;
+ ptrRight += pitch;
+ }
+
+ } else if (Base::_fillMode == kFillGradient) {
+ precalcGradient(h);
+
+ for (int i = 0; i < h; i++) {
+ if (_clippingArea.top <= i && i < _clippingArea.bottom) {
+ gradientFill((PixelType *)ptr + _clippingArea.left, _clippingArea.width(), 0, i);
+ }
+
+ ptr += pitch;
+ }
+ }
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
copyFrame(OSystem *sys, const Common::Rect &r) {
sys->copyRectToOverlay(
@@ -553,7 +799,59 @@ blitSubSurface(const Graphics::Surface *source, const Common::Rect &r) {
template<typename PixelType>
void VectorRendererSpec<PixelType>::
-blitAlphaBitmap(const Graphics::Surface *source, const Common::Rect &r) {
+blitSubSurfaceClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) {
+ if (clipping.isEmpty() || clipping.contains(r)) {
+ blitSubSurface(source, r);
+ return;
+ }
+
+ int16 x = r.left;
+ int16 y = r.top;
+
+ if (r.width() > source->w)
+ x = x + (r.width() >> 1) - (source->w >> 1);
+
+ if (r.height() > source->h)
+ y = y + (r.height() >> 1) - (source->h >> 1);
+
+ int w = source->w, h = source->h;
+ int usedW = w, usedH = h;
+ int offsetX = 0, offsetY = 0;
+
+ if (x > clipping.right || x + w < clipping.left) return;
+ if (y > clipping.bottom || y + h < clipping.top) return;
+ if (x < clipping.left) {
+ offsetX = clipping.left - x;
+ usedW -= offsetX;
+ x = clipping.left;
+ }
+ if (y < clipping.top) {
+ offsetY = clipping.top - y;
+ usedH -= offsetY;
+ y = clipping.top;
+ }
+ if (usedW > clipping.width()) usedW = clipping.width();
+ if (usedH > clipping.height()) usedH = clipping.height();
+
+ byte *dst_ptr = (byte *)_activeSurface->getBasePtr(x, y);
+ const byte *src_ptr = (const byte *)source->getBasePtr(offsetX, offsetY);
+
+ const int dst_pitch = _activeSurface->pitch;
+ const int src_pitch = source->pitch;
+
+ int lines = usedH;
+ const int sz = usedW * sizeof(PixelType);
+
+ while (lines--) {
+ memcpy(dst_ptr, src_ptr, sz);
+ dst_ptr += dst_pitch;
+ src_ptr += src_pitch;
+ }
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r) {
int16 x = r.left;
int16 y = r.top;
@@ -589,6 +887,99 @@ blitAlphaBitmap(const Graphics::Surface *source, const Common::Rect &r) {
template<typename PixelType>
void VectorRendererSpec<PixelType>::
+blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, GUI::ThemeEngine::AutoScaleMode autoscale,
+ Graphics::DrawStep::VectorAlignment xAlign, Graphics::DrawStep::VectorAlignment yAlign, int alpha) {
+ if (autoscale == GUI::ThemeEngine::kAutoScaleStretch) {
+ source->blit(*_activeSurface, r.left, r.top, Graphics::FLIP_NONE,
+ nullptr, TS_ARGB(alpha, 255, 255, 255),
+ r.width(), r.height());
+ } else if (autoscale == GUI::ThemeEngine::kAutoScaleFit) {
+ double ratio = (double)r.width() / source->w;
+ double ratio2 = (double)r.height() / source->h;
+
+ if (ratio2 < ratio)
+ ratio = ratio2;
+
+ int offx = 0, offy = 0;
+ if (xAlign == Graphics::DrawStep::kVectorAlignCenter)
+ offx = (r.width() - (int)(source->w * ratio)) >> 1;
+
+ if (yAlign == Graphics::DrawStep::kVectorAlignCenter)
+ offy = (r.height() - (int)(source->h * ratio)) >> 1;
+
+ source->blit(*_activeSurface, r.left + offx, r.top + offy, Graphics::FLIP_NONE,
+ nullptr, TS_ARGB(alpha, 255, 255, 255),
+ (int)(source->w * ratio), (int)(source->h * ratio));
+
+ } else if (autoscale == GUI::ThemeEngine::kAutoScaleNinePatch) {
+ Graphics::NinePatchBitmap nine(source, false);
+ nine.blit(*_activeSurface, r.left, r.top, r.width(), r.height());
+ } else {
+ source->blit(*_activeSurface, r.left, r.top);
+ }
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+blitKeyBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) {
+ if (clipping.isEmpty() || clipping.contains(r)) {
+ blitKeyBitmap(source, r);
+ return;
+ }
+
+ int16 x = r.left;
+ int16 y = r.top;
+
+ if (r.width() > source->w)
+ x = x + (r.width() >> 1) - (source->w >> 1);
+
+ if (r.height() > source->h)
+ y = y + (r.height() >> 1) - (source->h >> 1);
+
+ int w = source->w, h = source->h;
+ int usedW = w, usedH = h;
+ int offsetX = 0, offsetY = 0;
+
+ if (x > clipping.right || x + w < clipping.left) return;
+ if (y > clipping.bottom || y + h < clipping.top) return;
+ if (x < clipping.left) {
+ offsetX = clipping.left - x;
+ usedW -= offsetX;
+ x = clipping.left;
+ }
+ if (y < clipping.top) {
+ offsetY = clipping.top - y;
+ usedH -= offsetY;
+ y = clipping.top;
+ }
+ if (usedW > clipping.width()) usedW = clipping.width();
+ if (usedH > clipping.height()) usedH = clipping.height();
+
+ PixelType *dst_ptr = (PixelType *)_activeSurface->getBasePtr(x, y);
+ const PixelType *src_ptr = (const PixelType *)source->getBasePtr(offsetX, offsetY);
+
+ int dst_pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int src_pitch = source->pitch / source->format.bytesPerPixel;
+
+ h = usedH;
+ while (h--) {
+ w = usedW;
+
+ while (w--) {
+ if (*src_ptr != _bitmapAlphaColor)
+ *dst_ptr = *src_ptr;
+
+ dst_ptr++;
+ src_ptr++;
+ }
+
+ dst_ptr = dst_ptr - usedW + dst_pitch;
+ src_ptr = src_ptr - usedW + src_pitch;
+ }
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle) {
int pixels = _activeSurface->w * _activeSurface->h;
PixelType *ptr = (PixelType *)_activeSurface->getPixels();
@@ -666,6 +1057,13 @@ blendPixelPtr(PixelType *ptr, PixelType color, uint8 alpha) {
template<typename PixelType>
inline void VectorRendererSpec<PixelType>::
+blendPixelPtrClip(PixelType *ptr, PixelType color, uint8 alpha, int x, int y) {
+ if (IS_IN_CLIP(x, y))
+ blendPixelPtr(ptr, color, alpha);
+}
+
+template<typename PixelType>
+inline void VectorRendererSpec<PixelType>::
blendPixelDestAlphaPtr(PixelType *ptr, PixelType color, uint8 alpha) {
int idst = *ptr;
// This function is only used for corner pixels in rounded rectangles, so
@@ -709,6 +1107,36 @@ darkenFill(PixelType *ptr, PixelType *end) {
}
}
+template<typename PixelType>
+inline void VectorRendererSpec<PixelType>::
+darkenFillClip(PixelType *ptr, PixelType *end, int x, int y) {
+ PixelType mask = (PixelType)((3 << _format.rShift) | (3 << _format.gShift) | (3 << _format.bShift));
+
+ if (!g_system->hasFeature(OSystem::kFeatureOverlaySupportsAlpha)) {
+ // !kFeatureOverlaySupportsAlpha (but might have alpha bits)
+
+ while (ptr != end) {
+ if (IS_IN_CLIP(x, y)) *ptr = ((*ptr & ~mask) >> 2) | _alphaMask;
+ ++ptr;
+ ++x;
+ }
+ } else {
+ // kFeatureOverlaySupportsAlpha
+ // assuming at least 3 alpha bits
+
+ mask |= 3 << _format.aShift;
+ PixelType addA = (PixelType)(3 << (_format.aShift + 6 - _format.aLoss));
+
+ while (ptr != end) {
+ // Darken the color, and increase the alpha
+ // (0% -> 75%, 100% -> 100%)
+ if (IS_IN_CLIP(x, y)) *ptr = (PixelType)(((*ptr & ~mask) >> 2) + addA);
+ ++ptr;
+ ++x;
+ }
+ }
+}
+
/********************************************************************
********************************************************************
* Primitive shapes drawing - Public API calls - VectorRendererSpec *
@@ -773,8 +1201,8 @@ drawLine(int x1, int y1, int x2, int y2) {
SWAP(y1, y2);
}
- int dx = ABS(x2 - x1);
- int dy = ABS(y2 - y1);
+ uint dx = ABS(x2 - x1);
+ uint dy = ABS(y2 - y1);
// this is a point, not a line. stoopid.
if (dy == 0 && dx == 0)
@@ -803,7 +1231,7 @@ drawLine(int x1, int y1, int x2, int y2) {
ptr += pitch;
}
- } else if (ABS(dx) == ABS(dy)) { // diagonal lines
+ } else if (dx == dy) { // diagonal lines
// these ones also use a fixed pitch increase
pitch += (x2 > x1) ? 1 : -1;
@@ -817,6 +1245,77 @@ drawLine(int x1, int y1, int x2, int y2) {
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawLineClip(int x1, int y1, int x2, int y2, Common::Rect clipping) {
+ x1 = CLIP(x1, 0, (int)Base::_activeSurface->w);
+ x2 = CLIP(x2, 0, (int)Base::_activeSurface->w);
+ y1 = CLIP(y1, 0, (int)Base::_activeSurface->h);
+ y2 = CLIP(y2, 0, (int)Base::_activeSurface->h);
+
+ // we draw from top to bottom
+ if (y2 < y1) {
+ SWAP(x1, x2);
+ SWAP(y1, y2);
+ }
+
+ uint dx = ABS(x2 - x1);
+ uint dy = ABS(y2 - y1);
+
+ // this is a point, not a line. stoopid.
+ if (dy == 0 && dx == 0)
+ return;
+
+ if (Base::_strokeWidth == 0)
+ return;
+
+ PixelType *ptr = (PixelType *)_activeSurface->getBasePtr(x1, y1);
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int st = Base::_strokeWidth >> 1;
+
+ Common::Rect backup = _clippingArea;
+ _clippingArea = clipping;
+ bool needsClipping = !_clippingArea.isEmpty() && (!_clippingArea.contains(x1, y1) || !_clippingArea.contains(x2, y2));
+ if (!needsClipping) {
+ drawLine(x1, y1, x2, y2);
+ _clippingArea = backup;
+ return;
+ }
+
+ int ptr_x = x1, ptr_y = y1;
+
+ if (dy == 0) { // horizontal lines
+ colorFillClip<PixelType>(ptr, ptr + dx + 1, (PixelType)_fgColor, x1, y1, _clippingArea);
+
+ for (int i = 0, p = pitch; i < st; ++i, p += pitch) {
+ colorFillClip<PixelType>(ptr + p, ptr + dx + 1 + p, (PixelType)_fgColor, x1, y1 + p/pitch, _clippingArea);
+ colorFillClip<PixelType>(ptr - p, ptr + dx + 1 - p, (PixelType)_fgColor, x1, y1 - p/pitch, _clippingArea);
+ }
+
+ } else if (dx == 0) { // vertical lines
+ // these ones use a static pitch increase.
+ while (y1++ <= y2) {
+ colorFillClip<PixelType>(ptr - st, ptr + st, (PixelType)_fgColor, x1 - st, ptr_y, _clippingArea);
+ ptr += pitch;
+ ++ptr_y;
+ }
+
+ } else if (dx == dy) { // diagonal lines
+ // these ones also use a fixed pitch increase
+ pitch += (x2 > x1) ? 1 : -1;
+
+ while (dy--) {
+ colorFillClip<PixelType>(ptr - st, ptr + st, (PixelType)_fgColor, ptr_x - st, ptr_y, _clippingArea);
+ ptr += pitch;
+ ++ptr_y;
+ if (x2 > x1) ++ptr_x; else --ptr_x;
+ }
+
+ } else { // generic lines, use the standard algorithm...
+ drawLineAlgClip(x1, y1, x2, y2, dx, dy, (PixelType)_fgColor);
+ }
+}
+
/** CIRCLES **/
template<typename PixelType>
void VectorRendererSpec<PixelType>::
@@ -856,6 +1355,70 @@ drawCircle(int x, int y, int r) {
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawCircleClip(int x, int y, int r, Common::Rect clipping) {
+ if (x + r > Base::_activeSurface->w || y + r > Base::_activeSurface->h ||
+ x - r < 0 || y - r < 0 || x == 0 || y == 0 || r <= 0)
+ return;
+
+ Common::Rect backup = _clippingArea;
+ _clippingArea = clipping;
+ bool useClippingVersions = !(_clippingArea.isEmpty() || _clippingArea.contains(Common::Rect(x - r, y - r, x + r, y + r)));
+
+ if (Base::_fillMode != kFillDisabled && Base::_shadowOffset
+ && x + r + Base::_shadowOffset < Base::_activeSurface->w
+ && y + r + Base::_shadowOffset < Base::_activeSurface->h) {
+ if (useClippingVersions)
+ drawCircleAlgClip(x + Base::_shadowOffset + 1, y + Base::_shadowOffset + 1, r, 0, kFillForeground);
+ else
+ drawCircleAlg(x + Base::_shadowOffset + 1, y + Base::_shadowOffset + 1, r, 0, kFillForeground);
+ }
+
+ switch (Base::_fillMode) {
+ case kFillDisabled:
+ if (Base::_strokeWidth) {
+ if (useClippingVersions)
+ drawCircleAlgClip(x, y, r, _fgColor, kFillDisabled);
+ else
+ drawCircleAlg(x, y, r, _fgColor, kFillDisabled);
+ }
+ break;
+
+ case kFillForeground:
+ if (useClippingVersions)
+ drawCircleAlgClip(x, y, r, _fgColor, kFillForeground);
+ else
+ drawCircleAlg(x, y, r, _fgColor, kFillForeground);
+ break;
+
+ case kFillBackground:
+ if (Base::_strokeWidth > 1) {
+ if (useClippingVersions) {
+ drawCircleAlgClip(x, y, r, _fgColor, kFillForeground);
+ drawCircleAlgClip(x, y, r - Base::_strokeWidth, _bgColor, kFillBackground);
+ } else {
+ drawCircleAlg(x, y, r, _fgColor, kFillForeground);
+ drawCircleAlg(x, y, r - Base::_strokeWidth, _bgColor, kFillBackground);
+ }
+ } else {
+ if (useClippingVersions) {
+ drawCircleAlgClip(x, y, r, _bgColor, kFillBackground);
+ drawCircleAlgClip(x, y, r, _fgColor, kFillDisabled);
+ } else {
+ drawCircleAlg(x, y, r, _bgColor, kFillBackground);
+ drawCircleAlg(x, y, r, _fgColor, kFillDisabled);
+ }
+ }
+ break;
+
+ case kFillGradient:
+ break;
+ }
+
+ _clippingArea = backup;
+}
+
/** SQUARES **/
template<typename PixelType>
void VectorRendererSpec<PixelType>::
@@ -893,6 +1456,67 @@ drawSquare(int x, int y, int w, int h) {
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawSquareClip(int x, int y, int w, int h, Common::Rect clipping) {
+ if (x + w > Base::_activeSurface->w || y + h > Base::_activeSurface->h ||
+ w <= 0 || h <= 0 || x < 0 || y < 0)
+ return;
+
+ Common::Rect backup = _clippingArea;
+ _clippingArea = clipping;
+ bool useClippingVersions = !(_clippingArea.isEmpty() || _clippingArea.contains(Common::Rect(x, y, x + w, y + h)));
+
+ if (Base::_fillMode != kFillDisabled && Base::_shadowOffset
+ && x + w + Base::_shadowOffset < Base::_activeSurface->w
+ && y + h + Base::_shadowOffset < Base::_activeSurface->h) {
+ if (useClippingVersions)
+ drawSquareShadowClip(x, y, w, h, Base::_shadowOffset);
+ else
+ drawSquareShadow(x, y, w, h, Base::_shadowOffset);
+ }
+
+ switch (Base::_fillMode) {
+ case kFillDisabled:
+ if (Base::_strokeWidth) {
+ if (useClippingVersions)
+ drawSquareAlgClip(x, y, w, h, _fgColor, kFillDisabled);
+ else
+ drawSquareAlg(x, y, w, h, _fgColor, kFillDisabled);
+ }
+ break;
+
+ case kFillForeground:
+ if (useClippingVersions)
+ drawSquareAlgClip(x, y, w, h, _fgColor, kFillForeground);
+ else
+ drawSquareAlg(x, y, w, h, _fgColor, kFillForeground);
+ break;
+
+ case kFillBackground:
+ if (useClippingVersions) {
+ drawSquareAlgClip(x, y, w, h, _bgColor, kFillBackground);
+ drawSquareAlgClip(x, y, w, h, _fgColor, kFillDisabled);
+ } else {
+ drawSquareAlg(x, y, w, h, _bgColor, kFillBackground);
+ drawSquareAlg(x, y, w, h, _fgColor, kFillDisabled);
+ }
+ break;
+
+ case kFillGradient:
+ VectorRendererSpec::drawSquareAlg(x, y, w, h, 0, kFillGradient);
+ if (Base::_strokeWidth) {
+ if (useClippingVersions)
+ drawSquareAlgClip(x, y, w, h, _fgColor, kFillDisabled);
+ else
+ drawSquareAlg(x, y, w, h, _fgColor, kFillDisabled);
+ }
+ break;
+ }
+
+ _clippingArea = backup;
+}
+
/** ROUNDED SQUARES **/
template<typename PixelType>
void VectorRendererSpec<PixelType>::
@@ -919,6 +1543,43 @@ drawRoundedSquare(int x, int y, int r, int w, int h) {
template<typename PixelType>
void VectorRendererSpec<PixelType>::
+drawRoundedSquareClip(int x, int y, int r, int w, int h, Common::Rect clipping) {
+ if (x + w > Base::_activeSurface->w || y + h > Base::_activeSurface->h ||
+ w <= 0 || h <= 0 || x < 0 || y < 0 || r <= 0)
+ return;
+
+ if ((r * 2) > w || (r * 2) > h)
+ r = MIN(w / 2, h / 2);
+
+ if (r <= 0)
+ return;
+
+ Common::Rect backup = _clippingArea;
+ _clippingArea = clipping;
+ bool useOriginal = (_clippingArea.isEmpty() || _clippingArea.contains(Common::Rect(x, y, x + w, y + h)));
+
+ if (Base::_fillMode != kFillDisabled && Base::_shadowOffset
+ && x + w + Base::_shadowOffset + 1 < Base::_activeSurface->w
+ && y + h + Base::_shadowOffset + 1 < Base::_activeSurface->h
+ && h > (Base::_shadowOffset + 1) * 2) {
+ if (useOriginal) {
+ drawRoundedSquareShadow(x, y, r, w, h, Base::_shadowOffset);
+ } else {
+ drawRoundedSquareShadowClip(x, y, r, w, h, Base::_shadowOffset);
+ }
+ }
+
+ if (useOriginal) {
+ drawRoundedSquareAlg(x, y, r, w, h, _fgColor, Base::_fillMode);
+ } else {
+ drawRoundedSquareAlgClip(x, y, r, w, h, _fgColor, Base::_fillMode);
+ }
+
+ _clippingArea = backup;
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
drawTab(int x, int y, int r, int w, int h) {
if (x + w > Base::_activeSurface->w || y + h > Base::_activeSurface->h ||
w <= 0 || h <= 0 || x < 0 || y < 0 || r > w || r > h)
@@ -955,6 +1616,66 @@ drawTab(int x, int y, int r, int w, int h) {
template<typename PixelType>
void VectorRendererSpec<PixelType>::
+drawTabClip(int x, int y, int r, int w, int h, Common::Rect clipping) {
+ if (x + w > Base::_activeSurface->w || y + h > Base::_activeSurface->h ||
+ w <= 0 || h <= 0 || x < 0 || y < 0 || r > w || r > h)
+ return;
+
+ Common::Rect backup = _clippingArea;
+ _clippingArea = clipping;
+ bool useClippingVersions = !(_clippingArea.isEmpty() || _clippingArea.contains(Common::Rect(x, y, x + w, y + h)));
+
+ if (r == 0 && Base::_bevel > 0) {
+ if (useClippingVersions)
+ drawBevelTabAlgClip(x, y, w, h, Base::_bevel, _bevelColor, _fgColor, (Base::_dynamicData >> 16), (Base::_dynamicData & 0xFFFF));
+ else
+ drawBevelTabAlg(x, y, w, h, Base::_bevel, _bevelColor, _fgColor, (Base::_dynamicData >> 16), (Base::_dynamicData & 0xFFFF));
+ _clippingArea = backup;
+ return;
+ }
+
+ if (r == 0) {
+ _clippingArea = backup;
+ return;
+ }
+
+ switch (Base::_fillMode) {
+ case kFillDisabled:
+ // FIXME: Implement this
+ _clippingArea = backup;
+ return;
+
+ case kFillGradient:
+ case kFillBackground:
+ // 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.)
+ if (useClippingVersions) {
+ drawTabShadowClip(x, y, w - 2, h, r);
+ drawTabAlgClip(x, y, w - 2, h, r, _bgColor, Base::_fillMode);
+ if (Base::_strokeWidth)
+ drawTabAlgClip(x, y, w, h, r, _fgColor, kFillDisabled, (Base::_dynamicData >> 16), (Base::_dynamicData & 0xFFFF));
+ } else {
+ 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:
+ if (useClippingVersions)
+ drawTabAlgClip(x, y, w, h, r, _fgColor, Base::_fillMode);
+ else
+ drawTabAlg(x, y, w, h, r, _fgColor, Base::_fillMode);
+ break;
+ }
+
+ _clippingArea = backup;
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
drawTriangle(int x, int y, int w, int h, TriangleOrientation orient) {
if (x + w > Base::_activeSurface->w || y + h > Base::_activeSurface->h)
@@ -1021,8 +1742,88 @@ drawTriangle(int x, int y, int w, int h, TriangleOrientation orient) {
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawTriangleClip(int x, int y, int w, int h, TriangleOrientation orient, Common::Rect clipping) {
+ if (x + w > Base::_activeSurface->w || y + h > Base::_activeSurface->h)
+ return;
+ PixelType color = 0;
+ if (Base::_strokeWidth <= 1) {
+ if (Base::_fillMode == kFillForeground)
+ color = _fgColor;
+ else if (Base::_fillMode == kFillBackground)
+ color = _bgColor;
+ } else {
+ if (Base::_fillMode == kFillDisabled)
+ return;
+ color = _fgColor;
+ }
+
+ if (Base::_dynamicData != 0)
+ orient = (TriangleOrientation)Base::_dynamicData;
+
+ Common::Rect backup = _clippingArea;
+ _clippingArea = clipping;
+ bool useClippingVersions = !(_clippingArea.isEmpty() || _clippingArea.contains(Common::Rect(x, y, x + w, y + h)));
+
+ if (w == h) {
+ int newW = w;
+
+ switch (orient) {
+ case kTriangleUp:
+ case kTriangleDown:
+ if (useClippingVersions)
+ drawTriangleVertAlgClip(x, y, newW, newW, (orient == kTriangleDown), color, Base::_fillMode);
+ else
+ drawTriangleVertAlg(x, y, newW, newW, (orient == kTriangleDown), color, Base::_fillMode);
+ break;
+
+ case kTriangleLeft:
+ case kTriangleRight:
+ case kTriangleAuto:
+ break;
+ }
+
+ if (Base::_strokeWidth > 0)
+ if (Base::_fillMode == kFillBackground || Base::_fillMode == kFillGradient) {
+ if (useClippingVersions)
+ drawTriangleVertAlgClip(x, y, newW, newW, (orient == kTriangleDown), color, Base::_fillMode);
+ else
+ drawTriangleVertAlg(x, y, newW, newW, (orient == kTriangleDown), color, Base::_fillMode);
+ }
+ } else {
+ int newW = w;
+ int newH = h;
+
+ switch (orient) {
+ case kTriangleUp:
+ case kTriangleDown:
+ if (useClippingVersions)
+ drawTriangleVertAlgClip(x, y, newW, newH, (orient == kTriangleDown), color, Base::_fillMode);
+ else
+ drawTriangleVertAlg(x, y, newW, newH, (orient == kTriangleDown), color, Base::_fillMode);
+ break;
+
+ case kTriangleLeft:
+ case kTriangleRight:
+ case kTriangleAuto:
+ break;
+ }
+
+ if (Base::_strokeWidth > 0) {
+ if (Base::_fillMode == kFillBackground || Base::_fillMode == kFillGradient) {
+ if (useClippingVersions)
+ drawTriangleVertAlgClip(x, y, newW, newH, (orient == kTriangleDown), _fgColor, kFillDisabled);
+ else
+ drawTriangleVertAlg(x, y, newW, newH, (orient == kTriangleDown), _fgColor, kFillDisabled);
+ }
+ }
+ }
+
+ _clippingArea = backup;
+}
/********************************************************************
@@ -1034,6 +1835,11 @@ drawTriangle(int x, int y, int w, int h, TriangleOrientation orient) {
template<typename PixelType>
void VectorRendererSpec<PixelType>::
drawTabAlg(int x1, int y1, int w, int h, int r, PixelType color, VectorRenderer::FillMode fill_m, int baseLeft, int baseRight) {
+ // Don't draw anything for empty rects.
+ if (w <= 0 || h <= 0) {
+ return;
+ }
+
int f, ddF_x, ddF_y;
int x, y, px, py;
int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
@@ -1128,6 +1934,120 @@ drawTabAlg(int x1, int y1, int w, int h, int r, PixelType color, VectorRenderer:
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawTabAlgClip(int x1, int y1, int w, int h, int r, PixelType color, VectorRenderer::FillMode fill_m, int baseLeft, int baseRight) {
+ // Don't draw anything for empty rects.
+ if (w <= 0 || h <= 0) {
+ return;
+ }
+
+ int f, ddF_x, ddF_y;
+ int x, y, px, py;
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int sw = 0, sp = 0, hp = 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_fill = (PixelType *)Base::_activeSurface->getBasePtr(x1, y1);
+ int tl_x = x1 + r, tl_y = y1 + r;
+ int tr_x = x1 + w - r, tr_y = y1 + r;
+ int fill_x = x1, fill_y = y1;
+
+ int real_radius = r;
+ int short_h = h - r + 2;
+ int long_h = h;
+
+ if (fill_m == kFillDisabled) {
+ while (sw++ < Base::_strokeWidth) {
+ colorFillClip<PixelType>(ptr_fill + sp + r, ptr_fill + w + 1 + sp - r, color, fill_x + r, fill_y + sp/pitch, _clippingArea);
+ colorFillClip<PixelType>(ptr_fill + hp - sp + r, ptr_fill + w + hp + 1 - sp - r, color, fill_x + r, fill_y + hp / pitch - sp / pitch, _clippingArea);
+ sp += pitch;
+
+ BE_RESET();
+ r--;
+
+ while (x++ < y) {
+ BE_ALGORITHM();
+ BE_DRAWCIRCLE_TOP_CLIP(ptr_tr, ptr_tl, x, y, px, py, tr_x, tr_y, tl_x, tl_y);
+
+ if (Base::_strokeWidth > 1)
+ BE_DRAWCIRCLE_TOP_CLIP(ptr_tr, ptr_tl, x, y, px - pitch, py, tr_x, tr_y, tl_x, tl_y);
+ }
+ }
+
+ ptr_fill += pitch * real_radius;
+ fill_y += real_radius;
+ while (short_h--) {
+ colorFillClip<PixelType>(ptr_fill, ptr_fill + Base::_strokeWidth, color, fill_x, fill_y, _clippingArea);
+ colorFillClip<PixelType>(ptr_fill + w - Base::_strokeWidth + 1, ptr_fill + w + 1, color, fill_x + w - Base::_strokeWidth + 1, fill_y, _clippingArea);
+ ptr_fill += pitch;
+ ++fill_y;
+ }
+
+ if (baseLeft) {
+ sw = 0;
+ ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(x1, y1 + h + 1);
+ fill_x = x1;
+ fill_y = y1 + h + 1;
+ while (sw++ < Base::_strokeWidth) {
+ colorFillClip<PixelType>(ptr_fill - baseLeft, ptr_fill, color, fill_x - baseLeft, fill_y, _clippingArea);
+ ptr_fill += pitch;
+ ++fill_y;
+ }
+ }
+
+ if (baseRight) {
+ sw = 0;
+ ptr_fill = (PixelType *)Base::_activeSurface->getBasePtr(x1 + w, y1 + h + 1);
+ fill_x = x1 + w;
+ fill_y = y1 + h + 1;
+ while (sw++ < Base::_strokeWidth) {
+ colorFillClip<PixelType>(ptr_fill, ptr_fill + baseRight, color, fill_x, fill_y, _clippingArea);
+ ptr_fill += pitch;
+ ++fill_y;
+ }
+ }
+ } else {
+ BE_RESET();
+
+ precalcGradient(long_h);
+
+ PixelType color1, color2;
+ color1 = color2 = color;
+
+ while (x++ < y) {
+ BE_ALGORITHM();
+
+ if (fill_m == kFillGradient) {
+ color1 = calcGradient(real_radius - x, long_h);
+ color2 = calcGradient(real_radius - y, long_h);
+
+ gradientFillClip(ptr_tl - x - py, w - 2 * r + 2 * x, x1 + r - x - y, real_radius - y, tl_x - x, tl_y - y);
+ gradientFillClip(ptr_tl - y - px, w - 2 * r + 2 * y, x1 + r - y - x, real_radius - x, tl_x - y, tl_y - x);
+
+ BE_DRAWCIRCLE_XCOLOR_TOP_CLIP(ptr_tr, ptr_tl, x, y, px, py, tr_x, tr_y, tl_x, tl_y);
+ } else {
+ colorFillClip<PixelType>(ptr_tl - x - py, ptr_tr + x - py, color, tl_x - x, tl_y - y, _clippingArea);
+ colorFillClip<PixelType>(ptr_tl - y - px, ptr_tr + y - px, color, tl_x - y, tl_y - x, _clippingArea);
+
+ BE_DRAWCIRCLE_TOP_CLIP(ptr_tr, ptr_tl, x, y, px, py, tr_x, tr_y, tl_x, tl_y);
+ }
+ }
+
+ ptr_fill += pitch * r;
+ fill_y += r;
+ while (short_h--) {
+ if (fill_m == kFillGradient) {
+ gradientFillClip(ptr_fill, w + 1, x1, real_radius++, fill_x, fill_y);
+ } else {
+ colorFillClip<PixelType>(ptr_fill, ptr_fill + w + 1, color, fill_x, fill_y, _clippingArea);
+ }
+ ptr_fill += pitch;
+ ++fill_y;
+ }
+ }
+}
template<typename PixelType>
void VectorRendererSpec<PixelType>::
@@ -1190,6 +2110,72 @@ drawTabShadow(int x1, int y1, int w, int h, int r) {
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawTabShadowClip(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 tl_x = xstart + r, tl_y = ystart + r;
+ int fill_x = xstart, fill_y = 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) {
+ blendFillClip(ptr_tl - y - px, ptr_tr + y - px, color, (uint8)alpha, tl_x - y, tl_y - x);
+ hb |= (1 << x);
+ }
+
+ if (((1 << y) & hb) == 0) {
+ blendFillClip(ptr_tl - x - py, ptr_tr + x - py, color, (uint8)alpha, tl_x - x, tl_y - y);
+ hb |= (1 << y);
+ }
+ }
+
+ ptr_fill += pitch * r;
+ fill_y += r;
+ while (short_h--) {
+ blendFillClip(ptr_fill, ptr_fill + width + 1, color, (uint8)alpha, fill_x, fill_y);
+ ptr_fill += pitch;
+ ++fill_y;
+ }
+
+ // 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>::
@@ -1234,10 +2220,66 @@ drawBevelTabAlg(int x, int y, int w, int h, int bevel, PixelType top_color, Pixe
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawBevelTabAlgClip(int x, int y, int w, int h, int bevel, PixelType top_color, PixelType bottom_color, int baseLeft, int baseRight) {
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int i, j;
+
+ PixelType *ptr_left = (PixelType *)_activeSurface->getBasePtr(x, y);
+ int ptr_x = x, ptr_y = y;
+
+ i = bevel;
+ while (i--) {
+ colorFillClip<PixelType>(ptr_left, ptr_left + w, top_color, ptr_x, ptr_y, _clippingArea);
+ ptr_left += pitch;
+ ++ptr_y;
+ }
+
+ if (baseLeft > 0) {
+ i = h - bevel;
+ ptr_left = (PixelType *)_activeSurface->getBasePtr(x, y);
+ ptr_x = x; ptr_y = y;
+ while (i--) {
+ colorFillClip<PixelType>(ptr_left, ptr_left + bevel, top_color, ptr_x, ptr_y, _clippingArea);
+ ptr_left += pitch;
+ ++ptr_y;
+ }
+ }
+
+ i = h - bevel;
+ j = bevel - 1;
+ ptr_left = (PixelType *)_activeSurface->getBasePtr(x + w - bevel, y);
+ ptr_x = x + w - bevel; ptr_y = y;
+ while (i--) {
+ colorFillClip<PixelType>(ptr_left + j, ptr_left + bevel, bottom_color, ptr_x + j, ptr_y, _clippingArea);
+ if (j > 0) j--;
+ ptr_left += pitch;
+ ++ptr_y;
+ }
+
+ i = bevel;
+ ptr_left = (PixelType *)_activeSurface->getBasePtr(x + w - bevel, y + h - bevel);
+ ptr_x = x + w - bevel; ptr_y = y + h - bevel;
+ while (i--) {
+ colorFillClip<PixelType>(ptr_left, ptr_left + baseRight + bevel, bottom_color, ptr_x, ptr_y, _clippingArea);
+
+ if (baseLeft)
+ colorFillClip<PixelType>(ptr_left - w - baseLeft + bevel, ptr_left - w + bevel + bevel, top_color, ptr_x - w - baseLeft + bevel, ptr_y, _clippingArea);
+ ptr_left += pitch;
+ ++ptr_y;
+ }
+}
+
/** SQUARE ALGORITHM **/
template<typename PixelType>
void VectorRendererSpec<PixelType>::
drawSquareAlg(int x, int y, int w, int h, PixelType color, VectorRenderer::FillMode fill_m) {
+ // Do not draw anything for empty rects.
+ if (w <= 0 || h <= 0) {
+ return;
+ }
+
PixelType *ptr = (PixelType *)_activeSurface->getBasePtr(x, y);
int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
int max_h = h;
@@ -1267,6 +2309,46 @@ drawSquareAlg(int x, int y, int w, int h, PixelType color, VectorRenderer::FillM
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawSquareAlgClip(int x, int y, int w, int h, PixelType color, VectorRenderer::FillMode fill_m) {
+ // Do not draw anything for empty rects.
+ if (w <= 0 || h <= 0) {
+ return;
+ }
+
+ PixelType *ptr = (PixelType *)_activeSurface->getBasePtr(x, y);
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int max_h = h;
+ int ptr_y = y;
+
+ if (fill_m != kFillDisabled) {
+ while (h--) {
+ if (fill_m == kFillGradient)
+ color = calcGradient(max_h - h, max_h);
+
+ colorFillClip<PixelType>(ptr, ptr + w, color, x, ptr_y, _clippingArea);
+ ptr += pitch;
+ ++ptr_y;
+ }
+ } else {
+ int sw = Base::_strokeWidth, sp = 0, hp = pitch * (h - 1);
+
+ while (sw--) {
+ colorFillClip<PixelType>(ptr + sp, ptr + w + sp, color, x, ptr_y + sp/pitch, _clippingArea);
+ colorFillClip<PixelType>(ptr + hp - sp, ptr + w + hp - sp, color, x, ptr_y + h - sp/pitch, _clippingArea);
+ sp += pitch;
+ }
+
+ while (h--) {
+ colorFillClip<PixelType>(ptr, ptr + Base::_strokeWidth, color, x, ptr_y, _clippingArea);
+ colorFillClip<PixelType>(ptr + w - Base::_strokeWidth, ptr + w, color, x + w - Base::_strokeWidth, ptr_y, _clippingArea);
+ ptr += pitch;
+ ptr_y += 1;
+ }
+ }
+}
+
/** SQUARE ALGORITHM **/
template<typename PixelType>
void VectorRendererSpec<PixelType>::
@@ -1323,10 +2405,76 @@ drawBevelSquareAlg(int x, int y, int w, int h, int bevel, PixelType top_color, P
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawBevelSquareAlgClip(int x, int y, int w, int h, int bevel, PixelType top_color, PixelType bottom_color, bool fill) {
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int i, j;
+ PixelType *ptr_left;
+ int ptr_x, ptr_y;
+
+ // Fill Background
+ ptr_left = (PixelType *)_activeSurface->getBasePtr(x, y);
+ ptr_x = x; ptr_y = y;
+ i = h;
+ if (fill) {
+ assert((_bgColor & ~_alphaMask) == 0); // only support black
+ while (i--) {
+ darkenFillClip(ptr_left, ptr_left + w, ptr_x, ptr_y);
+ ptr_left += pitch;
+ ++ptr_y;
+ }
+ }
+
+ x = MAX(x - bevel, 0);
+ y = MAX(y - bevel, 0);
+
+ w = MIN(w + (bevel * 2), (int)_activeSurface->w);
+ h = MIN(h + (bevel * 2), (int)_activeSurface->h);
+
+ ptr_left = (PixelType *)_activeSurface->getBasePtr(x, y);
+ ptr_x = x; ptr_y = y;
+ i = bevel;
+ while (i--) {
+ colorFillClip<PixelType>(ptr_left, ptr_left + w, top_color, ptr_x, ptr_y, _clippingArea);
+ ptr_left += pitch;
+ ++ptr_y;
+ }
+
+ ptr_left = (PixelType *)_activeSurface->getBasePtr(x, y + bevel);
+ ptr_x = x; ptr_y = y + bevel;
+ i = h - bevel;
+ while (i--) {
+ colorFillClip<PixelType>(ptr_left, ptr_left + bevel, top_color, ptr_x, ptr_y, _clippingArea);
+ ptr_left += pitch;
+ ++ptr_y;
+ }
+
+ ptr_left = (PixelType *)_activeSurface->getBasePtr(x, y + h - bevel);
+ ptr_x = x; ptr_y = y + h - bevel;
+ i = bevel;
+ while (i--) {
+ colorFillClip<PixelType>(ptr_left + i, ptr_left + w, bottom_color, ptr_x + i, ptr_y, _clippingArea);
+ ptr_left += pitch;
+ ++ptr_y;
+ }
+
+ ptr_left = (PixelType *)_activeSurface->getBasePtr(x + w - bevel, y);
+ ptr_x = x + w - bevel; ptr_y = y;
+ i = h - bevel;
+ j = bevel - 1;
+ while (i--) {
+ colorFillClip<PixelType>(ptr_left + j, ptr_left + bevel, bottom_color, ptr_x + j, ptr_y, _clippingArea);
+ if (j > 0) j--;
+ ptr_left += pitch;
+ ++ptr_y;
+ }
+}
+
/** GENERIC LINE ALGORITHM **/
template<typename PixelType>
void VectorRendererSpec<PixelType>::
-drawLineAlg(int x1, int y1, int x2, int y2, int dx, int dy, PixelType color) {
+drawLineAlg(int x1, int y1, int x2, int y2, uint dx, uint dy, PixelType color) {
PixelType *ptr = (PixelType *)_activeSurface->getBasePtr(x1, y1);
int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
int xdir = (x2 > x1) ? 1 : -1;
@@ -1371,6 +2519,59 @@ drawLineAlg(int x1, int y1, int x2, int y2, int dx, int dy, PixelType color) {
*ptr = (PixelType)color;
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawLineAlgClip(int x1, int y1, int x2, int y2, uint dx, uint dy, PixelType color) {
+ PixelType *ptr = (PixelType *)_activeSurface->getBasePtr(x1, y1);
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int xdir = (x2 > x1) ? 1 : -1;
+ int ptr_x = x1, ptr_y = y1;
+
+ if (IS_IN_CLIP(ptr_x, ptr_y)) *ptr = (PixelType)color;
+
+ if (dx > dy) {
+ int ddy = dy * 2;
+ int dysub = ddy - (dx * 2);
+ int error_term = ddy - dx;
+
+ while (dx--) {
+ if (error_term >= 0) {
+ ptr += pitch;
+ ++ptr_y;
+ error_term += dysub;
+ } else {
+ error_term += ddy;
+ }
+
+ ptr += xdir;
+ ptr_x += xdir;
+ if (IS_IN_CLIP(ptr_x, ptr_y)) *ptr = (PixelType)color;
+ }
+ } else {
+ int ddx = dx * 2;
+ int dxsub = ddx - (dy * 2);
+ int error_term = ddx - dy;
+
+ while (dy--) {
+ if (error_term >= 0) {
+ ptr += xdir;
+ ptr_x += xdir;
+ error_term += dxsub;
+ } else {
+ error_term += ddx;
+ }
+
+ ptr += pitch;
+ ++ptr_y;
+ if (IS_IN_CLIP(ptr_x, ptr_y)) *ptr = (PixelType)color;
+ }
+ }
+
+ ptr = (PixelType *)_activeSurface->getBasePtr(x2, y2);
+ ptr_x = x2; ptr_y = y2;
+ if (IS_IN_CLIP(ptr_x, ptr_y)) *ptr = (PixelType)color;
+}
+
/** VERTICAL TRIANGLE DRAWING ALGORITHM **/
/**
FIXED POINT ARITHMETIC
@@ -1393,6 +2594,12 @@ drawLineAlg(int x1, int y1, int x2, int y2, int dx, int dy, PixelType color) {
template<typename PixelType>
void VectorRendererSpec<PixelType>::
drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color, VectorRenderer::FillMode fill_m) {
+ // Don't draw anything for empty rects. This assures dy is always different
+ // from zero.
+ if (w <= 0 || h <= 0) {
+ return;
+ }
+
int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
int gradient_h = 0;
if (!inverted) {
@@ -1422,6 +2629,9 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
blendPixelPtr(floor, color, 50);
#if FIXED_POINT
+ // In this branch dx is always different from zero. This is because
+ // abs(dx) is strictly greater than abs(dy), and abs returns zero
+ // as minimal value.
int gradient = (dy << 8) / dx;
int intery = (y1 << 8) + gradient;
#else
@@ -1560,10 +2770,221 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color
}
+/////////////
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawTriangleVertAlgClip(int x1, int y1, int w, int h, bool inverted, PixelType color, VectorRenderer::FillMode fill_m) {
+ // Don't draw anything for empty rects. This assures dy is always different
+ // from zero.
+ if (w <= 0 || h <= 0) {
+ return;
+ }
+
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int gradient_h = 0;
+ int y_pitch_sign = 1;
+ if (!inverted) {
+ pitch = -pitch;
+ y1 += h;
+ y_pitch_sign = -1;
+ }
+
+ PixelType *ptr_right = (PixelType *)_activeSurface->getBasePtr(x1, y1);
+ PixelType *floor = ptr_right - 1;
+ PixelType *ptr_left = (PixelType *)_activeSurface->getBasePtr(x1 + w, y1);
+
+ int x2 = x1 + w / 2;
+ int y2 = y1 + h;
+ int x_right = x1;
+ int y_right = y1;
+ int x_left = x1 + w;
+ int y_left = y1;
+ int x_floor = x_right - 1;
+ int y_floor = y_right;
+
+#if FIXED_POINT
+ int dx = (x2 - x1) << 8;
+ int dy = (y2 - y1) << 8;
+
+ if (abs(dx) > abs(dy)) {
+#else
+ double dx = (double)x2 - (double)x1;
+ double dy = (double)y2 - (double)y1;
+
+ if (fabs(dx) > fabs(dy)) {
+#endif
+ while (floor++ != ptr_left)
+ blendPixelPtrClip(floor, color, 50, ++x_floor, y_floor);
+
+#if FIXED_POINT
+ // In this branch dx is always different from zero. This is because
+ // abs(dx) is strictly greater than abs(dy), and abs returns zero
+ // as minimal value.
+ int gradient = (dy << 8) / dx;
+ int intery = (y1 << 8) + gradient;
+#else
+ double gradient = dy / dx;
+ double intery = y1 + gradient;
+#endif
+
+ for (int x = x1 + 1; x < x2; x++) {
+#if FIXED_POINT
+ if (intery + gradient > ipart(intery) + 0x100) {
+#else
+ if (intery + gradient > ipart(intery) + 1) {
+#endif
+ ptr_right++;
+ ptr_left--;
+ ++x_right;
+ --x_left;
+ }
+
+ ptr_left += pitch;
+ ptr_right += pitch;
+ y_right += y_pitch_sign;
+ y_left += y_pitch_sign;
+
+ intery += gradient;
+
+ switch (fill_m) {
+ case kFillDisabled:
+ if (IS_IN_CLIP(x_left, y_left)) *ptr_left = color;
+ if (IS_IN_CLIP(x_right, y_right)) *ptr_right = color;
+ break;
+ case kFillForeground:
+ case kFillBackground:
+ colorFillClip<PixelType>(ptr_right + 1, ptr_left, color, x_right+1, y_right, _clippingArea);
+ blendPixelPtrClip(ptr_right, color, rfpart(intery), x_right, y_right);
+ blendPixelPtrClip(ptr_left, color, rfpart(intery), x_left, y_left);
+ break;
+ case kFillGradient:
+ colorFillClip<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h), x_right, y_right, _clippingArea);
+ blendPixelPtrClip(ptr_right, color, rfpart(intery), x_right, y_right);
+ blendPixelPtrClip(ptr_left, color, rfpart(intery), x_left, y_left);
+ break;
+ }
+ }
+
+ return;
+ }
+
+#if FIXED_POINT
+ if (abs(dx) < abs(dy)) {
+#else
+ if (fabs(dx) < fabs(dy)) {
+#endif
+ ptr_left--;
+ --x_left;
+ while (floor++ != ptr_left)
+ blendPixelPtrClip(floor, color, 50, ++x_floor, y_floor);
+
+#if FIXED_POINT
+ int gradient = (dx << 8) / (dy + 0x100);
+ int interx = (x1 << 8) + gradient;
+#else
+ double gradient = dx / (dy + 1);
+ double interx = x1 + gradient;
+#endif
+
+ for (int y = y1 + 1; y < y2; y++) {
+#if FIXED_POINT
+ if (interx + gradient > ipart(interx) + 0x100) {
+#else
+ if (interx + gradient > ipart(interx) + 1) {
+#endif
+ ptr_right++;
+ ptr_left--;
+ ++x_right;
+ --x_left;
+ }
+
+ ptr_left += pitch;
+ ptr_right += pitch;
+ y_right += y_pitch_sign;
+ y_left += y_pitch_sign;
+
+ interx += gradient;
+
+ switch (fill_m) {
+ case kFillDisabled:
+ if (IS_IN_CLIP(x_left, y_left)) *ptr_left = color;
+ if (IS_IN_CLIP(x_right, y_right)) *ptr_right = color;
+ break;
+ case kFillForeground:
+ case kFillBackground:
+ colorFillClip<PixelType>(ptr_right + 1, ptr_left, color, x_right+1, y_right, _clippingArea);
+ blendPixelPtrClip(ptr_right, color, rfpart(interx), x_right, y_right);
+ blendPixelPtrClip(ptr_left, color, rfpart(interx), x_left, y_left);
+ break;
+ case kFillGradient:
+ colorFillClip<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h), x_right, y_right, _clippingArea);
+ blendPixelPtrClip(ptr_right, color, rfpart(interx), x_right, y_right);
+ blendPixelPtrClip(ptr_left, color, rfpart(interx), x_left, y_left);
+ break;
+ }
+ }
+
+ return;
+ }
+
+ ptr_left--;
+ --x_left;
+ while (floor++ != ptr_left)
+ blendPixelPtrClip(floor, color, 50, ++x_floor, y_floor);
+
+#if FIXED_POINT
+ int gradient = (dx / dy) << 8;
+ int interx = (x1 << 8) + gradient;
+#else
+ double gradient = dx / dy;
+ double interx = x1 + gradient;
+#endif
+
+ for (int y = y1 + 1; y < y2; y++) {
+ ptr_right++;
+ ptr_left--;
+ ++x_right;
+ --x_left;
+
+ ptr_left += pitch;
+ ptr_right += pitch;
+ y_right += y_pitch_sign;
+ y_left += y_pitch_sign;
+
+ interx += gradient;
+
+ switch (fill_m) {
+ case kFillDisabled:
+ if (IS_IN_CLIP(x_left, y_left)) *ptr_left = color;
+ if (IS_IN_CLIP(x_right, y_right)) *ptr_right = color;
+ break;
+ case kFillForeground:
+ case kFillBackground:
+ colorFillClip<PixelType>(ptr_right + 1, ptr_left, color, x_right+1, y_right, _clippingArea);
+ blendPixelPtrClip(ptr_right, color, rfpart(interx), x_right, y_right);
+ blendPixelPtrClip(ptr_left, color, rfpart(interx), x_left, y_left);
+ break;
+ case kFillGradient:
+ colorFillClip<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h), x_right, y_right, _clippingArea);
+ blendPixelPtrClip(ptr_right, color, rfpart(interx), x_right, y_right);
+ blendPixelPtrClip(ptr_left, color, rfpart(interx), x_left, y_left);
+ break;
+ }
+ }
+}
+
+/////////////
+
/** VERTICAL TRIANGLE DRAWING - FAST VERSION FOR SQUARED TRIANGLES */
template<typename PixelType>
void VectorRendererSpec<PixelType>::
drawTriangleFast(int x1, int y1, int size, bool inverted, PixelType color, VectorRenderer::FillMode fill_m) {
+ // Do not draw anything for empty rects.
+ if (size <= 0) {
+ return;
+ }
+
int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
if (!inverted) {
@@ -1657,6 +3078,9 @@ drawBorderRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color,
while (x++ < (y - 2)) {
BE_ALGORITHM();
+ if (x < _clippingArea.left || x > _clippingArea.right) continue;
+ if (y < _clippingArea.top || y > _clippingArea.bottom) continue;
+
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)));
@@ -1684,7 +3108,80 @@ drawBorderRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color,
template<typename PixelType>
void VectorRendererSpec<PixelType>::
+drawBorderRoundedSquareAlgClip(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;
+
+ 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;
+
+ PixelType color1 = color;
+ PixelType color2 = color;
+
+ while (sw++ < Base::_strokeWidth) {
+ blendFillClip(ptr_fill + sp + r, ptr_fill + w + 1 + sp - r, color1, alpha_t,
+ x1 + r, y1 + sp/pitch); // top
+ blendFillClip(ptr_fill + hp - sp + r, ptr_fill + w + hp + 1 - sp - r, color2, alpha_b,
+ x1 + r, y1 + (hp - sp)/ pitch); // bottom
+ sp += pitch;
+
+ 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();
+
+ BE_DRAWCIRCLE_BCOLOR_TR_CW_CLIP(ptr_tr, x, y, px, py, (uint8)(alpha_r + (alphaStep_tr * x)), x1 + w - r, y1 + r);
+ BE_DRAWCIRCLE_BCOLOR_BR_CW_CLIP(ptr_br, x, y, px, py, (uint8)(alpha_b + (alphaStep_br * x)), x1 + w - r, y1 + h - r);
+ BE_DRAWCIRCLE_BCOLOR_BL_CW_CLIP(ptr_bl, x, y, px, py, (uint8)(alpha_l + (alphaStep_bl * x)), x1 + r, y1 + h - r);
+ BE_DRAWCIRCLE_BCOLOR_TL_CW_CLIP(ptr_tl, x, y, px, py, (uint8)(alpha_t + (alphaStep_tl * x)), x1 + r, y1 + r);
+
+ BE_DRAWCIRCLE_BCOLOR_TR_CCW_CLIP(ptr_tr, x, y, px, py, (uint8)(alpha_t - (alphaStep_tr * x)), x1 + w - r, y1 + r);
+ BE_DRAWCIRCLE_BCOLOR_BR_CCW_CLIP(ptr_br, x, y, px, py, (uint8)(alpha_r - (alphaStep_br * x)), x1 + w - r, y1 + h - r);
+ BE_DRAWCIRCLE_BCOLOR_BL_CCW_CLIP(ptr_bl, x, y, px, py, (uint8)(alpha_b - (alphaStep_bl * x)), x1 + r, y1 + h - r);
+ BE_DRAWCIRCLE_BCOLOR_TL_CCW_CLIP(ptr_tl, x, y, px, py, (uint8)(alpha_l - (alphaStep_tl * x)), x1 + r, y1 + r);
+
+ if (Base::_strokeWidth > 1) {
+ BE_DRAWCIRCLE_BCOLOR_CLIP(ptr_tr, ptr_tl, ptr_bl, ptr_br, x - 1, y, px, py,
+ x1 + w - r, y1 + r, x1 + r, y1 + r, x1 + r, y1 + h - r, x1 + w - r, y1 + h - r);
+ BE_DRAWCIRCLE_BCOLOR_CLIP(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px - pitch, py,
+ x1 + w - r, y1 + r, x1 + r, y1 + r, x1 + r, y1 + h - r, x1 + w - r, y1 + h - r);
+ }
+ }
+ }
+
+ ptr_fill += pitch * real_radius;
+ while (short_h--) {
+ blendFillClip(ptr_fill, ptr_fill + Base::_strokeWidth, color1, alpha_l,
+ x1, y1 + real_radius + h - (2 * r) + 2 - short_h - 1); // left
+ blendFillClip(ptr_fill + w - Base::_strokeWidth + 1, ptr_fill + w + 1, color2, alpha_r,
+ x1 + w - Base::_strokeWidth + 1, y1 + real_radius + h - (2 * r) + 2 - short_h - 1); // right
+ ptr_fill += pitch;
+ }
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
drawInteriorRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color, VectorRenderer::FillMode fill_m) {
+ // Do not draw empty space rounded squares.
+ if (w <= 0 || h <= 0) {
+ return;
+ }
+
int f, ddF_x, ddF_y;
int x, y, px, py;
int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
@@ -1710,6 +3207,8 @@ drawInteriorRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType colo
while (x++ < y) {
BE_ALGORITHM();
+ if (y1 + r + y < _clippingArea.top || y1 + r + y > _clippingArea.bottom) continue;
+
color1 = calcGradient(real_radius - x, long_h);
color2 = calcGradient(real_radius - y, long_h);
color3 = calcGradient(long_h - r + x, long_h);
@@ -1751,6 +3250,91 @@ drawInteriorRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType colo
template<typename PixelType>
void VectorRendererSpec<PixelType>::
+drawInteriorRoundedSquareAlgClip(int x1, int y1, int r, int w, int h, PixelType color, VectorRenderer::FillMode fill_m) {
+ // Do not draw empty space rounded squares.
+ if (w <= 0 || h <= 0) {
+ return;
+ }
+
+ int f, ddF_x, ddF_y;
+ int x, y, px, py;
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+
+ 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;
+
+ BE_RESET();
+
+ PixelType color1 = color;
+
+ if (fill_m == kFillGradient) {
+ PixelType color2, color3, color4;
+ precalcGradient(long_h);
+
+ 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);
+
+ //TL = (x1 + r, y1 + r)
+ gradientFillClip(ptr_tl - x - py, w - 2 * r + 2 * x, x1 + r - x - y, real_radius - y,
+ x1 + r - x, y1 + r - y);
+ gradientFillClip(ptr_tl - y - px, w - 2 * r + 2 * y, x1 + r - y - x, real_radius - x,
+ x1 + r - y, y1 + r - x);
+
+ //BL = (x1 + r, y1 + h - r)
+ gradientFillClip(ptr_bl - x + py, w - 2 * r + 2 * x, x1 + r - x - y, long_h - r + y,
+ x1 + r - x, y1 + h - r + y);
+ gradientFillClip(ptr_bl - y + px, w - 2 * r + 2 * y, x1 + r - y - x, long_h - r + x,
+ x1 + r - y, y1 + h - r + x);
+
+ BE_DRAWCIRCLE_XCOLOR_CLIP(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px, py,
+ x1 + w - r, y1 + r, x1 + r, y1 + r, x1 + r, y1 + h - r, x1 + w - r, y1 + h - r);
+ }
+ } else {
+ while (x++ < y) {
+ BE_ALGORITHM();
+
+ colorFillClip<PixelType>(ptr_tl - x - py, ptr_tr + x - py, color1,
+ x1 + r - x, y1 + r - y, _clippingArea);
+ colorFillClip<PixelType>(ptr_tl - y - px, ptr_tr + y - px, color1,
+ x1 + r - y, y1 + r - x, _clippingArea);
+
+ colorFillClip<PixelType>(ptr_bl - x + py, ptr_br + x + py, color1,
+ x1 + r - x, y1 + h - r + y, _clippingArea);
+ colorFillClip<PixelType>(ptr_bl - y + px, ptr_br + y + px, color1,
+ x1 + r - y, y1 + h - r + x, _clippingArea);
+
+ // do not remove - messes up the drawing at lower resolutions
+ BE_DRAWCIRCLE_CLIP(ptr_tr, ptr_tl, ptr_bl, ptr_br, x, y, px, py,
+ x1 + w - r, y1 + r, x1 + r, y1 + r, x1 + r, y1 + h - r, x1 + w - r, y1 + h - r);
+ }
+ }
+
+ ptr_fill += pitch * r;
+ int short_h_orig = short_h;
+ while (short_h--) {
+ if (fill_m == kFillGradient) {
+ gradientFillClip(ptr_fill, w + 1, x1, real_radius++, x1, y1 + r + short_h_orig - short_h -1);
+ } else {
+ colorFillClip<PixelType>(ptr_fill, ptr_fill + w + 1, color1, x1, y1 + r + short_h_orig - short_h - 1, _clippingArea);
+ }
+ 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;
@@ -1780,6 +3364,38 @@ drawRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color, Vecto
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawRoundedSquareAlgClip(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)
+ drawInteriorRoundedSquareAlgClip(x1, y1, r, w, h, _bgColor, fill_m);
+ else
+ drawInteriorRoundedSquareAlgClip(x1, y1, r, w, h, color, fill_m);
+ }
+
+ //I expect these to work fine with clipping:
+ if (Base::_strokeWidth) {
+ if (r != 0 && _bevel > 0) {
+ drawBorderRoundedSquareAlgClip(x1, y1, r, w, h, color, fill_m, borderAlpha_t, borderAlpha_r, borderAlpha_b, borderAlpha_l);
+ drawBorderRoundedSquareAlgClip(x1, y1, r, w, h, _bevelColor, fill_m, bevelAlpha_t, bevelAlpha_r, bevelAlpha_b, bevelAlpha_l);
+ } else {
+ drawBorderRoundedSquareAlgClip(x1, y1, r, w, h, color, fill_m, 255, 255, 255, 255);
+ }
+ }
+}
+
/** CIRCLE ALGORITHM **/
template<typename PixelType>
void VectorRendererSpec<PixelType>::
@@ -1824,7 +3440,47 @@ drawCircleAlg(int x1, int y1, int r, PixelType color, VectorRenderer::FillMode f
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawCircleAlgClip(int x1, int y1, int r, PixelType color, VectorRenderer::FillMode fill_m) {
+ int f, ddF_x, ddF_y;
+ int x, y, px, py, sw = 0;
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ PixelType *ptr = (PixelType *)Base::_activeSurface->getBasePtr(x1, y1);
+
+ if (fill_m == kFillDisabled) {
+ while (sw++ < Base::_strokeWidth) {
+ BE_RESET();
+ r--;
+
+ if (IS_IN_CLIP(x1 + y, y1)) *(ptr + y) = color;
+ if (IS_IN_CLIP(x1 - y, y1)) *(ptr - y) = color;
+ if (IS_IN_CLIP(x1, y1 + y)) *(ptr + py) = color;
+ if (IS_IN_CLIP(x1, y1 - y)) *(ptr - py) = color;
+
+ while (x++ < y) {
+ BE_ALGORITHM();
+ BE_DRAWCIRCLE_CLIP(ptr, ptr, ptr, ptr, x, y, px, py, x1, y1, x1, y1, x1, y1, x1, y1);
+
+ if (Base::_strokeWidth > 1) {
+ BE_DRAWCIRCLE_CLIP(ptr, ptr, ptr, ptr, x - 1, y, px, py, x1, y1, x1, y1, x1, y1, x1, y1);
+ BE_DRAWCIRCLE_CLIP(ptr, ptr, ptr, ptr, x, y, px - pitch, py, x1, y1, x1, y1, x1, y1, x1, y1);
+ }
+ }
+ }
+ } else {
+ colorFillClip<PixelType>(ptr - r, ptr + r, color, x1 - r, y1 + r, _clippingArea);
+ BE_RESET();
+ while (x++ < y) {
+ BE_ALGORITHM();
+ colorFillClip<PixelType>(ptr - x + py, ptr + x + py, color, x1 - x, y1 + y, _clippingArea);
+ colorFillClip<PixelType>(ptr - x - py, ptr + x - py, color, x1 - x, y1 - y, _clippingArea);
+ colorFillClip<PixelType>(ptr - y + px, ptr + y + px, color, x1 - y, y1 + x, _clippingArea);
+ colorFillClip<PixelType>(ptr - y - px, ptr + y - px, color, x1 - y, y1 - x, _clippingArea);
+ }
+ }
+}
/********************************************************************
@@ -1835,6 +3491,11 @@ 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 offset) {
+ // Do nothing for empty rects or no shadow offset.
+ if (w <= 0 || h <= 0 || offset <= 0) {
+ return;
+ }
+
PixelType *ptr = (PixelType *)_activeSurface->getBasePtr(x + w - 1, y + offset);
int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
int i, j;
@@ -1870,6 +3531,54 @@ drawSquareShadow(int x, int y, int w, int h, int offset) {
template<typename PixelType>
void VectorRendererSpec<PixelType>::
+drawSquareShadowClip(int x, int y, int w, int h, int offset) {
+ // Do nothing for empty rects or no shadow offset.
+ if (w <= 0 || h <= 0 || offset <= 0) {
+ return;
+ }
+
+ PixelType *ptr = (PixelType *)_activeSurface->getBasePtr(x + w - 1, y + offset);
+ int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
+ int i, j, ptr_x = x+w-1, ptr_y = y+offset;
+
+ i = h - offset;
+
+ while (i--) {
+ j = offset;
+ while (j--)
+ blendPixelPtrClip(ptr + j, 0, ((offset - j) << 8) / offset, ptr_x + j, ptr_y);
+ ptr += pitch;
+ ++ptr_y;
+ }
+
+ ptr = (PixelType *)_activeSurface->getBasePtr(x + offset, y + h - 1);
+ ptr_x = x + offset;
+ ptr_y = y + h - 1;
+
+ while (i++ < offset) {
+ j = w - offset;
+ while (j--)
+ blendPixelPtrClip(ptr + j, 0, ((offset - i) << 8) / offset, ptr_x + j, ptr_y);
+ ptr += pitch;
+ ++ptr_y;
+ }
+
+ ptr = (PixelType *)_activeSurface->getBasePtr(x + w, y + h);
+ ptr_x = x + w;
+ ptr_y = y + h;
+
+ i = 0;
+ while (i++ < offset) {
+ j = offset - 1;
+ while (j--)
+ blendPixelPtrClip(ptr + j, 0, (((offset - j) * (offset - i)) << 8) / (offset * offset), ptr_x + j, ptr_y);
+ ptr += pitch;
+ ++ptr_y;
+ }
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
drawRoundedSquareShadow(int x1, int y1, int r, int w, int h, int offset) {
int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel;
@@ -1907,7 +3616,6 @@ drawRoundedSquareShadow(int x1, int y1, int r, int w, int h, int offset) {
while (x++ < y) {
BE_ALGORITHM();
-
if (((1 << x) & hb) == 0) {
blendFill(ptr_tl - y - px, ptr_tr + y - px, color, (uint8)alpha);
@@ -1943,6 +3651,83 @@ drawRoundedSquareShadow(int x1, int y1, int r, int w, int h, int offset) {
}
}
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
+drawRoundedSquareShadowClip(int x1, int y1, int r, int w, int h, int offset) {
+ 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;
+
+ // 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);
+
+ 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) {
+ blendFillClip(ptr_tl - y - px, ptr_tr + y - px, color, (uint8)alpha,
+ xstart + r - y, ystart + r - x);
+
+ // Will create a dark line of pixles if left out
+ if (hb > 0) {
+ blendFillClip(ptr_bl - y + px, ptr_br + y + px, color, (uint8)alpha,
+ xstart + r - y, ystart + height - r + x);
+ }
+ hb |= (1 << x);
+ }
+
+ if (((1 << y) & hb) == 0) {
+ blendFillClip(ptr_tl - x - py, ptr_tr + x - py, color, (uint8)alpha, xstart + r - x, ystart + r - y);
+ blendFillClip(ptr_bl - x + py, ptr_br + x + py, color, (uint8)alpha, xstart + r - x, ystart + height - r + y);
+ hb |= (1 << y);
+ }
+ }
+
+ ptr_fill += pitch * r;
+ int orig_short_h = short_h;
+ while (short_h--) {
+ blendFillClip(ptr_fill, ptr_fill + width + 1, color, (uint8)alpha,
+ xstart, ystart + r + orig_short_h - short_h - 1);
+ 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;
+ }
+}
/******************************************************************************/
@@ -1956,8 +3741,7 @@ drawRoundedSquareShadow(int x1, int y1, int r, int w, int h, int offset) {
/** LINES **/
template<typename PixelType>
void VectorRendererAA<PixelType>::
-drawLineAlg(int x1, int y1, int x2, int y2, int dx, int dy, PixelType color) {
-
+drawLineAlg(int x1, int y1, int x2, int y2, uint dx, uint dy, PixelType color) {
PixelType *ptr = (PixelType *)Base::_activeSurface->getBasePtr(x1, y1);
int pitch = Base::_activeSurface->pitch / Base::_activeSurface->format.bytesPerPixel;
int xdir = (x2 > x1) ? 1 : -1;
@@ -1967,7 +3751,7 @@ drawLineAlg(int x1, int y1, int x2, int y2, int dx, int dy, PixelType color) {
*ptr = (PixelType)color;
if (dx > dy) {
- gradient = (uint32)(dy << 16) / (uint32)dx;
+ gradient = (dy << 16) / dx;
error_acc = 0;
while (--dx) {
@@ -1983,8 +3767,8 @@ drawLineAlg(int x1, int y1, int x2, int y2, int dx, int dy, PixelType color) {
this->blendPixelPtr(ptr, color, ~alpha);
this->blendPixelPtr(ptr + pitch, color, alpha);
}
- } else {
- gradient = (uint32)(dx << 16) / (uint32)dy;
+ } else if (dy != 0) {
+ gradient = (dx << 16) / dy;
error_acc = 0;
while (--dy) {
@@ -2009,6 +3793,11 @@ drawLineAlg(int x1, int y1, int x2, int y2, int dx, int dy, PixelType color) {
template<typename PixelType>
void VectorRendererAA<PixelType>::
drawTabAlg(int x1, int y1, int w, int h, int r, PixelType color, VectorRenderer::FillMode fill_m, int baseLeft, int baseRight) {
+ // Don't draw anything for empty rects.
+ if (w <= 0 || h <= 0) {
+ return;
+ }
+
int x, y, px, py;
int pitch = Base::_activeSurface->pitch / Base::_activeSurface->format.bytesPerPixel;
int sw = 0, sp = 0, hp = 0;
@@ -2217,6 +4006,14 @@ drawBorderRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color,
template<typename PixelType>
void VectorRendererAA<PixelType>::
drawInteriorRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType color, VectorRenderer::FillMode fill_m) {
+ w -= 2*Base::_strokeWidth;
+ h -= 2*Base::_strokeWidth;
+
+ // Do not draw empty space rounded squares.
+ if (w <= 0 || h <= 0) {
+ return;
+ }
+
int x, y;
const int pitch = Base::_activeSurface->pitch / Base::_activeSurface->format.bytesPerPixel;
int px, py;
@@ -2228,8 +4025,6 @@ drawInteriorRoundedSquareAlg(int x1, int y1, int r, int w, int h, PixelType colo
r -= Base::_strokeWidth;
x1 += Base::_strokeWidth;
y1 += Base::_strokeWidth;
- w -= 2*Base::_strokeWidth;
- h -= 2*Base::_strokeWidth;
rsq = r*r;
PixelType *ptr_tl = (PixelType *)Base::_activeSurface->getBasePtr(x1 + r, y1 + r);
diff --git a/graphics/VectorRendererSpec.h b/graphics/VectorRendererSpec.h
index f47cc3997a..84c802f6df 100644
--- a/graphics/VectorRendererSpec.h
+++ b/graphics/VectorRendererSpec.h
@@ -51,14 +51,31 @@ public:
VectorRendererSpec(PixelFormat format);
void drawLine(int x1, int y1, int x2, int y2);
+ void drawLineClip(int x1, int y1, int x2, int y2, Common::Rect clipping);
void drawCircle(int x, int y, int r);
+ void drawCircleClip(int x, int y, int r, Common::Rect clipping);
void drawSquare(int x, int y, int w, int h);
+ void drawSquareClip(int x, int y, int w, int h, Common::Rect clipping);
void drawRoundedSquare(int x, int y, int r, int w, int h);
+ void drawRoundedSquareClip(int x, int y, int r, int w, int h, Common::Rect clipping);
void drawTriangle(int x, int y, int base, int height, TriangleOrientation orient);
+ void drawTriangleClip(int x, int y, int base, int height, TriangleOrientation orient, Common::Rect clipping);
void drawTab(int x, int y, int r, int w, int h);
+ void drawTabClip(int x, int y, int r, int w, int h, Common::Rect clipping);
void drawBeveledSquare(int x, int y, int w, int h, int bevel) {
drawBevelSquareAlg(x, y, w, h, bevel, _bevelColor, _fgColor, Base::_fillMode != kFillDisabled);
}
+ void drawBeveledSquareClip(int x, int y, int w, int h, int bevel, Common::Rect clipping) {
+ bool useClippingVersions = !(clipping.isEmpty() || clipping.contains(Common::Rect(x, y, x + w, y + h)));
+ if (useClippingVersions) {
+ Common::Rect backup = _clippingArea;
+ _clippingArea = clipping;
+ drawBevelSquareAlgClip(x, y, w, h, bevel, _bevelColor, _fgColor, Base::_fillMode != kFillDisabled);
+ _clippingArea = backup;
+ } else {
+ drawBevelSquareAlg(x, y, w, h, bevel, _bevelColor, _fgColor, Base::_fillMode != kFillDisabled);
+ }
+ }
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, const Common::Rect &textDrawableArea = Common::Rect(0, 0, 0, 0));
@@ -72,14 +89,24 @@ public:
void copyWholeFrame(OSystem *sys) { copyFrame(sys, Common::Rect(0, 0, _activeSurface->w, _activeSurface->h)); }
void fillSurface();
+ void fillSurfaceClip(Common::Rect clipping);
void blitSurface(const Graphics::Surface *source, const Common::Rect &r);
void blitSubSurface(const Graphics::Surface *source, const Common::Rect &r);
- void blitAlphaBitmap(const Graphics::Surface *source, const Common::Rect &r);
+ void blitSubSurfaceClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping);
+ void blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r);
+ void blitKeyBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping);
+ void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r,
+ GUI::ThemeEngine::AutoScaleMode autoscale = GUI::ThemeEngine::kAutoScaleNone,
+ Graphics::DrawStep::VectorAlignment xAlign = Graphics::DrawStep::kVectorAlignManual,
+ Graphics::DrawStep::VectorAlignment yAlign = Graphics::DrawStep::kVectorAlignManual,
+ int alpha = 255);
void applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle);
protected:
+ Common::Rect _clippingArea;
+
/**
* Draws a single pixel on the surface with the given coordinates and
* the given color.
@@ -119,6 +146,7 @@ protected:
* @param alpha Alpha intensity of the pixel (0-255)
*/
inline void blendPixelPtr(PixelType *ptr, PixelType color, uint8 alpha);
+ inline void blendPixelPtrClip(PixelType *ptr, PixelType color, uint8 alpha, int x, int y);
/**
* Blends a single pixel on the surface in the given pixel pointer, using supplied color
@@ -150,42 +178,76 @@ protected:
* @see VectorRendererAA::drawCircleAlg
*/
virtual void drawLineAlg(int x1, int y1, int x2, int y2,
- int dx, int dy, PixelType color);
+ uint dx, uint dy, PixelType color);
+
+ virtual void drawLineAlgClip(int x1, int y1, int x2, int y2,
+ uint dx, uint dy, PixelType color);
virtual void drawCircleAlg(int x, int y, int r,
PixelType color, FillMode fill_m);
+ virtual void drawCircleAlgClip(int x, int y, int r,
+ PixelType color, FillMode fill_m);
+
virtual void drawRoundedSquareAlg(int x1, int y1, int r, int w, int h,
PixelType color, FillMode fill_m);
+ virtual void drawRoundedSquareAlgClip(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 drawBorderRoundedSquareAlgClip(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 drawInteriorRoundedSquareAlgClip(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);
+ virtual void drawSquareAlgClip(int x, int y, int w, int h,
+ PixelType color, FillMode fill_m);
+
virtual void drawTriangleVertAlg(int x, int y, int w, int h,
bool inverted, PixelType color, FillMode fill_m);
+ virtual void drawTriangleVertAlgClip(int x, int y, int w, int h,
+ bool inverted, PixelType color, FillMode fill_m);
+
virtual void drawTriangleFast(int x, int y, int size,
bool inverted, PixelType color, FillMode fill_m);
virtual void drawBevelSquareAlg(int x, int y, int w, int h,
int bevel, PixelType top_color, PixelType bottom_color, bool fill);
+ virtual void drawBevelSquareAlgClip(int x, int y, int w, int h,
+ int bevel, PixelType top_color, PixelType bottom_color, bool fill);
+
virtual void drawTabAlg(int x, int y, int w, int h, int r,
PixelType color, VectorRenderer::FillMode fill_m,
int baseLeft = 0, int baseRight = 0);
+ virtual void drawTabAlgClip(int x, int y, int w, int h, int r,
+ 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 drawTabShadowClip(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);
+ virtual void drawBevelTabAlgClip(int x, int y, int w, int h,
+ int bevel, PixelType topColor, PixelType bottomColor,
+ int baseLeft = 0, int baseRight = 0);
+
/**
* SHADOW DRAWING ALGORITHMS
*
@@ -197,7 +259,9 @@ protected:
* @param offset Intensity/size of the shadow.
*/
virtual void drawSquareShadow(int x, int y, int w, int h, int offset);
+ virtual void drawSquareShadowClip(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);
+ virtual void drawRoundedSquareShadowClip(int x, int y, int r, int w, int h, int offset);
/**
* Calculates the color gradient on a given point.
@@ -212,6 +276,7 @@ protected:
void precalcGradient(int h);
void gradientFill(PixelType *first, int width, int x, int y);
+ void gradientFillClip(PixelType *first, int width, int x, int y, int realX, int realY);
/**
* Fills several pixels in a row with a given color and the specified alpha blending.
@@ -227,7 +292,20 @@ protected:
while (first != last) blendPixelPtr(first++, color, alpha);
}
+ inline void blendFillClip(PixelType *first, PixelType *last, PixelType color, uint8 alpha, int realX, int realY) {
+ if (_clippingArea.top <= realY && realY < _clippingArea.bottom) {
+ while (first != last) {
+ if (_clippingArea.left <= realX && realX < _clippingArea.right)
+ blendPixelPtr(first++, color, alpha);
+ else
+ ++first;
+ ++realX;
+ }
+ }
+ }
+
void darkenFill(PixelType *first, PixelType *last);
+ void darkenFillClip(PixelType *first, PixelType *last, int x, int y);
const PixelFormat _format;
const PixelType _redMask, _greenMask, _blueMask, _alphaMask;
@@ -278,7 +356,7 @@ protected:
*
* @see VectorRenderer::drawLineAlg()
*/
- virtual void drawLineAlg(int x1, int y1, int x2, int y2, int dx, int dy, PixelType color);
+ virtual void drawLineAlg(int x1, int y1, int x2, int y2, uint dx, uint dy, PixelType color);
/**
* "Wu's Circle Antialiasing Algorithm" as published by Xiaolin Wu, July 1991
diff --git a/graphics/font.cpp b/graphics/font.cpp
index dba48249bc..97662dc15d 100644
--- a/graphics/font.cpp
+++ b/graphics/font.cpp
@@ -21,6 +21,7 @@
*/
#include "graphics/font.h"
+#include "graphics/managed_surface.h"
#include "common/array.h"
#include "common/util.h"
@@ -264,6 +265,14 @@ int Font::getStringWidth(const Common::U32String &str) const {
return getStringWidthImpl(*this, str);
}
+void Font::drawChar(ManagedSurface *dst, uint32 chr, int x, int y, uint32 color) const {
+ drawChar(&dst->_innerSurface, chr, x, y, color);
+
+ Common::Rect charBox = getBoundingBox(chr);
+ charBox.translate(x, y);
+ dst->addDirtyRect(charBox);
+}
+
void Font::drawString(Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlign align, int deltax, bool useEllipsis) const {
Common::String renderStr = useEllipsis ? handleEllipsis(str, w) : str;
drawStringImpl(*this, dst, renderStr, x, y, w, color, align, deltax);
@@ -273,6 +282,20 @@ void Font::drawString(Surface *dst, const Common::U32String &str, int x, int y,
drawStringImpl(*this, dst, str, x, y, w, color, align, 0);
}
+void Font::drawString(ManagedSurface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlign align, int deltax, bool useEllipsis) const {
+ drawString(&dst->_innerSurface, str, x, y, w, color, align, deltax, useEllipsis);
+ if (w != 0) {
+ dst->addDirtyRect(getBoundingBox(str, x, y, w, align, deltax, useEllipsis));
+ }
+}
+
+void Font::drawString(ManagedSurface *dst, const Common::U32String &str, int x, int y, int w, uint32 color, TextAlign align) const {
+ drawString(&dst->_innerSurface, str, x, y, w, color, align);
+ if (w != 0) {
+ dst->addDirtyRect(getBoundingBox(str, x, y, w, align));
+ }
+}
+
int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array<Common::String> &lines) const {
return wordWrapTextImpl(*this, str, maxWidth, lines);
}
diff --git a/graphics/font.h b/graphics/font.h
index 35f6792d7f..0478608708 100644
--- a/graphics/font.h
+++ b/graphics/font.h
@@ -34,6 +34,7 @@ template<class T> class Array;
namespace Graphics {
struct Surface;
+class ManagedSurface;
/** Text alignment modes */
enum TextAlign {
@@ -141,10 +142,13 @@ public:
* @param color The color of the character.
*/
virtual void drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const = 0;
+ void drawChar(ManagedSurface *dst, uint32 chr, int x, int y, uint32 color) const;
// 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;
+ void drawString(ManagedSurface *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(ManagedSurface *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.
diff --git a/graphics/fonts/ttf.cpp b/graphics/fonts/ttf.cpp
index ba57613601..76b7f731be 100644
--- a/graphics/fonts/ttf.cpp
+++ b/graphics/fonts/ttf.cpp
@@ -33,11 +33,15 @@
#include "common/singleton.h"
#include "common/stream.h"
+#include "common/memstream.h"
#include "common/hashmap.h"
+#include "common/ptr.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
+#include FT_TRUETYPE_TABLES_H
+#include FT_TRUETYPE_TAGS_H
namespace Graphics {
@@ -47,6 +51,10 @@ inline int ftCeil26_6(FT_Pos x) {
return (x + 63) / 64;
}
+inline int divRoundToNearest(int dividend, int divisor) {
+ return (dividend + (divisor / 2)) / divisor;
+}
+
} // End of anonymous namespace
class TTFLibrary : public Common::Singleton<TTFLibrary> {
@@ -101,7 +109,7 @@ public:
TTFFont();
virtual ~TTFFont();
- bool load(Common::SeekableReadStream &stream, int size, uint dpi, TTFRenderMode renderMode, const uint32 *mapping);
+ bool load(Common::SeekableReadStream &stream, int size, TTFSizeMode sizeMode, uint dpi, TTFRenderMode renderMode, const uint32 *mapping);
virtual int getFontHeight() const;
@@ -137,6 +145,12 @@ private:
bool _allowLateCaching;
void assureCached(uint32 chr) const;
+ Common::SeekableReadStream *readTTFTable(FT_ULong tag) const;
+
+ int computePointSize(int size, TTFSizeMode sizeMode) const;
+ int readPointSizeFromVDMXTable(int height) const;
+ int computePointSizeFromHeaders(int height) const;
+
FT_Int32 _loadFlags;
FT_Render_Mode _renderMode;
bool _hasKerning;
@@ -162,7 +176,7 @@ TTFFont::~TTFFont() {
}
}
-bool TTFFont::load(Common::SeekableReadStream &stream, int size, uint dpi, TTFRenderMode renderMode, const uint32 *mapping) {
+bool TTFFont::load(Common::SeekableReadStream &stream, int size, TTFSizeMode sizeMode, uint dpi, TTFRenderMode renderMode, const uint32 *mapping) {
if (!g_ttf.isInitialized())
return false;
@@ -200,7 +214,7 @@ bool TTFFont::load(Common::SeekableReadStream &stream, int size, uint dpi, TTFRe
// Check whether we have kerning support
_hasKerning = (FT_HAS_KERNING(_face) != 0);
- if (FT_Set_Char_Size(_face, 0, size * 64, dpi, dpi)) {
+ if (FT_Set_Char_Size(_face, 0, computePointSize(size, sizeMode) * 64, dpi, dpi)) {
delete[] _ttfFile;
_ttfFile = 0;
@@ -262,6 +276,126 @@ bool TTFFont::load(Common::SeekableReadStream &stream, int size, uint dpi, TTFRe
return _initialized;
}
+int TTFFont::computePointSize(int size, TTFSizeMode sizeMode) const {
+ int ptSize = 0;
+ switch (sizeMode) {
+ case kTTFSizeModeCell: {
+ ptSize = readPointSizeFromVDMXTable(size);
+
+ if (ptSize == 0) {
+ ptSize = computePointSizeFromHeaders(size);
+ }
+
+ if (ptSize == 0) {
+ warning("Unable to compute point size for font '%s'", _face->family_name);
+ ptSize = 1;
+ }
+ break;
+ }
+ case kTTFSizeModeCharacter:
+ ptSize = size;
+ break;
+ }
+
+ return ptSize;
+}
+
+Common::SeekableReadStream *TTFFont::readTTFTable(FT_ULong tag) const {
+ // Find the required buffer size by calling the load function with nullptr
+ FT_ULong size = 0;
+ FT_Error err = FT_Load_Sfnt_Table(_face, tag, 0, nullptr, &size);
+ if (err) {
+ return nullptr;
+ }
+
+ byte *buf = (byte *)malloc(size);
+ if (!buf) {
+ return nullptr;
+ }
+
+ err = FT_Load_Sfnt_Table(_face, tag, 0, buf, &size);
+ if (err) {
+ free(buf);
+ return nullptr;
+ }
+
+ return new Common::MemoryReadStream(buf, size, DisposeAfterUse::YES);
+}
+
+int TTFFont::readPointSizeFromVDMXTable(int height) const {
+ // The Vertical Device Metrics table matches font heights with point sizes.
+ // FreeType does not expose it, we have to parse it ourselves.
+ // See https://www.microsoft.com/typography/otspec/vdmx.htm
+
+ Common::ScopedPtr<Common::SeekableReadStream> vdmxBuf(readTTFTable(TTAG_VDMX));
+ if (!vdmxBuf) {
+ return 0;
+ }
+
+ // Read the main header
+ vdmxBuf->skip(4); // Skip the version
+ uint16 numRatios = vdmxBuf->readUint16BE();
+
+ // Compute the starting position for the group table positions table
+ int32 offsetTableStart = vdmxBuf->pos() + 4 * numRatios;
+
+ // Search the ratio table for the 1:1 ratio, or the default record (0, 0, 0)
+ int32 selectedRatio = -1;
+ for (uint16 i = 0; i < numRatios; i++) {
+ vdmxBuf->skip(1); // Skip the charset subset
+ uint8 xRatio = vdmxBuf->readByte();
+ uint8 yRatio1 = vdmxBuf->readByte();
+ uint8 yRatio2 = vdmxBuf->readByte();
+
+ if ((xRatio == 1 && yRatio1 <= 1 && yRatio2 >= 1)
+ || (xRatio == 0 && yRatio1 == 0 && yRatio2 == 0)) {
+ selectedRatio = i;
+ break;
+ }
+ }
+ if (selectedRatio < 0) {
+ return 0;
+ }
+
+ // Read from group table positions table to get the group table offset
+ vdmxBuf->seek(offsetTableStart + sizeof(uint16) * selectedRatio);
+ uint16 groupOffset = vdmxBuf->readUint16BE();
+
+ // Read the group table header
+ vdmxBuf->seek(groupOffset);
+ uint16 numRecords = vdmxBuf->readUint16BE();
+ vdmxBuf->skip(2); // Skip the table bounds
+
+ // Search a record matching the required height
+ for (uint16 i = 0; i < numRecords; i++) {
+ uint16 pointSize = vdmxBuf->readUint16BE();
+ int16 yMax = vdmxBuf->readSint16BE();
+ int16 yMin = vdmxBuf->readSint16BE();
+
+ if (yMax + -yMin > height) {
+ return 0;
+ }
+ if (yMax + -yMin == height) {
+ return pointSize;
+ }
+ }
+
+ return 0;
+}
+
+int TTFFont::computePointSizeFromHeaders(int height) const {
+ TT_OS2 *os2Header = (TT_OS2 *)FT_Get_Sfnt_Table(_face, ft_sfnt_os2);
+ TT_HoriHeader *horiHeader = (TT_HoriHeader *)FT_Get_Sfnt_Table(_face, ft_sfnt_hhea);
+
+ if (os2Header && (os2Header->usWinAscent + os2Header->usWinDescent != 0)) {
+ return divRoundToNearest(_face->units_per_EM * height, os2Header->usWinAscent + os2Header->usWinDescent);
+ } else if (horiHeader && (horiHeader->Ascender + horiHeader->Descender != 0)) {
+ return divRoundToNearest(_face->units_per_EM * height, horiHeader->Ascender + horiHeader->Descender);
+ }
+
+ return 0;
+}
+
int TTFFont::getFontHeight() const {
return _height;
}
@@ -474,11 +608,11 @@ bool TTFFont::cacheGlyph(Glyph &glyph, uint32 chr) const {
switch (bitmap.pixel_mode) {
case FT_PIXEL_MODE_MONO:
- for (int y = 0; y < bitmap.rows; ++y) {
+ for (int y = 0; y < (int)bitmap.rows; ++y) {
const uint8 *curSrc = src;
uint8 mask = 0;
- for (int x = 0; x < bitmap.width; ++x) {
+ for (int x = 0; x < (int)bitmap.width; ++x) {
if ((x % 8) == 0)
mask = *curSrc++;
@@ -494,7 +628,7 @@ bool TTFFont::cacheGlyph(Glyph &glyph, uint32 chr) const {
break;
case FT_PIXEL_MODE_GRAY:
- for (int y = 0; y < bitmap.rows; ++y) {
+ for (int y = 0; y < (int)bitmap.rows; ++y) {
memcpy(dst, src, bitmap.width);
dst += glyph.image.pitch;
src += srcPitch;
@@ -521,10 +655,10 @@ void TTFFont::assureCached(uint32 chr) const {
}
}
-Font *loadTTFFont(Common::SeekableReadStream &stream, int size, uint dpi, TTFRenderMode renderMode, const uint32 *mapping) {
+Font *loadTTFFont(Common::SeekableReadStream &stream, int size, TTFSizeMode sizeMode, uint dpi, TTFRenderMode renderMode, const uint32 *mapping) {
TTFFont *font = new TTFFont();
- if (!font->load(stream, size, dpi, renderMode, mapping)) {
+ if (!font->load(stream, size, sizeMode, dpi, renderMode, mapping)) {
delete font;
return 0;
}
diff --git a/graphics/fonts/ttf.h b/graphics/fonts/ttf.h
index bd25b69f21..4110486357 100644
--- a/graphics/fonts/ttf.h
+++ b/graphics/fonts/ttf.h
@@ -56,10 +56,32 @@ enum TTFRenderMode {
};
/**
+ * This specifies how the font size is defined.
+ */
+enum TTFSizeMode {
+ /**
+ * Character height only.
+ *
+ * This matches rendering obtained when calling
+ * CreateFont in Windows with negative height values.
+ */
+ kTTFSizeModeCharacter,
+
+ /**
+ * Full cell height.
+ *
+ * This matches rendering obtained when calling
+ * CreateFont in Windows with positive height values.
+ */
+ kTTFSizeModeCell
+};
+
+/**
* Loads a TTF font file from a given data stream object.
*
* @param stream Stream object to load font data from.
* @param size The point size to load.
+ * @param sizeMode The point size definition used for the size parameter.
* @param dpi The dpi to use for size calculations, by default 72dpi
* are used.
* @param renderMode FreeType2 mode used to render glyphs. @see TTFRenderMode
@@ -71,7 +93,7 @@ enum TTFRenderMode {
* supported.
* @return 0 in case loading fails, otherwise a pointer to the Font object.
*/
-Font *loadTTFFont(Common::SeekableReadStream &stream, int size, uint dpi = 0, TTFRenderMode renderMode = kTTFRenderModeLight, const uint32 *mapping = 0);
+Font *loadTTFFont(Common::SeekableReadStream &stream, int size, TTFSizeMode sizeMode = kTTFSizeModeCharacter, uint dpi = 0, TTFRenderMode renderMode = kTTFRenderModeLight, const uint32 *mapping = 0);
void shutdownTTF();
diff --git a/graphics/macgui/macmenu.cpp b/graphics/macgui/macmenu.cpp
new file mode 100644
index 0000000000..a78fa2e6ce
--- /dev/null
+++ b/graphics/macgui/macmenu.cpp
@@ -0,0 +1,583 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "common/system.h"
+#include "common/keyboard.h"
+
+#include "graphics/primitives.h"
+#include "graphics/font.h"
+#include "graphics/macgui/macwindowmanager.h"
+#include "graphics/macgui/macwindow.h"
+#include "graphics/macgui/macmenu.h"
+
+namespace Graphics {
+
+enum {
+ kMenuHeight = 20,
+ kMenuLeftMargin = 7,
+ kMenuSpacing = 13,
+ kMenuPadding = 16,
+ kMenuDropdownPadding = 14,
+ kMenuDropdownItemHeight = 16,
+ kMenuItemHeight = 20
+};
+
+enum {
+ kMenuHighLevel = -1
+};
+
+enum {
+ kFontStyleBold = 1,
+ kFontStyleItalic = 2,
+ kFontStyleUnderline = 4,
+ kFontStyleOutline = 8,
+ kFontStyleShadow = 16,
+ kFontStyleCondensed = 32,
+ kFontStyleExtended = 64
+};
+
+enum {
+ kMenuActionCommand
+};
+
+
+struct MenuSubItem {
+ Common::String text;
+ int action;
+ int style;
+ char shortcut;
+ bool enabled;
+ Common::Rect bbox;
+
+ MenuSubItem(const char *t, int a, int s = 0, char sh = 0, bool e = true) : text(t), action(a), style(s), shortcut(sh), enabled(e) {}
+};
+
+typedef Common::Array<MenuSubItem *> SubItemArray;
+
+struct MenuItem {
+ Common::String name;
+ SubItemArray subitems;
+ Common::Rect bbox;
+ Common::Rect subbbox;
+
+ MenuItem(const char *n) : name(n) {}
+};
+
+Menu::Menu(int id, const Common::Rect &bounds, MacWindowManager *wm)
+ : BaseMacWindow(id, false, wm) {
+ _font = getMenuFont();
+
+ _screen.create(bounds.width(), bounds.height(), PixelFormat::createFormatCLUT8());
+
+ _bbox.left = 0;
+ _bbox.top = 0;
+ _bbox.right = _screen.w;
+ _bbox.bottom = kMenuHeight;
+
+ _menuActivated = false;
+ _activeItem = -1;
+ _activeSubItem = -1;
+
+ _ccallback = NULL;
+ _cdata = NULL;
+
+ _tempSurface.create(_screen.w, _font->getFontHeight(), PixelFormat::createFormatCLUT8());
+}
+
+Menu::~Menu() {
+ for (uint i = 0; i < _items.size(); i++) {
+ for (uint j = 0; j < _items[i]->subitems.size(); j++)
+ delete _items[i]->subitems[j];
+ delete _items[i];
+ }
+}
+
+void Menu::addStaticMenus(const MenuData *data) {
+ MenuItem *about = new MenuItem(_wm->hasBuiltInFonts() ? "\xa9" : "\xf0"); // (c) Symbol as the most resembling apple
+ _items.push_back(about);
+
+ for (int i = 0; data[i].menunum; i++) {
+ const MenuData *m = &data[i];
+
+ if (m->menunum == kMenuHighLevel) {
+ MenuItem *item = new MenuItem(m->title);
+ _items.push_back(item);
+
+ continue;
+ }
+
+ _items[m->menunum]->subitems.push_back(new MenuSubItem(m->title, m->action, 0, m->shortcut, m->enabled));
+ }
+}
+
+int Menu::addMenuItem(const char *name) {
+ MenuItem *i = new MenuItem(name);
+ _items.push_back(i);
+
+ return _items.size() - 1;
+}
+
+void Menu::addMenuSubItem(int id, const char *text, int action, int style, char shortcut, bool enabled) {
+ _items[id]->subitems.push_back(new MenuSubItem(text, action, style, shortcut, enabled));
+
+ calcMenuBounds(_items[id]);
+}
+
+void Menu::calcDimensions() {
+ // Calculate menu dimensions
+ int y = 1;
+ int x = 18;
+
+ for (uint i = 0; i < _items.size(); i++) {
+ int w = _font->getStringWidth(_items[i]->name);
+
+ if (_items[i]->bbox.bottom == 0) {
+ _items[i]->bbox.left = x - kMenuLeftMargin;
+ _items[i]->bbox.top = y;
+ _items[i]->bbox.right = x + w + kMenuSpacing - kMenuLeftMargin;
+ _items[i]->bbox.bottom = y + _font->getFontHeight() + (_wm->hasBuiltInFonts() ? 3 : 2);
+ }
+
+ calcMenuBounds(_items[i]);
+
+ x += w + kMenuSpacing;
+ }
+}
+
+void Menu::clearSubMenu(int id) {
+ MenuItem *menu = _items[id];
+
+ for (uint j = 0; j < menu->subitems.size(); j++)
+ delete menu->subitems[j];
+
+ menu->subitems.clear();
+}
+
+void Menu::createSubMenuFromString(int id, const char *str) {
+ clearSubMenu(id);
+
+ MenuItem *menu = _items[id];
+ Common::String string(str);
+
+ Common::String item;
+
+ for (uint i = 0; i < string.size(); i++) {
+ while(i < string.size() && string[i] != ';') // Read token
+ item += string[i++];
+
+ if (item == "(-") {
+ menu->subitems.push_back(new MenuSubItem(NULL, 0));
+ } else {
+ bool enabled = true;
+ int style = 0;
+ char shortcut = 0;
+ const char *shortPtr = strrchr(item.c_str(), '/');
+ if (shortPtr != NULL) {
+ if (strlen(shortPtr) >= 2) {
+ shortcut = shortPtr[1];
+ item.deleteChar(shortPtr - item.c_str());
+ item.deleteChar(shortPtr - item.c_str());
+ } else {
+ error("Unexpected shortcut: '%s', item '%s' in menu '%s'", shortPtr, item.c_str(), string.c_str());
+ }
+ }
+
+ while (item.size() >= 2 && item[item.size() - 2] == '<') {
+ char c = item.lastChar();
+ if (c == 'B') {
+ style |= kFontStyleBold;
+ } else if (c == 'I') {
+ style |= kFontStyleItalic;
+ } else if (c == 'U') {
+ style |= kFontStyleUnderline;
+ } else if (c == 'O') {
+ style |= kFontStyleOutline;
+ } else if (c == 'S') {
+ style |= kFontStyleShadow;
+ } else if (c == 'C') {
+ style |= kFontStyleCondensed;
+ } else if (c == 'E') {
+ style |= kFontStyleExtended;
+ }
+ item.deleteLastChar();
+ item.deleteLastChar();
+ }
+
+ Common::String tmpitem(item);
+ tmpitem.trim();
+ if (tmpitem.size() > 0 && tmpitem[0] == '(') {
+ enabled = false;
+
+ for (uint j = 0; j < item.size(); j++)
+ if (item[j] == '(') {
+ item.deleteChar(j);
+ break;
+ }
+ }
+
+ menu->subitems.push_back(new MenuSubItem(item.c_str(), kMenuActionCommand, style, shortcut, enabled));
+ }
+
+ item.clear();
+ }
+
+ calcMenuBounds(menu);
+}
+
+const Font *Menu::getMenuFont() {
+ return _wm->getFont("Chicago-12", FontManager::kBigGUIFont);
+}
+
+const char *Menu::getAcceleratorString(MenuSubItem *item, const char *prefix) {
+ static char res[20];
+ *res = 0;
+
+ if (item->shortcut != 0)
+ sprintf(res, "%s%c%c", prefix, (_wm->hasBuiltInFonts() ? '^' : '\x11'), item->shortcut);
+
+ return res;
+}
+
+int Menu::calculateMenuWidth(MenuItem *menu) {
+ int maxWidth = 0;
+ for (uint i = 0; i < menu->subitems.size(); i++) {
+ MenuSubItem *item = menu->subitems[i];
+ if (!item->text.empty()) {
+ Common::String text(item->text);
+ Common::String acceleratorText(getAcceleratorString(item, " "));
+ if (!acceleratorText.empty()) {
+ text += acceleratorText;
+ }
+
+ int width = _font->getStringWidth(text);
+ if (width > maxWidth) {
+ maxWidth = width;
+ }
+ }
+ }
+ return maxWidth;
+}
+
+void Menu::calcMenuBounds(MenuItem *menu) {
+ // TODO: cache maxWidth
+ int maxWidth = calculateMenuWidth(menu);
+ int x1 = menu->bbox.left - 1;
+ int y1 = menu->bbox.bottom + 1;
+ int x2 = x1 + maxWidth + kMenuDropdownPadding * 2 - 4;
+ int y2 = y1 + menu->subitems.size() * kMenuDropdownItemHeight + 2;
+
+ menu->subbbox.left = x1;
+ menu->subbbox.top = y1;
+ menu->subbbox.right = x2;
+ menu->subbbox.bottom = y2;
+}
+
+static void drawPixelPlain(int x, int y, int color, void *data) {
+ ManagedSurface *surface = (ManagedSurface *)data;
+
+ if (x >= 0 && x < surface->w && y >= 0 && y < surface->h)
+ *((byte *)surface->getBasePtr(x, y)) = (byte)color;
+}
+
+static void drawFilledRoundRect(ManagedSurface *surface, Common::Rect &rect, int arc, int color) {
+ drawRoundRect(rect, arc, color, true, drawPixelPlain, surface);
+}
+
+bool Menu::draw(ManagedSurface *g, bool forceRedraw) {
+ Common::Rect r(_bbox);
+
+ if (!_contentIsDirty && !forceRedraw)
+ return false;
+
+ _contentIsDirty = false;
+
+ _screen.clear(kColorGreen);
+
+ drawFilledRoundRect(&_screen, r, kDesktopArc, kColorWhite);
+ r.top = 7;
+ _screen.fillRect(r, kColorWhite);
+ r.top = kMenuHeight - 1;
+ r.bottom++;
+ _screen.fillRect(r, kColorGreen);
+ r.bottom--;
+ _screen.fillRect(r, kColorBlack);
+
+ for (uint i = 0; i < _items.size(); i++) {
+ int color = kColorBlack;
+ MenuItem *it = _items[i];
+
+ if ((uint)_activeItem == i) {
+ Common::Rect hbox = it->bbox;
+
+ hbox.left -= 1;
+ hbox.right += 3;
+ hbox.bottom += 1;
+
+ _screen.fillRect(hbox, kColorBlack);
+ color = kColorWhite;
+
+ if (!it->subitems.empty())
+ renderSubmenu(it);
+ }
+
+ _font->drawString(&_screen, it->name, it->bbox.left + kMenuLeftMargin, it->bbox.top + (_wm->hasBuiltInFonts() ? 2 : 1), it->bbox.width(), color);
+ }
+
+ g->transBlitFrom(_screen, kColorGreen);
+
+ g_system->copyRectToScreen(g->getPixels(), g->pitch, 0, 0, g->w, g->h);
+
+ return true;
+}
+
+void Menu::renderSubmenu(MenuItem *menu) {
+ Common::Rect *r = &menu->subbbox;
+
+ if (r->width() == 0 || r->height() == 0)
+ return;
+
+ _screen.fillRect(*r, kColorWhite);
+ _screen.frameRect(*r, kColorBlack);
+ _screen.vLine(r->right, r->top + 3, r->bottom + 1, kColorBlack);
+ _screen.vLine(r->right + 1, r->top + 3, r->bottom + 1, kColorBlack);
+ _screen.hLine(r->left + 3, r->bottom, r->right + 1, kColorBlack);
+ _screen.hLine(r->left + 3, r->bottom + 1, r->right + 1, kColorBlack);
+
+ int x = r->left + kMenuDropdownPadding;
+ int y = r->top + 1;
+ for (uint i = 0; i < menu->subitems.size(); i++) {
+ Common::String text(menu->subitems[i]->text);
+ Common::String acceleratorText(getAcceleratorString(menu->subitems[i], ""));
+ int accelX = r->right - 25;
+
+ int color = kColorBlack;
+ if (i == (uint)_activeSubItem && !text.empty() && menu->subitems[i]->enabled) {
+ color = kColorWhite;
+ Common::Rect trect(r->left, y - (_wm->hasBuiltInFonts() ? 1 : 0), r->right, y + _font->getFontHeight());
+
+ _screen.fillRect(trect, kColorBlack);
+ }
+
+ if (!text.empty()) {
+ ManagedSurface *s = &_screen;
+ int tx = x, ty = y;
+
+ if (!menu->subitems[i]->enabled) {
+ s = &_tempSurface;
+ tx = 0;
+ ty = 0;
+ accelX -= x;
+
+ _tempSurface.clear(kColorGreen);
+ }
+
+ _font->drawString(s, text, tx, ty, r->width(), color);
+
+ if (!acceleratorText.empty())
+ _font->drawString(s, acceleratorText, accelX, ty, r->width(), color);
+
+ if (!menu->subitems[i]->enabled) {
+ // I am lazy to extend drawString() with plotProc as a parameter, so
+ // fake it here
+ for (int ii = 0; ii < _tempSurface.h; ii++) {
+ const byte *src = (const byte *)_tempSurface.getBasePtr(0, ii);
+ byte *dst = (byte *)_screen.getBasePtr(x, y+ii);
+ byte pat = _wm->getPatterns()[kPatternCheckers2 - 1][ii % 8];
+ for (int j = 0; j < r->width(); j++) {
+ if (*src != kColorGreen && (pat & (1 << (7 - (x + j) % 8))))
+ *dst = *src;
+ src++;
+ dst++;
+ }
+ }
+ }
+ } else { // Delimiter
+ bool flip = r->left & 2;
+ byte *ptr = (byte *)_screen.getBasePtr(r->left + 1, y + kMenuDropdownItemHeight / 2);
+ for (int xx = r->left + 1; xx <= r->right - 1; xx++, ptr++) {
+ *ptr = flip ? kColorBlack : kColorWhite;
+ flip = !flip;
+ }
+ }
+
+ y += kMenuDropdownItemHeight;
+ }
+
+ _contentIsDirty = true;
+ //g_system->copyRectToScreen(_screen.getBasePtr(r->left, r->top), _screen.pitch, r->left, r->top, r->width() + 2, r->height() + 2);
+}
+
+bool Menu::processEvent(Common::Event &event) {
+ switch (event.type) {
+ case Common::EVENT_KEYDOWN:
+ return keyEvent(event);
+ case Common::EVENT_LBUTTONDOWN:
+ return mouseClick(event.mouse.x, event.mouse.y);
+ case Common::EVENT_LBUTTONUP:
+ return mouseRelease(event.mouse.x, event.mouse.y);
+ case Common::EVENT_MOUSEMOVE:
+ return mouseMove(event.mouse.x, event.mouse.y);
+ default:
+ return false;
+ }
+}
+
+bool Menu::keyEvent(Common::Event &event) {
+ if (event.type != Common::EVENT_KEYDOWN)
+ return false;
+
+ if (event.kbd.flags & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_META)) {
+ if (event.kbd.ascii >= 0x20 && event.kbd.ascii <= 0x7f) {
+ return processMenuShortCut(event.kbd.flags, event.kbd.ascii);
+ }
+ }
+
+ return false;
+}
+
+bool Menu::mouseClick(int x, int y) {
+ if (_bbox.contains(x, y)) {
+ for (uint i = 0; i < _items.size(); i++)
+ if (_items[i]->bbox.contains(x, y)) {
+ if ((uint)_activeItem == i)
+ return false;
+
+ if (_activeItem != -1) { // Restore background
+ Common::Rect r(_items[_activeItem]->subbbox);
+ r.right += 3;
+ r.bottom += 3;
+
+ _wm->setFullRefresh(true);
+ }
+
+ _activeItem = i;
+ _activeSubItem = -1;
+ _menuActivated = true;
+
+ _contentIsDirty = true;
+
+ return true;
+ }
+ } else if (_menuActivated && _items[_activeItem]->subbbox.contains(x, y)) {
+ MenuItem *it = _items[_activeItem];
+ int numSubItem = (y - it->subbbox.top) / kMenuDropdownItemHeight;
+
+ if (numSubItem != _activeSubItem) {
+ _activeSubItem = numSubItem;
+
+ renderSubmenu(_items[_activeItem]);
+ _contentIsDirty = true;
+ }
+ } else if (_menuActivated && _activeItem != -1) {
+ _activeSubItem = -1;
+
+ renderSubmenu(_items[_activeItem]);
+ _contentIsDirty = true;
+ }
+
+ return false;
+}
+
+bool Menu::mouseMove(int x, int y) {
+ if (_menuActivated)
+ if (mouseClick(x, y))
+ return true;
+
+ return false;
+}
+
+bool Menu::mouseRelease(int x, int y) {
+ if (_menuActivated) {
+ _menuActivated = false;
+
+ if (_activeItem != -1 && _activeSubItem != -1 && _items[_activeItem]->subitems[_activeSubItem]->enabled)
+ (*_ccallback)(_items[_activeItem]->subitems[_activeSubItem]->action,
+ _items[_activeItem]->subitems[_activeSubItem]->text, _cdata);
+
+ _activeItem = -1;
+ _activeSubItem = -1;
+
+ _wm->setFullRefresh(true);
+
+ return true;
+ }
+
+ return false;
+}
+
+bool Menu::processMenuShortCut(byte flags, uint16 ascii) {
+ ascii = tolower(ascii);
+
+ if (flags & (Common::KBD_CTRL | Common::KBD_META)) {
+ for (uint i = 0; i < _items.size(); i++)
+ for (uint j = 0; j < _items[i]->subitems.size(); j++)
+ if (_items[i]->subitems[j]->enabled && tolower(_items[i]->subitems[j]->shortcut) == ascii) {
+ (*_ccallback)(_items[i]->subitems[j]->action, _items[i]->subitems[j]->text, _cdata);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Menu::enableCommand(int menunum, int action, bool state) {
+ for (uint i = 0; i < _items[menunum]->subitems.size(); i++)
+ if (_items[menunum]->subitems[i]->action == action)
+ _items[menunum]->subitems[i]->enabled = state;
+
+ _contentIsDirty = true;
+}
+
+void Menu::disableAllMenus() {
+ for (uint i = 1; i < _items.size(); i++) // Leave About menu on
+ for (uint j = 0; j < _items[i]->subitems.size(); j++)
+ _items[i]->subitems[j]->enabled = false;
+
+ _contentIsDirty = true;
+}
+
+} // End of namespace Wage
diff --git a/graphics/macgui/macmenu.h b/graphics/macgui/macmenu.h
new file mode 100644
index 0000000000..7114066ae6
--- /dev/null
+++ b/graphics/macgui/macmenu.h
@@ -0,0 +1,123 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef GRAPHICS_MACGUI_MACMENU_H
+#define GRAPHICS_MACGUI_MACMENU_H
+
+namespace Graphics {
+
+struct MenuItem;
+struct MenuSubItem;
+
+struct MenuData {
+ int menunum;
+ const char *title;
+ int action;
+ byte shortcut;
+ bool enabled;
+};
+
+class Menu : public BaseMacWindow {
+public:
+ Menu(int id, const Common::Rect &bounds, MacWindowManager *wm);
+ ~Menu();
+
+ void setCommandsCallback(void (*callback)(int, Common::String &, void *), void *data) { _ccallback = callback; _cdata = data; }
+
+ void addStaticMenus(const MenuData *data);
+ void calcDimensions();
+
+ int addMenuItem(const char *name);
+ void addMenuSubItem(int id, const char *text, int action, int style = 0, char shortcut = 0, bool enabled = true);
+ void createSubMenuFromString(int id, const char *string);
+ void clearSubMenu(int id);
+
+ bool draw(ManagedSurface *g, bool forceRedraw = false);
+ bool processEvent(Common::Event &event);
+
+ void enableCommand(int menunum, int action, bool state);
+ void disableAllMenus();
+
+ void setActive(bool active) { _menuActivated = active; }
+ bool hasAllFocus() { return _menuActivated; }
+
+ Common::Rect _bbox;
+
+private:
+ ManagedSurface _screen;
+ ManagedSurface _tempSurface;
+
+private:
+ const Font *getMenuFont();
+ const char *getAcceleratorString(MenuSubItem *item, const char *prefix);
+ int calculateMenuWidth(MenuItem *menu);
+ void calcMenuBounds(MenuItem *menu);
+ void renderSubmenu(MenuItem *menu);
+
+ bool keyEvent(Common::Event &event);
+ bool mouseClick(int x, int y);
+ bool mouseRelease(int x, int y);
+ bool mouseMove(int x, int y);
+
+ bool processMenuShortCut(byte flags, uint16 ascii);
+
+ Common::Array<MenuItem *> _items;
+
+ const Font *_font;
+
+ bool _menuActivated;
+
+ int _activeItem;
+ int _activeSubItem;
+
+ void (*_ccallback)(int action, Common::String &text, void *data);
+ void *_cdata;
+};
+
+} // End of namespace Graphics
+
+#endif
diff --git a/graphics/macgui/macwindow.cpp b/graphics/macgui/macwindow.cpp
new file mode 100644
index 0000000000..dbb600ba82
--- /dev/null
+++ b/graphics/macgui/macwindow.cpp
@@ -0,0 +1,496 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "graphics/font.h"
+#include "graphics/primitives.h"
+#include "common/events.h"
+#include "graphics/macgui/macwindowmanager.h"
+#include "graphics/macgui/macwindow.h"
+#include "image/bmp.h"
+
+namespace Graphics {
+
+BaseMacWindow::BaseMacWindow(int id, bool editable, MacWindowManager *wm) :
+ _id(id), _editable(editable), _wm(wm) {
+ _callback = 0;
+ _dataPtr = 0;
+
+ _contentIsDirty = true;
+
+ _type = kWindowUnknown;
+}
+
+MacWindow::MacWindow(int id, bool scrollable, bool resizable, bool editable, MacWindowManager *wm) :
+ BaseMacWindow(id, editable, wm), _scrollable(scrollable), _resizable(resizable) {
+ _active = false;
+ _borderIsDirty = true;
+
+ _highlightedPart = kBorderNone;
+
+ _scrollPos = _scrollSize = 0.0;
+
+ _beingDragged = false;
+ _beingResized = false;
+
+ _draggedX = _draggedY = 0;
+
+ _type = kWindowWindow;
+
+ _closeable = false;
+
+ _borderWidth = kBorderWidth;
+}
+
+MacWindow::~MacWindow() {
+}
+
+const Font *MacWindow::getTitleFont() {
+ return _wm->getFont("Chicago-12", FontManager::kBigGUIFont);
+}
+
+void MacWindow::setActive(bool active) {
+ if (active == _active)
+ return;
+
+ _active = active;
+ _borderIsDirty = true;
+}
+
+bool MacWindow::isActive() { return _active; }
+
+void MacWindow::resize(int w, int h) {
+ if (_surface.w == w && _surface.h == h)
+ return;
+
+ _surface.free();
+ _surface.create(w, h, PixelFormat::createFormatCLUT8());
+ _borderSurface.free();
+ _borderSurface.create(w, h, PixelFormat::createFormatCLUT8());
+ _composeSurface.free();
+ _composeSurface.create(w, h, PixelFormat::createFormatCLUT8());
+
+ _dims.setWidth(w);
+ _dims.setHeight(h);
+
+ updateInnerDims();
+
+ _contentIsDirty = true;
+ _borderIsDirty = true;
+}
+
+void MacWindow::move(int x, int y) {
+ if (_dims.left == x && _dims.top == y)
+ return;
+
+ _dims.moveTo(x, y);
+ updateInnerDims();
+
+ _contentIsDirty = true;
+}
+
+void MacWindow::setDimensions(const Common::Rect &r) {
+ resize(r.width(), r.height());
+ _dims.moveTo(r.left, r.top);
+ updateInnerDims();
+
+ _contentIsDirty = true;
+}
+
+bool MacWindow::draw(ManagedSurface *g, bool forceRedraw) {
+ if (!_borderIsDirty && !_contentIsDirty && !forceRedraw)
+ return false;
+
+ if (_borderIsDirty || forceRedraw)
+ drawBorder();
+
+ _contentIsDirty = false;
+
+ // Compose
+ _composeSurface.blitFrom(_surface, Common::Rect(0, 0, _surface.w - 2, _surface.h - 2), Common::Point(2, 2));
+ _composeSurface.transBlitFrom(_borderSurface, kColorGreen);
+
+ g->transBlitFrom(_composeSurface, _composeSurface.getBounds(), Common::Point(_dims.left - 2, _dims.top - 2), kColorGreen2);
+
+ return true;
+}
+
+
+#define ARROW_W 12
+#define ARROW_H 6
+const int arrowPixels[ARROW_H][ARROW_W] = {
+ {0,0,0,0,0,1,1,0,0,0,0,0},
+ {0,0,0,0,1,1,1,1,0,0,0,0},
+ {0,0,0,1,1,1,1,1,1,0,0,0},
+ {0,0,1,1,1,1,1,1,1,1,0,0},
+ {0,1,1,1,1,1,1,1,1,1,1,0},
+ {1,1,1,1,1,1,1,1,1,1,1,1}};
+
+static void drawPixelInverted(int x, int y, int color, void *data) {
+ ManagedSurface *surface = (ManagedSurface *)data;
+
+ if (x >= 0 && x < surface->w && y >= 0 && y < surface->h) {
+ byte *p = (byte *)surface->getBasePtr(x, y);
+
+ *p = *p == kColorWhite ? kColorBlack : kColorWhite;
+ }
+}
+
+void MacWindow::updateInnerDims() {
+ if (_macBorder.hasBorder(_active) && _macBorder.hasOffsets()) {
+ _innerDims = Common::Rect(
+ _dims.left + _macBorder.getOffset(kBorderOffsetLeft),
+ _dims.top + _macBorder.getOffset(kBorderOffsetTop),
+ _dims.right - _macBorder.getOffset(kBorderOffsetRight),
+ _dims.bottom - _macBorder.getOffset(kBorderOffsetBottom));
+ } else {
+ _innerDims = _dims;
+ _innerDims.grow(-kBorderWidth);
+ }
+}
+
+void MacWindow::drawBorder() {
+ _borderIsDirty = false;
+
+ ManagedSurface *g = &_borderSurface;
+
+ if (_macBorder.hasBorder(_active)) {
+ drawBorderFromSurface(g);
+ } else {
+ drawSimpleBorder(g);
+ }
+}
+
+void MacWindow::prepareBorderSurface(ManagedSurface *g) {
+ // We draw rect with outer kColorGreen2 and inner kColorGreen, so on 2 passes we cut out
+ // scene by external shape of the border
+ int sz = kBorderWidth / 2;
+ int width = g->w;
+ int height = g->h;
+ g->clear(kColorGreen2);
+ g->fillRect(Common::Rect(sz, sz, width - sz, height - sz), kColorGreen);
+}
+
+void MacWindow::drawBorderFromSurface(ManagedSurface *g) {
+ g->clear(kColorGreen2);
+ Common::Rect inside = _innerDims;
+ inside.moveTo(_macBorder.getOffset(kBorderOffsetLeft), _macBorder.getOffset(kBorderOffsetTop));
+ g->fillRect(inside, kColorGreen);
+
+ _macBorder.blitBorderInto(_borderSurface, _active);
+}
+
+void MacWindow::drawSimpleBorder(ManagedSurface *g) {
+
+ bool active = _active, scrollable = _scrollable, closeable = _active, drawTitle = !_title.empty();
+ const int size = kBorderWidth;
+ int x = 0;
+ int y = 0;
+ int width = _borderSurface.w;
+ int height = _borderSurface.h;
+
+ prepareBorderSurface(g);
+
+ drawBox(g, x, y, size, size);
+ drawBox(g, x + width - size - 1, y, size, size);
+ drawBox(g, x + width - size - 1, y + height - size - 1, size, size);
+ drawBox(g, x, y + height - size - 1, size, size);
+ drawBox(g, x + size, y + 2, width - 2 * size - 1, size - 4);
+ drawBox(g, x + size, y + height - size + 1, width - 2 * size - 1, size - 4);
+ drawBox(g, x + 2, y + size, size - 4, height - 2 * size - 1);
+ drawBox(g, x + width - size + 1, y + size, size - 4, height - 2 * size - 1);
+
+ if (active) {
+ fillRect(g, x + size, y + 5, width - 2 * size - 1, 8, kColorBlack);
+ fillRect(g, x + size, y + height - 13, width - 2 * size - 1, 8, kColorBlack);
+ fillRect(g, x + 5, y + size, 8, height - 2 * size - 1, kColorBlack);
+ if (!scrollable) {
+ fillRect(g, x + width - 13, y + size, 8, height - 2 * size - 1, kColorBlack);
+ } else {
+ int x1 = x + width - 15;
+ int y1 = y + size + 1;
+
+ for (int yy = 0; yy < ARROW_H; yy++) {
+ for (int xx = 0; xx < ARROW_W; xx++)
+ g->hLine(x1 + xx, y1 + yy, x1 + xx, (arrowPixels[yy][xx] != 0 ? kColorBlack : kColorWhite));
+ }
+
+ fillRect(g, x + width - 13, y + size + ARROW_H, 8, height - 2 * size - 1 - ARROW_H * 2, kColorBlack);
+
+ y1 += height - 2 * size - ARROW_H - 2;
+ for (int yy = 0; yy < ARROW_H; yy++) {
+ for (int xx = 0; xx < ARROW_W; xx++)
+ g->hLine(x1 + xx, y1 + yy, x1 + xx, (arrowPixels[ARROW_H - yy - 1][xx] != 0 ? kColorBlack : kColorWhite));
+ }
+
+ if (_highlightedPart == kBorderScrollUp || _highlightedPart == kBorderScrollDown) {
+ int rx1 = x + width - kBorderWidth + 2;
+ int ry1 = y + size + _dims.height() * _scrollPos;
+ int rx2 = rx1 + size - 4;
+ int ry2 = ry1 + _dims.height() * _scrollSize;
+ Common::Rect rr(rx1, ry1, rx2, ry2);
+
+ Graphics::drawFilledRect(rr, kColorBlack, drawPixelInverted, g);
+ }
+ }
+ if (closeable) {
+ if (_highlightedPart == kBorderCloseButton) {
+ fillRect(g, x + 6, y + 6, 6, 6, kColorBlack);
+ } else {
+ drawBox(g, x + 5, y + 5, 7, 7);
+ }
+ }
+ }
+
+ if (drawTitle) {
+ const Graphics::Font *font = getTitleFont();
+ int yOff = _wm->hasBuiltInFonts() ? 3 : 1;
+
+ int w = font->getStringWidth(_title) + 10;
+ int maxWidth = width - size * 2 - 7;
+ if (w > maxWidth)
+ w = maxWidth;
+ drawBox(g, x + (width - w) / 2, y, w, size);
+ font->drawString(g, _title, x + (width - w) / 2 + 5, y + yOff, w, kColorBlack);
+ }
+}
+
+void MacWindow::setHighlight(WindowClick highlightedPart) {
+ if (_highlightedPart == highlightedPart)
+ return;
+
+ _highlightedPart = highlightedPart;
+ _borderIsDirty = true;
+}
+
+void MacWindow::setScroll(float scrollPos, float scrollSize) {
+ if (_scrollPos == scrollPos && _scrollSize == scrollSize)
+ return;
+
+ _scrollPos = scrollPos;
+ _scrollSize = scrollSize;
+ _borderIsDirty = true;
+}
+
+void MacWindow::loadBorder(Common::SeekableReadStream &file, bool active, int lo, int ro, int to, int bo) {
+ Image::BitmapDecoder bmpDecoder;
+ Graphics::Surface source;
+ Graphics::TransparentSurface *surface = new Graphics::TransparentSurface();
+
+ bmpDecoder.loadStream(file);
+ source = *(bmpDecoder.getSurface());
+
+ source.convertToInPlace(surface->getSupportedPixelFormat(), bmpDecoder.getPalette());
+ surface->create(source.w, source.h, source.format);
+ surface->copyFrom(source);
+ surface->applyColorKey(255, 0, 255, false);
+
+ if (active)
+ _macBorder.addActiveBorder(*surface);
+ else
+ _macBorder.addInactiveBorder(*surface);
+
+ if (!_macBorder.hasOffsets())
+ _macBorder.setOffsets(lo, ro, to, bo);
+
+ updateInnerDims();
+}
+
+void MacWindow::setCloseable(bool closeable) {
+ _closeable = closeable;
+}
+
+void MacWindow::drawBox(ManagedSurface *g, int x, int y, int w, int h) {
+ Common::Rect r(x, y, x + w + 1, y + h + 1);
+
+ g->fillRect(r, kColorWhite);
+ g->frameRect(r, kColorBlack);
+}
+
+void MacWindow::fillRect(ManagedSurface *g, int x, int y, int w, int h, int color) {
+ Common::Rect r(x, y, x + w, y + h);
+
+ g->fillRect(r, color);
+}
+
+WindowClick MacWindow::isInBorder(int x, int y) {
+ if (_innerDims.contains(x, y))
+ return kBorderInner;
+
+ if (isInCloseButton(x, y))
+ return kBorderCloseButton;
+
+ if (_resizable)
+ if (isInResizeButton(x, y))
+ return kBorderResizeButton;
+
+ if (_scrollable)
+ return isInScroll(x, y);
+
+ return kBorderBorder;
+}
+
+bool MacWindow::isInCloseButton(int x, int y) {
+ int bLeft = kBorderWidth;
+ int bTop = kBorderWidth;
+ if (_macBorder.hasOffsets()) {
+ bLeft = _macBorder.getOffset(kBorderOffsetLeft);
+ bTop = _macBorder.getOffset(kBorderOffsetTop);
+ }
+ return (x >= _innerDims.left - bLeft && x < _innerDims.left && y >= _innerDims.top - bTop && y < _innerDims.top);
+}
+
+bool MacWindow::isInResizeButton(int x, int y) {
+ int bRight = kBorderWidth;
+ int bBottom = kBorderWidth;
+ if (_macBorder.hasOffsets()) {
+ bRight = _macBorder.getOffset(kBorderOffsetRight);
+ bBottom = _macBorder.getOffset(kBorderOffsetBottom);
+ }
+ return (x >= _innerDims.right && x < _innerDims.right + bRight && y >= _innerDims.bottom && y < _innerDims.bottom + bBottom);
+}
+
+WindowClick MacWindow::isInScroll(int x, int y) {
+ int bTop = kBorderWidth;
+ int bRight = kBorderWidth;
+ int bBottom = kBorderWidth;
+ if (_macBorder.hasOffsets()) {
+ bTop = _macBorder.getOffset(kBorderOffsetTop);
+ bRight = _macBorder.getOffset(kBorderOffsetRight);
+ bBottom = _macBorder.getOffset(kBorderOffsetBottom);
+ }
+
+ if (x >= _innerDims.right && x < _innerDims.right + bRight) {
+ if (y < _innerDims.top - bTop)
+ return kBorderBorder;
+
+ if (y >= _innerDims.bottom + bBottom)
+ return kBorderBorder;
+
+ if (y >= _innerDims.top + _innerDims.height() / 2)
+ return kBorderScrollDown;
+
+ return kBorderScrollUp;
+ }
+
+ if (y >= _innerDims.bottom && y < _innerDims.bottom + bBottom) {
+ if (x < _innerDims.left - bTop)
+ return kBorderBorder;
+
+ if (x >= _innerDims.right + bRight)
+ return kBorderBorder;
+
+ if (x >= _innerDims.left + _innerDims.width() / 2)
+ return kBorderScrollRight;
+
+ return kBorderScrollLeft;
+ }
+
+ return kBorderBorder;
+}
+
+bool MacWindow::processEvent(Common::Event &event) {
+ WindowClick click = isInBorder(event.mouse.x, event.mouse.y);
+
+ switch (event.type) {
+ case Common::EVENT_MOUSEMOVE:
+ if (_beingDragged) {
+ _dims.translate(event.mouse.x - _draggedX, event.mouse.y - _draggedY);
+ updateInnerDims();
+
+ _draggedX = event.mouse.x;
+ _draggedY = event.mouse.y;
+
+ _wm->setFullRefresh(true);
+ }
+
+ if (_beingResized) {
+ resize(MAX(_borderWidth * 4, _dims.width() + event.mouse.x - _draggedX),
+ MAX(_borderWidth * 4, _dims.height() + event.mouse.y - _draggedY));
+
+ _draggedX = event.mouse.x;
+ _draggedY = event.mouse.y;
+
+ _wm->setFullRefresh(true);
+ (*_callback)(click, event, _dataPtr);
+ }
+ break;
+ case Common::EVENT_LBUTTONDOWN:
+ setHighlight(click);
+
+ if (click == kBorderBorder) {
+ _beingDragged = true;
+
+ _draggedX = event.mouse.x;
+ _draggedY = event.mouse.y;
+ }
+
+ if (click == kBorderResizeButton) {
+ _beingResized = true;
+
+ _draggedX = event.mouse.x;
+ _draggedY = event.mouse.y;
+ }
+
+ if (click == kBorderCloseButton && _closeable) {
+ _wm->removeWindow(this);
+ }
+
+ break;
+ case Common::EVENT_LBUTTONUP:
+ _beingDragged = false;
+ _beingResized = false;
+
+ setHighlight(kBorderNone);
+ break;
+ default:
+ return false;
+ }
+
+ return (*_callback)(click, event, _dataPtr);
+}
+
+} // End of namespace Wage
diff --git a/graphics/macgui/macwindow.h b/graphics/macgui/macwindow.h
new file mode 100644
index 0000000000..c40c1a4a38
--- /dev/null
+++ b/graphics/macgui/macwindow.h
@@ -0,0 +1,350 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef GRAPHICS_MACGUI_MACWINDOW_H
+#define GRAPHICS_MACGUI_MACWINDOW_H
+
+#include "graphics/managed_surface.h"
+#include "graphics/transparent_surface.h"
+#include "graphics/nine_patch.h"
+#include "graphics/palette.h"
+
+#include "graphics/macgui/macwindowborder.h"
+
+namespace Graphics {
+
+class MacWindowManager;
+class MacWindowBorder;
+
+namespace MacWindowConstants {
+enum WindowType {
+ kWindowUnknown,
+ kWindowWindow,
+ kWindowMenu
+};
+
+enum {
+ kBorderWidth = 17
+};
+
+enum WindowClick {
+ kBorderNone = 0,
+ kBorderScrollUp,
+ kBorderScrollDown,
+ kBorderScrollLeft,
+ kBorderScrollRight,
+ kBorderCloseButton,
+ kBorderInner,
+ kBorderBorder,
+ kBorderResizeButton
+};
+}
+using namespace MacWindowConstants;
+
+/**
+ * Abstract class that defines common functionality for all window classes.
+ * It supports event callbacks and drawing.
+ */
+class BaseMacWindow {
+public:
+ /**
+ * Base constructor.
+ * @param id ID of the window.
+ * @param editable True if the window is editable.
+ * @param wm Pointer to the MacWindowManager that owns the window.
+ */
+ BaseMacWindow(int id, bool editable, MacWindowManager *wm);
+ virtual ~BaseMacWindow() {}
+
+ /**
+ * Accessor method for the complete dimensions of the window.
+ * @return Dimensions of the window (including border) relative to the WM's screen.
+ */
+ const Common::Rect &getDimensions() { return _dims; }
+
+ /**
+ * Accessor method to the id of the window.
+ * @return The id set in the constructor.
+ */
+ int getId() { return _id; }
+
+ /**
+ * Accessor method to the type of window.
+ * Each subclass must indicate it's type.
+ * @return The type of the window.
+ */
+ WindowType getType() { return _type; }
+
+ /**
+ * Accessor method to check whether the window is editable (e.g. for resizing).
+ * @return True if the window is editable as indicated in the constructor.
+ */
+ bool isEditable() { return _editable; }
+
+ /**
+ * Method to access the entire surface of the window (e.g. to draw an image).
+ * @return A pointer to the entire surface of the window.
+ */
+ ManagedSurface *getSurface() { return &_surface; }
+
+ /**
+ * Abstract method for indicating whether the window is active or inactive.
+ * Used by the WM to handle focus on windows, etc.
+ * @param active Desired state of the window.
+ */
+ virtual void setActive(bool active) = 0;
+
+ /**
+ * Method for marking the window for redraw.
+ * @param dirty True if the window needs to be redrawn.
+ */
+ void setDirty(bool dirty) { _contentIsDirty = dirty; }
+
+ /**
+ * Method called to draw the window into the target surface.
+ * This method is most often called by the WM, and relies on
+ * the window being marked as dirty unless otherwise specified.
+ * @param g Surface on which to draw the window.
+ * @param forceRedraw It's behavior depends on the subclass.
+ */
+ virtual bool draw(ManagedSurface *g, bool forceRedraw = false) = 0;
+
+ /**
+ * Method called by the WM when there is an event concerning the window.
+ * Note that depending on the subclass of the window, it might not be called
+ * if the window is not active.
+ * @param event Event to be processed.
+ * @return true If the event was successfully consumed and processed.
+ */
+ virtual bool processEvent(Common::Event &event) = 0;
+
+ virtual bool hasAllFocus() = 0;
+
+ /**
+ * Set the callback that will be used when an event needs to be processed.
+ * @param callback A function pointer to a function that accepts:
+ * - A WindowClick, the pert of the window that was clicked.
+ * - The event to be processed.
+ * - Any additional required data (e.g. the engine's GUI).
+ */
+ void setCallback(bool (*callback)(WindowClick, Common::Event &, void *), void *data) { _callback = callback; _dataPtr = data; }
+
+protected:
+ int _id;
+ WindowType _type;
+
+ bool _editable;
+
+ ManagedSurface _surface;
+ bool _contentIsDirty;
+
+ Common::Rect _dims;
+
+ bool (*_callback)(WindowClick, Common::Event &, void *);
+ void *_dataPtr;
+
+ MacWindowManager *_wm;
+};
+
+/**
+ * An implementation of an ordinary window in the Mac interface.
+ * It supports custom resizing, scrolling, borders, etc.
+ */
+class MacWindow : public BaseMacWindow {
+public:
+ /**
+ * Construct a simple window, with the default settings.
+ * Note that the scroll must be implemented in the event handling,
+ * even if the scrollable flag is set to true.
+ * @param id See BaseMacWindow.
+ * @param scrollable True if the window can be scrolled.
+ * @param resizable True if the window can be resized.
+ * @param editable See BaseMacWindow.
+ * @param wm See BaseMacWindow.
+ */
+ MacWindow(int id, bool scrollable, bool resizable, bool editable, MacWindowManager *wm);
+ virtual ~MacWindow();
+
+ /**
+ * Change the window's location to fixed coordinates (not delta).
+ * @param x New left position of the window relative to the WM's screen.
+ * @param y New top position of the window relative to the WM's screen.
+ */
+ void move(int x, int y);
+
+ /*
+ * Change the width and the height of the window.
+ * @param w New width of the window.
+ * @param h New height of the window.
+ */
+ void resize(int w, int h);
+
+ /**
+ * Change the dimensions of the window ([0, 0, 0, 0] by default).
+ * Note that this can be used to update both the position and the size
+ * of the window, although move() and resize() might be more comfortable.
+ * @param r The desired dimensions of the window.
+ */
+ void setDimensions(const Common::Rect &r);
+
+ /**
+ * Accessor to retrieve the dimensions of the inner surface of the window
+ * (i.e. without taking borders into account).
+ * Note that the returned dimensions' position is relative to the WM's
+ * screen, just like in getDimensions().
+ * @return The inner dimensions of the window.
+ */
+ const Common::Rect &getInnerDimensions() { return _innerDims; }
+
+ /**
+ * Similar to that described in BaseMacWindow.
+ * @param g See BaseMacWindow.
+ * @param forceRedraw If true, the borders are guarranteed to redraw.
+ */
+ bool draw(ManagedSurface *g, bool forceRedraw = false);
+
+ /**
+ * Mutator to change the active state of the window.
+ * Most often called from the WM.
+ * @param active Target state.
+ */
+ void setActive(bool active);
+ /**
+ * Accessor to determine whether a window is active.
+ * @return True if the window is active.
+ */
+ bool isActive();
+
+ /**
+ * Mutator to change the title of the window.
+ * @param title Target title of the window.
+ */
+ void setTitle(Common::String &title) { _title = title; }
+ /**
+ * Highlight the target part of the window.
+ * Used for the default borders.
+ * @param highlightedPart Part to be highlighted.
+ */
+ void setHighlight(WindowClick highlightedPart);
+ /**
+ * Set the scroll poisition.
+ * @param scrollPos Target scroll position.
+ * @param scrollSize Size of the scrolling bar.
+ */
+ void setScroll(float scrollPos, float scrollSize);
+ /**
+ * See BaseMacWindow.
+ */
+ bool processEvent(Common::Event &event);
+ bool hasAllFocus() { return _beingDragged || _beingResized; }
+
+ /**
+ * Set arbitrary border from a BMP data stream, with custom border offsets.
+ * Note that the BMP has to be 9patch compliant. For examples, go to:
+ * https://github.com/blorente/MacVenture-Extract-Guide/tree/master/borders
+ * @param file The BMP data stream with the desired border.
+ * @param active Whether the border corresponds with the active state of the window.
+ * @param lo Width of the left side of the border, in pixels.
+ * @param ro Width of the right side of the border, in pixels.
+ * @param to Width of the top side of the border, in pixels.
+ * @param bo Width of the bottom side of the border, in pixels.
+ */
+ void loadBorder(Common::SeekableReadStream &file, bool active, int lo, int ro, int to, int bo);
+ //void setBorder(TransparentSurface &border, bool active);
+
+ /**
+ * Indicate whether the window can be closed (false by default).
+ * @param closeable True if the window can be closed.
+ */
+ void setCloseable(bool closeable);
+
+private:
+ void drawBorder();
+ void prepareBorderSurface(ManagedSurface *g);
+ void drawSimpleBorder(ManagedSurface *g);
+ void drawBorderFromSurface(ManagedSurface *g);
+ void drawBox(ManagedSurface *g, int x, int y, int w, int h);
+ void fillRect(ManagedSurface *g, int x, int y, int w, int h, int color);
+ const Font *getTitleFont();
+ void updateInnerDims();
+ WindowClick isInBorder(int x, int y);
+
+ bool isInCloseButton(int x, int y);
+ bool isInResizeButton(int x, int y);
+ WindowClick isInScroll(int x, int y);
+
+private:
+ ManagedSurface _borderSurface;
+ ManagedSurface _composeSurface;
+
+ MacWindowBorder _macBorder;
+
+ bool _scrollable;
+ bool _resizable;
+ bool _active;
+ bool _borderIsDirty;
+
+ bool _closeable;
+
+ int _borderWidth;
+
+ bool _beingDragged, _beingResized;
+ int _draggedX, _draggedY;
+
+ WindowClick _highlightedPart;
+ float _scrollPos, _scrollSize;
+
+ Common::Rect _innerDims;
+
+ Common::String _title;
+};
+
+
+
+} // End of namespace Graphics
+
+#endif
diff --git a/graphics/macgui/macwindowborder.cpp b/graphics/macgui/macwindowborder.cpp
new file mode 100644
index 0000000000..b77fa35603
--- /dev/null
+++ b/graphics/macgui/macwindowborder.cpp
@@ -0,0 +1,117 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+* MIT License:
+*
+* Copyright (c) 2016 Borja Lorente
+*
+* Permission is hereby granted, free of charge, to any person
+* obtaining a copy of this software and associated documentation
+* files (the "Software"), to deal in the Software without
+* restriction, including without limitation the rights to use,
+* copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following
+* conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+*/
+
+#include "common/system.h"
+
+#include "graphics/macgui/macwindowborder.h"
+#include "graphics/macgui/macwindowmanager.h"
+
+namespace Graphics {
+
+using namespace Graphics::MacGUIConstants;
+
+MacWindowBorder::MacWindowBorder() : _activeInitialized(false), _inactiveInitialized(false) {
+ _activeBorder = nullptr;
+ _inactiveBorder = nullptr;
+ _hasOffsets = false;
+}
+
+MacWindowBorder::~MacWindowBorder() {
+ if (_activeBorder)
+ delete _activeBorder;
+ if (_inactiveBorder)
+ delete _inactiveBorder;
+}
+
+bool MacWindowBorder::hasBorder(bool active) {
+ return active ? _activeInitialized : _inactiveInitialized;
+}
+
+void MacWindowBorder::addActiveBorder(TransparentSurface &source) {
+ assert(!_activeBorder);
+ _activeBorder = new NinePatchBitmap(&source, false);
+ _activeInitialized = true;
+}
+
+void MacWindowBorder::addInactiveBorder(TransparentSurface &source) {
+ assert(!_inactiveBorder);
+ _inactiveBorder = new NinePatchBitmap(&source, false);
+ _inactiveInitialized = true;
+}
+
+bool MacWindowBorder::hasOffsets() {
+ return _hasOffsets;
+}
+
+void MacWindowBorder::setOffsets(int left, int right, int top, int bottom) {
+ _borderOffsets[0] = left;
+ _borderOffsets[1] = right;
+ _borderOffsets[2] = top;
+ _borderOffsets[3] = bottom;
+ _hasOffsets = true;
+}
+
+int MacWindowBorder::getOffset(MacBorderOffset offset) {
+ return _borderOffsets[offset];
+}
+
+void MacWindowBorder::blitBorderInto(ManagedSurface &destination, bool active) {
+
+ TransparentSurface srf;
+ NinePatchBitmap *src = active ? _activeBorder : _inactiveBorder;
+
+ srf.create(destination.w, destination.h, destination.format);
+ srf.fillRect(Common::Rect(0, 0, srf.w, srf.h), kColorGreen2);
+
+ byte palette[kColorCount];
+ g_system->getPaletteManager()->grabPalette(palette, 0, kColorCount);
+
+ src->blit(srf, 0, 0, srf.w, srf.h, palette, kColorCount);
+ destination.transBlitFrom(srf, kColorGreen2);
+}
+
+} // End of namespace Graphics
diff --git a/graphics/macgui/macwindowborder.h b/graphics/macgui/macwindowborder.h
new file mode 100644
index 0000000000..54938e5143
--- /dev/null
+++ b/graphics/macgui/macwindowborder.h
@@ -0,0 +1,149 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+* MIT License:
+*
+* Copyright (c) 2016 Borja Lorente
+*
+* Permission is hereby granted, free of charge, to any person
+* obtaining a copy of this software and associated documentation
+* files (the "Software"), to deal in the Software without
+* restriction, including without limitation the rights to use,
+* copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following
+* conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+*/
+
+#ifndef GRAPHICS_MACGUI_MACWINDOWBORDER_H
+#define GRAPHICS_MACGUI_MACWINDOWBORDER_H
+
+#include "common/str.h"
+#include "common/list.h"
+
+#include "graphics/nine_patch.h"
+#include "graphics/managed_surface.h"
+#include "graphics/transparent_surface.h"
+
+namespace Graphics {
+
+enum MacBorderOffset {
+ kBorderOffsetLeft = 0,
+ kBorderOffsetRight = 1,
+ kBorderOffsetTop = 2,
+ kBorderOffsetBottom = 3
+};
+
+/**
+ * A representation of a custom border, which allows for arbitrary border offsets
+ * and nine-patch resizable displays for both active and inactive states.
+ * However, the border offsets are the same for both active and inactive states.
+ */
+class MacWindowBorder {
+public:
+ MacWindowBorder();
+ ~MacWindowBorder();
+
+ /**
+ * Accessor to check whether or not a border is loaded.
+ * @param active State that we want to check. If true it checks for active border, if false it checks for inactive.
+ * @return True if the checked state has a border loaded, false otherwise.
+ */
+ bool hasBorder(bool active);
+
+ /**
+ * Add the given surface as the display of the border in the active state.
+ * Will fail if there is already an active border.
+ * @param The surface that will be displayed.
+ */
+ void addActiveBorder(TransparentSurface &source);
+
+ /**
+ * Add the given surface as the display of the border in the inactive state.
+ * Will fail if there is already an inactive border.
+ * @param The surface that will be displayed.
+ */
+ void addInactiveBorder(TransparentSurface &source);
+
+ /**
+ * Accessor function for the custom offsets.
+ * @return True if custom offsets have been indicated (setOffsets has been called previously).
+ */
+ bool hasOffsets();
+
+ /**
+ * Mutator method to indicate the custom border offsets.
+ * These should be set to the desired thickness of each side of the border.
+ * e.g. For a border that is 10 pixels wide and 5 pixels tall, the call should be:
+ * setOffsets(10, 10, 5, 5)
+ * Note that this function does not check whether those borders form
+ * a valid rect when combined with the window dimensions.
+ * @param left Thickness (in pixels) of the left side of the border.
+ * @param right Thickness (in pixels) of the right side of the border.
+ * @param top Thickness (in pixels) of the top side of the border.
+ * @param bottom Thickness (in pixels) of the bottom side of the border.
+ */
+ void setOffsets(int left, int right, int top, int bottom);
+
+ /**
+ * Accessor method to retrieve a given border.
+ * Note that it does not check for validity, and thus if setOffsets
+ * was not called before it might return garbage.
+ * @param offset The identifier of the offset wanted.
+ * @return The desired offset in pixels.
+ */
+ int getOffset(MacBorderOffset offset);
+
+ /**
+ * Blit the desired border (active or inactive) into a destination surface.
+ * It automatically resizes the border to fit the given surface.
+ * @param destination The surface we want to blit into.
+ * @param active True if we want to blit the active border, false otherwise.
+ */
+ void blitBorderInto(ManagedSurface &destination, bool active);
+
+private:
+
+ NinePatchBitmap *_activeBorder;
+ NinePatchBitmap *_inactiveBorder;
+
+ bool _activeInitialized;
+ bool _inactiveInitialized;
+
+ bool _hasOffsets;
+ int _borderOffsets[4];
+
+};
+
+} // End of namespace Graphics
+#endif
diff --git a/graphics/macgui/macwindowmanager.cpp b/graphics/macgui/macwindowmanager.cpp
new file mode 100644
index 0000000000..d19e40781f
--- /dev/null
+++ b/graphics/macgui/macwindowmanager.cpp
@@ -0,0 +1,478 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "common/array.h"
+#include "common/events.h"
+#include "common/list.h"
+#include "common/unzip.h"
+#include "common/system.h"
+#include "common/stream.h"
+
+#include "graphics/cursorman.h"
+#include "graphics/fonts/bdf.h"
+#include "graphics/managed_surface.h"
+#include "graphics/palette.h"
+#include "graphics/primitives.h"
+#include "graphics/macgui/macwindowmanager.h"
+#include "graphics/macgui/macwindow.h"
+#include "graphics/macgui/macmenu.h"
+
+namespace Graphics {
+
+static const byte palette[] = {
+ 0, 0, 0, // Black
+ 0x80, 0x80, 0x80, // Gray
+ 0xff, 0xff, 0xff, // White
+ 0x00, 0xff, 0x00, // Green
+ 0x00, 0xcf, 0x00 // Green2
+};
+
+static byte fillPatterns[][8] = { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, // kPatternSolid
+ { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }, // kPatternStripes
+ { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 }, // kPatternCheckers
+ { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa } // kPatternCheckers2
+};
+
+static const byte macCursorArrow[] = {
+ 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 0, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 0, 0, 2, 3, 3, 3, 3, 3, 3, 3,
+ 2, 0, 0, 0, 2, 3, 3, 3, 3, 3, 3,
+ 2, 0, 0, 0, 0, 2, 3, 3, 3, 3, 3,
+ 2, 0, 0, 0, 0, 0, 2, 3, 3, 3, 3,
+ 2, 0, 0, 0, 0, 0, 0, 2, 3, 3, 3,
+ 2, 0, 0, 0, 0, 0, 0, 0, 2, 3, 3,
+ 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3,
+ 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2,
+ 2, 0, 0, 2, 0, 0, 2, 3, 3, 3, 3,
+ 2, 0, 2, 3, 2, 0, 0, 2, 3, 3, 3,
+ 2, 2, 3, 3, 2, 0, 0, 2, 3, 3, 3,
+ 2, 3, 3, 3, 3, 2, 0, 0, 2, 3, 3,
+ 3, 3, 3, 3, 3, 2, 0, 0, 2, 3, 3,
+ 3, 3, 3, 3, 3, 3, 2, 2, 2, 3, 3
+};
+
+static const byte macCursorBeam[] = {
+ 0, 0, 3, 3, 3, 0, 0, 3, 3, 3, 3,
+ 3, 3, 0, 3, 0, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 0, 3, 0, 3, 3, 3, 3, 3, 3,
+ 0, 0, 3, 3, 3, 0, 0, 3, 3, 3, 3,
+};
+
+MacWindowManager::MacWindowManager() {
+ _screen = 0;
+ _lastId = 0;
+ _activeWindow = -1;
+
+ _menu = 0;
+
+ _fullRefresh = true;
+
+ _builtInFonts = true;
+
+ for (int i = 0; i < ARRAYSIZE(fillPatterns); i++)
+ _patterns.push_back(fillPatterns[i]);
+
+ loadFonts();
+
+ g_system->getPaletteManager()->setPalette(palette, 0, ARRAYSIZE(palette) / 3);
+
+ CursorMan.replaceCursorPalette(palette, 0, ARRAYSIZE(palette) / 3);
+ CursorMan.replaceCursor(macCursorArrow, 11, 16, 1, 1, 3);
+ _cursorIsArrow = true;
+ CursorMan.showMouse(true);
+}
+
+MacWindowManager::~MacWindowManager() {
+ for (int i = 0; i < _lastId; i++)
+ delete _windows[i];
+}
+
+MacWindow *MacWindowManager::addWindow(bool scrollable, bool resizable, bool editable) {
+ MacWindow *w = new MacWindow(_lastId, scrollable, resizable, editable, this);
+
+ _windows.push_back(w);
+ _windowStack.push_back(w);
+
+ setActive(_lastId);
+
+ _lastId++;
+
+ return w;
+}
+
+Menu *MacWindowManager::addMenu() {
+ _menu = new Menu(_lastId, _screen->getBounds(), this);
+
+ _windows.push_back(_menu);
+
+ _lastId++;
+
+ return _menu;
+}
+
+void MacWindowManager::setActive(int id) {
+ if (_activeWindow == id)
+ return;
+
+ if (_activeWindow != -1)
+ _windows[_activeWindow]->setActive(false);
+
+ _activeWindow = id;
+
+ _windows[id]->setActive(true);
+
+ _windowStack.remove(_windows[id]);
+ _windowStack.push_back(_windows[id]);
+
+ _fullRefresh = true;
+}
+
+void MacWindowManager::removeWindow(MacWindow *target) {
+ _windowsToRemove.push_back(target);
+ _needsRemoval = true;
+}
+
+struct PlotData {
+ Graphics::ManagedSurface *surface;
+ MacPatterns *patterns;
+ uint fillType;
+ int thickness;
+
+ PlotData(Graphics::ManagedSurface *s, MacPatterns *p, int f, int t) :
+ surface(s), patterns(p), fillType(f), thickness(t) {}
+};
+
+static void drawPixel(int x, int y, int color, void *data) {
+ PlotData *p = (PlotData *)data;
+
+ if (p->fillType > p->patterns->size())
+ return;
+
+ byte *pat = p->patterns->operator[](p->fillType - 1);
+
+ if (p->thickness == 1) {
+ if (x >= 0 && x < p->surface->w && y >= 0 && y < p->surface->h) {
+ uint xu = (uint)x; // for letting compiler optimize it
+ uint yu = (uint)y;
+
+ *((byte *)p->surface->getBasePtr(xu, yu)) =
+ (pat[yu % 8] & (1 << (7 - xu % 8))) ?
+ color : kColorWhite;
+ }
+ } else {
+ int x1 = x;
+ int x2 = x1 + p->thickness;
+ int y1 = y;
+ int y2 = y1 + p->thickness;
+
+ for (y = y1; y < y2; y++)
+ for (x = x1; x < x2; x++)
+ if (x >= 0 && x < p->surface->w && y >= 0 && y < p->surface->h) {
+ uint xu = (uint)x; // for letting compiler optimize it
+ uint yu = (uint)y;
+ *((byte *)p->surface->getBasePtr(xu, yu)) =
+ (pat[yu % 8] & (1 << (7 - xu % 8))) ?
+ color : kColorWhite;
+ }
+ }
+}
+
+void MacWindowManager::drawDesktop() {
+ Common::Rect r(_screen->getBounds());
+
+ PlotData pd(_screen, &_patterns, kPatternCheckers, 1);
+
+ Graphics::drawRoundRect(r, kDesktopArc, kColorBlack, true, drawPixel, &pd);
+
+ g_system->copyRectToScreen(_screen->getPixels(), _screen->pitch, 0, 0, _screen->w, _screen->h);
+}
+
+void MacWindowManager::draw() {
+ assert(_screen);
+
+ removeMarked();
+
+ if (_fullRefresh)
+ drawDesktop();
+
+ for (Common::List<BaseMacWindow *>::const_iterator it = _windowStack.begin(); it != _windowStack.end(); it++) {
+ BaseMacWindow *w = *it;
+ if (w->draw(_screen, _fullRefresh)) {
+ w->setDirty(false);
+
+ Common::Rect clip(w->getDimensions().left - 2, w->getDimensions().top - 2, w->getDimensions().right - 2, w->getDimensions().bottom - 2);
+ clip.clip(_screen->getBounds());
+
+ g_system->copyRectToScreen(_screen->getBasePtr(clip.left, clip.top), _screen->pitch, clip.left, clip.top, clip.width(), clip.height());
+ }
+ }
+
+ // Menu is drawn on top of everything and always
+ if (_menu)
+ _menu->draw(_screen, _fullRefresh);
+
+ _fullRefresh = false;
+}
+
+bool MacWindowManager::processEvent(Common::Event &event) {
+ // Menu gets events first fir shortcuts and menu bar
+ if (_menu && _menu->processEvent(event))
+ return true;
+
+ if (event.type != Common::EVENT_MOUSEMOVE && event.type != Common::EVENT_LBUTTONDOWN &&
+ event.type != Common::EVENT_LBUTTONUP)
+ return false;
+
+ if (_windows[_activeWindow]->isEditable() && _windows[_activeWindow]->getType() == kWindowWindow &&
+ ((MacWindow *)_windows[_activeWindow])->getInnerDimensions().contains(event.mouse.x, event.mouse.y)) {
+ if (_cursorIsArrow) {
+ CursorMan.replaceCursor(macCursorBeam, 11, 16, 3, 8, 3);
+ _cursorIsArrow = false;
+ }
+ } else {
+ if (_cursorIsArrow == false) {
+ CursorMan.replaceCursor(macCursorArrow, 11, 16, 1, 1, 3);
+ _cursorIsArrow = true;
+ }
+ }
+
+ for (Common::List<BaseMacWindow *>::const_iterator it = _windowStack.end(); it != _windowStack.begin();) {
+ it--;
+ BaseMacWindow *w = *it;
+
+
+ if (w->hasAllFocus() || w->getDimensions().contains(event.mouse.x, event.mouse.y)) {
+ if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_LBUTTONUP)
+ setActive(w->getId());
+
+ return w->processEvent(event);
+ }
+ }
+
+ return false;
+}
+
+void MacWindowManager::removeMarked() {
+ if (!_needsRemoval) return;
+
+ Common::List<BaseMacWindow *>::const_iterator it;
+ for (it = _windowsToRemove.begin(); it != _windowsToRemove.end(); it++) {
+ removeFromStack(*it);
+ removeFromWindowList(*it);
+ delete *it;
+ _activeWindow = 0;
+ _fullRefresh = true;
+ }
+ _windowsToRemove.clear();
+ _needsRemoval = false;
+}
+
+void MacWindowManager::removeFromStack(BaseMacWindow *target) {
+ Common::List<BaseMacWindow *>::iterator stackIt;
+ for (stackIt = _windowStack.begin(); stackIt != _windowStack.end(); stackIt++) {
+ if (*stackIt == target) {
+ stackIt = _windowStack.erase(stackIt);
+ stackIt--;
+ }
+ }
+}
+
+void MacWindowManager::removeFromWindowList(BaseMacWindow *target) {
+ int size = _windows.size();
+ int ndx = 0;
+ for (int i = 0; i < size; i++) {
+ if (_windows[i] == target) {
+ ndx = i;
+ }
+ }
+ _windows.remove_at(ndx);
+}
+
+//////////////////////
+// Font stuff
+//////////////////////
+void MacWindowManager::loadFonts() {
+ Common::Archive *dat;
+
+ dat = Common::makeZipArchive("classicmacfonts.dat");
+
+ if (!dat) {
+ warning("Could not find classicmacfonts.dat. Falling back to built-in fonts");
+ _builtInFonts = true;
+
+ return;
+ }
+
+ Common::ArchiveMemberList list;
+ dat->listMembers(list);
+
+ for (Common::ArchiveMemberList::iterator it = list.begin(); it != list.end(); ++it) {
+ Common::SeekableReadStream *stream = dat->createReadStreamForMember((*it)->getName());
+
+ Graphics::BdfFont *font = Graphics::BdfFont::loadFont(*stream);
+
+ delete stream;
+
+ Common::String fontName = (*it)->getName();
+
+ // Trim the .bdf extension
+ for (int i = fontName.size() - 1; i >= 0; --i) {
+ if (fontName[i] == '.') {
+ while ((uint)i < fontName.size()) {
+ fontName.deleteLastChar();
+ }
+ break;
+ }
+ }
+
+ FontMan.assignFontToName(fontName, font);
+
+ debug(2, " %s", fontName.c_str());
+ }
+
+ _builtInFonts = false;
+
+ delete dat;
+}
+
+const Graphics::Font *MacWindowManager::getFont(const char *name, Graphics::FontManager::FontUsage fallback) {
+ const Graphics::Font *font = 0;
+
+ if (!_builtInFonts) {
+ font = FontMan.getFontByName(name);
+
+ if (!font)
+ warning("Cannot load font %s", name);
+ }
+
+ if (_builtInFonts || !font)
+ font = FontMan.getFontByUsage(fallback);
+
+ return font;
+}
+
+// Source: Apple IIGS Technical Note #41, "Font Family Numbers"
+// http://apple2.boldt.ca/?page=til/tn.iigs.041
+static const char *const fontNames[] = {
+ "Chicago", // system font
+ "Geneva", // application font
+ "New York",
+ "Geneva",
+
+ "Monaco",
+ "Venice",
+ "London",
+ "Athens",
+
+ "San Francisco",
+ "Toronto",
+ NULL,
+ "Cairo",
+ "Los Angeles", // 12
+
+ "Zapf Dingbats",
+ "Bookman",
+ "Helvetica Narrow",
+ "Palatino",
+ NULL,
+ "Zapf Chancery",
+ NULL,
+
+ "Times", // 20
+ "Helvetica",
+ "Courier",
+ "Symbol",
+ "Taliesin", // mobile?
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL, // 30
+ NULL,
+ NULL,
+ "Avant Garde",
+ "New Century Schoolbook"
+};
+
+const char *MacWindowManager::getFontName(int id, int size) {
+ static char name[128];
+
+ if (id > ARRAYSIZE(fontNames))
+ return NULL;
+
+ snprintf(name, 128, "%s-%d", fontNames[id], size);
+
+ return name;
+}
+
+/////////////////
+// Cursor stuff
+/////////////////
+void MacWindowManager::pushArrowCursor() {
+ CursorMan.pushCursor(macCursorArrow, 11, 16, 1, 1, 3);
+}
+
+void MacWindowManager::popCursor() {
+ CursorMan.popCursor();
+}
+
+
+} // End of namespace Graphics
diff --git a/graphics/macgui/macwindowmanager.h b/graphics/macgui/macwindowmanager.h
new file mode 100644
index 0000000000..cb432d9699
--- /dev/null
+++ b/graphics/macgui/macwindowmanager.h
@@ -0,0 +1,226 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef GRAPHICS_MACGUI_MACWINDOWMANAGER_H
+#define GRAPHICS_MACGUI_MACWINDOWMANAGER_H
+
+#include "common/array.h"
+#include "common/list.h"
+#include "common/events.h"
+#include "common/archive.h"
+
+#include "graphics/fontman.h"
+#include "graphics/macgui/macwindow.h"
+
+namespace Graphics {
+
+namespace MacGUIConstants {
+enum {
+ kDesktopArc = 7
+};
+
+enum {
+ kColorBlack = 0,
+ kColorGray = 1,
+ kColorWhite = 2,
+ kColorGreen = 3,
+ kColorGreen2 = 4,
+ kColorCount
+};
+
+enum {
+ kPatternSolid = 1,
+ kPatternStripes = 2,
+ kPatternCheckers = 3,
+ kPatternCheckers2 = 4
+};
+}
+using namespace MacGUIConstants;
+
+class ManagedSurface;
+
+class Menu;
+
+typedef Common::Array<byte *> MacPatterns;
+
+/**
+ * A manager class to handle window creation, destruction,
+ * drawing, moving and event handling.
+ */
+class MacWindowManager {
+public:
+ MacWindowManager();
+ ~MacWindowManager();
+
+ /**
+ * Mutator to indicate the surface onto which the desktop will be drawn.
+ * Note that this method should be called as soon as the WM is created.
+ * @param screen Surface on which the desktop will be drawn.
+ */
+ void setScreen(ManagedSurface *screen) { _screen = screen; }
+ /**
+ * Accessor method to check the presence of built-in fonts.
+ * @return True if there are bult-in fonts.
+ */
+ bool hasBuiltInFonts() { return _builtInFonts; }
+ /**
+ * Retrieve a font from the available ones.
+ * @param name Name of the desired font.
+ * @param fallback Fallback policy in case the desired font isn't there.
+ * @return The requested font or the fallback.
+ */
+ const Font *getFont(const char *name, FontManager::FontUsage fallback);
+
+ /**
+ * Return font name from standard ID
+ * @param id ID of the font
+ * @param size size of the font
+ * @return the font name or NULL if ID goes beyond the mapping
+ */
+ const char *getFontName(int id, int size);
+
+ /**
+ * Create a window with the given parameters.
+ * Note that this method allocates the necessary memory for the window.
+ * @param scrollable True if the window has to be scrollable.
+ * @param resizable True if the window can be resized.
+ * @param editable True if the window can be edited.
+ * @return Pointer to the newly created window.
+ */
+ MacWindow *addWindow(bool scrollable, bool resizable, bool editable);
+ /**
+ * Add the menu to the desktop.
+ * Note that the returned menu is empty, and therefore must be filled
+ * afterwards.
+ * @return Pointer to a new empty menu.
+ */
+ Menu *addMenu();
+ /**
+ * Set the desired window state to active.
+ * @param id ID of the window that has to be set to active.
+ */
+ void setActive(int id);
+ /**
+ * Mark a window for removal.
+ * Note that the window data will be destroyed.
+ * @param target Window to be removed.
+ */
+ void removeWindow(MacWindow *target);
+
+ /**
+ * Mutator to indicate that the entire desktop must be refreshed.
+ * @param redraw Currently unused.
+ */
+ void setFullRefresh(bool redraw) { _fullRefresh = true; }
+
+ /**
+ * Method to draw the desktop into the screen,
+ * It will take into accout the contents set as dirty.
+ * Note that this method does not refresh the screen,
+ * g_system must be called separately.
+ */
+ void draw();
+
+ /**
+ * Method to process the events from the engine.
+ * Most often this method will be called from the engine's GUI, and
+ * will send the event to the relevant windows for them to process.
+ * @param event The event to be processed.
+ * @return True if the event was processed.
+ */
+ bool processEvent(Common::Event &event);
+
+ /**
+ * Accessor to retrieve an arbitrary window.
+ * @param id The id of the desired window.
+ * @return Pointer to the requested window, if it exists.
+ */
+ BaseMacWindow *getWindow(int id) { return _windows[id]; }
+
+ /**
+ * Retrieve the patterns used to fill surfaces.
+ * @return A MacPatterns object reference with the patterns.
+ */
+ MacPatterns &getPatterns() { return _patterns; }
+ void drawFilledRoundRect(ManagedSurface *surface, Common::Rect &rect, int arc, int color);
+
+ void pushArrowCursor();
+ void popCursor();
+
+private:
+ void drawDesktop();
+ void loadFonts();
+
+ void removeMarked();
+ void removeFromStack(BaseMacWindow *target);
+ void removeFromWindowList(BaseMacWindow *target);
+
+private:
+ ManagedSurface *_screen;
+
+ Common::List<BaseMacWindow *> _windowStack;
+ Common::Array<BaseMacWindow *> _windows;
+
+ Common::List<BaseMacWindow *> _windowsToRemove;
+ bool _needsRemoval;
+
+ int _lastId;
+ int _activeWindow;
+
+ bool _fullRefresh;
+
+ MacPatterns _patterns;
+
+ Menu *_menu;
+
+ bool _builtInFonts;
+ bool _cursorIsArrow;
+};
+
+} // End of namespace Graphics
+
+#endif
diff --git a/graphics/managed_surface.cpp b/graphics/managed_surface.cpp
new file mode 100644
index 0000000000..273b15de55
--- /dev/null
+++ b/graphics/managed_surface.cpp
@@ -0,0 +1,260 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "graphics/managed_surface.h"
+#include "common/algorithm.h"
+#include "common/textconsole.h"
+
+namespace Graphics {
+
+const int SCALE_THRESHOLD = 0x100;
+
+ManagedSurface::ManagedSurface() :
+ w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format),
+ _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) {
+}
+
+ManagedSurface::ManagedSurface(ManagedSurface &surf) :
+ w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format),
+ _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) {
+ *this = surf;
+}
+
+ManagedSurface::ManagedSurface(int width, int height) :
+ w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format),
+ _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) {
+ create(width, height);
+}
+
+ManagedSurface::ManagedSurface(int width, int height, const Graphics::PixelFormat &pixelFormat) :
+ w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format),
+ _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) {
+ create(width, height, pixelFormat);
+}
+
+ManagedSurface::ManagedSurface(ManagedSurface &surf, const Common::Rect &bounds) :
+ w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format),
+ _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) {
+ create(surf, bounds);
+}
+
+ManagedSurface::~ManagedSurface() {
+ free();
+}
+
+ManagedSurface &ManagedSurface::operator=(ManagedSurface &surf) {
+ // Free any current surface
+ free();
+
+ if (surf._disposeAfterUse == DisposeAfterUse::YES) {
+ // Create a new surface and copy the pixels from the source surface
+ create(surf.w, surf.h, surf.format);
+ Common::copy((const byte *)surf.getPixels(), (const byte *)surf.getPixels() +
+ surf.w * surf.h * surf.format.bytesPerPixel, (byte *)this->getPixels());
+ } else {
+ // Source isn't managed, so simply copy its fields
+ _owner = surf._owner;
+ _offsetFromOwner = surf._offsetFromOwner;
+ void *srcPixels = surf._innerSurface.getPixels();
+ _innerSurface.setPixels(srcPixels);
+ _innerSurface.w = surf.w;
+ _innerSurface.h = surf.h;
+ _innerSurface.pitch = surf.pitch;
+ this->format = surf.format;
+ }
+
+ return *this;
+}
+
+void ManagedSurface::setPixels(void *newPixels) {
+ free();
+ _innerSurface.setPixels(newPixels);
+}
+
+void ManagedSurface::create(uint16 width, uint16 height) {
+ create(width, height, PixelFormat::createFormatCLUT8());
+}
+
+void ManagedSurface::create(uint16 width, uint16 height, const PixelFormat &pixelFormat) {
+ free();
+ _innerSurface.create(width, height, pixelFormat);
+
+ _disposeAfterUse = DisposeAfterUse::YES;
+ markAllDirty();
+}
+
+void ManagedSurface::create(ManagedSurface &surf, const Common::Rect &bounds) {
+ free();
+
+ _offsetFromOwner = Common::Point(bounds.left, bounds.top);
+ _innerSurface.setPixels(surf.getBasePtr(bounds.left, bounds.top));
+ _innerSurface.pitch = surf.pitch;
+ _innerSurface.format = surf.format;
+ _innerSurface.w = bounds.width();
+ _innerSurface.h = bounds.height();
+ _owner = &surf;
+ _disposeAfterUse = DisposeAfterUse::NO;
+}
+
+void ManagedSurface::free() {
+ if (_disposeAfterUse == DisposeAfterUse::YES)
+ _innerSurface.free();
+
+ _disposeAfterUse = DisposeAfterUse::NO;
+ _owner = nullptr;
+ _offsetFromOwner = Common::Point(0, 0);
+}
+
+bool ManagedSurface::clip(Common::Rect &srcBounds, Common::Rect &destBounds) {
+ if (destBounds.left >= this->w || destBounds.top >= this->h ||
+ destBounds.right <= 0 || destBounds.bottom <= 0)
+ return false;
+
+ // Clip the bounds if necessary to fit on-screen
+ if (destBounds.right > this->w) {
+ srcBounds.right -= destBounds.right - this->w;
+ destBounds.right = this->w;
+ }
+
+ if (destBounds.bottom > this->h) {
+ srcBounds.bottom -= destBounds.bottom - this->h;
+ destBounds.bottom = this->h;
+ }
+
+ if (destBounds.top < 0) {
+ srcBounds.top += -destBounds.top;
+ destBounds.top = 0;
+ }
+
+ if (destBounds.left < 0) {
+ srcBounds.left += -destBounds.left;
+ destBounds.left = 0;
+ }
+
+ return true;
+}
+
+void ManagedSurface::blitFrom(const Surface &src) {
+ blitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Point(0, 0));
+}
+
+void ManagedSurface::blitFrom(const Surface &src, const Common::Point &destPos) {
+ blitFrom(src, Common::Rect(0, 0, src.w, src.h), destPos);
+}
+
+void ManagedSurface::blitFrom(const Surface &src, const Common::Rect &srcRect,
+ const Common::Point &destPos) {
+ Common::Rect srcBounds = srcRect;
+ Common::Rect destBounds(destPos.x, destPos.y, destPos.x + srcRect.width(),
+ destPos.y + srcRect.height());
+ assert(src.format.bytesPerPixel == format.bytesPerPixel);
+
+ if (!srcRect.isValidRect() || !clip(srcBounds, destBounds))
+ return;
+
+ for (int y = 0; y < srcBounds.height(); ++y) {
+ const byte *srcP = (const byte *)src.getBasePtr(srcBounds.left, srcBounds.top + y);
+ byte *destP = (byte *)getBasePtr(destBounds.left, destBounds.top + y);
+ Common::copy(srcP, srcP + srcBounds.width() * format.bytesPerPixel, destP);
+ }
+
+ addDirtyRect(Common::Rect(0, 0, this->w, this->h));
+}
+
+void ManagedSurface::transBlitFrom(const Surface &src, uint transColor, bool flipped, uint overrideColor) {
+ transBlitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Rect(0, 0, this->w, this->h),
+ transColor, false, overrideColor);
+}
+
+void ManagedSurface::transBlitFrom(const Surface &src, const Common::Point &destPos,
+ uint transColor, bool flipped, uint overrideColor) {
+ transBlitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Rect(destPos.x, destPos.y,
+ destPos.x + src.w, destPos.y + src.h), transColor, false, overrideColor);
+}
+
+void ManagedSurface::transBlitFrom(const Surface &src, const Common::Rect &srcRect,
+ const Common::Point &destPos, uint transColor, bool flipped, uint overrideColor) {
+ transBlitFrom(src, srcRect, Common::Rect(destPos.x, destPos.y,
+ destPos.x + src.w, destPos.y + src.h), transColor, false, overrideColor);
+}
+
+template<typename T>
+void transBlit(const Surface &src, const Common::Rect &srcRect, Surface *dest, const Common::Rect &destRect, uint transColor, bool flipped, uint overrideColor) {
+ int scaleX = SCALE_THRESHOLD * srcRect.width() / destRect.width();
+ int scaleY = SCALE_THRESHOLD * srcRect.height() / destRect.height();
+
+ // Loop through drawing output lines
+ for (int destY = destRect.top, scaleYCtr = 0; destY < destRect.bottom; ++destY, scaleYCtr += scaleY) {
+ if (destY < 0 || destY >= dest->h)
+ continue;
+ const T *srcLine = (const T *)src.getBasePtr(0, scaleYCtr / SCALE_THRESHOLD);
+ T *destLine = (T *)dest->getBasePtr(destRect.left, destY);
+
+ // Loop through drawing the pixels of the row
+ for (int destX = destRect.left, xCtr = 0, scaleXCtr = 0; destX < destRect.right; ++destX, ++xCtr, scaleXCtr += scaleX) {
+ if (destX < 0 || destX >= dest->w)
+ continue;
+
+ T srcVal = srcLine[flipped ? src.w - scaleXCtr / SCALE_THRESHOLD - 1 : scaleXCtr / SCALE_THRESHOLD];
+ if (srcVal != transColor) {
+ destLine[xCtr] = overrideColor ? overrideColor : srcVal;
+ }
+ }
+ }
+}
+
+void ManagedSurface::transBlitFrom(const Surface &src, const Common::Rect &srcRect,
+ const Common::Rect &destRect, uint transColor, bool flipped, uint overrideColor) {
+ if (src.w == 0 || src.h == 0 || destRect.width() == 0 || destRect.height() == 0)
+ return;
+
+ if (format.bytesPerPixel == 1)
+ transBlit<byte>(src, srcRect, &_innerSurface, destRect, transColor, flipped, overrideColor);
+ else if (format.bytesPerPixel == 2)
+ transBlit<uint16>(src, srcRect, &_innerSurface, destRect, transColor, flipped, overrideColor);
+ else if (format.bytesPerPixel == 4)
+ transBlit<uint32>(src, srcRect, &_innerSurface, destRect, transColor, flipped, overrideColor);
+ else
+ error("Surface::transBlitFrom: bytesPerPixel must be 1, 2, or 4");
+
+ // Mark the affected area
+ addDirtyRect(destRect);
+}
+
+void ManagedSurface::markAllDirty() {
+ addDirtyRect(Common::Rect(0, 0, this->w, this->h));
+}
+
+void ManagedSurface::addDirtyRect(const Common::Rect &r) {
+ if (_owner) {
+ Common::Rect bounds = r;
+ bounds.clip(Common::Rect(0, 0, this->w, this->h));
+ bounds.translate(_offsetFromOwner.x, _offsetFromOwner.y);
+ _owner->addDirtyRect(bounds);
+ }
+}
+
+void ManagedSurface::clear(uint color) {
+ fillRect(getBounds(), color);
+}
+
+} // End of namespace Graphics
diff --git a/graphics/managed_surface.h b/graphics/managed_surface.h
new file mode 100644
index 0000000000..8cbd463266
--- /dev/null
+++ b/graphics/managed_surface.h
@@ -0,0 +1,376 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GRAPHICS_MANAGED_SURFACE_H
+#define GRAPHICS_MANAGED_SURFACE_H
+
+#include "graphics/pixelformat.h"
+#include "graphics/surface.h"
+#include "common/rect.h"
+#include "common/types.h"
+
+namespace Graphics {
+
+class Font;
+
+/**
+ * A derived graphics surface, which handles automatically managing the allocated
+ * surface data block, as well as introducing several new blitting methods
+ */
+class ManagedSurface {
+ friend class Font;
+private:
+ /**
+ * The Graphics::Surface that the managed surface encapsulates
+ */
+ Surface _innerSurface;
+
+ /**
+ * If set, the inner surface will be freed when the surface is recreated,
+ * as well as when the surface is destroyed
+ */
+ DisposeAfterUse::Flag _disposeAfterUse;
+
+ /**
+ * Stores the owning surface if this If this managed surface represents
+ * a sub-section of another
+ */
+ ManagedSurface *_owner;
+
+ /**
+ * For sub-section areas of an owning parent managed surface, this represents
+ * the offset from the parent's top-left corner this sub-surface starts at
+ */
+ Common::Point _offsetFromOwner;
+protected:
+ /**
+ * Clips the given source bounds so the passed destBounds will be entirely on-screen
+ */
+ bool clip(Common::Rect &srcBounds, Common::Rect &destBounds);
+
+ /**
+ * Base method that descendent classes can override for recording affected
+ * dirty areas of the surface
+ */
+ virtual void addDirtyRect(const Common::Rect &r);
+public:
+ uint16 &w;
+ uint16 &h;
+ uint16 &pitch;
+ PixelFormat &format;
+public:
+ /**
+ * Create the managed surface
+ */
+ ManagedSurface();
+
+ /**
+ * Create a managed surface from another one.
+ * If the source surface is maintaining it's own surface data, then
+ * this surface will create it's own surface of the same size and copy
+ * the contents from the source surface
+ */
+ ManagedSurface(ManagedSurface &surf);
+
+ /**
+ * Create the managed surface
+ */
+ ManagedSurface(int width, int height);
+
+ /**
+ * Create the managed surface
+ */
+ ManagedSurface(int width, int height, const Graphics::PixelFormat &pixelFormat);
+
+ /**
+ * Create the managed surface
+ */
+ ManagedSurface(ManagedSurface &surf, const Common::Rect &bounds);
+
+ /**
+ * Destroy the managed surface
+ */
+ virtual ~ManagedSurface();
+
+ /**
+ * Implements automatic conversion to a Graphics::Surface by
+ * simply returning the inner surface. This must be const,
+ * because we don't want changes being done directly to it,
+ * since it would bypass dirty rect handling
+ */
+ operator const Surface &() const { return _innerSurface; }
+ const Surface &rawSurface() const { return _innerSurface; }
+
+ /**
+ * Reassign one managed surface to another one
+ * Note that if the source has a managed surface, it will be duplicated
+ */
+ ManagedSurface &operator=(ManagedSurface &surf);
+
+ /**
+ * Returns true if the surface has not yet been allocated
+ */
+ bool empty() const { return w == 0 || h == 0 || _innerSurface.getPixels() == nullptr; }
+
+ /**
+ * Returns true if the surface is managing its own pixels
+ */
+ DisposeAfterUse::Flag disposeAfterUse() const { return _disposeAfterUse; }
+
+ /**
+ * Return a pointer to the pixel at the specified point.
+ *
+ * @param x The x coordinate of the pixel.
+ * @param y The y coordinate of the pixel.
+ * @return Pointer to the pixel.
+ */
+ inline const void *getBasePtr(int x, int y) const {
+ return _innerSurface.getBasePtr(x, y);
+ }
+
+ /**
+ * Return a pointer to the pixel at the specified point.
+ *
+ * @param x The x coordinate of the pixel.
+ * @param y The y coordinate of the pixel.
+ * @return Pointer to the pixel.
+ */
+ inline void *getBasePtr(int x, int y) {
+ return _innerSurface.getBasePtr(x, y);
+ }
+
+ /**
+ * Get a reference to the pixel data
+ */
+ inline void *getPixels() { return _innerSurface.getPixels(); }
+ inline const void *getPixels() const { return _innerSurface.getPixels(); }
+
+ /**
+ * Sets the pixel data.
+ */
+ virtual void setPixels(void *newPixels);
+
+ /**
+ * Allocate memory for the pixel data of the surface.
+ */
+ virtual void create(uint16 width, uint16 height);
+
+ /**
+ * Allocate memory for the pixel data of the surface.
+ */
+ virtual void create(uint16 width, uint16 height, const PixelFormat &pixelFormat);
+
+ /**
+ * Sets up the surface as a sub-section of another passed parent surface. This surface
+ * will not own the pixels, and any dirty rect notifications will automatically be
+ * passed to the original parent surface.
+ * @remarks Note that this differs from Graphics::Surface::getSubArea, in that that
+ * method only adds a single initial dirty rect for the whole area, and then none further
+ */
+ virtual void create(ManagedSurface &surf, const Common::Rect &bounds);
+
+ /**
+ * Release the memory used by the pixels memory of this surface. This is the
+ * counterpart to create().
+ */
+ virtual void free();
+
+ /**
+ * Clears any pending dirty rects that have been generated for the surface
+ */
+ virtual void clearDirtyRects() {}
+
+ /**
+ * When the managed surface is a sub-section of a parent surface, returns the
+ * the offset in the parent surface that the surface starts at
+ */
+ const Common::Point getOffsetFromOwner() const { return _offsetFromOwner; }
+
+ /**
+ * Return a rect giving the bounds of the surface
+ */
+ const Common::Rect getBounds() const {
+ return Common::Rect(0, 0, this->w, this->h);
+ }
+
+ /**
+ * Copies another surface into this one
+ */
+ void blitFrom(const Surface &src);
+
+ /**
+ * Copies another surface into this one at a given destination position
+ */
+ void blitFrom(const Surface &src, const Common::Point &destPos);
+
+ /**
+ * Copies another surface into this one at a given destination position
+ */
+ void blitFrom(const Surface &src, const Common::Rect &srcRect,
+ const Common::Point &destPos);
+
+ /**
+ * Copies another surface into this one ignoring pixels of a designated transparent color
+ * @param src Source surface
+ * @param transColor Transparency color to ignore copying
+ * @param flipped Specifies whether to horizontally flip the image
+ * @param overrideColor Optional color to use instead of non-transparent pixels from
+ * the source surface
+ */
+ void transBlitFrom(const Surface &src, uint transColor = 0, bool flipped = false, uint overrideColor = 0);
+
+ /**
+ * Copies another surface into this one ignoring pixels of a designated transparent color
+ * @param src Source surface
+ * @param destPos Destination position to draw the surface
+ * @param transColor Transparency color to ignore copying
+ * @param flipped Specifies whether to horizontally flip the image
+ * @param overrideColor Optional color to use instead of non-transparent pixels from
+ * the source surface
+ */
+ void transBlitFrom(const Surface &src, const Common::Point &destPos,
+ uint transColor = 0, bool flipped = false, uint overrideColor = 0);
+
+ /**
+ * Copies another surface into this one ignoring pixels of a designated transparent color
+ * @param src Source surface
+ * @param srcRect Sub-section of source surface to draw
+ * @param destPos Destination position to draw the surface
+ * @param transColor Transparency color to ignore copying
+ * @param flipped Specifies whether to horizontally flip the image
+ * @param overrideColor Optional color to use instead of non-transparent pixels from
+ * the source surface
+ */
+ void transBlitFrom(const Surface &src, const Common::Rect &srcRect, const Common::Point &destPos,
+ uint transColor = 0, bool flipped = false, uint overrideColor = 0);
+
+ /**
+ * Copies another surface into this one ignoring pixels of a designated transparent color
+ * @param src Source surface
+ * @param srcRect Sub-section of source surface to draw
+ * @param destRect Destination area to draw the surface in. This can be sized differently
+ * then srcRect, allowing for arbitrary scaling of the image
+ * @param transColor Transparency color to ignore copying
+ * @param flipped Specifies whether to horizontally flip the image
+ * @param overrideColor Optional color to use instead of non-transparent pixels from
+ * the source surface
+ */
+ void transBlitFrom(const Surface &src, const Common::Rect &srcRect, const Common::Rect &destRect,
+ uint transColor = 0, bool flipped = false, uint overrideColor = 0);
+
+ /**
+ * Clear the entire surface
+ */
+ void clear(uint color = 0);
+
+ /**
+ * Mark the entire surface as dirty
+ */
+ void markAllDirty();
+
+ /**
+ * Copies a bitmap to the Surface internal buffer. The pixel format
+ * of buffer must match the pixel format of the Surface.
+ */
+ void copyRectToSurface(const void *buffer, int srcPitch, int destX, int destY, int width, int height) {
+ _innerSurface.copyRectToSurface(buffer, srcPitch, destX, destY, width, height);
+ }
+
+ /**
+ * Copies a bitmap to the Surface internal buffer. The pixel format
+ * of buffer must match the pixel format of the Surface.
+ */
+ void copyRectToSurface(const Graphics::Surface &srcSurface, int destX, int destY, const Common::Rect subRect) {
+ _innerSurface.copyRectToSurface(srcSurface, destX, destY, subRect);
+ }
+
+ /**
+ * Copy the data from another Surface, reinitializing the
+ * surface to match the dimensions of the passed surface
+ */
+ void copyFrom(const ManagedSurface &surf) {
+ clearDirtyRects();
+ _innerSurface.copyFrom(surf._innerSurface);
+ }
+
+ /**
+ * Draw a line.
+ */
+ void drawLine(int x0, int y0, int x1, int y1, uint32 color) {
+ _innerSurface.drawLine(x0, y0, x1, y1, color);
+ addDirtyRect(Common::Rect(x0, y0, x1, y1));
+ }
+
+ /**
+ * Draw a thick line.
+ */
+ void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, uint32 color) {
+ _innerSurface.drawThickLine(x0, y0, x1, y1, penX, penY, color);
+ addDirtyRect(Common::Rect(x0, y0, x1 + penX, y1 + penY));
+ }
+
+ /**
+ * Draw a horizontal line.
+ */
+ void hLine(int x, int y, int x2, uint32 color) {
+ _innerSurface.hLine(x, y, x2, color);
+ addDirtyRect(Common::Rect(x, y, x2 + 1, y + 1));
+ }
+
+ /**
+ * Draw a vertical line.
+ */
+ void vLine(int x, int y, int y2, uint32 color) {
+ _innerSurface.vLine(x, y, y2, color);
+ addDirtyRect(Common::Rect(x, y, x + 1, y2 + 1));
+ }
+
+ /**
+ * Fill a rect with a given color.
+ */
+ void fillRect(Common::Rect r, uint32 color) {
+ _innerSurface.fillRect(r, color);
+ addDirtyRect(r);
+ }
+
+ /**
+ * Draw a frame around a specified rect.
+ */
+ void frameRect(const Common::Rect &r, uint32 color) {
+ _innerSurface.frameRect(r, color);
+ addDirtyRect(r);
+ }
+
+ /**
+ * Returns a sub-area of the screen, but only adds a single initial dirty rect
+ * for the retrieved area.
+ */
+ Surface getSubArea(const Common::Rect &area) {
+ addDirtyRect(area);
+ return _innerSurface.getSubArea(area);
+ }
+};
+
+} // End of namespace Graphics
+
+
+#endif
diff --git a/graphics/module.mk b/graphics/module.mk
index 2705322c79..1c87e74ba7 100644
--- a/graphics/module.mk
+++ b/graphics/module.mk
@@ -12,9 +12,17 @@ MODULE_OBJS := \
fonts/ttf.o \
fonts/winfont.o \
maccursor.o \
+ macgui/macmenu.o \
+ macgui/macwindow.o \
+ macgui/macwindowborder.o \
+ macgui/macwindowmanager.o \
+ managed_surface.o \
+ nine_patch.o \
+ pixelformat.o \
primitives.o \
scaler.o \
scaler/thumbnail_intern.o \
+ screen.o \
sjis.o \
surface.o \
transform_struct.o \
diff --git a/graphics/nine_patch.cpp b/graphics/nine_patch.cpp
new file mode 100644
index 0000000000..fa2ef20a6e
--- /dev/null
+++ b/graphics/nine_patch.cpp
@@ -0,0 +1,359 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/* This code is based on Nine Patch code by Matthew Leverton
+ taken from https://github.com/konforce/Allegro-Nine-Patch
+
+ Copyright (C) 2011 Matthew Leverton
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
+ this software and associated documentation files (the "Software"), to deal in
+ the Software without restriction, including without limitation the rights to
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is furnished to do
+ so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+
+#include "common/array.h"
+#include "graphics/transparent_surface.h"
+#include "graphics/nine_patch.h"
+
+#include "graphics/managed_surface.h"
+
+namespace Graphics {
+
+NinePatchSide::~NinePatchSide() {
+ for (uint i = 0; i < _m.size(); i++)
+ delete _m[i];
+
+ _m.clear();
+}
+
+
+bool NinePatchSide::init(Graphics::TransparentSurface *bmp, bool vertical) {
+ const uint len = vertical ? bmp->h : bmp->w;
+ uint i;
+ int s, t, z;
+
+ _m.clear();
+
+ for (i = 1, s = -1, t = 0, z = -1; i < len; ++i) {
+ int zz;
+ uint8 r, g, b, a;
+ uint32 *color = vertical ? (uint32 *)bmp->getBasePtr(0, i) : (uint32 *)bmp->getBasePtr(i, 0);
+ bmp->format.colorToARGB(*color, a, r, g, b);
+
+ if (i == len - 1)
+ zz = -1;
+ else if (r == 0 && g == 0 && b == 0 && a == 255)
+ zz = 0;
+ else if (a == 0 || r + g + b + a == 255 * 4)
+ zz = 1;
+ else
+ return false;
+
+ if (z != zz) {
+ if (s != -1) {
+ NinePatchMark *mrk = new NinePatchMark;
+
+ mrk->offset = s;
+ mrk->length = i - s;
+ if (z == 0) {
+ mrk->ratio = 1;
+ t += mrk->length;
+ } else {
+ mrk->ratio = 0;
+ }
+ _m.push_back(mrk);
+ }
+ s = i;
+ z = zz;
+ }
+ }
+
+ _fix = len - 2 - t;
+ for (i = 0; i < _m.size(); ++i) {
+ if (_m[i]->ratio)
+ _m[i]->ratio = _m[i]->length / (float)t;
+ }
+
+ return true;
+}
+
+void NinePatchSide::calcOffsets(int len) {
+ uint i, j;
+ int dest_offset = 0;
+ int remaining_stretch = len - _fix;
+
+ for (i = 0, j = 0; i < _m.size(); ++i) {
+ _m[i]->dest_offset = dest_offset;
+ if (_m[i]->ratio == 0) {
+ _m[i]->dest_length = _m[i]->length;
+ } else {
+ _m[i]->dest_length = (len - _fix) * _m[i]->ratio;
+ remaining_stretch -= _m[i]->dest_length;
+ j = i;
+ }
+
+ dest_offset += _m[i]->dest_length;
+ }
+
+ if (remaining_stretch) {
+ _m[j]->dest_length += remaining_stretch;
+ if (j + 1 < _m.size())
+ _m[j + 1]->dest_offset += remaining_stretch;
+ }
+}
+
+NinePatchBitmap::NinePatchBitmap(Graphics::TransparentSurface *bmp, bool owns_bitmap) {
+ int i;
+ uint8 r, g, b, a;
+
+ _bmp = bmp;
+ _destroy_bmp = owns_bitmap;
+ _h._m.clear();
+ _v._m.clear();
+ _cached_dw = 0;
+ _cached_dh = 0;
+ _width = bmp->w - 2;
+ _height = bmp->h - 2;
+
+ if (_width <= 0 || _height <= 0)
+ goto bad_bitmap;
+
+ /* make sure all four corners are transparent */
+#define _check_pixel(x, y) \
+ bmp->format.colorToARGB(*(uint32 *)bmp->getBasePtr(x, y), a, r, g, b); \
+ if (a != 0 && r + g + b + a != 4) goto bad_bitmap;
+
+ _check_pixel(0,0);
+ _check_pixel(bmp->w - 1, 0);
+ _check_pixel(0, bmp->h - 1);
+ _check_pixel(bmp->w - 1, bmp->h - 1);
+#undef _check_pixel
+
+ _padding.top = _padding.right = _padding.bottom = _padding.left = -1;
+
+ i = 1;
+ while (i < bmp->w) {
+ bmp->format.colorToARGB(*(uint32 *)bmp->getBasePtr(i, bmp->h - 1), a, r, g, b);
+
+ if (r + g + b == 0 && a == 1) {
+ if (_padding.left == -1)
+ _padding.left = i - 1;
+ else if (_padding.right != -1)
+ goto bad_bitmap;
+ } else if (a == 0 || r + g + b + a == 4) {
+ if (_padding.left != -1 && _padding.right == -1)
+ _padding.right = bmp->w - i - 1;
+ }
+ ++i;
+ }
+
+ i = 1;
+ while (i < bmp->h) {
+ bmp->format.colorToARGB(*(uint32 *)bmp->getBasePtr(bmp->w - 1, i), a, r, g, b);
+
+ if (r + g + b == 0 && a == 1) {
+ if (_padding.top == -1)
+ _padding.top = i - 1;
+ else if (_padding.bottom != -1)
+ goto bad_bitmap;
+ } else if (a == 0 || r + g + b + a == 4) {
+ if (_padding.top != -1 && _padding.bottom == -1)
+ _padding.bottom = bmp->h - i - 1;
+ }
+ ++i;
+ }
+
+ if (!_h.init(bmp, false) || !_v.init(bmp, true)) {
+bad_bitmap:
+ _h._m.clear();
+ _v._m.clear();
+ }
+}
+
+void NinePatchBitmap::blit(Graphics::Surface &target, int dx, int dy, int dw, int dh, byte *palette, byte numColors) {
+ /* don't draw bitmaps that are smaller than the fixed area */
+ if (dw < _h._fix || dh < _v._fix)
+ return;
+
+ /* if the bitmap is the same size as the origin, then draw it as-is */
+ if (dw == _width && dh == _height) {
+ Common::Rect r(1, 1, dw, dh);
+
+ _bmp->blit(target, dx, dy, Graphics::FLIP_NONE, &r);
+ return;
+ }
+
+ /* only recalculate the offsets if they have changed since the last draw */
+ if (_cached_dw != dw || _cached_dh != dh) {
+ _h.calcOffsets(dw);
+ _v.calcOffsets(dh);
+
+ _cached_dw = dw;
+ _cached_dh = dh;
+ }
+
+ /* Handle CLUT8 */
+ if (target.format.bytesPerPixel == 1) {
+ if (!palette)
+ warning("Trying to blit into a surface with 1bpp, you need the palette.");
+
+ Surface srf;
+ srf.create(target.w, target.h, _bmp->format);
+
+ drawRegions(srf, dx, dy, dw, dh);
+
+ //TODO: This can be further optimized by keeping the data between draws,
+ // and using a unique identifier for each palette, so that it only gets
+ // recalculated when the palette changes.
+ _cached_colors.clear();
+
+ for (uint i = 0; i < srf.w; ++i) {
+ for (uint j = 0; j < srf.h; ++j) {
+ uint32 color = *(uint32*)srf.getBasePtr(i, j);
+ if (color > 0) {
+ *((byte *)target.getBasePtr(i, j)) = closestGrayscale(color, palette, numColors);
+ }
+ }
+ }
+
+ return;
+ }
+
+ /* Else, draw regions normally */
+ drawRegions(target, dx, dy, dw, dh);
+}
+
+NinePatchBitmap::~NinePatchBitmap() {
+ if (_destroy_bmp)
+ delete _bmp;
+}
+
+void NinePatchBitmap::drawRegions(Graphics::Surface &target, int dx, int dy, int dw, int dh) {
+ /* draw each region */
+ for (uint i = 0; i < _v._m.size(); ++i) {
+ for (uint j = 0; j < _h._m.size(); ++j) {
+ Common::Rect r(_h._m[j]->offset, _v._m[i]->offset,
+ _h._m[j]->offset + _h._m[j]->length, _v._m[i]->offset + _v._m[i]->length);
+
+ _bmp->blit(target, dx + _h._m[j]->dest_offset, dy + _v._m[i]->dest_offset,
+ Graphics::FLIP_NONE, &r, TS_ARGB(255, 255, 255, 255),
+ _h._m[j]->dest_length, _v._m[i]->dest_length);
+ }
+ }
+}
+
+void NinePatchBitmap::blitClip(Graphics::Surface &target, Common::Rect clip, int dx, int dy, int dw, int dh) {
+ /* don't draw bitmaps that are smaller than the fixed area */
+ if (dw < _h._fix || dh < _v._fix)
+ return;
+
+ /* if the bitmap is the same size as the origin, then draw it as-is */
+ if (dw == _width && dh == _height) {
+ Common::Rect r(1, 1, dw, dh);
+
+ _bmp->blitClip(target, clip, dx, dy, Graphics::FLIP_NONE, &r);
+ return;
+ }
+
+ /* only recalculate the offsets if they have changed since the last draw */
+ if (_cached_dw != dw || _cached_dh != dh) {
+ _h.calcOffsets(dw);
+ _v.calcOffsets(dh);
+
+ _cached_dw = dw;
+ _cached_dh = dh;
+ }
+
+ /* draw each region */
+ for (uint i = 0; i < _v._m.size(); ++i) {
+ for (uint j = 0; j < _h._m.size(); ++j) {
+ Common::Rect r(_h._m[j]->offset, _v._m[i]->offset,
+ _h._m[j]->offset + _h._m[j]->length, _v._m[i]->offset + _v._m[i]->length);
+
+ _bmp->blitClip(target, clip, dx + _h._m[j]->dest_offset, dy + _v._m[i]->dest_offset,
+ Graphics::FLIP_NONE, &r, TS_ARGB(255, 255, 255, 255),
+ _h._m[j]->dest_length, _v._m[i]->dest_length);
+ }
+ }
+}
+
+byte NinePatchBitmap::getColorIndex(uint32 target, byte* palette) {
+ byte *pal = palette;
+ uint i = 0;
+ uint32 color = TS_RGB(pal[0], pal[1], pal[2]);
+ while (color != target) {
+ i += 3;
+ color = TS_RGB(pal[i], pal[i + 1], pal[i + 2]);
+ }
+ return (i / 3);
+}
+
+uint32 NinePatchBitmap::grayscale(uint32 color) {
+ byte r, g, b;
+ _bmp->format.colorToRGB(color, r, g, b);
+ return grayscale(r, g, b);
+}
+
+uint32 NinePatchBitmap::grayscale(byte r, byte g, byte b) {
+ return (0.29 * r + 0.58 * g + 0.11 * b) / 3;
+}
+
+static inline uint32 dist(uint32 a, uint32 b) {
+ if (a > b)
+ return (a - b);
+
+ return b - a;
+}
+
+byte NinePatchBitmap::closestGrayscale(uint32 color, byte* palette, byte paletteLength) {
+ if (!_cached_colors.contains(color)) {
+ byte target = grayscale(color);
+ byte bestNdx = 0;
+ byte bestColor = grayscale(palette[0], palette[1], palette[2]);
+ for (byte i = 1; i < paletteLength; ++i) {
+ byte current = grayscale(palette[i * 3], palette[(i * 3) + 1], palette[(i * 3) + 2]);
+ if (dist(target, bestColor) >= dist(target, current)) {
+ bestColor = current;
+ bestNdx = i;
+ }
+ }
+ _cached_colors[color] = bestNdx;
+ }
+
+ return _cached_colors[color];
+}
+
+} // end of namespace Graphics
diff --git a/graphics/nine_patch.h b/graphics/nine_patch.h
new file mode 100644
index 0000000000..faf44e553f
--- /dev/null
+++ b/graphics/nine_patch.h
@@ -0,0 +1,114 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/* This code is based on Nine Patch code by Matthew Leverton
+ taken from https://github.com/konforce/Allegro-Nine-Patch
+
+ Copyright (C) 2011 Matthew Leverton
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
+ this software and associated documentation files (the "Software"), to deal in
+ the Software without restriction, including without limitation the rights to
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is furnished to do
+ so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+#ifndef GRAPHICS_NINE_PATCH_H
+#define GRAPHICS_NINE_PATCH_H
+
+#include "common/array.h"
+#include "common/rect.h"
+#include "common/hashmap.h"
+
+namespace Graphics {
+
+struct TransparentSurface;
+struct Surface;
+
+struct NinePatchMark {
+ int offset;
+ int length;
+ int dest_offset;
+ int dest_length;
+ float ratio;
+};
+
+class NinePatchSide {
+public:
+ Common::Array<NinePatchMark *> _m;
+ int _fix;
+
+ NinePatchSide() : _fix(0) {}
+ ~NinePatchSide();
+
+ bool init(Graphics::TransparentSurface *bmp, bool vertical);
+ void calcOffsets(int len);
+};
+
+class NinePatchBitmap {
+ Graphics::TransparentSurface *_bmp;
+ NinePatchSide _h, _v;
+ Common::Rect _padding;
+ bool _destroy_bmp;
+ int _width, _height;
+ int _cached_dw, _cached_dh;
+ Common::HashMap<uint32, int> _cached_colors;
+
+public:
+ NinePatchBitmap(Graphics::TransparentSurface *bmp, bool owns_bitmap);
+ ~NinePatchBitmap();
+
+ void blit(Graphics::Surface &target, int dx, int dy, int dw, int dh, byte *palette = NULL, byte numColors = 0);
+ void blitClip(Graphics::Surface &target, Common::Rect clip, int dx, int dy, int dw, int dh);
+
+ int getWidth() { return _width; }
+ int getHeight() { return _height; }
+ int getMinWidth() { return _h._fix; }
+ int getMinHeight() { return _v._fix; }
+ Graphics::TransparentSurface *getSource() { return _bmp; }
+ Common::Rect &getPadding() { return _padding; }
+
+private:
+
+ void drawRegions(Graphics::Surface &target, int dx, int dy, int dw, int dh);
+
+ // Assumes color is in the palette
+ byte getColorIndex(uint32 target, byte *palette);
+ uint32 grayscale(uint32 color);
+ uint32 grayscale(byte r, byte g, byte b);
+ byte closestGrayscale(uint32 color, byte* palette, byte paletteLength);
+};
+
+} // end of namespace Graphics
+
+#endif // GRAPHICS_NINE_PATCH_H
diff --git a/graphics/pixelformat.cpp b/graphics/pixelformat.cpp
new file mode 100644
index 0000000000..0a46411254
--- /dev/null
+++ b/graphics/pixelformat.cpp
@@ -0,0 +1,64 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "graphics/pixelformat.h"
+#include "common/algorithm.h"
+
+namespace Graphics {
+
+Common::String PixelFormat::toString() const {
+ if (bytesPerPixel == 1)
+ return "CLUT8";
+
+ // We apply a trick to simplify the code here. We encode all the shift,
+ // loss, and component name in the component entry. By having the shift as
+ // highest entry we can sort according to shift.
+ // This works because in valid RGB PixelFormats shift values needs to be
+ // distinct except when the loss is 8. However, components with loss value
+ // of 8 are not printed, thus their position does not matter.
+ int component[4];
+ component[0] = (rShift << 16) | (rLoss << 8) | 'R';
+ component[1] = (gShift << 16) | (gLoss << 8) | 'G';
+ component[2] = (bShift << 16) | (bLoss << 8) | 'B';
+ component[3] = (aShift << 16) | (aLoss << 8) | 'A';
+
+ // Sort components according to descending shift value.
+ Common::sort(component, component + ARRAYSIZE(component), Common::Greater<int>());
+
+ Common::String letters, digits;
+ for (int i = 0; i < ARRAYSIZE(component); ++i) {
+ const int componentLoss = (component[i] >> 8) & 0xFF;
+ // A loss of 8 means that the component does not exist.
+ if (componentLoss == 8) {
+ continue;
+ }
+
+ const char componentName = component[i] & 0xFF;
+
+ letters += componentName;
+ digits += '0' + 8 - componentLoss;
+ }
+
+ return letters + digits;
+}
+
+} // End of namespace Graphics
diff --git a/graphics/pixelformat.h b/graphics/pixelformat.h
index 00db6702fc..9dd06241b7 100644
--- a/graphics/pixelformat.h
+++ b/graphics/pixelformat.h
@@ -24,6 +24,7 @@
#define GRAPHICS_PIXELFORMAT_H
#include "common/scummsys.h"
+#include "common/str.h"
namespace Graphics {
@@ -260,6 +261,8 @@ struct PixelFormat {
// Unsupported
return 0;
}
+
+ Common::String toString() const;
};
} // End of namespace Graphics
diff --git a/graphics/primitives.cpp b/graphics/primitives.cpp
index 564bdb9673..8663a61606 100644
--- a/graphics/primitives.cpp
+++ b/graphics/primitives.cpp
@@ -20,7 +20,9 @@
*
*/
+#include "common/algorithm.h"
#include "common/util.h"
+#include "graphics/primitives.h"
namespace Graphics {
@@ -62,6 +64,22 @@ void drawLine(int x0, int y0, int x1, int y1, int color, void (*plotProc)(int, i
}
}
+void drawHLine(int x1, int x2, int y, int color, void (*plotProc)(int, int, int, void *), void *data) {
+ if (x1 > x2)
+ SWAP(x1, x2);
+
+ for (int x = x1; x <= x2; x++)
+ (*plotProc)(x, y, color, data);
+}
+
+void drawVLine(int x, int y1, int y2, int color, void (*plotProc)(int, int, int, void *), void *data) {
+ if (y1 > y2)
+ SWAP(y1, y2);
+
+ for (int y = y1; y <= y2; y++)
+ (*plotProc)(x, y, color, data);
+}
+
void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, int color, void (*plotProc)(int, int, int, void *), void *data) {
assert(penX > 0 && penY > 0);
@@ -79,4 +97,333 @@ void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, int color
drawLine(x0 + x, y0 + y, x1 + x, y1 + y, color, plotProc, data);
}
+/* Bresenham as presented in Foley & Van Dam */
+/* Code is based on GD lib http://libgd.github.io/ */
+void drawThickLine2(int x1, int y1, int x2, int y2, int thick, int color, void (*plotProc)(int, int, int, void *), void *data) {
+ int incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag;
+ int wid;
+ int w, wstart;
+
+ int dx = abs(x2 - x1);
+ int dy = abs(y2 - y1);
+
+ if (dx == 0) {
+ int xn = x1 - thick / 2;
+ Common::Rect r(xn, MIN(y1, y2), xn + thick - 1, MAX(y1, y2));
+ drawFilledRect(r, color, plotProc, data);
+ return;
+ } else if (dy == 0) {
+ int yn = y1 - thick / 2;
+ Common::Rect r(MIN(x1, x2), yn, MAX(x1, x2), yn + thick - 1);
+ drawFilledRect(r, color, plotProc, data);
+ return;
+ }
+
+ if (dy <= dx) {
+ /* More-or-less horizontal. use wid for vertical stroke */
+ /* Doug Claar: watch out for NaN in atan2 (2.0.5) */
+
+ /* 2.0.12: Michael Schwartz: divide rather than multiply;
+ TBB: but watch out for /0! */
+ double ac = cos(atan2((double)dy, (double)dx));
+ if (ac != 0) {
+ wid = thick / ac;
+ } else {
+ wid = 1;
+ }
+ if (wid == 0) {
+ wid = 1;
+ }
+ d = 2 * dy - dx;
+ incr1 = 2 * dy;
+ incr2 = 2 * (dy - dx);
+ if (x1 > x2) {
+ x = x2;
+ y = y2;
+ ydirflag = (-1);
+ xend = x1;
+ } else {
+ x = x1;
+ y = y1;
+ ydirflag = 1;
+ xend = x2;
+ }
+
+ /* Set up line thickness */
+ wstart = y - wid / 2;
+ for (w = wstart; w < wstart + wid; w++)
+ (*plotProc)(x, y, color, data);
+
+ if (((y2 - y1) * ydirflag) > 0) {
+ while (x < xend) {
+ x++;
+ if (d < 0) {
+ d += incr1;
+ } else {
+ y++;
+ d += incr2;
+ }
+ wstart = y - wid / 2;
+ for (w = wstart; w < wstart + wid; w++)
+ (*plotProc)(x, w, color, data);
+ }
+ } else {
+ while (x < xend) {
+ x++;
+ if (d < 0) {
+ d += incr1;
+ } else {
+ y--;
+ d += incr2;
+ }
+ wstart = y - wid / 2;
+ for (w = wstart; w < wstart + wid; w++)
+ (*plotProc)(x, w, color, data);
+ }
+ }
+ } else {
+ /* More-or-less vertical. use wid for horizontal stroke */
+ /* 2.0.12: Michael Schwartz: divide rather than multiply;
+ TBB: but watch out for /0! */
+ double as = sin(atan2((double)dy, (double)dx));
+ if (as != 0) {
+ wid = thick / as;
+ } else {
+ wid = 1;
+ }
+ if (wid == 0)
+ wid = 1;
+
+ d = 2 * dx - dy;
+ incr1 = 2 * dx;
+ incr2 = 2 * (dx - dy);
+ if (y1 > y2) {
+ y = y2;
+ x = x2;
+ yend = y1;
+ xdirflag = (-1);
+ } else {
+ y = y1;
+ x = x1;
+ yend = y2;
+ xdirflag = 1;
+ }
+
+ /* Set up line thickness */
+ wstart = x - wid / 2;
+ for (w = wstart; w < wstart + wid; w++)
+ (*plotProc)(w, y, color, data);
+
+ if (((x2 - x1) * xdirflag) > 0) {
+ while (y < yend) {
+ y++;
+ if (d < 0) {
+ d += incr1;
+ } else {
+ x++;
+ d += incr2;
+ }
+ wstart = x - wid / 2;
+ for (w = wstart; w < wstart + wid; w++)
+ (*plotProc)(w, y, color, data);
+ }
+ } else {
+ while (y < yend) {
+ y++;
+ if (d < 0) {
+ d += incr1;
+ } else {
+ x--;
+ d += incr2;
+ }
+ wstart = x - wid / 2;
+ for (w = wstart; w < wstart + wid; w++)
+ (*plotProc)(w, y, color, data);
+ }
+ }
+ }
+}
+
+void drawFilledRect(Common::Rect &rect, int color, void (*plotProc)(int, int, int, void *), void *data) {
+ for (int y = rect.top; y <= rect.bottom; y++)
+ drawHLine(rect.left, rect.right, y, color, plotProc, data);
+}
+
+// http://members.chello.at/easyfilter/bresenham.html
+void drawRoundRect(Common::Rect &rect, int arc, int color, bool filled, void (*plotProc)(int, int, int, void *), void *data) {
+ if (rect.height() < rect.width()) {
+ int x = -arc, y = 0, err = 2-2*arc; /* II. Quadrant */
+ int dy = rect.height() - arc * 2;
+ int r = arc;
+ int stop = 0;
+ int lastx = 0, lasty = 0;
+ if (dy < 0)
+ stop = -dy / 2;
+
+ do {
+ if (filled) {
+ drawHLine(rect.left+x+r, rect.right-x-r, rect.top-y+r-stop, color, plotProc, data);
+ drawHLine(rect.left+x+r, rect.right-x-r, rect.bottom+y-r+stop, color, plotProc, data);
+ } else {
+ (*plotProc)(rect.left+x+r, rect.top-y+r-stop, color, data);
+ (*plotProc)(rect.right-x-r, rect.top-y+r-stop, color, data);
+ (*plotProc)(rect.left+x+r, rect.bottom+y-r+stop, color, data);
+ (*plotProc)(rect.right-x-r, rect.bottom+y-r+stop, color, data);
+
+ lastx = x;
+ lasty = y;
+ }
+ arc = err;
+ if (arc <= y) err += ++y*2+1; /* e_xy+e_y < 0 */
+ if (arc > x || err > y) err += ++x*2+1; /* e_xy+e_x > 0 or no 2nd y-step */
+ if (stop && y > stop)
+ break;
+ } while (x < 0);
+
+ if (!filled) {
+ x = lastx;
+ y = lasty;
+
+ drawHLine(rect.left+x+r, rect.right-x-r, rect.top-y+r-stop, color, plotProc, data);
+ drawHLine(rect.left+x+r, rect.right-x-r, rect.bottom+y-r+stop, color, plotProc, data);
+ }
+
+ for (int i = 0; i < dy; i++) {
+ if (filled) {
+ drawHLine(rect.left, rect.right, rect.top + r + i, color, plotProc, data);
+ } else {
+ (*plotProc)(rect.left, rect.top + r + i, color, data);
+ (*plotProc)(rect.right, rect.top + r + i, color, data);
+ }
+ }
+ } else {
+ int y = -arc, x = 0, err = 2-2*arc; /* II. Quadrant */
+ int dx = rect.width() - arc * 2;
+ int r = arc;
+ int stop = 0;
+ int lastx = 0, lasty = 0;
+ if (dx < 0)
+ stop = -dx / 2;
+
+ do {
+ if (filled) {
+ drawVLine(rect.left-x+r-stop, rect.top+y+r, rect.bottom-y-r, color, plotProc, data);
+ drawVLine(rect.right+x-r+stop, rect.top+y+r, rect.bottom-y-r, color, plotProc, data);
+ } else {
+ (*plotProc)(rect.left-x+r-stop, rect.top+y+r, color, data);
+ (*plotProc)(rect.left-x+r-stop, rect.bottom-y-r, color, data);
+ (*plotProc)(rect.right+x-r+stop, rect.top+y+r, color, data);
+ (*plotProc)(rect.right+x-r+stop, rect.bottom-y-r, color, data);
+
+ lastx = x;
+ lasty = y;
+ }
+
+ arc = err;
+ if (arc <= x) err += ++x*2+1; /* e_xy+e_y < 0 */
+ if (arc > y || err > x) err += ++y*2+1; /* e_xy+e_x > 0 or no 2nd y-step */
+ if (stop && x > stop)
+ break;
+ } while (y < 0);
+
+ if (!filled) {
+ x = lastx;
+ y = lasty;
+ drawVLine(rect.left-x+r-stop, rect.top+y+r, rect.bottom-y-r, color, plotProc, data);
+ drawVLine(rect.right+x-r+stop, rect.top+y+r, rect.bottom-y-r, color, plotProc, data);
+ }
+
+ for (int i = 0; i < dx; i++) {
+ if (filled) {
+ drawVLine(rect.left + r + i, rect.top, rect.bottom, color, plotProc, data);
+ } else {
+ (*plotProc)(rect.left + r + i, rect.top, color, data);
+ (*plotProc)(rect.left + r + i, rect.bottom, color, data);
+ }
+ }
+ }
+}
+
+// Based on public-domain code by Darel Rex Finley, 2007
+// http://alienryderflex.com/polygon_fill/
+void drawPolygonScan(int *polyX, int *polyY, int npoints, Common::Rect &bbox, int color, void (*plotProc)(int, int, int, void *), void *data) {
+ int *nodeX = (int *)calloc(npoints, sizeof(int));
+ int i, j;
+
+ // Loop through the rows of the image.
+ for (int pixelY = bbox.top; pixelY < bbox.bottom; pixelY++) {
+ // Build a list of nodes.
+ int nodes = 0;
+ j = npoints - 1;
+
+ for (i = 0; i < npoints; i++) {
+ if ((polyY[i] < pixelY && polyY[j] >= pixelY) || (polyY[j] < pixelY && polyY[i] >= pixelY)) {
+ nodeX[nodes++] = (int)(polyX[i] + (double)(pixelY - polyY[i]) / (double)(polyY[j]-polyY[i]) *
+ (double)(polyX[j] - polyX[i]) + 0.5);
+ }
+ j = i;
+ }
+
+ // Sort the nodes
+ Common::sort(nodeX, &nodeX[nodes]);
+
+ // Fill the pixels between node pairs.
+ for (i = 0; i < nodes; i += 2) {
+ if (nodeX[i ] >= bbox.right)
+ break;
+ if (nodeX[i + 1] > bbox.left) {
+ nodeX[i] = MAX<int16>(nodeX[i], bbox.left);
+ nodeX[i + 1] = MIN<int16>(nodeX[i + 1], bbox.right);
+
+ drawHLine(nodeX[i], nodeX[i + 1], pixelY, color, plotProc, data);
+ }
+ }
+ }
+
+ free(nodeX);
+}
+
+// http://members.chello.at/easyfilter/bresenham.html
+void drawEllipse(int x0, int y0, int x1, int y1, int color, bool filled, void (*plotProc)(int, int, int, void *), void *data) {
+ int a = abs(x1-x0), b = abs(y1-y0), b1 = b&1; /* values of diameter */
+ long dx = 4*(1-a)*b*b, dy = 4*(b1+1)*a*a; /* error increment */
+ long err = dx+dy+b1*a*a, e2; /* error of 1.step */
+
+ if (x0 > x1) { x0 = x1; x1 += a; } /* if called with swapped points */
+ if (y0 > y1) y0 = y1; /* .. exchange them */
+ y0 += (b+1)/2; y1 = y0-b1; /* starting pixel */
+ a *= 8*a; b1 = 8*b*b;
+
+ do {
+ if (filled) {
+ drawHLine(x0, x1, y0, color, plotProc, data);
+ drawHLine(x0, x1, y1, color, plotProc, data);
+ } else {
+ (*plotProc)(x1, y0, color, data); /* I. Quadrant */
+ (*plotProc)(x0, y0, color, data); /* II. Quadrant */
+ (*plotProc)(x0, y1, color, data); /* III. Quadrant */
+ (*plotProc)(x1, y1, color, data); /* IV. Quadrant */
+ }
+ e2 = 2*err;
+ if (e2 <= dy) { y0++; y1--; err += dy += a; } /* y step */
+ if (e2 >= dx || 2*err > dy) { x0++; x1--; err += dx += b1; } /* x step */
+ } while (x0 <= x1);
+
+ while (y0-y1 < b) { /* too early stop of flat ellipses a=1 */
+ if (filled) {
+ drawHLine(x0-1, x0-1, y0, color, plotProc, data); /* -> finish tip of ellipse */
+ drawHLine(x1+1, x1+1, y0, color, plotProc, data);
+ drawHLine(x0-1, x0-1, y1, color, plotProc, data);
+ drawHLine(x1+1, x1+1, y1, color, plotProc, data);
+ } else {
+ (*plotProc)(x0-1, y0, color, data); /* -> finish tip of ellipse */
+ (*plotProc)(x1+1, y0, color, data);
+ (*plotProc)(x0-1, y1, color, data);
+ (*plotProc)(x1+1, y1, color, data);
+ }
+ y0++;
+ y1--;
+ }
+}
+
} // End of namespace Graphics
diff --git a/graphics/primitives.h b/graphics/primitives.h
index a3e8ab1565..62dc10bfdf 100644
--- a/graphics/primitives.h
+++ b/graphics/primitives.h
@@ -23,10 +23,21 @@
#ifndef GRAPHICS_PRIMITIVES_H
#define GRAPHICS_PRIMITIVES_H
+#include "common/rect.h"
+
namespace Graphics {
void drawLine(int x0, int y0, int x1, int y1, int color, void (*plotProc)(int, int, int, void *), void *data);
+void drawHLine(int x1, int x2, int y, int color, void (*plotProc)(int, int, int, void *), void *data);
+void drawVLine(int x, int y1, int y2, int color, void (*plotProc)(int, int, int, void *), void *data);
void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, int color, void (*plotProc)(int, int, int, void *), void *data);
+void drawThickLine2(int x1, int y1, int x2, int y2, int thick, int color,
+ void (*plotProc)(int, int, int, void *), void *data);
+void drawFilledRect(Common::Rect &rect, int color, void (*plotProc)(int, int, int, void *), void *data);
+void drawRoundRect(Common::Rect &rect, int arc, int color, bool filled, void (*plotProc)(int, int, int, void *), void *data);
+void drawPolygonScan(int *polyX, int *polyY, int npoints, Common::Rect &bbox, int color,
+ void (*plotProc)(int, int, int, void *), void *data);
+void drawEllipse(int x0, int y0, int x1, int y1, int color, bool filled, void (*plotProc)(int, int, int, void *), void *data);
} // End of namespace Graphics
diff --git a/graphics/scaler/thumbnail_intern.cpp b/graphics/scaler/thumbnail_intern.cpp
index 65e327f9c2..78fb7828d7 100644
--- a/graphics/scaler/thumbnail_intern.cpp
+++ b/graphics/scaler/thumbnail_intern.cpp
@@ -171,7 +171,8 @@ static bool grabScreen565(Graphics::Surface *surf) {
if (!screen)
return false;
- assert(screen->format.bytesPerPixel == 1 || screen->format.bytesPerPixel == 2);
+ assert(screen->format.bytesPerPixel == 1 || screen->format.bytesPerPixel == 2
+ || screen->format.bytesPerPixel == 4);
assert(screen->getPixels() != 0);
Graphics::PixelFormat screenFormat = g_system->getScreenFormat();
@@ -197,6 +198,9 @@ static bool grabScreen565(Graphics::Surface *surf) {
} else if (screenFormat.bytesPerPixel == 2) {
uint16 col = READ_UINT16(screen->getBasePtr(x, y));
screenFormat.colorToRGB(col, r, g, b);
+ } else if (screenFormat.bytesPerPixel == 4) {
+ uint32 col = READ_UINT32(screen->getBasePtr(x, y));
+ screenFormat.colorToRGB(col, r, g, b);
}
*((uint16 *)surf->getBasePtr(x, y)) = Graphics::RGBToColor<Graphics::ColorMasks<565> >(r, g, b);
diff --git a/graphics/screen.cpp b/graphics/screen.cpp
new file mode 100644
index 0000000000..ccf651f536
--- /dev/null
+++ b/graphics/screen.cpp
@@ -0,0 +1,129 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/system.h"
+#include "common/algorithm.h"
+#include "graphics/screen.h"
+#include "graphics/palette.h"
+
+namespace Graphics {
+
+Screen::Screen(): ManagedSurface() {
+ create(g_system->getWidth(), g_system->getHeight(), g_system->getScreenFormat());
+}
+
+Screen::Screen(int width, int height): ManagedSurface() {
+ create(width, height);
+}
+
+Screen::Screen(int width, int height, PixelFormat pixelFormat): ManagedSurface() {
+ create(width, height, pixelFormat);
+}
+
+void Screen::update() {
+ // Merge the dirty rects
+ mergeDirtyRects();
+
+ // Loop through copying dirty areas to the physical screen
+ Common::List<Common::Rect>::iterator i;
+ for (i = _dirtyRects.begin(); i != _dirtyRects.end(); ++i) {
+ const Common::Rect &r = *i;
+ const byte *srcP = (const byte *)getBasePtr(r.left, r.top);
+ g_system->copyRectToScreen(srcP, pitch, r.left, r.top,
+ r.width(), r.height());
+ }
+
+ // Signal the physical screen to update
+ g_system->updateScreen();
+ _dirtyRects.clear();
+}
+
+
+void Screen::addDirtyRect(const Common::Rect &r) {
+ Common::Rect bounds = r;
+ bounds.clip(getBounds());
+ bounds.translate(getOffsetFromOwner().x, getOffsetFromOwner().y);
+
+ if (bounds.width() > 0 && bounds.height() > 0)
+ _dirtyRects.push_back(bounds);
+}
+
+void Screen::makeAllDirty() {
+ addDirtyRect(Common::Rect(0, 0, this->w, this->h));
+}
+
+void Screen::mergeDirtyRects() {
+ Common::List<Common::Rect>::iterator rOuter, rInner;
+
+ // Process the dirty rect list to find any rects to merge
+ for (rOuter = _dirtyRects.begin(); rOuter != _dirtyRects.end(); ++rOuter) {
+ rInner = rOuter;
+ while (++rInner != _dirtyRects.end()) {
+
+ if ((*rOuter).intersects(*rInner)) {
+ // These two rectangles overlap, so merge them
+ unionRectangle(*rOuter, *rOuter, *rInner);
+
+ // remove the inner rect from the list
+ _dirtyRects.erase(rInner);
+
+ // move back to beginning of list
+ rInner = rOuter;
+ }
+ }
+ }
+}
+
+bool Screen::unionRectangle(Common::Rect &destRect, const Common::Rect &src1, const Common::Rect &src2) {
+ destRect = src1;
+ destRect.extend(src2);
+
+ return !destRect.isEmpty();
+}
+
+void Screen::getPalette(byte palette[PALETTE_SIZE]) {
+ assert(format.bytesPerPixel == 1);
+ g_system->getPaletteManager()->grabPalette(palette, 0, PALETTE_COUNT);
+}
+
+void Screen::getPalette(byte *palette, uint start, uint num) {
+ assert(format.bytesPerPixel == 1);
+ g_system->getPaletteManager()->grabPalette(palette, start, num);
+}
+
+void Screen::setPalette(const byte palette[PALETTE_SIZE]) {
+ assert(format.bytesPerPixel == 1);
+ g_system->getPaletteManager()->setPalette(palette, 0, PALETTE_COUNT);
+}
+
+void Screen::setPalette(const byte *palette, uint start, uint num) {
+ assert(format.bytesPerPixel == 1);
+ g_system->getPaletteManager()->setPalette(palette, start, num);
+}
+
+void Screen::clearPalette() {
+ byte palette[PALETTE_SIZE];
+ Common::fill(&palette[0], &palette[PALETTE_SIZE], 0);
+ setPalette(palette);
+}
+
+} // End of namespace Graphics
diff --git a/graphics/screen.h b/graphics/screen.h
new file mode 100644
index 0000000000..b3bb2d3eb2
--- /dev/null
+++ b/graphics/screen.h
@@ -0,0 +1,118 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GRAPHICS_SCREEN_H
+#define GRAPHICS_SCREEN_H
+
+#include "graphics/managed_surface.h"
+#include "graphics/pixelformat.h"
+#include "common/list.h"
+#include "common/rect.h"
+
+namespace Graphics {
+
+#define PALETTE_COUNT 256
+#define PALETTE_SIZE (256 * 3)
+
+/**
+ * Implements a specialised surface that represents the screen.
+ * It keeps track of any areas of itself that are updated by drawing
+ * calls, and provides an update that method that blits the affected
+ * areas to the physical screen
+ */
+class Screen : public ManagedSurface {
+private:
+ /**
+ * List of affected areas of the screen
+ */
+ Common::List<Common::Rect> _dirtyRects;
+private:
+ /**
+ * Merges together overlapping dirty areas of the screen
+ */
+ void mergeDirtyRects();
+
+ /**
+ * Returns the union of two dirty area rectangles
+ */
+ bool unionRectangle(Common::Rect &destRect, const Common::Rect &src1, const Common::Rect &src2);
+protected:
+ /**
+ * Adds a rectangle to the list of modified areas of the screen during the
+ * current frame
+ */
+ virtual void addDirtyRect(const Common::Rect &r);
+public:
+ Screen();
+ Screen(int width, int height);
+ Screen(int width, int height, PixelFormat pixelFormat);
+
+ /**
+ * Returns true if there are any pending screen updates (dirty areas)
+ */
+ bool isDirty() const { return !_dirtyRects.empty(); }
+
+ /**
+ * Marks the whole screen as dirty. This forces the next call to update
+ * to copy the entire screen contents
+ */
+ void makeAllDirty();
+
+ /**
+ * Clear the current dirty rects list
+ */
+ virtual void clearDirtyRects() { _dirtyRects.clear(); }
+
+ /**
+ * Updates the screen by copying any affected areas to the system
+ */
+ virtual void update();
+
+ /**
+ * Return the currently active palette
+ */
+ void getPalette(byte palette[PALETTE_SIZE]);
+
+ /**
+ * Return a portion of the currently active palette
+ */
+ void getPalette(byte *palette, uint start, uint num);
+
+ /**
+ * Set the palette
+ */
+ void setPalette(const byte palette[PALETTE_SIZE]);
+
+ /**
+ * Set a subsection of the palette
+ */
+ void setPalette(const byte *palette, uint start, uint num);
+
+ /**
+ * Clears the current palette, setting all entries to black
+ */
+ void clearPalette();
+};
+
+} // End of namespace Graphics
+
+#endif
diff --git a/graphics/surface.cpp b/graphics/surface.cpp
index 67ed942b0b..699e1ccd22 100644
--- a/graphics/surface.cpp
+++ b/graphics/surface.cpp
@@ -498,4 +498,109 @@ Graphics::Surface *Surface::convertTo(const PixelFormat &dstFormat, const byte *
return surface;
}
+FloodFill::FloodFill(Graphics::Surface *surface, uint32 oldColor, uint32 fillColor, bool maskMode) {
+ _surface = surface;
+ _oldColor = oldColor;
+ _fillColor = fillColor;
+ _w = surface->w;
+ _h = surface->h;
+
+ _mask = nullptr;
+ _maskMode = maskMode;
+
+ if (_maskMode) {
+ _mask = new Graphics::Surface();
+ _mask->create(_w, _h, Graphics::PixelFormat::createFormatCLUT8()); // Uses calloc()
+ }
+
+ _visited = (byte *)calloc(_w * _h, 1);
+}
+
+FloodFill::~FloodFill() {
+ while(!_queue.empty()) {
+ Common::Point *p = _queue.front();
+
+ delete p;
+ _queue.pop_front();
+ }
+
+ free(_visited);
+
+ if (_mask)
+ delete _mask;
+}
+
+void FloodFill::addSeed(int x, int y) {
+ if (x >= 0 && x < _w && y >= 0 && y < _h) {
+ if (!_visited[y * _w + x]) {
+ _visited[y * _w + x] = 1;
+ void *src = _surface->getBasePtr(x, y);
+ void *dst;
+ bool changed = false;
+
+ if (_maskMode)
+ dst = _mask->getBasePtr(x, y);
+ else
+ dst = src;
+
+ if (_surface->format.bytesPerPixel == 1) {
+ if (*((byte *)src) == _oldColor) {
+ *((byte *)dst) = _maskMode ? 255 : _fillColor;
+ changed = true;
+ }
+ } else if (_surface->format.bytesPerPixel == 2) {
+ if (READ_UINT16(src) == _oldColor) {
+ if (!_maskMode)
+ WRITE_UINT16(src, _fillColor);
+ else
+ *((byte *)dst) = 255;
+
+ changed = true;
+ }
+ } else if (_surface->format.bytesPerPixel == 4) {
+ if (READ_UINT32(src) == _oldColor) {
+ if (!_maskMode)
+ WRITE_UINT32(src, _fillColor);
+ else
+ *((byte *)dst) = 255;
+
+ changed = true;
+ }
+ } else {
+ error("Unsupported bpp in FloodFill");
+ }
+
+ if (changed) {
+ Common::Point *pt = new Common::Point(x, y);
+
+ _queue.push_back(pt);
+ }
+ }
+ }
+}
+
+void FloodFill::fill() {
+ while (!_queue.empty()) {
+ Common::Point *p = _queue.front();
+ _queue.pop_front();
+ addSeed(p->x , p->y - 1);
+ addSeed(p->x - 1, p->y );
+ addSeed(p->x , p->y + 1);
+ addSeed(p->x + 1, p->y );
+
+ delete p;
+ }
+}
+
+void FloodFill::fillMask() {
+ _maskMode = true;
+
+ if (!_mask) {
+ _mask = new Graphics::Surface();
+ _mask->create(_w, _h, Graphics::PixelFormat::createFormatCLUT8()); // Uses calloc()
+ }
+
+ fill();
+}
+
} // End of namespace Graphics
diff --git a/graphics/surface.h b/graphics/surface.h
index aaa386b168..87c5f52503 100644
--- a/graphics/surface.h
+++ b/graphics/surface.h
@@ -24,9 +24,11 @@
#define GRAPHICS_SURFACE_H
#include "common/scummsys.h"
+#include "common/list.h"
namespace Common {
struct Rect;
+struct Point;
}
#include "graphics/pixelformat.h"
@@ -341,6 +343,72 @@ struct SharedPtrSurfaceDeleter {
}
};
+/**
+ * Stack-based flood fill algorithm for arbitrary Surfaces.
+ *
+ * It could be used in 2 ways. One is to fill the pixels of oldColor
+ * with fillColor. Second is when the surface stays intact but another
+ * surface with mask is created, where filled colors are marked with 255.
+ *
+ * Before running fill() or fillMask(), the initial pixels must be addSeed
+ * with addSeed() method.
+ */
+class FloodFill {
+public:
+ /**
+ * Construct a simple Surface object.
+ *
+ * @param surface Input surface
+ * @param oldColor Color on the surface to change
+ * @param fillColor Color to fill with
+ */
+ FloodFill(Surface *surface, uint32 oldColor, uint32 fillColor, bool maskMode = false);
+ ~FloodFill();
+
+ /**
+ * Add pixels to the fill queue.
+ *
+ * @param x The x coordinate of the pixel.
+ * @param y The x coordinate of the pixel.
+ */
+ void addSeed(int x, int y);
+
+ /**
+ * Fill the surface as requested.
+ *
+ * It uses pixels which were added with addSeed() method.
+ *
+ * @see addSeed
+ */
+ void fill();
+
+ /**
+ * Fill the mask. The mask is a CLUT8 Surface with pixels 0 and 255.
+ * 255 means that the pixel has been filled.
+ *
+ * It uses pixels which were added with addSeed() method.
+ *
+ * @see addSeed
+ */
+ void fillMask();
+
+ /**
+ * Get the resulting mask.
+ *
+ * @see fillMask
+ */
+ Surface *getMask() { return _mask; }
+
+private:
+ Common::List<Common::Point *> _queue;
+ Surface *_surface;
+ Surface *_mask;
+ uint32 _oldColor, _fillColor;
+ byte *_visited;
+ int _w, _h;
+
+ bool _maskMode;
+};
} // End of namespace Graphics
diff --git a/graphics/thumbnail.cpp b/graphics/thumbnail.cpp
index 802ad09cbb..a3037e5ad5 100644
--- a/graphics/thumbnail.cpp
+++ b/graphics/thumbnail.cpp
@@ -67,7 +67,7 @@ HeaderState loadHeader(Common::SeekableReadStream &in, ThumbnailHeader &header,
header.size = in.readUint32BE();
header.version = in.readByte();
- // Do a check whether any read errors had occured. If so we cannot use the
+ // Do a check whether any read errors had occurred. 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
diff --git a/graphics/transparent_surface.cpp b/graphics/transparent_surface.cpp
index f6e8cacb8b..81b7f3d647 100644
--- a/graphics/transparent_surface.cpp
+++ b/graphics/transparent_surface.cpp
@@ -41,8 +41,6 @@
namespace Graphics {
-static const int kAShift = 0;//img->format.aShift;
-
static const int kBModShift = 0;//img->format.bShift;
static const int kGModShift = 8;//img->format.gShift;
static const int kRModShift = 16;//img->format.rShift;
@@ -118,7 +116,7 @@ void doBlitBinaryFast(byte *ino, byte *outo, uint32 width, uint32 height, uint32
in = ino;
for (uint32 j = 0; j < width; j++) {
uint32 pix = *(uint32 *)in;
- int a = (pix >> kAShift) & 0xff;
+ int a = in[kAIndex];
if (a != 0) { // Full opacity (Any value not exactly 0 is Opaque here)
*(uint32 *)out = pix;
@@ -338,7 +336,7 @@ Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int p
retSize.setWidth(0);
retSize.setHeight(0);
// Check if we need to draw anything at all
- int ca = (color >> 24) & 0xff;
+ int ca = (color >> kAModShift) & 0xff;
if (ca == 0) {
return retSize;
@@ -348,7 +346,7 @@ Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int p
TransparentSurface srcImage(*this, false);
// TODO: Is the data really in the screen format?
if (format.bytesPerPixel != 4) {
- warning("TransparentSurface can only blit 32bpp images, but got %d", format.bytesPerPixel * 8);
+ warning("TransparentSurface can only blit 32bpp images, but got %d", format.bytesPerPixel * 8);
return retSize;
}
@@ -464,6 +462,139 @@ Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int p
return retSize;
}
+Common::Rect TransparentSurface::blitClip(Graphics::Surface &target, Common::Rect clippingArea, int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, int width, int height, TSpriteBlendMode blendMode) {
+ Common::Rect retSize;
+ retSize.top = 0;
+ retSize.left = 0;
+ retSize.setWidth(0);
+ retSize.setHeight(0);
+ // Check if we need to draw anything at all
+ int ca = (color >> kAModShift) & 0xff;
+
+ if (ca == 0) {
+ return retSize;
+ }
+
+ // Create an encapsulating surface for the data
+ TransparentSurface srcImage(*this, false);
+ // TODO: Is the data really in the screen format?
+ if (format.bytesPerPixel != 4) {
+ warning("TransparentSurface can only blit 32bpp images, but got %d", format.bytesPerPixel * 8);
+ return retSize;
+ }
+
+ if (pPartRect) {
+
+ int xOffset = pPartRect->left;
+ int yOffset = pPartRect->top;
+
+ if (flipping & FLIP_V) {
+ yOffset = srcImage.h - pPartRect->bottom;
+ }
+
+ if (flipping & FLIP_H) {
+ xOffset = srcImage.w - pPartRect->right;
+ }
+
+ srcImage.pixels = getBasePtr(xOffset, yOffset);
+ srcImage.w = pPartRect->width();
+ srcImage.h = pPartRect->height();
+
+ debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping,
+ pPartRect->left, pPartRect->top, pPartRect->width(), pPartRect->height(), color, width, height);
+ } else {
+
+ debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping, 0, 0,
+ srcImage.w, srcImage.h, color, width, height);
+ }
+
+ if (width == -1) {
+ width = srcImage.w;
+ }
+ if (height == -1) {
+ height = srcImage.h;
+ }
+
+#ifdef SCALING_TESTING
+ // Hardcode scaling to 66% to test scaling
+ width = width * 2 / 3;
+ height = height * 2 / 3;
+#endif
+
+ Graphics::Surface *img = nullptr;
+ Graphics::Surface *imgScaled = nullptr;
+ byte *savedPixels = nullptr;
+ if ((width != srcImage.w) || (height != srcImage.h)) {
+ // Scale the image
+ img = imgScaled = srcImage.scale(width, height);
+ savedPixels = (byte *)img->getPixels();
+ } else {
+ img = &srcImage;
+ }
+
+ // Handle off-screen clipping
+ if (posY < clippingArea.top) {
+ img->h = MAX(0, (int)img->h - (clippingArea.top - posY));
+ img->setPixels((byte *)img->getBasePtr(0, clippingArea.top - posY));
+ posY = clippingArea.top;
+ }
+
+ if (posX < clippingArea.left) {
+ img->w = MAX(0, (int)img->w - (clippingArea.left - posX));
+ img->setPixels((byte *)img->getBasePtr(clippingArea.left - posX, 0));
+ posX = clippingArea.left;
+ }
+
+ img->w = CLIP((int)img->w, 0, (int)MAX((int)clippingArea.right - posX, 0));
+ img->h = CLIP((int)img->h, 0, (int)MAX((int)clippingArea.bottom - posY, 0));
+
+ if ((img->w > 0) && (img->h > 0)) {
+ int xp = 0, yp = 0;
+
+ int inStep = 4;
+ int inoStep = img->pitch;
+ if (flipping & FLIP_H) {
+ inStep = -inStep;
+ xp = img->w - 1;
+ }
+
+ if (flipping & FLIP_V) {
+ inoStep = -inoStep;
+ yp = img->h - 1;
+ }
+
+ byte *ino = (byte *)img->getBasePtr(xp, yp);
+ byte *outo = (byte *)target.getBasePtr(posX, posY);
+
+ if (color == 0xFFFFFFFF && blendMode == BLEND_NORMAL && _alphaMode == ALPHA_OPAQUE) {
+ doBlitOpaqueFast(ino, outo, img->w, img->h, target.pitch, inStep, inoStep);
+ } else if (color == 0xFFFFFFFF && blendMode == BLEND_NORMAL && _alphaMode == ALPHA_BINARY) {
+ doBlitBinaryFast(ino, outo, img->w, img->h, target.pitch, inStep, inoStep);
+ } else {
+ if (blendMode == BLEND_ADDITIVE) {
+ doBlitAdditiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
+ } else if (blendMode == BLEND_SUBTRACTIVE) {
+ doBlitSubtractiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
+ } else {
+ assert(blendMode == BLEND_NORMAL);
+ doBlitAlphaBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
+ }
+ }
+
+ }
+
+ retSize.setWidth(img->w);
+ retSize.setHeight(img->h);
+
+ if (imgScaled) {
+ imgScaled->setPixels(savedPixels);
+ imgScaled->free();
+ delete imgScaled;
+ }
+
+ return retSize;
+}
+
/**
* Writes a color key to the alpha channel of the surface
* @param rKey the red component of the color key
@@ -848,4 +979,82 @@ TransparentSurface *TransparentSurface::scale(uint16 newWidth, uint16 newHeight)
}
+TransparentSurface *TransparentSurface::convertTo(const PixelFormat &dstFormat, const byte *palette) const {
+ assert(pixels);
+
+ TransparentSurface *surface = new TransparentSurface();
+
+ // If the target format is the same, just copy
+ if (format == dstFormat) {
+ surface->copyFrom(*this);
+ return surface;
+ }
+
+ if (format.bytesPerPixel == 0 || format.bytesPerPixel > 4)
+ error("Surface::convertTo(): Can only convert from 1Bpp, 2Bpp, 3Bpp, and 4Bpp");
+
+ if (dstFormat.bytesPerPixel != 2 && dstFormat.bytesPerPixel != 4)
+ error("Surface::convertTo(): Can only convert to 2Bpp and 4Bpp");
+
+ surface->create(w, h, dstFormat);
+
+ if (format.bytesPerPixel == 1) {
+ // Converting from paletted to high color
+ assert(palette);
+
+ for (int y = 0; y < h; y++) {
+ const byte *srcRow = (const byte *)getBasePtr(0, y);
+ byte *dstRow = (byte *)surface->getBasePtr(0, y);
+
+ for (int x = 0; x < w; x++) {
+ byte index = *srcRow++;
+ byte r = palette[index * 3];
+ byte g = palette[index * 3 + 1];
+ byte b = palette[index * 3 + 2];
+
+ uint32 color = dstFormat.RGBToColor(r, g, b);
+
+ if (dstFormat.bytesPerPixel == 2)
+ *((uint16 *)dstRow) = color;
+ else
+ *((uint32 *)dstRow) = color;
+
+ dstRow += dstFormat.bytesPerPixel;
+ }
+ }
+ } else {
+ // Converting from high color to high color
+ for (int y = 0; y < h; y++) {
+ const byte *srcRow = (const byte *)getBasePtr(0, y);
+ byte *dstRow = (byte *)surface->getBasePtr(0, y);
+
+ for (int x = 0; x < w; x++) {
+ uint32 srcColor;
+ if (format.bytesPerPixel == 2)
+ srcColor = READ_UINT16(srcRow);
+ else if (format.bytesPerPixel == 3)
+ srcColor = READ_UINT24(srcRow);
+ else
+ srcColor = READ_UINT32(srcRow);
+
+ srcRow += format.bytesPerPixel;
+
+ // Convert that color to the new format
+ byte r, g, b, a;
+ format.colorToARGB(srcColor, a, r, g, b);
+ uint32 color = dstFormat.ARGBToColor(a, r, g, b);
+
+ if (dstFormat.bytesPerPixel == 2)
+ *((uint16 *)dstRow) = color;
+ else
+ *((uint32 *)dstRow) = color;
+
+ dstRow += dstFormat.bytesPerPixel;
+ }
+ }
+ }
+
+ return surface;
+}
+
} // End of namespace Graphics
diff --git a/graphics/transparent_surface.h b/graphics/transparent_surface.h
index efb28149a5..8654183548 100644
--- a/graphics/transparent_surface.h
+++ b/graphics/transparent_surface.h
@@ -50,22 +50,22 @@ namespace Graphics {
@brief The possible flipping parameters for the blit method.
*/
enum FLIP_FLAGS {
- /// The image will not be flipped.
- FLIP_NONE = 0,
- /// The image will be flipped at the horizontal axis.
- FLIP_H = 1,
- /// The image will be flipped at the vertical axis.
- FLIP_V = 2,
- /// The image will be flipped at the horizontal and vertical axis.
- FLIP_HV = FLIP_H | FLIP_V,
- /// The image will be flipped at the horizontal and vertical axis.
- FLIP_VH = FLIP_H | FLIP_V
+ /// The image will not be flipped.
+ FLIP_NONE = 0,
+ /// The image will be flipped at the horizontal axis.
+ FLIP_H = 1,
+ /// The image will be flipped at the vertical axis.
+ FLIP_V = 2,
+ /// The image will be flipped at the horizontal and vertical axis.
+ FLIP_HV = FLIP_H | FLIP_V,
+ /// The image will be flipped at the horizontal and vertical axis.
+ FLIP_VH = FLIP_H | FLIP_V
};
enum AlphaType {
- ALPHA_OPAQUE = 0,
- ALPHA_BINARY = 1,
- ALPHA_FULL = 2
+ ALPHA_OPAQUE = 0,
+ ALPHA_BINARY = 1,
+ ALPHA_FULL = 2
};
/**
@@ -75,6 +75,18 @@ struct TransparentSurface : public Graphics::Surface {
TransparentSurface();
TransparentSurface(const Graphics::Surface &surf, bool copyData = false);
+ /**
+ * Returns the pixel format all operations of TransparentSurface support.
+ *
+ * Unlike Surface TransparentSurface only works with a fixed pixel format.
+ * This format can be queried using this static function.
+ *
+ * @return Supported pixel format.
+ */
+ static PixelFormat getSupportedPixelFormat() {
+ return PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
+ }
+
void setColorKey(char r, char g, char b);
void disableColorKey();
@@ -111,6 +123,14 @@ struct TransparentSurface : public Graphics::Surface {
uint color = TS_ARGB(255, 255, 255, 255),
int width = -1, int height = -1,
TSpriteBlendMode blend = BLEND_NORMAL);
+ Common::Rect blitClip(Graphics::Surface &target, Common::Rect clippingArea,
+ int posX = 0, int posY = 0,
+ int flipping = FLIP_NONE,
+ Common::Rect *pPartRect = nullptr,
+ uint color = TS_ARGB(255, 255, 255, 255),
+ int width = -1, int height = -1,
+ TSpriteBlendMode blend = BLEND_NORMAL);
+
void applyColorKey(uint8 r, uint8 g, uint8 b, bool overwriteAlpha = false);
/**
@@ -131,6 +151,16 @@ struct TransparentSurface : public Graphics::Surface {
*
*/
TransparentSurface *rotoscale(const TransformStruct &transform) const;
+
+ TransparentSurface *convertTo(const PixelFormat &dstFormat, const byte *palette = 0) const;
+
+ float getRatio() {
+ if (!w)
+ return 0;
+
+ return h / (float)w;
+ }
+
AlphaType getAlphaMode() const;
void setAlphaMode(AlphaType);
private: