1 /*
2 * Photos - access, organize and share your photos on GNOME
3 * Copyright © 2016 – 2019 Red Hat, Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20 #include "config.h"
21
22 #include <stdio.h>
23
24 #include <babl/babl.h>
25 #include <gegl.h>
26 #include <png.h>
27
28 #include "photos-png-count.h"
29 #include "photos-operation-png-guess-sizes.h"
30
31
32 struct _PhotosOperationPngGuessSizes
33 {
34 GeglOperationSink parent_instance;
35 gboolean background;
36 gint bitdepth;
37 gint compression;
38 gsize sizes[2];
39 };
40
41 enum
42 {
43 PROP_0,
44 PROP_BACKGROUND,
45 PROP_BITDEPTH,
46 PROP_COMPRESSION,
47 PROP_SIZE,
48 PROP_SIZE_1
49 };
50
51
52 G_DEFINE_TYPE (PhotosOperationPngGuessSizes, photos_operation_png_guess_sizes, GEGL_TYPE_OPERATION_SINK);
53
54
55 static gsize
photos_operation_png_guess_sizes_count(GeglBuffer * buffer,gint compression,gint bitdepth,gboolean background,gdouble zoom,gint src_x,gint src_y,gint width,gint height)56 photos_operation_png_guess_sizes_count (GeglBuffer *buffer,
57 gint compression,
58 gint bitdepth,
59 gboolean background,
60 gdouble zoom,
61 gint src_x,
62 gint src_y,
63 gint width,
64 gint height)
65 {
66 gint bpp;
67 gint i;
68 gint png_color_type;
69 gchar format_string[16];
70 const Babl *format;
71 const Babl *format_buffer;
72 gsize ret_val = 0;
73 gsize size;
74 guchar *pixels = NULL;
75 png_infop info_ptr = NULL;
76 png_structp png_ptr = NULL;
77
78 format_buffer = gegl_buffer_get_format (buffer);
79 if (babl_format_has_alpha (format_buffer))
80 {
81 if (babl_format_get_n_components (format_buffer) != 2)
82 {
83 png_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
84 strcpy (format_string, "R'G'B'A ");
85 }
86 else
87 {
88 png_color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
89 strcpy (format_string, "Y'A ");
90 }
91 }
92 else
93 {
94 if (babl_format_get_n_components (format_buffer) != 1)
95 {
96 png_color_type = PNG_COLOR_TYPE_RGB;
97 strcpy (format_string, "R'G'B' ");
98 }
99 else
100 {
101 png_color_type = PNG_COLOR_TYPE_GRAY;
102 strcpy (format_string, "Y' ");
103 }
104 }
105
106 if (bitdepth == 16)
107 strcat (format_string, "u16");
108 else
109 strcat (format_string, "u8");
110
111 png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
112 if (png_ptr == NULL)
113 goto out;
114
115 info_ptr = png_create_info_struct (png_ptr);
116 if (info_ptr == NULL)
117 goto out;
118
119 if (setjmp (png_jmpbuf (png_ptr)))
120 {
121 ret_val = 0;
122 goto out;
123 }
124
125 if (compression >= 0)
126 png_set_compression_level (png_ptr, compression);
127
128 photos_png_init_count (png_ptr, &size);
129
130 png_set_IHDR (png_ptr,
131 info_ptr,
132 width,
133 height,
134 bitdepth,
135 png_color_type,
136 PNG_INTERLACE_NONE,
137 PNG_COMPRESSION_TYPE_BASE,
138 PNG_FILTER_TYPE_DEFAULT);
139
140 if (background)
141 {
142 png_color_16 white;
143
144 if (png_color_type == PNG_COLOR_TYPE_RGB || png_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
145 {
146 white.red = 0xff;
147 white.blue = 0xff;
148 white.green = 0xff;
149 }
150 else
151 {
152 white.gray = 0xff;
153 }
154
155 png_set_bKGD (png_ptr, info_ptr, &white);
156 }
157
158 png_write_info (png_ptr, info_ptr);
159
160 #if BYTE_ORDER == LITTLE_ENDIAN
161 if (bitdepth > 8)
162 png_set_swap (png_ptr);
163 #endif
164
165 format = babl_format (format_string);
166 bpp = babl_format_get_bytes_per_pixel (format);
167 pixels = g_malloc0 (width * bpp);
168
169 for (i = 0; i < height; i++)
170 {
171 GeglRectangle rect;
172
173 rect.x = src_x;
174 rect.y = src_y + i;
175 rect.width = width;
176 rect.height = 1;
177 gegl_buffer_get (buffer, &rect, zoom, format, pixels, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
178 png_write_rows (png_ptr, &pixels, 1);
179 }
180
181 png_write_end (png_ptr, info_ptr);
182 ret_val = size;
183
184 out:
185 g_free (pixels);
186 png_destroy_write_struct (&png_ptr, &info_ptr);
187 return ret_val;
188 }
189
190
191 static gboolean
photos_operation_png_guess_sizes_process(GeglOperation * operation,GeglBuffer * input,const GeglRectangle * roi,gint level)192 photos_operation_png_guess_sizes_process (GeglOperation *operation,
193 GeglBuffer *input,
194 const GeglRectangle *roi,
195 gint level)
196 {
197 PhotosOperationPngGuessSizes *self = PHOTOS_OPERATION_PNG_GUESS_SIZES (operation);
198 gsize i;
199
200 for (i = 0; i < G_N_ELEMENTS (self->sizes); i++)
201 {
202 GeglRectangle roi_zoomed;
203 gdouble zoom = 1.0 / (gdouble) (1 << i);
204
205 roi_zoomed.height = (gint) (zoom * roi->height + 0.5);
206 roi_zoomed.width = (gint) (zoom * roi->width + 0.5);
207 roi_zoomed.x = (gint) (zoom * roi->x + 0.5);
208 roi_zoomed.y = (gint) (zoom * roi->y + 0.5);
209
210 self->sizes[i] = photos_operation_png_guess_sizes_count (input,
211 self->compression,
212 self->bitdepth,
213 self->background,
214 zoom,
215 roi_zoomed.x,
216 roi_zoomed.y,
217 roi_zoomed.width,
218 roi_zoomed.height);
219 }
220
221 return TRUE;
222 }
223
224
225 static void
photos_operation_png_guess_sizes_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)226 photos_operation_png_guess_sizes_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
227 {
228 PhotosOperationPngGuessSizes *self = PHOTOS_OPERATION_PNG_GUESS_SIZES (object);
229
230 switch (prop_id)
231 {
232 case PROP_BACKGROUND:
233 g_value_set_boolean (value, self->background);
234 break;
235
236 case PROP_BITDEPTH:
237 g_value_set_int (value, self->bitdepth);
238 break;
239
240 case PROP_COMPRESSION:
241 g_value_set_int (value, self->compression);
242 break;
243
244 case PROP_SIZE:
245 g_value_set_uint64 (value, (guint64) self->sizes[0]);
246 break;
247
248 case PROP_SIZE_1:
249 g_value_set_uint64 (value, (guint64) self->sizes[1]);
250 break;
251
252 default:
253 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
254 break;
255 }
256 }
257
258
259 static void
photos_operation_png_guess_sizes_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)260 photos_operation_png_guess_sizes_set_property (GObject *object,
261 guint prop_id,
262 const GValue *value,
263 GParamSpec *pspec)
264 {
265 PhotosOperationPngGuessSizes *self = PHOTOS_OPERATION_PNG_GUESS_SIZES (object);
266
267 switch (prop_id)
268 {
269 case PROP_BACKGROUND:
270 self->background = g_value_get_boolean (value);
271 break;
272
273 case PROP_BITDEPTH:
274 self->bitdepth = g_value_get_int (value);
275 break;
276
277 case PROP_COMPRESSION:
278 self->compression = g_value_get_int (value);
279 break;
280
281 default:
282 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
283 break;
284 }
285 }
286
287
288 static void
photos_operation_png_guess_sizes_init(PhotosOperationPngGuessSizes * self)289 photos_operation_png_guess_sizes_init (PhotosOperationPngGuessSizes *self)
290 {
291 }
292
293
294 static void
photos_operation_png_guess_sizes_class_init(PhotosOperationPngGuessSizesClass * class)295 photos_operation_png_guess_sizes_class_init (PhotosOperationPngGuessSizesClass *class)
296 {
297 GObjectClass *object_class = G_OBJECT_CLASS (class);
298 GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (class);
299 GeglOperationSinkClass *sink_class = GEGL_OPERATION_SINK_CLASS (class);
300
301 operation_class->opencl_support = FALSE;
302 sink_class->needs_full = TRUE;
303
304 object_class->get_property = photos_operation_png_guess_sizes_get_property;
305 object_class->set_property = photos_operation_png_guess_sizes_set_property;
306 sink_class->process = photos_operation_png_guess_sizes_process;
307
308 g_object_class_install_property (object_class,
309 PROP_BACKGROUND,
310 g_param_spec_boolean ("background",
311 "Background",
312 "Set bKGD chunk information",
313 TRUE,
314 G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
315
316 g_object_class_install_property (object_class,
317 PROP_BITDEPTH,
318 g_param_spec_int ("bitdepth",
319 "Bitdepth",
320 "Number of bits per channel — 8 and 16 are the currently "
321 "accepted values",
322 8,
323 16,
324 16,
325 G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
326
327 g_object_class_install_property (object_class,
328 PROP_COMPRESSION,
329 g_param_spec_int ("compression",
330 "Compression",
331 "PNG compression level (between -1 and 9)",
332 -1,
333 9,
334 3,
335 G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
336
337 g_object_class_install_property (object_class,
338 PROP_SIZE,
339 g_param_spec_uint64 ("size",
340 "Size (level=0)",
341 "Approximate size in bytes after applying PNG compression"
342 "at zoom=1.0",
343 0,
344 G_MAXSIZE,
345 0,
346 G_PARAM_READABLE));
347
348 g_object_class_install_property (object_class,
349 PROP_SIZE_1,
350 g_param_spec_uint64 ("size-1",
351 "Size (level=1)",
352 "Approximate size in bytes after applying PNG compression"
353 "at zoom=0.5",
354 0,
355 G_MAXSIZE,
356 0,
357 G_PARAM_READABLE));
358
359 gegl_operation_class_set_keys (operation_class,
360 "name", "photos:png-guess-sizes",
361 "title", "PNG Guess Sizes",
362 "description", "Guesses the size of a GeglBuffer after applying PNG compression",
363 NULL);
364 }
365