1 //       _________ __                 __
2 //      /   _____//  |_____________ _/  |______     ____  __ __  ______
3 //      \_____  \\   __\_  __ \__  \\   __\__  \   / ___\|  |  \/  ___/
4 //      /        \|  |  |  | \// __ \|  |  / __ \_/ /_/  >  |  /\___ |
5 //     /_______  /|__|  |__|  (____  /__| (____  /\___  /|____//____  >
6 //             \/                  \/          \//_____/            \/
7 //  ______________________                           ______________________
8 //                        T H E   W A R   B E G I N S
9 //         Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name graphic.cpp - The general graphic functions. */
12 //
13 //      (c) Copyright 1999-2011 by Lutz Sammer, Nehal Mistry, Jimmy Salmon and
14 //                                 Pali Rohár
15 //
16 //      This program is free software; you can redistribute it and/or modify
17 //      it under the terms of the GNU General Public License as published by
18 //      the Free Software Foundation; only version 2 of the License.
19 //
20 //      This program is distributed in the hope that it will be useful,
21 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
22 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 //      GNU General Public License for more details.
24 //
25 //      You should have received a copy of the GNU General Public License
26 //      along with this program; if not, write to the Free Software
27 //      Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28 //      02111-1307, USA.
29 //
30 
31 //@{
32 
33 /*----------------------------------------------------------------------------
34 --  Includes
35 ----------------------------------------------------------------------------*/
36 
37 #include "stratagus.h"
38 
39 #include <string>
40 #include <map>
41 #include <list>
42 
43 #include "SDL_image.h"
44 
45 #include "video.h"
46 #include "player.h"
47 #include "intern_video.h"
48 #include "iocompat.h"
49 #include "iolib.h"
50 #include "ui.h"
51 
52 /*----------------------------------------------------------------------------
53 --  Variables
54 ----------------------------------------------------------------------------*/
55 
56 static int HashCount;
57 static std::map<std::string, CGraphic *> GraphicHash;
58 static std::list<CGraphic *> Graphics;
59 
60 /*----------------------------------------------------------------------------
61 --  Functions
62 ----------------------------------------------------------------------------*/
63 
64 /**
65 **  Video draw the graphic clipped.
66 **
67 **  @param x   X position on the target surface
68 **  @param y   Y position on the target surface
69 **  @param surface target surface
70 */
DrawClip(int x,int y,SDL_Surface * surface) const71 void CGraphic::DrawClip(int x, int y,
72 						SDL_Surface *surface /*= TheScreen*/) const
73 {
74 	int oldx = x;
75 	int oldy = y;
76 	int w = Width;
77 	int h = Height;
78 	CLIP_RECTANGLE(x, y, w, h);
79 	DrawSub(x - oldx, y - oldy, w, h, x, y, surface);
80 }
81 
82 /**
83 **  Video draw part of graphic.
84 **
85 **  @param gx  X offset into object
86 **  @param gy  Y offset into object
87 **  @param w   width to display
88 **  @param h   height to display
89 **  @param x   X position on the target surface
90 **  @param y   Y position on the target surface
91 **	@param surface target surface
92 */
DrawSub(int gx,int gy,int w,int h,int x,int y,SDL_Surface * surface) const93 void CGraphic::DrawSub(int gx, int gy, int w, int h, int x, int y,
94 					   SDL_Surface *surface /*= TheScreen*/) const
95 {
96 	Assert(surface);
97 
98 	SDL_Rect srect = {Sint16(gx), Sint16(gy), Uint16(w), Uint16(h)};
99 	SDL_Rect drect = {Sint16(x), Sint16(y), 0, 0};
100 
101 	SDL_BlitSurface(Surface, &srect, surface, &drect);
102 }
103 
104 /**
105 **  Video draw part of graphic by custom pixel modifier.
106 **  32bpp only (yet?)
107 **
108 **  @param gx  X offset into object
109 **  @param gy  Y offset into object
110 **  @param w   width to display
111 **  @param h   height to display
112 **  @param x   X position on the target surface
113 **  @param y   Y position on the target surface
114 **  @param modifier method used to draw pixels instead of SDL_BlitSurface
115 **  @param param    parameter for modifier
116 **  @param surface  target surface
117 */
DrawSubCustomMod(int gx,int gy,int w,int h,int x,int y,pixelModifier modifier,const uint32_t param,SDL_Surface * surface) const118 void CGraphic::DrawSubCustomMod(int gx, int gy, int w, int h, int x, int y,
119 							    pixelModifier modifier, const uint32_t param,
120 								SDL_Surface *surface /*= TheScreen*/) const
121 {
122 	Assert(surface);
123 	Assert(surface->format->BitsPerPixel == 32);
124 
125 
126 	size_t srcOffset = Surface->w * gy + gx;
127 	size_t dstOffset = surface->w * y + x;
128 
129 	uint32_t *const src = reinterpret_cast<uint32_t *>(Surface->pixels);
130 	uint32_t *const dst = reinterpret_cast<uint32_t *>(surface->pixels);
131 
132 	for (uint16_t posY = 0; posY < h; posY++) {
133 		for (uint16_t posX = 0; posX < w; posX++) {
134 			const uint32_t srcColor = src[srcOffset + posX];
135 			const uint32_t dstColor = dst[dstOffset + posX];
136 			const uint32_t resColor =  modifier(srcColor, dstColor, param);
137 
138 			dst[dstOffset + posX] = resColor;
139 		}
140 		dstOffset += surface->w;
141 		srcOffset += Surface->w;
142 	}
143 
144 }
145 
146 
147 /**
148 **  Video draw part of graphic clipped.
149 **
150 **  @param gx  X offset into object
151 **  @param gy  Y offset into object
152 **  @param w   width to display
153 **  @param h   height to display
154 **  @param x   X position on the target surface
155 **  @param y   Y position on the target surface
156 **  @param surface target surface
157 */
DrawSubClip(int gx,int gy,int w,int h,int x,int y,SDL_Surface * surface) const158 void CGraphic::DrawSubClip(int gx, int gy, int w, int h, int x, int y,
159 						   SDL_Surface *surface /*= TheScreen*/) const
160 {
161 	Assert(surface);
162 
163 	int oldx = x;
164 	int oldy = y;
165 	CLIP_RECTANGLE(x, y, w, h);
166 	gx += x - oldx;
167 	gy += y - oldy;
168 
169 	SDL_Rect srect = {Sint16(gx), Sint16(gy), Uint16(w), Uint16(h)};
170 	SDL_Rect drect = {Sint16(x), Sint16(y), 0, 0};
171 	SDL_BlitSurface(Surface, &srect, surface, &drect);
172 }
173 
174 /**
175 **  Video draw part of graphic with alpha.
176 **
177 **  @param gx     X offset into object
178 **  @param gy     Y offset into object
179 **  @param w      width to display
180 **  @param h      height to display
181 **  @param x      X position on the target surface
182 **  @param y      Y position on the target surface
183 **  @param alpha  Alpha
184 **  @param surface target surface
185 */
DrawSubTrans(int gx,int gy,int w,int h,int x,int y,unsigned char alpha,SDL_Surface * surface) const186 void CGraphic::DrawSubTrans(int gx, int gy, int w, int h, int x, int y,
187 							unsigned char alpha,
188 							SDL_Surface *surface /*= TheScreen*/) const
189 {
190 	Assert(surface);
191 
192 	Uint8 oldalpha = 0xff;
193 	SDL_GetSurfaceAlphaMod(Surface, &oldalpha);
194 	SDL_SetSurfaceAlphaMod(Surface, alpha);
195 	DrawSub(gx, gy, w, h, x, y, surface);
196 	SDL_SetSurfaceAlphaMod(Surface, oldalpha);
197 }
198 
199 /**
200 **  Video draw part of graphic with alpha and clipped.
201 **
202 **  @param gx     X offset into object
203 **  @param gy     Y offset into object
204 **  @param w      width to display
205 **  @param h      height to display
206 **  @param x      X position on the target surface
207 **  @param y      Y position on the target surface
208 **  @param alpha  Alpha
209 **  @param surface target surface
210 */
DrawSubClipTrans(int gx,int gy,int w,int h,int x,int y,unsigned char alpha,SDL_Surface * surface) const211 void CGraphic::DrawSubClipTrans(int gx, int gy, int w, int h, int x, int y,
212 								unsigned char alpha,
213 								SDL_Surface *surface /*= TheScreen*/) const
214 {
215 	int oldx = x;
216 	int oldy = y;
217 	CLIP_RECTANGLE(x, y, w, h);
218 	DrawSubTrans(gx + x - oldx, gy + y - oldy, w, h, x, y, alpha, surface);
219 }
220 
221 
222 /**
223 **  Video draw clipped part of graphic by custom pixel modifier.
224 **  32bpp only (yet?)
225 **
226 **  @param gx  X offset into object
227 **  @param gy  Y offset into object
228 **  @param w   width to display
229 **  @param h   height to display
230 **  @param x   X position on the target surface
231 **  @param y   Y position on the target surface
232 **  @param modifier method used to draw pixels instead of SDL_BlitSurface
233 **  @param param    parameter for modifier
234 **  @param surface  target surface
235 */
DrawSubClipCustomMod(int gx,int gy,int w,int h,int x,int y,pixelModifier modifier,const uint32_t param,SDL_Surface * surface) const236 void CGraphic::DrawSubClipCustomMod(int gx, int gy, int w, int h, int x, int y,
237 								    pixelModifier modifier,
238 									const uint32_t param,
239 								    SDL_Surface *surface /*= TheScreen*/) const
240 {
241 	int oldx = x;
242 	int oldy = y;
243 	CLIP_RECTANGLE(x, y, w, h);
244 	DrawSubCustomMod(gx + x - oldx, gy + y - oldy, w, h, x, y, modifier, param, surface);
245 }
246 
247 /**
248 **  Draw graphic object unclipped.
249 **
250 **  @param frame   number of frame (object index)
251 **  @param x       x coordinate on the target surface
252 **  @param y       y coordinate on the target surface
253 **  @param surface target surface
254 */
DrawFrame(unsigned frame,int x,int y,SDL_Surface * surface) const255 void CGraphic::DrawFrame(unsigned frame, int x, int y,
256 						 SDL_Surface *surface /*= TheScreen*/) const
257 {
258 	DrawSub(frame_map[frame].x, frame_map[frame].y,
259 			Width, Height, x, y, surface);
260 }
261 
262 /**
263 **  Draw graphic object clipped.
264 **
265 **  @param frame   number of frame (object index)
266 **  @param x       x coordinate on the target surface
267 **  @param y       y coordinate on the target surface
268 **  @param surface target surface
269 */
DrawFrameClip(unsigned frame,int x,int y,SDL_Surface * surface) const270 void CGraphic::DrawFrameClip(unsigned frame, int x, int y,
271 							 SDL_Surface *surface /*= TheScreen*/) const
272 {
273 	DrawSubClip(frame_map[frame].x, frame_map[frame].y,
274 				Width, Height, x, y, surface);
275 }
276 
DrawFrameTrans(unsigned frame,int x,int y,int alpha,SDL_Surface * surface) const277 void CGraphic::DrawFrameTrans(unsigned frame, int x, int y, int alpha,
278 							  SDL_Surface *surface /*= TheScreen*/) const
279 {
280 	DrawSubTrans(frame_map[frame].x, frame_map[frame].y,
281 				 Width, Height, x, y, alpha, surface);
282 }
283 
DrawFrameClipTrans(unsigned frame,int x,int y,int alpha,SDL_Surface * surface) const284 void CGraphic::DrawFrameClipTrans(unsigned frame, int x, int y, int alpha,
285 								  SDL_Surface *surface /* = TheScreen*/) const
286 {
287 	DrawSubClipTrans(frame_map[frame].x, frame_map[frame].y,
288 					 Width, Height, x, y, alpha, surface);
289 }
290 
DrawFrameClipCustomMod(unsigned frame,int x,int y,pixelModifier modifier,const uint32_t param,SDL_Surface * surface) const291 void CGraphic::DrawFrameClipCustomMod(unsigned frame, int x, int y,
292 									  pixelModifier modifier,
293 									  const uint32_t param,
294 									  SDL_Surface *surface /* = TheScreen*/) const
295 {
296 	DrawSubClipCustomMod(frame_map[frame].x, frame_map[frame].y,
297 						 Width, Height, x, y, modifier, param, surface);
298 }
299 
300 /**
301 **  Draw graphic object clipped and with player colors.
302 **
303 **  @param player  player number
304 **  @param frame   number of frame (object index)
305 **  @param x       x coordinate on the target surface
306 **  @param y       y coordinate on the target surface
307 **	@param surface target surface
308 */
DrawPlayerColorFrameClip(int player,unsigned frame,int x,int y,SDL_Surface * surface)309 void CPlayerColorGraphic::DrawPlayerColorFrameClip(int player, unsigned frame,
310 												   int x, int y,
311 												   SDL_Surface *surface /*= TheScreen*/)
312 {
313 	GraphicPlayerPixels(Players[player], *this);
314 	DrawFrameClip(frame, x, y, surface);
315 }
316 
317 /**
318 **  Draw graphic object unclipped and flipped in X direction.
319 **
320 **  @param frame   number of frame (object index)
321 **  @param x       x coordinate on the target surface
322 **  @param y       y coordinate on the target surface
323 **	@param surface target surface
324 */
DrawFrameX(unsigned frame,int x,int y,SDL_Surface * surface) const325 void CGraphic::DrawFrameX(unsigned frame, int x, int y,
326 						  SDL_Surface *surface /*= TheScreen*/) const
327 {
328 	SDL_Rect srect = {frameFlip_map[frame].x, frameFlip_map[frame].y, Uint16(Width), Uint16(Height)};
329 	SDL_Rect drect = {Sint16(x), Sint16(y), 0, 0};
330 
331 	SDL_BlitSurface(SurfaceFlip, &srect, surface, &drect);
332 }
333 
334 /**
335 **  Draw graphic object clipped and flipped in X direction.
336 **
337 **  @param frame   number of frame (object index)
338 **  @param x       x coordinate on the target surface
339 **  @param y       y coordinate on the target surface
340 **	@param surface target surface
341 */
DrawFrameClipX(unsigned frame,int x,int y,SDL_Surface * surface) const342 void CGraphic::DrawFrameClipX(unsigned frame, int x, int y,
343 							  SDL_Surface *surface /*= TheScreen*/) const
344 {
345 	SDL_Rect srect = {frameFlip_map[frame].x, frameFlip_map[frame].y, Uint16(Width), Uint16(Height)};
346 
347 	const int oldx = x;
348 	const int oldy = y;
349 	CLIP_RECTANGLE(x, y, srect.w, srect.h);
350 	srect.x += x - oldx;
351 	srect.y += y - oldy;
352 
353 	SDL_Rect drect = {Sint16(x), Sint16(y), 0, 0};
354 
355 	SDL_BlitSurface(SurfaceFlip, &srect, surface, &drect);
356 }
357 
DrawFrameTransX(unsigned frame,int x,int y,int alpha,SDL_Surface * surface) const358 void CGraphic::DrawFrameTransX(unsigned frame, int x, int y, int alpha,
359 							   SDL_Surface *surface /*= TheScreen*/) const
360 {
361 	SDL_Rect srect = {frameFlip_map[frame].x, frameFlip_map[frame].y, Uint16(Width), Uint16(Height)};
362 	SDL_Rect drect = {Sint16(x), Sint16(y), 0, 0};
363 	Uint8 oldalpha = 0xff;
364 	SDL_GetSurfaceAlphaMod(SurfaceFlip, &oldalpha);
365 
366 	SDL_SetSurfaceAlphaMod(SurfaceFlip, alpha);
367 	SDL_BlitSurface(SurfaceFlip, &srect, surface, &drect);
368 	SDL_SetSurfaceAlphaMod(SurfaceFlip, oldalpha);
369 }
370 
DrawFrameClipTransX(unsigned frame,int x,int y,int alpha,SDL_Surface * surface) const371 void CGraphic::DrawFrameClipTransX(unsigned frame, int x, int y, int alpha,
372 								   SDL_Surface *surface /*= TheScreen*/) const
373 {
374 	SDL_Rect srect = {frameFlip_map[frame].x, frameFlip_map[frame].y, Uint16(Width), Uint16(Height)};
375 
376 	int oldx = x;
377 	int oldy = y;
378 	CLIP_RECTANGLE(x, y, srect.w, srect.h);
379 	srect.x += x - oldx;
380 	srect.y += y - oldy;
381 
382 	SDL_Rect drect = {Sint16(x), Sint16(y), 0, 0};
383 	Uint8 oldalpha = 0xff;
384 	SDL_GetSurfaceAlphaMod(SurfaceFlip, &oldalpha);
385 
386 	SDL_SetSurfaceAlphaMod(SurfaceFlip, alpha);
387 	SDL_BlitSurface(SurfaceFlip, &srect, surface, &drect);
388 	SDL_SetSurfaceAlphaMod(SurfaceFlip, oldalpha);
389 }
390 
391 /**
392 **  Draw graphic object clipped, flipped, and with player colors.
393 **
394 **  @param player  player number
395 **  @param frame   number of frame (object index)
396 **  @param x       x coordinate on the target surface
397 **  @param y       y coordinate on the target surface
398 **  @param surface target surface
399 */
DrawPlayerColorFrameClipX(int player,unsigned frame,int x,int y,SDL_Surface * surface)400 void CPlayerColorGraphic::DrawPlayerColorFrameClipX(int player, unsigned frame,
401 													int x, int y,
402 													SDL_Surface *surface /*= TheScreen*/)
403 {
404 	GraphicPlayerPixels(Players[player], *this);
405 	DrawFrameClipX(frame, x, y, surface);
406 }
407 
408 /*----------------------------------------------------------------------------
409 --  Global functions
410 ----------------------------------------------------------------------------*/
411 
412 /**
413 **  Make a new graphic object.
414 **
415 **  @param filename  Filename
416 **  @param w     Width of a frame (optional)
417 **  @param h     Height of a frame (optional)
418 **
419 **  @return      New graphic object
420 */
New(const std::string & filename,int w,int h)421 CGraphic *CGraphic::New(const std::string &filename, int w, int h)
422 {
423 	if (filename.empty()) {
424 		return new CGraphic;
425 	}
426 
427 	const std::string file = LibraryFileName(filename.c_str());
428 	CGraphic *&g = GraphicHash[file];
429 	if (g == NULL) {
430 		g = new CGraphic;
431 		if (!g) {
432 			fprintf(stderr, "Out of memory\n");
433 			ExitFatal(-1);
434 		}
435 		// FIXME: use a constructor for this
436 		g->File = file;
437 		g->HashFile = g->File;
438 		g->Width = w;
439 		g->Height = h;
440 	} else {
441 		++g->Refs;
442 		DebugPrint("f:%s,w%d,W%d,H%d,h%d\n" _C_ filename.c_str() _C_ w _C_ g->Width _C_ g->Height _C_ h);
443 		Assert((w == 0 || g->Width == w) && (g->Height == h || h == 0));
444 	}
445 
446 	return g;
447 }
448 
449 /**
450 **  Make a new player color graphic object.
451 **
452 **  @param filename  Filename
453 **  @param w     Width of a frame (optional)
454 **  @param h     Height of a frame (optional)
455 **
456 **  @return      New graphic object
457 */
New(const std::string & filename,int w,int h)458 CPlayerColorGraphic *CPlayerColorGraphic::New(const std::string &filename, int w, int h)
459 {
460 	if (filename.empty()) {
461 		return new CPlayerColorGraphic;
462 	}
463 
464 	const std::string file = LibraryFileName(filename.c_str());
465 	CPlayerColorGraphic *g = dynamic_cast<CPlayerColorGraphic *>(GraphicHash[file]);
466 	if (g == NULL) {
467 		g = new CPlayerColorGraphic;
468 		if (!g) {
469 			fprintf(stderr, "Out of memory\n");
470 			ExitFatal(-1);
471 		}
472 		// FIXME: use a constructor for this
473 		g->File = file;
474 		g->HashFile = g->File;
475 		g->Width = w;
476 		g->Height = h;
477 		GraphicHash[g->HashFile] = g;
478 	} else {
479 		++g->Refs;
480 		Assert((w == 0 || g->Width == w) && (g->Height == h || h == 0));
481 	}
482 
483 	return g;
484 }
485 
486 /**
487 **  Make a new graphic object.  Don't reuse a graphic from the hash table.
488 **
489 **  @param file  Filename
490 **  @param w     Width of a frame (optional)
491 **  @param h     Height of a frame (optional)
492 **
493 **  @return      New graphic object
494 */
ForceNew(const std::string & file,int w,int h)495 CGraphic *CGraphic::ForceNew(const std::string &file, int w, int h)
496 {
497 	CGraphic *g = new CGraphic;
498 	if (!g) {
499 		fprintf(stderr, "Out of memory\n");
500 		ExitFatal(-1);
501 	}
502 	g->File = file;
503 	int bufSize = file.size() + 32;
504 	char *hashfile = new char[bufSize];
505 	snprintf(hashfile, bufSize, "%s%d", file.c_str(), HashCount++);
506 	g->HashFile = hashfile;
507 	delete[] hashfile;
508 	g->Width = w;
509 	g->Height = h;
510 	GraphicHash[g->HashFile] = g;
511 
512 	return g;
513 }
514 
515 /**
516 **  Clone a graphic
517 **
518 **  @param grayscale  Make grayscale texture
519 */
Clone(bool grayscale) const520 CPlayerColorGraphic *CPlayerColorGraphic::Clone(bool grayscale) const
521 {
522 	CPlayerColorGraphic *g = CPlayerColorGraphic::ForceNew(this->File, this->Width, this->Height);
523 
524 	if (this->IsLoaded()) {
525 		g->Load(grayscale);
526 	}
527 
528 	return g;
529 }
530 
531 /**
532 **  Get a graphic object.
533 **
534 **  @param filename  Filename
535 **
536 **  @return      Graphic object
537 */
Get(const std::string & filename)538 CGraphic *CGraphic::Get(const std::string &filename)
539 {
540 	if (filename.empty()) {
541 		return NULL;
542 	}
543 
544 	const std::string file = LibraryFileName(filename.c_str());
545 	CGraphic *&g = GraphicHash[file];
546 
547 	return g;
548 }
549 
550 /**
551 **  Get a player color graphic object.
552 **
553 **  @param filename  Filename
554 **
555 **  @return      Graphic object
556 */
Get(const std::string & filename)557 CPlayerColorGraphic *CPlayerColorGraphic::Get(const std::string &filename)
558 {
559 	if (filename.empty()) {
560 		return NULL;
561 	}
562 
563 	const std::string file = LibraryFileName(filename.c_str());
564 	CPlayerColorGraphic *g = dynamic_cast<CPlayerColorGraphic *>(GraphicHash[file]);
565 
566 	return g;
567 }
568 
569 /**
570 **  Make a new player color graphic object.  Don't reuse a graphic from the
571 **  hash table.
572 **
573 **  @param file  Filename
574 **  @param w     Width of a frame (optional)
575 **  @param h     Height of a frame (optional)
576 **
577 **  @return      New graphic object
578 */
ForceNew(const std::string & file,int w,int h)579 CPlayerColorGraphic *CPlayerColorGraphic::ForceNew(const std::string &file, int w, int h)
580 {
581 	CPlayerColorGraphic *g = new CPlayerColorGraphic;
582 	if (!g) {
583 		fprintf(stderr, "Out of memory\n");
584 		ExitFatal(-1);
585 	}
586 	g->File = file;
587 	size_t bufSize = file.size() + 32;
588 	char *hashfile = new char[bufSize];
589 	snprintf(hashfile, bufSize, "%s%d", file.c_str(), HashCount++);
590 	g->HashFile = hashfile;
591 	delete[] hashfile;
592 	g->Width = w;
593 	g->Height = h;
594 	GraphicHash[g->HashFile] = g;
595 
596 	return g;
597 }
598 
GenFramesMap()599 void CGraphic::GenFramesMap()
600 {
601 	Assert(NumFrames != 0);
602 	Assert(Surface != NULL);
603 	Assert(Width != 0);
604 	Assert(Height != 0);
605 
606 	delete[] frame_map;
607 
608 	frame_map = new frame_pos_t[NumFrames];
609 
610 	for (int frame = 0; frame < NumFrames; ++frame) {
611 		frame_map[frame].x = (frame % (Surface->w / Width)) * Width;
612 		frame_map[frame].y = (frame / (Surface->w / Width)) * Height;
613 	}
614 }
615 
ApplyGrayScale(SDL_Surface * Surface,int Width,int Height)616 static void ApplyGrayScale(SDL_Surface *Surface, int Width, int Height)
617 {
618 	SDL_LockSurface(Surface);
619 	const SDL_PixelFormat *f = Surface->format;
620 	const int bpp = Surface->format->BytesPerPixel;
621 	const double redGray = 0.21;
622 	const double greenGray = 0.72;
623 	const double blueGray = 0.07;
624 
625 	switch (bpp) {
626 		case 1: {
627 			SDL_Color colors[256];
628 			SDL_Palette &pal = *Surface->format->palette;
629 			for (int i = 0; i < 256; ++i) {
630 				const int gray = redGray * pal.colors[i].r + greenGray * pal.colors[i].g + blueGray * pal.colors[i].b;
631 				colors[i].r = colors[i].g = colors[i].b = gray;
632 			}
633 			SDL_SetPaletteColors(&pal, &colors[0], 0, 256);
634 			break;
635 		}
636 		case 4: {
637 			Uint32 *p;
638 			for (int i = 0; i < Height; ++i) {
639 				for (int j = 0; j < Width; ++j) {
640 					p = static_cast<Uint32 *>(Surface->pixels) + (i * Surface->w + j);
641 					const Uint32 gray = ((Uint8)((*p) * redGray) >> f->Rshift) +
642 										((Uint8)(*(p + 1) * greenGray) >> f->Gshift) +
643 										((Uint8)(*(p + 2) * blueGray) >> f->Bshift) +
644 										((Uint8)(*(p + 3)) >> f->Ashift);
645 					*p = gray;
646 				}
647 			}
648 			break;
649 		}
650 	}
651 	SDL_UnlockSurface(Surface);
652 }
653 
654 /**
655 **  Load a graphic
656 **
657 **  @param grayscale  Make a grayscale surface
658 */
Load(bool grayscale)659 void CGraphic::Load(bool grayscale)
660 {
661 	if (Surface) {
662 		return;
663 	}
664 
665 	CFile fp;
666 	const std::string name = LibraryFileName(File.c_str());
667 	if (name.empty()) {
668 		perror("Cannot find file");
669 		goto error;
670 	}
671 	if (fp.open(name.c_str(), CL_OPEN_READ) == -1) {
672 		perror("Can't open file");
673 		goto error;
674 	}
675 	Surface = IMG_Load_RW(fp.as_SDL_RWops(), 0);
676 	if (Surface == NULL) {
677 		fprintf(stderr, "Couldn't load file %s: %s", name.c_str(), IMG_GetError());
678 		goto error;
679 	}
680 
681 	GraphicWidth = Surface->w;
682 	GraphicHeight = Surface->h;
683 	fp.close();
684 
685 	if (Surface->format->BytesPerPixel == 1) {
686 		VideoPaletteListAdd(Surface);
687 	}
688 
689 	if (!Width) {
690 		Width = GraphicWidth;
691 	}
692 	if (!Height) {
693 		Height = GraphicHeight;
694 	}
695 
696 	Assert(Width <= GraphicWidth && Height <= GraphicHeight);
697 
698 	if ((GraphicWidth / Width) * Width != GraphicWidth ||
699 		(GraphicHeight / Height) * Height != GraphicHeight) {
700 		fprintf(stderr, "Invalid graphic (width, height) %s\n", File.c_str());
701 		fprintf(stderr, "Expected: (%d,%d)  Found: (%d,%d)\n",
702 				Width, Height, GraphicWidth, GraphicHeight);
703 		ExitFatal(-1);
704 	}
705 
706 	NumFrames = GraphicWidth / Width * GraphicHeight / Height;
707 
708 	if (grayscale) {
709 		ApplyGrayScale(Surface, Width, Height);
710 	}
711 
712 	GenFramesMap();
713 	return;
714 
715  error:
716 	fprintf(stderr, "Can't load the graphic '%s'\n", File.c_str());
717 	ExitFatal(-1);
718 }
719 
720 /**
721 **  Free a SDL surface
722 **
723 **  @param surface  SDL surface to free
724 */
FreeSurface(SDL_Surface ** surface)725 static void FreeSurface(SDL_Surface **surface)
726 {
727 	if (!*surface) {
728 		return;
729 	}
730 	VideoPaletteListRemove(*surface);
731 
732 	unsigned char *pixels = NULL;
733 
734 	if ((*surface)->flags & SDL_PREALLOC) {
735 		pixels = (unsigned char *)(*surface)->pixels;
736 	}
737 
738 	SDL_FreeSurface(*surface);
739 	delete[] pixels;
740 	*surface = NULL;
741 }
742 
743 /**
744 **  Free a graphic
745 **
746 **  @param g  Pointer to the graphic
747 */
Free(CGraphic * g)748 void CGraphic::Free(CGraphic *g)
749 {
750 	if (!g) {
751 		return;
752 	}
753 
754 	Assert(g->Refs);
755 
756 	--g->Refs;
757 	if (!g->Refs) {
758 		FreeSurface(&g->Surface);
759 		delete[] g->frame_map;
760 		g->frame_map = NULL;
761 
762 		FreeSurface(&g->SurfaceFlip);
763 		delete[] g->frameFlip_map;
764 		g->frameFlip_map = NULL;
765 
766 		if (!g->HashFile.empty()) {
767 			GraphicHash.erase(g->HashFile);
768 		}
769 		delete g;
770 	}
771 }
772 
773 /**
774 **  Flip graphic and store in graphic->SurfaceFlip
775 */
Flip()776 void CGraphic::Flip()
777 {
778 	if (SurfaceFlip) {
779 		return;
780 	}
781 
782 	SDL_Surface *s = SurfaceFlip = SDL_ConvertSurface(Surface, Surface->format, 0);
783 	Uint32 ckey;
784 	if (!SDL_GetColorKey(Surface, &ckey)) {
785 		SDL_SetColorKey(SurfaceFlip, SDL_TRUE, ckey);
786 	}
787 	SDL_SetSurfaceBlendMode(SurfaceFlip, SDL_BLENDMODE_NONE);
788 	if (SurfaceFlip->format->BytesPerPixel == 1) {
789 		VideoPaletteListAdd(SurfaceFlip);
790 	}
791 	SDL_LockSurface(Surface);
792 	SDL_LockSurface(s);
793 	switch (s->format->BytesPerPixel) {
794 		case 1:
795 			for (int i = 0; i < s->h; ++i) {
796 				for (int j = 0; j < s->w; ++j) {
797 					((char *)s->pixels)[j + i * s->pitch] =
798 						((char *)Surface->pixels)[s->w - j - 1 + i * Surface->pitch];
799 				}
800 			}
801 			break;
802 		case 3:
803 			for (int i = 0; i < s->h; ++i) {
804 				for (int j = 0; j < s->w; ++j) {
805 					memcpy(&((char *)s->pixels)[j + i * s->pitch],
806 						   &((char *)Surface->pixels)[(s->w - j - 1) * 3 + i * Surface->pitch], 3);
807 				}
808 			}
809 			break;
810 		case 4: {
811 			for (int i = 0; i < s->h; ++i) {
812 				for (int j = 0; j < s->w; ++j) {
813 					memcpy(&((char *)s->pixels)[j + i * s->pitch],
814 						   &((char *)Surface->pixels)[(s->w - j - 1) * 4 + i * Surface->pitch], 4);
815 				}
816 			}
817 		}
818 		break;
819 	}
820 	SDL_UnlockSurface(Surface);
821 	SDL_UnlockSurface(s);
822 
823 	delete[] frameFlip_map;
824 
825 	frameFlip_map = new frame_pos_t[NumFrames];
826 
827 	for (int frame = 0; frame < NumFrames; ++frame) {
828 		frameFlip_map[frame].x = ((NumFrames - frame - 1) % (SurfaceFlip->w / Width)) * Width;
829 		frameFlip_map[frame].y = (frame / (SurfaceFlip->w / Width)) * Height;
830 	}
831 }
832 
833 /**
834 **  Resize a graphic
835 **
836 **  @param w  New width of graphic.
837 **  @param h  New height of graphic.
838 */
Resize(int w,int h)839 void CGraphic::Resize(int w, int h)
840 {
841 	Assert(Surface); // can't resize before it's been loaded
842 
843 	if (GraphicWidth == w && GraphicHeight == h) {
844 		return;
845 	}
846 
847 	// Resizing the same image multiple times looks horrible
848 	// If the image has already been resized then get a clean copy first
849 	if (Resized) {
850 		this->SetOriginalSize();
851 		if (GraphicWidth == w && GraphicHeight == h) {
852 			return;
853 		}
854 	}
855 
856 	Resized = true;
857 	Uint32 ckey;
858 	bool useckey = !SDL_GetColorKey(Surface, &ckey);
859 
860 	int bpp = Surface->format->BytesPerPixel;
861 	if (bpp == 1) {
862 		SDL_Color pal[256];
863 
864 		SDL_LockSurface(Surface);
865 
866 		unsigned char *pixels = (unsigned char *)Surface->pixels;
867 		unsigned char *data = new unsigned char[w * h];
868 		int x = 0;
869 
870 		for (int i = 0; i < h; ++i) {
871 			for (int j = 0; j < w; ++j) {
872 				data[x] = pixels[(i * GraphicHeight / h) * Surface->pitch + j * GraphicWidth / w];
873 				++x;
874 			}
875 		}
876 
877 		SDL_UnlockSurface(Surface);
878 		VideoPaletteListRemove(Surface);
879 
880 		memcpy(pal, Surface->format->palette->colors, sizeof(SDL_Color) * 256);
881 		SDL_FreeSurface(Surface);
882 
883 		Surface = SDL_CreateRGBSurfaceFrom(data, w, h, 8, w, 0, 0, 0, 0);
884 		if (Surface->format->BytesPerPixel == 1) {
885 			VideoPaletteListAdd(Surface);
886 		}
887 		SDL_SetPaletteColors(Surface->format->palette, pal, 0, 256);
888 	} else {
889 		SDL_LockSurface(Surface);
890 
891 		unsigned char *pixels = (unsigned char *)Surface->pixels;
892 		unsigned char *data = new unsigned char[w * h * bpp];
893 		int x = 0;
894 
895 		for (int i = 0; i < h; ++i) {
896 			float fy = (float)i * GraphicHeight / h;
897 			int iy = (int)fy;
898 			fy -= iy;
899 			for (int j = 0; j < w; ++j) {
900 				float fx = (float)j * GraphicWidth / w;
901 				int ix = (int)fx;
902 				fx -= ix;
903 				float fz = (fx + fy) / 2;
904 
905 				unsigned char *p1 = &pixels[iy * Surface->pitch + ix * bpp];
906 				unsigned char *p2 = (iy != Surface->h - 1) ?
907 									&pixels[(iy + 1) * Surface->pitch + ix * bpp] :
908 									p1;
909 				unsigned char *p3 = (ix != Surface->w - 1) ?
910 									&pixels[iy * Surface->pitch + (ix + 1) * bpp] :
911 									p1;
912 				unsigned char *p4 = (iy != Surface->h - 1 && ix != Surface->w - 1) ?
913 									&pixels[(iy + 1) * Surface->pitch + (ix + 1) * bpp] :
914 									p1;
915 
916 				data[x * bpp + 0] = static_cast<unsigned char>(
917 										(p1[0] * (1 - fy) + p2[0] * fy +
918 										 p1[0] * (1 - fx) + p3[0] * fx +
919 										 p1[0] * (1 - fz) + p4[0] * fz) / 3.0 + .5);
920 				data[x * bpp + 1] = static_cast<unsigned char>(
921 										(p1[1] * (1 - fy) + p2[1] * fy +
922 										 p1[1] * (1 - fx) + p3[1] * fx +
923 										 p1[1] * (1 - fz) + p4[1] * fz) / 3.0 + .5);
924 				data[x * bpp + 2] = static_cast<unsigned char>(
925 										(p1[2] * (1 - fy) + p2[2] * fy +
926 										 p1[2] * (1 - fx) + p3[2] * fx +
927 										 p1[2] * (1 - fz) + p4[2] * fz) / 3.0 + .5);
928 				if (bpp == 4) {
929 					data[x * bpp + 3] = static_cast<unsigned char>(
930 											(p1[3] * (1 - fy) + p2[3] * fy +
931 											 p1[3] * (1 - fx) + p3[3] * fx +
932 											 p1[3] * (1 - fz) + p4[3] * fz) / 3.0 + .5);
933 				}
934 				++x;
935 			}
936 		}
937 
938 		int Rmask = Surface->format->Rmask;
939 		int Gmask = Surface->format->Gmask;
940 		int Bmask = Surface->format->Bmask;
941 		int Amask = Surface->format->Amask;
942 
943 		SDL_UnlockSurface(Surface);
944 		VideoPaletteListRemove(Surface);
945 		SDL_FreeSurface(Surface);
946 
947 		Surface = SDL_CreateRGBSurfaceFrom(data, w, h, 8 * bpp, w * bpp,
948 										   Rmask, Gmask, Bmask, Amask);
949 	}
950 	if (useckey) {
951 		SDL_SetColorKey(Surface, SDL_TRUE, ckey);
952 	}
953 
954 	Height = h / (GraphicHeight / Height);
955 	Width = w / (GraphicWidth / Width);
956 	GraphicWidth = w;
957 	GraphicHeight = h;
958 	Assert(GraphicWidth / Width * GraphicHeight / Height == NumFrames);
959 
960 	GenFramesMap();
961 }
962 
963 /**
964 **  Sets the original size for a graphic
965 **
966 */
SetOriginalSize()967 void CGraphic::SetOriginalSize()
968 {
969 	Assert(Surface); // can't resize before it's been loaded
970 
971 	if (!Resized) {
972 		return;
973 	}
974 
975 
976 	if (Surface) {
977 		FreeSurface(&Surface);
978 		Surface = NULL;
979 	}
980 	delete[] frame_map;
981 	frame_map = NULL;
982 	if (SurfaceFlip) {
983 		FreeSurface(&SurfaceFlip);
984 		SurfaceFlip = NULL;
985 	}
986 	delete[] frameFlip_map;
987 	frameFlip_map = NULL;
988 
989 	this->Width = this->Height = 0;
990 	this->Surface = NULL;
991 	this->Load();
992 
993 	Resized = false;
994 }
995 
996 /**
997 **  Check if a pixel is transparent
998 **
999 **  @param x  X coordinate
1000 **  @param y  Y coordinate
1001 **
1002 **  @return   True if the pixel is transparent, False otherwise
1003 */
TransparentPixel(int x,int y)1004 bool CGraphic::TransparentPixel(int x, int y)
1005 {
1006 	int bpp = Surface->format->BytesPerPixel;
1007 	Uint32 colorkey;
1008 	bool has_colorkey = !SDL_GetColorKey(Surface, &colorkey);
1009 	if ((bpp == 1 && !has_colorkey) || bpp == 3) {
1010 		return false;
1011 	}
1012 
1013 	bool ret = false;
1014 	SDL_LockSurface(Surface);
1015 	unsigned char *p = (unsigned char *)Surface->pixels + y * Surface->pitch + x * bpp;
1016 	if (bpp == 1) {
1017 		if (*p == colorkey) {
1018 			ret = true;
1019 		}
1020 	} else {
1021 		bool ckey = has_colorkey;
1022 		if (ckey && *p == colorkey) {
1023 			ret = true;
1024 		} else if (p[Surface->format->Ashift >> 3] == 255) {
1025 			ret = true;
1026 		}
1027 	}
1028 	SDL_UnlockSurface(Surface);
1029 
1030 	return ret;
1031 }
1032 
1033 /**
1034 ** Change a palette color.
1035 */
SetPaletteColor(int idx,int r,int g,int b)1036 void CGraphic::SetPaletteColor(int idx, int r, int g, int b) {
1037 	if (!Surface) {
1038 		return;
1039 	}
1040 	SDL_Color color;
1041 	color.r = r;
1042 	color.g = g;
1043 	color.b = b;
1044 	SDL_SetPaletteColors(Surface->format->palette, &color, idx, 1);
1045 }
1046 
dither(SDL_Surface * Surface)1047 static inline void dither(SDL_Surface *Surface) {
1048 	for (int x = 0; x < Surface->w; x++) {
1049 		for (int y = 0; y < Surface->h; y++) {
1050 			Uint8* pixel = ((Uint8*)(Surface->pixels)) + x + y * Surface->pitch;
1051 			if (*pixel != 255) {
1052 				*pixel = (y % 2 == x % 2) ? 255 : 0;
1053 			}
1054 		}
1055 	}
1056 }
1057 
1058 /**
1059 **  Make shadow sprite
1060 **
1061 **  @todo FIXME: 32bpp
1062 */
MakeShadow()1063 void CGraphic::MakeShadow()
1064 {
1065 	if (Surface->format->BytesPerPixel == 1) {
1066 		SDL_Color colors[256];
1067 
1068 		// Set all colors in the palette to black and use dithering for alpha
1069 		memset(colors, 0, sizeof(colors));
1070 		SDL_SetPaletteColors(Surface->format->palette, colors, 0, 256);
1071 		dither(Surface);
1072 
1073 		if (SurfaceFlip) {
1074 			SDL_SetPaletteColors(SurfaceFlip->format->palette, colors, 0, 256);
1075 			dither(Surface);
1076 		}
1077 	}
1078 }
1079 
FreeGraphics()1080 void FreeGraphics()
1081 {
1082 	std::map<std::string, CGraphic *>::iterator i;
1083 	while (!GraphicHash.empty()) {
1084 		i = GraphicHash.begin();
1085 		CGraphic::Free((*i).second);
1086 	}
1087 }
1088 
~bits_map()1089 CFiller::bits_map::~bits_map()
1090 {
1091 	if (bstore) {
1092 		free(bstore);
1093 		bstore = NULL;
1094 	}
1095 	Width = 0;
1096 	Height = 0;
1097 }
1098 
Init(CGraphic * g)1099 void CFiller::bits_map::Init(CGraphic *g)
1100 {
1101 	SDL_Surface *s = g->Surface;
1102 	int bpp = s->format->BytesPerPixel;
1103 	unsigned int ckey;
1104 
1105 	if (bstore) {
1106 		free(bstore);
1107 		bstore = NULL;
1108 		Width = 0;
1109 		Height = 0;
1110 	}
1111 
1112 	if ((bpp == 1 && SDL_GetColorKey(s, &ckey) != 0) || bpp == 3) {
1113 		return;
1114 	}
1115 
1116 	Width = g->Width;
1117 	Height = g->Height;
1118 
1119 	size_t line = (Width + (sizeof(int) * 8) - 1) / (sizeof(int) * 8);
1120 	size_t size = line * Height;
1121 
1122 	bstore = (unsigned int *)calloc(size, sizeof(unsigned int));
1123 
1124 	SDL_LockSurface(s);
1125 
1126 	switch (s->format->BytesPerPixel) {
1127 		case 1: {
1128 			unsigned char *ptr = (unsigned char *)s->pixels;
1129 
1130 			for (int i = 0; i < Height; ++i) {
1131 				int l = i * Width;
1132 				int lm = i * line;
1133 				int k = 0;
1134 				int p = 0;
1135 				for (int j = 0; j < Width; ++j) {
1136 					bstore[lm + k] |= ((ptr[j + l] != ckey) ? (1 << p) : 0);
1137 					if (++p > 31) {
1138 						p = 0;
1139 						k++;
1140 					}
1141 				}
1142 			}
1143 			break;
1144 		}
1145 		case 2:
1146 		case 3:
1147 			break;
1148 		case 4:
1149 		{
1150 			if (!SDL_GetColorKey(s, &ckey)) {
1151 				unsigned int *ptr = (unsigned int *)s->pixels;
1152 
1153 				for (int i = 0; i < Height; ++i) {
1154 					int l = i * Width;
1155 					int lm = i * line;
1156 					int k = 0;
1157 					int p = 0;
1158 					for (int j = 0; j < Width; ++j) {
1159 						bstore[lm + k] |= ((ptr[j + l] != ckey) ? (1 << p) : 0);
1160 						if (++p > 31) {
1161 							p = 0;
1162 							k++;
1163 						}
1164 					}
1165 				}
1166 			} else {
1167 				unsigned int *ptr = (unsigned int *)s->pixels;
1168 
1169 				for (int i = 0; i < Height; ++i) {
1170 					int l = i * Width;
1171 					int lm = i * line;
1172 					int k = 0;
1173 					int p = 0;
1174 					for (int j = 0; j < Width; ++j) {
1175 						bstore[lm + k] |= ((ptr[j + l] & AMASK) ? (1 << p) : 0);
1176 						if (++p > 31) {
1177 							p = 0;
1178 							k++;
1179 						}
1180 					}
1181 				}
1182 			}
1183 			break;
1184 		}
1185 		default:
1186 			break;
1187 	}
1188 
1189 	SDL_UnlockSurface(s);
1190 }
1191 
Load()1192 void CFiller::Load()
1193 {
1194 	if (G) {
1195 		G->Load();
1196 		map.Init(G);
1197 	}
1198 }
1199 
1200 //@}
1201