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