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