Atrinik Client 2.5
client/tilestretcher.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00045 #include <global.h>
00046 
00079 static int std_tile_half_len[] =
00080 {
00081     0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,
00082     10, 10, 11, 11, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5,
00083     5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0
00084 };
00085 
00087 typedef struct line_and_slope
00088 {
00090     int sx;
00091 
00093     int sy;
00094 
00096     int end_x;
00097 
00099     int end_y;
00100 
00105     float slope;
00106 } line_and_slope;
00107 
00117 static void determine_line(line_and_slope *dest, int sx, int sy, int ex, int ey)
00118 {
00119     float y_diff, x_diff, slope;
00120 
00121     if (sy > ey)
00122     {
00123         y_diff = sy - ey;
00124     }
00125     else
00126     {
00127         y_diff = ey - sy;
00128     }
00129 
00130     if (sx > ex)
00131     {
00132         x_diff = sx - ex;
00133     }
00134     else
00135     {
00136         x_diff = ex - sx;
00137     }
00138 
00139     if (x_diff == 0)
00140     {
00141         slope = 0.0;
00142     }
00143     else
00144     {
00145         slope = y_diff / x_diff;
00146     }
00147 
00148     dest->sx = sx;
00149     dest->sy = sy;
00150     dest->end_x = ex;
00151     dest->end_y = ey;
00152     dest->slope = slope;
00153 }
00154 
00157 int add_color_to_surface(SDL_Surface *dest, Uint8 red, Uint8 green, Uint8 blue)
00158 {
00159     int i;
00160     Uint8 ncol = dest->format->palette->ncolors;
00161     SDL_Color colors[256];
00162 
00163     for (i = 0; i < ncol; i++)
00164     {
00165         colors[i].r = dest->format->palette->colors[i].r;
00166         colors[i].g = dest->format->palette->colors[i].g;
00167         colors[i].b = dest->format->palette->colors[i].b;
00168     }
00169 
00170     colors[ncol].r = red;
00171     colors[ncol].g = green;
00172     colors[ncol].b = blue;
00173     ncol++;
00174 
00175     SDL_SetColors(dest, colors, 0, ncol);
00176     dest->format->palette->ncolors = ncol;
00177 
00178     return 0;
00179 }
00180 
00193 int copy_pixel_to_pixel(SDL_Surface *src, SDL_Surface *dest, int x, int y, int x2, int y2, float brightness)
00194 {
00195     Uint32 color;
00196     Uint8 red, green, blue, alpha, alpha_2;
00197     Uint8 red_2, green_2, blue_2;
00198     Uint16 n;
00199 
00200     if (x < 0 || y < 0 || x2 < 0 || y2 < 0)
00201     {
00202         return 0;
00203     }
00204 
00205     if (x >= src->w || x2 >= dest->w || y >= src->h || y2 >= dest->h)
00206     {
00207         return 0;
00208     }
00209 
00210     color = getpixel(src, x, y);
00211 
00212     /* No need to copy transparent pixels */
00213     if (src->format->BitsPerPixel == 8 && (color == src->format->colorkey))
00214     {
00215         return 0;
00216     }
00217 
00218     SDL_GetRGBA(color, src->format, &red, &green, &blue, &alpha);
00219 
00220     if (alpha == 0)
00221     {
00222         return 0;
00223     }
00224 
00225     /* We must clamp to 255 since it is allowable for brightness to exceed 1.0 */
00226     n = (Uint16) red * brightness;
00227     red = (n <= 255) ? n : 255;
00228     n = (Uint16) green * brightness;
00229     green = (n <= 255) ? n : 255;
00230     n = (Uint16) blue * brightness;
00231     blue = (n <= 255) ? n : 255;
00232 
00233     color = SDL_MapRGBA(dest->format, red, green, blue, alpha);
00234 
00235     if (color == dest->format->colorkey)
00236     {
00237         blue += 256 >> (8 - dest->format->Bloss);
00238         color = SDL_MapRGBA(dest->format, red, green, blue, alpha);
00239     }
00240 
00241     if (dest->format->BitsPerPixel == 8)
00242     {
00243         SDL_GetRGBA(color, dest->format, &red_2, &green_2, &blue_2, &alpha_2);
00244 
00245         if (red != red_2 || green != green_2 || blue != blue_2 || alpha != alpha_2)
00246         {
00247             add_color_to_surface(dest, red, green, blue);
00248             color = SDL_MapRGBA(dest->format, red, green, blue, alpha);
00249         }
00250     }
00251 
00252     putpixel(dest, x2, y2, color);
00253     return 0;
00254 }
00255 
00277 int 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, float brightness, int extra)
00278 {
00279     int src_h, dest_h, y;
00280     float ratio;
00281 
00282     SDL_LockSurface(src);
00283     SDL_LockSurface(dest);
00284 
00285     if (src_sy > src_ey)
00286     {
00287         int tmp = src_sy;
00288 
00289         src_sy = src_ey;
00290         src_ey = tmp;
00291     }
00292 
00293     if (dest_sy > dest_ey)
00294     {
00295         int tmp = dest_sy;
00296 
00297         dest_sy = dest_ey;
00298         dest_ey = tmp;
00299     }
00300 
00301     src_h = src_ey - src_sy;
00302     dest_h = dest_ey - dest_sy;
00303 
00304     /* Special cases */
00305     if (dest_h == 0)
00306     {
00307         if (src_h == 0)
00308         {
00309             copy_pixel_to_pixel(src, dest, src_x, src_sy, dest_x, dest_sy, brightness);
00310 
00311             SDL_UnlockSurface(src);
00312             SDL_UnlockSurface(dest);
00313             return 0;
00314         }
00315         else
00316         {
00317             copy_pixel_to_pixel(src, dest, src_x, (src_ey - src_sy) / 2, dest_x, dest_sy, brightness);
00318 
00319             SDL_UnlockSurface(src);
00320             SDL_UnlockSurface(dest);
00321             return 0;
00322         }
00323     }
00324 
00325     if (src_h == 0)
00326     {
00327         Uint32 color = getpixel(src, src_x, src_sy);
00328 
00329         for (y = dest_sy; y <= dest_ey; y++)
00330         {
00331             putpixel(dest, dest_x, y, color);
00332         }
00333 
00334         return 0;
00335     }
00336 
00337     /* The stretching */
00338     ratio = (float) src_h / (float) dest_h;
00339 
00340     for (y = 0; y <= dest_h; y++)
00341     {
00342         int go_y = dest_sy + y;
00343         int get_y = src_sy + (y * ratio);
00344 
00345         copy_pixel_to_pixel(src, dest, src_x, get_y, dest_x, go_y, brightness);
00346     }
00347 
00348     if (extra)
00349     {
00350         if (dest_ey + 1 < dest->h)
00351         {
00352             copy_pixel_to_pixel(src, dest, src_x, src_ey, dest_x, dest_ey + 1, brightness);
00353         }
00354     }
00355 
00356     SDL_UnlockSurface(src);
00357     SDL_UnlockSurface(dest);
00358     return 0;
00359 }
00360 
00387 SDL_Surface *tile_stretch(SDL_Surface *src, int n, int e, int s, int w)
00388 {
00389     SDL_Surface *destination, *tmp;
00390     float e_dark = 1.0, w_dark = 1.0;
00391     /* If set, copy_vertical_line will attempt to extend the line further
00392      * by 1 pixel (no idea why this is named "flat") */
00393     int flat;
00394     int sx, sy, ex, ey;
00395     int ln_num;
00396     int dest_sx, dest_sy, dest_ex, dest_ey;
00397     float dest_slope;
00398     int dest_sy_2, dest_ey_2;
00399     float dest_slope_2;
00400     int dest_x_inc, dest_y_inc;
00401     float kicker, kicker_2;
00402     int dest_y_inc_2;
00403     int x, y, y2;
00404     int at_least_one;
00405     int src_len;
00406     Uint32 color;
00407     Uint8 red, green, blue, alpha;
00408     /* Only index numbers 0-3 are actually used */
00409     line_and_slope dest_lines[10];
00410 
00411     /* Initialisation and housekeeping */
00412     SDL_LockSurface(src);
00413 
00414     tmp = SDL_CreateRGBSurface(src->flags, src->w, src->h + n, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
00415 
00416     destination = SDL_DisplayFormatAlpha(tmp);
00417     SDL_FreeSurface(tmp);
00418     SDL_LockSurface(destination);
00419 
00420     color = getpixel(src, 0, 0);
00421 
00422     SDL_GetRGBA(color, src->format, &red, &green, &blue, &alpha);
00423 
00424     if (src->format->BitsPerPixel == 8)
00425     {
00426         add_color_to_surface(destination, red, green, blue);
00427     }
00428 
00429     /* We fill with black and full trans... */
00430     color = SDL_MapRGBA(destination->format, 0, 0, 0, 0);
00431     SDL_FillRect( destination, NULL, color);
00432 
00433     if (src->format->BitsPerPixel == 8)
00434     {
00435         SDL_SetColorKey(destination, SDL_SRCCOLORKEY, color);
00436     }
00437 
00438     /* If the target is the same size we don't want copy_vertical_line()
00439      * to try to extent the line by 1 pixel */
00440     if (n == 0 && e == 0 && w == 0 && s == 0)
00441     {
00442         flat = 0;
00443     }
00444     else
00445     {
00446         flat = 1;
00447     }
00448 
00449     /* Calculate the darkness (contrast) of one or both sides */
00450     if (w > e)
00451     {
00452         w_dark = 1.0 - ((w - e) / 25.0);
00453 
00454         if (n > 0 || s > 0)
00455         {
00456             e_dark = w_dark;
00457         }
00458     }
00459 
00460     if (e > w)
00461     {
00462         e_dark = 1.0 + ((e - w) / 25.0);
00463 
00464         if (s > 0 || n > 0)
00465         {
00466             w_dark = e_dark;
00467         }
00468     }
00469 
00470     /* Calculate the information about the lines which will form the
00471      * edge of the stretched tile (see the comments above
00472      * std_tile_half_len for a picture).
00473      *
00474      * In the isometric view, closer objects are displayed lower down
00475      * on the VDU (smaller y co-ordinate) and further objects are
00476      * displayed higher up (larger y co-ordinate).
00477      *
00478      * The SW corner moves closer the more West the tile is stretched.
00479      * The NE corner moves closer the more East the tile is stretched.
00480      * Both the SW and NE corners move further away the more North the
00481      * tile is stretched.
00482      * Both the SE and NW corners move close the more South and further
00483      * away the more North the tile is stretched */
00484 
00485     /* 0: Southern edge: SW to SE corner */
00486     sx = 2;
00487     sy = (10 - w) + n;
00488     ex = 22;
00489     ey = 0;
00490     determine_line(&dest_lines[0], sx, sy, ex, ey);
00491     /* 1: Western edge: SW to NW corner */
00492     sx = 2;
00493     sy = (12 - w) + n;
00494     ex = 22;
00495     ey = 22 + n - s;
00496     determine_line(&dest_lines[1], sx, sy, ex, ey);
00497     /* 2: Eastern edge: NE to SE corner */
00498     sx = 45;
00499     sy = (10 - e) + n;
00500     ex = 25;
00501     ey = 0;
00502     determine_line(&dest_lines[2], sx, sy, ex, ey);
00503     /* 3: Northern edge: NE to NW corner */
00504     sx = 45;
00505     sy = (12 - e) + n;
00506     ex = 25;
00507     ey = 22 + n - s;
00508     determine_line(&dest_lines[3], sx, sy, ex, ey);
00509 
00510     /* loop information:
00511      * effective loop control:
00512      * for (ln_num = 0; ln_num < 4; ln_num += 2) */
00513     for (ln_num = 0; ln_num < 4; ln_num++)
00514     {
00515         /* see "effective loop control" */
00516         if (ln_num == 1 || ln_num == 3)
00517         {
00518             continue;
00519         }
00520 
00521         /* Extract the information for the first, i.e. bottom, line (S or E edge) */
00522         dest_sx = dest_lines[ln_num].sx;
00523         dest_sy = dest_lines[ln_num].sy;
00524         dest_ex = dest_lines[ln_num].end_x;
00525         dest_ey = dest_lines[ln_num].end_y;
00526         dest_slope = dest_lines[ln_num].slope;
00527 
00528         /* ln_num is always either 0 or 2 here */
00529         if (ln_num == 0 || ln_num == 2)
00530         {
00531             /* Extract the information for the second, i.e. top, line (W or N edge) */
00532             dest_sy_2 = dest_lines[ln_num + 1].sy;
00533             dest_ey_2 = dest_lines[ln_num + 1].end_y;
00534             dest_slope_2 = dest_lines[ln_num + 1].slope;
00535         }
00536         else
00537         {
00538             /* Dead code: information about the second line is the same as the first! */
00539             dest_sy_2 = dest_lines[ln_num].sy;
00540             dest_ey_2 = dest_lines[ln_num].end_y;
00541             dest_slope_2 = dest_lines[ln_num].slope;
00542         }
00543 
00544         /* Calculate the direction of the y co-ordinate */
00545         if (dest_sy > dest_ey)
00546         {
00547             dest_y_inc = -1;
00548         }
00549         else
00550         {
00551             dest_y_inc = 1;
00552         }
00553 
00554         /* Calculate the direction of the x co-ordinate */
00555         if (dest_sx > dest_ex)
00556         {
00557             dest_x_inc = -1;
00558         }
00559         else
00560         {
00561             dest_x_inc = 1;
00562         }
00563 
00564         /* Calculate the direction of the 2nd y co-ordinate */
00565         if (dest_sy_2 > dest_ey_2)
00566         {
00567             dest_y_inc_2 = -1;
00568         }
00569         else
00570         {
00571             dest_y_inc_2 = 1;
00572         }
00573 
00574         /* Initialise loop controls: "kicker" means the co-ordinate
00575          * crosses the line (another weird name) */
00576         x = dest_sx;
00577         y = dest_sy;
00578         kicker = 0.0;
00579         y2 = dest_sy_2;
00580         kicker_2 = 0.0;
00581         /* Make sure at least one row of pixels is output (who chose that name?) */
00582         at_least_one = 0;
00583 
00584         /* Main inner loop to draw each vertical line in the stretched bitmap
00585          * loop information:
00586          *
00587          * horizontal (or vertical) edges are drawn with only one line of pixels (at_least_one)
00588          *
00589          * effective loop control when non-horizontal:
00590          * for (x1 = dest_sx; x1 != dest_ex; x1 += dest_x_inc) */
00591         while (((dest_slope != 0.0) && (x != dest_ex) && (y != dest_ey)) || ((at_least_one == 0) && (dest_slope == 0.0)))
00592         {
00593             /* Exit the loop after the first iteration if the line is exactly horizontal (or vertical) */
00594             at_least_one = 1;
00595 
00596             if (kicker >= 1.0)
00597             {
00598                 kicker = kicker - 1.0;
00599                 y = y + dest_y_inc;
00600             }
00601 
00602             if (kicker_2 >= 1.0)
00603             {
00604                 kicker_2 = kicker_2 - 1.0;
00605                 y2 = y2 + dest_y_inc_2;
00606             }
00607 
00608             /* Choose y co-ordinates either side of the central horizontal */
00609             src_len = std_tile_half_len[x];
00610 
00611             if (ln_num < 2)
00612             {
00613                 copy_vertical_line(src, destination, x, 11 + src_len, 11 - src_len, x, y, y2, w_dark, flat);
00614             }
00615             else
00616             {
00617                 copy_vertical_line(src, destination, x, 11 + src_len, 11 - src_len, x, y, y2, e_dark, flat);
00618             }
00619 
00620             x = x + dest_x_inc;
00621 
00622             kicker = kicker + dest_slope;
00623             kicker_2 = kicker_2 + dest_slope_2;
00624         }
00625     }
00626 
00627     for (x = 22; x < 22 + 2; x++)
00628     {
00629         copy_vertical_line(src, destination, x, 0, 23, x, 0, 23 + n - s, w_dark, flat);
00630     }
00631 
00632     for (x = 24; x < 24 + 2; x++)
00633     {
00634         copy_vertical_line(src, destination, x, 0, 23, x, 0, 23 + n - s, e_dark, flat);
00635     }
00636 
00637     for (x = 0; x < 2; x++)
00638     {
00639         copy_pixel_to_pixel(src,destination, x, 11, x, 11 + n - w, w_dark);
00640     }
00641 
00642     for (x = 46; x < 48; x++)
00643     {
00644         copy_pixel_to_pixel(src, destination, x, 11, x, 11 + n - e, e_dark);
00645     }
00646 
00647     SDL_UnlockSurface(src);
00648     SDL_UnlockSurface(destination);
00649     return destination;
00650 }