1 /*
2 * gnome-thumbnail-pixbuf-utils.c: Utilities for handling pixbufs when thumbnailing
3 *
4 * Copyright (C) 2002 Red Hat, Inc.
5 *
6 * This file is part of the Gnome Library.
7 *
8 * The Gnome Library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
12 *
13 * The Gnome Library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with the Gnome Library; see the file COPYING.LIB. If not,
20 * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 * Author: Alexander Larsson <alexl@redhat.com>
24 */
25
26 #include <config.h>
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <glib.h>
31
32 #define GNOME_DESKTOP_USE_UNSTABLE_API
33 #include "gnome-desktop-thumbnail.h"
34
35 #define LOAD_BUFFER_SIZE 65536
36
37 /**
38 * gnome_desktop_thumbnail_scale_down_pixbuf:
39 * @pixbuf: a #GdkPixbuf
40 * @dest_width: the desired new width
41 * @dest_height: the desired new height
42 *
43 * Scales the pixbuf to the desired size. This function
44 * is a lot faster than gdk-pixbuf when scaling down by
45 * large amounts.
46 *
47 * Return value: (transfer full): a scaled pixbuf
48 *
49 * Since: 2.2
50 **/
51 GdkPixbuf *
gnome_desktop_thumbnail_scale_down_pixbuf(GdkPixbuf * pixbuf,int dest_width,int dest_height)52 gnome_desktop_thumbnail_scale_down_pixbuf (GdkPixbuf *pixbuf,
53 int dest_width,
54 int dest_height)
55 {
56 int source_width, source_height;
57 int s_x1, s_y1, s_x2, s_y2;
58 int s_xfrac, s_yfrac;
59 int dx, dx_frac, dy, dy_frac;
60 div_t ddx, ddy;
61 int x, y;
62 int r, g, b, a;
63 int n_pixels;
64 gboolean has_alpha;
65 guchar *dest, *src, *xsrc, *src_pixels;
66 GdkPixbuf *dest_pixbuf;
67 int pixel_stride;
68 int source_rowstride, dest_rowstride;
69
70 if (dest_width == 0 || dest_height == 0) {
71 return NULL;
72 }
73
74 source_width = gdk_pixbuf_get_width (pixbuf);
75 source_height = gdk_pixbuf_get_height (pixbuf);
76
77 g_assert (source_width >= dest_width);
78 g_assert (source_height >= dest_height);
79
80 ddx = div (source_width, dest_width);
81 dx = ddx.quot;
82 dx_frac = ddx.rem;
83
84 ddy = div (source_height, dest_height);
85 dy = ddy.quot;
86 dy_frac = ddy.rem;
87
88 has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
89 source_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
90 src_pixels = gdk_pixbuf_get_pixels (pixbuf);
91
92 dest_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, has_alpha, 8,
93 dest_width, dest_height);
94 dest = gdk_pixbuf_get_pixels (dest_pixbuf);
95 dest_rowstride = gdk_pixbuf_get_rowstride (dest_pixbuf);
96
97 pixel_stride = (has_alpha)?4:3;
98
99 s_y1 = 0;
100 s_yfrac = -dest_height/2;
101 while (s_y1 < source_height) {
102 s_y2 = s_y1 + dy;
103 s_yfrac += dy_frac;
104 if (s_yfrac > 0) {
105 s_y2++;
106 s_yfrac -= dest_height;
107 }
108
109 s_x1 = 0;
110 s_xfrac = -dest_width/2;
111 while (s_x1 < source_width) {
112 s_x2 = s_x1 + dx;
113 s_xfrac += dx_frac;
114 if (s_xfrac > 0) {
115 s_x2++;
116 s_xfrac -= dest_width;
117 }
118
119 /* Average block of [x1,x2[ x [y1,y2[ and store in dest */
120 r = g = b = a = 0;
121 n_pixels = 0;
122
123 src = src_pixels + s_y1 * source_rowstride + s_x1 * pixel_stride;
124 for (y = s_y1; y < s_y2; y++) {
125 xsrc = src;
126 if (has_alpha) {
127 for (x = 0; x < s_x2-s_x1; x++) {
128 n_pixels++;
129
130 r += xsrc[3] * xsrc[0];
131 g += xsrc[3] * xsrc[1];
132 b += xsrc[3] * xsrc[2];
133 a += xsrc[3];
134 xsrc += 4;
135 }
136 } else {
137 for (x = 0; x < s_x2-s_x1; x++) {
138 n_pixels++;
139 r += *xsrc++;
140 g += *xsrc++;
141 b += *xsrc++;
142 }
143 }
144 src += source_rowstride;
145 }
146
147 if (n_pixels != 0) /* avoid any possible divide by zero */
148 {
149 if (has_alpha) {
150 if (a != 0) {
151 *dest++ = r / a;
152 *dest++ = g / a;
153 *dest++ = b / a;
154 *dest++ = a / n_pixels;
155 } else {
156 *dest++ = 0;
157 *dest++ = 0;
158 *dest++ = 0;
159 *dest++ = 0;
160 }
161 } else {
162 *dest++ = r / n_pixels;
163 *dest++ = g / n_pixels;
164 *dest++ = b / n_pixels;
165 }
166 }
167
168 s_x1 = s_x2;
169 }
170 s_y1 = s_y2;
171 dest += dest_rowstride - dest_width * pixel_stride;
172 }
173
174 return dest_pixbuf;
175 }
176