~ubuntu-branches/debian/sid/openbox/sid

« back to all changes in this revision

Viewing changes to render/image.c

  • Committer: Bazaar Package Importer
  • Author(s): Nico Golde, Nico Golde, Eugenio Paolantonio
  • Date: 2011-10-03 22:59:30 UTC
  • mfrom: (1.1.11 upstream)
  • Revision ID: james.westby@ubuntu.com-20111003225930-tdvyax5tx63dyoez
Tags: 3.5.0-1
[Nico Golde]
* New upstream release (Closes: #638783).
  - Fix crashes in the menu code (Closes: #563891).
* Add Brazilian translation to openbox.desktop,
  thanks Sérgio Cipolla (Closes: #627912).
* Remove 06_fix_swap_byte_order.patch, applied upstream.
* Bump debhelper dependency to >= 7.0.50~ due to override.
* Remove CHANGELOG from openbox.docs to prevent double installation.
* Add 02_fix_freedesktop_compliance.dpatch desktop file to
  /usr/share/applications.

[Eugenio Paolantonio]
* debian/patches:
  - Disabled 03_place_windows_in_quadrants.patch
  - Updated 01_rc.xml.patch and 06_fix_swap_byte_order.patch
  - Removed 04_fix_ftbfs_no-add-needed.patch and 20_24bits_support.patch
* debian/control:
  - Added myself to the Uploaders.
  - Build-Depends: removed libxau-dev, libxft-dev and python-xdg;
    added libimlib2-dev
  - openbox Suggests: added python-xdg
  - libobrender21 renamed to libobrender27
  - libobparser21 renamed to libobt0
* debian/rules:
  - Rewrote using a simpler debhelper syntax
  - Moved the install pass to openbox.install
* debian/*.{install,links,dirs}:
  - Updated.
* debian/openbox.xsession:
  - Removed. Openbox now ships it by default.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
void RrImagePicInit(RrImagePic *pic, gint w, gint h, RrPixel32 *data)
32
 
{
33
 
    gint i;
34
 
 
35
 
    pic->width = w;
36
 
    pic->height = h;
37
 
    pic->data = data;
38
 
    pic->sum = 0;
39
 
    for (i = w*h; i > 0; --i)
40
 
        pic->sum += *(data++);
41
 
}
42
 
 
43
 
static void RrImagePicFree(RrImagePic *pic)
44
 
{
45
 
    if (pic) {
46
 
        g_free(pic->data);
47
 
        g_free(pic);
48
 
    }
49
 
}
50
 
 
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
53
 
  "resized" list. */
54
 
static void AddPicture(RrImage *self, RrImagePic ***list, gint *len,
55
 
                       RrImagePic *pic)
56
 
{
57
 
    gint i;
58
 
 
59
 
    g_assert(pic->width > 0 && pic->height > 0);
60
 
 
61
 
    g_assert(g_hash_table_lookup(self->cache->table, pic) == NULL);
62
 
 
63
 
    /* grow the list */
64
 
    *list = g_renew(RrImagePic*, *list, ++*len);
65
 
 
66
 
    /* move everything else down one */
67
 
    for (i = *len-1; i > 0; --i)
68
 
        (*list)[i] = (*list)[i-1];
69
 
 
70
 
    /* set the new picture up at the front of the list */
71
 
    (*list)[0] = pic;
72
 
 
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);
75
 
 
76
 
/*
77
 
#ifdef DEBUG
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));
82
 
#endif
83
 
*/
84
 
}
85
 
 
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,
89
 
                          gint i, gint *len)
90
 
{
91
 
    gint j;
92
 
 
93
 
/*
94
 
#ifdef DEBUG
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]));
100
 
#endif
101
 
*/
102
 
 
103
 
    /* remove the picture as a key in the cache */
104
 
    g_hash_table_remove(self->cache->table, (*list)[i]);
105
 
 
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);
113
 
}
114
 
 
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
119
 
*/
120
 
static RrImagePic* ResizeImage(RrPixel32 *src,
121
 
                               gulong srcW, gulong srcH,
122
 
                               gulong dstW, gulong dstH)
123
 
{
124
 
    RrPixel32 *dst, *dststart;
125
 
    RrImagePic *pic;
126
 
    gulong dstX, dstY, srcX, srcY;
127
 
    gulong srcX1, srcX2, srcY1, srcY2;
128
 
    gulong ratioX, ratioY;
129
 
    gulong aspectW, aspectH;
130
 
 
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;
136
 
 
137
 
    /* keep the aspect ratio */
138
 
    aspectW = dstW;
139
 
    aspectH = (gint)(dstW * ((gdouble)srcH / srcW));
140
 
    if (aspectH > dstH) {
141
 
        aspectH = dstH;
142
 
        aspectW = (gint)(dstH * ((gdouble)srcW / srcH));
143
 
    }
144
 
    dstW = aspectW ? aspectW : 1;
145
 
    dstH = aspectH ? aspectH : 1;
146
 
 
147
 
    if (srcW == dstW && srcH == dstH)
148
 
        return NULL; /* no scaling needed! */
149
 
 
150
 
    dststart = dst = g_new(RrPixel32, dstW * dstH);
151
 
 
152
 
    ratioX = (srcW << FRACTION) / dstW;
153
 
    ratioY = (srcH << FRACTION) / dstH;
154
 
 
155
 
    srcY2 = 0;
156
 
    for (dstY = 0; dstY < dstH; dstY++) {
157
 
        srcY1 = srcY2;
158
 
        srcY2 += ratioY;
159
 
 
160
 
        srcX2 = 0;
161
 
        for (dstX = 0; dstX < dstW; dstX++) {
162
 
            gulong red = 0, green = 0, blue = 0, alpha = 0;
163
 
            gulong portionX, portionY, portionXY, sumXY = 0;
164
 
            RrPixel32 pixel;
165
 
 
166
 
            srcX1 = srcX2;
167
 
            srcX2 += ratioX;
168
 
 
169
 
            for (srcY = srcY1; srcY < srcY2; srcY += (1UL << FRACTION)) {
170
 
                if (srcY == srcY1) {
171
 
                    srcY = FLOOR(srcY);
172
 
                    portionY = (1UL << FRACTION) - (srcY1 - srcY);
173
 
                    if (portionY > srcY2 - srcY1)
174
 
                        portionY = srcY2 - srcY1;
175
 
                }
176
 
                else if (srcY == FLOOR(srcY2))
177
 
                    portionY = srcY2 - srcY;
178
 
                else
179
 
                    portionY = (1UL << FRACTION);
180
 
 
181
 
                for (srcX = srcX1; srcX < srcX2; srcX += (1UL << FRACTION)) {
182
 
                    if (srcX == srcX1) {
183
 
                        srcX = FLOOR(srcX);
184
 
                        portionX = (1UL << FRACTION) - (srcX1 - srcX);
185
 
                        if (portionX > srcX2 - srcX1)
186
 
                            portionX = srcX2 - srcX1;
187
 
                    }
188
 
                    else if (srcX == FLOOR(srcX2))
189
 
                        portionX = srcX2 - srcX;
190
 
                    else
191
 
                        portionX = (1UL << FRACTION);
192
 
 
193
 
                    portionXY = (portionX * portionY) >> FRACTION;
194
 
                    sumXY += portionXY;
195
 
 
196
 
                    pixel = *(src + (srcY >> FRACTION) * srcW
197
 
                            + (srcX >> FRACTION));
198
 
                    red   += ((pixel >> RrDefaultRedOffset)   & 0xFF)
199
 
                             * portionXY;
200
 
                    green += ((pixel >> RrDefaultGreenOffset) & 0xFF)
201
 
                             * portionXY;
202
 
                    blue  += ((pixel >> RrDefaultBlueOffset)  & 0xFF)
203
 
                             * portionXY;
204
 
                    alpha += ((pixel >> RrDefaultAlphaOffset) & 0xFF)
205
 
                             * portionXY;
206
 
                }
207
 
            }
208
 
 
209
 
            g_assert(sumXY != 0);
210
 
            red   /= sumXY;
211
 
            green /= sumXY;
212
 
            blue  /= sumXY;
213
 
            alpha /= sumXY;
214
 
 
215
 
            *dst++ = (red   << RrDefaultRedOffset)   |
216
 
                     (green << RrDefaultGreenOffset) |
217
 
                     (blue  << RrDefaultBlueOffset)  |
218
 
                     (alpha << RrDefaultAlphaOffset);
219
 
        }
220
 
    }
221
 
 
222
 
    pic = g_new(RrImagePic, 1);
223
 
    RrImagePicInit(pic, dstW, dstH, dststart);
224
 
 
225
 
    return pic;
226
 
}
227
 
 
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)
234
 
{
235
 
    RrPixel32 *dest;
236
 
    gint col, num_pixels;
237
 
    gint dw, dh;
238
 
 
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);
242
 
 
243
 
    /* keep the aspect ratio */
244
 
    dw = area->width;
245
 
    dh = (gint)(dw * ((gdouble)source_h / source_w));
246
 
    if (dh > area->height) {
247
 
        dh = area->height;
248
 
        dw = (gint)(dh * ((gdouble)source_w / source_h));
249
 
    }
250
 
 
251
 
    /* copy source -> dest, and apply the alpha channel.
252
 
       center the image if it is smaller than the area */
253
 
    col = 0;
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;
259
 
 
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;
265
 
 
266
 
        /* background color */
267
 
        bgr = *dest >> RrDefaultRedOffset;
268
 
        bgg = *dest >> RrDefaultGreenOffset;
269
 
        bgb = *dest >> RrDefaultBlueOffset;
270
 
 
271
 
        r = bgr + (((r - bgr) * a) >> 8);
272
 
        g = bgg + (((g - bgg) * a) >> 8);
273
 
        b = bgb + (((b - bgb) * a) >> 8);
274
 
 
275
 
        *dest = ((r << RrDefaultRedOffset) |
276
 
                 (g << RrDefaultGreenOffset) |
277
 
                 (b << RrDefaultBlueOffset));
278
 
 
279
 
        dest++;
280
 
        source++;
281
 
 
282
 
        if (++col >= dw) {
283
 
            col = 0;
284
 
            dest += target_w - dw;
285
 
        }
286
 
    }
287
 
}
288
 
 
289
 
/*! Draw an RGBA texture into a target pixel buffer. */
290
 
void RrImageDrawRGBA(RrPixel32 *target, RrTextureRGBA *rgba,
291
 
                     gint target_w, gint target_h,
292
 
                     RrRect *area)
293
 
{
294
 
    RrImagePic *scaled;
295
 
 
296
 
    scaled = ResizeImage(rgba->data, rgba->width, rgba->height,
297
 
                         area->width, area->height);
298
 
 
299
 
    if (scaled) {
300
 
#ifdef DEBUG
301
 
            g_warning("Scaling an RGBA! You should avoid this and just make "
302
 
                      "it the right size yourself!");
303
 
#endif
304
 
            DrawRGBA(target, target_w, target_h,
305
 
                     scaled->data, scaled->width, scaled->height,
306
 
                     rgba->alpha, area);
307
 
            RrImagePicFree(scaled);
308
 
    }
309
 
    else
310
 
        DrawRGBA(target, target_w, target_h,
311
 
                 rgba->data, rgba->width, rgba->height,
312
 
                 rgba->alpha, area);
313
 
}
314
 
 
315
 
/*! Create a new RrImage, which is linked to an image cache */
316
 
RrImage* RrImageNew(RrImageCache *cache)
317
 
{
318
 
    RrImage *self;
319
 
 
320
 
    g_assert(cache != NULL);
321
 
 
322
 
    self = g_new0(RrImage, 1);
323
 
    self->ref = 1;
324
 
    self->cache = cache;
325
 
    return self;
326
 
}
327
 
 
328
 
void RrImageRef(RrImage *self)
329
 
{
330
 
    ++self->ref;
331
 
}
332
 
 
333
 
void RrImageUnref(RrImage *self)
334
 
{
335
 
    if (self && --self->ref == 0) {
336
 
/*
337
 
#ifdef DEBUG
338
 
        g_message("Refcount to 0, removing ALL pictures from the cache:\n    "
339
 
                  "Image 0x%x", (guint)self);
340
 
#endif
341
 
*/
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);
346
 
        g_free(self);
347
 
    }
348
 
}
349
 
 
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.
352
 
*/
353
 
void RrImageAddPicture(RrImage *self, RrPixel32 *data, gint w, gint h)
354
 
{
355
 
    gint i;
356
 
    RrImagePic *pic;
357
 
 
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) {
361
 
/*
362
 
#ifdef DEBUG
363
 
            g_message("Found duplicate ORIGINAL image:\n    "
364
 
                      "Image 0x%x, w %d h %d", (guint)self, w, h);
365
 
#endif
366
 
*/
367
 
            return;
368
 
        }
369
 
 
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);
374
 
            break;
375
 
        }
376
 
 
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);
381
 
}
382
 
 
383
 
/*! Remove the picture from the RrImage which has the given dimensions. This
384
 
 removes an "original" picture from the image.
385
 
*/
386
 
void RrImageRemovePicture(RrImage *self, gint w, gint h)
387
 
{
388
 
    gint i;
389
 
 
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);
394
 
            break;
395
 
        }
396
 
}
397
 
 
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"
401
 
  picture).
402
 
 */
403
 
void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img,
404
 
                      gint target_w, gint target_h,
405
 
                      RrRect *area)
406
 
{
407
 
    gint i, min_diff, min_i, min_aspect_diff, min_aspect_i;
408
 
    RrImage *self;
409
 
    RrImagePic *pic;
410
 
    gboolean free_pic;
411
 
 
412
 
    self = img->image;
413
 
    pic = NULL;
414
 
    free_pic = FALSE;
415
 
 
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))
423
 
        {
424
 
            pic = self->original[i];
425
 
            break;
426
 
        }
427
 
 
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))
434
 
        {
435
 
            gint j;
436
 
            RrImagePic *saved;
437
 
 
438
 
            /* save the selected one */
439
 
            saved = self->resized[i];
440
 
 
441
 
            /* shift all the others down */
442
 
            for (j = i; j > 0; --j)
443
 
                self->resized[j] = self->resized[j-1];
444
 
 
445
 
            /* and move the selected one to the top of the list */
446
 
            self->resized[0] = saved;
447
 
 
448
 
            pic = self->resized[0];
449
 
            break;
450
 
        }
451
 
 
452
 
    if (!pic) {
453
 
        gdouble aspect;
454
 
 
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) {
460
 
            gint diff;
461
 
            gint wdiff, hdiff;
462
 
            gdouble myasp;
463
 
 
464
 
            /* our size difference metric.. */
465
 
            wdiff = self->original[i]->width - area->width;
466
 
            if (wdiff < 0) wdiff *= 2; /* prefer scaling down than up */
467
 
            hdiff = self->original[i]->height - area->height;
468
 
            if (hdiff < 0) hdiff *= 2; /* prefer scaling down than up */
469
 
            diff = (wdiff * wdiff) + (hdiff * hdiff);
470
 
 
471
 
            /* find the smallest difference */
472
 
            if (min_diff < 0 || diff < min_diff) {
473
 
                min_diff = diff;
474
 
                min_i = i;
475
 
            }
476
 
            /* and also find the smallest difference with the same aspect
477
 
               ratio (and prefer this one) */
478
 
            myasp = ((gdouble)self->original[i]->width) /
479
 
                self->original[i]->height;
480
 
            if (ABS(aspect - myasp) < 0.0000001 &&
481
 
                (min_aspect_diff < 0 || diff < min_aspect_diff))
482
 
            {
483
 
                min_aspect_diff = diff;
484
 
                min_aspect_i = i;
485
 
            }
486
 
        }
487
 
 
488
 
        /* use the aspect ratio correct source if there is one */
489
 
        if (min_aspect_i >= 0)
490
 
            min_i = min_aspect_i;
491
 
 
492
 
        /* resize the original to the given area */
493
 
        pic = ResizeImage(self->original[min_i]->data,
494
 
                          self->original[min_i]->width,
495
 
                          self->original[min_i]->height,
496
 
                          area->width, area->height);
497
 
 
498
 
        /* add the resized image to the image, as the first in the resized
499
 
           list */
500
 
        if (self->n_resized >= self->cache->max_resized_saved)
501
 
            /* remove the last one (last used one) */
502
 
            RemovePicture(self, &self->resized, self->n_resized - 1,
503
 
                          &self->n_resized);
504
 
        if (self->cache->max_resized_saved)
505
 
            /* add it to the top of the resized list */
506
 
            AddPicture(self, &self->resized, &self->n_resized, pic);
507
 
        else
508
 
            free_pic = TRUE; /* don't leak mem! */
509
 
    }
510
 
 
511
 
    g_assert(pic != NULL);
512
 
 
513
 
    DrawRGBA(target, target_w, target_h,
514
 
             pic->data, pic->width, pic->height,
515
 
             img->alpha, area);
516
 
    if (free_pic)
517
 
        RrImagePicFree(pic);
518
 
}