1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2013 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 #include "SDLSurfaceSprite2D.h"
22
23 #include "SDLPixelIterator.h"
24 #include "SDLSurfaceDrawing.h"
25 #include "SDLVideo.h"
26
27 #include "System/Logging.h"
28
29 namespace GemRB {
30
SDLSurfaceSprite2D(const Region & rgn,int Bpp,void * pixels,Uint32 rmask,Uint32 gmask,Uint32 bmask,Uint32 amask)31 SDLSurfaceSprite2D::SDLSurfaceSprite2D (const Region& rgn, int Bpp, void* pixels,
32 Uint32 rmask, Uint32 gmask, Uint32 bmask, Uint32 amask)
33 : Sprite2D(rgn, Bpp, pixels)
34 {
35 if (pixels) {
36 surface = new SurfaceHolder(SDL_CreateRGBSurfaceFrom( pixels, Frame.w, Frame.h, Bpp < 8 ? 8 : Bpp, Frame.w * ( Bpp / 8 ),
37 rmask, gmask, bmask, amask ));
38 } else {
39 surface = new SurfaceHolder(SDL_CreateRGBSurface(0, Frame.w, Frame.h, Bpp < 8 ? 8 : Bpp, rmask, gmask, bmask, amask));
40 SDL_FillRect(*surface, NULL, 0);
41 }
42
43 assert(*surface);
44 original = surface;
45 }
46
SDLSurfaceSprite2D(const Region & rgn,int Bpp,Uint32 rmask,Uint32 gmask,Uint32 bmask,Uint32 amask)47 SDLSurfaceSprite2D::SDLSurfaceSprite2D (const Region& rgn, int Bpp,
48 Uint32 rmask, Uint32 gmask, Uint32 bmask, Uint32 amask)
49 : Sprite2D(rgn, Bpp, NULL)
50 {
51 surface = new SurfaceHolder(SDL_CreateRGBSurface( 0, Frame.w, Frame.h, Bpp < 8 ? 8 : Bpp,
52 rmask, gmask, bmask, amask ));
53 SDL_FillRect(*surface, NULL, 0);
54
55 assert(*surface);
56 original = surface;
57 }
58
SDLSurfaceSprite2D(const SDLSurfaceSprite2D & obj)59 SDLSurfaceSprite2D::SDLSurfaceSprite2D(const SDLSurfaceSprite2D &obj)
60 : Sprite2D(obj)
61 {
62 SDL_PixelFormat* fmt = (*obj.surface)->format;
63 pixels = (*obj.surface)->pixels;
64
65 surface = new SurfaceHolder(
66 SDL_CreateRGBSurfaceFrom(
67 pixels,
68 Frame.w,
69 Frame.h,
70 Bpp < 8 ? 8 : Bpp,
71 Frame.w * ( Bpp / 8 ),
72 fmt->Rmask,
73 fmt->Gmask,
74 fmt->Bmask,
75 fmt->Amask
76 )
77 );
78
79 if (Bpp == 8) {
80 SDLVideoDriver::SetSurfacePalette(*surface, fmt->palette->colors);
81 SetColorKey(obj.GetColorKey());
82 }
83
84 original = surface;
85 }
86
copy() const87 Holder<Sprite2D> SDLSurfaceSprite2D::copy() const
88 {
89 return new SDLSurfaceSprite2D(*this);
90 }
91
LockSprite() const92 const void* SDLSurfaceSprite2D::LockSprite() const
93 {
94 SDL_LockSurface(*surface);
95 return (*surface)->pixels;
96 }
97
LockSprite()98 void* SDLSurfaceSprite2D::LockSprite()
99 {
100 SDL_LockSurface(*surface);
101 return (*surface)->pixels;
102 }
103
UnlockSprite() const104 void SDLSurfaceSprite2D::UnlockSprite() const
105 {
106 SDL_UnlockSurface(*surface);
107 }
108
109 /** Get the Palette of a Sprite */
GetPalette() const110 PaletteHolder SDLSurfaceSprite2D::GetPalette() const
111 {
112 PaletteHolder palette = surface->palette;
113 if (palette == NULL) {
114 SDL_PixelFormat* fmt = (*surface)->format;
115 if (fmt->BytesPerPixel != 1) {
116 return NULL;
117 }
118 assert(fmt->palette->ncolors <= 256);
119 const Color* begin = reinterpret_cast<const Color*>(fmt->palette->colors);
120 const Color* end = begin + fmt->palette->ncolors;
121 palette = new Palette(begin, end);
122 }
123 surface->palette = palette;
124
125 return palette;
126 }
127
IsPaletteStale() const128 bool SDLSurfaceSprite2D::IsPaletteStale() const
129 {
130 PaletteHolder pal = GetPalette();
131 return pal && pal->GetVersion() != palVersion;
132 }
133
SetPalette(PaletteHolder pal)134 void SDLSurfaceSprite2D::SetPalette(PaletteHolder pal)
135 {
136 PaletteHolder palette = surface->palette;
137
138 // we don't use shared palettes because it is a performance bottleneck on SDL2
139 assert(pal != palette);
140
141 if (palette) {
142 palette = nullptr;
143 }
144
145 if (version == 0) {
146 original->palette = palette;
147 } else {
148 Restore();
149 }
150
151 ieDword ck = GetColorKey();
152 if (pal && SetPalette(pal->col) == 0) {
153 palette = pal->Copy();
154 // must reset the color key or SDL 2 wont render properly
155 SetColorKey(ck);
156 }
157 surface->palette = palette;
158 }
159
SetPalette(const Color * pal) const160 int SDLSurfaceSprite2D::SetPalette(const Color* pal) const
161 {
162 return SDLVideoDriver::SetSurfacePalette(*surface, reinterpret_cast<const SDL_Color*>(pal), 0x01 << Bpp);
163 }
164
GetColorKey() const165 int32_t SDLSurfaceSprite2D::GetColorKey() const
166 {
167 #if SDL_VERSION_ATLEAST(1,3,0)
168 Uint32 ck = -1;
169 int ret = SDL_GetColorKey(*surface, &ck);
170 if (ret == 0) {
171 return ck;
172 }
173 #else
174 if ((*surface)->flags & SDL_SRCCOLORKEY) {
175 return (*surface)->format->colorkey;
176 }
177 #endif
178 return -1;
179 }
180
SetColorKey(ieDword ck)181 void SDLSurfaceSprite2D::SetColorKey(ieDword ck)
182 {
183 #if SDL_VERSION_ATLEAST(1,3,0)
184 SDL_SetColorKey(*surface, SDL_TRUE, ck);
185 // don't RLE with SDL 2
186 // this only benifits SDL_BlitSurface which we don't use. its a slowdown for us.
187 //SDL_SetSurfaceRLE(surface, SDL_TRUE);
188 #else
189 SDL_SetColorKey(*surface, SDL_SRCCOLORKEY | SDL_RLEACCEL, ck);
190 #endif
191 }
192
HasTransparency() const193 bool SDLSurfaceSprite2D::HasTransparency() const
194 {
195 SDL_PixelFormat* fmt = (*surface)->format;
196 #if SDL_VERSION_ATLEAST(1,3,0)
197 return SDL_ISPIXELFORMAT_ALPHA(fmt->format) || SDL_GetColorKey(*surface, NULL) != -1;
198 #else
199 return fmt->Amask > 0 || ((*surface)->flags & SDL_SRCCOLORKEY);
200 #endif
201 }
202
GetPixel(const Point & p) const203 Color SDLSurfaceSprite2D::GetPixel(const Point& p) const
204 {
205 if (Region(0, 0, Frame.w, Frame.h).PointInside(p)) {
206 IPixelIterator::Direction xdir = (renderFlags & BlitFlags::MIRRORX) ? IPixelIterator::Reverse : IPixelIterator::Forward;
207 IPixelIterator::Direction ydir = (renderFlags & BlitFlags::MIRRORY) ? IPixelIterator::Reverse : IPixelIterator::Forward;
208 SDLPixelIterator it(*surface, xdir, ydir);
209 it.Advance(p.y * Frame.w + p.x);
210 return it.ReadRGBA();
211 }
212 return Color();
213 }
214
ConvertFormatTo(int bpp,ieDword rmask,ieDword gmask,ieDword bmask,ieDword amask)215 bool SDLSurfaceSprite2D::ConvertFormatTo(int bpp, ieDword rmask, ieDword gmask,
216 ieDword bmask, ieDword amask)
217 {
218 if (bpp >= 8) {
219 #if SDL_VERSION_ATLEAST(1,3,0)
220 Uint32 fmt = SDL_MasksToPixelFormatEnum(bpp, rmask, gmask, bmask, amask);
221 if (fmt != SDL_PIXELFORMAT_UNKNOWN) {
222 SDL_Surface* ns = SDL_ConvertSurfaceFormat( *surface, fmt, 0);
223 #else
224 SDL_Surface* tmp = SDL_CreateRGBSurface(SDL_SWSURFACE, Frame.w, Frame.h, bpp, rmask, gmask, bmask, amask);
225 if (tmp) {
226 SDL_Surface* ns = SDL_ConvertSurface( *surface, tmp->format, 0);
227 SDL_FreeSurface(tmp);
228 #endif
229 if (ns) {
230 if (freePixels) {
231 free(pixels);
232 }
233 freePixels = false;
234 surface = new SurfaceHolder(ns);
235 Bpp = bpp;
236 return true;
237 } else {
238 Log(MESSAGE, "SDLSurfaceSprite2D",
239 #if SDL_VERSION_ATLEAST(1,3,0)
240 "Cannot convert sprite to format: %s\nError: %s", SDL_GetPixelFormatName(fmt),
241 #else
242 "Cannot convert sprite to format: %s",
243 #endif
244 SDL_GetError());
245 }
246 }
247 }
248 return false;
249 }
250
251 void SDLSurfaceSprite2D::Restore() const
252 {
253 version = 0;
254 surface = original;
255 if (Bpp == 8 && original->palette) {
256 SetPalette(original->palette->col);
257 }
258 }
259
260 void* SDLSurfaceSprite2D::NewVersion(version_t newversion) const
261 {
262 if (newversion == 0 || version != newversion) {
263 Restore();
264 version = newversion;
265 }
266
267 if (Bpp == 8) {
268 PaletteHolder pal = GetPalette(); // generate the backup
269
270 palVersion = pal->GetVersion();
271
272 // we just allow overwritting the palette
273 return surface->surface->format->palette;
274 }
275
276 palVersion = 0;
277
278 if (version != newversion) {
279 SDL_Surface* newVersion = SDL_ConvertSurface(*original, (*original)->format, 0);
280 surface = new SurfaceHolder(newVersion);
281
282 return newVersion;
283 } else {
284 return surface->surface;
285 }
286 }
287
288 #if SDL_VERSION_ATLEAST(1,3,0)
289 SDLTextureSprite2D::SDLTextureSprite2D(const Region& rgn, int Bpp, void* pixels,
290 ieDword rmask, ieDword gmask, ieDword bmask, ieDword amask)
291 : SDLSurfaceSprite2D(rgn, Bpp, pixels, rmask, gmask, bmask, amask)
292 {
293 }
294
295 SDLTextureSprite2D::SDLTextureSprite2D(const Region& rgn, int Bpp,
296 ieDword rmask, ieDword gmask, ieDword bmask, ieDword amask)
297 : SDLSurfaceSprite2D(rgn, Bpp, rmask, gmask, bmask, amask)
298 {
299 }
300
301 Holder<Sprite2D> SDLTextureSprite2D::copy() const
302 {
303 return new SDLTextureSprite2D(*this);
304 }
305
306 SDL_Texture* SDLTextureSprite2D::GetTexture(SDL_Renderer* renderer) const
307 {
308 if (texture == nullptr) {
309 SDL_Texture *tex = SDL_CreateTextureFromSurface(renderer, GetSurface());
310 SDL_QueryTexture(tex, &texFormat, nullptr, nullptr, nullptr);
311 texture = new TextureHolder(tex);
312 } else if (staleTexture) {
313 SDL_Surface *surface = GetSurface();
314 if (texFormat == surface->format->format) {
315 SDL_UpdateTexture(*texture, nullptr, surface->pixels, surface->pitch);
316 } else {
317 /* Set up a destination surface for the texture update */
318 SDL_PixelFormat *dst_fmt = SDL_AllocFormat(texFormat);
319 assert(dst_fmt);
320
321 SDL_Surface *temp = SDL_ConvertSurface(surface, dst_fmt, 0);
322 SDL_FreeFormat(dst_fmt);
323 assert(temp);
324 SDL_UpdateTexture(*texture, nullptr, temp->pixels, temp->pitch);
325 SDL_FreeSurface(temp);
326 }
327 staleTexture = false;
328 }
329 return *texture;
330 }
331
332 void SDLTextureSprite2D::UnlockSprite() const
333 {
334 SDLSurfaceSprite2D::UnlockSprite();
335 staleTexture = true;
336 }
337
338 void SDLTextureSprite2D::SetColorKey(ieDword pxvalue)
339 {
340 staleTexture = true;
341 SDLSurfaceSprite2D::SetColorKey(pxvalue);
342 }
343
344 void* SDLTextureSprite2D::NewVersion(version_t version) const
345 {
346 staleTexture = true;
347 return SDLSurfaceSprite2D::NewVersion(version);
348 }
349
350 void SDLTextureSprite2D::Restore() const
351 {
352 staleTexture = true;
353 SDLSurfaceSprite2D::Restore();
354 }
355 #endif
356
357 }
358