]> Dogcows Code - chaz/openbox/blob - render/image.c
Introducing the icon cache.
[chaz/openbox] / render / image.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 image.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
6
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.
11
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.
16
17 See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "geom.h"
21 #include "image.h"
22 #include "color.h"
23 #include "imagecache.h"
24
25 #include <glib.h>
26
27 #define FRACTION 12
28 #define FLOOR(i) ((i) & (~0UL << FRACTION))
29 #define AVERAGE(a, b) (((((a) ^ (b)) & 0xfefefefeL) >> 1) + ((a) & (b)))
30
31 static void AddPicture(RrImage *self, RrImagePic ***list, gint *len,
32 RrImagePic *pic)
33 {
34 gint i;
35
36 g_assert(pic->width > 0 && pic->height > 0);
37
38 g_assert(g_hash_table_lookup(self->cache->table, pic) == NULL);
39
40 /* grow the list */
41 *list = g_renew(RrImagePic*, *list, ++*len);
42
43 /* move everything else down one */
44 for (i = *len-1; i > 0; --i)
45 (*list)[i] = (*list)[i-1];
46
47 /* set the new picture up at the front of the list */
48 (*list)[0] = pic;
49
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);
52
53 #ifdef DEBUG
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));
58 #endif
59 }
60
61 static void RemovePicture(RrImage *self, RrImagePic ***list,
62 gint i, gint *len)
63 {
64 gint j;
65
66 #ifdef DEBUG
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]));
72 #endif
73
74 /* remove the picture as a key in the cache */
75 g_hash_table_remove(self->cache->table, (*list)[i]);
76
77 /* free the picture (and its rgba data) */
78 g_free((*list)[i]);
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];
83 /* shrink the list */
84 *list = g_renew(RrImagePic*, *list, --*len);
85 }
86
87 static RrImagePic* ResizeImage(RrPixel32 *src,
88 gulong srcW, gulong srcH,
89 gulong dstW, gulong dstH)
90 {
91 RrPixel32 *dst;
92 RrImagePic *pic;
93 gulong dstX, dstY, srcX, srcY;
94 gulong srcX1, srcX2, srcY1, srcY2;
95 gulong ratioX, ratioY;
96 gulong aspectW, aspectH;
97
98 /* keep the aspect ratio */
99 aspectW = dstW;
100 aspectH = (gint)(dstW * ((gdouble)srcH / srcW));
101 if (aspectH > dstH) {
102 aspectH = dstH;
103 aspectW = (gint)(dstH * ((gdouble)srcW / srcH));
104 }
105 dstW = aspectW;
106 dstH = aspectH;
107
108 if (srcW == dstW && srcH == dstH)
109 return NULL; /* no scaling needed ! */
110
111 pic = g_new(RrImagePic, 1);
112 dst = g_new(RrPixel32, dstW * dstH);
113 pic->width = dstW;
114 pic->height = dstH;
115 pic->data = dst;
116
117 ratioX = (srcW << FRACTION) / dstW;
118 ratioY = (srcH << FRACTION) / dstH;
119
120 srcY2 = 0;
121 for (dstY = 0; dstY < dstH; dstY++) {
122 srcY1 = srcY2;
123 srcY2 += ratioY;
124
125 srcX2 = 0;
126 for (dstX = 0; dstX < dstW; dstX++) {
127 gulong red = 0, green = 0, blue = 0, alpha = 0;
128 gulong portionX, portionY, portionXY, sumXY = 0;
129 RrPixel32 pixel;
130
131 srcX1 = srcX2;
132 srcX2 += ratioX;
133
134 for (srcY = srcY1; srcY < srcY2; srcY += (1UL << FRACTION)) {
135 if (srcY == srcY1) {
136 srcY = FLOOR(srcY);
137 portionY = (1UL << FRACTION) - (srcY1 - srcY);
138 if (portionY > srcY2 - srcY1)
139 portionY = srcY2 - srcY1;
140 }
141 else if (srcY == FLOOR(srcY2))
142 portionY = srcY2 - srcY;
143 else
144 portionY = (1UL << FRACTION);
145
146 for (srcX = srcX1; srcX < srcX2; srcX += (1UL << FRACTION)) {
147 if (srcX == srcX1) {
148 srcX = FLOOR(srcX);
149 portionX = (1UL << FRACTION) - (srcX1 - srcX);
150 if (portionX > srcX2 - srcX1)
151 portionX = srcX2 - srcX1;
152 }
153 else if (srcX == FLOOR(srcX2))
154 portionX = srcX2 - srcX;
155 else
156 portionX = (1UL << FRACTION);
157
158 portionXY = (portionX * portionY) >> FRACTION;
159 sumXY += portionXY;
160
161 pixel = *(src + (srcY >> FRACTION) * srcW
162 + (srcX >> FRACTION));
163 red += ((pixel >> RrDefaultRedOffset) & 0xFF)
164 * portionXY;
165 green += ((pixel >> RrDefaultGreenOffset) & 0xFF)
166 * portionXY;
167 blue += ((pixel >> RrDefaultBlueOffset) & 0xFF)
168 * portionXY;
169 alpha += ((pixel >> RrDefaultAlphaOffset) & 0xFF)
170 * portionXY;
171 }
172 }
173
174 g_assert(sumXY != 0);
175 red /= sumXY;
176 green /= sumXY;
177 blue /= sumXY;
178 alpha /= sumXY;
179
180 *dst++ = (red << RrDefaultRedOffset) |
181 (green << RrDefaultGreenOffset) |
182 (blue << RrDefaultBlueOffset) |
183 (alpha << RrDefaultAlphaOffset);
184 }
185 }
186
187 return pic;
188 }
189
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)
196 {
197 RrPixel32 *dest;
198 gint col, num_pixels;
199 gint dw, dh;
200
201 g_assert(source_w <= area->width && source_h <= area->height);
202
203 /* keep the aspect ratio */
204 dw = area->width;
205 dh = (gint)(dw * ((gdouble)source_h / source_w));
206 if (dh > area->height) {
207 dh = area->height;
208 dw = (gint)(dh * ((gdouble)source_w / source_h));
209 }
210
211 /* copy source -> dest, and apply the alpha channel.
212 center the image if it is smaller than the area */
213 col = 0;
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;
219
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;
225
226 /* background color */
227 bgr = *dest >> RrDefaultRedOffset;
228 bgg = *dest >> RrDefaultGreenOffset;
229 bgb = *dest >> RrDefaultBlueOffset;
230
231 r = bgr + (((r - bgr) * a) >> 8);
232 g = bgg + (((g - bgg) * a) >> 8);
233 b = bgb + (((b - bgb) * a) >> 8);
234
235 *dest = ((r << RrDefaultRedOffset) |
236 (g << RrDefaultGreenOffset) |
237 (b << RrDefaultBlueOffset));
238
239 dest++;
240 source++;
241
242 if (++col >= dw) {
243 col = 0;
244 dest += target_w - dw;
245 }
246 }
247 }
248
249 void RrImageDrawRGBA(RrPixel32 *target, RrTextureRGBA *rgba,
250 gint target_w, gint target_h,
251 RrRect *area)
252 {
253 RrImagePic *scaled;
254
255 scaled = ResizeImage(rgba->data, rgba->width, rgba->height,
256 area->width, area->height);
257
258 if (scaled) {
259 #ifdef DEBUG
260 g_warning("Scaling an RGBA! You should avoid this and just make "
261 "it the right size yourself!");
262 #endif
263 DrawRGBA(target, target_w, target_h,
264 scaled->data, scaled->width, scaled->height,
265 rgba->alpha, area);
266 }
267 else
268 DrawRGBA(target, target_w, target_h,
269 rgba->data, rgba->width, rgba->height,
270 rgba->alpha, area);
271 }
272
273 RrImage* RrImageNew(RrImageCache *cache)
274 {
275 RrImage *self;
276
277 self = g_new0(RrImage, 1);
278 self->ref = 1;
279 self->cache = cache;
280 return self;
281 }
282
283 void RrImageRef(RrImage *self)
284 {
285 ++self->ref;
286 }
287
288 void RrImageUnref(RrImage *self)
289 {
290 if (self && --self->ref == 0) {
291 #ifdef DEBUG
292 g_print("Refcount to 0, removing ALL pictures from the cache: "
293 "Image 0x%x\n", (guint)self);
294 #endif
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);
299 g_free(self);
300 }
301 }
302
303 void RrImageAddPicture(RrImage *self, RrPixel32 *data, gint w, gint h)
304 {
305 gint i;
306 RrImagePic *pic;
307
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) {
311 #ifdef DEBUG
312 g_print("Found duplicate ORIGINAL image: "
313 "Image 0x%x, w %d h %d\n", (guint)self, w, h);
314 #endif
315 return;
316 }
317
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);
322 break;
323 }
324
325 /* add the new picture */
326 pic = g_new(RrImagePic, 1);
327 pic->width = w;
328 pic->height = h;
329 pic->data = g_memdup(data, w*h*sizeof(RrPixel32));
330 AddPicture(self, &self->original, &self->n_original, pic);
331 }
332
333 void RrImageRemovePicture(RrImage *self, gint w, gint h)
334 {
335 gint i;
336
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);
341 break;
342 }
343 }
344
345 void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img,
346 gint target_w, gint target_h,
347 RrRect *area)
348 {
349 gint i, min_diff, min_i;
350 RrImage *self;
351 RrImagePic *pic;
352
353 self = img->image;
354 pic = NULL;
355
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)
361 {
362 pic = self->original[i];
363 break;
364 }
365
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)
370 {
371 gint j;
372 RrImagePic *saved;
373
374 /* save the selected one */
375 saved = self->resized[i];
376
377 /* shift all the others down */
378 for (j = i; j > 0; --j)
379 self->resized[j] = self->resized[j-1];
380
381 /* and move the selected one to the top of the list */
382 self->resized[0] = saved;
383
384 pic = self->resized[0];
385 break;
386 }
387
388 if (!pic) {
389 /* find an original with a close size */
390 min_diff = -1;
391 min_i = 0;
392 for (i = 0; i < self->n_original; ++i) {
393 gint diff;
394 gint wdiff, hdiff;
395
396 /* our size difference metric.. */
397 wdiff = self->original[i]->width - area->width;
398 hdiff = self->original[i]->height - area->height;
399 diff = (wdiff * wdiff) + (hdiff * hdiff);
400
401 if (min_diff < 0 || diff < min_diff) {
402 min_diff = diff;
403 min_i = i;
404 }
405 }
406
407 /* resize the original to the given area */
408 pic = ResizeImage(self->original[min_i]->data,
409 self->original[min_i]->width,
410 self->original[min_i]->height,
411 area->width, area->height);
412
413 /* add the resized image to the image, as the first in the resized
414 list */
415 if (self->n_resized >= MAX_CACHE_RESIZED) {
416 /* remove the last one (last used one) */
417 RemovePicture(self, &self->resized, self->n_resized - 1,
418 &self->n_resized);
419 }
420 /* add it to the top of the resized list */
421 AddPicture(self, &self->resized, &self->n_resized, pic);
422 }
423
424 g_assert(pic != NULL);
425
426 DrawRGBA(target, target_w, target_h,
427 pic->data, pic->width, pic->height,
428 img->alpha, area);
429 }
This page took 0.061271 seconds and 5 git commands to generate.