fix mask vframe setup, add unshared vframe constructor
[goodguy/history.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,
81         BC_WindowBase* &format_window,
82         int audio_options,
83         int video_options)
84 {
85         if(video_options)
86         {
87                 PNGConfigVideo *window = new PNGConfigVideo(parent_window, asset);
88                 format_window = window;
89                 window->create_objects();
90                 window->run_window();
91                 delete window;
92         }
93 }
94
95
96
97
98 int FilePNG::can_copy_from(Asset *asset, int64_t position)
99 {
100         if(asset->format == FILE_PNG ||
101                 asset->format == FILE_PNG_LIST)
102                 return 1;
103
104         return 0;
105 }
106
107
108 int FilePNG::colormodel_supported(int colormodel)
109 {
110         if( ((colormodel == BC_RGBA8888)  && (native_cmodel == BC_RGBA16161616)) ||
111             ((colormodel == BC_RGB161616) && (native_cmodel == BC_RGBA16161616)) ||
112              (colormodel == BC_RGB888) || (colormodel == BC_RGBA8888) )
113                 return colormodel;
114         if( (colormodel == BC_RGB161616) && (native_cmodel == BC_RGBA8888) )
115                 return BC_RGB888;
116         if( native_cmodel >= 0 )
117                 return native_cmodel;
118         return asset->png_use_alpha ? BC_RGBA8888 : BC_RGB888;
119 }
120
121
122 int FilePNG::get_best_colormodel(Asset *asset, int driver)
123 {
124         if(asset->png_use_alpha)
125                 return BC_RGBA8888;
126         else
127                 return BC_RGB888;
128 }
129
130
131
132
133 int FilePNG::read_frame_header(char *path)
134 {
135         int result = 0;
136         int color_type;
137         int color_depth;
138         int num_trans = 0;
139
140         FILE *stream;
141
142         if(!(stream = fopen(path, "rb")))
143         {
144                 eprintf("Error while opening \"%s\" for reading. \n%m\n", asset->path);
145                 return 1;
146         }
147
148         png_structp png_ptr;
149         png_infop info_ptr;
150         png_infop end_info = 0;
151         png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
152         info_ptr = png_create_info_struct(png_ptr);
153         png_init_io(png_ptr, stream);
154
155
156         png_read_info(png_ptr, info_ptr);
157
158         asset->width = png_get_image_width(png_ptr, info_ptr);
159         asset->height = png_get_image_height(png_ptr, info_ptr);
160         asset->interlace_mode = ILACE_MODE_NOTINTERLACED;
161         color_type = png_get_color_type(png_ptr, info_ptr);
162         color_depth = png_get_bit_depth(png_ptr,info_ptr);
163
164         png_get_tRNS(png_ptr, info_ptr, NULL, &num_trans, NULL);
165         native_cmodel = color_depth == 16 ?
166             ((color_type & PNG_COLOR_MASK_ALPHA) ?
167                 BC_RGBA16161616 : BC_RGB161616) :
168             ((color_type & PNG_COLOR_MASK_ALPHA) || (num_trans > 0) ?
169                 BC_RGBA8888 : BC_RGB888);
170         asset->png_use_alpha = BC_CModels::has_alpha(native_cmodel) ? 1 : 0;
171
172         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
173         fclose(stream);
174         return result;
175 }
176
177
178
179
180 static void read_function(png_structp png_ptr,
181         png_bytep data,
182         png_uint_32 length)
183 {
184         VFrame *input = (VFrame*)png_get_io_ptr(png_ptr);
185
186         memcpy(data, input->get_data() + input->get_compressed_size(), length);
187         input->set_compressed_size(input->get_compressed_size() + length);
188 }
189
190 static void write_function(png_structp png_ptr, png_bytep data, png_uint_32 length)
191 {
192         VFrame *output = (VFrame*)png_get_io_ptr(png_ptr);
193         long total_length = output->get_compressed_size() + length;
194         if(output->get_compressed_allocated() < total_length) {
195                 long new_length = 2 * (output->get_compressed_allocated() + length);
196                 output->allocate_compressed_data(new_length);
197         }
198         memcpy(output->get_data() + output->get_compressed_size(), data, length);
199         output->set_compressed_size(total_length);
200 }
201
202 static void flush_function(png_structp png_ptr)
203 {
204 }
205
206
207 int FilePNG::write_frame(VFrame *frame, VFrame *data, FrameWriterUnit *unit)
208 {
209         PNGUnit *png_unit = (PNGUnit*)unit;
210         png_structp png_ptr = 0;
211         png_infop info_ptr = 0;
212         VFrame *output_frame;
213         int result = 1;
214         data->set_compressed_size(0);
215 //printf("FilePNG::write_frame 1\n");
216         native_cmodel = asset->png_use_alpha ? BC_RGBA8888 : BC_RGB888;
217         if(frame->get_color_model() != native_cmodel)
218         {
219                 if(!png_unit->temp_frame) png_unit->temp_frame =
220                         new VFrame(asset->width, asset->height, native_cmodel, 0);
221
222                 png_unit->temp_frame->transfer_from(frame);
223                 output_frame = png_unit->temp_frame;
224         }
225         else
226                 output_frame = frame;
227
228 //printf("FilePNG::write_frame 1\n");
229         png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
230         if( png_ptr ) {
231                 if( !setjmp(png_jmpbuf(png_ptr)) ) {
232                         info_ptr = png_create_info_struct(png_ptr);
233                         png_set_write_fn(png_ptr, data,
234                                 (png_rw_ptr)write_function, (png_flush_ptr)flush_function);
235                         png_set_compression_level(png_ptr, 5);
236
237                         png_set_IHDR(png_ptr, info_ptr, asset->width, asset->height, 8,
238                                 asset->png_use_alpha ?  PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB,
239                                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
240                         png_write_info(png_ptr, info_ptr);
241                         png_write_image(png_ptr, output_frame->get_rows());
242                         png_write_end(png_ptr, info_ptr);
243                         result = 0;
244                 }
245                 png_destroy_write_struct(&png_ptr, &info_ptr);
246         }
247         if( result ) {
248                 char error[256];  png_error(png_ptr, error);
249                 fprintf(stderr, "FilePNG::write_frame failed: %s\n", error);
250         }
251 //printf("FilePNG::write_frame 3 %d\n", data->get_compressed_size());
252         return result;
253 }
254
255 int FilePNG::read_frame(VFrame *output, VFrame *input)
256 {
257         png_structp png_ptr;
258         png_infop info_ptr;
259         png_infop end_info = 0;
260         int result = 0;
261         int color_type;
262         int color_depth;
263         int colormodel;
264         int size = input->get_compressed_size();
265         input->set_compressed_size(0);
266
267         //printf("FilePNG::read_frame 1 %d %d\n", native_cmodel, output->get_color_model());
268         png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
269         info_ptr = png_create_info_struct(png_ptr);
270         png_set_read_fn(png_ptr, input, (png_rw_ptr)read_function);
271         png_read_info(png_ptr, info_ptr);
272
273         int png_color_type = png_get_color_type(png_ptr, info_ptr);
274         if( png_color_type == PNG_COLOR_TYPE_GRAY ||
275             png_color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
276                 png_set_gray_to_rgb(png_ptr);
277
278         colormodel = output->get_color_model();
279         color_type = png_get_color_type(png_ptr, info_ptr);
280         color_depth = png_get_bit_depth(png_ptr,info_ptr);
281
282         int num_trans = 0;
283         png_get_tRNS(png_ptr, info_ptr, NULL, &num_trans, NULL);
284
285         native_cmodel = color_depth == 16 ?
286             ((color_type & PNG_COLOR_MASK_ALPHA) ?
287                 BC_RGBA16161616 : BC_RGB161616) :
288             ((color_type & PNG_COLOR_MASK_ALPHA) || (num_trans > 0) ?
289                 BC_RGBA8888 : BC_RGB888);
290
291         if( ((native_cmodel == BC_RGBA16161616) || (native_cmodel == BC_RGB161616)) &&
292             ((colormodel == BC_RGBA8888) || (colormodel == BC_RGB888)) )
293                 png_set_strip_16(png_ptr);
294
295 // If we're dropping the alpha channel
296         if( !BC_CModels::has_alpha(colormodel) && BC_CModels::has_alpha(native_cmodel) ) {
297                 png_color_16 my_background;
298                 png_color_16p image_background;
299                 memset(&my_background,0,sizeof(png_color_16));
300 // use the background color of the image
301                 if( png_get_bKGD(png_ptr, info_ptr, &image_background) )
302                         png_set_background(png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
303                 else
304 // otherwise, use black
305                         png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
306         }
307         else if( BC_CModels::has_alpha(colormodel) && !BC_CModels::has_alpha(native_cmodel) )
308 // If we're adding the alpha channel, alpha = max pixel value
309                 png_set_add_alpha(png_ptr, BC_CModels::calculate_max(colormodel), PNG_FILLER_AFTER);
310
311 // Little endian
312         if( (color_depth == 16) &&
313             ((colormodel == BC_RGBA16161616) || (colormodel == BC_RGB161616)) )
314                 png_set_swap(png_ptr);
315
316         if( !(color_type & PNG_COLOR_MASK_COLOR) )
317                 png_set_gray_to_rgb(png_ptr);
318
319         if( color_type & PNG_COLOR_MASK_PALETTE )
320                 png_set_palette_to_rgb(png_ptr);
321
322         if( color_depth <= 8 )
323                 png_set_expand(png_ptr);
324
325 // read the image
326         png_read_image(png_ptr, output->get_rows());
327
328 //printf("FilePNG::read_frame 3\n");
329         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
330         input->set_compressed_size(size);
331 //printf("FilePNG::read_frame 4\n");
332         return result;
333 }
334
335 FrameWriterUnit* FilePNG::new_writer_unit(FrameWriter *writer)
336 {
337         return new PNGUnit(this, writer);
338 }
339
340
341
342
343
344
345
346
347
348
349
350
351 PNGUnit::PNGUnit(FilePNG *file, FrameWriter *writer)
352  : FrameWriterUnit(writer)
353 {
354         this->file = file;
355         temp_frame = 0;
356 }
357 PNGUnit::~PNGUnit()
358 {
359         if(temp_frame) delete temp_frame;
360 }
361
362
363
364
365
366
367
368
369
370 PNGConfigVideo::PNGConfigVideo(BC_WindowBase *parent_window, Asset *asset)
371  : BC_Window(_(PROGRAM_NAME ": Video Compression"),
372         parent_window->get_abs_cursor_x(1),
373         parent_window->get_abs_cursor_y(1),
374         200,
375         100)
376 {
377         this->parent_window = parent_window;
378         this->asset = asset;
379 }
380
381 PNGConfigVideo::~PNGConfigVideo()
382 {
383 }
384
385 void PNGConfigVideo::create_objects()
386 {
387         lock_window("PNGConfigVideo::create_objects()");
388         int x = 10, y = 10;
389         add_subwindow(new PNGUseAlpha(this, x, 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
402 PNGUseAlpha::PNGUseAlpha(PNGConfigVideo *gui, int x, int y)
403  : BC_CheckBox(x, y, gui->asset->png_use_alpha, _("Use alpha"))
404 {
405         this->gui = gui;
406 }
407
408 int PNGUseAlpha::handle_event()
409 {
410         gui->asset->png_use_alpha = get_value();
411         return 1;
412 }
413
414
415