Date:2014-07-18 23:53:51 (3 months 12 days 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
2727
2828#include <SDL_gfxPrimitives.h>
2929
30#include <algorithm>
31#include <cassert>
3032#include <iostream>
3133
3234using namespace std;
...... 
154156    if (c.a == 255) {
155157        SDL_FillRect(raw, &re, c.pixelValue(raw->format));
156158    } 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);
158160    }
159161}
160162
...... 
178180    SDL_SetClipRect(raw,&rect);
179181}
180182
183void 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
181206void Surface::blit(Surface *destination, SDL_Rect container, Font::HAlign halign, Font::VAlign valign) const {
182207    switch (halign) {
183208    case Font::HAlignLeft:
...... 
203228
204229    blit(destination,container.x,container.y);
205230}
231
232static 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
237void 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
9999    void blitCenter(SDL_Surface *destination, int x, int y, int w=0, int h=0, int a=-1) const;
100100    void blitRight(SDL_Surface *destination, int x, int y, int w=0, int h=0, int a=-1) const;
101101
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
102112    SDL_Surface *raw;
103113    bool freeWhenDone;
104114    int halfW, halfH;

Archive Download the corresponding diff file



interactive