image.cpp

00001 /***************************************************************************
00002  *   Copyright (C) 2005-2011 by the FIFE team                              *
00003  *   http://www.fifengine.net                                              *
00004  *   This file is part of FIFE.                                            *
00005  *                                                                         *
00006  *   FIFE is free software; you can redistribute it and/or                 *
00007  *   modify it under the terms of the GNU Lesser General Public            *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2.1 of the License, or (at your option) any later version.    *
00010  *                                                                         *
00011  *   This library is distributed in the hope that it will be useful,       *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00014  *   Lesser General Public License for more details.                       *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Lesser General Public      *
00017  *   License along with this library; if not, write to the                 *
00018  *   Free Software Foundation, Inc.,                                       *
00019  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
00020  ***************************************************************************/
00021 
00022 // Standard C++ library includes
00023 #include <cassert>
00024 #include <iostream>
00025 #include <sstream>
00026 
00027 // 3rd party library includes
00028 #include <SDL.h>
00029 
00030 // FIFE includes
00031 // These includes are split up in two parts, separated by one empty line
00032 // First block: files included from the FIFE root src directory
00033 // Second block: files included from the same folder
00034 #include "util/resource/resource.h"
00035 #include "loaders/native/video/imageloader.h"
00036 
00037 #include "image.h"
00038 
00039 namespace FIFE {
00040 
00041     Image::Image(IResourceLoader* loader):
00042         IResource(createUniqueImageName(), loader),
00043         m_surface(NULL),
00044         m_xshift(0),
00045         m_yshift(0),
00046         m_shared(false){
00047     }
00048 
00049     Image::Image(const std::string& name, IResourceLoader* loader):
00050         IResource(name, loader),
00051         m_surface(NULL),
00052         m_xshift(0),
00053         m_yshift(0),
00054         m_shared(false){
00055     }
00056 
00057     Image::Image(SDL_Surface* surface):
00058         IResource(createUniqueImageName()),
00059         m_surface(NULL),
00060         m_xshift(0),
00061         m_yshift(0),
00062         m_shared(false){
00063         reset(surface);
00064     }
00065 
00066     Image::Image(const std::string& name, SDL_Surface* surface):
00067         IResource(name),
00068         m_surface(NULL),
00069         m_xshift(0),
00070         m_yshift(0),
00071         m_shared(false){
00072         reset(surface);
00073     }
00074 
00075     //@todo make a private function to handle this
00076     Image::Image(const uint8_t* data, uint32_t width, uint32_t height):
00077         IResource(createUniqueImageName()),
00078         m_surface(NULL),
00079         m_xshift(0),
00080         m_yshift(0),
00081         m_shared(false){
00082         SDL_Surface* surface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA, width,height, 32,
00083                                                     RMASK, GMASK, BMASK ,AMASK);
00084         SDL_LockSurface(surface);
00085 
00086         uint32_t size = width * height * 4;
00087         uint8_t* pixeldata = static_cast<uint8_t*>(surface->pixels);
00088         std::copy(data, data + size, pixeldata);
00089         SDL_UnlockSurface(surface);
00090         reset(surface);
00091     }
00092 
00093     Image::Image(const std::string& name, const uint8_t* data, uint32_t width, uint32_t height):
00094         IResource(name),
00095         m_surface(NULL),
00096         m_xshift(0),
00097         m_yshift(0),
00098         m_shared(false) {
00099         SDL_Surface* surface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA, width,height, 32,
00100                                                     RMASK, GMASK, BMASK ,AMASK);
00101         SDL_LockSurface(surface);
00102 
00103         uint32_t size = width * height * 4;
00104         uint8_t* pixeldata = static_cast<uint8_t*>(surface->pixels);
00105         std::copy(data, data + size, pixeldata);
00106         SDL_UnlockSurface(surface);
00107         reset(surface);
00108     }
00109 
00110     void Image::reset(SDL_Surface* surface) {
00111         if( m_surface && !m_shared) {
00112             SDL_FreeSurface(m_surface);
00113         }
00114 
00115         m_xshift = 0;
00116         m_yshift = 0;
00117         m_surface = surface;
00118     }
00119 
00120     Image::~Image() {
00121         reset(NULL);
00122     }
00123 
00124     void Image::load() {
00125         if (m_loader){
00126             m_loader->load(this);
00127         }
00128         else {
00129             ImageLoader loader;
00130             loader.load(this);
00131         }
00132         m_state = IResource::RES_LOADED;
00133     }
00134 
00135     void Image::free() {
00136         reset(NULL);
00137         m_state = IResource::RES_NOT_LOADED;
00138     }
00139 
00140     SDL_Surface* Image::detachSurface() {
00141         SDL_Surface* srf = m_surface;
00142         m_surface = NULL;
00143         return srf;
00144     }
00145 
00146     uint32_t Image::getWidth() const {
00147         if (m_shared) {
00148             return m_subimagerect.w;
00149         } else if (!m_surface) {
00150             return 0;
00151         }
00152         return m_surface->w;
00153     }
00154 
00155     uint32_t Image::getHeight() const {
00156         if (m_shared) {
00157             return m_subimagerect.h;
00158         } else if (!m_surface) {
00159             return 0;
00160         }
00161         return m_surface->h;
00162     }
00163 
00164     size_t Image::getSize() {
00165         if (!m_surface || m_shared) {
00166             return 0;
00167         }
00168         return m_surface->h * m_surface->pitch;
00169     }
00170 
00171     const Rect& Image::getArea() const {
00172         static Rect r(0, 0, getWidth(), getHeight());
00173         return r;
00174     }
00175 
00176     void Image::getPixelRGBA(int32_t x, int32_t y, uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) {
00177         Uint8 *p;
00178 
00179         assert(m_surface);
00180 
00181         int32_t bpp = m_surface->format->BytesPerPixel;
00182 
00183         if(!isSharedImage()) {
00184             if ((x < 0) || (x >= m_surface->w) || (y < 0) || (y >= m_surface->h)) {
00185                 r = g = b = a = 0;
00186                 return;
00187             }
00188             p = (Uint8*)m_surface->pixels + y * m_surface->pitch + x * bpp;
00189         } else {
00190             if ((x < 0) || ((x + m_subimagerect.x) >= m_surface->w) || (y < 0) || ((y + m_subimagerect.y) >= m_surface->h)) {
00191                 r = g = b = a = 0;
00192                 return;
00193             }
00194             p = (Uint8*)m_surface->pixels + (y + m_subimagerect.y) * m_surface->pitch + (x + m_subimagerect.x) * bpp;
00195         }
00196 
00197         uint32_t pixel = 0;
00198         switch(bpp) {
00199         case 1:
00200             pixel = *p;
00201             break;
00202 
00203         case 2:
00204             pixel = *(Uint16 *)p;
00205             break;
00206 
00207         case 3:
00208             if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
00209                 pixel = p[0] << 16 | p[1] << 8 | p[2];
00210             } else {
00211                 pixel = p[0] | p[1] << 8 | p[2] << 16;
00212             }
00213             break;
00214 
00215         case 4:
00216             pixel = *(Uint32 *)p;
00217             break;
00218         }
00219         SDL_GetRGBA(pixel, m_surface->format, r, g, b, a);
00220     }
00221 
00222     void Image::saveImage(const std::string& filename) {
00223         saveAsPng(filename, *m_surface);
00224     }
00225 
00226     void Image::saveAsPng(const std::string& filename, const SDL_Surface& surface) {
00227         FILE *fp;
00228         png_structp pngptr;
00229         png_infop infoptr;
00230         int32_t colortype;
00231         png_bytep *rowpointers = NULL;
00232 
00233         fp = fopen(filename.c_str(), "wb");
00234 
00235         if (fp == NULL) {
00236             return;
00237         }
00238 
00239         //create the png file
00240         pngptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
00241         NULL, NULL, NULL);
00242         if (pngptr == NULL) {
00243             fclose(fp);
00244             return;
00245         }
00246 
00247         //create information struct
00248         infoptr = png_create_info_struct(pngptr);
00249         if (infoptr == NULL) {
00250             fclose(fp);
00251             png_destroy_write_struct(&pngptr, (png_infopp)NULL);
00252             return;
00253         }
00254 
00255         if (setjmp(png_jmpbuf(pngptr))) {
00256             png_destroy_write_struct(&pngptr, &infoptr);
00257             fclose(fp);
00258             return;
00259         }
00260 
00261         //initialize io
00262         png_init_io(pngptr, fp);
00263 
00264         // lock the surface for access (we strip it off of const but we promise not to modify it, just read)
00265         SDL_LockSurface(const_cast<SDL_Surface*>(&surface));
00266 
00267         colortype = PNG_COLOR_TYPE_RGB;
00268         if(surface.format->palette){
00269             colortype |= PNG_COLOR_TYPE_PALETTE;
00270         }
00271         else if (surface.format->Amask){
00272             colortype |= PNG_COLOR_TYPE_RGB_ALPHA;
00273         }
00274         else{}
00275 
00276         png_set_IHDR(pngptr, infoptr, surface.w, surface.h, 8, colortype,
00277             PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00278 
00279         png_write_info(pngptr, infoptr);
00280         png_set_packing(pngptr);
00281 
00282         rowpointers = new png_bytep[surface.h];
00283         for (int32_t i = 0; i < surface.h; i++) {
00284             rowpointers[i] = (png_bytep)(Uint8 *)surface.pixels + i*surface.pitch;
00285         }
00286         //write the image
00287         png_write_image(pngptr, rowpointers);
00288         png_write_end(pngptr, infoptr);
00289 
00290         SDL_UnlockSurface(const_cast<SDL_Surface*>(&surface));
00291         delete [] rowpointers;
00292         png_destroy_write_struct(&pngptr, &infoptr);
00293         fclose(fp);
00294     }
00295 
00296     std::string Image::createUniqueImageName() {
00297         // automated counting for name generation, in case the user doesn't provide a name
00298         static uint32_t uniqueNumber = 0;
00299         static std::string baseName = "image";
00300 
00301         std::ostringstream oss;
00302         oss << uniqueNumber << "_" << baseName;
00303 
00304         const std::string name = oss.str();
00305         ++uniqueNumber;
00306 
00307         return name;
00308     }
00309 
00310     void Image::copySubimage(uint32_t xoffset, uint32_t yoffset, const ImagePtr& srcimg){
00311         if (!srcimg->m_surface) {
00312             return;
00313         } else if (!m_surface) {
00314             m_surface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA, srcimg->getWidth(),
00315                 srcimg->getHeight(), 32, RMASK, GMASK, BMASK ,AMASK);
00316         }
00317         SDL_SetAlpha(srcimg->m_surface, 0, 0);
00318         if(this->isSharedImage()) {
00319             Rect const& rect = this->getSubImageRect();
00320             SDL_Rect dstrect = {
00321                 rect.x + xoffset, rect.y + yoffset,
00322                 static_cast<Uint16>(srcimg->getWidth()),
00323                 static_cast<Uint16>(srcimg->getHeight()) };
00324             if(srcimg->isSharedImage()) {
00325                 Rect const& rect = srcimg->getSubImageRect();
00326                 SDL_Rect srcrect = { rect.x, rect.y, rect.w, rect.h };
00327                 SDL_BlitSurface(srcimg->m_surface, &srcrect, m_surface, &dstrect);
00328             } else {
00329                 SDL_BlitSurface(srcimg->m_surface, NULL, m_surface, &dstrect);
00330             }
00331         } else {
00332             SDL_Rect dstrect = { xoffset, yoffset,
00333                 static_cast<Uint16>(srcimg->getWidth()),
00334                 static_cast<Uint16>(srcimg->getHeight()) };
00335             if(srcimg->isSharedImage()) {
00336                 Rect const& rect = srcimg->getSubImageRect();
00337                 SDL_Rect srcrect = { rect.x, rect.y, rect.w, rect.h };
00338                 SDL_BlitSurface(srcimg->m_surface, &srcrect, m_surface, &dstrect);
00339             } else {
00340                 SDL_BlitSurface(srcimg->m_surface, NULL, m_surface, &dstrect);
00341             }
00342         }
00343         SDL_SetAlpha(srcimg->m_surface, SDL_SRCALPHA, 0);
00344     }
00345 
00346     bool Image::putPixel(SDL_Surface* surface, int32_t x, int32_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
00347         if ((x < 0) || (x >= surface->w) || (y < 0) || (y >= surface->h)) {
00348             return false;
00349         }
00350 
00351         int32_t bpp = surface->format->BytesPerPixel;
00352         SDL_LockSurface(surface);
00353         Uint8* p = (Uint8*)surface->pixels + y * surface->pitch + x * bpp;
00354         Uint32 pixel = SDL_MapRGBA(surface->format, r, g, b, a);
00355         switch(bpp)
00356         {
00357         case 1:
00358             *p = pixel;
00359             break;
00360 
00361         case 2:
00362             *(Uint16 *)p = pixel;
00363             break;
00364 
00365         case 3:
00366             if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
00367                 p[0] = (pixel >> 16) & 0xff;
00368                 p[1] = (pixel >> 8) & 0xff;
00369                 p[2] = pixel & 0xff;
00370             }
00371             else {
00372                 p[0] = pixel & 0xff;
00373                 p[1] = (pixel >> 8) & 0xff;
00374                 p[2] = (pixel >> 16) & 0xff;
00375             }
00376             break;
00377 
00378         case 4:
00379             *(Uint32 *)p = pixel;
00380             break;
00381         }
00382         SDL_UnlockSurface(surface);
00383         return true;
00384     }
00385 }