]>
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 /*! Add a picture to an Image, that is, add another copy of the image at
32 another size. This may add it to the "originals" list or to the
34 static void AddPicture(RrImage
*self
, RrImagePic
***list
, gint
*len
,
39 g_assert(pic
->width
> 0 && pic
->height
> 0);
41 g_assert(g_hash_table_lookup(self
->cache
->table
, pic
) == NULL
);
44 *list
= g_renew(RrImagePic
*, *list
, ++*len
);
46 /* move everything else down one */
47 for (i
= *len
-1; i
> 0; --i
)
48 (*list
)[i
] = (*list
)[i
-1];
50 /* set the new picture up at the front of the list */
53 /* add the picture as a key to point to this image in the cache */
54 g_hash_table_insert(self
->cache
->table
, (*list
)[0], self
);
57 g_print("Adding %s picture to the cache: "
58 "Image 0x%x, w %d h %d Hash %u\n",
59 (*list
== self
->original
? "ORIGINAL" : "RESIZED"),
60 (guint
)self
, pic
->width
, pic
->height
, RrImagePicHash(pic
));
64 /*! Remove a picture from an Image. This may remove it from the "originals"
65 list or the "resized" list. */
66 static void RemovePicture(RrImage
*self
, RrImagePic
***list
,
72 g_print("Removing %s picture from the cache: "
73 "Image 0x%x, w %d h %d Hash %u\n",
74 (*list
== self
->original
? "ORIGINAL" : "RESIZED"),
75 (guint
)self
, (*list
)[i
]->width
, (*list
)[i
]->height
,
76 RrImagePicHash((*list
)[i
]));
79 /* remove the picture as a key in the cache */
80 g_hash_table_remove(self
->cache
->table
, (*list
)[i
]);
82 /* free the picture (and its rgba data) */
84 g_free((*list
)[i
]->data
);
85 /* shift everything down one */
86 for (j
= i
; j
< *len
-1; ++j
)
87 (*list
)[j
] = (*list
)[j
+1];
89 *list
= g_renew(RrImagePic
*, *list
, --*len
);
92 /*! Given a picture in RGBA format, of a specified size, resize it to the new
93 requested size (but keep its aspect ratio). If the image does not need to
94 be resized (it is already the right size) then this returns NULL. Otherwise
95 it returns a newly allocated RrImagePic with the resized picture inside it
97 static RrImagePic
* ResizeImage(RrPixel32
*src
,
98 gulong srcW
, gulong srcH
,
99 gulong dstW
, gulong dstH
)
103 gulong dstX
, dstY
, srcX
, srcY
;
104 gulong srcX1
, srcX2
, srcY1
, srcY2
;
105 gulong ratioX
, ratioY
;
106 gulong aspectW
, aspectH
;
108 /* keep the aspect ratio */
110 aspectH
= (gint
)(dstW
* ((gdouble
)srcH
/ srcW
));
111 if (aspectH
> dstH
) {
113 aspectW
= (gint
)(dstH
* ((gdouble
)srcW
/ srcH
));
118 if (srcW
== dstW
&& srcH
== dstH
)
119 return NULL
; /* no scaling needed ! */
121 pic
= g_new(RrImagePic
, 1);
122 dst
= g_new(RrPixel32
, dstW
* dstH
);
127 ratioX
= (srcW
<< FRACTION
) / dstW
;
128 ratioY
= (srcH
<< FRACTION
) / dstH
;
131 for (dstY
= 0; dstY
< dstH
; dstY
++) {
136 for (dstX
= 0; dstX
< dstW
; dstX
++) {
137 gulong red
= 0, green
= 0, blue
= 0, alpha
= 0;
138 gulong portionX
, portionY
, portionXY
, sumXY
= 0;
144 for (srcY
= srcY1
; srcY
< srcY2
; srcY
+= (1UL << FRACTION
)) {
147 portionY
= (1UL << FRACTION
) - (srcY1
- srcY
);
148 if (portionY
> srcY2
- srcY1
)
149 portionY
= srcY2
- srcY1
;
151 else if (srcY
== FLOOR(srcY2
))
152 portionY
= srcY2
- srcY
;
154 portionY
= (1UL << FRACTION
);
156 for (srcX
= srcX1
; srcX
< srcX2
; srcX
+= (1UL << FRACTION
)) {
159 portionX
= (1UL << FRACTION
) - (srcX1
- srcX
);
160 if (portionX
> srcX2
- srcX1
)
161 portionX
= srcX2
- srcX1
;
163 else if (srcX
== FLOOR(srcX2
))
164 portionX
= srcX2
- srcX
;
166 portionX
= (1UL << FRACTION
);
168 portionXY
= (portionX
* portionY
) >> FRACTION
;
171 pixel
= *(src
+ (srcY
>> FRACTION
) * srcW
172 + (srcX
>> FRACTION
));
173 red
+= ((pixel
>> RrDefaultRedOffset
) & 0xFF)
175 green
+= ((pixel
>> RrDefaultGreenOffset
) & 0xFF)
177 blue
+= ((pixel
>> RrDefaultBlueOffset
) & 0xFF)
179 alpha
+= ((pixel
>> RrDefaultAlphaOffset
) & 0xFF)
184 g_assert(sumXY
!= 0);
190 *dst
++ = (red
<< RrDefaultRedOffset
) |
191 (green
<< RrDefaultGreenOffset
) |
192 (blue
<< RrDefaultBlueOffset
) |
193 (alpha
<< RrDefaultAlphaOffset
);
200 /*! This drawns an RGBA picture into the target, within the rectangle specified
201 by the area parameter. If the area's size differs from the source's then it
202 will be centered within the rectangle */
203 void DrawRGBA(RrPixel32
*target
, gint target_w
, gint target_h
,
204 RrPixel32
*source
, gint source_w
, gint source_h
,
205 gint alpha
, RrRect
*area
)
208 gint col
, num_pixels
;
211 g_assert(source_w
<= area
->width
&& source_h
<= area
->height
);
212 g_assert(area
->x
+ area
->width
<= target_w
);
213 g_assert(area
->y
+ area
->height
<= target_h
);
215 /* keep the aspect ratio */
217 dh
= (gint
)(dw
* ((gdouble
)source_h
/ source_w
));
218 if (dh
> area
->height
) {
220 dw
= (gint
)(dh
* ((gdouble
)source_w
/ source_h
));
223 /* copy source -> dest, and apply the alpha channel.
224 center the image if it is smaller than the area */
226 num_pixels
= dw
* dh
;
227 dest
= target
+ area
->x
+ (area
->width
- dw
) / 2 +
228 (target_w
* (area
->y
+ (area
->height
- dh
) / 2));
229 while (num_pixels
-- > 0) {
230 guchar a
, r
, g
, b
, bgr
, bgg
, bgb
;
232 /* apply the rgba's opacity as well */
233 a
= ((*source
>> RrDefaultAlphaOffset
) * alpha
) >> 8;
234 r
= *source
>> RrDefaultRedOffset
;
235 g
= *source
>> RrDefaultGreenOffset
;
236 b
= *source
>> RrDefaultBlueOffset
;
238 /* background color */
239 bgr
= *dest
>> RrDefaultRedOffset
;
240 bgg
= *dest
>> RrDefaultGreenOffset
;
241 bgb
= *dest
>> RrDefaultBlueOffset
;
243 r
= bgr
+ (((r
- bgr
) * a
) >> 8);
244 g
= bgg
+ (((g
- bgg
) * a
) >> 8);
245 b
= bgb
+ (((b
- bgb
) * a
) >> 8);
247 *dest
= ((r
<< RrDefaultRedOffset
) |
248 (g
<< RrDefaultGreenOffset
) |
249 (b
<< RrDefaultBlueOffset
));
256 dest
+= target_w
- dw
;
261 /*! Draw an RGBA texture into a target pixel buffer. */
262 void RrImageDrawRGBA(RrPixel32
*target
, RrTextureRGBA
*rgba
,
263 gint target_w
, gint target_h
,
268 scaled
= ResizeImage(rgba
->data
, rgba
->width
, rgba
->height
,
269 area
->width
, area
->height
);
273 g_warning("Scaling an RGBA! You should avoid this and just make "
274 "it the right size yourself!");
276 DrawRGBA(target
, target_w
, target_h
,
277 scaled
->data
, scaled
->width
, scaled
->height
,
281 DrawRGBA(target
, target_w
, target_h
,
282 rgba
->data
, rgba
->width
, rgba
->height
,
286 /*! Create a new RrImage, which is linked to an image cache */
287 RrImage
* RrImageNew(RrImageCache
*cache
)
291 g_assert(cache
!= NULL
);
293 self
= g_new0(RrImage
, 1);
299 void RrImageRef(RrImage
*self
)
304 void RrImageUnref(RrImage
*self
)
306 if (self
&& --self
->ref
== 0) {
308 g_print("Refcount to 0, removing ALL pictures from the cache: "
309 "Image 0x%x\n", (guint
)self
);
311 while (self
->n_original
> 0)
312 RemovePicture(self
, &self
->original
, 0, &self
->n_original
);
313 while (self
->n_resized
> 0)
314 RemovePicture(self
, &self
->resized
, 0, &self
->n_resized
);
319 /*! Add a new picture with the given RGBA pixel data and dimensions into the
320 RrImage. This adds an "original" picture to the image.
322 void RrImageAddPicture(RrImage
*self
, RrPixel32
*data
, gint w
, gint h
)
327 /* make sure we don't already have this size.. */
328 for (i
= 0; i
< self
->n_original
; ++i
)
329 if (self
->original
[i
]->width
== w
&& self
->original
[i
]->height
== h
) {
331 g_print("Found duplicate ORIGINAL image: "
332 "Image 0x%x, w %d h %d\n", (guint
)self
, w
, h
);
337 /* remove any resized pictures of this same size */
338 for (i
= 0; i
< self
->n_resized
; ++i
)
339 if (self
->resized
[i
]->width
== w
|| self
->resized
[i
]->height
== h
) {
340 RemovePicture(self
, &self
->resized
, i
, &self
->n_resized
);
344 /* add the new picture */
345 pic
= g_new(RrImagePic
, 1);
348 pic
->data
= g_memdup(data
, w
*h
*sizeof(RrPixel32
));
349 AddPicture(self
, &self
->original
, &self
->n_original
, pic
);
352 /*! Remove the picture from the RrImage which has the given dimensions. This
353 removes an "original" picture from the image.
355 void RrImageRemovePicture(RrImage
*self
, gint w
, gint h
)
359 /* remove any resized pictures of this same size */
360 for (i
= 0; i
< self
->n_original
; ++i
)
361 if (self
->original
[i
]->width
== w
&& self
->original
[i
]->height
== h
) {
362 RemovePicture(self
, &self
->original
, i
, &self
->n_original
);
367 /*! Draw an RrImage texture into a target pixel buffer. If the RrImage does
368 not contain a picture of the appropriate size, then one of its "original"
369 pictures will be resized and used (and stored in the RrImage as a "resized"
372 void RrImageDrawImage(RrPixel32
*target
, RrTextureImage
*img
,
373 gint target_w
, gint target_h
,
376 gint i
, min_diff
, min_i
, min_aspect_diff
, min_aspect_i
;
383 /* is there an original of this size? (only w or h has to be right cuz
384 we maintain aspect ratios) */
385 for (i
= 0; i
< self
->n_original
; ++i
)
386 if (self
->original
[i
]->width
== area
->width
||
387 self
->original
[i
]->height
== area
->height
)
389 pic
= self
->original
[i
];
393 /* is there a resize of this size? */
394 for (i
= 0; i
< self
->n_resized
; ++i
)
395 if (self
->resized
[i
]->width
== area
->width
||
396 self
->resized
[i
]->height
== area
->height
)
401 /* save the selected one */
402 saved
= self
->resized
[i
];
404 /* shift all the others down */
405 for (j
= i
; j
> 0; --j
)
406 self
->resized
[j
] = self
->resized
[j
-1];
408 /* and move the selected one to the top of the list */
409 self
->resized
[0] = saved
;
411 pic
= self
->resized
[0];
418 /* find an original with a close size */
419 min_diff
= min_aspect_diff
= -1;
420 min_i
= min_aspect_i
= 0;
421 aspect
= ((gdouble
)area
->width
) / area
->height
;
422 for (i
= 0; i
< self
->n_original
; ++i
) {
427 /* our size difference metric.. */
428 wdiff
= self
->original
[i
]->width
- area
->width
;
429 hdiff
= self
->original
[i
]->height
- area
->height
;
430 diff
= (wdiff
* wdiff
) + (hdiff
* hdiff
);
432 /* find the smallest difference */
433 if (min_diff
< 0 || diff
< min_diff
) {
437 /* and also find the smallest difference with the same aspect
438 ratio (and prefer this one) */
439 myasp
= ((gdouble
)self
->original
[i
]->width
) /
440 self
->original
[i
]->height
;
441 if (ABS(aspect
- myasp
) < 0.0000001 &&
442 (min_aspect_diff
< 0 || diff
< min_aspect_diff
))
444 min_aspect_diff
= diff
;
449 /* use the aspect ratio correct source if there is one */
450 if (min_aspect_i
>= 0)
451 min_i
= min_aspect_i
;
453 /* resize the original to the given area */
454 pic
= ResizeImage(self
->original
[min_i
]->data
,
455 self
->original
[min_i
]->width
,
456 self
->original
[min_i
]->height
,
457 area
->width
, area
->height
);
459 /* add the resized image to the image, as the first in the resized
461 if (self
->n_resized
>= self
->cache
->max_resized_saved
)
462 /* remove the last one (last used one) */
463 RemovePicture(self
, &self
->resized
, self
->n_resized
- 1,
465 if (self
->cache
->max_resized_saved
)
466 /* add it to the top of the resized list */
467 AddPicture(self
, &self
->resized
, &self
->n_resized
, pic
);
470 g_assert(pic
!= NULL
);
472 DrawRGBA(target
, target_w
, target_h
,
473 pic
->data
, pic
->width
, pic
->height
,
This page took 0.059957 seconds and 4 git commands to generate.