Fix some typos in comments.
[mikachu/openbox.git] / render / image.c
index d22ef9e..6fbd8a8 100644 (file)
 #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,11 +108,16 @@ 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;
@@ -108,11 +137,7 @@ static RrImagePic* ResizeImage(RrPixel32 *src,
     if (srcW == dstW && srcH == dstH)
         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 +209,9 @@ static RrImagePic* ResizeImage(RrPixel32 *src,
         }
     }
 
+    pic = g_new(RrImagePic, 1);
+    RrImagePicInit(pic, dstW, dstH, dststart);
+
     return pic;
 }
 
@@ -199,6 +227,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 +276,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)
@@ -270,10 +301,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 +323,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 +334,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 +346,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 +361,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 +380,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 +431,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 +476,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 +492,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);
 }