gambas-source-code/main/lib/image/image.c
Benoît Minisini 075ded4dee [GB.IMAGE]
* NEW: Image.DrawImage() and Image.PaintImage() now takes a destination 
  width and height arguments. But using them to stretch the source image is
  not supported. Only gb.image.imlib, gb.gtk and gb.qt4 implement that
  feature.


git-svn-id: svn://localhost/gambas/trunk@3822 867c0c6c-44f3-4631-809d-bfa615b0a4ec
2011-05-01 02:54:51 +00:00

1283 lines
27 KiB
C

/***************************************************************************
image.c
(c) 2000-2011 Benoît Minisini <gambas@users.sourceforge.net>
This program 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; either version 2, or (at your option)
any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***************************************************************************/
#define __IMAGE_C
#include "c_color.h"
#include "image.h"
bool IMAGE_debug = FALSE;
typedef
struct { unsigned char d[3]; } PACKED uint24;
typedef
struct {
int format;
const char *name;
}
FORMAT;
//#define DEBUG_CONVERT
//#define DEBUG_ME 1
static int _default_format = GB_IMAGE_RGBA;
static FORMAT _formats[] =
{
{ GB_IMAGE_BGRX, "BGRX" },
{ GB_IMAGE_XRGB, "XRGB" },
{ GB_IMAGE_RGBX, "RGBX" },
{ GB_IMAGE_XBGR, "XBGR" },
{ GB_IMAGE_BGR , "BGR" },
{ GB_IMAGE_RGB , "RGB" },
{ GB_IMAGE_BGRA, "BGRA" },
{ GB_IMAGE_ARGB, "ARGB" },
{ GB_IMAGE_RGBA, "RGBA" },
{ GB_IMAGE_ABGR, "ABGR" },
{ GB_IMAGE_BGRP, "BGRP" },
{ GB_IMAGE_PRGB, "PRGB" },
{ GB_IMAGE_RGBP, "RGBP" },
{ GB_IMAGE_PBGR, "PBGR" },
{ 0, NULL }
};
/*static inline unsigned char *GET_END_POINTER(GB_IMG *image)
{
return &image->data[IMAGE_size(image)];
}*/
#define GET_END_POINTER(_img) (&(_img)->data[IMAGE_size(_img)])
#define PREMUL(_x) \
({ \
uint x = (_x); \
uint a = x >> 24; \
\
if (a == 0) \
x = 0; \
else if (a != 0xFF) \
{ \
uint t = (x & 0xFF00FF) * a; \
t = (t + ((t >> 8) & 0xFF00FF) + 0x800080) >> 8; \
t &= 0xff00ff; \
\
x = ((x >> 8) & 0xff) * a; \
x = (x + ((x >> 8) & 0xFF) + 0x80); \
x &= 0xFF00; \
x |= t | (a << 24); \
} \
x; \
})
#define INV_PREMUL(__p) \
({ \
uint _p = (__p); \
if (ALPHA(_p) == 0) \
_p = 0; \
else if (ALPHA(_p) != 0xFF) \
_p = ((ALPHA(_p) << 24) \
| (((255*RED(_p))/ ALPHA(_p)) << 16) \
| (((255*GREEN(_p)) / ALPHA(_p)) << 8) \
| ((255*BLUE(_p)) / ALPHA(_p))); \
_p; \
})
#define SWAP(__p) \
({ \
uint _p = (__p); \
RGBA(ALPHA(_p), BLUE(_p), GREEN(_p), RED(_p)); \
})
#define SWAP_RED_BLUE(__p) \
({ \
uint _p = (__p); \
RGBA(BLUE(_p), GREEN(_p), RED(_p), ALPHA(_p)); \
})
// Convert from GB_COLOR to a specific format
static uint GB_COLOR_to_format(GB_COLOR col, int format)
{
col ^= 0xFF000000;
if (GB_IMAGE_FMT_IS_PREMULTIPLIED(format))
col = PREMUL(col);
if (GB_IMAGE_FMT_IS_SWAPPED(format))
col = SWAP(col);
if (GB_IMAGE_FMT_IS_RGBA(format))
col = SWAP_RED_BLUE(col);
return col;
}
// Convert from a specific format to GB_COLOR
static GB_COLOR GB_COLOR_from_format(uint col, int format)
{
if (GB_IMAGE_FMT_IS_RGBA(format))
col = SWAP_RED_BLUE(col);
if (GB_IMAGE_FMT_IS_SWAPPED(format))
col = SWAP(col);
if (GB_IMAGE_FMT_IS_PREMULTIPLIED(format))
col = INV_PREMUL(col);
return col ^ 0xFF000000;
}
// Convert from BGRA to a specific format
static inline uint BGRA_to_format(uint col, int format)
{
if (GB_IMAGE_FMT_IS_PREMULTIPLIED(format))
col = PREMUL(col);
if (GB_IMAGE_FMT_IS_SWAPPED(format))
col = SWAP(col);
if (GB_IMAGE_FMT_IS_RGBA(format))
col = SWAP_RED_BLUE(col);
return col;
}
// Convert from a specific format to BGRA
static inline uint BGRA_from_format(uint col, int format)
{
if (GB_IMAGE_FMT_IS_RGBA(format))
col = SWAP_RED_BLUE(col);
if (GB_IMAGE_FMT_IS_SWAPPED(format))
col = SWAP(col);
if (GB_IMAGE_FMT_IS_PREMULTIPLIED(format))
col = INV_PREMUL(col);
return col;
}
// Convert from GB_COLOR to BGRA
static inline uint GB_COLOR_to_BGRA(GB_COLOR col)
{
return col ^ 0xFF000000;
}
// Compose two BGRA colors
static inline uint BGRA_compose(uint dst, uint src)
{
unsigned char a = ALPHA(src);
if (a == 255)
return src;
else if (a == 0)
return dst;
else
{
unsigned char r = ((RED(src) - RED(dst)) * a) / 256 + RED(dst);
unsigned char g = ((GREEN(src) - GREEN(dst)) * a) / 256 + GREEN(dst);
unsigned char b = ((BLUE(src) - BLUE(dst)) * a) / 256 + BLUE(dst);
if (ALPHA(dst) > a)
a = ALPHA(dst);
return RGBA(r, g, b, a);
}
}
static inline bool is_valid(GB_IMG *img, int x, int y)
{
return !(x >= img->width || y >= img->height || x < 0 || y < 0);
}
static void free_image(GB_IMG *img, void *image)
{
//fprintf(stderr, "free_image: %p %p : %d\n", img, img->data, IMAGE_size(img));
GB.Free(POINTER(&img->data));
}
static GB_IMG_OWNER _image_owner = {
"gb.image",
0,
free_image,
free_image,
NULL,
NULL
};
// Only converts to the following formats:
// - GB_IMAGE_BGRA
// - GB_IMAGE_BGRX
// - GB_IMAGE_RGBA
// - GB_IMAGE_RGBX
static void convert_image(uchar *dst, int dst_format, uchar *src, int src_format, int w, int h)
{
unsigned char *s = src;
unsigned char *d = dst;
int len;
unsigned char *dm;
uint *p, *pm;
bool psrc, pdst;
#ifdef DEBUG_CONVERT
fprintf(stderr, "convert_image: src_format = %d dst_format = %d\n", src_format, dst_format);
#endif
len = w * h * sizeof(uint);
dm = &d[len];
psrc = GB_IMAGE_FMT_IS_PREMULTIPLIED(src_format);
src_format = GB_IMAGE_FMT_CLEAR_PREMULTIPLIED(src_format);
pdst = GB_IMAGE_FMT_IS_PREMULTIPLIED(dst_format);
dst_format = GB_IMAGE_FMT_CLEAR_PREMULTIPLIED(dst_format);
#ifdef DEBUG_CONVERT
fprintf(stderr, "convert_image: after: src_format = %d dst_format = %d\n", src_format, dst_format);
#endif
if (dst_format == GB_IMAGE_BGRA || dst_format == GB_IMAGE_BGRX)
{
switch (src_format)
{
case GB_IMAGE_BGRA: case GB_IMAGE_BGRX:
goto __0123;
case GB_IMAGE_ARGB: case GB_IMAGE_XRGB:
goto __3210;
case GB_IMAGE_RGBA: case GB_IMAGE_RGBX:
goto __2103;
case GB_IMAGE_ABGR: case GB_IMAGE_XBGR:
goto __1230;
case GB_IMAGE_BGR:
goto __012X;
case GB_IMAGE_RGB:
goto __210X;
}
}
else if (dst_format == GB_IMAGE_RGBA || dst_format == GB_IMAGE_RGBX)
{
switch (src_format)
{
case GB_IMAGE_RGBA: case GB_IMAGE_RGBX:
goto __0123;
case GB_IMAGE_ABGR: case GB_IMAGE_XBGR:
goto __3210;
case GB_IMAGE_BGRA: case GB_IMAGE_BGRX:
goto __2103;
case GB_IMAGE_ARGB: case GB_IMAGE_XRGB:
goto __1230;
case GB_IMAGE_RGB:
goto __012X;
case GB_IMAGE_BGR:
goto __210X;
}
}
__0123:
#ifdef DEBUG_CONVERT
fprintf(stderr, "convert_image: 0123\n");
#endif
memcpy(dst, src, len);
goto __PREMULTIPLIED;
__3210:
#ifdef DEBUG_CONVERT
fprintf(stderr, "convert_image: 3210\n");
#endif
while (d != dm)
{
d[0] = s[3];
d[1] = s[2];
d[2] = s[1];
d[3] = s[0];
s += 4;
d += 4;
}
goto __PREMULTIPLIED;
__2103:
#ifdef DEBUG_CONVERT
fprintf(stderr, "convert_image: 2103\n");
#endif
while (d != dm)
{
d[0] = s[2];
d[1] = s[1];
d[2] = s[0];
d[3] = s[3];
s += 4;
d += 4;
}
goto __PREMULTIPLIED;
__1230:
#ifdef DEBUG_CONVERT
fprintf(stderr, "convert_image: 1230\n");
#endif
while (d != dm)
{
d[0] = s[1];
d[1] = s[2];
d[2] = s[3];
d[3] = s[0];
s += 4;
d += 4;
}
goto __PREMULTIPLIED;
__012X:
#ifdef DEBUG_CONVERT
fprintf(stderr, "convert_image: 012X\n");
#endif
while (d != dm)
{
d[0] = s[0];
d[1] = s[1];
d[2] = s[2];
d[3] = 0xFF;
s += 3;
d += 4;
}
return;
__210X:
#ifdef DEBUG_CONVERT
fprintf(stderr, "convert_image: 210X\n");
#endif
while (d != dm)
{
d[0] = s[2];
d[1] = s[1];
d[2] = s[0];
d[3] = 0xFF;
s += 3;
d += 4;
}
return;
__PREMULTIPLIED:
if (psrc == pdst)
return;
p = (uint *)dst;
pm = (uint *)dm;
if (psrc)
{
#ifdef DEBUG_CONVERT
fprintf(stderr, "convert_image: premultiplied -> normal\n");
#endif
// convert premultiplied to normal
while (p != pm)
{
*p = INV_PREMUL(*p);
p++;
}
}
else
{
#ifdef DEBUG_CONVERT
fprintf(stderr, "convert_image: normal -> premultiplied\n");
#endif
// convert normal to premultiplied
while (p != pm)
{
*p = PREMUL(*p);
p++;
}
}
}
#define SYNCHRONIZE(_img) ({ if ((_img)->sync) (*(_img)->temp_owner->sync)(_img); })
#define MODIFY(_img) ((_img)->modified = TRUE)
int IMAGE_size(GB_IMG *img)
{
return img->width * img->height * (GB_IMAGE_FMT_IS_24_BITS(img->format) ? 3 : 4);
}
void IMAGE_create(GB_IMG *img, int width, int height, int format)
{
GB_BASE save = img->ob;
CLEAR(img);
img->ob = save;
img->owner = &_image_owner;
if (width <= 0 || height <= 0)
{
img->is_void = TRUE;
return;
}
img->width = width;
img->height = height;
img->format = format;
GB.Alloc(POINTER(&img->data), IMAGE_size(img));
img->owner_handle = img->data;
#ifdef DEBUG_ME
fprintf(stderr, "IMAGE_create: %p\n", img);
#endif
}
void IMAGE_create_with_data(GB_IMG *img, int width, int height, int format, unsigned char *data)
{
IMAGE_create(img, width, height, format);
if (data && !IMAGE_is_void(img))
memcpy(img->data, data, IMAGE_size(img));
}
const char *IMAGE_format_to_string(int fmt)
{
FORMAT *pf;
for (pf = _formats; pf->name; pf++)
{
if (fmt == pf->format)
return pf->name;
}
return NULL;
}
void IMAGE_convert(GB_IMG *img, int dst_format)
{
uchar *data;
int src_format = img->format;
if (src_format == dst_format)
return;
//IMAGE_create(&tmp, img->width, img->height, format);
img->format = dst_format;
if (IMAGE_is_void(img))
return;
if (IMAGE_debug)
fprintf(stderr, "gb.image: convert: %s -> %s\n", IMAGE_format_to_string(src_format), IMAGE_format_to_string(dst_format));
GB.Alloc(POINTER(&data), IMAGE_size(img));
convert_image(data, dst_format, img->data, src_format, img->width, img->height);
//GB.Free(POINTER(&img->data));
IMAGE_take(img, &_image_owner, data, img->width, img->height, data);
}
// Check if a temporary handle is needed, and create it if needed by calling the owner "temp" function
void *IMAGE_check(GB_IMG *img, GB_IMG_OWNER *temp_owner)
{
if (!img)
return NULL;
// If we already have the temporary handle, then do nothing
if (img->temp_owner == temp_owner)
return img->temp_handle;
#ifdef DEBUG_ME
fprintf(stderr, "IMAGE_check: %p: %s (%p) / %s (%p) -> %s\n",
img,
img->owner->name, img->owner_handle,
img->temp_owner ? img->temp_owner->name : "NULL", img->temp_handle,
temp_owner ? temp_owner->name : "NULL");
#endif
// If somebody else has a temporary handle
if (img->temp_owner)
{
// release it only if it is not the owner
if (img->temp_owner != img->owner && img->temp_owner->release)
(*img->temp_owner->release)(img, img->temp_handle);
img->temp_handle = 0;
img->temp_owner = NULL;
}
// Get the temporary handle
if (temp_owner)
{
// If we are the owner, we must use our owner handle as temporary handle
if (img->owner == temp_owner)
img->temp_handle = img->owner_handle;
// If we are not the owner, then we will have to create the temporary handle ourself
else
{
// Synchronize the image if needed
SYNCHRONIZE(img);
// Conversion can make gb.image the new owner and temporary owner
IMAGE_convert(img, temp_owner->format);
img->temp_handle = (*temp_owner->temp)(img);
}
}
// Become the temporary owner
img->temp_owner = temp_owner;
#ifdef DEBUG_ME
fprintf(stderr, "==========>: %p: %s (%p) / %s (%p)\n",
img,
img->owner->name, img->owner_handle,
img->temp_owner ? img->temp_owner->name : "NULL", img->temp_handle);
#endif
return img->temp_handle;
}
// Take ownership of the image
void IMAGE_take(GB_IMG *img, GB_IMG_OWNER *owner, void *owner_handle, int width, int height, unsigned char *data)
{
if (!img)
return;
// If we are already the owner with the same handle, then do nothing
if (img->owner == owner && img->owner_handle == owner_handle)
return;
// Release the old owner
//fprintf(stderr, "releasing image %p owned by %s\n", img, img->owner->name);
(*img->owner->free)(img, img->owner_handle);
// If we have the temporary handle too, then clean it as it is necessarily the same
if (img->temp_owner == img->owner)
{
img->temp_owner = NULL;
img->temp_handle = 0;
}
// Become the owner
img->owner = owner;
img->owner_handle = owner_handle;
// As we are now the owner, then we must have the temporary handle too
IMAGE_check(img, NULL);
img->temp_owner = owner;
img->temp_handle = owner_handle;
// Initialize the data
img->width = width;
img->height = height;
img->data = data;
if (owner && owner->format)
img->format = owner->format;
img->is_void = width <= 0 || height <= 0;
}
void IMAGE_delete(GB_IMG *img)
{
//IMAGE_take(img, &_image_owner, NULL, 0, 0, NULL);
// Release the temporary handle before the owner, because the temporary owner may write to the data before freeing!
if (img->temp_owner && img->temp_owner != img->owner && img->temp_handle)
(*img->temp_owner->release)(img, img->temp_handle);
if (!IMAGE_is_void(img))
(*img->owner->free)(img, img->owner_handle);
img->width = img->height = 0;
img->format = 0;
img->temp_owner = NULL;
img->temp_handle = NULL;
img->owner = &_image_owner;
img->owner_handle = NULL;
img->is_void = TRUE;
}
void IMAGE_synchronize(GB_IMG *img)
{
SYNCHRONIZE(img);
}
#define GET_POINTER(_img, _p, _pm) \
uint *_p = (uint *)(_img)->data; \
uint *_pm = (uint *)GET_END_POINTER(_img); \
if ((_img)->is_void) return;
void IMAGE_fill(GB_IMG *img, GB_COLOR col)
{
GET_POINTER(img, p, pm);
col = GB_COLOR_to_format(col, img->format);
while (p != pm)
*p++ = col;
MODIFY(img);
}
// GB_IMAGE_RGB[APX] only
void IMAGE_make_gray(GB_IMG *img)
{
GET_POINTER(img, p, pm);
uint col;
uchar g;
int format = img->format;
SYNCHRONIZE(img);
while (p != pm)
{
col = BGRA_from_format(*p, format);
g = (((RED(col) + BLUE(col)) >> 1) + GREEN(col)) >> 1;
*p++ = BGRA_to_format(RGBA(g, g, g, ALPHA(col)), format);
}
MODIFY(img);
}
GB_COLOR IMAGE_get_pixel(GB_IMG *img, int x, int y)
{
uint col;
if (!is_valid(img, x, y))
return (-1);
SYNCHRONIZE(img);
col = ((uint *)img->data)[y * img->width + x];
return GB_COLOR_from_format(col, img->format);
}
void IMAGE_set_pixel(GB_IMG *img, int x, int y, GB_COLOR col)
{
if (!is_valid(img, x, y))
return;
SYNCHRONIZE(img);
((uint *)img->data)[y * img->width + x] = GB_COLOR_to_format(col, img->format);
MODIFY(img);
}
void IMAGE_fill_rect(GB_IMG *img, int x, int y, int w, int h, GB_COLOR col, bool opaque)
{
uint *p;
int i;
uint c;
int format = img->format;
if (x >= img->width || y >= img->height) return;
if (x < 0) { w += x; x = 0; }
if (y < 0) { h += y; y = 0; }
if ((x + w) > img->width) { w = img->width - x; }
if ((y + h) > img->height) { h = img->height - y; }
if (w <= 0 || h <= 0) return;
SYNCHRONIZE(img);
p = &((uint *)img->data)[y * img->width + x];
c = GB_COLOR_to_BGRA(col);
if (opaque || ALPHA(c) == 255)
{
c = BGRA_to_format(c, format);
while (h)
{
for(i = w; i; i--)
*p++ = c;
h--;
p += img->width - w;
}
}
else
{
while (h)
{
for(i = w; i; i--, p++)
*p = BGRA_to_format(BGRA_compose(BGRA_from_format(*p, format), c), format);
h--;
p += img->width - w;
}
}
MODIFY(img);
}
void IMAGE_replace(GB_IMG *img, GB_COLOR src, GB_COLOR dst, bool noteq)
{
GET_POINTER(img, p, pm);
src = GB_COLOR_to_format(src, img->format);
dst = GB_COLOR_to_format(dst, img->format);
SYNCHRONIZE(img);
if (noteq)
{
while (p != pm)
{
if (*p != src)
*p = dst;
p++;
}
}
else
{
while (p != pm)
{
if (*p == src)
*p = dst;
p++;
}
}
MODIFY(img);
}
// Comes from the GIMP
typedef
struct {
float r;
float b;
float g;
float a;
}
FLOAT_RGB;
static void color_to_alpha(FLOAT_RGB *src, const FLOAT_RGB *color)
{
FLOAT_RGB alpha;
alpha.a = src->a;
if (color->r < 0.0001)
alpha.r = src->r;
else if (src->r > color->r)
alpha.r = (src->r - color->r) / (1.0 - color->r);
else if (src->r < color->r)
alpha.r = (color->r - src->r) / color->r;
else alpha.r = 0.0;
if (color->g < 0.0001)
alpha.g = src->g;
else if (src->g > color->g)
alpha.g = (src->g - color->g) / (1.0 - color->g);
else if (src->g < color->g)
alpha.g = (color->g - src->g) / (color->g);
else alpha.g = 0.0;
if (color->b < 0.0001)
alpha.b = src->b;
else if (src->b > color->b)
alpha.b = (src->b - color->b) / (1.0 - color->b);
else if (src->b < color->b)
alpha.b = (color->b - src->b) / (color->b);
else alpha.b = 0.0;
if (alpha.r > alpha.g)
{
if (alpha.r > alpha.b)
src->a = alpha.r;
else
src->a = alpha.b;
}
else if (alpha.g > alpha.b)
{
src->a = alpha.g;
}
else
{
src->a = alpha.b;
}
if (src->a < 0.0001)
return;
src->r = (src->r - color->r) / src->a + color->r;
src->g = (src->g - color->g) / src->a + color->g;
src->b = (src->b - color->b) / src->a + color->b;
src->a *= alpha.a;
}
void IMAGE_make_transparent(GB_IMG *img, GB_COLOR col)
{
uint color;
FLOAT_RGB rgb_color;
FLOAT_RGB rgb_src;
int format = img->format;
GET_POINTER(img, p, pm);
//uint *p = (uint *)img->data;
//uint *pm = (uint *)(img->data + IMAGE_size(img));
//fprintf(stderr, "IMAGE_make_transparent: %d x %d / %d\n", img->width, img->height, img->format);
SYNCHRONIZE(img);
color = GB_COLOR_to_BGRA(col);
rgb_color.b = BLUE(color) / 255.0;
rgb_color.g = GREEN(color) / 255.0;
rgb_color.r = RED(color) / 255.0;
rgb_color.a = 1.0;
while (p != pm)
{
color = BGRA_from_format(*p, format);
rgb_src.b = BLUE(color) / 255.0;
rgb_src.g = GREEN(color) / 255.0;
rgb_src.r = RED(color) / 255.0;
rgb_src.a = ALPHA(color) / 255.0;
color_to_alpha(&rgb_src, &rgb_color);
color = RGBA(
(unsigned char)(255.0 * rgb_src.r + 0.5),
(unsigned char)(255.0 * rgb_src.g + 0.5),
(unsigned char)(255.0 * rgb_src.b + 0.5),
(unsigned char)(255.0 * rgb_src.a + 0.5)
);
*p = BGRA_to_format(color, format);
//fprintf(stderr, "[%d] %08X\n", p - (uint *)img->data, *p);
p++;
}
MODIFY(img);
//fprintf(stderr, "IMAGE_make_transparent: ** DONE **\n");
}
void IMAGE_set_default_format(int format)
{
_default_format = GB_IMAGE_FMT_CLEAR_PREMULTIPLIED(format);
}
int IMAGE_get_default_format()
{
return _default_format;
}
// Parameter correction
#define CHECK_PARAMETERS(dst, dx, dy, dw, dh, src, sx, sy, sw, sh) \
if ( sw < 0 ) sw = src->width; \
if ( sh < 0 ) sh = src->height; \
if (dw < 0) dw = sw; \
if (dh < 0) dh = sh; \
if (dw != sw || dh != sh) \
{ \
GB.Error("Stretching images is not implemented in gb.image"); \
return; \
} \
if ( sx < 0 ) { dx -= sx; dw += sx; sw += sx; sx = 0; } \
if ( sy < 0 ) { dy -= sy; dh += sx; sh += sy; sy = 0; } \
if ( dx < 0 ) { sx -= dx; sw += dx; dx = 0; } \
if ( dy < 0 ) { sy -= dy; sh += dy; dy = 0; } \
if ( (sx + sw) > src->width ) sw = src->width - sx; \
if ( (sy + sh) > src->height ) sh = src->height - sy; \
if ( (dx + sw) > dst->width ) sw = dst->width - dx; \
if ( (dy + sh) > dst->height ) sh = dst->height - dy; \
if (sw <= 0 || sh <= 0) \
return;
void IMAGE_bitblt(GB_IMG *dst, int dx, int dy, int dw, int dh, GB_IMG *src, int sx, int sy, int sw, int sh)
{
int sfmt = src->format;
int dfmt = dst->format;
CHECK_PARAMETERS(dst, dx, dy, dw, dh, src, sx, sy, sw, sh);
SYNCHRONIZE(src);
SYNCHRONIZE(dst);
uint *d = (uint *)dst->data + dy * dst->width + dx;
uint *s = (uint *)src->data + sy * src->width + sx;
if (GB_IMAGE_FMT_IS_32_BITS(sfmt) && GB_IMAGE_FMT_IS_32_BITS(dfmt))
{
if (sfmt != dfmt)
{
const int dd = dst->width - sw;
const int ds = src->width - sw;
int t;
while (sh--)
{
for (t = sw; t--;)
{
*d = BGRA_to_format(BGRA_from_format(*s, sfmt), dfmt);
d++;
s++;
}
d += dd;
s += ds;
}
}
else if (sw < 64)
{
const int dd = dst->width - sw;
const int ds = src->width - sw;
int t;
while (sh--)
{
for (t = sw; t--;)
*d++ = *s++;
d += dd;
s += ds;
}
}
else
{
// Trust libc
const int dd = dst->width;
const int ds = src->width;
const int b = sw * sizeof(uint);
while (sh--)
{
memcpy(d, s, b);
d += dd;
s += ds;
}
}
}
else if (GB_IMAGE_FMT_IS_24_BITS(sfmt) && GB_IMAGE_FMT_IS_24_BITS(sfmt))
{
char *d = (char *)dst->data + (dy * dst->width + dx) * 3;
char *s = (char *)src->data + (sy * src->width + sx) * 3;
const int dd = dst->width * 3;
const int ds = src->width * 3;
const int b = sw * 3;
while (sh--)
{
memcpy(d, s, b);
d += dd;
s += ds;
}
}
else
{
GB.Error("The pixel size of both images must be the same");
}
MODIFY(dst);
}
void IMAGE_draw_alpha(GB_IMG *dst, int dx, int dy, GB_IMG *src, int sx, int sy, int sw, int sh)
{
if (!GB_IMAGE_FMT_IS_32_BITS(src->format) || !GB_IMAGE_FMT_IS_32_BITS(dst->format))
{
GB.Error("The images must have an alpha channel");
return;
}
CHECK_PARAMETERS(dst, dx, dy, sw, sh, src, sx, sy, sw, sh);
SYNCHRONIZE(src);
SYNCHRONIZE(dst);
uchar *d = (uchar *)((uint *)dst->data + dy * dst->width + dx);
uchar *s = (uchar *)((uint *)src->data + sy * src->width + sx);
const int dd = (dst->width - sw) * 4;
const int ds = (src->width - sw) * 4;
//uint cs, cd;
int sformat = src->format;
int dformat = dst->format;
int t;
if (!GB_IMAGE_FMT_IS_SWAPPED(sformat))
s += 3;
if (!GB_IMAGE_FMT_IS_SWAPPED(dformat))
d += 3;
while (sh--)
{
for (t = sw; t--; d += 4,s += 4)
{
if (*s < *d)
*d = *s;
}
d += dd;
s += ds;
}
MODIFY(dst);
}
void IMAGE_compose(GB_IMG *dst, int dx, int dy, int dw, int dh, GB_IMG *src, int sx, int sy, int sw, int sh)
{
if (dst->format != src->format)
{
GB.Error("The images must have the same format");
return;
}
CHECK_PARAMETERS(dst, dx, dy, dw, dh, src, sx, sy, sw, sh);
SYNCHRONIZE(src);
SYNCHRONIZE(dst);
switch(src->format)
{
case GB_IMAGE_RGBA: case GB_IMAGE_BGRA:
{
#if 0
uint *d = (uint *)dst->data + dy * dst->width + dx;
uint *s = (uint *)src->data + sy * src->width + sx;
const int dd = dst->width - sw;
const int ds = src->width - sw;
int t;
while (sh--)
{
for (t = sw; t--;)
{
unsigned char a = ALPHA(*s);
if (a == 255)
*d = *s;
else if (a)
{
unsigned char r = ((RED(*s)-RED(*d)) * a) / 256 + RED(*d);
unsigned char g = ((GREEN(*s)-GREEN(*d)) * a) / 256 + GREEN(*d);
unsigned char b = ((BLUE(*s)-BLUE(*d)) * a) / 256 + BLUE(*d);
if (ALPHA(*d) > a)
a = ALPHA(*d);
*d = RGBA(r,g,b,a);
}
d++;
s++;
}
d += dd;
s += ds;
}
#else
uchar *d = (uchar *)((uint *)dst->data + dy * dst->width + dx);
uchar *s = (uchar *)((uint *)src->data + sy * src->width + sx);
const int dd = (dst->width - sw) * 4;
const int ds = (src->width - sw) * 4;
int t;
while (sh--)
{
for (t = sw; t--;)
{
unsigned char a = s[3];
if (a == 255)
*(uint *)d = *(uint *)s;
else if (a)
{
d[0] = ((s[0] - d[0]) * a) / 256 + d[0];
d[1] = ((s[1] - d[1]) * a) / 256 + d[1];
d[2] = ((s[2] - d[2]) * a) / 256 + d[2];
if (d[3] < a)
d[3] = a;
}
d += 4;
s += 4;
}
d += dd;
s += ds;
}
#endif
break;
}
default:
GB.Error("Unsupported image format");
return;
}
MODIFY(dst);
}
void IMAGE_colorize(GB_IMG *img, GB_COLOR color)
{
GET_POINTER(img, p, pm);
uint col;
int h, s, v;
int r, g, b;
int hcol, scol, vcol;
int format = img->format;
SYNCHRONIZE(img);
col = GB_COLOR_to_BGRA(color);
COLOR_rgb_to_hsv(RED(col), GREEN(col), BLUE(col), &hcol, &scol, &vcol);
while (p != pm)
{
col = BGRA_from_format(*p, format);
COLOR_rgb_to_hsv(RED(col), GREEN(col), BLUE(col), &h, &s, &v);
COLOR_hsv_to_rgb(hcol, scol, v, &r, &g, &b);
*p++ = BGRA_to_format(RGBA(r, g, b, ALPHA(col)), img->format);
}
MODIFY(img);
}
void IMAGE_mask(GB_IMG *img, GB_COLOR color)
{
GET_POINTER(img, p, pm);
uint col;
unsigned char red[256], blue[256], green[256], alpha[256];
int i, r, g, b, a;
int format = img->format;
SYNCHRONIZE(img);
col = GB_COLOR_to_format(color, img->format);
r = RED(col);
g = GREEN(col);
b = BLUE(col);
a = ALPHA(col);
for (i = 0; i < 256; i++)
{
red[i] = i * r / 255;
green[i] = i * g / 255;
blue[i] = i * b / 255;
alpha[i] = i * a / 255;
}
while (p != pm)
{
col = BGRA_from_format(*p, format);
*p++ = BGRA_to_format(RGBA(red[RED(col)], green[GREEN(col)], blue[BLUE(col)], alpha[ALPHA(col)]), format);
}
MODIFY(img);
}
void IMAGE_mirror(GB_IMG *src, GB_IMG *dst, bool horizontal, bool vertical)
{
if (dst->width != src->width || dst->height != src->height || dst->format != src->format || IMAGE_is_void(src))
return;
int w = src->width;
int h = src->height;
int dxi = horizontal ? -1 : 1;
int dxs = horizontal ? (w - 1) : 0;
int dyi = vertical ? -1 : 1;
int dy = vertical ? (h - 1) : 0;
int sx, sy;
SYNCHRONIZE(src);
if (GB_IMAGE_FMT_IS_24_BITS(src->format))
{
for (sy = 0; sy < h; sy++, dy += dyi)
{
uint24 *ssl = (uint24 *)(src->data + sy * src->width * 3);
uint24 *dsl = (uint24 *)(dst->data + dy * dst->width * 3);
int dx = dxs;
for (sx = 0; sx < w; sx++, dx += dxi)
dsl[dx] = ssl[sx];
}
}
else
{
for (sy = 0; sy < h; sy++, dy += dyi)
{
uint *ssl = (uint *)(src->data + sy * src->width * 4);
uint *dsl = (uint *)(dst->data + dy * dst->width * 4);
int dx = dxs;
for (sx = 0; sx < w; sx++, dx += dxi)
dsl[dx] = ssl[sx];
}
}
MODIFY(dst);
}
#if 0
#define GET_RGBA(_col, _r, _g, _b, _a) { _r = RED(_col); _g = GREEN(_col); _b = BLUE(_col); _a = ALPHA(_col); }
void IMAGE_transform(GB_IMG *dst, GB_IMG *src, double sx, double sy, double sdx, double sdy)
{
int x, y;
double ssx;
double ssy;
uint col, cdp;
int ix, iy;
uchar r, g, b, a;
uchar rc, gc, bc, ac;
int wx, wy;
uint *dp = (uint *)dst->data;
memset(dp, 0, IMAGE_size(dst));
dp += dst->width;
for (y = 1; y < (dst->height - 1); y++)
{
ssx = sx;
ssy = sy;
dp++;
for (x = 1; x < (dst->width - 1); x++)
{
ix = sx;
iy = sy;
if (is_valid(src, ix, iy))
{
col = BGRA_from_format(*((uint *)src->data + iy * src->width + ix), src->format);
//dp[1] = RGBA(RED(col) * wx / 256, GREEN(col) * wx / 256, BLUE(col) * wx / 256, ALPHA(col) * wx / 256);
wx = 128;
cdp = BGRA_from_format(*dp, dst->format);
dp[0] = RGBA(RED(cdp) + RED(col) * wx / 256, GREEN(cdp) + GREEN(col) * wx / 256, BLUE(cdp) + BLUE(col) * wx / 256, ALPHA(col));
wx = 32;
cdp = BGRA_from_format(dp[-1], dst->format);
dp[-1] = RGBA(RED(cdp) + RED(col) * wx / 256, GREEN(cdp) + GREEN(col) * wx / 256, BLUE(cdp) + BLUE(col) * wx / 256, ALPHA(col));
cdp = BGRA_from_format(dp[-dst->width], dst->format);
dp[-dst->width] = RGBA(RED(cdp) + RED(col) * wx / 256, GREEN(cdp) + GREEN(col) * wx / 256, BLUE(cdp) + BLUE(col) * wx / 256, ALPHA(col));
dp[1] = RGBA(RED(col) * wx / 256, GREEN(col) * wx / 256, BLUE(col) * wx / 256, 0);
dp[dst->width] = dp[1]; //RGBA(RED(col) * wx / 256, GREEN(col) * wx / 256, BLUE(col) * wx / 256, ALPHA(col) * wx / 256);
}
sx += sdx;
sy += sdy;
dp++;
}
dp++;
sx = ssx - sdy;
sy = ssy + sdx;
}
}
#endif