e16/src/eimage.c

750 lines
17 KiB
C

/*
* Copyright (C) 2004-2021 Kim Woelders
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies of the Software, its documentation and marketing & publicity
* materials, and acknowledgment shall be given in the documentation, materials
* and software packages that this Software was used.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <Imlib2.h>
#include <X11/Xlib.h>
#if USE_XRENDER
#include <X11/extensions/Xrender.h>
#endif
#include "E.h"
#include "eimage.h"
#include "file.h"
#include "xwin.h"
#define IMG_CACHE_DEFAULT_SIZE (2 * 1024 * 1024)
#define XIM_CACHE_DEFAULT_COUNT 0
void
EImageInit(void)
{
EImageSetCacheSize(Conf.testing.image_cache_size);
EImageSetXImageCacheSize(Conf.testing.ximage_cache_count, -1);
imlib_set_font_cache_size(512 * 1024);
imlib_set_color_usage(128);
imlib_context_set_display(disp);
imlib_context_set_visual(WinGetVisual(VROOT));
imlib_context_set_colormap(WinGetCmap(VROOT));
#ifdef HAVE_IMLIB_CONTEXT_SET_MASK_ALPHA_THRESHOLD
imlib_context_set_mask_alpha_threshold(Conf.testing.mask_alpha_threshold);
#endif
imlib_context_set_anti_alias(0);
imlib_context_set_dither(1);
}
void
EImageExit(int quit __UNUSED__)
{
#if HAVE_IMLIB_CONTEXT_DISCONNECT_DISPLAY
imlib_context_disconnect_display();
#endif
}
void
EImageSetCacheSize(int size)
{
Conf.testing.image_cache_size = size;
if (size < 0)
size = IMG_CACHE_DEFAULT_SIZE;
imlib_set_cache_size(size);
}
void
EImageSetXImageCacheSize(int count, int size __UNUSED__)
{
Conf.testing.ximage_cache_count = count;
#if HAVE_IMLIB_XIMAGE_CACHE_CONTROL
if (count < 0)
count = XIM_CACHE_DEFAULT_COUNT;
imlib_set_ximage_cache_count_max(count);
#endif
}
void
EImageGetCacheInfo(ECacheInfo * ci)
{
memset(ci, 0, sizeof(ECacheInfo));
ci->img.max_mem = imlib_get_cache_size();
#if HAVE_IMLIB_XIMAGE_CACHE_CONTROL
ci->img.used_mem = imlib_get_cache_used();
ci->xim.max_mem = imlib_get_ximage_cache_size_max();
ci->xim.used_mem = imlib_get_ximage_cache_size_used();
ci->xim.max_cnt = imlib_get_ximage_cache_count_max();
ci->xim.used_cnt = imlib_get_ximage_cache_count_used();
#endif
}
static void
_EImageFlagsSet(int flags)
{
if (flags & EIMAGE_ANTI_ALIAS)
imlib_context_set_anti_alias(1);
if (flags & EIMAGE_BLEND)
imlib_context_set_blend(1);
#ifdef HAVE_IMLIB_CONTEXT_SET_MASK_ALPHA_THRESHOLD
if (flags & EIMAGE_HIGH_MASK_THR)
imlib_context_set_mask_alpha_threshold(128);
#endif
}
static void
_EImageFlagsReset(void)
{
imlib_context_set_anti_alias(0);
imlib_context_set_blend(0);
#ifdef HAVE_IMLIB_CONTEXT_SET_MASK_ALPHA_THRESHOLD
imlib_context_set_mask_alpha_threshold(Conf.testing.mask_alpha_threshold);
#endif
}
EImage *
EImageLoad(const char *file)
{
return imlib_load_image(file);
}
EImage *
EImageLoadOrientate(const char *file, int orientation)
{
EImage *im, *im2;
char name[2048], path[4096], *s, *s2;
snprintf(name, sizeof(name), "%s", file);
for (s = name, s2 = NULL; *s; s++)
{
if (*s == '/')
*s = '.';
else if (*s == '.')
s2 = s;
}
if (s2)
*s2 = '\0';
snprintf(path, sizeof(path), "%s/cached/cfg/%s-%d.png",
EDirUserCache(), name, orientation);
if (!exists(path))
{
im = imlib_load_image(file);
if (!im)
return NULL;
imlib_context_set_image(im);
im2 = imlib_clone_image();
imlib_free_image();
imlib_context_set_image(im2);
imlib_image_orientate(orientation);
imlib_image_set_format("png");
imlib_save_image(path);
imlib_free_image();
}
im = imlib_load_image(path);
return im;
}
void
EImageSave(EImage * im, const char *file)
{
imlib_context_set_image(im);
imlib_image_set_format("png");
imlib_save_image(file);
}
EImage *
EImageCreate(int w, int h)
{
EImage *im;
im = imlib_create_image(w, h);
return im;
}
EImage *
EImageCreateFromData(int w, int h, unsigned int *data)
{
EImage *im;
im = imlib_create_image_using_copied_data(w, h, data);
return im;
}
EImage *
EImageCreateScaled(EImage * im, int sx, int sy, int sw, int sh, int dw, int dh)
{
imlib_context_set_image(im);
if (sw <= 0)
sw = imlib_image_get_width();
if (sh <= 0)
sh = imlib_image_get_height();
if (dw <= 0)
dw = sw;
if (dh <= 0)
dh = sh;
return imlib_create_cropped_scaled_image(sx, sy, sw, sh, dw, dh);
}
void
EImageFree(EImage * im)
{
imlib_context_set_image(im);
imlib_free_image();
}
void
EImageDecache(EImage * im)
{
imlib_context_set_image(im);
imlib_free_image_and_decache();
}
static int
_EImageCheckAlpha(void)
{
static const short oink = 3; /* For endianness checking */
unsigned char *pb, *pe;
if (!imlib_image_has_alpha())
return 0;
pb = (unsigned char *)imlib_image_get_data_for_reading_only();
if (!pb)
return 0;
pe = pb + 4 * imlib_image_get_width() * imlib_image_get_height();
pb += *((char *)(&oink));
for (; pb < pe; pb += 4)
if (*pb != 0xff)
return 1;
return 0;
}
void
EImageCheckAlpha(EImage * im)
{
imlib_context_set_image(im);
if (imlib_image_has_alpha() && !_EImageCheckAlpha())
{
#if 0
Eprintf("Alpha set but no shape %s\n", is->real_file);
#endif
imlib_image_set_has_alpha(0);
}
}
void
EImageSetHasAlpha(EImage * im, int has_alpha)
{
imlib_context_set_image(im);
imlib_image_set_has_alpha(has_alpha);
}
void
EImageSetBorder(EImage * im, EImageBorder * border)
{
Imlib_Border ib;
ib.left = border->left;
ib.right = border->right;
ib.top = border->top;
ib.bottom = border->bottom;
imlib_context_set_image(im);
imlib_image_set_border(&ib);
if (EDebug(1))
{
int l;
l = imlib_image_get_width();
if ((l < ib.left + ib.right) || (ib.left < 0) || (ib.left < 0))
Eprintf("%s: %s: Image W = %d < border L+R = %d+%d\n", __func__,
imlib_image_get_filename(), l, ib.left, ib.right);
l = imlib_image_get_height();
if ((l < ib.top + ib.bottom) || (ib.top < 0) || (ib.bottom < 0))
Eprintf("%s: %s: Image H = %d < border T+B = %d+%d\n", __func__,
imlib_image_get_filename(), l, ib.top, ib.bottom);
}
}
int
EImageHasAlpha(EImage * im)
{
imlib_context_set_image(im);
return imlib_image_has_alpha();
}
void
EImageGetSize(EImage * im, int *pw, int *ph)
{
imlib_context_set_image(im);
*pw = imlib_image_get_width();
*ph = imlib_image_get_height();
}
void *
EImageGetData(EImage * im)
{
imlib_context_set_image(im);
return imlib_image_get_data_for_reading_only();
}
void
EImageFill(EImage * im, int x, int y, int w, int h, unsigned int color)
{
int a, r, g, b;
imlib_context_set_image(im);
COLOR32_TO_ARGB(color, a, r, g, b);
imlib_context_set_color(r, g, b, a);
imlib_context_set_blend(0);
imlib_image_fill_rectangle(x, y, w, h);
}
void
EImageOrientate(EImage * im, int orientation)
{
imlib_context_set_image(im);
imlib_image_orientate(orientation);
}
void
EImageBlend(EImage * im, EImage * src, int flags,
int sx, int sy, int sw, int sh,
int dx, int dy, int dw, int dh, int merge_alpha)
{
imlib_context_set_image(im);
if (flags)
_EImageFlagsSet(flags);
imlib_blend_image_onto_image(src, merge_alpha, sx, sy, sw, sh,
dx, dy, dw, dh);
if (flags)
_EImageFlagsReset();
}
void
EImageTile(EImage * im, EImage * tile, int flags, int tw, int th,
int dx, int dy, int dw, int dh, int ox, int oy)
{
Imlib_Image tim;
int x, y, tx, ty, ww, hh;
int sw, sh;
if (tw <= 0 || th <= 0)
return;
if (flags)
_EImageFlagsSet(flags);
imlib_context_set_image(tile);
sw = imlib_image_get_width();
sh = imlib_image_get_height();
if (sw == tw && sh == th)
{
tim = tile;
}
else
{
tim = imlib_create_image(tw, th);
imlib_context_set_image(tim);
imlib_context_set_blend(0);
imlib_context_set_anti_alias(1);
imlib_blend_image_onto_image(tile, 0, 0, 0, sw, sh, 0, 0, tw, th);
imlib_context_set_anti_alias(0);
}
imlib_context_set_image(im);
if (ox)
{
ox = tw - ox;
ox %= tw;
if (ox < 0)
ox += tw;
}
if (oy)
{
oy = th - oy;
oy %= th;
if (oy < 0)
oy += th;
}
dw += dx;
dh += dy;
y = dy;
ty = oy;
hh = th - oy;
for (;;)
{
if (y + hh >= dh)
hh = dh - y;
if (hh <= 0)
break;
x = dx;
tx = ox;
ww = tw - ox;
for (;;)
{
if (x + ww >= dw)
ww = dw - x;
if (ww <= 0)
break;
imlib_blend_image_onto_image(tim, 0, tx, ty, ww, hh, x, y, ww, hh);
tx = 0;
x += ww;
ww = tw;
}
ty = 0;
y += hh;
hh = th;
}
if (tim != tile)
{
imlib_context_set_image(tim);
imlib_free_image();
imlib_context_set_image(im); /* FIXME - Remove */
}
if (flags)
_EImageFlagsReset();
}
EImage *
EImageGrabDrawable(EX_Drawable draw, EX_Pixmap mask, int x, int y, int w,
int h, int grab)
{
EImage *im;
EX_Colormap cm;
cm = imlib_context_get_colormap();
imlib_context_set_colormap(NoXID); /* Fix for grabbing bitmaps */
imlib_context_set_drawable(draw);
im = imlib_create_image_from_drawable(mask, x, y, w, h, grab);
imlib_context_set_colormap(cm);
return im;
}
EImage *
EImageGrabDrawableScaled(Win win, EX_Drawable draw, EX_Pixmap mask,
int x, int y, int w, int h,
int iw, int ih, int grab, int get_mask_from_shape)
{
EImage *im;
Visual *vis;
imlib_context_set_drawable(draw);
vis = (win) ? WinGetVisual(win) : NULL;
if (vis)
imlib_context_set_visual(vis);
im = imlib_create_scaled_image_from_drawable(mask, x, y, w, h, iw, ih, grab,
get_mask_from_shape);
if (vis)
imlib_context_set_visual(WinGetVisual(VROOT));
return im;
}
void
EImageRenderOnDrawable(EImage * im, Win win, EX_Drawable draw, int flags,
int x, int y, int w, int h)
{
Visual *vis;
imlib_context_set_image(im);
imlib_context_set_drawable((draw != NoXID) ? draw : WinGetXwin(win));
vis = (win) ? WinGetVisual(win) : NULL;
if (vis)
imlib_context_set_visual(vis);
if (flags)
_EImageFlagsSet(flags);
imlib_render_image_on_drawable_at_size(x, y, w, h);
if (flags)
_EImageFlagsReset();
if (vis)
imlib_context_set_visual(WinGetVisual(VROOT));
}
#if USE_XRENDER
void
EImageRenderOnDrawableARGB(EImage * im, EX_Drawable draw, int w, int h)
{
Visual *vis;
imlib_context_set_image(im);
imlib_context_set_drawable(draw);
vis = EVisualFindARGB();
if (vis)
imlib_context_set_visual(vis);
imlib_render_image_on_drawable_at_size(0, 0, w, h);
if (vis)
imlib_context_set_visual(WinGetVisual(VROOT));
}
#endif
void
EImageRenderPixmaps(EImage * im, Win win, int flags,
EX_Pixmap * ppmap, EX_Pixmap * pmask, int w, int h)
{
Visual *vis;
Pixmap pmap, mask, *pm;
imlib_context_set_image(im);
imlib_context_set_drawable((win) ? WinGetXwin(win) : WinGetXwin(VROOT));
vis = (win) ? WinGetVisual(win) : NULL;
if (vis)
imlib_context_set_visual(vis);
pmap = mask = NoXID;
pm = pmask ? &mask : NULL;
if (flags)
_EImageFlagsSet(flags);
if (w <= 0 || h <= 0)
imlib_render_pixmaps_for_whole_image(&pmap, pm);
else
imlib_render_pixmaps_for_whole_image_at_size(&pmap, pm, w, h);
if (flags)
_EImageFlagsReset();
if (vis)
imlib_context_set_visual(WinGetVisual(VROOT));
*ppmap = pmap;
if (pmask)
*pmask = mask;
}
void
EImagePixmapsFree(EX_Pixmap pmap, EX_Pixmap mask __UNUSED__)
{
imlib_free_pixmap_and_mask(pmap);
}
void
EImageApplyToWin(EImage * im, Win win, int flags, int w, int h)
{
EX_Pixmap pmap, mask;
EImageRenderPixmaps(im, win, flags, &pmap, &mask, w, h);
ESetWindowBackgroundPixmap(win, pmap, 0);
if ((mask != NoXID) || (mask == NoXID && WinIsShaped(win)))
EShapeSetMask(win, 0, 0, mask);
EImagePixmapsFree(pmap, mask);
EClearWindow(win);
}
void
ScaleRect(Win wsrc, EX_Drawable src, Win wdst, EX_Pixmap dst,
int sx, int sy, int sw, int sh,
int dx, int dy, int dw, int dh, int flags)
{
#if USE_XRENDER
if (Conf.testing.use_render_for_scaling)
{
XTransform tr;
EX_Picture psrc, pdst;
double scale_x, scale_y;
scale_x = (double)sw / (double)dw;
scale_y = (double)sh / (double)dh;
memset(&tr, 0, sizeof(tr));
tr.matrix[0][0] = XDoubleToFixed(scale_x);
tr.matrix[1][1] = XDoubleToFixed(scale_y);
tr.matrix[2][2] = XDoubleToFixed(1.);
psrc = EPictureCreateII(wsrc, src);
pdst = EPictureCreateII(wdst, dst);
XRenderSetPictureFilter(disp, psrc, (flags & EIMAGE_ANTI_ALIAS) ?
FilterBest : FilterNearest, NULL, 0);
XRenderSetPictureTransform(disp, psrc, &tr);
XRenderComposite(disp, PictOpSrc, psrc, NoXID, pdst,
(int)(sx / scale_x + .5), (int)(sy / scale_y + .5),
0, 0, dx, dy, dw, dh);
XRenderFreePicture(disp, psrc);
XRenderFreePicture(disp, pdst);
}
else
#endif
{
int scale;
Imlib_Image im;
if (flags & (EIMAGE_ISCALE))
{
scale = (flags & EIMAGE_ISCALE) >> 8;
im = EImageGrabDrawableScaled(wsrc, src, NoXID, sx, sy, sw, sh,
scale * dw, scale * dh, 0, 0);
flags |= EIMAGE_ANTI_ALIAS;
}
else
{
im = EImageGrabDrawableScaled(wsrc, src, NoXID, sx, sy, sw, sh,
sw, sh, 0, 0);
}
EImageRenderOnDrawable(im, wdst, dst, flags, dx, dy, dw, dh);
imlib_free_image();
}
}
void
ScaleTile(Win wsrc, EX_Drawable src, Win wdst, EX_Pixmap dst,
int dx, int dy, int dw, int dh, int scale)
{
Imlib_Image im, tim;
int sw, sh, stw, sth, tw, th;
sw = WinGetW(wsrc);
sh = WinGetH(wsrc);
EXGetSize(src, &stw, &sth);
if (stw >= sw && sth >= sh)
{
ScaleRect(wsrc, src, wdst, dst, 0, 0, sw, sh, dx, dy, dw, dh, scale);
return;
}
/* Source Drawawble is smaller than source window - do scaled tiling */
scale = (scale) ? 2 : 1;
tw = (int)((float)(stw * scale * dw) / sw + .5f);
th = (int)((float)(sth * scale * dh) / sh + .5f);
#if 0
Eprintf("%s: Tile %#x %dx%d -> %dx%d T %dx%d -> %dx%d\n", __func__,
src, stw, sth, tw, th, scale * dw, scale * dh, dw, dh);
#endif
tim =
EImageGrabDrawableScaled(wsrc, src, NoXID, 0, 0, stw, sth, tw, th, 0, 0);
im = EImageCreate(scale * dw, scale * dh);
EImageTile(im, tim, 0, tw, th, 0, 0, scale * dw, scale * dh, 0, 0);
EImageFree(tim);
EImageRenderOnDrawable(im, wdst, dst, EIMAGE_ANTI_ALIAS, dx, dy, dw, dh);
imlib_free_image();
}
#if 0 /* Unused */
void
EDrawableDumpImage(EX_Drawable draw, const char *txt)
{
static int seqn = 0;
char buf[1024];
Imlib_Image im;
int w, h;
EXGetSize(draw, &w, &h);
if (w <= 0 || h <= 0)
return;
im = EImageGrabDrawableScaled(ELookupXwin(draw), draw, NoXID,
0, 0, w, h, w, h, 0, 0);
imlib_context_set_image(im);
imlib_image_set_format("png");
sprintf(buf, "%s-%#x-%d.png", txt, draw, seqn++);
Eprintf("%s: %s\n", __func__, buf);
imlib_save_image(buf);
imlib_free_image_and_decache();
}
#endif
void
PmapMaskInit(PmapMask * pmm, Win win, int w, int h)
{
if (pmm->pmap)
{
if (pmm->w == w && pmm->h == h && pmm->depth == WinGetDepth(win))
return;
PmapMaskFree(pmm);
}
pmm->type = 0;
pmm->depth = WinGetDepth(win);
pmm->pmap = ECreatePixmap(win, w, h, 0);
pmm->mask = NoXID;
pmm->w = w;
pmm->h = h;
}
void
PmapMaskFree(PmapMask * pmm)
{
/* type !=0: Created by imlib_render_pixmaps_for_whole_image... */
if (pmm->pmap)
{
if (pmm->type == 0)
EFreePixmap(pmm->pmap);
else
imlib_free_pixmap_and_mask(pmm->pmap);
pmm->pmap = 0;
}
if (pmm->mask)
{
if (pmm->type == 0)
EFreePixmap(pmm->mask);
pmm->mask = 0;
}
}
#if USE_XRENDER
EX_Cursor
EImageDefineCursor(EImage * im, int xh, int yh)
{
EX_Cursor curs;
int w, h;
EX_Pixmap pmap;
EX_Picture pict;
EImageGetSize(im, &w, &h);
pict = EPictureCreateBuffer(RROOT, w, h, 1, &pmap);
EImageRenderOnDrawableARGB(im, pmap, w, h);
EFreePixmap(pmap);
curs = XRenderCreateCursor(disp, pict, xh, yh);
XRenderFreePicture(disp, pict);
return curs;
}
#endif /* USE_XRENDER */