Atrinik Client  4.0
tilestretcher.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 
43 #include <global.h>
44 
77 static int std_tile_half_len[] = {
78  0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,
79  10, 10, 11, 11, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5,
80  5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0
81 };
82 
84 typedef struct line_and_slope {
86  int sx;
87 
89  int sy;
90 
92  int end_x;
93 
95  int end_y;
96 
102  double slope;
104 
120 static void determine_line(line_and_slope *dest, int sx, int sy, int ex, int ey)
121 {
122  HARD_ASSERT(dest != NULL);
123 
124  dest->sx = sx;
125  dest->sy = sy;
126  dest->end_x = ex;
127  dest->end_y = ey;
128  int x_diff = abs(sx - ex);
129  int y_diff = abs(sy - ey);
130  dest->slope = x_diff == 0 ? 0 : (double) y_diff / (double) x_diff;
131 }
132 
149 static void determine_lines(line_and_slope *dest, int n, int e, int s, int w)
150 {
151  HARD_ASSERT(dest != NULL);
152 
153  /* 0: Western edge: SW to NW corner */
154  determine_line(&dest[0], 2, 10 - w + n, 22, 0);
155  /* 1: Southern edge: SW to SE corner */
156  determine_line(&dest[1], 2, 12 - w + n, 22, 22 + n - s);
157  /* 2: Northern edge: NE to NW corner */
158  determine_line(&dest[2], 45, 10 - e + n, 25, 0);
159  /* 3: Eastern edge: NE to SE corner */
160  determine_line(&dest[3], 45, 12 - e + n, 25, 22 + n - s);
161 }
162 
177 int tilestretcher_coords_in_tile(uint32_t stretch, int x, int y)
178 {
179  int8_t n = (int8_t) ((stretch >> 24) & 0xff);
180  int8_t e = (int8_t) ((stretch >> 16) & 0xff);
181  int8_t w = (int8_t) ((stretch >> 8) & 0xff);
182  int8_t s = (int8_t) (stretch & 0xff);
183 
184  line_and_slope lines[4];
185  determine_lines(lines, n, e, s, w);
186 
187  /* The following creates an array of points for coords_in_polygon().
188  * Basically, adjusts the coordinates returned by determine_lines() so that
189  * the isometric map tile shape would be entirely surrounded. */
190  double corners_x[8], corners_y[8];
191  corners_x[0] = lines[0].end_x - 1;
192  corners_y[0] = lines[0].end_y - 1;
193  corners_x[1] = lines[2].end_x + 1;
194  corners_y[1] = lines[2].end_y - 1;
195  corners_x[2] = lines[2].sx + 3;
196  corners_y[2] = lines[2].sy - 1;
197  corners_x[3] = lines[3].sx + 3;
198  corners_y[3] = lines[3].sy + 1;
199  corners_x[4] = lines[3].end_x + 1;
200  corners_y[4] = lines[3].end_y + 2;
201  corners_x[5] = lines[1].end_x - 1;
202  corners_y[5] = lines[1].end_y + 2;
203  corners_x[6] = lines[1].sx - 3;
204  corners_y[6] = lines[1].sy + 1;
205  corners_x[7] = lines[0].sx - 3;
206  corners_y[7] = lines[0].sy - 1;
207 
208  return polygon_check_coords(x, y, corners_x, corners_y,
209  arraysize(corners_x));
210 }
211 
215 int add_color_to_surface(SDL_Surface *dest, Uint8 red, Uint8 green, Uint8 blue)
216 {
217  HARD_ASSERT(dest != NULL);
218 
219  SDL_Color colors[256];
220  int ncol = dest->format->palette->ncolors;
221 
222  for (int i = 0; i < ncol; i++) {
223  colors[i].r = dest->format->palette->colors[i].r;
224  colors[i].g = dest->format->palette->colors[i].g;
225  colors[i].b = dest->format->palette->colors[i].b;
226  }
227 
228  colors[ncol].r = red;
229  colors[ncol].g = green;
230  colors[ncol].b = blue;
231  ncol++;
232 
233  SDL_SetColors(dest, colors, 0, ncol);
234  dest->format->palette->ncolors = ncol;
235 
236  return 0;
237 }
238 
253 void copy_pixel_to_pixel(SDL_Surface *src, SDL_Surface *dest, int x, int y,
254  int x2, int y2, double brightness)
255 {
256  HARD_ASSERT(src != NULL);
257  HARD_ASSERT(dest != NULL);
258 
259  if (x < 0 || y < 0 || x2 < 0 || y2 < 0 || x >= src->w || x2 >= dest->w ||
260  y >= src->h || y2 >= dest->h) {
261  return;
262  }
263 
264  Uint32 color = getpixel(src, x, y);
265 
266  /* No need to copy transparent pixels */
267  if (src->format->BitsPerPixel == 8 && (color == src->format->colorkey)) {
268  return;
269  }
270 
271  Uint8 red, green, blue, alpha;
272  SDL_GetRGBA(color, src->format, &red, &green, &blue, &alpha);
273  if (alpha == 0) {
274  return;
275  }
276 
277  /* We must clamp to 255 since it is allowable for brightness to exceed
278  * 1.0 */
279  Uint16 n;
280  n = red * brightness;
281  red = MIN(255, n);
282  n = green * brightness;
283  green = MIN(255, n);
284  n = blue * brightness;
285  blue = MIN(255, n);
286 
287  color = SDL_MapRGBA(dest->format, red, green, blue, alpha);
288 
289  if (color == dest->format->colorkey) {
290  blue += 256 >> (8 - dest->format->Bloss);
291  color = SDL_MapRGBA(dest->format, red, green, blue, alpha);
292  }
293 
294  if (dest->format->BitsPerPixel == 8) {
295  Uint8 red_2, green_2, blue_2, alpha_2;
296  SDL_GetRGBA(color, dest->format, &red_2, &green_2, &blue_2, &alpha_2);
297 
298  if (red != red_2 || green != green_2 || blue != blue_2 ||
299  alpha != alpha_2) {
300  add_color_to_surface(dest, red, green, blue);
301  color = SDL_MapRGBA(dest->format, red, green, blue, alpha);
302  }
303  }
304 
305  putpixel(dest, x2, y2, color);
306 }
307 
330 void copy_vertical_line(SDL_Surface *src, SDL_Surface *dest, int src_x,
331  int src_sy, int src_ey, int dest_x, int dest_sy, int dest_ey,
332  double brightness, bool extra)
333 {
334  HARD_ASSERT(src != NULL);
335  HARD_ASSERT(dest != NULL);
336 
337  SDL_LockSurface(src);
338  SDL_LockSurface(dest);
339 
340  int min_src_y = MIN(src_sy, src_ey);
341  int max_src_y = MAX(src_sy, src_ey);
342  int min_dest_y = MIN(dest_sy, dest_ey);
343  int max_dest_y = MAX(dest_sy, dest_ey);
344  int src_h = max_src_y - min_src_y;
345  int dest_h = max_dest_y - min_dest_y;
346 
347  if (dest_h == 0) {
348  int src_y = src_h == 0 ? min_src_y : (max_src_y - min_src_y) / 2;
349  copy_pixel_to_pixel(src, dest, src_x, src_y, dest_x, min_dest_y,
350  brightness);
351  } else if (src_h == 0) {
352  Uint32 color = getpixel(src, src_x, min_src_y);
353  for (int y = min_dest_y; y <= max_dest_y; y++) {
354  putpixel(dest, dest_x, y, color);
355  }
356  } else {
357  /* The stretching */
358  double ratio = (double) src_h / (double) dest_h;
359 
360  for (int y = 0; y <= dest_h; y++) {
361  int go_y = min_dest_y + y;
362  int get_y = (int) (min_src_y + (y * ratio));
363  copy_pixel_to_pixel(src, dest, src_x, get_y, dest_x, go_y,
364  brightness);
365  }
366 
367  if (extra && max_dest_y + 1 < dest->h) {
368  copy_pixel_to_pixel(src, dest, src_x, src_ey, dest_x,
369  max_dest_y + 1, brightness);
370  }
371  }
372 
373  SDL_UnlockSurface(src);
374  SDL_UnlockSurface(dest);
375 }
376 
404 SDL_Surface *tile_stretch(SDL_Surface *src, int n, int e, int s, int w)
405 {
406  HARD_ASSERT(src != NULL);
407 
408  /* Initialization and housekeeping */
409  SDL_LockSurface(src);
410 
411  SDL_Surface *tmp = SDL_CreateRGBSurface(src->flags, src->w, src->h + n,
412  src->format->BitsPerPixel, src->format->Rmask,
413  src->format->Gmask, src->format->Bmask, src->format->Amask);
414 
415  SDL_Surface *destination = SDL_DisplayFormatAlpha(tmp);
416  SDL_FreeSurface(tmp);
417  SDL_LockSurface(destination);
418 
419  Uint32 color = getpixel(src, 0, 0);
420 
421  Uint8 red, green, blue, alpha;
422  SDL_GetRGBA(color, src->format, &red, &green, &blue, &alpha);
423 
424  if (src->format->BitsPerPixel == 8) {
425  add_color_to_surface(destination, red, green, blue);
426  }
427 
428  /* We fill with black and full transparency */
429  color = SDL_MapRGBA(destination->format, 0, 0, 0, 0);
430  SDL_FillRect( destination, NULL, color);
431 
432  if (src->format->BitsPerPixel == 8) {
433  SDL_SetColorKey(destination, SDL_SRCCOLORKEY, color);
434  }
435 
436  /* If the target is the same size we don't want copy_vertical_line()
437  * to try to extent the line by 1 pixel */
438  bool flat = n != 0 || e != 0 || w != 0 || s != 0;
439 
440  /* Calculate the darkness (contrast) of one or both sides */
441  double e_dark, w_dark;
442  if (w > e) {
443  w_dark = 1.0 - ((w - e) / 25.0);
444 
445  if (n > 0 || s > 0) {
446  e_dark = w_dark;
447  } else {
448  e_dark = 1.0;
449  }
450  } else if (e > w) {
451  e_dark = 1.0 + ((e - w) / 25.0);
452 
453  if (s > 0 || n > 0) {
454  w_dark = e_dark;
455  } else {
456  w_dark = 1.0;
457  }
458  } else {
459  e_dark = 1.0;
460  w_dark = 1.0;
461  }
462 
463  line_and_slope dest_lines[4];
464  determine_lines(dest_lines, n, e, s, w);
465 
466  for (int ln_num = 0; ln_num < 4; ln_num += 2) {
467  /* Extract the information for the first, i.e. bottom, line (S or E
468  * edge) */
469  int dest_sx = dest_lines[ln_num].sx;
470  int dest_sy = dest_lines[ln_num].sy;
471  int dest_ex = dest_lines[ln_num].end_x;
472  int dest_ey = dest_lines[ln_num].end_y;
473  double dest_slope = dest_lines[ln_num].slope;
474 
475  /* Extract the information for the second, i.e. top, line (W or N
476  * edge) */
477  int dest_sy_2 = dest_lines[ln_num + 1].sy;
478  int dest_ey_2 = dest_lines[ln_num + 1].end_y;
479  double dest_slope_2 = dest_lines[ln_num + 1].slope;
480 
481  /* Calculate the direction of the y co-ordinate */
482  int dest_y_inc = dest_sy > dest_ey ? -1 : 1;
483  /* Calculate the direction of the x co-ordinate */
484  int dest_x_inc = dest_sx > dest_ex ? -1 : 1;
485  /* Calculate the direction of the 2nd y co-ordinate */
486  int dest_y_inc_2 = dest_sy_2 > dest_ey_2 ? -1 : 1;
487 
488  /* Initialise loop controls: "kicker" means the co-ordinate
489  * crosses the line (another weird name) */
490  int x = dest_sx;
491  int y = dest_sy;
492  double kicker = 0.0;
493  double kicker_2 = 0.0;
494  int y2 = dest_sy_2;
495  /* Make sure at least one row of pixels is output */
496  bool at_least_one = false;
497 
498  /* Main inner loop to draw each vertical line in the stretched bitmap
499  * loop information:
500  *
501  * horizontal (or vertical) edges are drawn with only one line of pixels
502  * (at_least_one)
503  *
504  * effective loop control when non-horizontal:
505  * for (x1 = dest_sx; x1 != dest_ex; x1 += dest_x_inc) */
506  while (((!DBL_EQUAL(dest_slope, 0.0)) && (x != dest_ex) &&
507  (y != dest_ey)) || (!at_least_one &&
508  DBL_EQUAL(dest_slope, 0.0))) {
509  /* Exit the loop after the first iteration if the line is exactly
510  * horizontal (or vertical) */
511  at_least_one = true;
512 
513  if (kicker >= 1.0) {
514  kicker = kicker - 1.0;
515  y = y + dest_y_inc;
516  }
517 
518  if (kicker_2 >= 1.0) {
519  kicker_2 = kicker_2 - 1.0;
520  y2 = y2 + dest_y_inc_2;
521  }
522 
523  /* Choose y co-ordinates either side of the central horizontal */
524  int src_len = std_tile_half_len[x];
525  copy_vertical_line(src, destination, x, 11 + src_len, 11 - src_len,
526  x, y, y2, ln_num < 2 ? w_dark : e_dark, flat);
527 
528  x = x + dest_x_inc;
529 
530  kicker = kicker + dest_slope;
531  kicker_2 = kicker_2 + dest_slope_2;
532  }
533  }
534 
535  for (int x = 22; x < 22 + 2; x++) {
536  copy_vertical_line(src, destination, x, 0, 23, x, 0, 23 + n - s,
537  w_dark, flat);
538  }
539 
540  for (int x = 24; x < 24 + 2; x++) {
541  copy_vertical_line(src, destination, x, 0, 23, x, 0, 23 + n - s,
542  e_dark, flat);
543  }
544 
545  for (int x = 0; x < 2; x++) {
546  copy_pixel_to_pixel(src, destination, x, 11, x, 11 + n - w, w_dark);
547  }
548 
549  for (int x = 46; x < 48; x++) {
550  copy_pixel_to_pixel(src, destination, x, 11, x, 11 + n - e, e_dark);
551  }
552 
553  SDL_UnlockSurface(src);
554  SDL_UnlockSurface(destination);
555  return destination;
556 }
static void determine_lines(line_and_slope *dest, int n, int e, int s, int w)
void copy_pixel_to_pixel(SDL_Surface *src, SDL_Surface *dest, int x, int y, int x2, int y2, double brightness)
void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
Definition: sprite.c:950
Uint32 getpixel(SDL_Surface *surface, int x, int y)
Definition: sprite.c:910
SDL_Surface * tile_stretch(SDL_Surface *src, int n, int e, int s, int w)
int polygon_check_coords(double x, double y, double corners_x[], double corners_y[], int corners_num)
Definition: sprite.c:1523
static int std_tile_half_len[]
Definition: tilestretcher.c:77
int add_color_to_surface(SDL_Surface *dest, Uint8 red, Uint8 green, Uint8 blue)
void copy_vertical_line(SDL_Surface *src, SDL_Surface *dest, int src_x, int src_sy, int src_ey, int dest_x, int dest_sy, int dest_ey, double brightness, bool extra)
int tilestretcher_coords_in_tile(uint32_t stretch, int x, int y)
struct line_and_slope line_and_slope
static void determine_line(line_and_slope *dest, int sx, int sy, int ex, int ey)