ScummVM API documentation
surface.h
1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #ifndef AGS_LIB_ALLEGRO_SURFACE_H
23 #define AGS_LIB_ALLEGRO_SURFACE_H
24 
25 #include "graphics/managed_surface.h"
26 #include "ags/lib/allegro/base.h"
27 #include "ags/lib/allegro/color.h"
28 #include "common/array.h"
29 
30 namespace AGS3 {
31 
32 class BITMAP {
33 private:
35  public:
36  int16 &w, &h;
37  int32 &pitch;
38  Graphics::PixelFormat &format;
39  bool clip;
40  int ct, cb, cl, cr;
42 public:
44  virtual ~BITMAP() {
45  }
46 
47  Graphics::ManagedSurface &operator*() const {
48  return *_owner;
49  }
50  Graphics::ManagedSurface &getSurface() {
51  return *_owner;
52  }
53  const Graphics::ManagedSurface &getSurface() const {
54  return *_owner;
55  }
56 
57  unsigned char *getPixels() const {
58  return (unsigned char *)_owner->getPixels();
59  }
60 
61  unsigned char *getBasePtr(uint16 x, uint16 y) const {
62  return (unsigned char *)_owner->getBasePtr(x, y);
63  }
64 
65  uint getTransparentColor() const {
66  // See allegro bitmap_mask_color
67  // For paletted sprites this is 0.
68  // For other color depths this is bright pink (RGB 255, 0, 255) with alpha set to 0.
69  if (format.bytesPerPixel == 1)
70  return 0;
71  return format.ARGBToColor(0, 255, 0, 255);
72  }
73 
74  inline const Common::Point getOffsetFromOwner() const {
75  return _owner->getOffsetFromOwner();
76  }
77 
78  int getpixel(int x, int y) const;
79 
80  void clear() {
81  _owner->clear();
82  }
83 
84  void makeOpaque();
85 
89  void circlefill(int x, int y, int radius, int color);
90 
94  void floodfill(int x, int y, int color);
95 
99  void hLine(int x, int y, int x2, uint32 color) {
100  _owner->hLine(x, y, x2, color);
101  }
102 
106  void vLine(int x, int y, int y2, uint32 color) {
107  _owner->vLine(x, y, y2, color);
108  }
109 
113  void draw(const BITMAP *srcBitmap, const Common::Rect &srcRect,
114  int dstX, int dstY, bool horizFlip, bool vertFlip,
115  bool skipTrans, int srcAlpha, int tintRed = -1, int tintGreen = -1,
116  int tintBlue = -1);
117 
121  void stretchDraw(const BITMAP *srcBitmap, const Common::Rect &srcRect,
122  const Common::Rect &destRect, bool skipTrans, int srcAlpha);
123 
124  inline bool isSubBitmap() const {
125  return _owner->disposeAfterUse() == DisposeAfterUse::NO;
126  }
127 
128  private:
129  // True color blender functions
130  // In Allegro all the blender functions are of the form
131  // unsigned int blender_func(unsigned long x, unsigned long y, unsigned long n)
132  // when x is the sprite color, y the destination color, and n an alpha value
133 
134  void blendPixel(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha, bool useTint, byte *destVal) const;
135 
136  inline void rgbBlend(uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
137  // Note: the original's handling varies slightly for R & B vs G.
138  // We need to exactly replicate it to ensure Lamplight City's
139  // calendar puzzle works correctly
140  if (alpha)
141  alpha++;
142 
143  uint32 x = ((uint32)rSrc << 16) | ((uint32)gSrc << 8) | (uint32)bSrc;
144  uint32 y = ((uint32)rDest << 16) | ((uint32)gDest << 8) | (uint32)bDest;
145 
146  uint32 res = ((x & 0xFF00FF) - (y & 0xFF00FF)) * alpha / 256 + y;
147  y &= 0xFF00;
148  x &= 0xFF00;
149  uint32 g = (x - y) * alpha / 256 + y;
150 
151  rDest = (res >> 16) & 0xff;
152  gDest = (g >> 8) & 0xff;
153  bDest = res & 0xff;
154  }
155 
156  inline void argbBlend(uint32 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest) const {
157  // Original logic has uint32 src and dst colors as ARGB8888
158  // ++src_alpha;
159  // uint32 dst_alpha = geta32(dst);
160  // if (dst_alpha)
161  // ++dst_alpha;
162  // uint32 dst_g = (dst & 0x00FF00) * dst_alpha / 256;
163  // dst = (dst & 0xFF00FF) * dst_alpha / 256;
164  // dst_g = (((src & 0x00FF00) - (dst_g & 0x00FF00)) * src_alpha / 256 + dst_g) & 0x00FF00;
165  // dst = (((src & 0xFF00FF) - (dst & 0xFF00FF)) * src_alpha / 256 + dst) & 0xFF00FF;
166  // dst_alpha = 256 - (256 - src_alpha) * (256 - dst_alpha) / 256;
167  // src_alpha = /* 256 * 256 == */ 0x10000 / dst_alpha;
168  // dst_g = (dst_g * src_alpha / 256) & 0x00FF00;
169  // dst = (dst * src_alpha / 256) & 0xFF00FF;
170  // return dst | dst_g | (--dst_alpha << 24);
171  double sAlpha = (double)(aSrc & 0xff) / 255.0;
172  double dAlpha = (double)aDest / 255.0;
173  dAlpha *= (1.0 - sAlpha);
174  rDest = static_cast<uint8>((rSrc * sAlpha + rDest * dAlpha) / (sAlpha + dAlpha));
175  gDest = static_cast<uint8>((gSrc * sAlpha + gDest * dAlpha) / (sAlpha + dAlpha));
176  bDest = static_cast<uint8>((bSrc * sAlpha + bDest * dAlpha) / (sAlpha + dAlpha));
177  aDest = static_cast<uint8>(255. * (sAlpha + dAlpha));
178  }
179 
180  // kRgbToRgbBlender
181  inline void blendRgbToRgb(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
182  // Default mode for set_trans_blender
183  rgbBlend(rSrc, gSrc, bSrc, rDest, gDest, bDest, alpha);
184  // Original doesn't set alpha (so it is 0), but the function is not meant to be used
185  // on bitmap with transparency. Should we set alpha to 0xff?
186  aDest = 0;
187  }
188 
189  // kAlphaPreservedBlenderMode
190  inline void blendPreserveAlpha(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
191  // Original blender function: _myblender_alpha_trans24
192  // Like blendRgbToRgb, but result as the same alpha as destColor
193  rgbBlend(rSrc, gSrc, bSrc, rDest, gDest, bDest, alpha);
194  // Preserve value in aDest
195  }
196 
197  // kArgbToArgbBlender
198  inline void blendArgbToArgb(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
199  // Original blender functions: _argb2argb_blender
200  if (alpha == 0)
201  alpha = aSrc;
202  else
203  alpha = aSrc * ((alpha & 0xff) + 1) / 256;
204  if (alpha != 0)
205  argbBlend(alpha, rSrc, gSrc, bSrc, aDest, rDest, gDest, bDest);
206  }
207 
208  // kRgbToArgbBlender
209  inline void blendRgbToArgb(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
210  // Original blender function: _rgb2argb_blenders
211  if (alpha == 0 || alpha == 0xff) {
212  aDest = 0xff;
213  rDest = rSrc;
214  gDest = gSrc;
215  bDest = bSrc;
216  } else
217  argbBlend(alpha, rSrc, gSrc, bSrc, aDest, rDest, gDest, bDest);
218  }
219 
220  // kArgbToRgbBlender
221  inline void blendArgbToRgb(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
222  // Original blender function: _argb2rgb_blender
223  if (alpha == 0)
224  alpha = aSrc;
225  else
226  alpha = aSrc * ((alpha & 0xff) + 1) / 256;
227  rgbBlend(rSrc, gSrc, bSrc, rDest, gDest, bDest, alpha);
228  // Original doesn't set alpha (so it is 0), but the function is not meant to be used
229  // on bitmap with transparency. Should we set alpha to 0xff?
230  aDest = 0;
231  }
232 
233  // kOpaqueBlenderMode
234  inline void blendOpaque(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
235  // Original blender function: _opaque_alpha_blender
236  aDest = 0xff;
237  rDest = rSrc;
238  gDest = gSrc;
239  bDest = bSrc;
240  }
241 
242  // kSourceAlphaBlender
243  inline void blendSourceAlpha(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
244  // Used after set_alpha_blender
245  // Uses alpha from source. Result is fully opaque
246  rgbBlend(rSrc, gSrc, bSrc, rDest, gDest, bDest, aSrc);
247  // Original doesn't set alpha (so it is 0), but the function is not meant to be used
248  // on bitmap with transparency. Should we set alpha to 0xff?
249  aDest = 0;
250  }
251 
252  // kAdditiveBlenderMode
253  inline void blendAdditiveAlpha(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha) const {
254  // Original blender function: _additive_alpha_copysrc_blender
255  rDest = rSrc;
256  gDest = gSrc;
257  bDest = bSrc;
258  uint32 a = (uint32)aSrc + (uint32)aDest;
259  if (a > 0xff)
260  aDest = 0xff;
261  else
262  aDest = static_cast<uint8>(a);
263  }
264 
265  // kTintBlenderMode and kTintLightBlenderMode
266  void blendTintSprite(uint8 aSrc, uint8 rSrc, uint8 gSrc, uint8 bSrc, uint8 &aDest, uint8 &rDest, uint8 &gDest, uint8 &bDest, uint32 alpha, bool light) const;
267 
268  friend class DrawInnerImpl_AVX2;
269  friend class DrawInnerImpl_SSE2;
270  friend class DrawInnerImpl_NEON;
271 
272  constexpr static int SCALE_THRESHOLD_BITS = 8;
273  constexpr static int SCALE_THRESHOLD = 1 << SCALE_THRESHOLD_BITS;
274  struct DrawInnerArgs {
275  const bool useTint, horizFlip, vertFlip, skipTrans;
276  bool sameFormat, shouldDraw;
277  int xStart, yStart, srcAlpha, tintRed, tintGreen, tintBlue, scaleX, scaleY;
278  uint32 transColor, alphaMask;
279  PALETTE palette;
280 
281  BlenderMode blenderMode;
282  Common::Rect dstRect, srcArea;
283 
284  BITMAP &dstBitmap;
285  const ::Graphics::ManagedSurface &src;
286  ::Graphics::Surface destArea;
287 
288  DrawInnerArgs(BITMAP *dstBitmap, const BITMAP *srcBitmap,
289  const Common::Rect &srcRect, const Common::Rect &dstRect,
290  bool skipTrans, int srcAlpha, bool horizFlip,
291  bool vertFlip, int tintRed, int tintGreen, int tintBlue,
292  bool doScale);
293  };
294 
295  template<bool Scale>
296  void drawGeneric(DrawInnerArgs &args);
297 #ifdef SCUMMVM_NEON
298  template<bool Scale>
299  void drawNEON(DrawInnerArgs &args);
300 #endif
301 #ifdef SCUMMVM_SSE2
302  template<bool Scale>
303  void drawSSE2(DrawInnerArgs &args);
304 #endif
305 #ifdef SCUMMVM_AVX2
306  template<bool Scale>
307  void drawAVX2(DrawInnerArgs &args);
308 #endif
309  template<int DestBytesPerPixel, int SrcBytesPerPixel, bool Scale>
310  void drawInnerGeneric(DrawInnerArgs &args);
311 
312  inline uint32 getColor(const byte *data, byte bpp) const {
313  switch (bpp) {
314  case 1:
315  return *data;
316  case 2:
317  return *(const uint16 *)data;
318  case 4:
319  return *(const uint32 *)data;
320  default:
321  error("Unsupported format in BITMAP::getColor");
322  }
323  }
324 };
325 
329 class Surface : public Graphics::ManagedSurface, public BITMAP {
330 public:
331  Surface(int width, int height, const Graphics::PixelFormat &pixelFormat) :
332  Graphics::ManagedSurface(width, height, pixelFormat), BITMAP(this) {
333  // Allegro uses 255, 0, 255 RGB as the transparent color
334  if (pixelFormat.bytesPerPixel == 2 || pixelFormat.bytesPerPixel == 4)
335  setTransparentColor(pixelFormat.RGBToColor(255, 0, 255));
336  }
337  Surface(Graphics::ManagedSurface &surf, const Common::Rect &bounds) :
338  Graphics::ManagedSurface(surf, bounds), BITMAP(this) {
339  // Allegro uses 255, 0, 255 RGB as the transparent color
340  if (surf.format.bytesPerPixel == 2 || surf.format.bytesPerPixel == 4)
341  setTransparentColor(surf.format.RGBToColor(255, 0, 255));
342  }
343  ~Surface() override {
344  }
345 };
346 
347 BITMAP *create_bitmap(int width, int height);
348 BITMAP *create_bitmap_ex(int color_depth, int width, int height);
349 BITMAP *create_sub_bitmap(BITMAP *parent, int x, int y, int width, int height);
350 BITMAP *create_video_bitmap(int width, int height);
351 BITMAP *create_system_bitmap(int width, int height);
352 void destroy_bitmap(BITMAP *bitmap);
353 
354 } // namespace AGS3
355 
356 #endif
void clear(uint32 color=0)
Definition: managed_surface.h:51
void floodfill(int x, int y, int color)
Definition: surface.h:67
Definition: pixelformat.h:138
DisposeAfterUse::Flag disposeAfterUse() const
Definition: managed_surface.h:217
const void * getBasePtr(int x, int y) const
Definition: managed_surface.h:250
Definition: rect.h:144
void vLine(int x, int y, int y2, uint32 color)
Definition: surface.h:106
void vLine(int x, int y, int y2, uint32 color)
Definition: managed_surface.h:746
Definition: surface.h:32
void hLine(int x, int y, int x2, uint32 color)
Definition: surface.h:99
void draw(const BITMAP *srcBitmap, const Common::Rect &srcRect, int dstX, int dstY, bool horizFlip, bool vertFlip, bool skipTrans, int srcAlpha, int tintRed=-1, int tintGreen=-1, int tintBlue=-1)
void * getPixels()
Definition: managed_surface.h:269
Definition: rect.h:45
Definition: surface.h:329
Definition: color.h:49
void NORETURN_PRE error(MSVC_PRINTF const char *s,...) GCC_PRINTF(1
const Common::Point getOffsetFromOwner() const
Definition: managed_surface.h:321
void circlefill(int x, int y, int radius, int color)
PixelFormat & format
Definition: managed_surface.h:120
uint32 ARGBToColor(uint8 a, uint8 r, uint8 g, uint8 b) const
Definition: pixelformat.h:223
void hLine(int x, int y, int x2, uint32 color)
Definition: managed_surface.h:738
void stretchDraw(const BITMAP *srcBitmap, const Common::Rect &srcRect, const Common::Rect &destRect, bool skipTrans, int srcAlpha)
Definition: ags.h:40
byte bytesPerPixel
Definition: pixelformat.h:139
uint32 RGBToColor(uint8 r, uint8 g, uint8 b) const
Definition: pixelformat.h:206