/* * 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 #include #if USE_XRENDER #include #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 */