]>
Dogcows Code - chaz/openbox/blob - image.c
f86a3ee008c2410964f435b49116743651735c26
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 void RrImagePicInit(RrImagePic
*pic
, const gchar
*name
,
35 gint w
, gint h
, RrPixel32
*data
)
43 for (i
= w
*h
; i
> 0; --i
)
44 pic
->sum
+= *(data
++);
45 pic
->name
= g_strdup(name
);
48 static void RrImagePicFree(RrImagePic
*pic
)
57 /*! Add a picture to an Image, that is, add another copy of the image at
58 another size. This may add it to the "originals" list or to the
60 static void AddPicture(RrImage
*self
, RrImagePic
***list
, gint
*len
,
65 g_assert(pic
->width
> 0 && pic
->height
> 0);
68 g_assert(!g_hash_table_lookup(self
->cache
->name_table
, pic
->name
));
70 g_assert(!g_hash_table_lookup(self
->cache
->pic_table
, pic
));
73 *list
= g_renew(RrImagePic
*, *list
, ++*len
);
75 /* move everything else down one */
76 for (i
= *len
-1; i
> 0; --i
)
77 (*list
)[i
] = (*list
)[i
-1];
79 /* set the new picture up at the front of the list */
82 /* add the name or the picture as a key to point to this image in the
85 g_hash_table_insert(self
->cache
->name_table
, pic
->name
, self
);
87 g_hash_table_insert(self
->cache
->pic_table
, (*list
)[0], self
);
91 g_debug("Adding %s picture to the cache:\n "
92 "Image 0x%lx, w %d h %d Hash %u",
93 (*list == self->original ? "ORIGINAL" : "RESIZED"),
94 (gulong)self, pic->width, pic->height, RrImagePicHash(pic));
99 /*! Remove a picture from an Image. This may remove it from the "originals"
100 list or the "resized" list. */
101 static void RemovePicture(RrImage
*self
, RrImagePic
***list
,
108 g_debug("Removing %s picture from the cache:\n "
109 "Image 0x%lx, w %d h %d Hash %u",
110 (*list == self->original ? "ORIGINAL" : "RESIZED"),
111 (gulong)self, (*list)[i]->width, (*list)[i]->height,
112 RrImagePicHash((*list)[i]));
116 /* remove the name or picture as a key in the cache */
117 if ((*list
)[i
]->name
)
118 g_hash_table_remove(self
->cache
->name_table
, (*list
)[i
]->name
);
120 g_hash_table_remove(self
->cache
->pic_table
, (*list
)[i
]);
122 /* free the picture */
123 RrImagePicFree((*list
)[i
]);
124 /* shift everything down one */
125 for (j
= i
; j
< *len
-1; ++j
)
126 (*list
)[j
] = (*list
)[j
+1];
127 /* shrink the list */
128 *list
= g_renew(RrImagePic
*, *list
, --*len
);
131 /*! Given a picture in RGBA format, of a specified size, resize it to the new
132 requested size (but keep its aspect ratio). If the image does not need to
133 be resized (it is already the right size) then this returns NULL. Otherwise
134 it returns a newly allocated RrImagePic with the resized picture inside it
136 static RrImagePic
* ResizeImage(RrPixel32
*src
,
137 gulong srcW
, gulong srcH
,
138 gulong dstW
, gulong dstH
)
140 RrPixel32
*dst
, *dststart
;
142 gulong dstX
, dstY
, srcX
, srcY
;
143 gulong srcX1
, srcX2
, srcY1
, srcY2
;
144 gulong ratioX
, ratioY
;
145 gulong aspectW
, aspectH
;
147 /* XXX should these variables be ensured to not be zero in the callers? */
148 srcW
= srcW
? srcW
: 1;
149 srcH
= srcH
? srcH
: 1;
150 dstW
= dstW
? dstW
: 1;
151 dstH
= dstH
? dstH
: 1;
153 /* keep the aspect ratio */
155 aspectH
= (gint
)(dstW
* ((gdouble
)srcH
/ srcW
));
156 if (aspectH
> dstH
) {
158 aspectW
= (gint
)(dstH
* ((gdouble
)srcW
/ srcH
));
160 dstW
= aspectW
? aspectW
: 1;
161 dstH
= aspectH
? aspectH
: 1;
163 if (srcW
== dstW
&& srcH
== dstH
)
164 return NULL
; /* no scaling needed! */
166 dststart
= dst
= g_new(RrPixel32
, dstW
* dstH
);
168 ratioX
= (srcW
<< FRACTION
) / dstW
;
169 ratioY
= (srcH
<< FRACTION
) / dstH
;
172 for (dstY
= 0; dstY
< dstH
; dstY
++) {
177 for (dstX
= 0; dstX
< dstW
; dstX
++) {
178 gulong red
= 0, green
= 0, blue
= 0, alpha
= 0;
179 gulong portionX
, portionY
, portionXY
, sumXY
= 0;
185 for (srcY
= srcY1
; srcY
< srcY2
; srcY
+= (1UL << FRACTION
)) {
188 portionY
= (1UL << FRACTION
) - (srcY1
- srcY
);
189 if (portionY
> srcY2
- srcY1
)
190 portionY
= srcY2
- srcY1
;
192 else if (srcY
== FLOOR(srcY2
))
193 portionY
= srcY2
- srcY
;
195 portionY
= (1UL << FRACTION
);
197 for (srcX
= srcX1
; srcX
< srcX2
; srcX
+= (1UL << FRACTION
)) {
200 portionX
= (1UL << FRACTION
) - (srcX1
- srcX
);
201 if (portionX
> srcX2
- srcX1
)
202 portionX
= srcX2
- srcX1
;
204 else if (srcX
== FLOOR(srcX2
))
205 portionX
= srcX2
- srcX
;
207 portionX
= (1UL << FRACTION
);
209 portionXY
= (portionX
* portionY
) >> FRACTION
;
212 pixel
= *(src
+ (srcY
>> FRACTION
) * srcW
213 + (srcX
>> FRACTION
));
214 red
+= ((pixel
>> RrDefaultRedOffset
) & 0xFF)
216 green
+= ((pixel
>> RrDefaultGreenOffset
) & 0xFF)
218 blue
+= ((pixel
>> RrDefaultBlueOffset
) & 0xFF)
220 alpha
+= ((pixel
>> RrDefaultAlphaOffset
) & 0xFF)
225 g_assert(sumXY
!= 0);
231 *dst
++ = (red
<< RrDefaultRedOffset
) |
232 (green
<< RrDefaultGreenOffset
) |
233 (blue
<< RrDefaultBlueOffset
) |
234 (alpha
<< RrDefaultAlphaOffset
);
238 pic
= g_new(RrImagePic
, 1);
239 RrImagePicInit(pic
, NULL
, dstW
, dstH
, dststart
);
244 /*! This draws an RGBA picture into the target, within the rectangle specified
245 by the area parameter. If the area's size differs from the source's then it
246 will be centered within the rectangle */
247 void DrawRGBA(RrPixel32
*target
, gint target_w
, gint target_h
,
248 RrPixel32
*source
, gint source_w
, gint source_h
,
249 gint alpha
, RrRect
*area
)
252 gint col
, num_pixels
;
255 g_assert(source_w
<= area
->width
&& source_h
<= area
->height
);
256 g_assert(area
->x
+ area
->width
<= target_w
);
257 g_assert(area
->y
+ area
->height
<= target_h
);
259 /* keep the aspect ratio */
261 dh
= (gint
)(dw
* ((gdouble
)source_h
/ source_w
));
262 if (dh
> area
->height
) {
264 dw
= (gint
)(dh
* ((gdouble
)source_w
/ source_h
));
267 /* copy source -> dest, and apply the alpha channel.
268 center the image if it is smaller than the area */
270 num_pixels
= dw
* dh
;
271 dest
= target
+ area
->x
+ (area
->width
- dw
) / 2 +
272 (target_w
* (area
->y
+ (area
->height
- dh
) / 2));
273 while (num_pixels
-- > 0) {
274 guchar a
, r
, g
, b
, bgr
, bgg
, bgb
;
276 /* apply the rgba's opacity as well */
277 a
= ((*source
>> RrDefaultAlphaOffset
) * alpha
) >> 8;
278 r
= *source
>> RrDefaultRedOffset
;
279 g
= *source
>> RrDefaultGreenOffset
;
280 b
= *source
>> RrDefaultBlueOffset
;
282 /* background color */
283 bgr
= *dest
>> RrDefaultRedOffset
;
284 bgg
= *dest
>> RrDefaultGreenOffset
;
285 bgb
= *dest
>> RrDefaultBlueOffset
;
287 r
= bgr
+ (((r
- bgr
) * a
) >> 8);
288 g
= bgg
+ (((g
- bgg
) * a
) >> 8);
289 b
= bgb
+ (((b
- bgb
) * a
) >> 8);
291 *dest
= ((r
<< RrDefaultRedOffset
) |
292 (g
<< RrDefaultGreenOffset
) |
293 (b
<< RrDefaultBlueOffset
));
300 dest
+= target_w
- dw
;
305 /*! Draw an RGBA texture into a target pixel buffer. */
306 void RrImageDrawRGBA(RrPixel32
*target
, RrTextureRGBA
*rgba
,
307 gint target_w
, gint target_h
,
312 scaled
= ResizeImage(rgba
->data
, rgba
->width
, rgba
->height
,
313 area
->width
, area
->height
);
317 g_warning("Scaling an RGBA! You should avoid this and just make "
318 "it the right size yourself!");
320 DrawRGBA(target
, target_w
, target_h
,
321 scaled
->data
, scaled
->width
, scaled
->height
,
323 RrImagePicFree(scaled
);
326 DrawRGBA(target
, target_w
, target_h
,
327 rgba
->data
, rgba
->width
, rgba
->height
,
331 /*! Create a new RrImage, which is linked to an image cache */
332 RrImage
* RrImageNew(RrImageCache
*cache
)
336 g_assert(cache
!= NULL
);
338 self
= g_new0(RrImage
, 1);
344 /*! Set function that will be called just before RrImage is destroyed. */
345 void RrImageSetDestroyFunc(RrImage
*image
, RrImageDestroyFunc func
,
348 image
->destroy_func
= func
;
349 image
->destroy_data
= data
;
352 void RrImageRef(RrImage
*self
)
357 void RrImageUnref(RrImage
*self
)
359 if (self
&& --self
->ref
== 0) {
362 g_debug("Refcount to 0, removing ALL pictures from the cache:\n "
363 "Image 0x%lx", (gulong)self);
366 if (self
->destroy_func
)
367 self
->destroy_func(self
, self
->destroy_data
);
368 while (self
->n_original
> 0)
369 RemovePicture(self
, &self
->original
, 0, &self
->n_original
);
370 while (self
->n_resized
> 0)
371 RemovePicture(self
, &self
->resized
, 0, &self
->n_resized
);
376 static void AddPictureFromData(RrImage
*self
, const char *name
,
377 const RrPixel32
*data
, gint w
, gint h
)
382 /* make sure we don't already have this size.. */
383 for (i
= 0; i
< self
->n_original
; ++i
)
384 if (self
->original
[i
]->width
== w
&& self
->original
[i
]->height
== h
) {
387 g_debug("Found duplicate ORIGINAL image:\n "
388 "Image 0x%lx, w %d h %d", (gulong)self, w, h);
394 /* remove any resized pictures of this same size */
395 for (i
= 0; i
< self
->n_resized
; ++i
)
396 if (self
->resized
[i
]->width
== w
|| self
->resized
[i
]->height
== h
) {
397 RemovePicture(self
, &self
->resized
, i
, &self
->n_resized
);
401 /* add the new picture */
402 pic
= g_new(RrImagePic
, 1);
403 RrImagePicInit(pic
, name
, w
, h
, g_memdup(data
, w
*h
*sizeof(RrPixel32
)));
404 AddPicture(self
, &self
->original
, &self
->n_original
, pic
);
407 gboolean
RrImageAddPictureName(RrImage
*self
, const gchar
*name
)
415 /* XXX find the path via freedesktop icon spec (use obt) ! */
416 path
= g_strdup(name
);
418 if (!(img
= imlib_load_image(path
)))
419 g_message("Cannot load image \"%s\" from file \"%s\"", name
, path
);
423 return FALSE
; /* failed to load it */
425 /* Get data and dimensions of the image.
427 WARNING: This stuff is NOT threadsafe !!
429 imlib_context_set_image(img
);
430 data
= imlib_image_get_data_for_reading_only();
431 w
= imlib_image_get_width();
432 h
= imlib_image_get_height();
434 /* add it to the RrImage, and set its name */
435 AddPictureFromData(self
, name
, data
, w
, h
);
444 /*! Add a new picture with the given RGBA pixel data and dimensions into the
445 RrImage. This adds an "original" picture to the image.
447 void RrImageAddPicture(RrImage
*self
, const RrPixel32
*data
, gint w
, gint h
)
449 AddPictureFromData(self
, NULL
, data
, w
, h
);
452 /*! Remove the picture from the RrImage which has the given dimensions. This
453 removes an "original" picture from the image.
455 void RrImageRemovePicture(RrImage
*self
, gint w
, gint h
)
459 /* remove any resized pictures of this same size */
460 for (i
= 0; i
< self
->n_original
; ++i
)
461 if (self
->original
[i
]->width
== w
&& self
->original
[i
]->height
== h
) {
462 RemovePicture(self
, &self
->original
, i
, &self
->n_original
);
467 /*! Draw an RrImage texture into a target pixel buffer. If the RrImage does
468 not contain a picture of the appropriate size, then one of its "original"
469 pictures will be resized and used (and stored in the RrImage as a "resized"
472 void RrImageDrawImage(RrPixel32
*target
, RrTextureImage
*img
,
473 gint target_w
, gint target_h
,
476 gint i
, min_diff
, min_i
, min_aspect_diff
, min_aspect_i
;
485 /* is there an original of this size? (only the larger of
486 w or h has to be right cuz we maintain aspect ratios) */
487 for (i
= 0; i
< self
->n_original
; ++i
)
488 if ((self
->original
[i
]->width
>= self
->original
[i
]->height
&&
489 self
->original
[i
]->width
== area
->width
) ||
490 (self
->original
[i
]->width
<= self
->original
[i
]->height
&&
491 self
->original
[i
]->height
== area
->height
))
493 pic
= self
->original
[i
];
497 /* is there a resize of this size? */
498 for (i
= 0; i
< self
->n_resized
; ++i
)
499 if ((self
->resized
[i
]->width
>= self
->resized
[i
]->height
&&
500 self
->resized
[i
]->width
== area
->width
) ||
501 (self
->resized
[i
]->width
<= self
->resized
[i
]->height
&&
502 self
->resized
[i
]->height
== area
->height
))
507 /* save the selected one */
508 saved
= self
->resized
[i
];
510 /* shift all the others down */
511 for (j
= i
; j
> 0; --j
)
512 self
->resized
[j
] = self
->resized
[j
-1];
514 /* and move the selected one to the top of the list */
515 self
->resized
[0] = saved
;
517 pic
= self
->resized
[0];
524 /* find an original with a close size */
525 min_diff
= min_aspect_diff
= -1;
526 min_i
= min_aspect_i
= 0;
527 aspect
= ((gdouble
)area
->width
) / area
->height
;
528 for (i
= 0; i
< self
->n_original
; ++i
) {
533 /* our size difference metric.. */
534 wdiff
= self
->original
[i
]->width
- area
->width
;
535 if (wdiff
< 0) wdiff
*= 2; /* prefer scaling down than up */
536 hdiff
= self
->original
[i
]->height
- area
->height
;
537 if (hdiff
< 0) hdiff
*= 2; /* prefer scaling down than up */
538 diff
= (wdiff
* wdiff
) + (hdiff
* hdiff
);
540 /* find the smallest difference */
541 if (min_diff
< 0 || diff
< min_diff
) {
545 /* and also find the smallest difference with the same aspect
546 ratio (and prefer this one) */
547 myasp
= ((gdouble
)self
->original
[i
]->width
) /
548 self
->original
[i
]->height
;
549 if (ABS(aspect
- myasp
) < 0.0000001 &&
550 (min_aspect_diff
< 0 || diff
< min_aspect_diff
))
552 min_aspect_diff
= diff
;
557 /* use the aspect ratio correct source if there is one */
558 if (min_aspect_i
>= 0)
559 min_i
= min_aspect_i
;
561 /* resize the original to the given area */
562 pic
= ResizeImage(self
->original
[min_i
]->data
,
563 self
->original
[min_i
]->width
,
564 self
->original
[min_i
]->height
,
565 area
->width
, area
->height
);
567 /* add the resized image to the image, as the first in the resized
569 if (self
->n_resized
>= self
->cache
->max_resized_saved
)
570 /* remove the last one (last used one) */
571 RemovePicture(self
, &self
->resized
, self
->n_resized
- 1,
573 if (self
->cache
->max_resized_saved
)
574 /* add it to the top of the resized list */
575 AddPicture(self
, &self
->resized
, &self
->n_resized
, pic
);
577 free_pic
= TRUE
; /* don't leak mem! */
580 g_assert(pic
!= NULL
);
582 DrawRGBA(target
, target_w
, target_h
,
583 pic
->data
, pic
->width
, pic
->height
,
This page took 0.058954 seconds and 3 git commands to generate.