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