Atrinik Client  4.0
sprite.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
30 #include <global.h>
31 #include <toolkit/string.h>
32 #include <toolkit/colorspace.h>
33 
38 typedef struct sprite_cache {
39  char *name;
40  SDL_Surface *surface;
41  time_t last_used;
42  UT_hash_handle hh;
44 
46 SDL_Surface *FormatHolder;
47 
49 static int dark_alpha[DARK_LEVELS] = {
50  0, 44, 80, 117, 153, 190, 226
51 };
52 
57 
61 void
63 {
64  FormatHolder = SDL_CreateRGBSurface(SDL_SRCALPHA,
65  1,
66  1,
67  32,
68  0xFF000000,
69  0x00FF0000,
70  0x0000FF00,
71  0x000000FF);
72  SDL_SetAlpha(FormatHolder, SDL_SRCALPHA, 255);
73 }
74 
86 sprite_load_file (char *fname, uint32_t flags)
87 {
88  sprite_struct *sprite = sprite_tryload_file(fname, flags, NULL);
89  if (sprite == NULL) {
90  LOG(ERROR, "Can't load sprite %s", fname);
91  return NULL;
92  }
93 
94  return sprite;
95 }
96 
110 sprite_tryload_file (char *fname, uint32_t flag, SDL_RWops *rwop)
111 {
112  SDL_Surface *bitmap;
113  if (fname != NULL) {
114  bitmap = IMG_Load_wrapper(fname);
115  if (bitmap == NULL) {
116  return NULL;
117  }
118  } else {
119  bitmap = IMG_LoadPNG_RW(rwop);
120  }
121 
122  sprite_struct *sprite = ecalloc(1, sizeof(*sprite));
123  if (sprite == NULL) {
124  return NULL;
125  }
126 
127  uint32_t ckflags = SDL_SRCCOLORKEY | SDL_ANYFORMAT | SDL_RLEACCEL;
128  uint32_t ckey = 0;
129 
130  if (bitmap->format->palette) {
131  ckey = bitmap->format->colorkey;
132  SDL_SetColorKey(bitmap, ckflags, ckey);
133  } else if (flag & SURFACE_FLAG_COLKEY_16M) {
134  /* Force a true color png to colorkey. Default ckey is black (0). */
135  SDL_SetColorKey(bitmap, ckflags, 0);
136  }
137 
138  surface_borders_get(bitmap,
139  &sprite->border_up,
140  &sprite->border_down,
141  &sprite->border_left,
142  &sprite->border_right,
143  ckey);
144  sprite->bitmap = bitmap;
145 
146  if (flag & SURFACE_FLAG_DISPLAYFORMATALPHA) {
147  sprite->bitmap = SDL_DisplayFormatAlpha(bitmap);
148  SDL_FreeSurface(bitmap);
149  } else if (flag & SURFACE_FLAG_DISPLAYFORMAT) {
150  sprite->bitmap = SDL_DisplayFormat(bitmap);
151  SDL_FreeSurface(bitmap);
152  }
153 
154  return sprite;
155 }
156 
163 void
165 {
166  if (sprite == NULL) {
167  return;
168  }
169 
170  if (sprite->bitmap != NULL) {
171  SDL_FreeSurface(sprite->bitmap);
172  }
173 
174  efree(sprite);
175 }
176 
185 static sprite_cache_t *
186 sprite_cache_find (const char *name)
187 {
188  HARD_ASSERT(name != NULL);
189 
190  sprite_cache_t *cache;
191  HASH_FIND_STR(sprites_cache, name, cache);
192 
193  if (cache != NULL) {
194  cache->last_used = time(NULL);
195  }
196 
197  return cache;
198 }
199 
208 static sprite_cache_t *
209 sprite_cache_create (const char *name)
210 {
211  HARD_ASSERT(name != NULL);
212 
213  sprite_cache_t *cache = ecalloc(1, sizeof(*cache));
214  cache->name = estrdup(name);
215  cache->last_used = time(NULL);
216  return cache;
217 }
218 
225 static void
227 {
228  HARD_ASSERT(cache != NULL);
229  HASH_ADD_KEYPTR(hh, sprites_cache, cache->name, strlen(cache->name), cache);
230 }
231 
238 static void
240 {
241  HARD_ASSERT(cache != NULL);
242  HASH_DEL(sprites_cache, cache);
243 }
244 
249 static void
251 {
252  HARD_ASSERT(cache != NULL);
253 
254  efree(cache->name);
255  SDL_FreeSurface(cache->surface);
256  efree(cache);
257 }
258 
263 {
264  sprite_cache_t *cache, *tmp;
265  HASH_ITER(hh, sprites_cache, cache, tmp) {
266  sprite_cache_remove(cache);
267  sprite_cache_free(cache);
268  }
269 }
270 
274 void sprite_cache_gc(void)
275 {
276  if (!rndm_chance(SPRITE_CACHE_GC_CHANCE)) {
277  return;
278  }
279 
280  time_t now = time(NULL);
281 
282  struct timeval tv1;
283  gettimeofday(&tv1, NULL);
284 
285  sprite_cache_t *cache, *tmp;
286  HASH_ITER(hh, sprites_cache, cache, tmp) {
287  if (now - cache->last_used >= SPRITE_CACHE_GC_FREE_TIME) {
288  sprite_cache_remove(cache);
289  sprite_cache_free(cache);
290  }
291 
292  /* Avoid executing this loop for too long. */
293  struct timeval tv2;
294  if (gettimeofday(&tv2, NULL) == 0 &&
295  tv2.tv_usec - tv1.tv_usec >= SPRITE_CACHE_GC_MAX_TIME) {
296  break;
297  }
298  }
299 }
300 
311 static SDL_Surface *
312 sprite_effect_red (SDL_Surface *surface)
313 {
314  SDL_Surface *tmp = SDL_ConvertSurface(surface,
315  FormatHolder->format,
316  FormatHolder->flags);
317  if (tmp == NULL) {
318  return NULL;
319  }
320 
321  for (int y = 0; y < tmp->h; y++) {
322  for (int x = 0; x < tmp->w; x++) {
323  Uint8 r, g, b, a;
324  SDL_GetRGBA(getpixel(tmp, x, y), tmp->format, &r, &g, &b, &a);
325  r = (Uint8) (0.212671 * r + 0.715160 * g + 0.072169 * b);
326  g = b = 0;
327  putpixel(tmp, x, y, SDL_MapRGBA(tmp->format, r, g, b, a));
328  }
329  }
330 
331  SDL_Surface *ret = SDL_DisplayFormatAlpha(tmp);
332  SDL_FreeSurface(tmp);
333  return ret;
334 }
335 
346 static SDL_Surface *
347 sprite_effect_gray (SDL_Surface *surface)
348 {
349  SDL_Surface *tmp = SDL_ConvertSurface(surface,
350  FormatHolder->format,
351  FormatHolder->flags);
352  if (tmp == NULL) {
353  return NULL;
354  }
355 
356  for (int y = 0; y < tmp->h; y++) {
357  for (int x = 0; x < tmp->w; x++) {
358  Uint8 r, g, b, a;
359  SDL_GetRGBA(getpixel(tmp, x, y), tmp->format, &r, &g, &b, &a);
360  r = g = b = (Uint8) (0.212671 * r + 0.715160 * g + 0.072169 * b);
361  putpixel(tmp, x, y, SDL_MapRGBA(tmp->format, r, g, b, a));
362  }
363  }
364 
365  SDL_Surface *ret = SDL_DisplayFormatAlpha(tmp);
366  SDL_FreeSurface(tmp);
367  return ret;
368 }
369 
380 static SDL_Surface *
381 sprite_effect_fow (SDL_Surface *surface)
382 {
383  SDL_Surface *tmp = SDL_ConvertSurface(surface,
384  FormatHolder->format,
385  FormatHolder->flags);
386  if (tmp == NULL) {
387  return NULL;
388  }
389 
390  for (int y = 0; y < tmp->h; y++) {
391  for (int x = 0; x < tmp->w; x++) {
392  Uint8 r, g, b, a;
393  SDL_GetRGBA(getpixel(tmp, x, y), tmp->format, &r, &g, &b, &a);
394  r = (Uint8) ((0.212671 * r + 0.715160 * g + 0.072169 * b) * 0.34);
395  g = b = r;
396  b += 16;
397  putpixel(tmp, x, y, SDL_MapRGBA(tmp->format, r, g, b, a));
398  }
399  }
400 
401  SDL_Surface *ret = SDL_DisplayFormatAlpha(tmp);
402  SDL_FreeSurface(tmp);
403  return ret;
404 }
405 
420 static SDL_Surface *
421 sprite_effect_glow (SDL_Surface *surface,
422  const SDL_Color *color,
423  double speed,
424  double state)
425 {
426  SDL_Surface *tmp = SDL_CreateRGBSurface(surface->flags,
427  surface->w + SPRITE_GLOW_SIZE * 2,
428  surface->h + SPRITE_GLOW_SIZE * 2,
429  surface->format->BitsPerPixel,
430  surface->format->Rmask,
431  surface->format->Gmask,
432  surface->format->Bmask,
433  surface->format->Amask);
434  if (tmp == NULL) {
435  return NULL;
436  }
437 
438 #define GLOW_GRID_PIXEL_NONE 0
439 #define GLOW_GRID_PIXEL_VISIBLE 1
440 #define GLOW_GRID_PIXEL_GLOW 2
441 #define GLOW_GRID_PIXEL_OUTLINE 3
442 
443  /* Create a 2D grid representation of the sprite's pixel surface for
444  * storing information about the processing state, such as which
445  * coordinates contain visible pixels. */
446  uint8_t *grid = ecalloc(1, sizeof(*grid) * tmp->w * tmp->h);
447 
448  for (int x = 0; x < surface->w; x++) {
449  for (int y = 0; y < surface->h; y++) {
450  Uint32 pixel = getpixel(surface, x, y);
451  if (pixel == surface->format->colorkey) {
452  /* Transparent pixel. */
453  continue;
454  }
455 
456  putpixel(tmp, x + SPRITE_GLOW_SIZE, y + SPRITE_GLOW_SIZE, pixel);
457 
458  Uint8 r, g, b, a;
459  SDL_GetRGBA(pixel, surface->format, &r, &g, &b, &a);
460  if (a < 127) {
461  /* Avoid outlining pixels with low alpha values, such as
462  * shadows or already existing glow effects. */
463  continue;
464  }
465 
466  int idx = tmp->w * (y + SPRITE_GLOW_SIZE) + (x + SPRITE_GLOW_SIZE);
467  grid[idx] = GLOW_GRID_PIXEL_VISIBLE;
468  }
469  }
470 
471  /* Figure out the alpha value based on the animation speed and the current
472  * animation state for a fade-out/pulsing effect. */
473  speed = MAX(1.0, speed);
474  state = MAX(1.0, state);
475  double mod = (speed - state - speed / 2.0) / (speed / 2.0);
476  Uint8 alpha = 200.0 * fabs(mod);
477 
478  /* It's much easier to work in HSV for this. */
479  double rgb[3], hsv[3];
480  rgb[0] = color->r / 255.0;
481  rgb[1] = color->g / 255.0;
482  rgb[2] = color->b / 255.0;
483  colorspace_rgb2hsv(rgb, hsv);
484 
485  /* Create some random variations of the specified color. */
486  Uint32 pixels[10];
487  for (size_t i = 0; i < arraysize(pixels); i++) {
488  double hsv2[3], rgb2[3];
489  memcpy(&hsv2, hsv, sizeof(hsv2));
490  hsv2[1] += (10 - rndm(1, 20)) * 0.01;
491  hsv2[2] += (10 - rndm(1, 20)) * 0.01;
492  hsv2[1] = MIN(1.0, MAX(0.0, hsv2[1]));
493  hsv2[2] = MIN(1.0, MAX(0.0, hsv2[2]));
494  colorspace_hsv2rgb(hsv2, rgb2);
495 
496  pixels[i] = SDL_MapRGBA(tmp->format,
497  rgb2[0] * 255.0,
498  rgb2[1] * 255.0,
499  rgb2[2] * 255.0,
500  alpha);
501  }
502 
503  hsv[1] += 0.10;
504  hsv[1] = MIN(1.0, hsv[1]);
505  hsv[2] -= 0.25;
506  hsv[2] = MAX(0.0, hsv[2]);
507  colorspace_hsv2rgb(hsv, rgb);
508 
509  /* Acquire the color to use for the glow's outline. */
510  Uint32 edge_color = SDL_MapRGBA(tmp->format,
511  rgb[0] * 255.0,
512  rgb[1] * 255.0,
513  rgb[2] * 255.0,
514  MAX(0, alpha - 25));
515 
516  /* Iterate the pixels in the sprite's surface. */
517  for (int x = 0; x < tmp->w; x++) {
518  for (int y = 0; y < tmp->h; y++) {
519  if (grid[tmp->w * y + x] != GLOW_GRID_PIXEL_VISIBLE) {
520  /* Transparent pixel, or an already processed one. */
521  continue;
522  }
523 
524  /* Scan adjacent pixels to see if there's a visible pixel. */
525  bool has_neighbors = false;
526  for (int dx = -1; dx <= 1 && !has_neighbors; dx++) {
527  for (int dy = -1; dy <= 1; dy++) {
528  if (dx == 0 && dy == 0) {
529  /* Skip self */
530  continue;
531  }
532 
533  int tx = x + dx;
534  int ty = y + dy;
535  if (tx < 0 || tx >= tmp->w || ty < 0 || ty >= tmp->h) {
536  continue;
537  }
538 
539  if (grid[tmp->w * ty + tx] == GLOW_GRID_PIXEL_VISIBLE) {
540  has_neighbors = true;
541  break;
542  }
543  }
544  }
545 
546  if (!has_neighbors) {
547  /* No visible neighboring pixels, move on. */
548  continue;
549  }
550 
551  /* Add glow pixels where applicable. */
552  for (int off = 1; off <= 2; off++) {
553  for (int dx = -1; dx <= 1; dx++) {
554  for (int dy = -1; dy <= 1; dy++) {
555  int tx = x + dx * off;
556  int ty = y + dy * off;
557  if (tx < 0 || tx >= tmp->w || ty < 0 || ty >= tmp->h) {
558  continue;
559  }
560 
561  uint8_t *point = &grid[tmp->w * ty + tx];
562  /* Only adjust pixels that don't have a visible pixel,
563  * or if they have been added a glow outline before. */
564  if (*point == GLOW_GRID_PIXEL_NONE ||
565  *point == GLOW_GRID_PIXEL_OUTLINE) {
566  Uint32 pixel;
567  if (off == 1) {
568  /* Glow pixel processing. */
569  pixel = pixels[rndm(0, arraysize(pixels) - 1)];
570  *point = GLOW_GRID_PIXEL_GLOW;
571  } else {
572  /* Glow outline pixel processing. */
573  pixel = edge_color;
574  *point = GLOW_GRID_PIXEL_OUTLINE;
575  }
576 
577  putpixel(tmp, tx, ty, pixel);
578  }
579  }
580  }
581  }
582  }
583  }
584 
585  efree(grid);
586 
587  SDL_Surface *ret = SDL_DisplayFormatAlpha(tmp);
588  SDL_FreeSurface(tmp);
589  return ret;
590 
591 #undef GLOW_GRID_PIXEL_NONE
592 #undef GLOW_GRID_PIXEL_VISIBLE
593 #undef GLOW_GRID_PIXEL_GLOW
594 #undef GLOW_GRID_PIXEL_OUTLINE
595 }
596 
608 static SDL_Surface *
609 sprite_effects_create (SDL_Surface *surface, const sprite_effects_t *effects)
610 {
611 #define FREE_TMP_SURFACE() \
612 do { \
613  if (tmp != NULL) { \
614  SDL_FreeSurface(tmp); \
615  } \
616  tmp = surface; \
617 } while (0)
618 
619  SDL_Surface *tmp = NULL;
620 
621  if (BIT_QUERY(effects->flags, SPRITE_FLAG_EFFECTS)) {
622  surface = effect_sprite_overlay(surface);
623  if (surface == NULL) {
624  goto done;
625  }
626 
627  FREE_TMP_SURFACE();
628  }
629 
630  if (BIT_QUERY(effects->flags, SPRITE_FLAG_DARK)) {
631  surface = SDL_DisplayFormatAlpha(surface);
632  if (surface == NULL) {
633  goto done;
634  }
635 
636  char buf[MAX_BUF];
637  snprintf(VS(buf),
638  "rectangle:500,500,%d",
639  dark_alpha[effects->dark_level]);
640  SDL_BlitSurface(texture_surface(texture_get(TEXTURE_TYPE_SOFTWARE,
641  buf)),
642  NULL,
643  surface,
644  NULL);
645  FREE_TMP_SURFACE();
646  } else if (BIT_QUERY(effects->flags, SPRITE_FLAG_FOW)) {
647  surface = sprite_effect_fow(surface);
648  if (surface == NULL) {
649  goto done;
650  }
651 
652  FREE_TMP_SURFACE();
653  } else if (BIT_QUERY(effects->flags, SPRITE_FLAG_RED)) {
654  surface = sprite_effect_red(surface);
655  if (surface == NULL) {
656  goto done;
657  }
658 
659  FREE_TMP_SURFACE();
660  } else if (BIT_QUERY(effects->flags, SPRITE_FLAG_GRAY)) {
661  surface = sprite_effect_gray(surface);
662  if (surface == NULL) {
663  goto done;
664  }
665 
666  FREE_TMP_SURFACE();
667  }
668 
669  /* Apply tile-stretching. */
670  if (effects->stretch != 0) {
671  Sint8 n = (effects->stretch >> 24) & 0xFF;
672  Sint8 e = (effects->stretch >> 16) & 0xFF;
673  Sint8 w = (effects->stretch >> 8) & 0xFF;
674  Sint8 s = effects->stretch & 0xFF;
675 
676  surface = tile_stretch(surface, n, e, s, w);
677  if (surface == NULL) {
678  goto done;
679  }
680 
681  FREE_TMP_SURFACE();
682  }
683 
684  /* Apply zoom and/or rotate effects. */
685  if ((effects->zoom_x != 0 && effects->zoom_x != 100) ||
686  (effects->zoom_y != 0 && effects->zoom_y != 100) ||
687  effects->rotate != 0) {
688  bool smooth;
689  /* Figure out whether to use smoothing. */
690  if (effects->rotate == 0 &&
691  (effects->zoom_x == 0 || abs(effects->zoom_x) == 100) &&
692  (effects->zoom_y == 0 || abs(effects->zoom_y) == 100)) {
693  smooth = false;
694  } else {
696  }
697 
698  double zoom_x = effects->zoom_x != 0 ? effects->zoom_x / 100.0 : 1.0;
699  double zoom_y = effects->zoom_y != 0 ? effects->zoom_y / 100.0 : 1.0;
700  surface = rotozoomSurfaceXY(surface,
701  effects->rotate,
702  zoom_x,
703  zoom_y,
704  smooth);
705  if (surface == NULL) {
706  goto done;
707  }
708 
709  FREE_TMP_SURFACE();
710  }
711 
712  /* Apply glow effects. */
713  if (effects->glow[0] != '\0') {
714  SDL_Color color;
715  if (text_color_parse(effects->glow, &color)) {
716  surface = sprite_effect_glow(surface,
717  &color,
718  effects->glow_speed,
719  effects->glow_state);
720  if (surface == NULL) {
721  goto done;
722  }
723 
724  FREE_TMP_SURFACE();
725  }
726  }
727 
728  /* Alpha transparency. */
729  if (effects->alpha != 0) {
730  surface = SDL_DisplayFormatAlpha(surface);
731  if (surface == NULL) {
732  goto done;
733  }
734 
735  surface_set_alpha(surface, effects->alpha);
736  FREE_TMP_SURFACE();
737  }
738 
739  SOFT_ASSERT_RC(tmp != NULL, NULL, "Generated NULL surface!");
740 
741 done:
742  return tmp;
743 #undef FREE_TMP_SURFACE
744 }
745 
760 void
761 surface_show (SDL_Surface *surface,
762  int x,
763  int y,
764  SDL_Rect *srcrect,
765  SDL_Surface *src)
766 {
767  SDL_Rect dstrect;
768  dstrect.x = x;
769  dstrect.y = y;
770  SDL_BlitSurface(src, srcrect, surface, &dstrect);
771 }
772 
791 void
792 surface_show_fill (SDL_Surface *surface,
793  int x,
794  int y,
795  SDL_Rect *srcsize,
796  SDL_Surface *src,
797  SDL_Rect *box)
798 {
799  int w = srcsize != NULL ? srcsize->w : src->w;
800  int h = srcsize != NULL ? srcsize->h : src->h;
801  for (int tx = 0; tx < box->w; tx += w) {
802  for (int ty = 0; ty < box->h; ty += h) {
803  SDL_Rect srcrect;
804  srcrect.x = srcsize ? MAX(0, srcsize->x) : 0;
805  srcrect.y = srcsize ? MAX(0, srcsize->y) : 0;
806  srcrect.w = MIN(w, box->w - tx);
807  srcrect.h = MIN(h, box->h - ty);
808  surface_show(surface, x + tx, y + ty, &srcrect, src);
809  }
810  }
811 }
812 
829 void
830 surface_show_effects (SDL_Surface *surface,
831  int x,
832  int y,
833  SDL_Rect *srcrect,
834  SDL_Surface *src,
835  const sprite_effects_t *effects)
836 {
837  HARD_ASSERT(surface != NULL);
838 
839  if (src == NULL) {
840  return;
841  }
842 
843  if (effects != NULL && SPRITE_EFFECTS_NEED_RENDERING(effects)) {
844  /* Maximum darkness; do not render at all. */
845  if (BIT_QUERY(effects->flags, SPRITE_FLAG_DARK) &&
846  effects->dark_level == DARK_LEVELS) {
847  return;
848  }
849 
850  /* Construct a cache entry string. */
851  char name[HUGE_BUF];
852  snprintf(VS(name),
853  "%p;%u;%u;%s;%u;%u;%d;%d;%d;%s;%u;%u",
854  src,
855  effects->flags,
856  effects->dark_level,
858  effects->alpha,
859  effects->stretch,
860  effects->zoom_x,
861  effects->zoom_y,
862  effects->rotate,
863  effects->glow,
864  effects->glow_speed,
865  effects->glow_state);
866 
867  /* Try to find the sprite we need in the cache, otherwise,
868  * render it out and add it to the cache. */
869  SDL_Surface *old_src = src;
870  sprite_cache_t *cache = sprite_cache_find(name);
871  if (cache != NULL) {
872  src = cache->surface;
873  } else {
874  SDL_Surface *tmp = sprite_effects_create(src, effects);
875  if (tmp != NULL) {
876  src = tmp;
877 
878  cache = sprite_cache_create(name);
879  cache->surface = src;
880  sprite_cache_add(cache);
881  }
882  }
883 
884  if (effects->stretch != 0) {
885  y -= src->h - old_src->h;
886  }
887 
888  if (effects->glow[0] != '\0') {
889  y -= SPRITE_GLOW_SIZE;
890  x -= SPRITE_GLOW_SIZE;
891  }
892  }
893 
894  surface_show(surface, x, y, srcrect, src);
895 }
896 
909 Uint32
910 getpixel (SDL_Surface *surface, int x, int y)
911 {
912  int bpp = surface->format->BytesPerPixel;
913  /* The address to the pixel we want to retrieve. */
914  Uint8 *p = (Uint8 *) surface->pixels + y * surface->pitch + x * bpp;
915 
916  switch (bpp) {
917  case 1:
918  return *p;
919 
920  case 2:
921  return *(Uint16 *) p;
922 
923  case 3:
924  if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
925  return p[0] << 16 | p[1] << 8 | p[2];
926  } else {
927  return p[0] | p[1] << 8 | p[2] << 16;
928  }
929 
930  case 4:
931  return *(Uint32 *) p;
932  }
933 
934  return 0;
935 }
936 
949 void
950 putpixel (SDL_Surface *surface, int x, int y, Uint32 pixel)
951 {
952  int bpp = surface->format->BytesPerPixel;
953  /* The address to the pixel we want to set. */
954  Uint8 *p = (Uint8 *) surface->pixels + y * surface->pitch + x * bpp;
955 
956  switch (bpp) {
957  case 1:
958  *p = pixel;
959  break;
960 
961  case 2:
962  *(Uint16 *) p = pixel;
963  break;
964 
965  case 3:
966  if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
967  p[0] = (pixel >> 16) & 0xff;
968  p[1] = (pixel >> 8) & 0xff;
969  p[2] = pixel & 0xff;
970  } else {
971  p[0] = pixel & 0xff;
972  p[1] = (pixel >> 8) & 0xff;
973  p[2] = (pixel >> 16) & 0xff;
974  }
975 
976  break;
977 
978  case 4:
979  *(Uint32 *) p = pixel;
980  break;
981  }
982 }
983 
997 static bool
998 surface_border_get_left (SDL_Surface *surface, int *pos, uint32_t color)
999 {
1000  for (int x = 0; x < surface->w; x++) {
1001  for (int y = 0; y < surface->h; y++) {
1002  if (getpixel(surface, x, y) != color) {
1003  *pos = x;
1004  return true;
1005  }
1006  }
1007  }
1008 
1009  return false;
1010 }
1011 
1025 static bool
1026 surface_border_get_right(SDL_Surface *surface, int *pos, uint32_t color)
1027 {
1028  for (int x = surface->w - 1; x >= 0; x--) {
1029  for (int y = 0; y < surface->h; y++) {
1030  if (getpixel(surface, x, y) != color) {
1031  *pos = (surface->w - 1) - x;
1032  return true;
1033  }
1034  }
1035  }
1036 
1037  return false;
1038 }
1039 
1053 static bool
1054 surface_border_get_top (SDL_Surface *surface, int *pos, uint32_t color)
1055 {
1056  for (int y = 0; y < surface->h; y++) {
1057  for (int x = 0; x < surface->w; x++) {
1058  if (getpixel(surface, x, y) != color) {
1059  *pos = y;
1060  return true;
1061  }
1062  }
1063  }
1064 
1065  return false;
1066 }
1067 
1081 static bool
1082 surface_border_get_bottom(SDL_Surface *surface, int *pos, uint32_t color)
1083 {
1084  for (int y = surface->h - 1; y >= 0; y--) {
1085  for (int x = 0; x < surface->w; x++) {
1086  if (getpixel(surface, x, y) != color) {
1087  *pos = (surface->h - 1) - y;
1088  return true;
1089  }
1090  }
1091  }
1092 
1093  return false;
1094 }
1095 
1116 int
1117 surface_borders_get (SDL_Surface *surface,
1118  int *top,
1119  int *bottom,
1120  int *left,
1121  int *right,
1122  uint32_t color)
1123 {
1124  *top = 0;
1125  *bottom = 0;
1126  *left = 0;
1127  *right = 0;
1128 
1129  /* If the border was not found, it means the surface is completely
1130  * filled with 'color' color. */
1131  if (!surface_border_get_top(surface, top, color)) {
1132  return 0;
1133  }
1134 
1135  surface_border_get_bottom(surface, bottom, color);
1136  surface_border_get_left(surface, left, color);
1137  surface_border_get_right(surface, right, color);
1138 
1139  return 1;
1140 }
1141 
1150 void
1151 surface_pan (SDL_Surface *surface, SDL_Rect *box)
1152 {
1153  if (box->x >= surface->w - box->w) {
1154  box->x = (Sint16) (surface->w - box->w);
1155  }
1156 
1157  if (box->x < 0) {
1158  box->x = 0;
1159  }
1160 
1161  if (box->y >= surface->h - box->h) {
1162  box->y = (Sint16) (surface->h - box->h);
1163  }
1164 
1165  if (box->y < 0) {
1166  box->y = 0;
1167  }
1168 }
1169 
1184 void
1185 draw_frame (SDL_Surface *surface, int x, int y, int w, int h)
1186 {
1187  SDL_Rect box;
1188 
1189  box.x = x;
1190  box.y = y;
1191  box.h = h;
1192  box.w = 1;
1193  SDL_FillRect(surface, &box, SDL_MapRGB(surface->format, 0x60, 0x60, 0x60));
1194  box.x = x + w;
1195  box.h++;
1196  SDL_FillRect(surface, &box, SDL_MapRGB(surface->format, 0x55, 0x55, 0x55));
1197  box.x = x;
1198  box.y += h;
1199  box.w = w;
1200  box.h = 1;
1201  SDL_FillRect(surface, &box, SDL_MapRGB(surface->format, 0x60, 0x60, 0x60));
1202  box.x++;
1203  box.y = y;
1204  SDL_FillRect(surface, &box, SDL_MapRGB(surface->format, 0x55, 0x55, 0x55));
1205 }
1206 
1225 void
1226 border_create (SDL_Surface *surface,
1227  int x,
1228  int y,
1229  int w,
1230  int h,
1231  int color,
1232  int size)
1233 {
1234  SDL_Rect box;
1235 
1236  /* Left border. */
1237  box.x = x;
1238  box.y = y;
1239  box.h = h;
1240  box.w = size;
1241  SDL_FillRect(surface, &box, color);
1242 
1243  /* Right border. */
1244  box.x = x + w - size;
1245  SDL_FillRect(surface, &box, color);
1246 
1247  /* Top border. */
1248  box.x = x + size;
1249  box.y = y;
1250  box.w = w - size * 2;
1251  box.h = size;
1252  SDL_FillRect(surface, &box, color);
1253 
1254  /* Bottom border. */
1255  box.y = y + h - size;
1256  SDL_FillRect(surface, &box, color);
1257 }
1258 
1276 void
1277 border_create_line (SDL_Surface *surface,
1278  int x,
1279  int y,
1280  int w,
1281  int h,
1282  uint32_t color)
1283 {
1284  SDL_Rect dst;
1285 
1286  dst.x = x;
1287  dst.y = y;
1288  dst.w = w;
1289  dst.h = h;
1290  SDL_FillRect(surface, &dst, color);
1291 }
1292 
1305 void
1306 border_create_sdl_color (SDL_Surface *surface,
1307  SDL_Rect *coords,
1308  int thickness,
1309  SDL_Color *color)
1310 {
1311  uint32_t color_mapped = SDL_MapRGB(surface->format,
1312  color->r,
1313  color->g,
1314  color->b);
1315 
1316  BORDER_CREATE_TOP(surface,
1317  coords->x,
1318  coords->y,
1319  coords->w,
1320  coords->h,
1321  color_mapped,
1322  thickness);
1323  BORDER_CREATE_BOTTOM(surface,
1324  coords->x,
1325  coords->y,
1326  coords->w,
1327  coords->h,
1328  color_mapped,
1329  thickness);
1330  BORDER_CREATE_LEFT(surface,
1331  coords->x,
1332  coords->y,
1333  coords->w,
1334  coords->h,
1335  color_mapped,
1336  thickness);
1337  BORDER_CREATE_RIGHT(surface,
1338  coords->x,
1339  coords->y,
1340  coords->w,
1341  coords->h,
1342  color_mapped,
1343  thickness);
1344 }
1345 
1358 void
1359 border_create_color (SDL_Surface *surface,
1360  SDL_Rect *coords,
1361  int thickness,
1362  const char *color_notation)
1363 {
1364  SDL_Color color;
1365  if (!text_color_parse(color_notation, &color)) {
1366  LOG(ERROR, "Invalid color: %s", color_notation);
1367  return;
1368  }
1369 
1370  border_create_sdl_color(surface, coords, thickness, &color);
1371 }
1372 
1387 void
1388 border_create_texture (SDL_Surface *surface,
1389  SDL_Rect *coords,
1390  int thickness,
1391  SDL_Surface *texture)
1392 {
1393  SDL_Rect box;
1394 
1395  box.w = coords->w;
1396  box.h = thickness;
1397  surface_show_fill(surface,
1398  coords->x,
1399  coords->y,
1400  NULL,
1401  texture,
1402  &box);
1403  surface_show_fill(surface,
1404  coords->x,
1405  coords->y + coords->h - thickness,
1406  NULL,
1407  texture,
1408  &box);
1409 
1410  box.w = thickness;
1411  box.h = coords->h;
1412  surface_show_fill(surface,
1413  coords->x,
1414  coords->y,
1415  NULL,
1416  texture,
1417  &box);
1418  surface_show_fill(surface,
1419  coords->x + coords->w - thickness,
1420  coords->y,
1421  NULL,
1422  texture,
1423  &box);
1424 }
1425 
1442 void
1443 rectangle_create (SDL_Surface *surface,
1444  int x,
1445  int y,
1446  int w,
1447  int h,
1448  const char *color_notation)
1449 {
1450  SDL_Color color;
1451  if (!text_color_parse(color_notation, &color)) {
1452  LOG(BUG, "Invalid color: %s", color_notation);
1453  return;
1454  }
1455 
1456  border_create_line(surface,
1457  x,
1458  y,
1459  w,
1460  h,
1461  SDL_MapRGB(surface->format, color.r, color.g, color.b));
1462 }
1463 
1475 void
1476 surface_set_alpha (SDL_Surface *surface, uint8_t alpha)
1477 {
1478  SDL_PixelFormat *fmt = surface->format;
1479 
1480  if (fmt->Amask == 0) {
1481  SDL_SetAlpha(surface, SDL_SRCALPHA, alpha);
1482  } else {
1483  Uint8 bpp = fmt->BytesPerPixel;
1484  double scale = alpha / 255.0f;
1485 
1486  SDL_LockSurface(surface);
1487 
1488  for (int y = 0; y < surface->h; y++) {
1489  for (int x = 0; x < surface->w; x++) {
1490  Uint8 r, g, b, a;
1491  Uint32 *pixel_ptr = (Uint32 *) ((Uint8 *) surface->pixels + y *
1492  surface->pitch + x * bpp);
1493  SDL_GetRGBA(*pixel_ptr, fmt, &r, &g, &b, &a);
1494  *pixel_ptr = SDL_MapRGBA(fmt, r, g, b, scale * a);
1495  }
1496 
1497  }
1498 
1499  SDL_UnlockSurface(surface);
1500  }
1501 }
1502 
1522 int
1524  double y,
1525  double corners_x[],
1526  double corners_y[],
1527  int corners_num)
1528 {
1529  int j = corners_num - 1;
1530  int odd_nodes = 0;
1531 
1532  for (int i = 0; i < corners_num; i++) {
1533  if (((corners_y[i] < y && corners_y[j] >= y) ||
1534  (corners_y[j] < y && corners_y[i] >= y)) &&
1535  (corners_x[i] <= x || corners_x[j] <= x)) {
1536  odd_nodes ^= (corners_x[i] + (y - corners_y[i]) /
1537  (corners_y[j] - corners_y[i]) *
1538  (corners_x[j] - corners_x[i]) < x);
1539  }
1540 
1541  j = i;
1542  }
1543 
1544  return odd_nodes;
1545 }
#define SPRITE_FLAG_FOW
Definition: sprite.h:72
static int dark_alpha[DARK_LEVELS]
Definition: sprite.c:49
void surface_show_effects(SDL_Surface *surface, int x, int y, SDL_Rect *srcrect, SDL_Surface *src, const sprite_effects_t *effects)
Definition: sprite.c:830
int text_color_parse(const char *color_notation, SDL_Color *color)
Definition: text.c:603
void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
Definition: sprite.c:950
void rectangle_create(SDL_Surface *surface, int x, int y, int w, int h, const char *color_notation)
Definition: sprite.c:1443
struct sprite_cache sprite_cache_t
static SDL_Surface * sprite_effect_gray(SDL_Surface *surface)
Definition: sprite.c:347
#define SURFACE_FLAG_COLKEY_16M
Definition: main.h:341
uint8_t alpha
Alpha value.
Definition: sprite.h:48
SDL_Surface * bitmap
Definition: sprite.h:96
static SDL_Surface * sprite_effect_glow(SDL_Surface *surface, const SDL_Color *color, double speed, double state)
Definition: sprite.c:421
SDL_Surface * texture_surface(texture_struct *texture)
Definition: texture.c:303
int16_t rotate
Rotate value.
Definition: sprite.h:52
int16_t zoom_x
Horizontal zoom.
Definition: sprite.h:50
#define SPRITE_FLAG_DARK
Definition: sprite.h:70
uint32_t flags
Bit combination of Sprite drawing flags.
Definition: sprite.h:46
UT_hash_handle hh
Hash handle.
Definition: sprite.c:42
Uint32 getpixel(SDL_Surface *surface, int x, int y)
Definition: sprite.c:910
void draw_frame(SDL_Surface *surface, int x, int y, int w, int h)
Definition: sprite.c:1185
SDL_Surface * FormatHolder
Definition: sprite.c:46
int border_left
Definition: sprite.h:90
static SDL_Surface * sprite_effects_create(SDL_Surface *surface, const sprite_effects_t *effects)
Definition: sprite.c:609
int border_down
Definition: sprite.h:87
static bool surface_border_get_bottom(SDL_Surface *surface, int *pos, uint32_t color)
Definition: sprite.c:1082
static sprite_cache_t * sprites_cache
Definition: sprite.c:56
SDL_Surface * tile_stretch(SDL_Surface *src, int n, int e, int s, int w)
static bool surface_border_get_right(SDL_Surface *surface, int *pos, uint32_t color)
Definition: sprite.c:1026
int polygon_check_coords(double x, double y, double corners_x[], double corners_y[], int corners_num)
Definition: sprite.c:1523
texture_struct * texture_get(texture_type_t type, const char *name)
Definition: texture.c:279
const char * effect_overlay_identifier(void)
Definition: effects.c:718
uint32_t stretch
Tile stretching value.
Definition: sprite.h:49
int border_right
Definition: sprite.h:93
#define SPRITE_GLOW_SIZE
Definition: sprite.h:40
static SDL_Surface * sprite_effect_fow(SDL_Surface *surface)
Definition: sprite.c:381
#define SPRITE_FLAG_RED
Definition: sprite.h:74
void sprite_init_system(void)
Definition: sprite.c:62
static bool surface_border_get_left(SDL_Surface *surface, int *pos, uint32_t color)
Definition: sprite.c:998
SDL_Surface * IMG_Load_wrapper(const char *file)
Definition: wrapper.c:524
sprite_struct * sprite_tryload_file(char *fname, uint32_t flag, SDL_RWops *rwop)
Definition: sprite.c:110
int64_t setting_get_int(int cat, int setting)
Definition: settings.c:414
sprite_struct * sprite_load_file(char *fname, uint32_t flags)
Definition: sprite.c:86
int surface_borders_get(SDL_Surface *surface, int *top, int *bottom, int *left, int *right, uint32_t color)
Definition: sprite.c:1117
int border_up
Definition: sprite.h:84
static void sprite_cache_add(sprite_cache_t *cache)
Definition: sprite.c:226
uint8_t dark_level
Dark level.
Definition: sprite.h:47
static void sprite_cache_remove(sprite_cache_t *cache)
Definition: sprite.c:239
#define SPRITE_FLAG_EFFECTS
Definition: sprite.h:78
static bool surface_border_get_top(SDL_Surface *surface, int *pos, uint32_t color)
Definition: sprite.c:1054
void border_create_line(SDL_Surface *surface, int x, int y, int w, int h, uint32_t color)
Definition: sprite.c:1277
static void sprite_cache_free(sprite_cache_t *cache)
Definition: sprite.c:250
char * name
Name of the sprite. Used for hash table lookups.
Definition: sprite.c:39
static SDL_Surface * sprite_effect_red(SDL_Surface *surface)
Definition: sprite.c:312
void border_create(SDL_Surface *surface, int x, int y, int w, int h, int color, int size)
Definition: sprite.c:1226
static sprite_cache_t * sprite_cache_find(const char *name)
Definition: sprite.c:186
SDL_Surface * effect_sprite_overlay(SDL_Surface *surface)
Definition: effects.c:734
void sprite_cache_free_all(void)
Definition: sprite.c:262
void border_create_texture(SDL_Surface *surface, SDL_Rect *coords, int thickness, SDL_Surface *texture)
Definition: sprite.c:1388
void surface_show_fill(SDL_Surface *surface, int x, int y, SDL_Rect *srcsize, SDL_Surface *src, SDL_Rect *box)
Definition: sprite.c:792
#define SPRITE_FLAG_GRAY
Definition: sprite.h:76
#define DARK_LEVELS
Definition: config.h:72
time_t last_used
Last time the sprite was used.
Definition: sprite.c:41
void surface_show(SDL_Surface *surface, int x, int y, SDL_Rect *srcrect, SDL_Surface *src)
Definition: sprite.c:761
SDL_Surface * surface
The sprite's surface.
Definition: sprite.c:40
int16_t zoom_y
Vertical zoom.
Definition: sprite.h:51
void border_create_color(SDL_Surface *surface, SDL_Rect *coords, int thickness, const char *color_notation)
Definition: sprite.c:1359
static sprite_cache_t * sprite_cache_create(const char *name)
Definition: sprite.c:209
void border_create_sdl_color(SDL_Surface *surface, SDL_Rect *coords, int thickness, SDL_Color *color)
Definition: sprite.c:1306
void surface_pan(SDL_Surface *surface, SDL_Rect *box)
Definition: sprite.c:1151
static effect_struct * effects
Definition: effects.c:36
void sprite_cache_gc(void)
Definition: sprite.c:274
void sprite_free_sprite(sprite_struct *sprite)
Definition: sprite.c:164
void surface_set_alpha(SDL_Surface *surface, uint8_t alpha)
Definition: sprite.c:1476