1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 image.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
23 #include "imagecache.h"
31 #define FLOOR(i) ((i) & (~0UL << FRACTION))
32 #define AVERAGE(a, b) (((((a) ^ (b)) & 0xfefefefeL) >> 1) + ((a) & (b)))
34 /************************************************************************
37 RrImagePics are pictures that are grouped together into RrImageSets. Each
38 RrImagePic in the set has the same logical image inside it, but they are
39 of different sizes. An RrImagePic can be an original (which comes from some
40 outside source, such as an image file), or resized from some other RrImagePic
41 to meet the needs of the user.
42 **************************************************************************/
45 /*! Set up an RrImagePic.
46 This does _not_ make a copy of the data. So the value of data must be
47 owned by the caller of this function, and not freed afterward.
48 This function does not allocate an RrImagePic, and can be used for setting
49 up a temporary RrImagePic on the stack. Such an object would then also
50 not be freed with RrImagePicFree.
52 static void RrImagePicInit(RrImagePic
*pic
, gint w
, gint h
, RrPixel32
*data
)
60 for (i
= w
*h
; i
> 0; --i
)
61 pic
->sum
+= *(data
++);
64 /*! Create a new RrImagePic from some picture data.
65 This makes a duplicate of the data.
67 static RrImagePic
* RrImagePicNew(gint w
, gint h
, RrPixel32
*data
)
71 pic
= g_slice_new(RrImagePic
);
72 RrImagePicInit(pic
, w
, h
, g_memdup(data
, w
*h
*sizeof(RrPixel32
)));
77 /*! Destroy an RrImagePic.
78 This frees the RrImagePic object and everything inside it.
80 static void RrImagePicFree(RrImagePic
*pic
)
84 g_slice_free(RrImagePic
, pic
);
88 /************************************************************************
91 RrImageSets hold a group of pictures, each of which represent the same logical
92 image, but are physically different sizes.
93 At any time, it may be discovered that two different RrImageSets are actually
94 holding the same logical image. At that time, they would be merged.
95 An RrImageSet holds both original images which come from an outside source,
96 and resized images, which are generated when requests for a specific size are
97 made, and kept around in case they are request again. There is a maximum
98 number of resized images that an RrImageSet will keep around, however.
100 Each RrImage points to a single RrImageSet, which keeps track of which
101 RrImages point to it. If two RrImageSets are merged, then the RrImages which
102 pointed to the two RrImageSets will all point at the resulting merged set.
103 **************************************************************************/
106 /*! Free an RrImageSet and the stuff inside it.
107 This should only occur when there are no more RrImages pointing to the set.
109 static void RrImageSetFree(RrImageSet
*self
)
115 g_assert(self
->images
== NULL
);
117 /* remove all names associated with this RrImageSet */
118 for (it
= self
->names
; it
; it
= g_slist_next(it
)) {
119 g_hash_table_remove(self
->cache
->name_table
, it
->data
);
122 g_slist_free(self
->names
);
124 /* destroy the RrImagePic objects stored in the RrImageSet. they will
125 be keys in the cache to RrImageSet objects, so remove them from
126 the cache's pic_table as well. */
127 for (i
= 0; i
< self
->n_original
; ++i
) {
128 g_hash_table_remove(self
->cache
->pic_table
, self
->original
[i
]);
129 RrImagePicFree(self
->original
[i
]);
131 g_free(self
->original
);
132 for (i
= 0; i
< self
->n_resized
; ++i
) {
133 g_hash_table_remove(self
->cache
->pic_table
, self
->resized
[i
]);
134 RrImagePicFree(self
->resized
[i
]);
136 g_free(self
->resized
);
138 g_slice_free(RrImageSet
, self
);
142 /*! Remove a picture from an RrImageSet as a given position.
143 @param set The RrImageSet to remove the picture from.
144 @param i The index of the picture in the RrImageSet in the list of
145 originals (if @original is TRUE), or in the list of resized pictures (if
147 @param original TRUE if the picture is an original, FALSE if it is a resized
148 version of another picture in the RrImageSet.
150 static void RrImageSetRemovePictureAt(RrImageSet
*self
, gint i
,
157 list
= &self
->original
;
158 len
= &self
->n_original
;
161 list
= &self
->resized
;
162 len
= &self
->n_resized
;
165 g_assert(i
>= 0 && i
< *len
);
167 /* remove the picture data as a key in the cache */
168 g_hash_table_remove(self
->cache
->pic_table
, (*list
)[i
]);
170 /* free the picture being removed */
171 RrImagePicFree((*list
)[i
]);
173 /* copy the elements after the removed one in the array forward one space
174 and shrink the array down one size */
175 for (i
= i
+1; i
< *len
; ++i
)
176 (*list
)[i
-1] = (*list
)[i
];
178 *list
= g_renew(RrImagePic
*, *list
, *len
);
181 /*! Add an RrImagePic to an RrImageSet.
182 The RrImagePic should _not_ exist in the image cache already.
183 Pictures are added to the front of the list, to maintain the ordering of
186 static void RrImageSetAddPicture(RrImageSet
*self
, RrImagePic
*pic
,
193 g_assert(pic
->width
> 0 && pic
->height
> 0);
194 g_assert(g_hash_table_lookup(self
->cache
->pic_table
, pic
) == NULL
);
196 /* choose which list in the RrImageSet to add the new picture to. */
198 /* remove the resized picture of the same size if one exists */
199 for (i
= 0; i
< self
->n_resized
; ++i
)
200 if (self
->resized
[i
]->width
== pic
->width
||
201 self
->resized
[i
]->height
== pic
->height
)
203 RrImageSetRemovePictureAt(self
, i
, FALSE
);
207 list
= &self
->original
;
208 len
= &self
->n_original
;
211 list
= &self
->resized
;
212 len
= &self
->n_resized
;
215 /* grow the list by one spot, shift everything down one, and insert the new
216 picture at the front of the list */
217 *list
= g_renew(RrImagePic
*, *list
, ++*len
);
218 for (i
= *len
-1; i
> 0; --i
)
219 (*list
)[i
] = (*list
)[i
-1];
222 /* add the picture as a key to point to this image in the cache */
223 g_hash_table_insert(self
->cache
->pic_table
, (*list
)[0], self
);
227 g_debug("Adding %s picture to the cache:\n "
228 "Image 0x%lx, w %d h %d Hash %u",
229 (*list == self->original ? "ORIGINAL" : "RESIZED"),
230 (gulong)self, pic->width, pic->height, RrImagePicHash(pic));
235 /*! Merges two image sets, destroying one, and returning the other. */
236 RrImageSet
* RrImageSetMergeSets(RrImageSet
*b
, RrImageSet
*a
)
238 gint a_i
, b_i
, merged_i
;
239 RrImagePic
**original
, **resized
;
240 gint n_original
, n_resized
, tmp
;
243 const gint max_resized
= a
->cache
->max_resized_saved
;
252 /* the original and resized picture lists in an RrImageSet are kept ordered
253 as newest to oldest. we don't have timestamps for them, so we cannot
254 preserve this in the merged RrImageSet exactly. a decent approximation,
255 i think, is to add them in alternating order (one from a, one from b,
256 repeat). this way, the newest from each will be near the front at
257 least, and in the resized list, when we drop an old picture, we will
258 not always only drop from a or b only, but from each of them equally (or
259 from whichever has more resized pictures.
262 g_assert(b
->cache
== a
->cache
);
264 a_i
= b_i
= merged_i
= 0;
265 n_original
= a
->n_original
+ b
->n_original
;
266 original
= g_new(RrImagePic
*, n_original
);
267 while (merged_i
< n_original
) {
268 if (a_i
< a
->n_original
)
269 original
[merged_i
++] = a
->original
[a_i
++];
270 if (b_i
< b
->n_original
)
271 original
[merged_i
++] = b
->original
[b_i
++];
274 a_i
= b_i
= merged_i
= 0;
275 n_resized
= MIN(max_resized
, a
->n_resized
+ b
->n_resized
);
276 resized
= g_new(RrImagePic
*, n_resized
);
277 while (merged_i
< n_resized
) {
278 if (a_i
< a
->n_resized
)
279 resized
[merged_i
++] = a
->resized
[a_i
++];
280 if (b_i
< b
->n_resized
&& merged_i
< n_resized
)
281 resized
[merged_i
++] = b
->resized
[b_i
++];
284 /* if there are any RrImagePic objects left over in a->resized or
285 b->resized, they need to be disposed of, and removed from the cache.
287 updates the size of the list, as we want to remember which pointers
288 were merged from which list (and don't want to remember the ones we
289 did not merge and have freed).
292 for (; a_i
< a
->n_resized
; ++a_i
) {
293 g_hash_table_remove(a
->cache
->pic_table
, a
->resized
[a_i
]);
294 RrImagePicFree(a
->resized
[a_i
]);
299 for (; b_i
< b
->n_resized
; ++b_i
) {
300 g_hash_table_remove(a
->cache
->pic_table
, b
->resized
[b_i
]);
301 RrImagePicFree(b
->resized
[b_i
]);
305 /* we will use the a object as the merge destination, so things in b will
308 the cache's name_table will point to b for all the names in b->names,
309 so these need to be updated to point at a instead.
310 also, the cache's pic_table will point to b for all the pictures in b,
311 so these need to be updated to point at a as well.
313 any RrImage objects that were using b should now use a instead.
315 the names and images will be all moved into a, and the merged picture
316 lists will be placed in a. the pictures in a and b are moved to new
317 arrays, so the arrays in a and b need to be freed explicitly (the
318 RrImageSetFree function would free the picture data too which we do not
319 want here). then b can be freed.
322 for (it
= b
->names
; it
; it
= g_slist_next(it
))
323 g_hash_table_insert(a
->cache
->name_table
, it
->data
, a
);
324 for (b_i
= 0; b_i
< b
->n_original
; ++b_i
)
325 g_hash_table_insert(a
->cache
->pic_table
, b
->original
[b_i
], a
);
326 for (b_i
= 0; b_i
< b
->n_resized
; ++b_i
)
327 g_hash_table_insert(a
->cache
->pic_table
, b
->resized
[b_i
], a
);
329 for (it
= b
->images
; it
; it
= g_slist_next(it
))
330 ((RrImage
*)it
->data
)->set
= a
;
332 a
->images
= g_slist_concat(a
->images
, b
->images
);
334 a
->names
= g_slist_concat(a
->names
, b
->names
);
337 a
->n_original
= a
->n_resized
= 0;
340 a
->original
= a
->resized
= NULL
;
341 b
->n_original
= b
->n_resized
= 0;
344 b
->original
= b
->resized
= NULL
;
346 a
->n_original
= n_original
;
347 a
->original
= original
;
348 a
->n_resized
= n_resized
;
349 a
->resized
= resized
;
356 static void RrImageSetAddName(RrImageSet
*set
, const gchar
*name
)
361 set
->names
= g_slist_prepend(set
->names
, n
);
363 /* add the new name to the hash table */
364 g_assert(g_hash_table_lookup(set
->cache
->name_table
, n
) == NULL
);
365 g_hash_table_insert(set
->cache
->name_table
, n
, set
);
369 /************************************************************************
371 **************************************************************************/
374 void RrImageRef(RrImage
*self
)
379 void RrImageUnref(RrImage
*self
)
381 if (self
&& --self
->ref
== 0) {
385 g_debug("Refcount to 0, removing ALL pictures from the cache:\n "
386 "Image 0x%lx", (gulong)self);
389 if (self
->destroy_func
)
390 self
->destroy_func(self
, self
->destroy_data
);
393 set
->images
= g_slist_remove(set
->images
, self
);
395 /* free the set as well if there are no images pointing to it */
398 g_slice_free(RrImage
, self
);
402 /*! Set function that will be called just before RrImage is destroyed. */
403 void RrImageSetDestroyFunc(RrImage
*self
, RrImageDestroyFunc func
,
406 self
->destroy_func
= func
;
407 self
->destroy_data
= data
;
410 void RrImageAddFromData(RrImage
*self
, RrPixel32
*data
, gint w
, gint h
)
412 RrImagePic pic
, *ppic
;
415 g_return_if_fail(self
!= NULL
);
416 g_return_if_fail(data
!= NULL
);
417 g_return_if_fail(w
> 0 && h
> 0);
419 RrImagePicInit(&pic
, w
, h
, data
);
420 set
= g_hash_table_lookup(self
->set
->cache
->pic_table
, &pic
);
422 self
->set
= RrImageSetMergeSets(self
->set
, set
);
424 ppic
= RrImagePicNew(w
, h
, data
);
425 RrImageSetAddPicture(self
->set
, ppic
, TRUE
);
429 RrImage
* RrImageNewFromData(RrImageCache
*cache
, RrPixel32
*data
,
432 RrImagePic pic
, *ppic
;
436 g_return_val_if_fail(cache
!= NULL
, NULL
);
437 g_return_val_if_fail(data
!= NULL
, NULL
);
438 g_return_val_if_fail(w
> 0 && h
> 0, NULL
);
440 /* finds a picture in the cache, if it is already in there, and use the
441 RrImageSet the picture lives in. */
442 RrImagePicInit(&pic
, w
, h
, data
);
443 set
= g_hash_table_lookup(cache
->pic_table
, &pic
);
445 self
= set
->images
->data
; /* just grab any RrImage from the list */
450 /* the image does not exist in any RrImageSet in the cache, so make
451 a new RrImageSet, and a new RrImage that points to it, and place the
452 new image inside the new RrImageSet */
454 self
= g_slice_new0(RrImage
);
456 self
->set
= g_slice_new0(RrImageSet
);
457 self
->set
->cache
= cache
;
458 self
->set
->images
= g_slist_append(self
->set
->images
, self
);
460 ppic
= RrImagePicNew(w
, h
, data
);
461 RrImageSetAddPicture(self
->set
, ppic
, TRUE
);
466 RrImage
* RrImageNewFromName(RrImageCache
*cache
, const gchar
*name
)
478 g_return_val_if_fail(cache
!= NULL
, NULL
);
479 g_return_val_if_fail(name
!= NULL
, NULL
);
481 set
= g_hash_table_lookup(cache
->name_table
, name
);
483 self
= set
->images
->data
;
488 /* XXX find the path via freedesktop icon spec (use obt) ! */
489 path
= g_strdup(name
);
491 if (!(img
= imlib_load_image(path
)))
492 g_message("Cannot load image \"%s\" from file \"%s\"", name
, path
);
498 /* Get data and dimensions of the image.
500 WARNING: This stuff is NOT threadsafe !!
502 imlib_context_set_image(img
);
503 data
= imlib_image_get_data_for_reading_only();
504 w
= imlib_image_get_width();
505 h
= imlib_image_get_height();
507 /* get an RrImage that contains an RrImageSet with this picture in it.
508 the RrImage might be new, or reused if the picture was already in the
511 either way, we get back an RrImageSet (via the RrImage), and we must add
512 the name to that RrImageSet. because of the check above, we know that
513 there is no RrImageSet in the cache which already has the given name
517 self
= RrImageNewFromData(cache
, data
, w
, h
);
518 RrImageSetAddName(self
->set
, name
);
525 /************************************************************************
526 Image drawing and resizing operations.
527 **************************************************************************/
529 /*! Given a picture in RGBA format, of a specified size, resize it to the new
530 requested size (but keep its aspect ratio). If the image does not need to
531 be resized (it is already the right size) then this returns NULL. Otherwise
532 it returns a newly allocated RrImagePic with the resized picture inside it
533 @return Returns a newly allocated RrImagePic object with a new version of the
534 image in the requested size (keeping aspect ratio).
536 static RrImagePic
* ResizeImage(RrPixel32
*src
,
537 gulong srcW
, gulong srcH
,
538 gulong dstW
, gulong dstH
)
540 RrPixel32
*dst
, *dststart
;
542 gulong dstX
, dstY
, srcX
, srcY
;
543 gulong srcX1
, srcX2
, srcY1
, srcY2
;
544 gulong ratioX
, ratioY
;
545 gulong aspectW
, aspectH
;
552 /* keep the aspect ratio */
554 aspectH
= (gint
)(dstW
* ((gdouble
)srcH
/ srcW
));
555 if (aspectH
> dstH
) {
557 aspectW
= (gint
)(dstH
* ((gdouble
)srcW
/ srcH
));
559 dstW
= aspectW
? aspectW
: 1;
560 dstH
= aspectH
? aspectH
: 1;
562 if (srcW
== dstW
&& srcH
== dstH
)
563 return NULL
; /* no scaling needed! */
565 dststart
= dst
= g_new(RrPixel32
, dstW
* dstH
);
567 ratioX
= (srcW
<< FRACTION
) / dstW
;
568 ratioY
= (srcH
<< FRACTION
) / dstH
;
571 for (dstY
= 0; dstY
< dstH
; dstY
++) {
576 for (dstX
= 0; dstX
< dstW
; dstX
++) {
577 gulong red
= 0, green
= 0, blue
= 0, alpha
= 0;
578 gulong portionX
, portionY
, portionXY
, sumXY
= 0;
584 for (srcY
= srcY1
; srcY
< srcY2
; srcY
+= (1UL << FRACTION
)) {
587 portionY
= (1UL << FRACTION
) - (srcY1
- srcY
);
588 if (portionY
> srcY2
- srcY1
)
589 portionY
= srcY2
- srcY1
;
591 else if (srcY
== FLOOR(srcY2
))
592 portionY
= srcY2
- srcY
;
594 portionY
= (1UL << FRACTION
);
596 for (srcX
= srcX1
; srcX
< srcX2
; srcX
+= (1UL << FRACTION
)) {
599 portionX
= (1UL << FRACTION
) - (srcX1
- srcX
);
600 if (portionX
> srcX2
- srcX1
)
601 portionX
= srcX2
- srcX1
;
603 else if (srcX
== FLOOR(srcX2
))
604 portionX
= srcX2
- srcX
;
606 portionX
= (1UL << FRACTION
);
608 portionXY
= (portionX
* portionY
) >> FRACTION
;
611 pixel
= *(src
+ (srcY
>> FRACTION
) * srcW
612 + (srcX
>> FRACTION
));
613 red
+= ((pixel
>> RrDefaultRedOffset
) & 0xFF)
615 green
+= ((pixel
>> RrDefaultGreenOffset
) & 0xFF)
617 blue
+= ((pixel
>> RrDefaultBlueOffset
) & 0xFF)
619 alpha
+= ((pixel
>> RrDefaultAlphaOffset
) & 0xFF)
624 g_assert(sumXY
!= 0);
630 *dst
++ = (red
<< RrDefaultRedOffset
) |
631 (green
<< RrDefaultGreenOffset
) |
632 (blue
<< RrDefaultBlueOffset
) |
633 (alpha
<< RrDefaultAlphaOffset
);
637 pic
= g_slice_new(RrImagePic
);
638 RrImagePicInit(pic
, dstW
, dstH
, dststart
);
643 /*! This draws an RGBA picture into the target, within the rectangle specified
644 by the area parameter. If the area's size differs from the source's then it
645 will be centered within the rectangle */
646 void DrawRGBA(RrPixel32
*target
, gint target_w
, gint target_h
,
647 RrPixel32
*source
, gint source_w
, gint source_h
,
648 gint alpha
, RrRect
*area
)
651 gint col
, num_pixels
;
654 g_assert(source_w
<= area
->width
&& source_h
<= area
->height
);
655 g_assert(area
->x
+ area
->width
<= target_w
);
656 g_assert(area
->y
+ area
->height
<= target_h
);
658 /* keep the aspect ratio */
660 dh
= (gint
)(dw
* ((gdouble
)source_h
/ source_w
));
661 if (dh
> area
->height
) {
663 dw
= (gint
)(dh
* ((gdouble
)source_w
/ source_h
));
666 /* copy source -> dest, and apply the alpha channel.
667 center the image if it is smaller than the area */
669 num_pixels
= dw
* dh
;
670 dest
= target
+ area
->x
+ (area
->width
- dw
) / 2 +
671 (target_w
* (area
->y
+ (area
->height
- dh
) / 2));
672 while (num_pixels
-- > 0) {
673 guchar a
, r
, g
, b
, bgr
, bgg
, bgb
;
675 /* apply the rgba's opacity as well */
676 a
= ((*source
>> RrDefaultAlphaOffset
) * alpha
) >> 8;
677 r
= *source
>> RrDefaultRedOffset
;
678 g
= *source
>> RrDefaultGreenOffset
;
679 b
= *source
>> RrDefaultBlueOffset
;
681 /* background color */
682 bgr
= *dest
>> RrDefaultRedOffset
;
683 bgg
= *dest
>> RrDefaultGreenOffset
;
684 bgb
= *dest
>> RrDefaultBlueOffset
;
686 r
= bgr
+ (((r
- bgr
) * a
) >> 8);
687 g
= bgg
+ (((g
- bgg
) * a
) >> 8);
688 b
= bgb
+ (((b
- bgb
) * a
) >> 8);
690 *dest
= ((r
<< RrDefaultRedOffset
) |
691 (g
<< RrDefaultGreenOffset
) |
692 (b
<< RrDefaultBlueOffset
));
699 dest
+= target_w
- dw
;
704 /*! Draw an RGBA texture into a target pixel buffer. */
705 void RrImageDrawRGBA(RrPixel32
*target
, RrTextureRGBA
*rgba
,
706 gint target_w
, gint target_h
,
711 scaled
= ResizeImage(rgba
->data
, rgba
->width
, rgba
->height
,
712 area
->width
, area
->height
);
716 g_warning("Scaling an RGBA! You should avoid this and just make "
717 "it the right size yourself!");
719 DrawRGBA(target
, target_w
, target_h
,
720 scaled
->data
, scaled
->width
, scaled
->height
,
722 RrImagePicFree(scaled
);
725 DrawRGBA(target
, target_w
, target_h
,
726 rgba
->data
, rgba
->width
, rgba
->height
,
730 /*! Draw an RrImage texture into a target pixel buffer. If the RrImage does
731 not contain a picture of the appropriate size, then one of its "original"
732 pictures will be resized and used (and stored in the RrImage as a "resized"
735 void RrImageDrawImage(RrPixel32
*target
, RrTextureImage
*img
,
736 gint target_w
, gint target_h
,
739 gint i
, min_diff
, min_i
, min_aspect_diff
, min_aspect_i
;
750 /* is there an original of this size? (only the larger of
751 w or h has to be right cuz we maintain aspect ratios) */
752 for (i
= 0; i
< set
->n_original
; ++i
)
753 if ((set
->original
[i
]->width
>= set
->original
[i
]->height
&&
754 set
->original
[i
]->width
== area
->width
) ||
755 (set
->original
[i
]->width
<= set
->original
[i
]->height
&&
756 set
->original
[i
]->height
== area
->height
))
758 pic
= set
->original
[i
];
762 /* is there a resize of this size? */
763 for (i
= 0; i
< set
->n_resized
; ++i
)
764 if ((set
->resized
[i
]->width
>= set
->resized
[i
]->height
&&
765 set
->resized
[i
]->width
== area
->width
) ||
766 (set
->resized
[i
]->width
<= set
->resized
[i
]->height
&&
767 set
->resized
[i
]->height
== area
->height
))
772 /* save the selected one */
773 saved
= set
->resized
[i
];
775 /* shift all the others down */
776 for (j
= i
; j
> 0; --j
)
777 set
->resized
[j
] = set
->resized
[j
-1];
779 /* and move the selected one to the top of the list */
780 set
->resized
[0] = saved
;
782 pic
= set
->resized
[0];
788 RrImageSet
*cache_set
;
790 /* find an original with a close size */
791 min_diff
= min_aspect_diff
= -1;
792 min_i
= min_aspect_i
= 0;
793 aspect
= ((gdouble
)area
->width
) / area
->height
;
794 for (i
= 0; i
< set
->n_original
; ++i
) {
799 /* our size difference metric.. */
800 wdiff
= set
->original
[i
]->width
- area
->width
;
801 if (wdiff
< 0) wdiff
*= 2; /* prefer scaling down than up */
802 hdiff
= set
->original
[i
]->height
- area
->height
;
803 if (hdiff
< 0) hdiff
*= 2; /* prefer scaling down than up */
804 diff
= (wdiff
* wdiff
) + (hdiff
* hdiff
);
806 /* find the smallest difference */
807 if (min_diff
< 0 || diff
< min_diff
) {
811 /* and also find the smallest difference with the same aspect
812 ratio (and prefer this one) */
813 myasp
= ((gdouble
)set
->original
[i
]->width
) /
814 set
->original
[i
]->height
;
815 if (ABS(aspect
- myasp
) < 0.0000001 &&
816 (min_aspect_diff
< 0 || diff
< min_aspect_diff
))
818 min_aspect_diff
= diff
;
823 /* use the aspect ratio correct source if there is one */
824 if (min_aspect_i
>= 0)
825 min_i
= min_aspect_i
;
827 /* resize the original to the given area */
828 pic
= ResizeImage(set
->original
[min_i
]->data
,
829 set
->original
[min_i
]->width
,
830 set
->original
[min_i
]->height
,
831 area
->width
, area
->height
);
833 /* is it already in the cache ? */
834 cache_set
= g_hash_table_lookup(set
->cache
->pic_table
, pic
);
836 /* merge this set with the one found in the cache - they are
837 apparently the same image ! then next time we won't have to do
838 this resizing, we will use the cache_set's pic instead. */
839 set
= RrImageSetMergeSets(set
, cache_set
);
843 /* add the resized image to the image, as the first in the resized
845 while (set
->n_resized
>= set
->cache
->max_resized_saved
)
846 /* remove the last one (last used one) to make space for
847 adding our resized picture */
848 RrImageSetRemovePictureAt(set
, set
->n_resized
-1, FALSE
);
849 if (set
->cache
->max_resized_saved
)
850 /* add it to the resized list */
851 RrImageSetAddPicture(set
, pic
, FALSE
);
853 free_pic
= TRUE
; /* don't leak mem! */
857 /* The RrImageSet may have changed if we merged it with another, so the
858 RrImage object needs to be updated to use the new merged RrImageSet. */
861 g_assert(pic
!= NULL
);
863 DrawRGBA(target
, target_w
, target_h
,
864 pic
->data
, pic
->width
, pic
->height
,