1 /* GXPSPngWriter
2 *
3 * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include <config.h>
21
22 #include "gxps-png-writer.h"
23 #include <png.h>
24 #include <stdint.h>
25 #include <string.h>
26
27 /* starting with libpng15, png.h no longer #includes zlib.h */
28 #ifndef Z_BEST_COMPRESSION
29 #define Z_BEST_COMPRESSION 9
30 #endif
31
32 struct _GXPSPngWriter {
33 GObject parent;
34
35 GXPSPngFormat format;
36
37 png_structp png_ptr;
38 png_infop info_ptr;
39 };
40
41 struct _GXPSPngWriterClass {
42 GObjectClass parent_class;
43 };
44
45 static void gxps_png_writer_image_writer_iface_init (GXPSImageWriterInterface *iface);
46
G_DEFINE_TYPE_WITH_CODE(GXPSPngWriter,gxps_png_writer,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (GXPS_TYPE_IMAGE_WRITER,gxps_png_writer_image_writer_iface_init))47 G_DEFINE_TYPE_WITH_CODE (GXPSPngWriter, gxps_png_writer, G_TYPE_OBJECT,
48 G_IMPLEMENT_INTERFACE (GXPS_TYPE_IMAGE_WRITER,
49 gxps_png_writer_image_writer_iface_init))
50
51 static void
52 gxps_png_writer_init (GXPSPngWriter *png_writer)
53 {
54 }
55
56 static void
gxps_png_writer_class_init(GXPSPngWriterClass * klass)57 gxps_png_writer_class_init (GXPSPngWriterClass *klass)
58 {
59 }
60
61 GXPSImageWriter *
gxps_png_writer_new(GXPSPngFormat format)62 gxps_png_writer_new (GXPSPngFormat format)
63 {
64 GXPSPngWriter *png_writer = GXPS_PNG_WRITER (g_object_new (GXPS_TYPE_PNG_WRITER, NULL));
65
66 png_writer->format = format;
67
68 return GXPS_IMAGE_WRITER (png_writer);
69 }
70
71 /* Unpremultiplies data and converts native endian ARGB => RGBA bytes */
72 static void
unpremultiply_data(png_structp png,png_row_infop row_info,png_bytep data)73 unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data)
74 {
75 unsigned int i;
76
77 for (i = 0; i < row_info->rowbytes; i += 4) {
78 uint8_t *b = &data[i];
79 uint32_t pixel;
80 uint8_t alpha;
81
82 memcpy (&pixel, b, sizeof (uint32_t));
83 alpha = (pixel & 0xff000000) >> 24;
84 if (alpha == 0) {
85 b[0] = b[1] = b[2] = b[3] = 0;
86 } else {
87 b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
88 b[1] = (((pixel & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
89 b[2] = (((pixel & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
90 b[3] = alpha;
91 }
92 }
93 }
94
95 /* Converts native endian xRGB => RGBx bytes */
96 static void
convert_data_to_bytes(png_structp png,png_row_infop row_info,png_bytep data)97 convert_data_to_bytes (png_structp png, png_row_infop row_info, png_bytep data)
98 {
99 unsigned int i;
100
101 for (i = 0; i < row_info->rowbytes; i += 4) {
102 uint8_t *b = &data[i];
103 uint32_t pixel;
104
105 memcpy (&pixel, b, sizeof (uint32_t));
106
107 b[0] = (pixel & 0xff0000) >> 16;
108 b[1] = (pixel & 0x00ff00) >> 8;
109 b[2] = (pixel & 0x0000ff) >> 0;
110 b[3] = 0;
111 }
112 }
113
114 static gboolean
gxps_png_writer_image_writer_init(GXPSImageWriter * image_writer,FILE * fd,guint width,guint height,guint x_resolution,guint y_resolution)115 gxps_png_writer_image_writer_init (GXPSImageWriter *image_writer,
116 FILE *fd,
117 guint width,
118 guint height,
119 guint x_resolution,
120 guint y_resolution)
121 {
122 GXPSPngWriter *png_writer = GXPS_PNG_WRITER (image_writer);
123
124 png_writer->png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
125 if (!png_writer->png_ptr) {
126 g_printerr ("Error initializing png writer: png_create_write_struct failed\n");
127 return FALSE;
128 }
129
130 png_writer->info_ptr = png_create_info_struct (png_writer->png_ptr);
131 if (!png_writer->info_ptr) {
132 g_printerr ("Error initializing png writer: png_create_info_struct failed\n");
133 return FALSE;
134 }
135
136 if (setjmp (png_jmpbuf (png_writer->png_ptr))) {
137 g_printerr ("Error initializing png writer: png_jmpbuf failed\n");
138 return FALSE;
139 }
140
141 /* write header */
142 png_init_io (png_writer->png_ptr, fd);
143 if (setjmp (png_jmpbuf (png_writer->png_ptr))) {
144 g_printerr ("Error initializing png writer: error writing PNG header\n");
145 return FALSE;
146 }
147
148 png_set_compression_level (png_writer->png_ptr, Z_BEST_COMPRESSION);
149
150 png_set_IHDR (png_writer->png_ptr, png_writer->info_ptr,
151 width, height, 8,
152 png_writer->format == GXPS_PNG_FORMAT_RGB ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA,
153 PNG_INTERLACE_NONE,
154 PNG_COMPRESSION_TYPE_DEFAULT,
155 PNG_FILTER_TYPE_DEFAULT);
156
157 png_set_pHYs (png_writer->png_ptr, png_writer->info_ptr,
158 x_resolution / 0.0254, y_resolution / 0.0254,
159 PNG_RESOLUTION_METER);
160
161 png_write_info (png_writer->png_ptr, png_writer->info_ptr);
162 if (setjmp (png_jmpbuf (png_writer->png_ptr))) {
163 g_printerr ("Error initializing png writer: error writing png info bytes\n");
164 return FALSE;
165 }
166
167 switch (png_writer->format) {
168 case GXPS_PNG_FORMAT_RGB:
169 png_set_write_user_transform_fn (png_writer->png_ptr, convert_data_to_bytes);
170 png_set_filler (png_writer->png_ptr, 0, PNG_FILLER_AFTER);
171
172 break;
173 case GXPS_PNG_FORMAT_RGBA:
174 png_set_write_user_transform_fn (png_writer->png_ptr, unpremultiply_data);
175
176 break;
177 }
178
179 return TRUE;
180 }
181
182 static gboolean
gxps_png_writer_image_writer_write(GXPSImageWriter * image_writer,guchar * row)183 gxps_png_writer_image_writer_write (GXPSImageWriter *image_writer,
184 guchar *row)
185 {
186 GXPSPngWriter *png_writer = GXPS_PNG_WRITER (image_writer);
187
188 png_write_rows (png_writer->png_ptr, &row, 1);
189 if (setjmp (png_jmpbuf (png_writer->png_ptr))) {
190 g_printerr ("Error writing png: error during png row write\n");
191 return FALSE;
192 }
193
194 return TRUE;
195 }
196
197 static gboolean
gxps_png_writer_image_writer_finish(GXPSImageWriter * image_writer)198 gxps_png_writer_image_writer_finish (GXPSImageWriter *image_writer)
199 {
200 GXPSPngWriter *png_writer = GXPS_PNG_WRITER (image_writer);
201
202 png_write_end (png_writer->png_ptr, png_writer->info_ptr);
203 if (setjmp (png_jmpbuf (png_writer->png_ptr))) {
204 g_printerr ("Error finishing png: error during end of write\n");
205 return FALSE;
206 }
207
208 png_destroy_write_struct (&png_writer->png_ptr, &png_writer->info_ptr);
209
210 return TRUE;
211 }
212
213 static void
gxps_png_writer_image_writer_iface_init(GXPSImageWriterInterface * iface)214 gxps_png_writer_image_writer_iface_init (GXPSImageWriterInterface *iface)
215 {
216 iface->init = gxps_png_writer_image_writer_init;
217 iface->write = gxps_png_writer_image_writer_write;
218 iface->finish = gxps_png_writer_image_writer_finish;
219 }
220
221
222