]>
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
);
78 g_message("Adding %s picture to the cache:\n "
79 "Image 0x%x, w %d h %d Hash %u",
80 (*list == self->original ? "ORIGINAL" : "RESIZED"),
81 (guint)self, pic->width, pic->height, RrImagePicHash(pic));
86 /*! Remove a picture from an Image. This may remove it from the "originals"
87 list or the "resized" list. */
88 static void RemovePicture(RrImage
*self
, RrImagePic
***list
,
95 g_message("Removing %s picture from the cache:\n "
96 "Image 0x%x, w %d h %d Hash %u",
97 (*list == self->original ? "ORIGINAL" : "RESIZED"),
98 (guint)self, (*list)[i]->width, (*list)[i]->height,
99 RrImagePicHash((*list)[i]));
103 /* remove the picture as a key in the cache */
104 g_hash_table_remove(self
->cache
->table
, (*list
)[i
]);
106 /* free the picture */
107 RrImagePicFree((*list
)[i
]);
108 /* shift everything down one */
109 for (j
= i
; j
< *len
-1; ++j
)
110 (*list
)[j
] = (*list
)[j
+1];
111 /* shrink the list */
112 *list
= g_renew(RrImagePic
*, *list
, --*len
);
115 /*! Given a picture in RGBA format, of a specified size, resize it to the new
116 requested size (but keep its aspect ratio). If the image does not need to
117 be resized (it is already the right size) then this returns NULL. Otherwise
118 it returns a newly allocated RrImagePic with the resized picture inside it
120 static RrImagePic
* ResizeImage(RrPixel32
*src
,
121 gulong srcW
, gulong srcH
,
122 gulong dstW
, gulong dstH
)
124 RrPixel32
*dst
, *dststart
;
126 gulong dstX
, dstY
, srcX
, srcY
;
127 gulong srcX1
, srcX2
, srcY1
, srcY2
;
128 gulong ratioX
, ratioY
;
129 gulong aspectW
, aspectH
;
131 /* XXX should these variables be ensured to not be zero in the callers? */
132 srcW
= srcW
? srcW
: 1;
133 srcH
= srcH
? srcH
: 1;
134 dstW
= dstW
? dstW
: 1;
135 dstH
= dstH
? dstH
: 1;
137 /* keep the aspect ratio */
139 aspectH
= (gint
)(dstW
* ((gdouble
)srcH
/ srcW
));
140 if (aspectH
> dstH
) {
142 aspectW
= (gint
)(dstH
* ((gdouble
)srcW
/ srcH
));
144 dstW
= aspectW
? aspectW
: 1;
145 dstH
= aspectH
? aspectH
: 1;
147 if (srcW
== dstW
&& srcH
== dstH
)
148 return NULL
; /* no scaling needed! */
150 dststart
= dst
= g_new(RrPixel32
, dstW
* dstH
);
152 ratioX
= (srcW
<< FRACTION
) / dstW
;
153 ratioY
= (srcH
<< FRACTION
) / dstH
;
156 for (dstY
= 0; dstY
< dstH
; dstY
++) {
161 for (dstX
= 0; dstX
< dstW
; dstX
++) {
162 gulong red
= 0, green
= 0, blue
= 0, alpha
= 0;
163 gulong portionX
, portionY
, portionXY
, sumXY
= 0;
169 for (srcY
= srcY1
; srcY
< srcY2
; srcY
+= (1UL << FRACTION
)) {
172 portionY
= (1UL << FRACTION
) - (srcY1
- srcY
);
173 if (portionY
> srcY2
- srcY1
)
174 portionY
= srcY2
- srcY1
;
176 else if (srcY
== FLOOR(srcY2
))
177 portionY
= srcY2
- srcY
;
179 portionY
= (1UL << FRACTION
);
181 for (srcX
= srcX1
; srcX
< srcX2
; srcX
+= (1UL << FRACTION
)) {
184 portionX
= (1UL << FRACTION
) - (srcX1
- srcX
);
185 if (portionX
> srcX2
- srcX1
)
186 portionX
= srcX2
- srcX1
;
188 else if (srcX
== FLOOR(srcX2
))
189 portionX
= srcX2
- srcX
;
191 portionX
= (1UL << FRACTION
);
193 portionXY
= (portionX
* portionY
) >> FRACTION
;
196 pixel
= *(src
+ (srcY
>> FRACTION
) * srcW
197 + (srcX
>> FRACTION
));
198 red
+= ((pixel
>> RrDefaultRedOffset
) & 0xFF)
200 green
+= ((pixel
>> RrDefaultGreenOffset
) & 0xFF)
202 blue
+= ((pixel
>> RrDefaultBlueOffset
) & 0xFF)
204 alpha
+= ((pixel
>> RrDefaultAlphaOffset
) & 0xFF)
209 g_assert(sumXY
!= 0);
215 *dst
++ = (red
<< RrDefaultRedOffset
) |
216 (green
<< RrDefaultGreenOffset
) |
217 (blue
<< RrDefaultBlueOffset
) |
218 (alpha
<< RrDefaultAlphaOffset
);
222 pic
= g_new(RrImagePic
, 1);
223 RrImagePicInit(pic
, dstW
, dstH
, dststart
);
228 /*! This draws an RGBA picture into the target, within the rectangle specified
229 by the area parameter. If the area's size differs from the source's then it
230 will be centered within the rectangle */
231 void DrawRGBA(RrPixel32
*target
, gint target_w
, gint target_h
,
232 RrPixel32
*source
, gint source_w
, gint source_h
,
233 gint alpha
, RrRect
*area
)
236 gint col
, num_pixels
;
239 g_assert(source_w
<= area
->width
&& source_h
<= area
->height
);
240 g_assert(area
->x
+ area
->width
<= target_w
);
241 g_assert(area
->y
+ area
->height
<= target_h
);
243 /* keep the aspect ratio */
245 dh
= (gint
)(dw
* ((gdouble
)source_h
/ source_w
));
246 if (dh
> area
->height
) {
248 dw
= (gint
)(dh
* ((gdouble
)source_w
/ source_h
));
251 /* copy source -> dest, and apply the alpha channel.
252 center the image if it is smaller than the area */
254 num_pixels
= dw
* dh
;
255 dest
= target
+ area
->x
+ (area
->width
- dw
) / 2 +
256 (target_w
* (area
->y
+ (area
->height
- dh
) / 2));
257 while (num_pixels
-- > 0) {
258 guchar a
, r
, g
, b
, bgr
, bgg
, bgb
;
260 /* apply the rgba's opacity as well */
261 a
= ((*source
>> RrDefaultAlphaOffset
) * alpha
) >> 8;
262 r
= *source
>> RrDefaultRedOffset
;
263 g
= *source
>> RrDefaultGreenOffset
;
264 b
= *source
>> RrDefaultBlueOffset
;
266 /* background color */
267 bgr
= *dest
>> RrDefaultRedOffset
;
268 bgg
= *dest
>> RrDefaultGreenOffset
;
269 bgb
= *dest
>> RrDefaultBlueOffset
;
271 r
= bgr
+ (((r
- bgr
) * a
) >> 8);
272 g
= bgg
+ (((g
- bgg
) * a
) >> 8);
273 b
= bgb
+ (((b
- bgb
) * a
) >> 8);
275 *dest
= ((r
<< RrDefaultRedOffset
) |
276 (g
<< RrDefaultGreenOffset
) |
277 (b
<< RrDefaultBlueOffset
));
284 dest
+= target_w
- dw
;
289 /*! Draw an RGBA texture into a target pixel buffer. */
290 void RrImageDrawRGBA(RrPixel32
*target
, RrTextureRGBA
*rgba
,
291 gint target_w
, gint target_h
,
296 scaled
= ResizeImage(rgba
->data
, rgba
->width
, rgba
->height
,
297 area
->width
, area
->height
);
301 g_warning("Scaling an RGBA! You should avoid this and just make "
302 "it the right size yourself!");
304 DrawRGBA(target
, target_w
, target_h
,
305 scaled
->data
, scaled
->width
, scaled
->height
,
307 RrImagePicFree(scaled
);
310 DrawRGBA(target
, target_w
, target_h
,
311 rgba
->data
, rgba
->width
, rgba
->height
,
315 /*! Create a new RrImage, which is linked to an image cache */
316 RrImage
* RrImageNew(RrImageCache
*cache
)
320 g_assert(cache
!= NULL
);
322 self
= g_new0(RrImage
, 1);
328 void RrImageRef(RrImage
*self
)
333 void RrImageUnref(RrImage
*self
)
335 if (self
&& --self
->ref
== 0) {
338 g_message("Refcount to 0, removing ALL pictures from the cache:\n "
339 "Image 0x%x", (guint)self);
342 while (self
->n_original
> 0)
343 RemovePicture(self
, &self
->original
, 0, &self
->n_original
);
344 while (self
->n_resized
> 0)
345 RemovePicture(self
, &self
->resized
, 0, &self
->n_resized
);
350 /*! Add a new picture with the given RGBA pixel data and dimensions into the
351 RrImage. This adds an "original" picture to the image.
353 void RrImageAddPicture(RrImage
*self
, RrPixel32
*data
, gint w
, gint h
)
358 /* make sure we don't already have this size.. */
359 for (i
= 0; i
< self
->n_original
; ++i
)
360 if (self
->original
[i
]->width
== w
&& self
->original
[i
]->height
== h
) {
363 g_message("Found duplicate ORIGINAL image:\n "
364 "Image 0x%x, w %d h %d", (guint)self, w, h);
370 /* remove any resized pictures of this same size */
371 for (i
= 0; i
< self
->n_resized
; ++i
)
372 if (self
->resized
[i
]->width
== w
|| self
->resized
[i
]->height
== h
) {
373 RemovePicture(self
, &self
->resized
, i
, &self
->n_resized
);
377 /* add the new picture */
378 pic
= g_new(RrImagePic
, 1);
379 RrImagePicInit(pic
, w
, h
, g_memdup(data
, w
*h
*sizeof(RrPixel32
)));
380 AddPicture(self
, &self
->original
, &self
->n_original
, pic
);
383 /*! Remove the picture from the RrImage which has the given dimensions. This
384 removes an "original" picture from the image.
386 void RrImageRemovePicture(RrImage
*self
, gint w
, gint h
)
390 /* remove any resized pictures of this same size */
391 for (i
= 0; i
< self
->n_original
; ++i
)
392 if (self
->original
[i
]->width
== w
&& self
->original
[i
]->height
== h
) {
393 RemovePicture(self
, &self
->original
, i
, &self
->n_original
);
398 /*! Draw an RrImage texture into a target pixel buffer. If the RrImage does
399 not contain a picture of the appropriate size, then one of its "original"
400 pictures will be resized and used (and stored in the RrImage as a "resized"
403 void RrImageDrawImage(RrPixel32
*target
, RrTextureImage
*img
,
404 gint target_w
, gint target_h
,
407 gint i
, min_diff
, min_i
, min_aspect_diff
, min_aspect_i
;
416 /* is there an original of this size? (only the larger of
417 w or h has to be right cuz we maintain aspect ratios) */
418 for (i
= 0; i
< self
->n_original
; ++i
)
419 if ((self
->original
[i
]->width
>= self
->original
[i
]->height
&&
420 self
->original
[i
]->width
== area
->width
) ||
421 (self
->original
[i
]->width
<= self
->original
[i
]->height
&&
422 self
->original
[i
]->height
== area
->height
))
424 pic
= self
->original
[i
];
428 /* is there a resize of this size? */
429 for (i
= 0; i
< self
->n_resized
; ++i
)
430 if ((self
->resized
[i
]->width
>= self
->resized
[i
]->height
&&
431 self
->resized
[i
]->width
== area
->width
) ||
432 (self
->resized
[i
]->width
<= self
->resized
[i
]->height
&&
433 self
->resized
[i
]->height
== area
->height
))
438 /* save the selected one */
439 saved
= self
->resized
[i
];
441 /* shift all the others down */
442 for (j
= i
; j
> 0; --j
)
443 self
->resized
[j
] = self
->resized
[j
-1];
445 /* and move the selected one to the top of the list */
446 self
->resized
[0] = saved
;
448 pic
= self
->resized
[0];
455 /* find an original with a close size */
456 min_diff
= min_aspect_diff
= -1;
457 min_i
= min_aspect_i
= 0;
458 aspect
= ((gdouble
)area
->width
) / area
->height
;
459 for (i
= 0; i
< self
->n_original
; ++i
) {
464 /* our size difference metric.. */
465 wdiff
= self
->original
[i
]->width
- area
->width
;
466 hdiff
= self
->original
[i
]->height
- area
->height
;
467 diff
= (wdiff
* wdiff
) + (hdiff
* hdiff
);
469 /* find the smallest difference */
470 if (min_diff
< 0 || diff
< min_diff
) {
474 /* and also find the smallest difference with the same aspect
475 ratio (and prefer this one) */
476 myasp
= ((gdouble
)self
->original
[i
]->width
) /
477 self
->original
[i
]->height
;
478 if (ABS(aspect
- myasp
) < 0.0000001 &&
479 (min_aspect_diff
< 0 || diff
< min_aspect_diff
))
481 min_aspect_diff
= diff
;
486 /* use the aspect ratio correct source if there is one */
487 if (min_aspect_i
>= 0)
488 min_i
= min_aspect_i
;
490 /* resize the original to the given area */
491 pic
= ResizeImage(self
->original
[min_i
]->data
,
492 self
->original
[min_i
]->width
,
493 self
->original
[min_i
]->height
,
494 area
->width
, area
->height
);
496 /* add the resized image to the image, as the first in the resized
498 if (self
->n_resized
>= self
->cache
->max_resized_saved
)
499 /* remove the last one (last used one) */
500 RemovePicture(self
, &self
->resized
, self
->n_resized
- 1,
502 if (self
->cache
->max_resized_saved
)
503 /* add it to the top of the resized list */
504 AddPicture(self
, &self
->resized
, &self
->n_resized
, pic
);
506 free_pic
= TRUE
; /* don't leak mem! */
509 g_assert(pic
!= NULL
);
511 DrawRGBA(target
, target_w
, target_h
,
512 pic
->data
, pic
->width
, pic
->height
,
This page took 0.055288 seconds and 4 git commands to generate.