Date: | 2014-07-18 23:53:51 (9 years 8 months ago) |
---|---|
Author: | Maarten ter Huurne |
Commit: | 0dd027fcf4e7ea7fdc71f0708124d31ade02a59d |
Message: | Use our own alpha blended rectangle fill instead of SDL_gfx I want to remove the dependency on SDL_gfx, since only two functions from that library are actually used. Also this new blend implementation is more optimized, especially the 32bpp case, which is the one most platforms are using. |
Files: |
src/surface.cpp (4 diffs) src/surface.h (1 diff) |
Change Details
src/surface.cpp | ||
---|---|---|
27 | 27 | |
28 | 28 | #include <SDL_gfxPrimitives.h> |
29 | 29 | |
30 | #include <algorithm> | |
31 | #include <cassert> | |
30 | 32 | #include <iostream> |
31 | 33 | |
32 | 34 | using namespace std; |
... | ... | |
154 | 156 | if (c.a == 255) { |
155 | 157 | SDL_FillRect(raw, &re, c.pixelValue(raw->format)); |
156 | 158 | } else if (c.a != 0) { |
157 | boxRGBA(raw, re.x, re.y, re.x + re.w - 1, re.y + re.h - 1, c.r, c.g, c.b, c.a); | |
159 | fillRectAlpha(re, c); | |
158 | 160 | } |
159 | 161 | } |
160 | 162 | |
... | ... | |
178 | 180 | SDL_SetClipRect(raw,&rect); |
179 | 181 | } |
180 | 182 | |
183 | void Surface::applyClipRect(SDL_Rect& rect) { | |
184 | SDL_Rect clip; | |
185 | SDL_GetClipRect(raw, &clip); | |
186 | ||
187 | // Clip along X-axis. | |
188 | if (rect.x < clip.x) { | |
189 | rect.w = max(rect.x + rect.w - clip.x, 0); | |
190 | rect.x = clip.x; | |
191 | } | |
192 | if (rect.x + rect.w > clip.x + clip.w) { | |
193 | rect.w = max(clip.x + clip.w - rect.x, 0); | |
194 | } | |
195 | ||
196 | // Clip along Y-axis. | |
197 | if (rect.y < clip.y) { | |
198 | rect.h = max(rect.y + rect.h - clip.y, 0); | |
199 | rect.y = clip.y; | |
200 | } | |
201 | if (rect.y + rect.h > clip.y + clip.h) { | |
202 | rect.h = max(clip.y + clip.h - rect.y, 0); | |
203 | } | |
204 | } | |
205 | ||
181 | 206 | void Surface::blit(Surface *destination, SDL_Rect container, Font::HAlign halign, Font::VAlign valign) const { |
182 | 207 | switch (halign) { |
183 | 208 | case Font::HAlignLeft: |
... | ... | |
203 | 228 | |
204 | 229 | blit(destination,container.x,container.y); |
205 | 230 | } |
231 | ||
232 | static inline uint32_t mult8x4(uint32_t c, uint8_t a) { | |
233 | return ((((c >> 8) & 0x00FF00FF) * a) & 0xFF00FF00) | |
234 | | ((((c & 0x00FF00FF) * a) & 0xFF00FF00) >> 8); | |
235 | } | |
236 | ||
237 | void Surface::fillRectAlpha(SDL_Rect rect, RGBAColor c) { | |
238 | applyClipRect(rect); | |
239 | if (rect.w == 0 || rect.h == 0) { | |
240 | // Entire rectangle is outside clipping area. | |
241 | return; | |
242 | } | |
243 | ||
244 | if (SDL_MUSTLOCK(raw)) { | |
245 | if (SDL_LockSurface(raw) < 0) { | |
246 | return; | |
247 | } | |
248 | } | |
249 | ||
250 | SDL_PixelFormat *format = raw->format; | |
251 | uint32_t color = c.pixelValue(format); | |
252 | uint8_t alpha = c.a; | |
253 | ||
254 | uint8_t* edge = static_cast<uint8_t*>(raw->pixels) | |
255 | + rect.y * raw->pitch | |
256 | + rect.x * format->BytesPerPixel; | |
257 | ||
258 | // Blending: surf' = surf * (1 - alpha) + fill * alpha | |
259 | ||
260 | if (format->BytesPerPixel == 2) { | |
261 | uint32_t Rmask = format->Rmask; | |
262 | uint32_t Gmask = format->Gmask; | |
263 | uint32_t Bmask = format->Bmask; | |
264 | ||
265 | // Pre-multiply the fill color. We're hardcoding alpha to 1: 15/16bpp | |
266 | // modes are unlikely to have an alpha channel and even if they do, | |
267 | // the written alpha isn't used by gmenu2x. | |
268 | uint16_t f = (((color & Rmask) * alpha >> 8) & Rmask) | |
269 | | (((color & Gmask) * alpha >> 8) & Gmask) | |
270 | | (((color & Bmask) * alpha >> 8) & Bmask) | |
271 | | format->Amask; | |
272 | alpha = 255 - alpha; | |
273 | ||
274 | for (auto y = 0; y < rect.h; y++) { | |
275 | for (auto x = 0; x < rect.w; x++) { | |
276 | uint16_t& pixel = reinterpret_cast<uint16_t*>(edge)[x]; | |
277 | uint32_t R = ((pixel & Rmask) * alpha >> 8) & Rmask; | |
278 | uint32_t G = ((pixel & Gmask) * alpha >> 8) & Gmask; | |
279 | uint32_t B = ((pixel & Bmask) * alpha >> 8) & Bmask; | |
280 | pixel = uint16_t(R | G | B) + f; | |
281 | } | |
282 | edge += raw->pitch; | |
283 | } | |
284 | } else if (format->BytesPerPixel == 4) { | |
285 | // Assume the pixel format uses 8 bits per component; we don't care | |
286 | // which component is where since they all blend the same. | |
287 | uint32_t f = mult8x4(color, alpha); // pre-multiply the fill color | |
288 | alpha = 255 - alpha; | |
289 | ||
290 | for (auto y = 0; y < rect.h; y++) { | |
291 | for (auto x = 0; x < rect.w; x++) { | |
292 | uint32_t& pixel = reinterpret_cast<uint32_t*>(edge)[x]; | |
293 | pixel = mult8x4(pixel, alpha) + f; | |
294 | } | |
295 | edge += raw->pitch; | |
296 | } | |
297 | } else { | |
298 | assert(false); | |
299 | } | |
300 | ||
301 | if (SDL_MUSTLOCK(raw)) { | |
302 | SDL_UnlockSurface(raw); | |
303 | } | |
304 | } |
src/surface.h | ||
---|---|---|
99 | 99 | void blitCenter(SDL_Surface *destination, int x, int y, int w=0, int h=0, int a=-1) const; |
100 | 100 | void blitRight(SDL_Surface *destination, int x, int y, int w=0, int h=0, int a=-1) const; |
101 | 101 | |
102 | /** Draws the given rectangle on this surface in the given color, blended | |
103 | * according to the alpha value of the color argument. | |
104 | */ | |
105 | void fillRectAlpha(SDL_Rect rect, RGBAColor c); | |
106 | ||
107 | /** Clips the given rectangle against this surface's active clipping | |
108 | * rectangle. | |
109 | */ | |
110 | void applyClipRect(SDL_Rect& rect); | |
111 | ||
102 | 112 | SDL_Surface *raw; |
103 | 113 | bool freeWhenDone; |
104 | 114 | int halfW, halfH; |
Branches:
install_locations
master
opkrun
packages