1 /* GemRB - Infinity Engine Emulator 2 * Copyright (C) 2012 The GemRB Project 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 2 7 * of the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 * 18 * 19 */ 20 21 using namespace GemRB; 22 23 template<bool PALALPHA> 24 struct SRTinter_NoTint { operatorSRTinter_NoTint25 void operator()(Uint8&, Uint8&, Uint8&, Uint8& a, unsigned int) const { 26 if (!PALALPHA) a = 255; 27 } 28 }; 29 30 template<bool PALALPHA, bool TINTALPHA> 31 struct SRTinter_Tint { SRTinter_TintSRTinter_Tint32 SRTinter_Tint(const Color& t) : tint(t) { } 33 operatorSRTinter_Tint34 void operator()(Uint8& r, Uint8& g, Uint8& b, Uint8& a, unsigned int) const { 35 r = (tint.r * r) >> 8; 36 g = (tint.g * g) >> 8; 37 b = (tint.b * b) >> 8; 38 if (TINTALPHA && PALALPHA) a = (tint.a * a) >> 8; 39 //if (!TINTALPHA && PALALPHA) a = a; 40 if (TINTALPHA && !PALALPHA) a = tint.a; 41 if (!TINTALPHA && !PALALPHA) a = 255; 42 } 43 Color tint; 44 }; 45 46 // Always tint, and conditionally handle grey, red 47 template<bool PALALPHA> 48 struct SRTinter_Flags { SRTinter_FlagsSRTinter_Flags49 SRTinter_Flags(const Color& t) : tint(t) { } 50 operatorSRTinter_Flags51 void operator()(Uint8& r, Uint8& g, Uint8& b, Uint8& a, unsigned int flags) const { 52 if (flags & BlitFlags::GREY) { 53 r = (tint.r * r) >> 10; 54 g = (tint.g * g) >> 10; 55 b = (tint.b * b) >> 10; 56 Uint8 avg = r + g + b; 57 r = g = b = avg; 58 } else if (flags & BlitFlags::SEPIA) { 59 r = (tint.r * r) >> 10; 60 g = (tint.g * g) >> 10; 61 b = (tint.b * b) >> 10; 62 Uint8 avg = r + g + b; 63 r = avg + 21; // can't overflow, since a is at most 189 64 g = avg; 65 b = avg < 32 ? 0 : avg - 32; 66 } else { 67 r = (tint.r * r) >> 8; 68 g = (tint.g * g) >> 8; 69 b = (tint.b * b) >> 8; 70 } 71 72 if (!PALALPHA) 73 a = tint.a; 74 else 75 a = (tint.a * a) >> 8; 76 } 77 78 Color tint; 79 }; 80 81 // Don't tint, but conditionally handle grey, sepia 82 template<bool PALALPHA> 83 struct SRTinter_FlagsNoTint { SRTinter_FlagsNoTintSRTinter_FlagsNoTint84 SRTinter_FlagsNoTint() { } 85 operatorSRTinter_FlagsNoTint86 void operator()(Uint8& r, Uint8& g, Uint8& b, Uint8& a, unsigned int flags) const { 87 if (flags & BlitFlags::GREY) { 88 r >>= 2; 89 g >>= 2; 90 b >>= 2; 91 Uint8 avg = r + g + b; 92 r = g = b = avg; 93 } else if (flags & BlitFlags::SEPIA) { 94 r >>= 2; 95 g >>= 2; 96 b >>= 2; 97 Uint8 avg = r + g + b; 98 r = avg + 21; // can't overflow, since a is at most 189 99 g = avg; 100 b = avg < 32 ? 0 : avg - 32; 101 } 102 103 if (!PALALPHA) a = 255; 104 } 105 }; 106 107 108 struct SRBlender_NoAlpha { }; 109 struct SRBlender_HalfAlpha { }; 110 struct SRBlender_Alpha { }; 111 112 template<typename PTYPE, typename BLENDER> 113 struct SRBlender { 114 SRBlender(SDL_PixelFormat* format); 115 116 void operator()(PTYPE& /*pix*/, Uint8 /*r*/, Uint8 /*g*/, Uint8 /*b*/, Uint8 /*a*/) const; 117 }; 118 119 template<typename BLENDER> 120 struct SRBlender<Uint16, BLENDER> { 121 uint8_t RLOSS; 122 uint8_t GLOSS; 123 uint8_t BLOSS; 124 125 uint8_t RSHIFT; 126 uint8_t GSHIFT; 127 uint8_t BSHIFT; 128 129 Uint16 AMASK; 130 Uint16 halfmask; 131 132 SRBlender(SDL_PixelFormat* fmt) { 133 RLOSS = fmt->Rloss; 134 GLOSS = fmt->Gloss; 135 BLOSS = fmt->Bloss; 136 137 RSHIFT = fmt->Rshift; 138 GSHIFT = fmt->Gshift; 139 BSHIFT = fmt->Bshift; 140 141 AMASK = fmt->Amask; 142 143 halfmask = ((0xFFU >> (RLOSS + 1)) << RSHIFT) | ((0xFFU >> (GLOSS + 1)) << GSHIFT) | ((0xFFU >> (BLOSS + 1)) << BSHIFT); 144 } 145 146 void operator()(Uint16& /*pix*/, Uint8 /*r*/, Uint8 /*g*/, Uint8 /*b*/, Uint8 /*a*/) const; 147 }; 148 149 template<typename BLENDER> 150 struct SRBlender<Uint32, BLENDER> { 151 uint8_t RSHIFT; 152 uint8_t GSHIFT; 153 uint8_t BSHIFT; 154 155 Uint32 AMASK; 156 Uint32 halfmask; 157 158 SRBlender(SDL_PixelFormat* fmt) { 159 RSHIFT = fmt->Rshift; 160 GSHIFT = fmt->Gshift; 161 BSHIFT = fmt->Bshift; 162 163 AMASK = fmt->Amask; 164 165 halfmask = ((0xFFU >> 1) << RSHIFT) | ((0xFFU >> 1) << GSHIFT) | ((0xFFU >> 1) << BSHIFT); 166 } 167 168 void operator()(Uint32& /*pix*/, Uint8 /*r*/, Uint8 /*g*/, Uint8 /*b*/, Uint8 /*a*/) const; 169 }; 170 171 template<> // 16 bpp, 565 172 void SRBlender<Uint16, SRBlender_NoAlpha>::operator()(Uint16& pix, Uint8 r, Uint8 g, Uint8 b, Uint8) const { 173 pix = ((r >> RLOSS) << RSHIFT) | 174 ((g >> GLOSS) << GSHIFT) | 175 ((b >> BLOSS) << BSHIFT); 176 } 177 178 template<> // 16 bpp, 565 179 void SRBlender<Uint16, SRBlender_HalfAlpha>::operator()(Uint16& pix, Uint8 r, Uint8 g, Uint8 b, Uint8) const { 180 pix = ((pix >> 1) & halfmask) + 181 ((((r >> (RLOSS + 1)) << RSHIFT) | ((g >> (GLOSS + 1)) << GSHIFT) | ((b >> (BLOSS + 1)) << BSHIFT)) & halfmask); 182 } 183 184 template<> // 16 bpp, 565 185 void SRBlender<Uint16, SRBlender_Alpha>::operator()(Uint16& pix, Uint8 r, Uint8 g, Uint8 b, Uint8 a) const { 186 unsigned int dr = 1 + a*(r >> RLOSS) + (255-a)*((pix >> RSHIFT) & ((1 << (8-RLOSS))-1)); 187 unsigned int dg = 1 + a*(g >> GLOSS) + (255-a)*((pix >> GSHIFT) & ((1 << (8-GLOSS))-1)); 188 unsigned int db = 1 + a*(b >> BLOSS) + (255-a)*((pix >> BSHIFT) & ((1 << (8-BLOSS))-1)); 189 190 r = (dr + (dr>>8)) >> 8; 191 g = (dg + (dg>>8)) >> 8; 192 b = (db + (db>>8)) >> 8; 193 pix = (r << RSHIFT) | 194 (g << GSHIFT) | 195 (b << BSHIFT); 196 } 197 198 template<> // 32 bpp, 888 199 void SRBlender<Uint32, SRBlender_NoAlpha>::operator()(Uint32& pix, Uint8 r, Uint8 g, Uint8 b, Uint8) const { 200 pix = (r << RSHIFT) | (g << GSHIFT) | (b << BSHIFT); 201 } 202 203 template<> // 32 bpp, 888 204 void SRBlender<Uint32, SRBlender_HalfAlpha>::operator()(Uint32& pix, Uint8 r, Uint8 g, Uint8 b, Uint8) const { 205 pix = ((pix >> 1) & halfmask) + 206 ((((r << RSHIFT) | (g << GSHIFT) | (b << BSHIFT)) >> 1) & halfmask); 207 } 208 209 template<> // 32 bpp, 888 210 void SRBlender<Uint32, SRBlender_Alpha>::operator()(Uint32& pix, Uint8 r, Uint8 g, Uint8 b, Uint8 a) const { 211 unsigned int dr = 1 + a*r + (255-a)*((pix >> RSHIFT) & 0xFF); 212 unsigned int dg = 1 + a*g + (255-a)*((pix >> GSHIFT) & 0xFF); 213 unsigned int db = 1 + a*b + (255-a)*((pix >> BSHIFT) & 0xFF); 214 r = (dr + (dr>>8)) >> 8; 215 g = (dg + (dg>>8)) >> 8; 216 b = (db + (db>>8)) >> 8; 217 pix = (r << RSHIFT) | (g << GSHIFT) | (b << BSHIFT); 218 } 219 220 // these always change together 221 #define ADVANCE_ITERATORS(count) dest.Advance(count); cover.Advance(count); 222 223 template<typename PTYPE, typename Tinter, typename Blender> 224 void TintedBlend(SDLPixelIterator& dest, Uint8 alpha, 225 Color col, BlitFlags flags, 226 const Tinter& tint, const Blender& blend) 227 { 228 PTYPE& pix = (PTYPE&)*dest; 229 230 tint(col.r, col.g, col.b, col.a, flags); 231 col.a = col.a - alpha; // FIXME: seems like this should be handled by something else, we shouldn't need the 'alpha' param 232 blend(pix, col.r, col.g, col.b, col.a); 233 234 // FIXME: we should probably address this in the blenders instead 235 pix |= blend.AMASK; // color keyed surface is 100% opaque 236 } 237 238 template<typename PTYPE, typename Tinter, typename Blender> 239 void MaskedTintedBlend(SDLPixelIterator& dest, Uint8 maskval, 240 const Color& col, BlitFlags flags, 241 const Tinter& tint, const Blender& blend) 242 { 243 if (maskval < 0xff) { 244 if ((flags & BlitFlags::STENCIL_DITHER) && maskval == 128) { 245 const Point& pos = dest.Position(); 246 if (pos.y % 2 == 0) { 247 maskval = (pos.x % 2) ? 0xC0 : 0x80; 248 } else { 249 maskval = (pos.x % 2) ? 0x80 : 0xC0; 250 } 251 } 252 253 TintedBlend<PTYPE>(dest, maskval, col, flags, tint, blend); 254 } 255 } 256 257 // use this when you need to copy the entire source sprite 258 template<typename PTYPE, typename Tinter, typename Blender> 259 static void BlitSpriteRLE_Total(const Uint8* rledata, 260 const Color* pal, Uint8 transindex, 261 SDLPixelIterator& dest, IAlphaIterator& cover, 262 BlitFlags flags, const Tinter& tint, const Blender& blend) 263 { 264 SDLPixelIterator end = SDLPixelIterator::end(dest); 265 while (dest != end) { 266 Uint8 p = *rledata++; 267 if (p == transindex) { 268 int count = (*rledata++) + 1; 269 ADVANCE_ITERATORS(count); 270 continue; 271 } 272 273 MaskedTintedBlend<PTYPE>(dest, *cover, pal[p], flags, tint, blend); 274 ADVANCE_ITERATORS(1); 275 } 276 } 277 278 // use this when you need a partial copy of the source sprite 279 template<typename PTYPE, typename Tinter, typename Blender> 280 static void BlitSpriteRLE_Partial(const Uint8* rledata, const int pitch, const Region& srect, 281 const Color* pal, Uint8 transindex, 282 SDLPixelIterator& dest, IAlphaIterator& cover, 283 BlitFlags flags, const Tinter& tint, const Blender& blend) 284 { 285 int count = srect.y * pitch; 286 while (count > 0) { 287 Uint8 p = *rledata++; 288 if (p == transindex) { 289 count -= (*rledata++) + 1; 290 } else { 291 --count; 292 } 293 } 294 295 int transQueue = -count; 296 const int endx = srect.x + srect.w; 297 const int endy = srect.y + srect.h; 298 for (int y = srect.y; y < endy; ++y) { 299 // We assume 'dest' and 'cover' are setup appropriately to accept 'srect.size' 300 301 if (transQueue >= pitch) { 302 transQueue -= pitch; 303 ADVANCE_ITERATORS(srect.w); 304 continue; 305 } 306 307 for (int x = 0; x < pitch;) { 308 assert(transQueue >= 0); 309 310 if (transQueue > 0) { 311 int segment = 0; 312 bool advance = false; 313 if (x < srect.x) { 314 segment = srect.x - x; 315 } else if (x >= endx) { 316 segment = pitch - x; 317 } else { 318 segment = endx - x; 319 advance = true; 320 } 321 assert(segment > 0); 322 323 if (transQueue < segment) { 324 if (advance) { 325 ADVANCE_ITERATORS(transQueue); 326 } 327 328 x += transQueue; 329 transQueue = 0; 330 } else { 331 if (advance) { 332 ADVANCE_ITERATORS(segment); 333 } 334 335 transQueue -= segment; 336 x += segment; 337 } 338 } else { 339 Uint8 p = *rledata++; 340 if (p == transindex) { 341 transQueue = (*rledata++) + 1; 342 } else { 343 if (x >= srect.x && x < endx) { 344 MaskedTintedBlend<PTYPE>(dest, *cover, pal[p], flags, tint, blend); 345 ADVANCE_ITERATORS(1); 346 } 347 ++x; 348 } 349 } 350 351 assert(x <= pitch); 352 } 353 } 354 } 355 356 template<typename Blender, typename Tinter> 357 static void BlitSpriteRLE(Holder<Sprite2D> spr, const Region& srect, 358 SDL_Surface* dst, const Region& drect, 359 IAlphaIterator* cover, 360 BlitFlags flags, const Tinter& tint) 361 { 362 assert(spr->BAM); 363 364 if (srect.Dimensions().IsEmpty()) 365 return; 366 367 if (drect.Dimensions().IsEmpty()) 368 return; 369 370 PaletteHolder palette = spr->GetPalette(); 371 const Uint8* rledata = (const Uint8*)spr->LockSprite(); 372 uint8_t ck = spr->GetColorKey(); 373 374 bool partial = spr->Frame.Dimensions() != srect.Dimensions(); 375 376 IPixelIterator::Direction xdir = (flags&BlitFlags::MIRRORX) ? IPixelIterator::Reverse : IPixelIterator::Forward; 377 IPixelIterator::Direction ydir = (flags&BlitFlags::MIRRORY) ? IPixelIterator::Reverse : IPixelIterator::Forward; 378 379 SDLPixelIterator dstit = SDLPixelIterator(dst, xdir, ydir, RectFromRegion(drect)); 380 381 static StaticAlphaIterator nomask(0); 382 if (cover == nullptr) { 383 cover = &nomask; 384 } 385 386 switch (dstit.format->BytesPerPixel) { 387 case 4: 388 { 389 SRBlender<Uint32, Blender> blend(dstit.format); 390 if (partial) { 391 BlitSpriteRLE_Partial<Uint32>(rledata, spr->Frame.w, srect, palette->col, ck, dstit, *cover, flags, tint, blend); 392 } else { 393 BlitSpriteRLE_Total<Uint32>(rledata, palette->col, ck, dstit, *cover, flags, tint, blend); 394 } 395 break; 396 } 397 case 2: 398 { 399 SRBlender<Uint16, Blender> blend(dstit.format); 400 if (partial) { 401 BlitSpriteRLE_Partial<Uint16>(rledata, spr->Frame.w, srect, palette->col, ck, dstit, *cover, flags, tint, blend); 402 } else { 403 BlitSpriteRLE_Total<Uint16>(rledata, palette->col, ck, dstit, *cover, flags, tint, blend); 404 } 405 break; 406 } 407 default: 408 Log(ERROR, "SpriteRenderer", "Invalid Bpp"); 409 break; 410 } 411 } 412 413 #undef ADVANCE_ITERATORS 414