Credit Andrew - fix vorbis audio which was scratchy and ensure aging plugin does...
[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         if(!png_ptr) return 0;
263         info_ptr = png_create_info_struct(png_ptr);
264         if(!info_ptr) return 0;
265         if (setjmp(png_jmpbuf(png_ptr))) {
266         png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
267         return 0;
268         }
269         png_set_read_fn(png_ptr, input, (png_rw_ptr)read_function);
270         png_read_info(png_ptr, info_ptr);
271
272         int png_color_type = png_get_color_type(png_ptr, info_ptr);
273         if( png_color_type == PNG_COLOR_TYPE_GRAY ||
274             png_color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
275                 png_set_gray_to_rgb(png_ptr);
276
277         colormodel = output->get_color_model();
278         color_type = png_get_color_type(png_ptr, info_ptr);
279         color_depth = png_get_bit_depth(png_ptr,info_ptr);
280
281         int num_trans = 0;
282         png_get_tRNS(png_ptr, info_ptr, NULL, &num_trans, NULL);
283
284         native_cmodel = color_depth == 16 ?
285             ((color_type & PNG_COLOR_MASK_ALPHA) ?
286                 BC_RGBA16161616 : BC_RGB161616) :
287             ((color_type & PNG_COLOR_MASK_ALPHA) || (num_trans > 0) ?
288                 BC_RGBA8888 : BC_RGB888);
289
290         if( ((native_cmodel == BC_RGBA16161616) || (native_cmodel == BC_RGB161616)) &&
291             ((colormodel == BC_RGBA8888) || (colormodel == BC_RGB888)) )
292                 png_set_strip_16(png_ptr);
293
294 // If we're dropping the alpha channel
295         if( !BC_CModels::has_alpha(colormodel) && BC_CModels::has_alpha(native_cmodel) ) {
296                 png_color_16 my_background;
297                 png_color_16p image_background;
298                 memset(&my_background,0,sizeof(png_color_16));
299 // use the background color of the image
300                 if( png_get_bKGD(png_ptr, info_ptr, &image_background) )
301                         png_set_background(png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
302                 else
303 // otherwise, use black
304                         png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
305         }
306         else if( BC_CModels::has_alpha(colormodel) && !BC_CModels::has_alpha(native_cmodel) )
307 // If we're adding the alpha channel, alpha = max pixel value
308                 png_set_add_alpha(png_ptr, BC_CModels::calculate_max(colormodel), PNG_FILLER_AFTER);
309
310 // Little endian
311         if( (color_depth == 16) &&
312             ((colormodel == BC_RGBA16161616) || (colormodel == BC_RGB161616)) )
313                 png_set_swap(png_ptr);
314
315         if( !(color_type & PNG_COLOR_MASK_COLOR) )
316                 png_set_gray_to_rgb(png_ptr);
317
318         if( color_type & PNG_COLOR_MASK_PALETTE )
319                 png_set_palette_to_rgb(png_ptr);
320
321         if( color_depth <= 8 )
322                 png_set_expand(png_ptr);
323
324 // read the image
325         png_read_image(png_ptr, output->get_rows());
326
327 //printf("FilePNG::read_frame 3\n");
328         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
329         input->set_compressed_size(size);
330 //printf("FilePNG::read_frame 4\n");
331         return result;
332 }
333
334 FrameWriterUnit* FilePNG::new_writer_unit(FrameWriter *writer)
335 {
336         return new PNGUnit(this, writer);
337 }
338
339
340 PNGUnit::PNGUnit(FilePNG *file, FrameWriter *writer)
341  : FrameWriterUnit(writer)
342 {
343         this->file = file;
344         temp_frame = 0;
345 }
346 PNGUnit::~PNGUnit()
347 {
348         if(temp_frame) delete temp_frame;
349 }
350
351
352 PNGConfigVideo::PNGConfigVideo(BC_WindowBase *parent_window, Asset *asset)
353  : BC_Window(_(PROGRAM_NAME ": Video Compression"),
354         parent_window->get_abs_cursor_x(1), parent_window->get_abs_cursor_y(1),
355         xS(240), yS(160))
356 {
357         this->parent_window = parent_window;
358         this->asset = asset;
359         use_alpha = 0;
360         compression = 0;
361         depth8 = 0;
362         depth16 = 0;
363 // *** CONTEXT_HELP ***
364         context_help_set_keyword("Single File Rendering");
365 }
366
367 PNGConfigVideo::~PNGConfigVideo()
368 {
369         delete compression;
370 }
371
372 void PNGConfigVideo::create_objects()
373 {
374         lock_window("PNGConfigVideo::create_objects");
375         BC_Title *title;
376         int xs5 = xS(5), ys5 = yS(5), xs10 = xS(10), ys10 = yS(10);
377         int x = xs10, y = ys10;
378         add_subwindow(use_alpha = new PNGUseAlpha(this, x, y));
379         y += use_alpha->get_h() + ys10;
380         add_subwindow(title = new BC_Title(x,y,_("Compression:")));
381         int x1 = x + title->get_w() + xs10;
382         compression = new PNGCompression(this, x1, y);
383         compression->create_objects();
384         y += compression->get_h() + ys5;
385         add_subwindow(title = new BC_Title(x,y,_("Depth:")));
386         x1 = x + title->get_w() + xs10;
387         add_subwindow(depth8 = new PNGDepth8bit(this, x1, y));
388         x1 += depth8->get_w() + xs5;
389         add_subwindow(depth16 = new PNGDepth16bit(this, x1, y));
390         add_subwindow(new BC_OKButton(this));
391         show_window(1);
392         unlock_window();
393 }
394
395 int PNGConfigVideo::close_event()
396 {
397         set_done(0);
398         return 1;
399 }
400
401 int PNGConfigVideo::update_depth(int depth)
402 {
403         asset->png_depth = depth;
404         depth8->update(depth == 8);
405         depth16->update(depth == 16);
406         return 1;
407 }
408
409
410 PNGUseAlpha::PNGUseAlpha(PNGConfigVideo *gui, int x, int y)
411  : BC_CheckBox(x, y, gui->asset->png_use_alpha, _("Use alpha"))
412 {
413         this->gui = gui;
414 }
415
416 int PNGUseAlpha::handle_event()
417 {
418         gui->asset->png_use_alpha = get_value();
419         return 1;
420 }
421
422 PNGCompression::PNGCompression(PNGConfigVideo *gui, int x, int y)
423  : BC_TumbleTextBox(gui, (int64_t)gui->asset->png_compression,
424         (int64_t)0, (int64_t)9, x, y, xS(40))
425 {
426         this->gui = gui;
427 }
428
429 int PNGCompression::handle_event()
430 {
431         gui->asset->png_compression = atol(get_text());
432         return 1;
433 }
434
435 PNGDepth8bit::PNGDepth8bit(PNGConfigVideo *gui, int x, int y)
436  : BC_Radial(x, y, gui->asset->png_depth==8,_("8 Bit"))
437 {
438         this->gui = gui;
439 }
440
441 int PNGDepth8bit::handle_event()
442 {
443         return gui->update_depth(8);
444 }
445
446 PNGDepth16bit::PNGDepth16bit(PNGConfigVideo *gui, int x, int y)
447  : BC_Radial(x, y, gui->asset->png_depth==16,_("16 Bit"))
448 {
449         this->gui = gui;
450 }
451
452 int PNGDepth16bit::handle_event()
453 {
454         return gui->update_depth(16);
455 }
456