gambas 09a0573e61 Replace my old sourceforge mail address by the new one.
[CONFIGURATION]
Replace my old sourceforge mail address by the new one.
2018-02-12 02:53:46 +01:00

312 lines
6.7 KiB
C

/***************************************************************************
c_image.c
(c) 2000-2017 Benoît Minisini <g4mba5@gmail.com>
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., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
***************************************************************************/
#define __C_IMAGE_C
#include "c_image.h"
#define LOAD_INC 65536L
static void load_image(const char *addr, int len)
{
GdkPixbuf *img = NULL;
const char *laddr;
int llen;
GdkPixbufLoader* loader = NULL;
GError *error = NULL;
int size;
int format;
GB_IMG *image;
loader = gdk_pixbuf_loader_new();
laddr = addr;
llen = len;
while (llen > 0)
{
size = llen > LOAD_INC ? LOAD_INC : llen;
if (!gdk_pixbuf_loader_write(loader, (guchar*)laddr, (gsize)size, &error))
{
fprintf(stderr, "gb.image.io: error: cannot load %d image bytes\n", size);
GB.Error(error->message);
goto __END;
}
laddr += size;
llen -= size;
}
gdk_pixbuf_loader_close(loader, NULL);
img = gdk_pixbuf_loader_get_pixbuf(loader);
if (!img)
{
GB.Error("Unable to load image");
goto __END;
}
g_object_ref(G_OBJECT(img));
// Rowstride breaks gb.image (it is rounded up so that a line is always a four bytes multiple).
if (gdk_pixbuf_get_n_channels(img) == 3)
{
GdkPixbuf *aimg;
aimg = gdk_pixbuf_add_alpha(img, FALSE, 0, 0, 0);
g_object_unref(G_OBJECT(img));
img = aimg;
}
//fprintf(stderr, "nchannels = %d size = %d x %d rowstride = %d\n", gdk_pixbuf_get_n_channels(img), gdk_pixbuf_get_width(img), gdk_pixbuf_get_height(img), gdk_pixbuf_get_rowstride(img));
switch (gdk_pixbuf_get_n_channels(img))
{
case 3: format = GB_IMAGE_RGB; break;
case 4: format = GB_IMAGE_RGBA; break;
default:
g_object_unref(G_OBJECT(img));
GB.Error("Unsupported number of channels");
goto __END;
}
image = IMAGE.Create(gdk_pixbuf_get_width(img), gdk_pixbuf_get_height(img), format, gdk_pixbuf_get_pixels(img));
IMAGE.Convert(image, IMAGE.GetDefaultFormat());
GB.ReturnObject(image);
g_object_unref(G_OBJECT(img));
__END:
if (loader)
g_object_unref(G_OBJECT(loader));
}
BEGIN_METHOD(Image_Load, GB_STRING path)
char *addr;
int len;
if (GB.LoadFile(STRING(path), LENGTH(path), &addr, &len))
{
GB.Error("Unable to load image");
return;
}
load_image(addr, len);
GB.ReleaseFile(addr, len);
END_METHOD
BEGIN_METHOD(Image_FromString, GB_STRING data)
load_image(STRING(data), LENGTH(data));
END_METHOD
static const char *FILE_get_ext(const char *path)
{
const char *p;
p = rindex(path, '/');
if (p)
path = &p[1];
p = rindex(path, '.');
if (p == NULL)
return &path[strlen(path)];
else
return p + 1;
}
BEGIN_METHOD(Image_Save, GB_STRING path; GB_INTEGER quality)
char *path = GB.FileName(STRING(path), LENGTH(path));
const char *ext = NULL;
bool ok = FALSE;
int b;
char *format = NULL;
GSList *formats, *iter;
GdkPixbuf *image = NULL;
int quality = VARGOPT(quality, -1);
char arg[4];
GError *error = NULL;
ext = FILE_get_ext(path);
if (!ext || !*ext)
{
GB.Error("No extension specified");
goto __END;
}
SYNCHRONIZE_IMAGE(THIS);
IMAGE.Convert(THIS, GB_IMAGE_RGBA);
image = gdk_pixbuf_new_from_data((const guchar *)THIS->data, GDK_COLORSPACE_RGB, TRUE, 8, THIS->width, THIS->height, THIS->width * sizeof(uint), NULL, NULL);
formats = gdk_pixbuf_get_formats();
iter = formats;
while (iter && (!ok) )
{
if (gdk_pixbuf_format_is_writable((GdkPixbufFormat*)iter->data))
{
format = gdk_pixbuf_format_get_name((GdkPixbufFormat*)iter->data);
if (!strcasecmp(format, ext))
{
ok = TRUE;
break;
}
else
g_free(format);
}
iter = iter->next;
}
g_slist_free(formats);
if (!ok)
{
if (!strcasecmp("jpg", ext))
format = (char *)"jpeg";
}
if (!format)
{
GB.Error("Unknown format");
goto __END;
}
if (quality >= 0)
{
if (strcmp(format, "jpeg") == 0)
{
if (quality > 100)
quality = 100;
sprintf(arg, "%d", quality);
b = gdk_pixbuf_save(image, path, format, &error, "quality", arg, (void *)NULL);
}
else if (strcmp(format, "png") == 0)
{
if (quality > 9)
quality = 9;
sprintf(arg, "%d", quality);
b = gdk_pixbuf_save(image, path, format, &error, "compression", arg, (void *)NULL);
}
else
b = gdk_pixbuf_save(image, path, format, &error, (void *)NULL);
}
else
b = gdk_pixbuf_save(image, path, format, &error, (void *)NULL);
if (ok)
g_free(format);
if (!b)
{
GB.Error(error->message);
goto __END;
}
__END:
if (image)
g_object_unref(G_OBJECT(image));
END_METHOD
static GdkPixbuf *stretch_pixbuf(GdkPixbuf *img, int w, int h)
{
GdkPixbuf *image = NULL;
int wimg, himg;
int ws, hs;
if (w <= 0 && h <= 0)
goto __RETURN;
wimg = gdk_pixbuf_get_width(img);
himg = gdk_pixbuf_get_height(img);
if (w < 0)
w = wimg * h / himg;
else if (h < 0)
h = himg * w / wimg;
if (w <= 0 || h <= 0)
goto __RETURN;
ws = w;
hs = h;
if (ws < (wimg / 4))
ws = w * 4;
if (hs < (himg / 4))
hs = h * 4;
if (ws != w || hs != h)
{
image = gdk_pixbuf_scale_simple(img, ws, hs, GDK_INTERP_NEAREST);
g_object_unref(G_OBJECT(img));
img = image;
}
image = gdk_pixbuf_scale_simple(img, w, h, GDK_INTERP_BILINEAR);
__RETURN:
g_object_unref(G_OBJECT(img));
return image;
}
BEGIN_METHOD(Image_Stretch, GB_INTEGER width; GB_INTEGER height)
GdkPixbuf *img;
GB_IMG *image;
SYNCHRONIZE_IMAGE(THIS);
IMAGE.Convert(THIS, GB_IMAGE_RGBA);
img = gdk_pixbuf_new_from_data((const guchar *)THIS->data, GDK_COLORSPACE_RGB, TRUE, 8, THIS->width, THIS->height, THIS->width * sizeof(uint), NULL, NULL);
img = stretch_pixbuf(img, VARG(width), VARG(height));
image = IMAGE.Create(gdk_pixbuf_get_width(img), gdk_pixbuf_get_height(img), GB_IMAGE_RGBA, gdk_pixbuf_get_pixels(img));
IMAGE.Convert(image, IMAGE.GetDefaultFormat());
GB.ReturnObject(image);
g_object_unref(G_OBJECT(img));
END_METHOD
GB_DESC CImageDesc[] =
{
GB_DECLARE("Image", sizeof(CIMAGE)),
GB_STATIC_METHOD("Load", "Image", Image_Load, "(Path)s"),
GB_STATIC_METHOD("FromString", "Image", Image_FromString, "(Data)s"),
GB_METHOD("Save", NULL, Image_Save, "(Path)s[(Quality)i]"),
GB_METHOD("Stretch", "Image", Image_Stretch, "(Width)i(Height)i"),
GB_END_DECLARE
};