]>
Dogcows Code - chaz/openbox/blob - render/image.c
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"
28 #define FLOOR(i) ((i) & (~0UL << FRACTION))
29 #define AVERAGE(a, b) (((((a) ^ (b)) & 0xfefefefeL) >> 1) + ((a) & (b)))
31 void RrImagePicInit(RrImagePic
*pic
, gint w
, gint h
, RrPixel32
*data
)
39 for (i
= w
*h
; i
> 0; --i
)
40 pic
->sum
+= *(data
++);
43 static void RrImagePicFree(RrImagePic
*pic
)
51 /*! Add a picture to an Image, that is, add another copy of the image at
52 another size. This may add it to the "originals" list or to the
54 static void AddPicture(RrImage
*self
, RrImagePic
***list
, gint
*len
,
59 g_assert(pic
->width
> 0 && pic
->height
> 0);
61 g_assert(g_hash_table_lookup(self
->cache
->table
, pic
) == NULL
);
64 *list
= g_renew(RrImagePic
*, *list
, ++*len
);
66 /* move everything else down one */
67 for (i
= *len
-1; i
> 0; --i
)
68 (*list
)[i
] = (*list
)[i
-1];
70 /* set the new picture up at the front of the list */
73 /* add the picture as a key to point to this image in the cache */
74 g_hash_table_insert(self
->cache
->table
, (*list
)[0], self
);
77 g_message("Adding %s picture to the cache:\n "
78 "Image 0x%x, w %d h %d Hash %u",
79 (*list
== self
->original
? "ORIGINAL" : "RESIZED"),
80 (guint
)self
, pic
->width
, pic
->height
, RrImagePicHash(pic
));
84 /*! Remove a picture from an Image. This may remove it from the "originals"
85 list or the "resized" list. */
86 static void RemovePicture(RrImage
*self
, RrImagePic
***list
,
92 g_message("Removing %s picture from the cache:\n "
93 "Image 0x%x, w %d h %d Hash %u",
94 (*list
== self
->original
? "ORIGINAL" : "RESIZED"),
95 (guint
)self
, (*list
)[i
]->width
, (*list
)[i
]->height
,
96 RrImagePicHash((*list
)[i
]));
99 /* remove the picture as a key in the cache */
100 g_hash_table_remove(self
->cache
->table
, (*list
)[i
]);
102 /* free the picture */
103 RrImagePicFree((*list
)[i
]);
104 /* shift everything down one */
105 for (j
= i
; j
< *len
-1; ++j
)
106 (*list
)[j
] = (*list
)[j
+1];
107 /* shrink the list */
108 *list
= g_renew(RrImagePic
*, *list
, --*len
);
111 /*! Given a picture in RGBA format, of a specified size, resize it to the new
112 requested size (but keep its aspect ratio). If the image does not need to
113 be resized (it is already the right size) then this returns NULL. Otherwise
114 it returns a newly allocated RrImagePic with the resized picture inside it
116 static RrImagePic
* ResizeImage(RrPixel32
*src
,
117 gulong srcW
, gulong srcH
,
118 gulong dstW
, gulong dstH
)
120 RrPixel32
*dst
, *dststart
;
122 gulong dstX
, dstY
, srcX
, srcY
;
123 gulong srcX1
, srcX2
, srcY1
, srcY2
;
124 gulong ratioX
, ratioY
;
125 gulong aspectW
, aspectH
;
127 /* keep the aspect ratio */
129 aspectH
= (gint
)(dstW
* ((gdouble
)srcH
/ srcW
));
130 if (aspectH
> dstH
) {
132 aspectW
= (gint
)(dstH
* ((gdouble
)srcW
/ srcH
));
137 if (srcW
== dstW
&& srcH
== dstH
)
138 return NULL
; /* no scaling needed ! */
140 dststart
= dst
= g_new(RrPixel32
, dstW
* dstH
);
142 ratioX
= (srcW
<< FRACTION
) / dstW
;
143 ratioY
= (srcH
<< FRACTION
) / dstH
;
146 for (dstY
= 0; dstY
< dstH
; dstY
++) {
151 for (dstX
= 0; dstX
< dstW
; dstX
++) {
152 gulong red
= 0, green
= 0, blue
= 0, alpha
= 0;
153 gulong portionX
, portionY
, portionXY
, sumXY
= 0;
159 for (srcY
= srcY1
; srcY
< srcY2
; srcY
+= (1UL << FRACTION
)) {
162 portionY
= (1UL << FRACTION
) - (srcY1
- srcY
);
163 if (portionY
> srcY2
- srcY1
)
164 portionY
= srcY2
- srcY1
;
166 else if (srcY
== FLOOR(srcY2
))
167 portionY
= srcY2
- srcY
;
169 portionY
= (1UL << FRACTION
);
171 for (srcX
= srcX1
; srcX
< srcX2
; srcX
+= (1UL << FRACTION
)) {
174 portionX
= (1UL << FRACTION
) - (srcX1
- srcX
);
175 if (portionX
> srcX2
- srcX1
)
176 portionX
= srcX2
- srcX1
;
178 else if (srcX
== FLOOR(srcX2
))
179 portionX
= srcX2
- srcX
;
181 portionX
= (1UL << FRACTION
);
183 portionXY
= (portionX
* portionY
) >> FRACTION
;
186 pixel
= *(src
+ (srcY
>> FRACTION
) * srcW
187 + (srcX
>> FRACTION
));
188 red
+= ((pixel
>> RrDefaultRedOffset
) & 0xFF)
190 green
+= ((pixel
>> RrDefaultGreenOffset
) & 0xFF)
192 blue
+= ((pixel
>> RrDefaultBlueOffset
) & 0xFF)
194 alpha
+= ((pixel
>> RrDefaultAlphaOffset
) & 0xFF)
199 g_assert(sumXY
!= 0);
205 *dst
++ = (red
<< RrDefaultRedOffset
) |
206 (green
<< RrDefaultGreenOffset
) |
207 (blue
<< RrDefaultBlueOffset
) |
208 (alpha
<< RrDefaultAlphaOffset
);
212 pic
= g_new(RrImagePic
, 1);
213 RrImagePicInit(pic
, dstW
, dstH
, dststart
);
218 /*! This drawns an RGBA picture into the target, within the rectangle specified
219 by the area parameter. If the area's size differs from the source's then it
220 will be centered within the rectangle */
221 void DrawRGBA(RrPixel32
*target
, gint target_w
, gint target_h
,
222 RrPixel32
*source
, gint source_w
, gint source_h
,
223 gint alpha
, RrRect
*area
)
226 gint col
, num_pixels
;
229 g_assert(source_w
<= area
->width
&& source_h
<= area
->height
);
230 g_assert(area
->x
+ area
->width
<= target_w
);
231 g_assert(area
->y
+ area
->height
<= target_h
);
233 /* keep the aspect ratio */
235 dh
= (gint
)(dw
* ((gdouble
)source_h
/ source_w
));
236 if (dh
> area
->height
) {
238 dw
= (gint
)(dh
* ((gdouble
)source_w
/ source_h
));
241 /* copy source -> dest, and apply the alpha channel.
242 center the image if it is smaller than the area */
244 num_pixels
= dw
* dh
;
245 dest
= target
+ area
->x
+ (area
->width
- dw
) / 2 +
246 (target_w
* (area
->y
+ (area
->height
- dh
) / 2));
247 while (num_pixels
-- > 0) {
248 guchar a
, r
, g
, b
, bgr
, bgg
, bgb
;
250 /* apply the rgba's opacity as well */
251 a
= ((*source
>> RrDefaultAlphaOffset
) * alpha
) >> 8;
252 r
= *source
>> RrDefaultRedOffset
;
253 g
= *source
>> RrDefaultGreenOffset
;
254 b
= *source
>> RrDefaultBlueOffset
;
256 /* background color */
257 bgr
= *dest
>> RrDefaultRedOffset
;
258 bgg
= *dest
>> RrDefaultGreenOffset
;
259 bgb
= *dest
>> RrDefaultBlueOffset
;
261 r
= bgr
+ (((r
- bgr
) * a
) >> 8);
262 g
= bgg
+ (((g
- bgg
) * a
) >> 8);
263 b
= bgb
+ (((b
- bgb
) * a
) >> 8);
265 *dest
= ((r
<< RrDefaultRedOffset
) |
266 (g
<< RrDefaultGreenOffset
) |
267 (b
<< RrDefaultBlueOffset
));
274 dest
+= target_w
- dw
;
279 /*! Draw an RGBA texture into a target pixel buffer. */
280 void RrImageDrawRGBA(RrPixel32
*target
, RrTextureRGBA
*rgba
,
281 gint target_w
, gint target_h
,
286 scaled
= ResizeImage(rgba
->data
, rgba
->width
, rgba
->height
,
287 area
->width
, area
->height
);
291 g_warning("Scaling an RGBA! You should avoid this and just make "
292 "it the right size yourself!");
294 DrawRGBA(target
, target_w
, target_h
,
295 scaled
->data
, scaled
->width
, scaled
->height
,
299 DrawRGBA(target
, target_w
, target_h
,
300 rgba
->data
, rgba
->width
, rgba
->height
,
304 /*! Create a new RrImage, which is linked to an image cache */
305 RrImage
* RrImageNew(RrImageCache
*cache
)
309 g_assert(cache
!= NULL
);
311 self
= g_new0(RrImage
, 1);
317 void RrImageRef(RrImage
*self
)
322 void RrImageUnref(RrImage
*self
)
324 if (self
&& --self
->ref
== 0) {
326 g_message("Refcount to 0, removing ALL pictures from the cache:\n "
327 "Image 0x%x", (guint
)self
);
329 while (self
->n_original
> 0)
330 RemovePicture(self
, &self
->original
, 0, &self
->n_original
);
331 while (self
->n_resized
> 0)
332 RemovePicture(self
, &self
->resized
, 0, &self
->n_resized
);
337 /*! Add a new picture with the given RGBA pixel data and dimensions into the
338 RrImage. This adds an "original" picture to the image.
340 void RrImageAddPicture(RrImage
*self
, RrPixel32
*data
, gint w
, gint h
)
345 /* make sure we don't already have this size.. */
346 for (i
= 0; i
< self
->n_original
; ++i
)
347 if (self
->original
[i
]->width
== w
&& self
->original
[i
]->height
== h
) {
349 g_message("Found duplicate ORIGINAL image:\n "
350 "Image 0x%x, w %d h %d", (guint
)self
, w
, h
);
355 /* remove any resized pictures of this same size */
356 for (i
= 0; i
< self
->n_resized
; ++i
)
357 if (self
->resized
[i
]->width
== w
|| self
->resized
[i
]->height
== h
) {
358 RemovePicture(self
, &self
->resized
, i
, &self
->n_resized
);
362 /* add the new picture */
363 pic
= g_new(RrImagePic
, 1);
364 RrImagePicInit(pic
, w
, h
, g_memdup(data
, w
*h
*sizeof(RrPixel32
)));
365 AddPicture(self
, &self
->original
, &self
->n_original
, pic
);
368 /*! Remove the picture from the RrImage which has the given dimensions. This
369 removes an "original" picture from the image.
371 void RrImageRemovePicture(RrImage
*self
, gint w
, gint h
)
375 /* remove any resized pictures of this same size */
376 for (i
= 0; i
< self
->n_original
; ++i
)
377 if (self
->original
[i
]->width
== w
&& self
->original
[i
]->height
== h
) {
378 RemovePicture(self
, &self
->original
, i
, &self
->n_original
);
383 /*! Draw an RrImage texture into a target pixel buffer. If the RrImage does
384 not contain a picture of the appropriate size, then one of its "original"
385 pictures will be resized and used (and stored in the RrImage as a "resized"
388 void RrImageDrawImage(RrPixel32
*target
, RrTextureImage
*img
,
389 gint target_w
, gint target_h
,
392 gint i
, min_diff
, min_i
, min_aspect_diff
, min_aspect_i
;
401 /* is there an original of this size? (only w or h has to be right cuz
402 we maintain aspect ratios) */
403 for (i
= 0; i
< self
->n_original
; ++i
)
404 if (self
->original
[i
]->width
== area
->width
||
405 self
->original
[i
]->height
== area
->height
)
407 pic
= self
->original
[i
];
411 /* is there a resize of this size? */
412 for (i
= 0; i
< self
->n_resized
; ++i
)
413 if (self
->resized
[i
]->width
== area
->width
||
414 self
->resized
[i
]->height
== area
->height
)
419 /* save the selected one */
420 saved
= self
->resized
[i
];
422 /* shift all the others down */
423 for (j
= i
; j
> 0; --j
)
424 self
->resized
[j
] = self
->resized
[j
-1];
426 /* and move the selected one to the top of the list */
427 self
->resized
[0] = saved
;
429 pic
= self
->resized
[0];
436 /* find an original with a close size */
437 min_diff
= min_aspect_diff
= -1;
438 min_i
= min_aspect_i
= 0;
439 aspect
= ((gdouble
)area
->width
) / area
->height
;
440 for (i
= 0; i
< self
->n_original
; ++i
) {
445 /* our size difference metric.. */
446 wdiff
= self
->original
[i
]->width
- area
->width
;
447 hdiff
= self
->original
[i
]->height
- area
->height
;
448 diff
= (wdiff
* wdiff
) + (hdiff
* hdiff
);
450 /* find the smallest difference */
451 if (min_diff
< 0 || diff
< min_diff
) {
455 /* and also find the smallest difference with the same aspect
456 ratio (and prefer this one) */
457 myasp
= ((gdouble
)self
->original
[i
]->width
) /
458 self
->original
[i
]->height
;
459 if (ABS(aspect
- myasp
) < 0.0000001 &&
460 (min_aspect_diff
< 0 || diff
< min_aspect_diff
))
462 min_aspect_diff
= diff
;
467 /* use the aspect ratio correct source if there is one */
468 if (min_aspect_i
>= 0)
469 min_i
= min_aspect_i
;
471 /* resize the original to the given area */
472 pic
= ResizeImage(self
->original
[min_i
]->data
,
473 self
->original
[min_i
]->width
,
474 self
->original
[min_i
]->height
,
475 area
->width
, area
->height
);
477 /* add the resized image to the image, as the first in the resized
479 if (self
->n_resized
>= self
->cache
->max_resized_saved
)
480 /* remove the last one (last used one) */
481 RemovePicture(self
, &self
->resized
, self
->n_resized
- 1,
483 if (self
->cache
->max_resized_saved
)
484 /* add it to the top of the resized list */
485 AddPicture(self
, &self
->resized
, &self
->n_resized
, pic
);
487 free_pic
= TRUE
; /* don't leak mem! */
490 g_assert(pic
!= NULL
);
492 DrawRGBA(target
, target_w
, target_h
,
493 pic
->data
, pic
->width
, pic
->height
,
This page took 0.058024 seconds and 4 git commands to generate.