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