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