sdlsurface.cpp

Go to the documentation of this file.
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     // Im 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

Generated Fri Jul 28 19:23:00 2006.
Copyright © 1998-2003 by the respective authors.

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.