#include "tile.h"
#include <stdio.h>
#include <memory.h>
#include <assert.h>
#include <SDL.h>
#include <SDL_image.h>
tile::tile() : m_width(0), m_height(0), m_pixels(NULL), m_shrink(true)
{
}
tile::tile(const tile &img, const char *enumnam, const char *parts) :
m_width(0), m_height(0), m_pixels(NULL)
{
copy(img);
if (enumnam)
m_enumname = enumnam;
if (parts)
m_parts_ctg = parts;
}
tile::~tile()
{
unload();
}
void tile::unload()
{
delete[] m_pixels;
m_pixels = NULL;
m_width = m_height = 0;
}
bool tile::valid() const
{
return m_pixels && m_width && m_height;
}
const std::string &tile::filename()
{
return m_filename;
}
const std::string &tile::enumname()
{
return m_enumname;
}
const std::string &tile::parts_ctg()
{
return m_parts_ctg;
}
int tile::width()
{
return m_width;
}
int tile::height()
{
return m_height;
}
bool tile::shrink()
{
return m_shrink;
}
void tile::set_shrink(bool new_shrink)
{
m_shrink = new_shrink;
}
void tile::resize(int new_width, int new_height)
{
delete[] m_pixels;
m_width = new_width;
m_height = new_height;
m_pixels = NULL;
if (!m_width || !m_height)
return;
m_pixels = new tile_colour[m_width * m_height];
}
void tile::add_rim(const tile_colour &rim)
{
bool *flags = new bool[m_width * m_height];
for (int y = 0; y < m_height; y++)
for (int x = 0; x < m_width; x++)
{
flags[x + y * m_width] =
(get_pixel(x, y).a > 0 && get_pixel(x,y) != rim);
}
for (int y = 0; y < m_height; y++)
for (int x = 0; x < m_width; x++)
{
if (flags[x + y * m_width])
continue;
if (x > 0 && flags[(x-1) + y * m_width]
|| y > 0 && flags[x + (y-1) * m_width]
|| x < m_width - 1 && flags[(x+1) + y * m_width]
|| y < m_height - 1 && flags[x + (y+1) * m_width])
{
get_pixel(x,y) = rim;
}
}
delete[] flags;
}
void tile::corpsify()
{
tile_colour red_blood(0, 0, 32, 255);
const int separate_x = 3;
const int separate_y = 4;
corpsify(32, 32, separate_x, separate_y, red_blood);
}
static int _corpse_cut_height(int x, int width, int height)
{
unsigned int cy = height / 2 + 2;
const int limit1 = width / 8;
const int limit2 = width / 3;
if (x < limit1 || x >= width - limit1)
cy += 2;
else if (x < limit2 || x >= width - limit2)
cy += 1;
return cy;
}
void tile::corpsify(int corpse_width, int corpse_height,
int cut_separate, int cut_height, const tile_colour &wound)
{
tile orig(*this);
resize(corpse_width, corpse_height);
fill(tile_colour::transparent);
bool *flags = new bool[corpse_width * corpse_height];
memset(flags, 0, corpse_width * corpse_height * sizeof(bool));
#define flags(x,y) (flags[((x) + (y) * corpse_width)])
int xmin, ymin, bbwidth, bbheight;
orig.get_bounding_box(xmin, ymin, bbwidth, bbheight);
const int xmax = xmin + bbwidth - 1;
const int ymax = ymin + bbheight - 1;
const int centerx = (xmax + xmin) / 2;
const int centery = (ymax + ymin) / 2;
const float width_scale = (float)m_width / (float)corpse_width;
const float height_scale = (float)m_height / (float)corpse_height;
const float image_scale = std::max(width_scale, height_scale);
const float height_proj = 2.0f;
for (int y = 0; y < corpse_height; y++)
for (int x = 0; x < corpse_width; x++)
{
const int cy = _corpse_cut_height(x, corpse_width, corpse_height);
if (y > cy - cut_height && y <= cy)
continue;
int x1 = (int)((x - m_width/2)*image_scale) + centerx;
int y1 = (int)((y - m_height/2)*height_proj*image_scale) + centery;
if (y >= cy)
{
x1 -= cut_separate;
y1 -= cut_height / 2;
}
else
{
x1 += cut_separate;
y1 += cut_height / 2 + cut_height % 2;
}
if (x1 < 0 || x1 >= m_width || y1 < 0 || y1 >= m_height)
continue;
tile_colour &mapped = orig.get_pixel(x1, y1);
if (mapped == tile_colour::black
|| mapped == tile_colour::transparent)
{
continue;
}
get_pixel(x,y) = mapped;
flags(x, y) = true;
}
const int wound_height = std::min(2, cut_height);
for (int x = 0; x < corpse_width; x++)
{
int cy = _corpse_cut_height(x, corpse_width, corpse_height);
if (flags(x, cy - cut_height))
{
const int start = cy - cut_height + 1;
for (int y = start; y < start + wound_height; y++)
get_pixel(x, y) = wound;
}
}
for (int y = 1; y < corpse_height; y++)
for (int x = 1; x < corpse_width; x++)
{
if (!flags(x, y) && flags(x-1, y-1)
&& get_pixel(x,y) == tile_colour::transparent)
{
get_pixel(x, y) = tile_colour::black;
}
}
for (int y = 3; y < corpse_height; y++)
for (int x = 3; x < corpse_width; x++)
{
if (get_pixel(x-1,y-1) == tile_colour::black
&& flags(x-2, y-2) && flags(x-3, y-3)
&& get_pixel(x-1, y) == tile_colour::black
&& get_pixel(x, y-1) == tile_colour::black)
{
get_pixel(x, y) = tile_colour::black;
}
}
delete[] flags;
}
void tile::copy(const tile &img)
{
unload();
m_width = img.m_width;
m_height = img.m_height;
m_filename = img.m_filename;
m_pixels = new tile_colour[m_width * m_height];
m_shrink = img.m_shrink;
memcpy(m_pixels, img.m_pixels, m_width * m_height * sizeof(tile_colour));
m_enumname.clear();
}
bool tile::compose(const tile &img)
{
if (!valid())
{
fprintf(stderr, "Error: can't compose onto an unloaded image.\n");
return (false);
}
if (!img.valid())
{
fprintf(stderr, "Error: can't compose from an unloaded image.\n");
return (false);
}
if (m_width != img.m_width || m_height != img.m_height)
{
fprintf(stderr, "Error: can't compose with mismatched dimensions. "
"(%d, %d) onto (%d, %d)\n",
img.m_width, img.m_height, m_width, m_height);
return (false);
}
for (int i = 0; i < m_width * m_height; i += 1)
{
const tile_colour *src = &img.m_pixels[i];
tile_colour *dest = &m_pixels[i];
dest->r = (src->r * src->a + dest->r * (255 - src->a)) / 255;
dest->g = (src->g * src->a + dest->g * (255 - src->a)) / 255;
dest->b = (src->b * src->a + dest->b * (255 - src->a)) / 255;
dest->a = (src->a * 255 + dest->a * (255 - src->a)) / 255;
}
return (true);
}
bool tile::load(const std::string &new_filename)
{
m_filename = new_filename;
if (m_pixels)
unload();
SDL_Surface *img = IMG_Load(new_filename.c_str());
if (!img)
return (false);
m_width = img->w;
m_height = img->h;
m_pixels = new tile_colour[m_width * m_height];
const unsigned int bpp = img->format->BytesPerPixel;
if (bpp == 1)
{
SDL_Palette *pal = img->format->palette;
assert(pal);
assert(pal->colors);
int src = 0;
int dest = 0;
for (int y = 0; y < img->h; y++)
for (int x = 0; x < img->w; x++)
{
int index = ((unsigned char*)img->pixels)[src++];
m_pixels[dest].r = pal->colors[index].r;
m_pixels[dest].g = pal->colors[index].g;
m_pixels[dest].b = pal->colors[index].b;
m_pixels[dest].a = (index != img->format->colorkey ? 255 : 0);
dest++;
}
}
else
{
SDL_LockSurface(img);
int dest = 0;
for (int y = 0; y < img->h; y++)
for (int x = 0; x < img->w; x++)
{
unsigned char *p = (unsigned char*)img->pixels
+ y*img->pitch + x*bpp;
unsigned int pixel;
switch (img->format->BytesPerPixel)
{
case 1:
pixel = *p;
break;
case 2:
pixel = *(unsigned short*)p;
break;
case 3:
if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
pixel = p[0] << 16 | p[1] << 8 | p[2];
else
pixel = p[0] | p[1] << 8 | p[2] << 16;
break;
case 4:
pixel = *(unsigned int*)p;
break;
default:
assert(!"Invalid bpp");
SDL_UnlockSurface(img);
SDL_FreeSurface(img);
return (false);
}
SDL_GetRGBA(pixel, img->format, &m_pixels[dest].r,
&m_pixels[dest].g, &m_pixels[dest].b,
&m_pixels[dest].a);
dest++;
}
SDL_UnlockSurface(img);
}
SDL_FreeSurface(img);
replace_colour(tile_colour::background, tile_colour::transparent);
return (true);
}
void tile::fill(const tile_colour &col)
{
for (int y = 0; y < m_height; y++)
for (int x = 0; x < m_width; x++)
get_pixel(x, y) = col;
}
void tile::replace_colour(tile_colour &find, tile_colour &replace)
{
for (int y = 0; y < m_height; y++)
for (int x = 0; x < m_width; x++)
{
tile_colour &p = get_pixel(x, y);
if (p == find)
p = replace;
}
}
tile_colour &tile::get_pixel(unsigned int x, unsigned int y)
{
assert(m_pixels && x < m_width && y < m_height);
return m_pixels[x + y * m_width];
}
void tile::get_bounding_box(int &x0, int &y0, int &w, int &h)
{
if (!valid())
{
x0 = y0 = w = h = 0;
return;
}
x0 = y0 = 0;
unsigned int x1 = m_width - 1;
unsigned int y1 = m_height - 1;
while (x0 <= x1)
{
bool found = false;
for (unsigned int y = y0; !found && y < y1; y++)
found |= (get_pixel(x0, y).a > 0);
if (found)
break;
x0++;
}
while (x0 <= x1)
{
bool found = false;
for (unsigned int y = y0; !found && y < y1; y++)
found |= (get_pixel(x1, y).a > 0);
if (found)
break;
x1--;
}
while (y0 <= y1)
{
bool found = false;
for (unsigned int x = x0; !found && x < x1; x++)
found |= (get_pixel(x, y0).a > 0);
if (found)
break;
y0++;
}
while (y0 <= y1)
{
bool found = false;
for (unsigned int x = x0; !found && x < x1; x++)
found |= (get_pixel(x, y1).a > 0);
if (found)
break;
y1--;
}
w = x1 - x0 + 1;
h = y1 - y0 + 1;
}