/* SEL - Simple DirectMedia Layer Extension Library Copyright (C) 2002 Matej Knopp This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../sel-config.h" #include "painter.h" #include "../base/surface.h" #include "../base/error.h" namespace SEL { /////////////////////////////////////////////////////////////////////// ///////////// EMPTY (to avoid crashing) drawing functions ///////////// /////////////////////////////////////////////////////////////////////// static void putPixelInvalid (Surface *, Uint16, Uint16, Uint32) { } static void drawHLineInvalid (Surface *, Uint16, Uint16, Uint16, Uint32) { } static void drawVLineInvalid (Surface *, Uint16, Uint16, Uint16, Uint32) { } static void drawLineInvalid (Surface *, Uint16, Uint16, Uint16, Uint16, Uint32) { } GFXTable GFXInvalid = { putPixelInvalid, drawHLineInvalid, drawVLineInvalid, drawLineInvalid }; #define __SET_PIXEL(bpp, pixelData, color) \ *(Uint##bpp*)pixelData = color; // anyone knows about something faster? #if SDL_BYTEORDER == SDL_BIG_ENDIAN # define __SET_PIXEL24(data, b1, b2, b3) \ data [0] = (b3); data [1] = (b2); data[2] = (b1); #else # define __SET_PIXEL24(data, b1, b2, b3) \ data [0] = (b1); data[1] = (b2); data[2] = (b3); #endif /////////////////////////////////////////////////////////////////////// ///////////// SOLID (not alpha blended) drawing functions ///////////// /////////////////////////////////////////////////////////////////////// static void putPixelSolid8 (Surface *surface, Uint16 x, Uint16 y, Uint32 color) { Uint8 *data = (Uint8*) surface->pixels () + y * surface->pitch () + x; __SET_PIXEL (8, data, color); } static void putPixelSolid16 (Surface *surface, Uint16 x, Uint16 y, Uint32 color) { Uint8 *data = (Uint8*) surface->pixels () + y * surface->pitch () + 2 * x; __SET_PIXEL (16, data, color); } static void putPixelSolid24 (Surface *surface, Uint16 x, Uint16 y, Uint32 color) { Uint8 *data = (Uint8*) surface->pixels () + y * surface->pitch () + 3 * x; __SET_PIXEL24 (data, color & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF); } static void putPixelSolid32 (Surface *surface, Uint16 x, Uint16 y, Uint32 color) { Uint8 *data = (Uint8*) surface->pixels () + y * surface->pitch () + 4 * x; __SET_PIXEL (32, data, color); } static void drawHLineSolid8 (Surface *surface, Uint16 x, Uint16 y, Uint16 w, Uint32 color) { Uint8 *data = (Uint8*) surface->pixels () + y * surface->pitch () + x; while (w > 0) { __SET_PIXEL (8, data, color); ++data; --w; } } static void drawHLineSolid16 (Surface *surface, Uint16 x, Uint16 y, Uint16 w, Uint32 color) { Uint16 *data = (Uint16*) ((Uint8*) surface->pixels () + y * surface->pitch () + 2 * x); while (w > 0) { __SET_PIXEL (16, data, color); ++data; --w; } } static void drawHLineSolid24 (Surface *surface, Uint16 x, Uint16 y, Uint16 w, Uint32 color) { Uint8 *data = (Uint8*) surface->pixels () + y * surface->pitch () + 3 * x; Uint8 c1 = color & 0xFF; Uint8 c2 = (color >> 8) & 0xFF; Uint8 c3 = (color >> 16) & 0xFF; while (w > 0) { __SET_PIXEL24 (data, c1, c2, c3); data += 3; --w; } } static void drawHLineSolid32 (Surface *surface, Uint16 x, Uint16 y, Uint16 w, Uint32 color) { Uint32 *data = (Uint32*) ((Uint8*) surface->pixels () + y * surface->pitch () + 4 * x); while (w > 0) { __SET_PIXEL (32, data, color); ++data; --w; } } static void drawVLineSolid8 (Surface *surface, Uint16 x, Uint16 y, Uint16 h, Uint32 color) { Uint16 pitch = surface->pitch (); Uint8 *data = (Uint8*) surface->pixels () + (y * pitch) + x; while (h > 0) { __SET_PIXEL (8, data, color); data += pitch; --h; } } static void drawVLineSolid16 (Surface *surface, Uint16 x, Uint16 y, Uint16 h, Uint32 color) { Uint16 pitch = surface->pitch (); Uint8 *data = (Uint8*) surface->pixels () + (y * pitch) + (2 * x); while (h > 0) { __SET_PIXEL (16, data, color); data += pitch; --h; } } static void drawVLineSolid24 (Surface *surface, Uint16 x, Uint16 y, Uint16 h, Uint32 color) { Uint16 pitch = surface->pitch (); Uint8 *data = (Uint8*) surface->pixels () + (y * pitch) + (3 * x); Uint8 c1 = color & 0xFF; Uint8 c2 = (color >> 8) & 0xFF; Uint8 c3 = (color >> 16) & 0xFF; while (h > 0) { __SET_PIXEL24 (data, c1, c2, c3); data += pitch; --h; } } static void drawVLineSolid32 (Surface *surface, Uint16 x, Uint16 y, Uint16 h, Uint32 color) { Uint16 pitch = surface->pitch (); Uint8 *data = (Uint8*) surface->pixels () + (y * pitch) + (4 * x); while (h > 0) { __SET_PIXEL (32, data, color); data += pitch; --h; } } // Line drawing algorithm (Bresenham) #define __DO_LINE(x1, y1, x2, y2, command, surface) \ { \ Sint16 dx = x2 - x1; \ Sint16 dy = y2 - y1; \ Uint16 x = 0; \ Uint16 y = 0; \ Uint8 *data = \ (Uint8*) surface->pixels () + y1 * surface->pitch () + x1 * surface->format ()->BytesPerPixel; \ Sint16 d_pitch = (dy < 0) ? -surface->pitch () : surface->pitch (); \ Sint16 d_data = (dx < 0) ? -surface->format ()->BytesPerPixel : surface->format ()->BytesPerPixel; \ dx = abs (dx); \ dy = abs (dy); \ \ if (dx >= dy) \ { \ for (x = 0; x <= dx; x++) \ { \ { command; } \ \ y += dy; \ if (y >= dx) \ { \ y -= dx; \ data += d_pitch; \ } \ \ data += d_data; \ } \ } \ else \ { \ for (y = 0; y <= dy; y++) \ { \ { command; } \ \ x += dx; \ if (x >= dy) \ { \ x -= dy; \ data += d_data; \ } \ data += d_pitch; \ } \ } \ } static void drawLineSolid8 (Surface *surface, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2, Uint32 color) { __DO_LINE (x1, y1, x2, y2, __SET_PIXEL (8, data, color), surface); } static void drawLineSolid16 (Surface *surface, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2, Uint32 color) { __DO_LINE (x1, y1, x2, y2, __SET_PIXEL (16, data, color), surface); } static void drawLineSolid24 (Surface *surface, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2, Uint32 color) { Uint8 c1 = color & 0xFF; Uint8 c2 = (color >> 8) & 0xFF; Uint8 c3 = (color >> 16) & 0xFF; __DO_LINE (x1, y1, x2, y2, __SET_PIXEL24 (data, c1, c2, c3), surface); } static void drawLineSolid32 (Surface *surface, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2, Uint32 color) { __DO_LINE (x1, y1, x2, y2, __SET_PIXEL (32, data, color), surface); } #define __MAKE_LOCKED_PUT_PIXEL(type) \ static void \ putPixel##type##L (Surface *surface, Uint16 x, Uint16 y, Uint32 color) \ { \ surface->lock (); \ putPixel##type (surface, x, y, color); \ surface->unlock (); \ } #define __MAKE_LOCKED_H_LINE(type) \ static void \ drawHLine##type##L (Surface *surface, Uint16 x, Uint16 y, Uint16 w, Uint32 color) \ { \ surface->lock (); \ drawHLine##type (surface, x, y, w, color); \ surface->unlock (); \ } #define __MAKE_LOCKED_V_LINE(type) \ static void \ drawVLine##type##L (Surface *surface, Uint16 x, Uint16 y, Uint16 h, Uint32 color) \ { \ surface->lock (); \ drawVLine##type (surface, x, y, h, color); \ surface->unlock (); \ } #define __MAKE_LOCKED_LINE(type) \ static void \ drawLine##type##L (Surface *surface, Uint16 x, Uint16 y, Uint16 x2, Uint16 y2, Uint32 color) \ { \ surface->lock (); \ drawLine##type (surface, x, y, x2, y2, color); \ surface->unlock (); \ } __MAKE_LOCKED_PUT_PIXEL (Solid8); __MAKE_LOCKED_H_LINE (Solid8); __MAKE_LOCKED_V_LINE (Solid8); __MAKE_LOCKED_LINE (Solid8); __MAKE_LOCKED_PUT_PIXEL (Solid16); __MAKE_LOCKED_H_LINE (Solid16); __MAKE_LOCKED_V_LINE (Solid16); __MAKE_LOCKED_LINE (Solid16); __MAKE_LOCKED_PUT_PIXEL (Solid24); __MAKE_LOCKED_H_LINE (Solid24); __MAKE_LOCKED_V_LINE (Solid24); __MAKE_LOCKED_LINE (Solid24); __MAKE_LOCKED_PUT_PIXEL (Solid32); __MAKE_LOCKED_H_LINE (Solid32); __MAKE_LOCKED_V_LINE (Solid32); __MAKE_LOCKED_LINE (Solid32); ////////////////////////////////////////////////////////////////////////// ////////// ALPHA BLENDED (transparency aware) drawing functions ////////// ////////////////////////////////////////////////////////////////////////// // macros for color manipulation (some are taken from SDL) #define __RGBA_FROM_RGBA8888(pixel, r, g, b, a) \ { \ r = (pixel>>24); \ g = ((pixel>>16)&0xFF); \ b = ((pixel>>8)&0xFF); \ a = (pixel&0xFF); \ } #define __RGB_FROM_RGB565(pixel, r, g, b) \ { \ r = (((pixel&0xF800)>>11)<<3); \ g = (((pixel&0x07E0)>>5)<<2); \ b = ((pixel&0x001F)<<3); \ } #define __RGB_FROM_RGB888(pixel, r, g, b) \ { \ r = ((pixel&0xFF0000)>>16); \ g = ((pixel&0xFF00)>>8); \ b = (pixel&0xFF); \ } #define __RGB565_FROM_RGB(pixel, r, g, b) \ { \ pixel = ((r>>3)<<11)|((g>>2)<<5)|(b>>3); \ } #define __RGB888_FROM_RGB(pixel, r, g, b) \ { \ pixel = (r<<16)|(g<<8)|b; \ } // Blend the RGB values of two pixels based on a source alpha value */ #define __ALPHA_BLEND(sR, sG, sB, A, dR, dG, dB) \ do { \ dR = ((((sR-dR)*(A))>>8)+dR) & 0xFF; \ dG = ((((sG-dG)*(A))>>8)+dG) & 0xFF; \ dB = ((((sB-dB)*(A))>>8)+dB) & 0xFF; \ } while(0) // this is in fact 128 (~127) #define __ALPHA_HALF 127 // Blend the RGB values two pixel with alpha = 128 (~127) - optimized #define __ALPHA_BLEND_HALF(sR, sG, sB, dR, dG, dB) \ do { \ dR = ((sR + dR) >> 1); \ dG = ((sG + dG) >> 1); \ dB = ((sB + dB) >> 1); \ } while (0) #define __ALPHA_BLEND_AUTO(sR, sG, sB, A, dR, dG, dB) \ { \ if (a == 127) \ __ALPHA_BLEND_HALF (sR, sG, sB, dR, dG, dB); \ else \ __ALPHA_BLEND (sR, sG, sB, A, dR, dG, dB); \ } static void putPixelBlend16 (Surface *surface, Uint16 x, Uint16 y, Uint32 color) { Uint8 dr, dg, db, a; __RGBA_FROM_RGBA8888 (color, dr, dg, db, a); if (a == 0) return; Uint16 *data = (Uint16*) ((Uint8*) surface->pixels () + y * surface->pitch () + 2 * x); if (a == 255) { __RGB565_FROM_RGB (*data, dr, dg, db); return; } Uint8 sr, sg, sb; __RGB_FROM_RGB565 (*data, sr, sg, sb); a = ~a; __ALPHA_BLEND_AUTO (sr, sg, sb, a, dr, dg, db); __RGB565_FROM_RGB (*data, dr, dg, db); } static void putPixelBlend24 (Surface *surface, Uint16 x, Uint16 y, Uint32 color) { Uint8 dr, dg, db, a; __RGBA_FROM_RGBA8888 (color, dr, dg, db, a); if (a == 0) return; Uint32 *data = (Uint32*) ((Uint8*) surface->pixels () + y * surface->pitch () + 3 * x); if (a == 255) { __SET_PIXEL24 (((Uint8*)data), db, dg, dr); return; } Uint8 sr, sg, sb; __RGB_FROM_RGB888 (*data, sr, sg, sb); a = ~a; __ALPHA_BLEND_AUTO (sr, sg, sb, a, dr, dg, db); __SET_PIXEL24 (((Uint8*)data), db, dg, dr); } static void putPixelBlend32 (Surface *surface, Uint16 x, Uint16 y, Uint32 color) { Uint8 dr, dg, db, a; __RGBA_FROM_RGBA8888 (color, dr, dg, db, a); if (a == 0) return; Uint32 *data = (Uint32*) ((Uint8*) surface->pixels () + y * surface->pitch () + 4 * x); if (a == 255) { __RGB888_FROM_RGB (*data, dr, dg, db); return; } Uint8 sr, sg, sb; __RGB_FROM_RGB888 (*data, sr, sg, sb); a = ~a; __ALPHA_BLEND_AUTO (sr, sg, sb, a, dr, dg, db); __RGB888_FROM_RGB (*data, dr, dg, db); } static void drawHLineBlend16 (Surface *surface, Uint16 x, Uint16 y, Uint16 w, Uint32 color) { Uint8 dr, dg, db, a; __RGBA_FROM_RGBA8888 (color, dr, dg, db, a); if (a == 0) return; if (a == 255) { __RGB565_FROM_RGB (color, dr, dg, db); drawHLineSolid16 (surface, x, y, w, color); return; } Uint16 *data = (Uint16*) ((Uint8*) surface->pixels () + y * surface->pitch () + 2 * x); Uint8 sr, sg, sb; Uint8 dr_, dg_, db_; a = ~a; if (a == __ALPHA_HALF) { while (w > 0) { __RGB_FROM_RGB565 (*data, sr, sg, sb); dr_ = dr; dg_ = dg; db_ = db; __ALPHA_BLEND_HALF (sr, sg, sb, dr_, dg_, db_); __RGB565_FROM_RGB (*data, dr_, dg_, db_); ++data; --w; } } else { while (w > 0) { __RGB_FROM_RGB565 (*data, sr, sg, sb); dr_ = dr; dg_ = dg; db_ = db; __ALPHA_BLEND (sr, sg, sb, a, dr_, dg_, db_); __RGB565_FROM_RGB (*data, dr_, dg_, db_); ++data; --w; } } } static void drawHLineBlend24 (Surface *surface, Uint16 x, Uint16 y, Uint16 w, Uint32 color) { Uint8 dr, dg, db, a; __RGBA_FROM_RGBA8888 (color, dr, dg, db, a); if (a == 0) return; if (a == 255) { __RGB888_FROM_RGB (color, dr, dg, db); drawHLineSolid24 (surface, x, y, w, color); return; } Uint32 *data = (Uint32*) ((Uint8*) surface->pixels () + y * surface->pitch () + 3 * x); Uint8 sr, sg, sb; Uint8 dr_, dg_, db_; a = ~a; if (a == __ALPHA_HALF) { while (w > 0) { __RGB_FROM_RGB888 (*data, sr, sg, sb); dr_ = dr; dg_ = dg; db_ = db; __ALPHA_BLEND_HALF (sr, sg, sb, dr_, dg_, db_); __SET_PIXEL24 (((Uint8*)data), db_, dg_, dr_); #ifdef _MSC_VER data = (Uint32*) ((Uint8*)data + 3); #else ((Uint8*)data) += 3; #endif --w; } } else { while (w > 0) { __RGB_FROM_RGB888 (*data, sr, sg, sb); dr_ = dr; dg_ = dg; db_ = db; __ALPHA_BLEND (sr, sg, sb, a, dr_, dg_, db_); __SET_PIXEL24 (((Uint8*)data), db_, dg_, dr_); #ifdef _MSC_VER data = (Uint32*) ((Uint8*)data + 3); #else ((Uint8*)data) += 3; #endif --w; } } } static void drawHLineBlend32 (Surface *surface, Uint16 x, Uint16 y, Uint16 w, Uint32 color) { Uint8 dr, dg, db, a; __RGBA_FROM_RGBA8888 (color, dr, dg, db, a); if (a == 0) return; if (a == 255) { __RGB888_FROM_RGB (color, dr, dg, db); drawHLineSolid32 (surface, x, y, w, color); return; } Uint32 *data = (Uint32*) ((Uint8*) surface->pixels () + y * surface->pitch () + 4 * x); Uint8 sr, sg, sb; Uint8 dr_, dg_, db_; a = ~a; if (a == __ALPHA_HALF) { while (w > 0) { __RGB_FROM_RGB888 (*data, sr, sg, sb); dr_ = dr; dg_ = dg; db_ = db; __ALPHA_BLEND_HALF (sr, sg, sb, dr_, dg_, db_); __RGB888_FROM_RGB (*data, dr_, dg_, db_); ++data; --w; } } else { while (w > 0) { __RGB_FROM_RGB888 (*data, sr, sg, sb); dr_ = dr; dg_ = dg; db_ = db; __ALPHA_BLEND (sr, sg, sb, a, dr_, dg_, db_); __RGB888_FROM_RGB (*data, dr_, dg_, db_); ++data; --w; } } } static void drawVLineBlend16 (Surface *surface, Uint16 x, Uint16 y, Uint16 h, Uint32 color) { Uint8 dr, dg, db, a; __RGBA_FROM_RGBA8888 (color, dr, dg, db, a); if (a == 0) return; if (a == 255) { __RGB565_FROM_RGB (color, dr, dg, db); drawVLineSolid16 (surface, x, y, h, color); return; } Uint16 pitch = surface->pitch (); Uint16 *data = (Uint16*) ((Uint8*) surface->pixels () + (y * pitch) + (2 * x)); Uint8 sr, sg, sb; Uint8 dr_, dg_, db_; a = ~a; if (a == __ALPHA_HALF) { while (h > 0) { __RGB_FROM_RGB565 (*data, sr, sg, sb); dr_ = dr; dg_ = dg; db_ = db; __ALPHA_BLEND_HALF (sr, sg, sb, dr_, dg_, db_); __RGB565_FROM_RGB (*data, dr_, dg_, db_); #ifdef _MSC_VER data = (Uint16*) ((Uint8*)data + pitch); #else ((Uint8*)data) += pitch; #endif --h; } } else { while (h > 0) { __RGB_FROM_RGB565 (*data, sr, sg, sb); dr_ = dr; dg_ = dg; db_ = db; __ALPHA_BLEND (sr, sg, sb, a, dr_, dg_, db_); __RGB565_FROM_RGB (*data, dr_, dg_, db_); #ifdef _MSC_VER data = (Uint16*) ((Uint8*)data + pitch); #else ((Uint8*)data) += pitch; #endif --h; } } } static void drawVLineBlend24 (Surface *surface, Uint16 x, Uint16 y, Uint16 h, Uint32 color) { Uint8 dr, dg, db, a; __RGBA_FROM_RGBA8888 (color, dr, dg, db, a); if (a == 0) return; if (a == 255) { __RGB888_FROM_RGB (color, dr, dg, db); drawVLineSolid24 (surface, x, y, h, color); return; } Uint16 pitch = surface->pitch (); Uint32 *data = (Uint32*) ((Uint8*) surface->pixels () + (y * pitch) + (3 * x)); Uint8 sr, sg, sb; Uint8 dr_, dg_, db_; a = ~a; if (a == __ALPHA_HALF) { while (h > 0) { __RGB_FROM_RGB888 (*data, sr, sg, sb); dr_ = dr; dg_ = dg; db_ = db; __ALPHA_BLEND_HALF (sr, sg, sb, dr_, dg_, db_); __SET_PIXEL24 (((Uint8*)data), db_, dg_, dr_); #ifdef _MSC_VER data = (Uint32*) ((Uint8*)data + pitch); #else ((Uint8*)data) += pitch; #endif --h; } } else { while (h > 0) { __RGB_FROM_RGB888 (*data, sr, sg, sb); dr_ = dr; dg_ = dg; db_ = db; __ALPHA_BLEND (sr, sg, sb, a, dr_, dg_, db_); __SET_PIXEL24 (((Uint8*)data), db_, dg_, dr_); #ifdef _MSC_VER data = (Uint32*) ((Uint8*)data + pitch); #else ((Uint8*)data) += pitch; #endif --h; } } } static void drawVLineBlend32 (Surface *surface, Uint16 x, Uint16 y, Uint16 h, Uint32 color) { Uint8 dr, dg, db, a; __RGBA_FROM_RGBA8888 (color, dr, dg, db, a); if (a == 0) return; if (a == 255) { __RGB888_FROM_RGB (color, dr, dg, db); drawVLineSolid32 (surface, x, y, h, color); return; } Uint16 pitch = surface->pitch (); Uint32 *data = (Uint32*) ((Uint8*) surface->pixels () + (y * pitch) + (4 * x)); Uint8 sr, sg, sb; Uint8 dr_, dg_, db_; a = ~a; if (a == __ALPHA_HALF) { while (h > 0) { __RGB_FROM_RGB888 (*data, sr, sg, sb); dr_ = dr; dg_ = dg; db_ = db; __ALPHA_BLEND_HALF (sr, sg, sb, dr_, dg_, db_); __RGB888_FROM_RGB (*data, dr_, dg_, db_); #ifdef _MSC_VER data = (Uint32*) ((Uint8*)data + pitch); #else ((Uint8*)data) += pitch; #endif --h; } } else { while (h > 0) { __RGB_FROM_RGB888 (*data, sr, sg, sb); dr_ = dr; dg_ = dg; db_ = db; __ALPHA_BLEND (sr, sg, sb, a, dr_, dg_, db_); __RGB888_FROM_RGB (*data, dr_, dg_, db_); #ifdef _MSC_VER data = (Uint32*) ((Uint8*)data + pitch); #else ((Uint8*)data) += pitch; #endif --h; } } } static void drawLineBlend16 (Surface *surface, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2, Uint32 color) { Uint8 dr, dg, db, a; __RGBA_FROM_RGBA8888 (color, dr, dg, db, a); if (a == 0) return; if (a == 255) { __RGB565_FROM_RGB (color, dr, dg, db); drawLineSolid16 (surface, x1, y1, x2, y2, color); return; } Uint8 sr, sg, sb; Uint8 dr_, dg_, db_; a = ~a; Uint16 *data_; if (a == __ALPHA_HALF) { __DO_LINE (x1, y1, x2, y2, data_ = (Uint16*)data; __RGB_FROM_RGB565 (*data_, sr, sg, sb); dr_ = dr; dg_= dg; db_ = db; __ALPHA_BLEND_HALF (sr, sg, sb, dr_, dg_, db_); __RGB565_FROM_RGB (*data_, dr_, dg_, db_), surface); } else { __DO_LINE (x1, y1, x2, y2, data_ = (Uint16*)data; __RGB_FROM_RGB565 (*data_, sr, sg, sb); dr_ = dr; dg_= dg; db_ = db; __ALPHA_BLEND (sr, sg, sb, a, dr_, dg_, db_); __RGB565_FROM_RGB (*data_, dr_, dg_, db_), surface); } } static void drawLineBlend24 (Surface *surface, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2, Uint32 color) { Uint8 dr, dg, db, a; __RGBA_FROM_RGBA8888 (color, dr, dg, db, a); if (a == 0) return; if (a == 255) { __RGB888_FROM_RGB (color, dr, dg, db); drawLineSolid24 (surface, x1, y1, x2, y2, color); return; } Uint8 sr, sg, sb; Uint8 dr_, dg_, db_; a = ~a; Uint32 *data_; if (a == __ALPHA_HALF) { __DO_LINE (x1, y1, x2, y2, data_ = (Uint32*)data; __RGB_FROM_RGB888 (*data_, sr, sg, sb); dr_ = dr; dg_= dg; db_ = db; __ALPHA_BLEND_HALF (sr, sg, sb, dr_, dg_, db_); __SET_PIXEL24 (data, db_, dg_, dr_), surface); } else { __DO_LINE (x1, y1, x2, y2, data_ = (Uint32*)data; __RGB_FROM_RGB888 (*data_, sr, sg, sb); dr_ = dr; dg_= dg; db_ = db; __ALPHA_BLEND (sr, sg, sb, a, dr_, dg_, db_); __SET_PIXEL24 (data, db_, dg_, dr_), surface); } } static void drawLineBlend32 (Surface *surface, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2, Uint32 color) { Uint8 dr, dg, db, a; __RGBA_FROM_RGBA8888 (color, dr, dg, db, a); if (a == 0) return; if (a == 255) { __RGB888_FROM_RGB (color, dr, dg, db); drawLineSolid32 (surface, x1, y1, x2, y2, color); return; } Uint8 sr, sg, sb; Uint8 dr_, dg_, db_; a = ~a; Uint32 *data_; if (a == __ALPHA_HALF) { __DO_LINE (x1, y1, x2, y2, data_ = (Uint32*)data; __RGB_FROM_RGB888 (*data_, sr, sg, sb); dr_ = dr; dg_= dg; db_ = db; __ALPHA_BLEND_HALF (sr, sg, sb, dr_, dg_, db_); __RGB888_FROM_RGB (*data_, dr_, dg_, db_), surface); } else { __DO_LINE (x1, y1, x2, y2, data_ = (Uint32*)data; __RGB_FROM_RGB888 (*data_, sr, sg, sb); dr_ = dr; dg_= dg; db_ = db; __ALPHA_BLEND (sr, sg, sb, a, dr_, dg_, db_); __RGB888_FROM_RGB (*data_, dr_, dg_, db_), surface); } } #undef __DO_LINE __MAKE_LOCKED_PUT_PIXEL (Blend16); __MAKE_LOCKED_H_LINE (Blend16); __MAKE_LOCKED_V_LINE (Blend16); __MAKE_LOCKED_LINE (Blend16); __MAKE_LOCKED_PUT_PIXEL (Blend24); __MAKE_LOCKED_H_LINE (Blend24); __MAKE_LOCKED_V_LINE (Blend24); __MAKE_LOCKED_LINE (Blend24); __MAKE_LOCKED_PUT_PIXEL (Blend32); __MAKE_LOCKED_H_LINE (Blend32); __MAKE_LOCKED_V_LINE (Blend32); __MAKE_LOCKED_LINE (Blend32); GFXTable Painter::m_gfx_tables [2][2][4] = { { { { putPixelSolid8, drawHLineSolid8, drawVLineSolid8, drawLineSolid8 }, { putPixelSolid16, drawHLineSolid16, drawVLineSolid16, drawLineSolid16 }, { putPixelSolid24, drawHLineSolid24, drawVLineSolid24, drawLineSolid24 }, { putPixelSolid32, drawHLineSolid32, drawVLineSolid32, drawLineSolid32 } }, { { putPixelSolid8L, drawHLineSolid8L, drawVLineSolid8L, drawLineSolid8L }, { putPixelSolid16L, drawHLineSolid16L, drawVLineSolid16L, drawLineSolid16L }, { putPixelSolid24L, drawHLineSolid24L, drawVLineSolid24L, drawLineSolid24L }, { putPixelSolid32L, drawHLineSolid32L, drawVLineSolid32L, drawLineSolid32L } } }, { { { putPixelSolid8, drawHLineSolid8, drawVLineSolid8, drawLineSolid8 }, { putPixelBlend16, drawHLineBlend16, drawVLineBlend16, drawLineBlend16 }, { putPixelBlend24, drawHLineBlend24, drawVLineBlend24, drawLineBlend24 }, { putPixelBlend32, drawHLineBlend32, drawVLineBlend32, drawLineBlend32 } }, { { putPixelSolid8L, drawHLineSolid8L, drawVLineSolid8L, drawLineSolid8L }, { putPixelBlend16L, drawHLineBlend16L, drawVLineBlend16L, drawLineBlend16L }, { putPixelBlend24L, drawHLineBlend24L, drawVLineBlend24L, drawLineBlend24L }, { putPixelBlend32L, drawHLineBlend32L, drawVLineBlend32L, drawLineBlend32L } } } }; #if 0 // this will not be included in the GFX table // shamelessly taken from allegro... static void do_circle (Surface *surface, Uint16 x, Uint16 y, Uint16 radius, int d, void (*proc)(Surface *, int, int, int)) { Sint16 cx = 0; Sint16 cy = radius; Sint16 df = 1 - radius; Sint16 d_e = 3; Sint16 d_se = -2 * radius + 5; do { proc (surface, x+cx, y+cy, d); if (cx) proc (surface, x-cx, y+cy, d); if (cy) proc (surface, x+cx, y-cy, d); if ((cx) && (cy)) proc (surface, x-cx, y-cy, d); if (cx != cy) { proc (surface, x+cy, y+cx, d); if (cx) proc (surface, x+cy, y-cx, d); if (cy) proc (surface, x-cy, y+cx, d); if (cx && cy) proc (surface, x-cy, y-cx, d); } if (df < 0) { df += d_e; d_e += 2; d_se += 2; } else { df += d_se; d_e += 2; d_se += 4; cy--; } cx++; } while (cx <= cy); } #endif // 0 ///////////////////////////////////////////////////////////////// /////////////////////// The Painter Class /////////////////////// ///////////////////////////////////////////////////////////////// Painter::Painter (Surface *target, int mode, bool nothrow) : m_gfx (&GFXInvalid), m_target_surface (target), m_mode (mode), m_clipping (true), m_auto_lock (true) { if (!target) { warning ("Painter: Cannot operate on NULL surface !"); if (!nothrow) throw Error ("Couldn't create painter with NULL target surface"); } else { updateGFXTable (); target->ref (); } resetClipRect (); } Painter::~Painter () { if (m_target_surface) m_target_surface->unref (); } bool Painter::updateGFXTable () { m_gfx = &GFXInvalid; return_val_if_fail (m_target_surface != NULL, false); if (m_mode != ModeSolid && m_mode != ModeBlended) { warning ("Unknown drawing mode!"); return false; } int depth = m_target_surface->format ()->BytesPerPixel; if (depth < 1 || depth > 4) { warning ("Unknown bit-depth of the target surface !"); return false; } m_gfx = &m_gfx_tables [m_mode][m_auto_lock][depth - 1]; return true; } void Painter::setMode (int mode) { m_mode = mode; updateGFXTable (); } void Painter::setTarget (Surface *target) { releaseTarget (); m_target_surface = target; updateGFXTable (); resetClipRect (); if (target) target->ref (); } void Painter::releaseTarget () { if (m_target_surface) m_target_surface->unref (); m_target_surface = NULL; m_gfx = NULL; m_clip_rect = Rect (0, 0, 0, 0); } void Painter::setClipping (bool clipping) { m_clipping = clipping; } void Painter::setAutoLock (bool auto_lock) { m_auto_lock = auto_lock; updateGFXTable (); } void Painter::setClipRect (const Rect & rect) { m_clip_rect = rect; m_clip_x2 = rect.x + rect.w - 1; m_clip_y2 = rect.y + rect.h - 1; } void Painter::retrieveClipRect () { if (!m_target_surface) setClipRect (Rect (0, 0, 0, 0)); else setClipRect (m_target_surface->clipRect ()); } void Painter::resetClipRect () { if (!m_target_surface) setClipRect (Rect (0, 0, 0, 0)); else setClipRect (Rect (0, 0, m_target_surface->w (), m_target_surface->h ())); } void Painter::putPixel (Uint16 x, Uint16 y, Uint32 color) { if (m_clipping) { if (x < m_clip_rect.x || x > m_clip_x2 || y < m_clip_rect.y || y > m_clip_y2) return; } m_gfx->putPixel (m_target_surface, x, y, color); } void Painter::drawHLine (Uint16 x, Uint16 y, Uint16 w, Uint32 color) { if (w == 0) return; if (m_clipping) { if (y < m_clip_rect.y || y > m_clip_y2) return; if ((x + w - 1) < m_clip_rect.x) return; if (x > m_clip_x2) return; if (x < m_clip_rect.x) { w -= (m_clip_rect.x - x); x = m_clip_rect.x; } if ((x + w) > (m_clip_x2 + 1)) { w -= (w + x) - m_clip_x2 - 1; } } m_gfx->drawHLine (m_target_surface, x, y, w, color); } void Painter::drawVLine (Uint16 x, Uint16 y, Uint16 h, Uint32 color) { if (h == 0) return; if (m_clipping) { if (x < m_clip_rect.x || x > m_clip_x2) return; if ((y + h - 1) < m_clip_rect.y) return; if (y > m_clip_y2) return; if (y < m_clip_rect.y) { h -= (m_clip_rect.y - y); y = m_clip_rect.y; } if ((y + h) > (m_clip_y2 + 1)) { h -= (h + y) - m_clip_y2 - 1; } } m_gfx->drawVLine (m_target_surface, x, y, h, color); } // This function was ripped from "Tricks of the Windows game programming gurus" // written by Andre Lamothe ISBN: 0-672-31361-8 // Then it was ripped from ClanLib and slightly adapted to work with SEL. // It returns false if the line is totally outside the clipping area and true // if the line was successfully clipped static bool clipLine (Uint16 &x1, Uint16 &y1, Uint16 &x2, Uint16 &y2, Uint16 min_clip_x, Uint16 max_clip_x, Uint16 min_clip_y, Uint16 max_clip_y) { // internal clipping codes #define CLIP_CODE_C 0x0000 #define CLIP_CODE_N 0x0008 #define CLIP_CODE_S 0x0004 #define CLIP_CODE_E 0x0002 #define CLIP_CODE_W 0x0001 #define CLIP_CODE_NE 0x000a #define CLIP_CODE_SE 0x0006 #define CLIP_CODE_NW 0x0009 #define CLIP_CODE_SW 0x0005 Uint16 xc1 = x1; Uint16 yc1 = y1; Uint16 xc2 = x2; Uint16 yc2 = y2; int p1_code = 0; int p2_code = 0; // determine codes for p1 and p2 if (y1 < min_clip_y) { p1_code |= CLIP_CODE_N; } else if (y1 > max_clip_y) { p1_code |= CLIP_CODE_S; } if (x1 < min_clip_x) { p1_code |= CLIP_CODE_W; } else if (x1 > max_clip_x) { p1_code |= CLIP_CODE_E; } if (y2 < min_clip_y) { p2_code |= CLIP_CODE_N; } else if (y2 > max_clip_y) { p2_code |= CLIP_CODE_S; } if (x2 < min_clip_x) { p2_code |= CLIP_CODE_W; } else if (x2 > max_clip_x) { p2_code |= CLIP_CODE_E; } // try and trivially reject if ((p1_code & p2_code)) { return 0; } // test for totally visible, if so leave points untouched if (p1_code == 0 && p2_code == 0) { return 1; } // determine end clip point for p1 switch (p1_code) { case CLIP_CODE_C: break; case CLIP_CODE_N: yc1 = (Uint16) min_clip_y; xc1 = (Uint16) (x1 + 0.5 + (min_clip_y - y1) * (x2 - x1) / (y2 - y1)); break; case CLIP_CODE_S: yc1 = (Uint16) max_clip_y; xc1 = (Uint16) (x1 + 0.5 + (max_clip_y-y1) * (x2 - x1) / (y2 - y1)); break; case CLIP_CODE_W: xc1 = (Uint16) min_clip_x; yc1 = (Uint16) (y1 + 0.5 + (min_clip_x - x1) * (y2 - y1) / (x2 - x1)); break; case CLIP_CODE_E: xc1 = (Uint16) max_clip_x; yc1 = (Uint16) (y1 + 0.5 + (max_clip_x - x1) * (y2 - y1) / (x2 - x1)); break; // these cases are more complex, must compute 2 intersections case CLIP_CODE_NE: // north hline intersection yc1 = (Uint16) min_clip_y; xc1 = (Uint16) (x1 + 0.5 + (min_clip_y - y1) * (x2 - x1) / (y2 - y1)); // test if intersection is valid, of so then done, else compute next if (xc1 < min_clip_x || xc1 > max_clip_x) { // east vline intersection xc1 = (Uint16) max_clip_x; yc1 = (Uint16) (y1 + 0.5 + (max_clip_x - x1) * (y2 - y1) / (x2 - x1)); } break; case CLIP_CODE_SE: // south hline intersection yc1 = (Uint16) max_clip_y; xc1 = (Uint16) (x1 + 0.5 + (max_clip_y - y1) * (x2 - x1) / (y2 - y1)); // test if intersection is valid, of so then done, else compute next if (xc1 < min_clip_x || xc1 > max_clip_x) { // east vline intersection xc1 = (Uint16) max_clip_x; yc1 = (Uint16) (y1 + 0.5 + (max_clip_x - x1) * (y2 - y1) / (x2 - x1)); } break; case CLIP_CODE_NW: // north hline intersection yc1 = (Uint16) min_clip_y; xc1 = (Uint16) (x1 + 0.5 + (min_clip_y - y1) * (x2 - x1) / (y2 - y1)); // test if intersection is valid, of so then done, else compute next if (xc1 < min_clip_x || xc1 > max_clip_x) { xc1 = (Uint16) min_clip_x; yc1 = (Uint16) (y1 + 0.5 + (min_clip_x - x1) * (y2 - y1) / (x2 - x1)); } break; case CLIP_CODE_SW: // south hline intersection yc1 = (Uint16) max_clip_y; xc1 = (Uint16) (x1 + 0.5 + (max_clip_y - y1) * (x2-x1)/(y2-y1)); // test if intersection is valid, of so then done, else compute next if (xc1 < min_clip_x || xc1 > max_clip_x) { xc1 = (Uint16) min_clip_x; yc1 = (Uint16) (y1 + 0.5 + (min_clip_x - x1) * (y2 - y1) / (x2 - x1)); } break; default: break; } // switch // determine clip point for p2 switch (p2_code) { case CLIP_CODE_C: break; case CLIP_CODE_N: yc2 = (Uint16) min_clip_y; xc2 = (Uint16) (x2 + (min_clip_y - y2) * (x1 - x2) / (y1 - y2)); break; case CLIP_CODE_S: yc2 = (Uint16) max_clip_y; xc2 = (Uint16) (x2 + (max_clip_y - y2) * (x1 - x2) / (y1 - y2)); break; case CLIP_CODE_W: xc2 = (Uint16) min_clip_x; yc2 = (Uint16) (y2 + (min_clip_x - x2) * (y1 - y2) / (x1 - x2)); break; case CLIP_CODE_E: xc2 = (Uint16) max_clip_x; yc2 = (Uint16) (y2 + (max_clip_x - x2) * (y1 - y2) / (x1 - x2)); break; // these cases are more complex, must compute 2 intersections case CLIP_CODE_NE: // north hline intersection yc2 = (Uint16) min_clip_y; xc2 = (Uint16) (x2 + 0.5 + (min_clip_y - y2) * (x1 - x2) / (y1 - y2)); // test if intersection is valid, of so then done, else compute next if (xc2 < min_clip_x || xc2 > max_clip_x) { // east vline intersection xc2 = (Uint16) max_clip_x; yc2 = (Uint16) (y2 + 0.5 + (max_clip_x - x2) * (y1 - y2) / (x1 - x2)); } break; case CLIP_CODE_SE: // south hline intersection yc2 = (Uint16) max_clip_y; xc2 = (Uint16) (x2 + 0.5 + (max_clip_y - y2) * (x1 - x2) / (y1 - y2)); // test if intersection is valid, of so then done, else compute next if (xc2 < min_clip_x || xc2 > max_clip_x) { // east vline intersection xc2 = (Uint16) max_clip_x; yc2 = (Uint16) (y2 + 0.5 + (max_clip_x - x2) * (y1 - y2) / (x1 - x2)); } break; case CLIP_CODE_NW: // north hline intersection yc2 = (Uint16) min_clip_y; xc2 = (Uint16) (x2 + 0.5 + (min_clip_y - y2) * (x1 - x2) / (y1 - y2)); // test if intersection is valid, of so then done, else compute next if (xc2 < min_clip_x || xc2 > max_clip_x) { xc2 = (Uint16) min_clip_x; yc2 = (Uint16) (y2 + 0.5 + (min_clip_x - x2) * (y1 - y2) / (x1 - x2)); } break; case CLIP_CODE_SW: // south hline intersection yc2 = (Uint16) max_clip_y; xc2 = (Uint16) (x2 + 0.5 + (max_clip_y - y2) * (x1 - x2) / (y1 - y2)); // test if intersection is valid, of so then done, else compute next if (xc2 < min_clip_x || xc2 > max_clip_x) { xc2 = (Uint16) min_clip_x; yc2 = (Uint16) (y2 + 0.5 + (min_clip_x - x2) * (y1 - y2) / (x1 - x2)); } break; default: break; } // switch // do bounds check if ((xc1 < min_clip_x) || (xc1 > max_clip_x) || (yc1 < min_clip_y) || (yc1 > max_clip_y) || (xc2 < min_clip_x) || (xc2 > max_clip_x) || (yc2 < min_clip_y) || (yc2 > max_clip_y) ) { return 0; } // store vars back x1 = xc1; y1 = yc1; x2 = xc2; y2 = yc2; return 1; #undef CLIP_CODE_C #undef CLIP_CODE_N #undef CLIP_CODE_S #undef CLIP_CODE_E #undef CLIP_CODE_W #undef CLIP_CODE_NE #undef CLIP_CODE_SE #undef CLIP_CODE_NW #undef CLIP_CODE_SW } void Painter::drawLine (Uint16 x, Uint16 y, Uint16 x2, Uint16 y2, Uint32 color) { if (y == y2) { Uint16 w; x > x2 ? w = x - x2 + 1, x = x2 : w = x2 - x + 1; drawHLine (x, y, w, color); return; } else if (x == x2) { Uint16 h; y > y2 ? h = y - y2 + 1, y = y2 : h = y2 - y + 1; drawVLine (x, y, h, color); return; } if (m_clipping) { if (!clipLine (x, y, x2, y2, m_clip_rect.x, m_clip_x2, m_clip_rect.y, m_clip_y2)) return; } m_gfx->drawLine (m_target_surface, x, y, x2, y2, color); } void Painter::drawRect (Uint16 x, Uint16 y, Uint16 w, Uint16 h, Uint32 color) { if (w == 0 || h == 0) return; if (w == 1 && h == 1) { putPixel (x, y, color); return; } if (h == 1) { drawHLine (x, y, w, color); return; } if (w == 1) { drawVLine (x, y, h, color); return; } drawHLine (x, y, w, color); drawHLine (x, y + h - 1, w, color); if (h > 2) { drawVLine (x, y + 1, h - 2, color); drawVLine (x + w - 1, y + 1, h - 2, color); } } void Painter::fillRect (Uint16 x, Uint16 y, Uint16 w, Uint16 h, Uint32 color) { Uint16 _x; Uint16 _y; Uint16 _w; Uint16 _h; #define ___MIN(a,b) (a < b ? a : b) #define ___MAX(a,b) (a > b ? a : b) if (m_clipping) { if (x > m_clip_x2 || y > m_clip_y2) return; if (x + w < m_clip_rect.x + 1 || y + h < m_clip_rect.y + 1) return; _x = ___MAX (x, m_clip_rect.x); _y = ___MAX (y, m_clip_rect.y); _w = ___MIN (x + w - 1, m_clip_x2) - _x + 1; _h = ___MIN (y + h - 1, m_clip_y2) - _y + 1; } else { _x = x; _y = y; _w = w; _h = h; } #undef ___MIN #undef ___MAX if (_w == 0 || _h == 0) return; if (_w == 1 && h == 1) { m_gfx->putPixel (m_target_surface, _x, _y, color); return; } if (_h == 1) { m_gfx->drawHLine (m_target_surface, _x, _y, _w, color); return; } if (_w == 1) { m_gfx->drawVLine (m_target_surface, _x, _y, _h, color); return; } Uint16 i; if (_h > _w) { for (i = 0; i < _w; ++i) { m_gfx->drawVLine (m_target_surface, _x + i, _y, _h, color); } } else { for (i = 0; i < _h; ++i) { m_gfx->drawHLine (m_target_surface, _x, _y + i, _w, color); } } } } // namespace SEL