#include "img.h"
#pragma pack(push,1)
typedef struct {
unsigned char idLength;
unsigned char colormapType;
unsigned char imageType;
unsigned char colormapIndex[2];
unsigned char colormapLength[2];
unsigned char colormapSize;
unsigned char xOrigin[2];
unsigned char yOrigin[2];
unsigned char width[2];
unsigned char height[2];
unsigned char pixelSize;
unsigned char attributes;
} TgaHeader;
#pragma pack(pop)
Image imgCreate(int w, int h) {
if (w <= 0 || h <= 0)
return (Image){};
Color *pixels = malloc(sizeof(Color)*w*h);
if (!pixels)
return LOGERR("imgCreate: cannot alocate memory for pixels (%dx%d)", w, h), (Image){};
return (Image){ .w = w, .h = h, .data = pixels };
}
void imgFree(Image *img) {
if (!img) return;
LOGDBG("imgFree");
free(img->data);
*img = (Image){};
}
Image imgCopy(Image img) {
if (!imgValid(img)) return (Image){};
Image res = imgCreate(img.w, img.h);
if (!imgValid(res)) return (Image){};
memcpy(res.data, img.data, sizeof(Color)*img.w*img.h);
return res;
}
Image imgSub(Image img, int x, int y, int w, int h) {
if (!imgValid(img)) return (Image){};
if (x < 0) w += x, x = 0;
if (y < 0) h += y, y = 0;
if (x + w > img.w) w = img.w - x;
if (y + h > img.h) h = img.h - y;
if (w <= 0 || h <= 0) return (Image){};
Image res = imgCreate(w, h);
if (!imgValid(res)) return (Image){};
for(int i = 0; i < h; ++i)
memcpy(res.data + i*w, img.data + (y+i)*img.w + x, sizeof(Color)*w);
return res;
}
void imgClear(Image img) {
if (!imgValid(img)) return;
memset(img.data, 0, sizeof(Color)*img.w*img.h);
}
void imgMultA(Image img) {
if (!imgValid(img)) return;
for(Color *p = img.data, *e = p + img.w*img.h; p < e; ++p) {
unsigned int a = p->a;
p->r = (p->r*a + 0xff) >> 8;
p->g = (p->g*a + 0xff) >> 8;
p->b = (p->b*a + 0xff) >> 8;
}
}
void imgDivA(Image img) {
if (!imgValid(img)) return;
for(Color *p = img.data, *e = p + img.w*img.h; p < e; ++p) {
unsigned int a = p->a;
if (!a) continue;
p->r = (((unsigned int)p->r << 8) - 1)/a;
p->g = (((unsigned int)p->g << 8) - 1)/a;
p->b = (((unsigned int)p->b << 8) - 1)/a;
}
}
int imgVisible(Image img) {
if (!imgValid(img)) return 0;
for(Color *p = img.data, *e = p + img.w*img.h; p < e; ++p)
if (p->a) return 1;
return 0;
}
int imgHasPicture(Image img) {
if (!imgValid(img)) return 0;
Color c = {};
for(Color *p = img.data, *e = p + img.w*img.h; p < e; ++p)
if (p->a) { c = *p; break; }
if (!c.a) return 0;
for(Color *p = img.data, *e = p + img.w*img.h; p < e; ++p)
for(int i = 0; i < 4; ++i)
if (p->c[i] != c.c[i]) return 1;
return 0;
}
static void rowResample(
Color *dst, unsigned int dcnt, unsigned int dstep,
Color *src, unsigned int scnt, unsigned int sstep
) {
if (dcnt < scnt) {
// downscale
unsigned int sistep = dcnt << 8, si = sistep, di1 = 0x100, w = 0, sum[4] = {};
for(Color *e = src + scnt*sstep; src != e; src += sstep, si += sistep) {
unsigned int di = si/scnt;
if (di < di1) {
w += 0xff;
for(int i = 0; i < 4; ++i)
sum[i] += src->c[i];
} else {
unsigned int k1 = (di - di1)*scnt/dcnt;
unsigned int k0 = 0xff - k1;
w += k0;
for(int i = 0; i < 4; ++i) {
unsigned int c = src->c[i];
unsigned int cc = (c*k0 + 0xff) >> 8;
dst->c[i] = (sum[i] + cc)*255/w;
sum[i] = (c*k1 + 0xff) >> 8;
}
w = k1;
di1 += 0x100;
dst += dstep;
}
}
} else {
// upscale
if (!--dcnt) dcnt = 1;
if (!--scnt) sstep = 0;
unsigned int di = 0, distep = (scnt << 8) - 1;
for(Color *e = dst + dcnt*dstep + dstep; dst != e; dst += dstep, di += distep) {
unsigned int si = di/dcnt;
unsigned int k1 = si & 0xff;
unsigned int k0 = 0xff - k1;
si >>= 8;
Color *s0 = src + si*sstep;
Color *s1 = s0 + sstep;
for(int i = 0; i < 4; ++i)
dst->c[i] = (s0->c[i]*k0 + s1->c[i]*k1 + 0xff) >> 8;
}
}
}
Image imgResampleX(Image img, int w) {
if (!imgValid(img) || w <= 0)
return (Image){};
if (img.w == w)
return imgCopy(img);
Image res = imgCreate(w, img.h);
for(int i = 0; i < res.h; ++i)
rowResample(res.data + i*w, w, 1, img.data + i*img.w, img.w, 1);
return res;
}
Image imgResampleY(Image img, int h) {
if (!imgValid(img) || h <= 0)
return (Image){};
if (img.h == h)
return imgCopy(img);
Image res = imgCreate(img.w, h);
for(int i = 0; i < res.w; ++i)
rowResample(res.data + i, h, img.w, img.data + i, img.h, img.w);
return res;
}
Image imgResample(Image img, int w, int h) {
if (!imgValid(img) || w <= 0 || h <= 0)
return (Image){};
if (img.w == w && img.h == h)
return imgCopy(img);
if (img.h == h)
return imgResampleX(img, w);
if (img.w == w)
return imgResampleY(img, h);
//clock_t start = clock();
Image tmp = imgResampleX(img, w);
Image res = imgResampleY(tmp, h);
imgFree(&tmp);
//LOGDBG("imgResample: %f sec", (clock() - start)/(double)CLOCKS_PER_SEC);
return res;
}
XImage* imgToX(Image *img) {
LOGDBG("imgToX: create XImage");
if (!img)
return LOGERR("imgToX: bad args"), NULL;
XImage *res = XCreateImage(dpy, visual, 24, ZPixmap, 0, (char*)img->data, img->w, img->h, 32, img->w*sizeof(Color));
if (!res)
return LOGERR("imgToX: cannot create XImage (%dx%d)", img->w, img->h), imgFree(img), NULL;
*img = (Image){};
return res;
}
Image imgLoadTga(const char *filename) {
LOGDBG("imgLoadTga: open: %s", filename);
FILE *f = fopen(filename, "rb");
if (!f)
return LOGERR("imgLoadTga: cannot open file for read: %s", filename), (Image){};
LOGDBG("imgLoadTga: read header");
TgaHeader header = {};
fread(&header, sizeof(header), 1, f);
if ( header.imageType != 2
|| (!header.width[0] && !header.width[1])
|| (!header.height[0] && !header.height[1])
|| header.colormapType
|| (header.pixelSize != 24 && header.pixelSize != 32) )
return LOGERR("imgLoadTga: unsupported format, only uncompressed 24 or 32 bits TGA are supported: %s", filename), fclose(f), (Image){};
int w = header.width[1]*256 + header.width[0];
int h = header.height[1]*256 + header.height[0];
LOGDBG("imgLoadTga: create image (%d x %d)", w, h);
Image res = imgCreate(w, h);
if (!imgValid(res))
return LOGERR("imgLoadTga: cannot create image (%d x %d): %s", w, h, filename), fclose(f), (Image){};
LOGDBG("imgLoadTga: read data");
fseek(f, header.idLength, SEEK_CUR);
Color *row = res.data + w*h;
for(unsigned short r = h; r; --r, row -= w) {
for(Color *c = row - w; c < row; ++c) {
c->r = fgetc(f);
c->g = fgetc(f);
c->b = fgetc(f);
c->a = header.pixelSize == 32 ? fgetc(f) : 255;
}
}
LOGDBG("imgLoadTga: done");
fclose(f);
return res;
}