@@ -34,6 +34,27 @@ extern "C" {
34
34
#undef jmpbuf
35
35
#endif
36
36
37
+ struct buffer_t {
38
+ PyObject *str;
39
+ size_t cursor;
40
+ size_t size;
41
+ };
42
+
43
+
44
+ static void write_png_data_buffer (png_structp png_ptr, png_bytep data, png_size_t length)
45
+ {
46
+ buffer_t *buff = (buffer_t *)png_get_io_ptr (png_ptr);
47
+ if (buff->cursor + length < buff->size ) {
48
+ memcpy (PyBytes_AS_STRING (buff->str ) + buff->cursor , data, length);
49
+ buff->cursor += length;
50
+ }
51
+ }
52
+
53
+ static void flush_png_data_buffer (png_structp png_ptr)
54
+ {
55
+
56
+ }
57
+
37
58
static void write_png_data (png_structp png_ptr, png_bytep data, png_size_t length)
38
59
{
39
60
PyObject *py_file_obj = (PyObject *)png_get_io_ptr (png_ptr);
@@ -69,20 +90,24 @@ static PyObject *Py_write_png(PyObject *self, PyObject *args, PyObject *kwds)
69
90
numpy::array_view<unsigned char , 3 > buffer;
70
91
PyObject *filein;
71
92
double dpi = 0 ;
72
- const char *names[] = { " buffer" , " file" , " dpi" , NULL };
93
+ int compression = 6 ;
94
+ int filter = -1 ;
95
+ const char *names[] = { " buffer" , " file" , " dpi" , " compression" , " filter" , NULL };
73
96
74
97
// We don't need strict contiguity, just for each row to be
75
98
// contiguous, and libpng has special handling for getting RGB out
76
99
// of RGBA, ARGB or BGR. But the simplest thing to do is to
77
100
// enforce contiguity using array_view::converter_contiguous.
78
101
if (!PyArg_ParseTupleAndKeywords (args,
79
102
kwds,
80
- " O&O|d :write_png" ,
103
+ " O&O|dii :write_png" ,
81
104
(char **)names,
82
105
&buffer.converter_contiguous ,
83
106
&buffer,
84
107
&filein,
85
- &dpi)) {
108
+ &dpi,
109
+ &compression,
110
+ &filter)) {
86
111
return NULL ;
87
112
}
88
113
@@ -104,6 +129,8 @@ static PyObject *Py_write_png(PyObject *self, PyObject *args, PyObject *kwds)
104
129
png_infop info_ptr = NULL ;
105
130
struct png_color_8_struct sig_bit;
106
131
int png_color_type;
132
+ buffer_t buff;
133
+ buff.str = NULL ;
107
134
108
135
switch (channels) {
109
136
case 1 :
@@ -122,6 +149,12 @@ static PyObject *Py_write_png(PyObject *self, PyObject *args, PyObject *kwds)
122
149
goto exit ;
123
150
}
124
151
152
+ if (compression < 0 || compression > 9 ) {
153
+ PyErr_Format (PyExc_ValueError,
154
+ " compression must be in range 0-9, got %d" , compression);
155
+ goto exit ;
156
+ }
157
+
125
158
if (PyBytes_Check (filein) || PyUnicode_Check (filein)) {
126
159
if ((py_file = mpl_PyFile_OpenFile (filein, (char *)" wb" )) == NULL ) {
127
160
goto exit ;
@@ -131,7 +164,14 @@ static PyObject *Py_write_png(PyObject *self, PyObject *args, PyObject *kwds)
131
164
py_file = filein;
132
165
}
133
166
134
- if ((fp = mpl_PyFile_Dup (py_file, (char *)" wb" , &offset))) {
167
+ if (filein == Py_None) {
168
+ buff.size = width * height * 4 + 1024 ;
169
+ buff.str = PyBytes_FromStringAndSize (NULL , buff.size );
170
+ if (buff.str == NULL ) {
171
+ goto exit ;
172
+ }
173
+ buff.cursor = 0 ;
174
+ } else if ((fp = mpl_PyFile_Dup (py_file, (char *)" wb" , &offset))) {
135
175
close_dup_file = true ;
136
176
} else {
137
177
PyErr_Clear ();
@@ -152,6 +192,11 @@ static PyObject *Py_write_png(PyObject *self, PyObject *args, PyObject *kwds)
152
192
goto exit ;
153
193
}
154
194
195
+ png_set_compression_level (png_ptr, compression);
196
+ if (filter >= 0 ) {
197
+ png_set_filter (png_ptr, 0 , filter);
198
+ }
199
+
155
200
info_ptr = png_create_info_struct (png_ptr);
156
201
if (info_ptr == NULL ) {
157
202
PyErr_SetString (PyExc_RuntimeError, " Could not create info struct" );
@@ -163,7 +208,9 @@ static PyObject *Py_write_png(PyObject *self, PyObject *args, PyObject *kwds)
163
208
goto exit ;
164
209
}
165
210
166
- if (fp) {
211
+ if (buff.str ) {
212
+ png_set_write_fn (png_ptr, (void *)&buff, &write_png_data_buffer, &flush_png_data_buffer);
213
+ } else if (fp) {
167
214
png_init_io (png_ptr, fp);
168
215
} else {
169
216
png_set_write_fn (png_ptr, (void *)py_file, &write_png_data, &flush_png_data);
@@ -227,8 +274,13 @@ static PyObject *Py_write_png(PyObject *self, PyObject *args, PyObject *kwds)
227
274
}
228
275
229
276
if (PyErr_Occurred ()) {
277
+ Py_XDECREF (buff.str );
230
278
return NULL ;
231
279
} else {
280
+ if (buff.str ) {
281
+ _PyBytes_Resize (&buff.str , buff.cursor );
282
+ return buff.str ;
283
+ }
232
284
Py_RETURN_NONE;
233
285
}
234
286
}
@@ -558,6 +610,15 @@ extern "C" {
558
610
559
611
import_array ();
560
612
613
+ if (PyModule_AddIntConstant (m, " PNG_FILTER_NONE" , PNG_FILTER_NONE) ||
614
+ PyModule_AddIntConstant (m, " PNG_FILTER_SUB" , PNG_FILTER_SUB) ||
615
+ PyModule_AddIntConstant (m, " PNG_FILTER_UP" , PNG_FILTER_UP) ||
616
+ PyModule_AddIntConstant (m, " PNG_FILTER_AVG" , PNG_FILTER_AVG) ||
617
+ PyModule_AddIntConstant (m, " PNG_FILTER_PAETH" , PNG_FILTER_PAETH)) {
618
+ INITERROR;
619
+ }
620
+
621
+
561
622
#if PY3K
562
623
return m;
563
624
#endif
0 commit comments