X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=render%2Fimage.c;h=964864371825bb93c081de801d787d81d44a2a21;hb=c564b6245fcfaf0efd523cfa17bc1a73fe640d68;hp=d22ef9e39d023f7e3d2a2b175b05c15b8e319482;hpb=4c7cc1cfa64bf5722f059eae0528d510c2ae636f;p=chaz%2Fopenbox diff --git a/render/image.c b/render/image.c index d22ef9e3..96486437 100644 --- a/render/image.c +++ b/render/image.c @@ -28,6 +28,29 @@ #define FLOOR(i) ((i) & (~0UL << FRACTION)) #define AVERAGE(a, b) (((((a) ^ (b)) & 0xfefefefeL) >> 1) + ((a) & (b))) +void RrImagePicInit(RrImagePic *pic, gint w, gint h, RrPixel32 *data) +{ + gint i; + + pic->width = w; + pic->height = h; + pic->data = data; + pic->sum = 0; + for (i = w*h; i > 0; --i) + pic->sum += *(data++); +} + +static void RrImagePicFree(RrImagePic *pic) +{ + if (pic) { + g_free(pic->data); + g_free(pic); + } +} + +/*! Add a picture to an Image, that is, add another copy of the image at + another size. This may add it to the "originals" list or to the + "resized" list. */ static void AddPicture(RrImage *self, RrImagePic ***list, gint *len, RrImagePic *pic) { @@ -51,32 +74,33 @@ static void AddPicture(RrImage *self, RrImagePic ***list, gint *len, g_hash_table_insert(self->cache->table, (*list)[0], self); #ifdef DEBUG - g_print("Adding %s picture to the cache: " - "Image 0x%x, w %d h %d Hash %u\n", - (*list == self->original ? "ORIGINAL" : "RESIZED"), - (guint)self, pic->width, pic->height, RrImagePicHash(pic)); + g_message("Adding %s picture to the cache:\n " + "Image 0x%x, w %d h %d Hash %u", + (*list == self->original ? "ORIGINAL" : "RESIZED"), + (guint)self, pic->width, pic->height, RrImagePicHash(pic)); #endif } +/*! Remove a picture from an Image. This may remove it from the "originals" + list or the "resized" list. */ static void RemovePicture(RrImage *self, RrImagePic ***list, gint i, gint *len) { gint j; #ifdef DEBUG - g_print("Removing %s picture from the cache: " - "Image 0x%x, w %d h %d Hash %u\n", - (*list == self->original ? "ORIGINAL" : "RESIZED"), - (guint)self, (*list)[i]->width, (*list)[i]->height, - RrImagePicHash((*list)[i])); + g_message("Removing %s picture from the cache:\n " + "Image 0x%x, w %d h %d Hash %u", + (*list == self->original ? "ORIGINAL" : "RESIZED"), + (guint)self, (*list)[i]->width, (*list)[i]->height, + RrImagePicHash((*list)[i])); #endif /* remove the picture as a key in the cache */ g_hash_table_remove(self->cache->table, (*list)[i]); - /* free the picture (and its rgba data) */ - g_free((*list)[i]); - g_free((*list)[i]->data); + /* free the picture */ + RrImagePicFree((*list)[i]); /* shift everything down one */ for (j = i; j < *len-1; ++j) (*list)[j] = (*list)[j+1]; @@ -84,17 +108,28 @@ static void RemovePicture(RrImage *self, RrImagePic ***list, *list = g_renew(RrImagePic*, *list, --*len); } +/*! Given a picture in RGBA format, of a specified size, resize it to the new + requested size (but keep its aspect ratio). If the image does not need to + be resized (it is already the right size) then this returns NULL. Otherwise + it returns a newly allocated RrImagePic with the resized picture inside it +*/ static RrImagePic* ResizeImage(RrPixel32 *src, - gulong srcW, gulong srcH, - gulong dstW, gulong dstH) + gulong srcW, gulong srcH, + gulong dstW, gulong dstH) { - RrPixel32 *dst; + RrPixel32 *dst, *dststart; RrImagePic *pic; gulong dstX, dstY, srcX, srcY; gulong srcX1, srcX2, srcY1, srcY2; gulong ratioX, ratioY; gulong aspectW, aspectH; + /* XXX should these variables be ensured to not be zero in the callers? */ + srcW = srcW ? srcW : 1; + srcH = srcH ? srcH : 1; + dstW = dstW ? dstW : 1; + dstH = dstH ? dstH : 1; + /* keep the aspect ratio */ aspectW = dstW; aspectH = (gint)(dstW * ((gdouble)srcH / srcW)); @@ -102,17 +137,13 @@ static RrImagePic* ResizeImage(RrPixel32 *src, aspectH = dstH; aspectW = (gint)(dstH * ((gdouble)srcW / srcH)); } - dstW = aspectW; - dstH = aspectH; + dstW = aspectW ? aspectW : 1; + dstH = aspectH ? aspectH : 1; if (srcW == dstW && srcH == dstH) - return NULL; /* no scaling needed ! */ + return NULL; /* no scaling needed! */ - pic = g_new(RrImagePic, 1); - dst = g_new(RrPixel32, dstW * dstH); - pic->width = dstW; - pic->height = dstH; - pic->data = dst; + dststart = dst = g_new(RrPixel32, dstW * dstH); ratioX = (srcW << FRACTION) / dstW; ratioY = (srcH << FRACTION) / dstH; @@ -184,6 +215,9 @@ static RrImagePic* ResizeImage(RrPixel32 *src, } } + pic = g_new(RrImagePic, 1); + RrImagePicInit(pic, dstW, dstH, dststart); + return pic; } @@ -199,6 +233,8 @@ void DrawRGBA(RrPixel32 *target, gint target_w, gint target_h, gint dw, dh; g_assert(source_w <= area->width && source_h <= area->height); + g_assert(area->x + area->width <= target_w); + g_assert(area->y + area->height <= target_h); /* keep the aspect ratio */ dw = area->width; @@ -246,6 +282,7 @@ void DrawRGBA(RrPixel32 *target, gint target_w, gint target_h, } } +/*! Draw an RGBA texture into a target pixel buffer. */ void RrImageDrawRGBA(RrPixel32 *target, RrTextureRGBA *rgba, gint target_w, gint target_h, RrRect *area) @@ -263,6 +300,7 @@ void RrImageDrawRGBA(RrPixel32 *target, RrTextureRGBA *rgba, DrawRGBA(target, target_w, target_h, scaled->data, scaled->width, scaled->height, rgba->alpha, area); + RrImagePicFree(scaled); } else DrawRGBA(target, target_w, target_h, @@ -270,10 +308,13 @@ void RrImageDrawRGBA(RrPixel32 *target, RrTextureRGBA *rgba, rgba->alpha, area); } +/*! Create a new RrImage, which is linked to an image cache */ RrImage* RrImageNew(RrImageCache *cache) { RrImage *self; + g_assert(cache != NULL); + self = g_new0(RrImage, 1); self->ref = 1; self->cache = cache; @@ -289,8 +330,8 @@ void RrImageUnref(RrImage *self) { if (self && --self->ref == 0) { #ifdef DEBUG - g_print("Refcount to 0, removing ALL pictures from the cache: " - "Image 0x%x\n", (guint)self); + g_message("Refcount to 0, removing ALL pictures from the cache:\n " + "Image 0x%x", (guint)self); #endif while (self->n_original > 0) RemovePicture(self, &self->original, 0, &self->n_original); @@ -300,6 +341,9 @@ void RrImageUnref(RrImage *self) } } +/*! Add a new picture with the given RGBA pixel data and dimensions into the + RrImage. This adds an "original" picture to the image. +*/ void RrImageAddPicture(RrImage *self, RrPixel32 *data, gint w, gint h) { gint i; @@ -309,8 +353,8 @@ void RrImageAddPicture(RrImage *self, RrPixel32 *data, gint w, gint h) for (i = 0; i < self->n_original; ++i) if (self->original[i]->width == w && self->original[i]->height == h) { #ifdef DEBUG - g_print("Found duplicate ORIGINAL image: " - "Image 0x%x, w %d h %d\n", (guint)self, w, h); + g_message("Found duplicate ORIGINAL image:\n " + "Image 0x%x, w %d h %d", (guint)self, w, h); #endif return; } @@ -324,12 +368,13 @@ void RrImageAddPicture(RrImage *self, RrPixel32 *data, gint w, gint h) /* add the new picture */ pic = g_new(RrImagePic, 1); - pic->width = w; - pic->height = h; - pic->data = g_memdup(data, w*h*sizeof(RrPixel32)); + RrImagePicInit(pic, w, h, g_memdup(data, w*h*sizeof(RrPixel32))); AddPicture(self, &self->original, &self->n_original, pic); } +/*! Remove the picture from the RrImage which has the given dimensions. This + removes an "original" picture from the image. +*/ void RrImageRemovePicture(RrImage *self, gint w, gint h) { gint i; @@ -342,16 +387,23 @@ void RrImageRemovePicture(RrImage *self, gint w, gint h) } } +/*! Draw an RrImage texture into a target pixel buffer. If the RrImage does + not contain a picture of the appropriate size, then one of its "original" + pictures will be resized and used (and stored in the RrImage as a "resized" + picture). + */ void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img, gint target_w, gint target_h, RrRect *area) { - gint i, min_diff, min_i; + gint i, min_diff, min_i, min_aspect_diff, min_aspect_i; RrImage *self; RrImagePic *pic; + gboolean free_pic; self = img->image; pic = NULL; + free_pic = FALSE; /* is there an original of this size? (only w or h has to be right cuz we maintain aspect ratios) */ @@ -386,24 +438,43 @@ void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img, } if (!pic) { + gdouble aspect; + /* find an original with a close size */ - min_diff = -1; - min_i = 0; + min_diff = min_aspect_diff = -1; + min_i = min_aspect_i = 0; + aspect = ((gdouble)area->width) / area->height; for (i = 0; i < self->n_original; ++i) { gint diff; gint wdiff, hdiff; + gdouble myasp; /* our size difference metric.. */ wdiff = self->original[i]->width - area->width; hdiff = self->original[i]->height - area->height; diff = (wdiff * wdiff) + (hdiff * hdiff); + /* find the smallest difference */ if (min_diff < 0 || diff < min_diff) { min_diff = diff; min_i = i; } + /* and also find the smallest difference with the same aspect + ratio (and prefer this one) */ + myasp = ((gdouble)self->original[i]->width) / + self->original[i]->height; + if (ABS(aspect - myasp) < 0.0000001 && + (min_aspect_diff < 0 || diff < min_aspect_diff)) + { + min_aspect_diff = diff; + min_aspect_i = i; + } } + /* use the aspect ratio correct source if there is one */ + if (min_aspect_i >= 0) + min_i = min_aspect_i; + /* resize the original to the given area */ pic = ResizeImage(self->original[min_i]->data, self->original[min_i]->width, @@ -412,13 +483,15 @@ void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img, /* add the resized image to the image, as the first in the resized list */ - if (self->n_resized >= MAX_CACHE_RESIZED) { + if (self->n_resized >= self->cache->max_resized_saved) /* remove the last one (last used one) */ RemovePicture(self, &self->resized, self->n_resized - 1, &self->n_resized); - } - /* add it to the top of the resized list */ - AddPicture(self, &self->resized, &self->n_resized, pic); + if (self->cache->max_resized_saved) + /* add it to the top of the resized list */ + AddPicture(self, &self->resized, &self->n_resized, pic); + else + free_pic = TRUE; /* don't leak mem! */ } g_assert(pic != NULL); @@ -426,4 +499,6 @@ void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img, DrawRGBA(target, target_w, target_h, pic->data, pic->width, pic->height, img->alpha, area); + if (free_pic) + RrImagePicFree(pic); }