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