efl/legacy/evas/src/modules/engines/software_16/evas_soft16_main.c

484 lines
11 KiB
C

#include "evas_soft16.h"
#define IMG_BYTE_SIZE(stride, height, has_alpha) \
((stride) * (height) * (!(has_alpha) ? 2 : 3))
static Evas_Hash *_soft16_image_cache_hash = NULL;
static inline int
_calc_stride(int w)
{
int pad;
pad = w % 4;
if (!pad) return w;
else return w + 4 - pad;
}
static void
soft16_image_alloc_pixels(Soft16_Image *im)
{
if (im->pixels) return;
im->pixels = malloc(IMG_BYTE_SIZE(im->stride, im->h, im->have_alpha));
if (!im->pixels) return;
if (im->have_alpha) im->alpha = (DATA8 *)(im->pixels + (im->stride * im->h));
im->free_pixels = 1;
}
static Soft16_Image *
soft16_image_alloc(int w, int h, int stride, int have_alpha, int have_pixels)
{
Soft16_Image *im;
im = calloc(1, sizeof(Soft16_Image));
if (!im) return NULL;
im->w = w;
im->h = h;
im->stride = stride;
im->have_alpha = have_alpha;
im->references = 1;
if (!have_pixels) return im;
soft16_image_alloc_pixels(im);
if (!im->pixels)
{
free(im);
return NULL;
}
return im;
}
Soft16_Image *
soft16_image_new(int w, int h, int stride, int have_alpha, DATA16 *pixels,
int copy)
{
Soft16_Image *im;
im = soft16_image_alloc(w, h, stride, have_alpha, copy);
if (!im) return NULL;
if (copy) memcpy(im->pixels, pixels, IMG_BYTE_SIZE(stride, h, have_alpha));
else
{
im->pixels = pixels;
if (have_alpha) im->alpha = (DATA8 *)(im->pixels + (stride * h));
}
return im;
}
static void
soft16_image_cache_key(const Evas_Image_Load_Opts *lo, const char *key,
const char *file, char *buf, unsigned bufsize)
{
if ((!lo) ||
((lo->scale_down_by == 0) && (lo->dpi == 0.0) &&
((lo->w == 0) || (lo->h == 0))))
{
if (key) snprintf(buf, bufsize, "%s//://%s", file, key);
else strncpy(buf, file, bufsize);
}
else
{
if (key)
snprintf(buf, bufsize, "//@/%i/%1.5f/%ix%i//%s//://%s",
lo->scale_down_by, lo->dpi, lo->w, lo->h,
file, key);
else
snprintf(buf, bufsize, "//@/%i/%1.5f/%ix%i//%s",
lo->scale_down_by, lo->dpi, lo->w, lo->h,
file);
}
}
static inline void
soft16_image_cache_key_from_img(const Soft16_Image *im, char *buf,
unsigned bufsize)
{
soft16_image_cache_key(&im->lo, im->key, im->file, buf, bufsize);
}
void
soft16_image_free(Soft16_Image *im)
{
if (!im) return;
im->references--;
if (im->references > 0) return;
if (im->file)
{
char buf[4096 + 1024];
soft16_image_cache_key_from_img(im, buf, sizeof(buf));
_soft16_image_cache_hash = evas_hash_del(_soft16_image_cache_hash,
buf, im);
}
if (im->file) evas_stringshare_del(im->file);
if (im->key) evas_stringshare_del(im->key);
if (im->free_pixels) free(im->pixels);
free(im);
}
#define STAT_GAP 2
static Soft16_Image *
soft16_image_cache_get(const char *cache_key)
{
Soft16_Image *im;
im = evas_hash_find(_soft16_image_cache_hash, cache_key);
if (im)
{
time_t t;
t = time(NULL);
if ((t - im->laststat) > STAT_GAP)
{
struct stat st;
if (stat(im->file, &st) < 0) return NULL;
if (st.st_mtime != im->timestamp) return NULL;
im->laststat = t;
}
im->references++;
}
return im;
}
static Soft16_Image *
soft16_image_load_new(const char *file, const char *key,
Evas_Image_Load_Opts *lo)
{
Soft16_Image *im;
RGBA_Image *sim;
int have_alpha;
sim = evas_common_load_image_from_file(file, key, lo);
if (!sim) return NULL;
have_alpha = !!(sim->flags & RGBA_IMAGE_HAS_ALPHA);
im = soft16_image_alloc(sim->image->w, sim->image->h,
_calc_stride(sim->image->w), have_alpha, 0);
if (!im)
{
evas_cache_image_drop(sim);
return NULL;
}
im->source_im = sim;
im->timestamp = im->source_im->timestamp;
im->laststat = im->source_im->laststat;
if (lo) im->lo = *lo;
if (file) im->file = evas_stringshare_add(file);
if (key) im->key = evas_stringshare_add(key);
return im;
}
Soft16_Image *
soft16_image_load(const char *file, const char *key, int *error,
Evas_Image_Load_Opts *lo)
{
Soft16_Image *im;
char buf[4096 + 1024];
*error = 0;
if (!file) return NULL;
soft16_image_cache_key(lo, key, file, buf, sizeof(buf));
im = soft16_image_cache_get(buf);
if (im) return im;
im = soft16_image_load_new(file, key, lo);
if (im) _soft16_image_cache_hash = evas_hash_add(_soft16_image_cache_hash, buf, im);
return im;
}
static void
_soft16_image_rgba32_import(Soft16_Image *im, DATA32 *src)
{
DATA32 *sp;
DATA16 *dpl;
/* FIXME: dither and optimize */
sp = src;
dpl = im->pixels;
if (im->alpha)
{
DATA8 *dal;
dal = im->alpha;
int y;
for (y = 0; y < im->h; y++)
{
DATA16 *dp, *dp_end;
DATA8 *da;
dp = dpl;
dp_end = dp + im->w;
da = dal;
for (; dp < dp_end; da++, dp++, sp++)
{
*da = A_VAL(sp) >> 3;
*dp = RGB_565_FROM_COMPONENTS(R_VAL(sp), G_VAL(sp), B_VAL(sp));
}
dpl += im->stride;
dal += im->stride;
}
}
else
{
int y;
for (y = 0; y < im->h; y++)
{
DATA16 *dp, *dp_end;
dp = dpl;
dp_end = dp + im->w;
for (; dp < dp_end; dp++, sp++)
*dp = RGB_565_FROM_COMPONENTS(R_VAL(sp), G_VAL(sp), B_VAL(sp));
dpl += im->stride;
}
}
}
void
soft16_image_load_data(Soft16_Image *im)
{
if (!im) return;
if (im->pixels) return;
if (!im->source_im) return;
evas_common_load_image_data_from_file(im->source_im);
if (im->source_im->image->data)
{
if (!im->pixels) soft16_image_alloc_pixels(im);
if (im->pixels) _soft16_image_rgba32_import(im, im->source_im->image->data);
}
done:
evas_cache_image_drop(im->source_im);
im->source_im = NULL;
}
static inline void
_get_clip(const RGBA_Draw_Context *dc, const Soft16_Image *im,
Evas_Rectangle *clip)
{
if (dc->clip.use)
{
clip->x = dc->clip.x;
clip->y = dc->clip.y;
clip->w = dc->clip.w;
clip->h = dc->clip.h;
if (clip->x < 0)
{
clip->w += clip->x;
clip->x = 0;
}
if (clip->y < 0)
{
clip->h += clip->y;
clip->y = 0;
}
if ((clip->x + clip->w) > im->w) clip->w = im->w - clip->x;
if ((clip->y + clip->h) > im->h) clip->h = im->h - clip->y;
}
else
{
clip->x = 0;
clip->y = 0;
clip->w = im->w;
clip->h = im->h;
}
}
static inline int
_is_empty_rectangle(const Evas_Rectangle *r)
{
return (r->w < 1) || (r->h < 1);
}
static inline void
_shrink(int *s_pos, int *s_size, int pos, int size)
{
int d;
d = (*s_pos) - pos;
if (d < 0)
{
(*s_size) += d;
(*s_pos) = pos;
}
d = size + pos - (*s_pos);
if ((*s_size) > d)
(*s_size) = d;
}
static int
_soft16_adjust_areas(Evas_Rectangle *src,
int src_max_x, int src_max_y,
Evas_Rectangle *dst,
int dst_max_x, int dst_max_y,
Evas_Rectangle *dst_clip)
{
if (_is_empty_rectangle(src) ||
_is_empty_rectangle(dst) ||
_is_empty_rectangle(dst_clip))
return 0;
/* shrink clip */
_shrink(&dst_clip->x, &dst_clip->w, dst->x, dst->w);
_shrink(&dst_clip->y, &dst_clip->h, dst->y, dst->h);
if (_is_empty_rectangle(dst_clip)) return 0;
/* sanitise x */
if (src->x < 0)
{
dst->x -= (src->x * dst->w) / src->w;
dst->w += (src->x * dst->w) / src->w;
src->w += src->x;
src->x = 0;
}
if (src->x >= src_max_x) return 0;
if ((src->x + src->w) > src_max_x)
{
dst->w = (dst->w * (src_max_x - src->x)) / (src->w);
src->w = src_max_x - src->x;
}
if (dst->w <= 0) return 0;
if (src->w <= 0) return 0;
if (dst_clip->x < 0)
{
dst_clip->w += dst_clip->x;
dst_clip->x = 0;
}
if (dst_clip->w <= 0) return 0;
if (dst_clip->x >= dst_max_x) return 0;
_shrink(&dst_clip->x, &dst_clip->w, dst->x, dst_max_x);
if (dst_clip->w <= 0) return 0;
/* sanitise y */
if (src->y < 0)
{
dst->y -= (src->y * dst->h) / src->h;
dst->h += (src->y * dst->h) / src->h;
src->h += src->y;
src->y = 0;
}
if (src->y >= src_max_y) return 0;
if ((src->y + src->h) > src_max_y)
{
dst->h = (dst->h * (src_max_y - src->y)) / (src->h);
src->h = src_max_y - src->y;
}
if (dst->h <= 0) return 0;
if (src->h <= 0) return 0;
if (dst_clip->y < 0)
{
dst_clip->h += dst_clip->y;
dst_clip->y = 0;
}
if (dst_clip->h <= 0) return 0;
if (dst_clip->y >= dst_max_y) return 0;
_shrink(&dst_clip->y, &dst_clip->h, dst->y, dst_max_y);
if (dst_clip->h <= 0) return 0;
return 1;
}
static void
_soft16_image_draw_sampled_int(Soft16_Image *src, Soft16_Image *dst,
RGBA_Draw_Context *dc,
Evas_Rectangle sr, Evas_Rectangle dr)
{
Evas_Rectangle cr;
if (!(RECTS_INTERSECT(dr.x, dr.y, dr.w, dr.h, 0, 0, dst->w, dst->h)))
return;
if (!(RECTS_INTERSECT(sr.x, sr.y, sr.w, sr.h, 0, 0, src->w, src->h)))
return;
_get_clip(dc, dst, &cr);
if (!_soft16_adjust_areas(&sr, src->w, src->h, &dr, dst->w, dst->h, &cr))
return;
if ((dr.w == sr.w) && (dr.h == sr.h))
soft16_image_draw_unscaled(src, dst, dc, sr, dr, cr);
else
soft16_image_draw_scaled_sampled(src, dst, dc, sr, dr, cr);
}
void
soft16_image_draw(Soft16_Image *src, Soft16_Image *dst,
RGBA_Draw_Context *dc,
int src_region_x, int src_region_y,
int src_region_w, int src_region_h,
int dst_region_x, int dst_region_y,
int dst_region_w, int dst_region_h,
int smooth)
{
Evas_Rectangle sr, dr;
Cutout_Rects *rects;
Cutout_Rect *r;
struct RGBA_Draw_Context_clip clip_bkp;
int i;
/* handle cutouts here! */
dr.x = dst_region_x;
dr.y = dst_region_y;
dr.w = dst_region_w;
dr.h = dst_region_h;
if (_is_empty_rectangle(&dr)) return;
if (!(RECTS_INTERSECT(dr.x, dr.y, dr.w, dr.h, 0, 0, dst->w, dst->h)))
return;
sr.x = src_region_x;
sr.y = src_region_y;
sr.w = src_region_w;
sr.h = src_region_h;
if (_is_empty_rectangle(&sr)) return;
if (!(RECTS_INTERSECT(sr.x, sr.y, sr.w, sr.h, 0, 0, src->w, src->h)))
return;
/* no cutouts - cut right to the chase */
if (!dc->cutout.rects)
{
_soft16_image_draw_sampled_int(src, dst, dc, sr, dr);
return;
}
/* save out clip info */
clip_bkp = dc->clip;
evas_common_draw_context_clip_clip(dc, 0, 0, dst->w, dst->h);
evas_common_draw_context_clip_clip(dc, dst_region_x, dst_region_y, dst_region_w, dst_region_h);
/* our clip is 0 size.. abort */
if ((dc->clip.w <= 0) || (dc->clip.h <= 0))
{
dc->clip = clip_bkp;
return;
}
rects = evas_common_draw_context_apply_cutouts(dc);
for (i = 0; i < rects->active; i++)
{
r = rects->rects + i;
evas_common_draw_context_set_clip(dc, r->x, r->y, r->w, r->h);
_soft16_image_draw_sampled_int(src, dst, dc, sr, dr);
}
evas_common_draw_context_apply_clear_cutouts(rects);
dc->clip = clip_bkp;
}