00001 /* 00002 libwt - Vassilis Virvilis Toolkit - a widget library 00003 Copyright (C) 2006 Vassilis Virvilis <vasvir2@fastmail.fm> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Lesser General Public 00007 License as published by the Free Software Foundation; either 00008 version 2.1 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Lesser General Public License for more details. 00014 00015 You should have received a copy of the GNU Lesser General Public 00016 License along with this library; if not, write to the 00017 Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00018 Boston, MA 02111-1307, SA. 00019 */ 00020 00021 #include <SDL/SDL_image.h> 00022 #include <SDL/SDL_rotozoom.h> 00023 00024 #include <boost/assign/list_of.hpp> // for 'map_list_of()' 00025 00026 #include <wt/trace.h> 00027 #include "sdlsurface.h" 00028 #include "sdldisplay.h" // for default depth 00029 00030 namespace Wt { 00031 00032 const FlagMap SDLSurface::flagMap = boost::assign::map_list_of 00033 (W::SWSurface, SDL_SWSURFACE) 00034 (W::HWSurface, SDL_HWSURFACE) 00035 (W::AsyncBlit, SDL_ASYNCBLIT) 00036 (W::HWPalette, SDL_HWPALETTE) 00037 (W::HWAccel, SDL_HWACCEL) 00038 (W::AnyFormat, SDL_ANYFORMAT) 00039 (W::DoubleBuffer, SDL_DOUBLEBUF) 00040 (W::FullScreen, SDL_FULLSCREEN) 00041 (W::OpenGL, SDL_OPENGL) 00042 (W::OpenGLBlit, SDL_OPENGLBLIT) 00043 (W::Resizable, SDL_RESIZABLE) 00044 (W::NoFrame, SDL_NOFRAME); 00045 00046 void SDLSurface::Deleter::operator()(SDL_Surface* s) { 00047 //trace("sdlsurface", "Deleting SDL_Surface %p\n", s); 00048 SDL_FreeSurface(s); 00049 } 00050 00051 void SDLSurface::detach() { 00052 //avoid detaching if we are the only client for this surface 00053 if (!sdl_surface.unique()) { 00054 /// \bug GCC bug. does not enfoce constness 00055 SDLPixelFormat f = pixelFormat(); 00056 sdl_surface.reset(SDL_ConvertSurface(*this, f, flags()), Deleter()); 00057 } 00058 } 00059 00060 static SDL_Surface *deconst(const SDLSurface src) { 00061 //SDL breakage 00062 return const_cast<SDL_Surface *> 00063 (static_cast<const SDL_Surface *>(src)); 00064 } 00065 00066 SDLSurface::SDLSurface(int w, int h, int depth, int flags) 00067 : sdl_surface( 00068 SDL_CreateRGBSurface(flagMap.state(flags ? flags : static_cast<int>(W::DefaultPixmapFlags)), 00069 w, h, (depth > 0) ? depth : defaultDepth(), 00070 0, 0, 0, 0), Deleter()) { 00071 trace("sdlsurface", 00072 "Creating new SDL_Surface %p [%d x %d]\n", 00073 static_cast<SDL_Surface *>(*this), width(), height()); 00074 onDepthChange(); 00075 } 00076 00077 SDLSurface::SDLSurface(SDL_Surface* surface) 00078 : sdl_surface(surface, Deleter()) { 00079 if (surface) { 00080 trace("sdlsurface", 00081 "Creating from existing SDL_Surface %p [%d x %d]\n", 00082 static_cast<SDL_Surface *>(*this), width(), height()); 00083 } else { 00084 trace("sdlsurface", "Creating NULL SDL_Surface %p\n", 0); 00085 } 00086 onDepthChange(); 00087 } 00088 00089 SDLSurface::SDLSurface(const std::string& filename) 00090 : sdl_surface(IMG_Load_RW(SDL_RWFromFile( 00091 filename.c_str(), "rb"), 1), Deleter()) { 00092 00093 if(!*this) { 00094 Error("IMG_Load_RW: %s (%s)\n", IMG_GetError(), filename.c_str()); 00095 } 00096 00097 onDepthChange(); 00098 } 00099 00100 int SDLSurface::lock() { 00101 detach() 00102 ; 00103 return SDL_LockSurface(*this); 00104 } 00105 00106 int SDLSurface::unlock() { 00107 detach(); 00108 SDL_UnlockSurface(*this); 00109 return 0; 00110 } 00111 00112 SDLRect SDLSurface::clipRect() const { 00113 SDLRect r; 00114 SDL_GetClipRect(deconst(*this), r); 00115 assert(r == sdl_surface->clip_rect); 00116 return r; 00117 } 00118 00119 SDLRect SDLSurface::setClipRect(const SDLRect& rect) { 00120 SDLRect r = rect; 00121 detach(); 00122 SDL_SetClipRect(*this, r); 00123 return r; 00124 } 00125 00126 void SDLSurface::setPalette(const std::vector<SDLColor>& ca, 00127 int offset, int flags) { 00128 //SDL breakage 00129 SDL_Color *colors = const_cast<SDL_Color *>( 00130 reinterpret_cast<const SDL_Color *>(&ca[0])); 00131 detach(); 00132 SDL_SetPalette(*this, flags, colors, offset, ca.size()); 00133 } 00134 00135 void SDLSurface::setColor(int i, const SDLColor& c) { 00136 std::vector<SDLColor> ca(1, c); 00137 setPalette(ca, i); 00138 } 00139 00140 int SDLSurface::numColors() const { 00141 SDLPalette pal = pixelFormat().palette(); 00142 return (pal) ? pal.size() : 0; 00143 } 00144 00145 void SDLSurface::setNumColors(int ncolors) { 00146 int old = numColors(); 00147 00148 if (ncolors < old) { 00149 SDLPalette pal = pixelFormat().palette(); 00150 std::vector<SDLColor> ca; 00151 ca.reserve(ncolors); 00152 for (int i = 0; i < ncolors; i++) 00153 ca[i] = pal[i]; 00154 setPalette(ca, 0); 00155 } else if (ncolors > old) { 00156 std::vector<SDLColor> ca(ncolors - old, SDLColor()); 00157 setPalette(ca, old); 00158 } 00159 } 00160 00161 bool SDLSurface::isGrayscale() const { 00162 SDLPalette pal = pixelFormat().palette(); 00163 if (pal) { 00164 int n = numColors(); 00165 for (int i = 0; i < n; i++) { 00166 SDLColor c = pal[i]; 00167 if (c.red() != c.green() || c.green() != c.blue()) 00168 return false; 00169 } 00170 return true; 00171 } else {} 00172 00173 return false; 00174 } 00175 00176 void SDLSurface::setTransparentColor(const SDLColor& color) { 00177 detach(); 00178 SDL_SetColorKey(*this, SDL_SRCCOLORKEY | SDL_RLEACCEL, 00179 pixelFormat().mapToPixelValue(color)); 00180 } 00181 00182 void SDLSurface::clearTransparentColor() { 00183 detach(); 00184 SDL_SetColorKey(*this, 0, 0); 00185 } 00186 00187 void SDLSurface::setAlpha(int alpha) { 00188 detach(); 00189 SDL_SetAlpha(*this, SDL_SRCALPHA, alpha); 00190 } 00191 00192 void SDLSurface::clearAlpha() { 00193 detach(); 00194 SDL_SetAlpha(*this, 0, SDLColor::Opaque); 00195 } 00196 00197 int SDLSurface::fill(int v) { 00198 detach(); 00199 return SDL_FillRect(*this, 0, v); 00200 } 00201 00202 int SDLSurface::fill(const SDLRect& dst, int v) { 00203 SDLRect r = dst; 00204 detach(); 00205 return SDL_FillRect(*this, r, v); 00206 } 00207 00208 static int safe_blit(SDL_Surface *src, SDL_Rect *srcrect, 00209 SDL_Surface *dst, SDL_Rect *dstrect) { 00210 int err; 00211 while ((err = SDL_BlitSurface(src, srcrect, dst, dstrect)) == -2) { 00212 while (SDL_LockSurface(src) < 0) 00213 //SDL_Delay(10); 00214 /// \todo Write image pixels to image->pixels 00215 SDL_UnlockSurface(src); 00216 } 00217 assert(!err); 00218 return err; 00219 } 00220 00221 /*! 00222 if (source surface has SDL_SRCALPHA set) { 00223 if (source surface has alpha channel (that is, format->Amask != 0)) 00224 blit using per-pixel alpha, ignoring any colour key 00225 else { 00226 if (source surface has SDL_SRCCOLORKEY set) 00227 blit using the colour key AND the per-surface alpha value 00228 else 00229 blit using the per-surface alpha value 00230 } 00231 } else { 00232 if (source surface has SDL_SRCCOLORKEY set) 00233 blit using the colour key 00234 else 00235 ordinary opaque rectangular blit 00236 } 00237 00238 Note: The per-surface alpha value of 128 is considered a special case 00239 and is optimised, so it's much faster than other per-surface values. 00240 00241 Alpha effects surface blitting in the following ways: 00242 00243 RGBA->RGB with SDL_SRCALPHA 00244 The source is alpha-blended with the destination, using the alpha 00245 channel. SDL_SRCCOLORKEY and the per-surface alpha are ignored. 00246 00247 RGBA->RGB without SDL_SRCALPHA 00248 The RGB data is copied from the source. The source alpha channel and the 00249 per-surface alpha value are ignored. 00250 00251 RGB->RGBA with SDL_SRCALPHA 00252 The source is alpha-blended with the destination using the per-surface 00253 alpha value. If SDL_SRCCOLORKEY is set, only the pixels not matching the 00254 colorkey value are copied. The alpha channel of the copied pixels is set 00255 to opaque. 00256 00257 RGB->RGBA without SDL_SRCALPHA 00258 The RGB data is copied from the source and the alpha value of the copied 00259 pixels is set to opaque. If SDL_SRCCOLORKEY is set, only the pixels not 00260 matching the colorkey value are copied. 00261 00262 RGBA->RGBA with SDL_SRCALPHA 00263 The source is alpha-blended with the destination using the source alpha 00264 channel. The alpha channel in the destination surface is left untouched. 00265 SDL_SRCCOLORKEY is ignored. 00266 00267 RGBA->RGBA without SDL_SRCALPHA 00268 The RGBA data is copied to the destination surface. If SDL_SRCCOLORKEY 00269 is set, only the pixels not matching the colorkey value are copied. 00270 00271 RGB->RGB with SDL_SRCALPHA 00272 The source is alpha-blended with the destination using the per-surface 00273 alpha value. If SDL_SRCCOLORKEY is set, only the pixels not matching the 00274 colorkey value are copied. 00275 00276 RGB->RGB without SDL_SRCALPHA 00277 The RGB data is copied from the source. If SDL_SRCCOLORKEY is set, only 00278 the pixels not matching the colorkey value are copied. 00279 00280 Note: Note that RGBA->RGBA blits (with SDL_SRCALPHA set) keep the alpha 00281 of the destination surface. This means that you cannot compose two 00282 arbitrary RGBA surfaces this way and get the result you would expect 00283 from "overlaying" them; the destination alpha will work as a mask. 00284 00285 Also note that per-pixel and per-surface alpha cannot be combined; the 00286 per-pixel alpha is always used if available 00287 */ 00288 00289 int SDLSurface::blit(const SDLSurface& src) { 00290 detach(); 00291 return safe_blit(deconst(src), 0, *this, 0); 00292 } 00293 00294 int SDLSurface::blit(int x, int y, const SDLSurface& src) { 00295 SDLRect dst(x, y, 0, 0); 00296 detach(); 00297 return safe_blit(deconst(src), 0, *this, dst); 00298 } 00299 00300 int SDLSurface::blit(int x, int y, 00301 const SDLSurface& src, const SDLRect& src_rect) { 00302 ///SDL breakage 00303 SDLRect s = src_rect; 00304 SDLRect dst(x, y, 0, 0); 00305 detach(); 00306 return safe_blit(deconst(src), s, *this, dst); 00307 } 00308 00309 bool SDLSurface::load(const std::string& filename) { 00310 SDLSurface dst = IMG_Load_RW(SDL_RWFromFile(filename.c_str(), "rb"), 1); 00311 00312 if (dst) { 00313 sdl_surface = dst; 00314 return true; 00315 } 00316 return false; 00317 } 00318 00319 bool SDLSurface::load(const void *p, size_t size) { 00320 SDLSurface dst = IMG_Load_RW(SDL_RWFromConstMem(p, size), 1); 00321 00322 if (dst) { 00323 sdl_surface = dst; 00324 return true; 00325 } 00326 return false; 00327 } 00328 00329 int SDLSurface::convertTo(const SDLSurface src) { 00330 if (*this == src) 00331 return 0; 00332 /// \bug GCC bug. does not enfoce constness 00333 SDLPixelFormat f = src.pixelFormat(); 00334 SDLSurface dst = SDL_ConvertSurface(*this, f, src.flags()); 00335 00336 if (dst) { 00337 sdl_surface = dst; 00338 return 0; 00339 } 00340 00341 return -1; 00342 } 00343 00344 int SDLSurface::convertToDisplay() { 00345 SDLSurface dst = SDL_DisplayFormatAlpha(*this); 00346 if (dst) { 00347 sdl_surface = dst; 00348 return 0; 00349 } 00350 00351 return -1; 00352 } 00353 00354 int SDLSurface::convertDepth(int new_depth) { 00355 SDLSurface dst = SDLSurface(width(), height(), new_depth); 00356 if (dst) { 00357 dst.blit(*this); 00358 sdl_surface = dst; 00359 return 0; 00360 } 00361 return -1; 00362 } 00363 00364 void SDLSurface::resize(int x, int y) { 00365 SDLPixelFormat f = pixelFormat(); 00366 SDLSurface dst = SDL_CreateRGBSurface(flags(), x, y, f.bitsPerPixel(), 00367 f.redMask(), f.greenMask(), 00368 f.blueMask(), f.alphaMask()); 00369 if (dst) 00370 sdl_surface = dst; 00371 } 00372 00373 int SDLSurface::defaultDepth() { 00374 return SDLDisplay::instance()->depth(); 00375 } 00376 00377 int SDLSurface::blitAlphaCopy(int x, int y, const SDLSurface& src, 00378 const SDLRect& src_rect) { 00379 assert(depth() == src.depth()); 00380 00381 bool has_alpha = src.hasAlpha(); 00382 bool has_colorkey = src.hasTransparentColor(); 00383 int alpha = 0, pv = 0; 00384 00385 // note: we can't use set/clearAlpha beacuse of detach 00386 if (has_alpha) { 00387 alpha = src.alpha(); 00388 SDL_SetAlpha(deconst(src), 0, SDLColor::Opaque); 00389 } 00390 00391 if (has_colorkey) { 00392 pv = src.pixelFormat().transparentPixelValue(); 00393 SDL_SetColorKey(deconst(src), 0, 0); 00394 } 00395 00396 int status = blit(x, y, src, src_rect); 00397 00398 if (has_alpha) { 00399 SDL_SetAlpha(deconst(src), SDL_SRCALPHA, alpha); 00400 } 00401 00402 if (has_colorkey) { 00403 SDL_SetColorKey(deconst(src), SDL_SRCCOLORKEY | SDL_RLEACCEL, pv); 00404 } 00405 return status; 00406 } 00407 00408 int SDLSurface::blitAlphaCopy(const SDLSurface& src) { 00409 return blitAlphaCopy(0, 0, src, SDLRect(0, 0, src.width(), src.height())); 00410 } 00411 00412 int SDLSurface::blitAlphaCopy(int x, int y, const SDLSurface& src) { 00413 return blitAlphaCopy(x, y, src, SDLRect(0, 0, src.width(), src.height())); 00414 } 00415 00416 int SDLSurface::readPixel8bpp(int pixel_offset) const { 00417 return * (Uint8 *) pixelAddress(pixel_offset); 00418 } 00419 00420 void SDLSurface::writePixel8bpp(int pixel_offset, int pixel_value) { 00421 * (Uint8 *) pixelAddress(pixel_offset) = pixel_value; 00422 } 00423 00424 int SDLSurface::readPixel16bpp(int pixel_offset) const { 00425 return * (Uint16 *) pixelAddress(pixel_offset); 00426 } 00427 00428 void SDLSurface::writePixel16bpp(int pixel_offset, int pixel_value) { 00429 * (Uint16 *) pixelAddress(pixel_offset) = pixel_value; 00430 } 00431 00432 int SDLSurface::readPixel24bpp(int pixel_offset) const { 00433 Uint8* screen_loc = (Uint8 *) pixelAddress(pixel_offset); 00434 const SDLPixelFormat f = pixelFormat(); 00435 Uint32 pixel = 0; 00436 int shift; 00437 00438 // I´m not sure on this, Karsten 01/1999 00439 shift = f.redShift(); 00440 pixel = *(screen_loc + shift / 8) << shift; 00441 shift = f.greenShift(); 00442 pixel |= *(screen_loc + shift / 8) << shift; 00443 shift = f.blueShift(); 00444 pixel |= *(screen_loc + shift / 8) << shift; 00445 return pixel; 00446 } 00447 00448 void SDLSurface::writePixel24bpp(int pixel_offset, int pixel_value) { 00449 Uint8* screen_loc = (Uint8 *) pixelAddress(pixel_offset); 00450 int shift; 00451 const SDLPixelFormat f = pixelFormat(); 00452 00453 shift = f.redShift(); 00454 *(screen_loc + shift / 8) = pixel_value >> shift; 00455 shift = f.greenShift(); 00456 *(screen_loc + shift / 8) = pixel_value >> shift; 00457 shift = f.blueShift(); 00458 *(screen_loc + shift / 8) = pixel_value >> shift; 00459 } 00460 00461 int SDLSurface::readPixel32bpp(int pixel_offset) const { 00462 return * (Uint32 *) pixelAddress(pixel_offset); 00463 } 00464 00465 void SDLSurface::writePixel32bpp(int pixel_offset, int pixel_value) { 00466 * (Uint32 *) pixelAddress(pixel_offset) = pixel_value; 00467 } 00468 00469 void SDLSurface::onDepthChange() { 00470 // don't crash on NULL surfaces 00471 if (!*this) 00472 return; 00473 switch (depth()) { 00474 case 0: 00475 read_pixel = 0; 00476 write_pixel = 0; 00477 assert(0); 00478 break; 00479 case 8: 00480 read_pixel = &SDLSurface::readPixel8bpp; 00481 write_pixel = &SDLSurface::writePixel8bpp; 00482 break; 00483 case 16: 00484 read_pixel = &SDLSurface::readPixel16bpp; 00485 write_pixel = &SDLSurface::writePixel16bpp; 00486 break; 00487 case 24: 00488 read_pixel = &SDLSurface::readPixel24bpp; 00489 write_pixel = &SDLSurface::writePixel24bpp; 00490 break; 00491 case 32: 00492 read_pixel = &SDLSurface::readPixel32bpp; 00493 write_pixel = &SDLSurface::writePixel32bpp; 00494 break; 00495 default: 00496 assert(0); 00497 break; 00498 } 00499 } 00500 00501 SDLSurface SDLSurface::scale_surface(int w, int h, 00502 bool smooth, ScaleMode mode) const { 00503 if (mode != ScaleFree && (width() * h != height() * w)) { 00504 double r1 = static_cast<double>(width()) / 00505 static_cast<double>(height()); 00506 double r2 = static_cast<double>(w) / 00507 static_cast<double>(h); 00508 if ( (mode == ScaleMin & (r2 > r1)) || (mode == ScaleMax & (r2 < r1)) ) 00509 w = static_cast<int>(h * r1); 00510 if ( (mode == ScaleMin & (r2 < r1)) || (mode == ScaleMax & (r2 > r1)) ) 00511 h = static_cast<int>(w / r1); 00512 } 00513 double zx = static_cast<double>(w) / static_cast<double>(width()); 00514 double zy = static_cast<double>(h) / static_cast<double>(height()); 00515 00516 return SDLSurface(zoomSurface(deconst(*this), zx, zy, smooth)); 00517 } 00518 00519 SDLSurface SDLSurface::scale(int w, int h, ScaleMode mode) const { 00520 return scale_surface(w, h, false, mode); 00521 } 00522 00523 SDLSurface SDLSurface::smoothScale(int w, int h, ScaleMode mode) const { 00524 return scale_surface(w, h, true, mode); 00525 } 00526 00527 SDLSurface SDLSurface::scaleWidth(int w) const { 00528 return scale_surface(w, height(), false, ScaleFree); 00529 } 00530 00531 SDLSurface SDLSurface::scaleHeight(int h) const { 00532 return scale_surface(width(), h, false, ScaleFree); 00533 } 00534 00535 } // namespace
This document is licensed under the terms of the GNU Free Documentation License and may be freely distributed under the conditions given by this license.