Reported by Fedora team for gcc-13 and Andrew created patch here
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / filepng.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  * Copyright (C) 2003-2016 Cinelerra CV contributors
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22
23 #include "asset.h"
24 #include "edit.h"
25 #include "file.h"
26 #include "filepng.h"
27 #include "interlacemodes.h"
28 #include "language.h"
29 #include "mwindow.inc"
30 #include "vframe.h"
31 #include "videodevice.inc"
32 #include "mainerror.h"
33
34 #include <png.h>
35 #include <string.h>
36
37 FilePNG::FilePNG(Asset *asset, File *file)
38  : FileList(asset, file, "PNGLIST", ".png", FILE_PNG, FILE_PNG_LIST)
39 {
40         native_cmodel = -1;
41 }
42
43 FilePNG::~FilePNG()
44 {
45 }
46
47
48
49 int FilePNG::check_sig(Asset *asset)
50 {
51         FILE *stream = fopen(asset->path, "rb");
52
53         if(stream)
54         {
55
56 //printf("FilePNG::check_sig 1\n");
57                 char test[16];
58                 (void)fread(test, 16, 1, stream);
59                 fclose(stream);
60
61 //              if(png_sig_cmp((unsigned char*)test, 0, 8))
62                 if(png_check_sig((unsigned char*)test, 8))
63                 {
64 //printf("FilePNG::check_sig 1\n");
65                         return 1;
66                 }
67                 else
68                 if(test[0] == 'P' && test[1] == 'N' && test[2] == 'G' &&
69                         test[3] == 'L' && test[4] == 'I' && test[5] == 'S' && test[6] == 'T')
70                 {
71 //printf("FilePNG::check_sig 1\n");
72                         return 1;
73                 }
74         }
75         return 0;
76 }
77
78
79
80 void FilePNG::get_parameters(BC_WindowBase *parent_window,
81         Asset *asset, BC_WindowBase* &format_window,
82         int audio_options, int video_options, EDL *edl)
83 {
84         if(video_options)
85         {
86                 PNGConfigVideo *window = new PNGConfigVideo(parent_window, asset);
87                 format_window = window;
88                 window->create_objects();
89                 window->run_window();
90                 delete window;
91         }
92 }
93
94
95
96
97 int FilePNG::can_copy_from(Asset *asset, int64_t position)
98 {
99         if(asset->format == FILE_PNG ||
100                 asset->format == FILE_PNG_LIST)
101                 return 1;
102
103         return 0;
104 }
105
106 int FilePNG::colormodel_supported(int colormodel)
107 {
108         if( colormodel == BC_RGB888 || colormodel == BC_RGBA8888 )
109                 return colormodel;
110         if( colormodel == BC_RGB161616 && native_cmodel == BC_RGBA16161616 )
111                 return colormodel;
112         if( native_cmodel >= 0 )
113                 return native_cmodel;
114         int use_16bit = BC_CModels::is_float(colormodel) ||
115                  BC_CModels::calculate_max(colormodel) > 255 ? 1 : 0;
116         return BC_CModels::has_alpha(colormodel) ?
117                 (use_16bit ? BC_RGBA16161616 : BC_RGBA8888) :
118                 (use_16bit ? BC_RGB161616 : BC_RGB888);
119 }
120
121 int FilePNG::get_best_colormodel(Asset *asset, int driver)
122 {
123         return asset->png_depth == 16 ?
124                 (asset->png_use_alpha ? BC_RGBA16161616 : BC_RGB161616) :
125                 (asset->png_use_alpha ? BC_RGBA8888 : BC_RGB888);
126 }
127
128 int FilePNG::read_frame_header(char *path)
129 {
130         int result = 0;
131         int color_type;
132         int color_depth;
133         int num_trans = 0;
134
135         FILE *stream;
136
137         if(!(stream = fopen(path, "rb")))
138         {
139                 eprintf("Error while opening \"%s\" for reading. \n%m\n", asset->path);
140                 return 1;
141         }
142
143         png_structp png_ptr;
144         png_infop info_ptr;
145         png_infop end_info = 0;
146         png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
147         info_ptr = png_create_info_struct(png_ptr);
148         png_init_io(png_ptr, stream);
149
150
151         png_read_info(png_ptr, info_ptr);
152
153         asset->width = png_get_image_width(png_ptr, info_ptr);
154         asset->height = png_get_image_height(png_ptr, info_ptr);
155         asset->interlace_mode = ILACE_MODE_NOTINTERLACED;
156         color_type = png_get_color_type(png_ptr, info_ptr);
157         color_depth = png_get_bit_depth(png_ptr,info_ptr);
158
159         png_get_tRNS(png_ptr, info_ptr, NULL, &num_trans, NULL);
160         native_cmodel = color_depth == 16 ?
161             ((color_type & PNG_COLOR_MASK_ALPHA) ?
162                 BC_RGBA16161616 : BC_RGB161616) :
163             ((color_type & PNG_COLOR_MASK_ALPHA) || (num_trans > 0) ?
164                 BC_RGBA8888 : BC_RGB888);
165         asset->png_use_alpha = BC_CModels::has_alpha(native_cmodel) ? 1 : 0;
166
167         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
168         fclose(stream);
169         return result;
170 }
171
172
173
174
175 static void read_function(png_structp png_ptr,
176         png_bytep data,
177         png_uint_32 length)
178 {
179         VFrame *input = (VFrame*)png_get_io_ptr(png_ptr);
180
181         memcpy(data, input->get_data() + input->get_compressed_size(), length);
182         input->set_compressed_size(input->get_compressed_size() + length);
183 }
184
185 static void write_function(png_structp png_ptr, png_bytep data, png_uint_32 length)
186 {
187         VFrame *output = (VFrame*)png_get_io_ptr(png_ptr);
188         long total_length = output->get_compressed_size() + length;
189         if(output->get_compressed_allocated() < total_length) {
190                 long new_length = 2 * (output->get_compressed_allocated() + length);
191                 output->allocate_compressed_data(new_length);
192         }
193         memcpy(output->get_data() + output->get_compressed_size(), data, length);
194         output->set_compressed_size(total_length);
195 }
196
197 static void flush_function(png_structp png_ptr)
198 {
199 }
200
201
202 int FilePNG::write_frame(VFrame *frame, VFrame *data, FrameWriterUnit *unit)
203 {
204         PNGUnit *png_unit = (PNGUnit*)unit;
205         png_structp png_ptr = 0;
206         png_infop info_ptr = 0;
207         VFrame *output_frame = frame;
208         int result = 1;
209         data->set_compressed_size(0);
210 //printf("FilePNG::write_frame 1\n");
211         native_cmodel = asset->png_depth == 16 ?
212                 (asset->png_use_alpha ?  BC_RGBA16161616 : BC_RGB161616) :
213                 (asset->png_use_alpha ?  BC_RGBA8888 : BC_RGB888) ;
214         if( frame->get_color_model() != native_cmodel ) {
215                 VFrame::get_temp(png_unit->temp_frame,
216                         asset->width, asset->height, native_cmodel);
217                 png_unit->temp_frame->transfer_from(frame);
218                 output_frame = png_unit->temp_frame;
219         }
220
221 //printf("FilePNG::write_frame 1\n");
222         png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
223         if( png_ptr ) {
224                 if( !setjmp(png_jmpbuf(png_ptr)) ) {
225                         info_ptr = png_create_info_struct(png_ptr);
226                         png_set_write_fn(png_ptr, data,
227                                 (png_rw_ptr)write_function, (png_flush_ptr)flush_function);
228                         png_set_compression_level(png_ptr, asset->png_compression);
229                         png_set_IHDR(png_ptr, info_ptr, asset->width, asset->height, asset->png_depth,
230                                 asset->png_use_alpha ?  PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB,
231                                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
232                         png_write_info(png_ptr, info_ptr);
233                         if( BC_Resources::little_endian ) png_set_swap(png_ptr);
234                         png_write_image(png_ptr, output_frame->get_rows());
235                         png_write_end(png_ptr, info_ptr);
236                         result = 0;
237                 }
238                 png_destroy_write_struct(&png_ptr, &info_ptr);
239         }
240         if( result ) {
241                 char error[256];  png_error(png_ptr, error);
242                 fprintf(stderr, "FilePNG::write_frame failed: %s\n", error);
243         }
244 //printf("FilePNG::write_frame 3 %d\n", data->get_compressed_size());
245         return result;
246 }
247
248 int FilePNG::read_frame(VFrame *output, VFrame *input)
249 {
250         png_structp png_ptr;
251         png_infop info_ptr;
252         png_infop end_info = 0;
253         int result = 0;
254         int color_type;
255         int color_depth;
256         int colormodel;
257         int size = input->get_compressed_size();
258         input->set_compressed_size(0);
259
260         //printf("FilePNG::read_frame 1 %d %d\n", native_cmodel, output->get_color_model());
261         png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
262         info_ptr = png_create_info_struct(png_ptr);
263         png_set_read_fn(png_ptr, input, (png_rw_ptr)read_function);
264         png_read_info(png_ptr, info_ptr);
265
266         int png_color_type = png_get_color_type(png_ptr, info_ptr);
267         if( png_color_type == PNG_COLOR_TYPE_GRAY ||
268             png_color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
269                 png_set_gray_to_rgb(png_ptr);
270
271         colormodel = output->get_color_model();
272         color_type = png_get_color_type(png_ptr, info_ptr);
273         color_depth = png_get_bit_depth(png_ptr,info_ptr);
274
275         int num_trans = 0;
276         png_get_tRNS(png_ptr, info_ptr, NULL, &num_trans, NULL);
277
278         native_cmodel = color_depth == 16 ?
279             ((color_type & PNG_COLOR_MASK_ALPHA) ?
280                 BC_RGBA16161616 : BC_RGB161616) :
281             ((color_type & PNG_COLOR_MASK_ALPHA) || (num_trans > 0) ?
282                 BC_RGBA8888 : BC_RGB888);
283
284         if( ((native_cmodel == BC_RGBA16161616) || (native_cmodel == BC_RGB161616)) &&
285             ((colormodel == BC_RGBA8888) || (colormodel == BC_RGB888)) )
286                 png_set_strip_16(png_ptr);
287
288 // If we're dropping the alpha channel
289         if( !BC_CModels::has_alpha(colormodel) && BC_CModels::has_alpha(native_cmodel) ) {
290                 png_color_16 my_background;
291                 png_color_16p image_background;
292                 memset(&my_background,0,sizeof(png_color_16));
293 // use the background color of the image
294                 if( png_get_bKGD(png_ptr, info_ptr, &image_background) )
295                         png_set_background(png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
296                 else
297 // otherwise, use black
298                         png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
299         }
300         else if( BC_CModels::has_alpha(colormodel) && !BC_CModels::has_alpha(native_cmodel) )
301 // If we're adding the alpha channel, alpha = max pixel value
302                 png_set_add_alpha(png_ptr, BC_CModels::calculate_max(colormodel), PNG_FILLER_AFTER);
303
304 // Little endian
305         if( (color_depth == 16) &&
306             ((colormodel == BC_RGBA16161616) || (colormodel == BC_RGB161616)) )
307                 png_set_swap(png_ptr);
308
309         if( !(color_type & PNG_COLOR_MASK_COLOR) )
310                 png_set_gray_to_rgb(png_ptr);
311
312         if( color_type & PNG_COLOR_MASK_PALETTE )
313                 png_set_palette_to_rgb(png_ptr);
314
315         if( color_depth <= 8 )
316                 png_set_expand(png_ptr);
317
318 // read the image
319         png_read_image(png_ptr, output->get_rows());
320
321 //printf("FilePNG::read_frame 3\n");
322         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
323         input->set_compressed_size(size);
324 //printf("FilePNG::read_frame 4\n");
325         return result;
326 }
327
328 FrameWriterUnit* FilePNG::new_writer_unit(FrameWriter *writer)
329 {
330         return new PNGUnit(this, writer);
331 }
332
333
334 PNGUnit::PNGUnit(FilePNG *file, FrameWriter *writer)
335  : FrameWriterUnit(writer)
336 {
337         this->file = file;
338         temp_frame = 0;
339 }
340 PNGUnit::~PNGUnit()
341 {
342         if(temp_frame) delete temp_frame;
343 }
344
345
346 PNGConfigVideo::PNGConfigVideo(BC_WindowBase *parent_window, Asset *asset)
347  : BC_Window(_(PROGRAM_NAME ": Video Compression"),
348         parent_window->get_abs_cursor_x(1), parent_window->get_abs_cursor_y(1),
349         xS(240), yS(160))
350 {
351         this->parent_window = parent_window;
352         this->asset = asset;
353         use_alpha = 0;
354         compression = 0;
355         depth8 = 0;
356         depth16 = 0;
357 // *** CONTEXT_HELP ***
358         context_help_set_keyword("Single File Rendering");
359 }
360
361 PNGConfigVideo::~PNGConfigVideo()
362 {
363         delete compression;
364 }
365
366 void PNGConfigVideo::create_objects()
367 {
368         lock_window("PNGConfigVideo::create_objects");
369         BC_Title *title;
370         int xs5 = xS(5), ys5 = yS(5), xs10 = xS(10), ys10 = yS(10);
371         int x = xs10, y = ys10;
372         add_subwindow(use_alpha = new PNGUseAlpha(this, x, y));
373         y += use_alpha->get_h() + ys10;
374         add_subwindow(title = new BC_Title(x,y,_("Compression:")));
375         int x1 = x + title->get_w() + xs10;
376         compression = new PNGCompression(this, x1, y);
377         compression->create_objects();
378         y += compression->get_h() + ys5;
379         add_subwindow(title = new BC_Title(x,y,_("Depth:")));
380         x1 = x + title->get_w() + xs10;
381         add_subwindow(depth8 = new PNGDepth8bit(this, x1, y));
382         x1 += depth8->get_w() + xs5;
383         add_subwindow(depth16 = new PNGDepth16bit(this, x1, y));
384         add_subwindow(new BC_OKButton(this));
385         show_window(1);
386         unlock_window();
387 }
388
389 int PNGConfigVideo::close_event()
390 {
391         set_done(0);
392         return 1;
393 }
394
395 int PNGConfigVideo::update_depth(int depth)
396 {
397         asset->png_depth = depth;
398         depth8->update(depth == 8);
399         depth16->update(depth == 16);
400         return 1;
401 }
402
403
404 PNGUseAlpha::PNGUseAlpha(PNGConfigVideo *gui, int x, int y)
405  : BC_CheckBox(x, y, gui->asset->png_use_alpha, _("Use alpha"))
406 {
407         this->gui = gui;
408 }
409
410 int PNGUseAlpha::handle_event()
411 {
412         gui->asset->png_use_alpha = get_value();
413         return 1;
414 }
415
416 PNGCompression::PNGCompression(PNGConfigVideo *gui, int x, int y)
417  : BC_TumbleTextBox(gui, (int64_t)gui->asset->png_compression,
418         (int64_t)0, (int64_t)9, x, y, xS(40))
419 {
420         this->gui = gui;
421 }
422
423 int PNGCompression::handle_event()
424 {
425         gui->asset->png_compression = atol(get_text());
426         return 1;
427 }
428
429 PNGDepth8bit::PNGDepth8bit(PNGConfigVideo *gui, int x, int y)
430  : BC_Radial(x, y, gui->asset->png_depth==8,_("8 Bit"))
431 {
432         this->gui = gui;
433 }
434
435 int PNGDepth8bit::handle_event()
436 {
437         return gui->update_depth(8);
438 }
439
440 PNGDepth16bit::PNGDepth16bit(PNGConfigVideo *gui, int x, int y)
441  : BC_Radial(x, y, gui->asset->png_depth==16,_("16 Bit"))
442 {
443         this->gui = gui;
444 }
445
446 int PNGDepth16bit::handle_event()
447 {
448         return gui->update_depth(16);
449 }
450