]>
Dogcows Code - chaz/openbox/blob - render/image.c
d0633d948d0a3b1f9f4edc5b24f26f316e8eaa82
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 /*! Add a picture to an Image, that is, add another copy of the image at
44 another size. This may add it to the "originals" list or to the
46 static void AddPicture(RrImage
*self
, RrImagePic
***list
, gint
*len
,
51 g_assert(pic
->width
> 0 && pic
->height
> 0);
53 g_assert(g_hash_table_lookup(self
->cache
->table
, pic
) == NULL
);
56 *list
= g_renew(RrImagePic
*, *list
, ++*len
);
58 /* move everything else down one */
59 for (i
= *len
-1; i
> 0; --i
)
60 (*list
)[i
] = (*list
)[i
-1];
62 /* set the new picture up at the front of the list */
65 /* add the picture as a key to point to this image in the cache */
66 g_hash_table_insert(self
->cache
->table
, (*list
)[0], self
);
69 g_message("Adding %s picture to the cache:\n "
70 "Image 0x%x, w %d h %d Hash %u\n",
71 (*list
== self
->original
? "ORIGINAL" : "RESIZED"),
72 (guint
)self
, pic
->width
, pic
->height
, RrImagePicHash(pic
));
76 /*! Remove a picture from an Image. This may remove it from the "originals"
77 list or the "resized" list. */
78 static void RemovePicture(RrImage
*self
, RrImagePic
***list
,
84 g_message("Removing %s picture from the cache:\n "
85 "Image 0x%x, w %d h %d Hash %u\n",
86 (*list
== self
->original
? "ORIGINAL" : "RESIZED"),
87 (guint
)self
, (*list
)[i
]->width
, (*list
)[i
]->height
,
88 RrImagePicHash((*list
)[i
]));
91 /* remove the picture as a key in the cache */
92 g_hash_table_remove(self
->cache
->table
, (*list
)[i
]);
94 /* free the picture (and its rgba data) */
96 g_free((*list
)[i
]->data
);
97 /* shift everything down one */
98 for (j
= i
; j
< *len
-1; ++j
)
99 (*list
)[j
] = (*list
)[j
+1];
100 /* shrink the list */
101 *list
= g_renew(RrImagePic
*, *list
, --*len
);
104 /*! Given a picture in RGBA format, of a specified size, resize it to the new
105 requested size (but keep its aspect ratio). If the image does not need to
106 be resized (it is already the right size) then this returns NULL. Otherwise
107 it returns a newly allocated RrImagePic with the resized picture inside it
109 static RrImagePic
* ResizeImage(RrPixel32
*src
,
110 gulong srcW
, gulong srcH
,
111 gulong dstW
, gulong dstH
)
113 RrPixel32
*dst
, *dststart
;
115 gulong dstX
, dstY
, srcX
, srcY
;
116 gulong srcX1
, srcX2
, srcY1
, srcY2
;
117 gulong ratioX
, ratioY
;
118 gulong aspectW
, aspectH
;
120 /* keep the aspect ratio */
122 aspectH
= (gint
)(dstW
* ((gdouble
)srcH
/ srcW
));
123 if (aspectH
> dstH
) {
125 aspectW
= (gint
)(dstH
* ((gdouble
)srcW
/ srcH
));
130 if (srcW
== dstW
&& srcH
== dstH
)
131 return NULL
; /* no scaling needed ! */
133 dststart
= dst
= g_new(RrPixel32
, dstW
* dstH
);
135 ratioX
= (srcW
<< FRACTION
) / dstW
;
136 ratioY
= (srcH
<< FRACTION
) / dstH
;
139 for (dstY
= 0; dstY
< dstH
; dstY
++) {
144 for (dstX
= 0; dstX
< dstW
; dstX
++) {
145 gulong red
= 0, green
= 0, blue
= 0, alpha
= 0;
146 gulong portionX
, portionY
, portionXY
, sumXY
= 0;
152 for (srcY
= srcY1
; srcY
< srcY2
; srcY
+= (1UL << FRACTION
)) {
155 portionY
= (1UL << FRACTION
) - (srcY1
- srcY
);
156 if (portionY
> srcY2
- srcY1
)
157 portionY
= srcY2
- srcY1
;
159 else if (srcY
== FLOOR(srcY2
))
160 portionY
= srcY2
- srcY
;
162 portionY
= (1UL << FRACTION
);
164 for (srcX
= srcX1
; srcX
< srcX2
; srcX
+= (1UL << FRACTION
)) {
167 portionX
= (1UL << FRACTION
) - (srcX1
- srcX
);
168 if (portionX
> srcX2
- srcX1
)
169 portionX
= srcX2
- srcX1
;
171 else if (srcX
== FLOOR(srcX2
))
172 portionX
= srcX2
- srcX
;
174 portionX
= (1UL << FRACTION
);
176 portionXY
= (portionX
* portionY
) >> FRACTION
;
179 pixel
= *(src
+ (srcY
>> FRACTION
) * srcW
180 + (srcX
>> FRACTION
));
181 red
+= ((pixel
>> RrDefaultRedOffset
) & 0xFF)
183 green
+= ((pixel
>> RrDefaultGreenOffset
) & 0xFF)
185 blue
+= ((pixel
>> RrDefaultBlueOffset
) & 0xFF)
187 alpha
+= ((pixel
>> RrDefaultAlphaOffset
) & 0xFF)
192 g_assert(sumXY
!= 0);
198 *dst
++ = (red
<< RrDefaultRedOffset
) |
199 (green
<< RrDefaultGreenOffset
) |
200 (blue
<< RrDefaultBlueOffset
) |
201 (alpha
<< RrDefaultAlphaOffset
);
205 pic
= g_new(RrImagePic
, 1);
206 RrImagePicInit(pic
, dstW
, dstH
, dststart
);
211 /*! This drawns an RGBA picture into the target, within the rectangle specified
212 by the area parameter. If the area's size differs from the source's then it
213 will be centered within the rectangle */
214 void DrawRGBA(RrPixel32
*target
, gint target_w
, gint target_h
,
215 RrPixel32
*source
, gint source_w
, gint source_h
,
216 gint alpha
, RrRect
*area
)
219 gint col
, num_pixels
;
222 g_assert(source_w
<= area
->width
&& source_h
<= area
->height
);
223 g_assert(area
->x
+ area
->width
<= target_w
);
224 g_assert(area
->y
+ area
->height
<= target_h
);
226 /* keep the aspect ratio */
228 dh
= (gint
)(dw
* ((gdouble
)source_h
/ source_w
));
229 if (dh
> area
->height
) {
231 dw
= (gint
)(dh
* ((gdouble
)source_w
/ source_h
));
234 /* copy source -> dest, and apply the alpha channel.
235 center the image if it is smaller than the area */
237 num_pixels
= dw
* dh
;
238 dest
= target
+ area
->x
+ (area
->width
- dw
) / 2 +
239 (target_w
* (area
->y
+ (area
->height
- dh
) / 2));
240 while (num_pixels
-- > 0) {
241 guchar a
, r
, g
, b
, bgr
, bgg
, bgb
;
243 /* apply the rgba's opacity as well */
244 a
= ((*source
>> RrDefaultAlphaOffset
) * alpha
) >> 8;
245 r
= *source
>> RrDefaultRedOffset
;
246 g
= *source
>> RrDefaultGreenOffset
;
247 b
= *source
>> RrDefaultBlueOffset
;
249 /* background color */
250 bgr
= *dest
>> RrDefaultRedOffset
;
251 bgg
= *dest
>> RrDefaultGreenOffset
;
252 bgb
= *dest
>> RrDefaultBlueOffset
;
254 r
= bgr
+ (((r
- bgr
) * a
) >> 8);
255 g
= bgg
+ (((g
- bgg
) * a
) >> 8);
256 b
= bgb
+ (((b
- bgb
) * a
) >> 8);
258 *dest
= ((r
<< RrDefaultRedOffset
) |
259 (g
<< RrDefaultGreenOffset
) |
260 (b
<< RrDefaultBlueOffset
));
267 dest
+= target_w
- dw
;
272 /*! Draw an RGBA texture into a target pixel buffer. */
273 void RrImageDrawRGBA(RrPixel32
*target
, RrTextureRGBA
*rgba
,
274 gint target_w
, gint target_h
,
279 scaled
= ResizeImage(rgba
->data
, rgba
->width
, rgba
->height
,
280 area
->width
, area
->height
);
284 g_warning("Scaling an RGBA! You should avoid this and just make "
285 "it the right size yourself!");
287 DrawRGBA(target
, target_w
, target_h
,
288 scaled
->data
, scaled
->width
, scaled
->height
,
292 DrawRGBA(target
, target_w
, target_h
,
293 rgba
->data
, rgba
->width
, rgba
->height
,
297 /*! Create a new RrImage, which is linked to an image cache */
298 RrImage
* RrImageNew(RrImageCache
*cache
)
302 g_assert(cache
!= NULL
);
304 self
= g_new0(RrImage
, 1);
310 void RrImageRef(RrImage
*self
)
315 void RrImageUnref(RrImage
*self
)
317 if (self
&& --self
->ref
== 0) {
319 g_message("Refcount to 0, removing ALL pictures from the cache:\n "
320 "Image 0x%x\n", (guint
)self
);
322 while (self
->n_original
> 0)
323 RemovePicture(self
, &self
->original
, 0, &self
->n_original
);
324 while (self
->n_resized
> 0)
325 RemovePicture(self
, &self
->resized
, 0, &self
->n_resized
);
330 /*! Add a new picture with the given RGBA pixel data and dimensions into the
331 RrImage. This adds an "original" picture to the image.
333 void RrImageAddPicture(RrImage
*self
, RrPixel32
*data
, gint w
, gint h
)
338 /* make sure we don't already have this size.. */
339 for (i
= 0; i
< self
->n_original
; ++i
)
340 if (self
->original
[i
]->width
== w
&& self
->original
[i
]->height
== h
) {
342 g_message("Found duplicate ORIGINAL image:\n "
343 "Image 0x%x, w %d h %d\n", (guint
)self
, w
, h
);
348 /* remove any resized pictures of this same size */
349 for (i
= 0; i
< self
->n_resized
; ++i
)
350 if (self
->resized
[i
]->width
== w
|| self
->resized
[i
]->height
== h
) {
351 RemovePicture(self
, &self
->resized
, i
, &self
->n_resized
);
355 /* add the new picture */
356 pic
= g_new(RrImagePic
, 1);
357 RrImagePicInit(pic
, w
, h
, g_memdup(data
, w
*h
*sizeof(RrPixel32
)));
358 AddPicture(self
, &self
->original
, &self
->n_original
, pic
);
361 /*! Remove the picture from the RrImage which has the given dimensions. This
362 removes an "original" picture from the image.
364 void RrImageRemovePicture(RrImage
*self
, gint w
, gint h
)
368 /* remove any resized pictures of this same size */
369 for (i
= 0; i
< self
->n_original
; ++i
)
370 if (self
->original
[i
]->width
== w
&& self
->original
[i
]->height
== h
) {
371 RemovePicture(self
, &self
->original
, i
, &self
->n_original
);
376 /*! Draw an RrImage texture into a target pixel buffer. If the RrImage does
377 not contain a picture of the appropriate size, then one of its "original"
378 pictures will be resized and used (and stored in the RrImage as a "resized"
381 void RrImageDrawImage(RrPixel32
*target
, RrTextureImage
*img
,
382 gint target_w
, gint target_h
,
385 gint i
, min_diff
, min_i
, min_aspect_diff
, min_aspect_i
;
392 /* is there an original of this size? (only w or h has to be right cuz
393 we maintain aspect ratios) */
394 for (i
= 0; i
< self
->n_original
; ++i
)
395 if (self
->original
[i
]->width
== area
->width
||
396 self
->original
[i
]->height
== area
->height
)
398 pic
= self
->original
[i
];
402 /* is there a resize of this size? */
403 for (i
= 0; i
< self
->n_resized
; ++i
)
404 if (self
->resized
[i
]->width
== area
->width
||
405 self
->resized
[i
]->height
== area
->height
)
410 /* save the selected one */
411 saved
= self
->resized
[i
];
413 /* shift all the others down */
414 for (j
= i
; j
> 0; --j
)
415 self
->resized
[j
] = self
->resized
[j
-1];
417 /* and move the selected one to the top of the list */
418 self
->resized
[0] = saved
;
420 pic
= self
->resized
[0];
427 /* find an original with a close size */
428 min_diff
= min_aspect_diff
= -1;
429 min_i
= min_aspect_i
= 0;
430 aspect
= ((gdouble
)area
->width
) / area
->height
;
431 for (i
= 0; i
< self
->n_original
; ++i
) {
436 /* our size difference metric.. */
437 wdiff
= self
->original
[i
]->width
- area
->width
;
438 hdiff
= self
->original
[i
]->height
- area
->height
;
439 diff
= (wdiff
* wdiff
) + (hdiff
* hdiff
);
441 /* find the smallest difference */
442 if (min_diff
< 0 || diff
< min_diff
) {
446 /* and also find the smallest difference with the same aspect
447 ratio (and prefer this one) */
448 myasp
= ((gdouble
)self
->original
[i
]->width
) /
449 self
->original
[i
]->height
;
450 if (ABS(aspect
- myasp
) < 0.0000001 &&
451 (min_aspect_diff
< 0 || diff
< min_aspect_diff
))
453 min_aspect_diff
= diff
;
458 /* use the aspect ratio correct source if there is one */
459 if (min_aspect_i
>= 0)
460 min_i
= min_aspect_i
;
462 /* resize the original to the given area */
463 pic
= ResizeImage(self
->original
[min_i
]->data
,
464 self
->original
[min_i
]->width
,
465 self
->original
[min_i
]->height
,
466 area
->width
, area
->height
);
468 /* add the resized image to the image, as the first in the resized
470 if (self
->n_resized
>= self
->cache
->max_resized_saved
)
471 /* remove the last one (last used one) */
472 RemovePicture(self
, &self
->resized
, self
->n_resized
- 1,
474 if (self
->cache
->max_resized_saved
)
475 /* add it to the top of the resized list */
476 AddPicture(self
, &self
->resized
, &self
->n_resized
, pic
);
479 g_assert(pic
!= NULL
);
481 DrawRGBA(target
, target_w
, target_h
,
482 pic
->data
, pic
->width
, pic
->height
,
This page took 0.061935 seconds and 4 git commands to generate.