/* vim:expandtab:ts=2 sw=2: */ /* Grafx2 - The Ultimate 256-color bitmap paint program Copyright 2011 Yves Rizoud Copyright 2010-2011 Adrien Destugues Grafx2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. Grafx2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Grafx2; if not, see */ #include "struct.h" #include "global.h" #include "graph.h" #include "sdlscreen.h" #include "engine.h" #include "windows.h" #include "input.h" #include "misc.h" #include "tiles.h" enum TILE_FLIPPED { TILE_FLIPPED_NONE = 0, TILE_FLIPPED_X = 1, TILE_FLIPPED_Y = 2, TILE_FLIPPED_XY = 3, // needs be TILE_FLIPPED_X|TILE_FLIPPED_Y }; // globals T_Tile * Main_tilemap; short Main_tilemap_width; short Main_tilemap_height; T_Tile * Spare_tilemap; short Spare_tilemap_width; short Spare_tilemap_height; // /// Build the tile-area from the current picture void build_tile_area() { word tileAreaWidth = Main_image_width / Snap_width + 1; word tileAreaHeight = Main_image_height / Snap_height + 1; int* tileArea = malloc(tileAreaWidth*tileAreaHeight*sizeof(int)); word tile_x, tile_y; // For each tile, we have to crawl up to the top of the picture and // find the first identical tile for (tile_y = 0; tile_y < tileAreaWidth; tile_y++) for(tile_x = 0; tile_x < tileAreaHeight; tile_x++) { // So, this is the "for each tile" short ctx, cty; // First we compare the tile with the others one in the same line at the left for(ctx = tile_x - 1; ctx >= 0; ctx--) if(compare_tiles(tile_x*Snap_width, tile_y*Snap_height, ctx*Snap_width, tile_y*Snap_height)) { // We found a match ! tileArea[tile_y*tileAreaWidth+tile_x] = tile_y*tileAreaWidth+ctx; goto found; } // Then we look at all the lines above for(cty = tile_y - 1; cty >= 0; cty--) for(ctx = tileAreaWidth - 1; ctx >= 0; ctx--) if(compare_tiles(tile_x*Snap_width, tile_y*Snap_height, ctx*Snap_width, cty*Snap_height)) { // We found a match ! tileArea[tile_y*tileAreaWidth+tile_x] = cty*tileAreaWidth+ctx; goto found; } // Then we look at all the lines below for(cty = tileAreaHeight - 1; cty >= tile_y; cty--) for(ctx = tileAreaWidth - 1; ctx >= 0; ctx--) if(compare_tiles(tile_x*Snap_width, tile_y*Snap_height, ctx*Snap_width, cty*Snap_height)) { // We found a match ! tileArea[tile_y*tileAreaWidth+tile_x] = cty*tileAreaWidth+ctx; goto found; } // Then we look at the tiles at the right of this one // (including the tile itself, so we are sure we match something this time) for(ctx = tileAreaHeight; ctx >= tile_x; ctx--) if(compare_tiles(tile_x*Snap_width, tile_y*Snap_height, ctx*Snap_width, tile_y*Snap_height)) { // We found a match ! tileArea[tile_y*tileAreaWidth+tile_x] = tile_y*tileAreaWidth+ctx; } found: ; } } // Compare tiles // The parameters are in pixel-space. int compare_tiles(word x1, word y1, word x2, word y2) { word pixel_x, pixel_y; byte c1, c2; for (pixel_y = 0; pixel_y < Snap_width; pixel_y++) for (pixel_x = 0; pixel_x < Snap_height; pixel_x++) { c1 = *(Main_screen + (y1+pixel_y) * Main_image_width + (x1+pixel_x)); c2 = *(Main_screen + (y2+pixel_y) * Main_image_width + (x2+pixel_x)); if (c1 != c2) return 0; } return 1; } /* /// Copy a tile pixeldata to all the identical ones // Call this after the end of an operation void update_tile(word pixel_x, word pixel_y) { int tileOffset = (pixel_y/Snap_height)*tileAreaHeight + pixel_x/Snap_width; int firstTileOffset = tileOffset + 1; // to make sure they are not equal while(firstTileOffset != tileOffset) { tileOffset = tileArea[tileOffset]; //do the copy of a block starting at (pixel_x, pixel_y) } } */ /* Basic repeat-all void Tilemap_draw(word x, word y, byte color) { int xx,yy; for (yy=(y+Snap_height-Snap_offset_Y)%Snap_height+Snap_offset_Y;yy=Limit_top&&yy<=Limit_bottom&&xx>=Limit_left&&xx<=Limit_right); Update_rect(0,0,0,0); } */ void Tilemap_draw(word x, word y, byte color) { int tile, first_tile; int rel_x, rel_y; if (x < Snap_offset_X || y < Snap_offset_Y || x >= Snap_offset_X + Main_tilemap_width*Snap_width || y >= Snap_offset_Y + Main_tilemap_height*Snap_height) return; tile = first_tile = TILE_FOR_COORDS(x,y); rel_x = (x - Snap_offset_X + Snap_width) % Snap_width; rel_y = (y - Snap_offset_Y + Snap_height) % Snap_height; do { int xx,yy; switch(Main_tilemap[tile].Flipped ^ Main_tilemap[first_tile].Flipped) { case 0: // no default: xx = (tile % Main_tilemap_width)*Snap_width+Snap_offset_X + rel_x; yy = (tile / Main_tilemap_width)*Snap_height+Snap_offset_Y + rel_y; break; case 1: // horizontal xx = (tile % Main_tilemap_width)*Snap_width+Snap_offset_X + Snap_width-rel_x-1; yy = (tile / Main_tilemap_width)*Snap_height+Snap_offset_Y + rel_y; break; case 2: // vertical xx = (tile % Main_tilemap_width)*Snap_width+Snap_offset_X + rel_x; yy = (tile / Main_tilemap_width)*Snap_height+Snap_offset_Y + Snap_height - rel_y - 1; break; case 3: // both xx = (tile % Main_tilemap_width)*Snap_width+Snap_offset_X + Snap_width-rel_x-1; yy = (tile / Main_tilemap_width)*Snap_height+Snap_offset_Y + Snap_height - rel_y - 1; break; } if (yy>=Limit_top&&yy<=Limit_bottom&&xx>=Limit_left&&xx<=Limit_right) Pixel_in_current_screen_with_preview(xx,yy,color); else Pixel_in_current_screen(xx,yy,color); tile = Main_tilemap[tile].Next; } while (tile != first_tile); Update_rect(0,0,0,0); } /// int Tile_is_same(int t1, int t2) { byte *bmp1,*bmp2; int y; bmp1 = Main_backups->Pages->Image[Main_current_layer].Pixels+(TILE_Y(t1))*Main_image_width+(TILE_X(t1)); bmp2 = Main_backups->Pages->Image[Main_current_layer].Pixels+(TILE_Y(t2))*Main_image_width+(TILE_X(t2)); for (y=0; y < Snap_height; y++) { if (memcmp(bmp1+y*Main_image_width, bmp2+y*Main_image_width, Snap_width)) return 0; } return 1; } /// int Tile_is_same_flipped_x(int t1, int t2) { byte *bmp1,*bmp2; int y, x; bmp1 = Main_backups->Pages->Image[Main_current_layer].Pixels+(TILE_Y(t1))*Main_image_width+(TILE_X(t1)); bmp2 = Main_backups->Pages->Image[Main_current_layer].Pixels+(TILE_Y(t2))*Main_image_width+(TILE_X(t2)+Snap_width-1); for (y=0; y < Snap_height; y++) { for (x=0; x < Snap_width; x++) if (*(bmp1+y*Main_image_width+x) != *(bmp2+y*Main_image_width-x)) return 0; } return 1; } /// int Tile_is_same_flipped_y(int t1, int t2) { byte *bmp1,*bmp2; int y; bmp1 = Main_backups->Pages->Image[Main_current_layer].Pixels+(TILE_Y(t1))*Main_image_width+(TILE_X(t1)); bmp2 = Main_backups->Pages->Image[Main_current_layer].Pixels+(TILE_Y(t2)+Snap_height-1)*Main_image_width+(TILE_X(t2)); for (y=0; y < Snap_height; y++) { if (memcmp(bmp1+y*Main_image_width, bmp2-y*Main_image_width, Snap_width)) return 0; } return 1; } /// int Tile_is_same_flipped_xy(int t1, int t2) { byte *bmp1,*bmp2; int y, x; bmp1 = Main_backups->Pages->Image[Main_current_layer].Pixels+(TILE_Y(t1))*Main_image_width+(TILE_X(t1)); bmp2 = Main_backups->Pages->Image[Main_current_layer].Pixels+(TILE_Y(t2)+Snap_height-1)*Main_image_width+(TILE_X(t2)+Snap_width-1); for (y=0; y < Snap_height; y++) { for (x=0; x < Snap_width; x++) if (*(bmp1+y*Main_image_width+x) != *(bmp2-y*Main_image_width-x)) return 0; } return 1; } /// Create or update a tilemap based on current screen (layer)'s pixels. void Tilemap_update(void) { int width; int height; int tile; int count=1; T_Tile * tile_ptr; int wait_window=0; byte old_cursor=0; if (!Main_tilemap_mode) return; width=(Main_image_width-Snap_offset_X)/Snap_width; height=(Main_image_height-Snap_offset_Y)/Snap_height; if (width<1 || height<1 || width*height>1000000l || (tile_ptr=(T_Tile *)malloc(width*height*sizeof(T_Tile))) == NULL) { // Cannot enable tilemap because either the image is too small // for the grid settings (and I don't want to implement partial tiles) // Or the number of tiles seems unreasonable (one million) : This can // happen if you set grid 1x1 for example. Disable_main_tilemap(); return; } if (Main_tilemap) { // Recycle existing tilemap free(Main_tilemap); Main_tilemap=NULL; } Main_tilemap=tile_ptr; Main_tilemap_width=width; Main_tilemap_height=height; if (width*height > 1000 || Config.Tilemap_show_count) { wait_window=1; old_cursor=Cursor_shape; Open_window(180,36,"Creating tileset"); Print_in_window(26, 20, "Please wait...",MC_Black,MC_Light); Cursor_shape=CURSOR_SHAPE_HOURGLASS; Update_window_area(0,0,Window_width, Window_height); Display_cursor(); Get_input(0); } // Initialize array with all tiles unique by default for (tile=0; tile