1 /*************************************************************************** 2 * Free Heroes of Might and Magic II: https://github.com/ihhub/fheroes2 * 3 * Copyright (C) 2020 * 4 * * 5 * This program is free software; you can redistribute it and/or modify * 6 * it under the terms of the GNU General Public License as published by * 7 * the Free Software Foundation; either version 2 of the License, or * 8 * (at your option) any later version. * 9 * * 10 * This program is distributed in the hope that it will be useful, * 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 * GNU General Public License for more details. * 14 * * 15 * You should have received a copy of the GNU General Public License * 16 * along with this program; if not, write to the * 17 * Free Software Foundation, Inc., * 18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 19 ***************************************************************************/ 20 21 #include "image_tool.h" 22 #include "image_palette.h" 23 24 #include <SDL_version.h> 25 #if SDL_VERSION_ATLEAST( 2, 0, 0 ) 26 #include <SDL_surface.h> 27 #else 28 #include <SDL_video.h> 29 #endif 30 31 #if defined( FHEROES2_IMAGE_SUPPORT ) 32 #if SDL_VERSION_ATLEAST( 2, 0, 0 ) 33 #define FHEROES2_ENABLE_PNG 1 34 #include <SDL_image.h> 35 #endif 36 #endif 37 38 namespace 39 { PALPalette()40 std::vector<uint8_t> PALPalette() 41 { 42 const uint8_t * gamePalette = fheroes2::getGamePalette(); 43 44 std::vector<uint8_t> palette( 256 * 3 ); 45 for ( size_t i = 0; i < palette.size(); ++i ) { 46 palette[i] = gamePalette[i] << 2; 47 } 48 49 return palette; 50 } 51 SaveImage(const fheroes2::Image & image,const std::string & path)52 bool SaveImage( const fheroes2::Image & image, const std::string & path ) 53 { 54 const std::vector<uint8_t> & palette = PALPalette(); 55 const uint8_t * currentPalette = palette.data(); 56 57 #if SDL_VERSION_ATLEAST( 2, 0, 0 ) 58 SDL_Surface * surface = SDL_CreateRGBSurface( 0, image.width(), image.height(), 32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 ); 59 #else 60 SDL_Surface * surface = SDL_CreateRGBSurface( SDL_SWSURFACE, image.width(), image.height(), 32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 ); 61 #endif 62 if ( surface == nullptr ) 63 return false; 64 65 const uint32_t width = image.width(); 66 const uint32_t height = image.height(); 67 68 uint32_t * out = static_cast<uint32_t *>( surface->pixels ); 69 const uint32_t * outEnd = out + width * height; 70 const uint8_t * in = image.image(); 71 72 if ( surface->format->Amask > 0 ) { 73 const uint8_t * transform = image.transform(); 74 75 for ( ; out != outEnd; ++out, ++in, ++transform ) { 76 if ( *transform == 1 ) { 77 *out = SDL_MapRGBA( surface->format, 0, 0, 0, 0 ); 78 } 79 else if ( *transform == 2 ) { 80 *out = SDL_MapRGBA( surface->format, 0, 0, 0, 64 ); 81 } 82 else { 83 const uint8_t * value = currentPalette + *in * 3; 84 *out = SDL_MapRGBA( surface->format, *value, *( value + 1 ), *( value + 2 ), 255 ); 85 } 86 } 87 } 88 else { 89 for ( ; out != outEnd; ++out, ++in ) { 90 const uint8_t * value = currentPalette + *in * 3; 91 *out = SDL_MapRGB( surface->format, *value, *( value + 1 ), *( value + 2 ) ); 92 } 93 } 94 95 #if defined( FHEROES2_ENABLE_PNG ) 96 int res = 0; 97 const std::string pngExtension( ".png" ); 98 if ( path.size() > pngExtension.size() && path.compare( path.size() - pngExtension.size(), pngExtension.size(), pngExtension ) == 0 ) { 99 res = IMG_SavePNG( surface, path.c_str() ); 100 } 101 else { 102 res = SDL_SaveBMP( surface, path.c_str() ); 103 } 104 #else 105 const int res = SDL_SaveBMP( surface, path.c_str() ); 106 #endif 107 108 SDL_FreeSurface( surface ); 109 110 return res == 0; 111 } 112 } 113 114 namespace fheroes2 115 { Save(const Image & image,const std::string & path,const uint8_t background)116 bool Save( const Image & image, const std::string & path, const uint8_t background ) 117 { 118 if ( image.empty() || path.empty() ) 119 return false; 120 121 Image temp( image.width(), image.height() ); 122 temp.fill( background ); 123 124 Blit( image, temp ); 125 126 return SaveImage( temp, path ); 127 } 128 Save(const Image & image,const std::string & path)129 bool Save( const Image & image, const std::string & path ) 130 { 131 if ( image.empty() || path.empty() ) 132 return false; 133 134 return SaveImage( image, path ); 135 } 136 Load(const std::string & path,Image & image)137 bool Load( const std::string & path, Image & image ) 138 { 139 SDL_Surface * surface = SDL_LoadBMP( path.c_str() ); 140 if ( surface == nullptr ) { 141 return false; 142 } 143 144 if ( surface->format->BytesPerPixel == 3 ) { 145 image.resize( surface->w, surface->h ); 146 memset( image.transform(), 0, surface->w * surface->h ); 147 148 const uint8_t * inY = reinterpret_cast<uint8_t *>( surface->pixels ); 149 uint8_t * outY = image.image(); 150 151 const uint8_t * inYEnd = inY + surface->h * surface->pitch; 152 153 for ( ; inY != inYEnd; inY += surface->pitch, outY += surface->w ) { 154 const uint8_t * inX = inY; 155 uint8_t * outX = outY; 156 const uint8_t * inXEnd = inX + surface->w * 3; 157 158 for ( ; inX != inXEnd; inX += 3, ++outX ) { 159 *outX = GetColorId( *( inX + 2 ), *( inX + 1 ), *inX ); 160 } 161 } 162 } 163 else if ( surface->format->BytesPerPixel == 4 ) { 164 image.resize( surface->w, surface->h ); 165 image.reset(); 166 167 const uint8_t * inY = reinterpret_cast<uint8_t *>( surface->pixels ); 168 uint8_t * outY = image.image(); 169 uint8_t * transformY = image.transform(); 170 171 const uint8_t * inYEnd = inY + surface->h * surface->pitch; 172 173 for ( ; inY != inYEnd; inY += surface->pitch, outY += surface->w, transformY += surface->w ) { 174 const uint8_t * inX = inY; 175 uint8_t * outX = outY; 176 uint8_t * transformX = transformY; 177 const uint8_t * inXEnd = inX + surface->w * 4; 178 179 for ( ; inX != inXEnd; inX += 4, ++outX, ++transformX ) { 180 const uint8_t alpha = *( inX + 3 ); 181 if ( alpha < 255 ) { 182 if ( alpha == 0 ) { 183 *transformX = 1; 184 } 185 else if ( *( inX ) == 0 && *( inX + 1 ) == 0 && *( inX + 2 ) == 0 ) { 186 *transformX = 2; 187 } 188 else { 189 *outX = GetColorId( *( inX + 2 ), *( inX + 1 ), *inX ); 190 *transformX = 0; 191 } 192 } 193 else { 194 *outX = GetColorId( *( inX + 2 ), *( inX + 1 ), *inX ); 195 *transformX = 0; 196 } 197 } 198 } 199 } 200 else { 201 SDL_FreeSurface( surface ); 202 return false; 203 } 204 205 SDL_FreeSurface( surface ); 206 207 return true; 208 } 209 decodeICNSprite(const uint8_t * data,uint32_t sizeData,const int32_t width,const int32_t height,const int16_t offsetX,const int16_t offsetY)210 Sprite decodeICNSprite( const uint8_t * data, uint32_t sizeData, const int32_t width, const int32_t height, const int16_t offsetX, const int16_t offsetY ) 211 { 212 Sprite sprite( width, height, offsetX, offsetY ); 213 sprite.reset(); 214 215 uint8_t * imageData = sprite.image(); 216 uint8_t * imageTransform = sprite.transform(); 217 218 uint32_t posX = 0; 219 220 const uint8_t * dataEnd = data + sizeData; 221 222 while ( true ) { 223 if ( 0 == *data ) { // 0x00 - end of row 224 imageData += width; 225 imageTransform += width; 226 posX = 0; 227 ++data; 228 } 229 else if ( 0x80 > *data ) { // 0x01-0x7F - repeat a pixel N times 230 uint32_t pixelCount = *data; 231 ++data; 232 while ( pixelCount > 0 && data != dataEnd ) { 233 imageData[posX] = *data; 234 imageTransform[posX] = 0; 235 ++posX; 236 ++data; 237 --pixelCount; 238 } 239 } 240 else if ( 0x80 == *data ) { // 0x80 - end of image 241 break; 242 } 243 else if ( 0xC0 > *data ) { // 0xBF - empty (transparent) pixels 244 posX += *data - 0x80; 245 ++data; 246 } 247 else if ( 0xC0 == *data ) { // 0xC0 - transform layer 248 ++data; 249 250 const uint8_t transformValue = *data; 251 const uint8_t transformType = static_cast<uint8_t>( ( ( transformValue & 0x3C ) << 6 ) / 256 + 2 ); // 1 is for skipping 252 253 uint32_t pixelCount = *data % 4 ? *data % 4 : *( ++data ); 254 255 if ( ( transformValue & 0x40 ) && ( transformType <= 15 ) ) { 256 while ( pixelCount > 0 ) { 257 imageTransform[posX] = transformType; 258 ++posX; 259 --pixelCount; 260 } 261 } 262 else { 263 posX += pixelCount; 264 } 265 266 ++data; 267 } 268 else if ( 0xC1 == *data ) { // 0xC1 269 ++data; 270 uint32_t pixelCount = *data; 271 ++data; 272 while ( pixelCount > 0 ) { 273 imageData[posX] = *data; 274 imageTransform[posX] = 0; 275 ++posX; 276 --pixelCount; 277 } 278 ++data; 279 } 280 else { 281 uint32_t pixelCount = *data - 0xC0; 282 ++data; 283 while ( pixelCount > 0 ) { 284 imageData[posX] = *data; 285 imageTransform[posX] = 0; 286 ++posX; 287 --pixelCount; 288 } 289 ++data; 290 } 291 292 if ( data >= dataEnd ) { 293 break; 294 } 295 } 296 297 return sprite; 298 } 299 } 300