]>
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 static void AddPicture(RrImage
*self
, RrImagePic
***list
, gint
*len
,
36 g_assert(pic
->width
> 0 && pic
->height
> 0);
38 g_assert(g_hash_table_lookup(self
->cache
->table
, pic
) == NULL
);
41 *list
= g_renew(RrImagePic
*, *list
, ++*len
);
43 /* move everything else down one */
44 for (i
= *len
-1; i
> 0; --i
)
45 (*list
)[i
] = (*list
)[i
-1];
47 /* set the new picture up at the front of the list */
50 /* add the picture as a key to point to this image in the cache */
51 g_hash_table_insert(self
->cache
->table
, (*list
)[0], self
);
54 g_print("Adding %s picture to the cache: "
55 "Image 0x%x, w %d h %d Hash %u\n",
56 (*list
== self
->original
? "ORIGINAL" : "RESIZED"),
57 (guint
)self
, pic
->width
, pic
->height
, RrImagePicHash(pic
));
61 static void RemovePicture(RrImage
*self
, RrImagePic
***list
,
67 g_print("Removing %s picture from the cache: "
68 "Image 0x%x, w %d h %d Hash %u\n",
69 (*list
== self
->original
? "ORIGINAL" : "RESIZED"),
70 (guint
)self
, (*list
)[i
]->width
, (*list
)[i
]->height
,
71 RrImagePicHash((*list
)[i
]));
74 /* remove the picture as a key in the cache */
75 g_hash_table_remove(self
->cache
->table
, (*list
)[i
]);
77 /* free the picture (and its rgba data) */
79 g_free((*list
)[i
]->data
);
80 /* shift everything down one */
81 for (j
= i
; j
< *len
-1; ++j
)
82 (*list
)[j
] = (*list
)[j
+1];
84 *list
= g_renew(RrImagePic
*, *list
, --*len
);
87 static RrImagePic
* ResizeImage(RrPixel32
*src
,
88 gulong srcW
, gulong srcH
,
89 gulong dstW
, gulong dstH
)
93 gulong dstX
, dstY
, srcX
, srcY
;
94 gulong srcX1
, srcX2
, srcY1
, srcY2
;
95 gulong ratioX
, ratioY
;
96 gulong aspectW
, aspectH
;
98 /* keep the aspect ratio */
100 aspectH
= (gint
)(dstW
* ((gdouble
)srcH
/ srcW
));
101 if (aspectH
> dstH
) {
103 aspectW
= (gint
)(dstH
* ((gdouble
)srcW
/ srcH
));
108 if (srcW
== dstW
&& srcH
== dstH
)
109 return NULL
; /* no scaling needed ! */
111 pic
= g_new(RrImagePic
, 1);
112 dst
= g_new(RrPixel32
, dstW
* dstH
);
117 ratioX
= (srcW
<< FRACTION
) / dstW
;
118 ratioY
= (srcH
<< FRACTION
) / dstH
;
121 for (dstY
= 0; dstY
< dstH
; dstY
++) {
126 for (dstX
= 0; dstX
< dstW
; dstX
++) {
127 gulong red
= 0, green
= 0, blue
= 0, alpha
= 0;
128 gulong portionX
, portionY
, portionXY
, sumXY
= 0;
134 for (srcY
= srcY1
; srcY
< srcY2
; srcY
+= (1UL << FRACTION
)) {
137 portionY
= (1UL << FRACTION
) - (srcY1
- srcY
);
138 if (portionY
> srcY2
- srcY1
)
139 portionY
= srcY2
- srcY1
;
141 else if (srcY
== FLOOR(srcY2
))
142 portionY
= srcY2
- srcY
;
144 portionY
= (1UL << FRACTION
);
146 for (srcX
= srcX1
; srcX
< srcX2
; srcX
+= (1UL << FRACTION
)) {
149 portionX
= (1UL << FRACTION
) - (srcX1
- srcX
);
150 if (portionX
> srcX2
- srcX1
)
151 portionX
= srcX2
- srcX1
;
153 else if (srcX
== FLOOR(srcX2
))
154 portionX
= srcX2
- srcX
;
156 portionX
= (1UL << FRACTION
);
158 portionXY
= (portionX
* portionY
) >> FRACTION
;
161 pixel
= *(src
+ (srcY
>> FRACTION
) * srcW
162 + (srcX
>> FRACTION
));
163 red
+= ((pixel
>> RrDefaultRedOffset
) & 0xFF)
165 green
+= ((pixel
>> RrDefaultGreenOffset
) & 0xFF)
167 blue
+= ((pixel
>> RrDefaultBlueOffset
) & 0xFF)
169 alpha
+= ((pixel
>> RrDefaultAlphaOffset
) & 0xFF)
174 g_assert(sumXY
!= 0);
180 *dst
++ = (red
<< RrDefaultRedOffset
) |
181 (green
<< RrDefaultGreenOffset
) |
182 (blue
<< RrDefaultBlueOffset
) |
183 (alpha
<< RrDefaultAlphaOffset
);
190 /*! This drawns an RGBA picture into the target, within the rectangle specified
191 by the area parameter. If the area's size differs from the source's then it
192 will be centered within the rectangle */
193 void DrawRGBA(RrPixel32
*target
, gint target_w
, gint target_h
,
194 RrPixel32
*source
, gint source_w
, gint source_h
,
195 gint alpha
, RrRect
*area
)
198 gint col
, num_pixels
;
201 g_assert(source_w
<= area
->width
&& source_h
<= area
->height
);
203 /* keep the aspect ratio */
205 dh
= (gint
)(dw
* ((gdouble
)source_h
/ source_w
));
206 if (dh
> area
->height
) {
208 dw
= (gint
)(dh
* ((gdouble
)source_w
/ source_h
));
211 /* copy source -> dest, and apply the alpha channel.
212 center the image if it is smaller than the area */
214 num_pixels
= dw
* dh
;
215 dest
= target
+ area
->x
+ (area
->width
- dw
) / 2 +
216 (target_w
* (area
->y
+ (area
->height
- dh
) / 2));
217 while (num_pixels
-- > 0) {
218 guchar a
, r
, g
, b
, bgr
, bgg
, bgb
;
220 /* apply the rgba's opacity as well */
221 a
= ((*source
>> RrDefaultAlphaOffset
) * alpha
) >> 8;
222 r
= *source
>> RrDefaultRedOffset
;
223 g
= *source
>> RrDefaultGreenOffset
;
224 b
= *source
>> RrDefaultBlueOffset
;
226 /* background color */
227 bgr
= *dest
>> RrDefaultRedOffset
;
228 bgg
= *dest
>> RrDefaultGreenOffset
;
229 bgb
= *dest
>> RrDefaultBlueOffset
;
231 r
= bgr
+ (((r
- bgr
) * a
) >> 8);
232 g
= bgg
+ (((g
- bgg
) * a
) >> 8);
233 b
= bgb
+ (((b
- bgb
) * a
) >> 8);
235 *dest
= ((r
<< RrDefaultRedOffset
) |
236 (g
<< RrDefaultGreenOffset
) |
237 (b
<< RrDefaultBlueOffset
));
244 dest
+= target_w
- dw
;
249 void RrImageDrawRGBA(RrPixel32
*target
, RrTextureRGBA
*rgba
,
250 gint target_w
, gint target_h
,
255 scaled
= ResizeImage(rgba
->data
, rgba
->width
, rgba
->height
,
256 area
->width
, area
->height
);
260 g_warning("Scaling an RGBA! You should avoid this and just make "
261 "it the right size yourself!");
263 DrawRGBA(target
, target_w
, target_h
,
264 scaled
->data
, scaled
->width
, scaled
->height
,
268 DrawRGBA(target
, target_w
, target_h
,
269 rgba
->data
, rgba
->width
, rgba
->height
,
273 RrImage
* RrImageNew(RrImageCache
*cache
)
277 self
= g_new0(RrImage
, 1);
283 void RrImageRef(RrImage
*self
)
288 void RrImageUnref(RrImage
*self
)
290 if (self
&& --self
->ref
== 0) {
292 g_print("Refcount to 0, removing ALL pictures from the cache: "
293 "Image 0x%x\n", (guint
)self
);
295 while (self
->n_original
> 0)
296 RemovePicture(self
, &self
->original
, 0, &self
->n_original
);
297 while (self
->n_resized
> 0)
298 RemovePicture(self
, &self
->resized
, 0, &self
->n_resized
);
303 void RrImageAddPicture(RrImage
*self
, RrPixel32
*data
, gint w
, gint h
)
308 /* make sure we don't already have this size.. */
309 for (i
= 0; i
< self
->n_original
; ++i
)
310 if (self
->original
[i
]->width
== w
&& self
->original
[i
]->height
== h
) {
312 g_print("Found duplicate ORIGINAL image: "
313 "Image 0x%x, w %d h %d\n", (guint
)self
, w
, h
);
318 /* remove any resized pictures of this same size */
319 for (i
= 0; i
< self
->n_resized
; ++i
)
320 if (self
->resized
[i
]->width
== w
|| self
->resized
[i
]->height
== h
) {
321 RemovePicture(self
, &self
->resized
, i
, &self
->n_resized
);
325 /* add the new picture */
326 pic
= g_new(RrImagePic
, 1);
329 pic
->data
= g_memdup(data
, w
*h
*sizeof(RrPixel32
));
330 AddPicture(self
, &self
->original
, &self
->n_original
, pic
);
333 void RrImageRemovePicture(RrImage
*self
, gint w
, gint h
)
337 /* remove any resized pictures of this same size */
338 for (i
= 0; i
< self
->n_original
; ++i
)
339 if (self
->original
[i
]->width
== w
&& self
->original
[i
]->height
== h
) {
340 RemovePicture(self
, &self
->original
, i
, &self
->n_original
);
345 void RrImageDrawImage(RrPixel32
*target
, RrTextureImage
*img
,
346 gint target_w
, gint target_h
,
349 gint i
, min_diff
, min_i
, min_aspect_diff
, min_aspect_i
;
356 /* is there an original of this size? (only w or h has to be right cuz
357 we maintain aspect ratios) */
358 for (i
= 0; i
< self
->n_original
; ++i
)
359 if (self
->original
[i
]->width
== area
->width
||
360 self
->original
[i
]->height
== area
->height
)
362 pic
= self
->original
[i
];
366 /* is there a resize of this size? */
367 for (i
= 0; i
< self
->n_resized
; ++i
)
368 if (self
->resized
[i
]->width
== area
->width
||
369 self
->resized
[i
]->height
== area
->height
)
374 /* save the selected one */
375 saved
= self
->resized
[i
];
377 /* shift all the others down */
378 for (j
= i
; j
> 0; --j
)
379 self
->resized
[j
] = self
->resized
[j
-1];
381 /* and move the selected one to the top of the list */
382 self
->resized
[0] = saved
;
384 pic
= self
->resized
[0];
391 /* find an original with a close size */
392 min_diff
= min_aspect_diff
= -1;
393 min_i
= min_aspect_i
= 0;
394 aspect
= ((gdouble
)area
->width
) / area
->height
;
395 for (i
= 0; i
< self
->n_original
; ++i
) {
400 /* our size difference metric.. */
401 wdiff
= self
->original
[i
]->width
- area
->width
;
402 hdiff
= self
->original
[i
]->height
- area
->height
;
403 diff
= (wdiff
* wdiff
) + (hdiff
* hdiff
);
405 /* find the smallest difference */
406 if (min_diff
< 0 || diff
< min_diff
) {
410 /* and also find the smallest difference with the same aspect
411 ratio (and prefer this one) */
412 myasp
= ((gdouble
)self
->original
[i
]->width
) /
413 self
->original
[i
]->height
;
414 if (ABS(aspect
- myasp
) < 0.0000001 &&
415 (min_aspect_diff
< 0 || diff
< min_aspect_diff
))
417 min_aspect_diff
= diff
;
422 /* use the aspect ratio correct source if there is one */
423 if (min_aspect_i
>= 0)
424 min_i
= min_aspect_i
;
426 /* resize the original to the given area */
427 pic
= ResizeImage(self
->original
[min_i
]->data
,
428 self
->original
[min_i
]->width
,
429 self
->original
[min_i
]->height
,
430 area
->width
, area
->height
);
432 /* add the resized image to the image, as the first in the resized
434 if (self
->n_resized
>= MAX_CACHE_RESIZED
) {
435 /* remove the last one (last used one) */
436 RemovePicture(self
, &self
->resized
, self
->n_resized
- 1,
439 /* add it to the top of the resized list */
440 AddPicture(self
, &self
->resized
, &self
->n_resized
, pic
);
443 g_assert(pic
!= NULL
);
445 DrawRGBA(target
, target_w
, target_h
,
446 pic
->data
, pic
->width
, pic
->height
,
This page took 0.05226 seconds and 4 git commands to generate.