/* * CINELERRA * Copyright (C) 2008 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "asset.h" #include "edit.h" #include "file.h" #include "filepng.h" #include "interlacemodes.h" #include "language.h" #include "mwindow.inc" #include "vframe.h" #include "videodevice.inc" #include "mainerror.h" #include #include FilePNG::FilePNG(Asset *asset, File *file) : FileList(asset, file, "PNGLIST", ".png", FILE_PNG, FILE_PNG_LIST) { } FilePNG::~FilePNG() { } int FilePNG::check_sig(Asset *asset) { FILE *stream = fopen(asset->path, "rb"); if(stream) { //printf("FilePNG::check_sig 1\n"); char test[16]; (void)fread(test, 16, 1, stream); fclose(stream); // if(png_sig_cmp((unsigned char*)test, 0, 8)) if(png_check_sig((unsigned char*)test, 8)) { //printf("FilePNG::check_sig 1\n"); return 1; } else if(test[0] == 'P' && test[1] == 'N' && test[2] == 'G' && test[3] == 'L' && test[4] == 'I' && test[5] == 'S' && test[6] == 'T') { //printf("FilePNG::check_sig 1\n"); return 1; } } return 0; } void FilePNG::get_parameters(BC_WindowBase *parent_window, Asset *asset, BC_WindowBase* &format_window, int audio_options, int video_options) { if(video_options) { PNGConfigVideo *window = new PNGConfigVideo(parent_window, asset); format_window = window; window->create_objects(); window->run_window(); delete window; } } int FilePNG::can_copy_from(Asset *asset, int64_t position) { if(asset->format == FILE_PNG || asset->format == FILE_PNG_LIST) return 1; return 0; } int FilePNG::colormodel_supported(int colormodel) { if (((colormodel == BC_RGBA8888) && (native_cmodel == BC_RGBA16161616)) || ((colormodel == BC_RGB161616) && (native_cmodel == BC_RGBA16161616)) || (colormodel == BC_RGB888)) { return colormodel; } else if ((colormodel == BC_RGB161616) && (native_cmodel == BC_RGBA8888)) { return BC_RGB888; } else { return native_cmodel; } } int FilePNG::get_best_colormodel(Asset *asset, int driver) { if(asset->png_use_alpha) return BC_RGBA8888; else return BC_RGB888; } int FilePNG::read_frame_header(char *path) { int result = 0; int color_type; int color_depth; int num_trans = 0; FILE *stream; if(!(stream = fopen(path, "rb"))) { eprintf("Error while opening \"%s\" for reading. \n%m\n", asset->path); return 1; } png_structp png_ptr; png_infop info_ptr; png_infop end_info = 0; png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); info_ptr = png_create_info_struct(png_ptr); png_init_io(png_ptr, stream); png_read_info(png_ptr, info_ptr); asset->width = png_get_image_width(png_ptr, info_ptr); asset->height = png_get_image_height(png_ptr, info_ptr); asset->interlace_mode = BC_ILACE_MODE_NOTINTERLACED; color_type = png_get_color_type(png_ptr, info_ptr); color_depth = png_get_bit_depth(png_ptr,info_ptr); png_get_tRNS(png_ptr, info_ptr, NULL, &num_trans, NULL); if (color_depth == 16) { if (color_type & PNG_COLOR_MASK_ALPHA) { native_cmodel = BC_RGBA16161616; } else { native_cmodel = BC_RGB161616; } } else if ((color_type & PNG_COLOR_MASK_ALPHA) || (num_trans > 0)) { native_cmodel = BC_RGBA8888; } else { native_cmodel = BC_RGB888; } png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); fclose(stream); return result; } static void read_function(png_structp png_ptr, png_bytep data, png_uint_32 length) { VFrame *input = (VFrame*)png_get_io_ptr(png_ptr); memcpy(data, input->get_data() + input->get_compressed_size(), length); input->set_compressed_size(input->get_compressed_size() + length); } static void write_function(png_structp png_ptr, png_bytep data, png_uint_32 length) { VFrame *output = (VFrame*)png_get_io_ptr(png_ptr); long total_length = output->get_compressed_size() + length; if(output->get_compressed_allocated() < total_length) { long new_length = 2 * (output->get_compressed_allocated() + length); output->allocate_compressed_data(new_length); } memcpy(output->get_data() + output->get_compressed_size(), data, length); output->set_compressed_size(total_length); } static void flush_function(png_structp png_ptr) { } int FilePNG::write_frame(VFrame *frame, VFrame *data, FrameWriterUnit *unit) { PNGUnit *png_unit = (PNGUnit*)unit; png_structp png_ptr = 0; png_infop info_ptr = 0; VFrame *output_frame; int result = 1; data->set_compressed_size(0); //printf("FilePNG::write_frame 1\n"); native_cmodel = asset->png_use_alpha ? BC_RGBA8888 : BC_RGB888; if(frame->get_color_model() != native_cmodel) { if(!png_unit->temp_frame) png_unit->temp_frame = new VFrame(0, -1, asset->width, asset->height, native_cmodel, -1); png_unit->temp_frame->transfer_from(frame); output_frame = png_unit->temp_frame; } else output_frame = frame; //printf("FilePNG::write_frame 1\n"); png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); if( png_ptr && !setjmp(png_jmpbuf(png_ptr)) ) { info_ptr = png_create_info_struct(png_ptr); png_set_write_fn(png_ptr, data, (png_rw_ptr)write_function, (png_flush_ptr)flush_function); png_set_compression_level(png_ptr, 5); png_set_IHDR(png_ptr, info_ptr, asset->width, asset->height, 8, asset->png_use_alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_write_info(png_ptr, info_ptr); png_write_image(png_ptr, output_frame->get_rows()); png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); result = 0; } else { char error[256]; png_error(png_ptr, error); fprintf(stderr, "FilePNG::write_frame failed: %s\n", error); } png_destroy_write_struct(&png_ptr, &info_ptr); //printf("FilePNG::write_frame 3 %d\n", data->get_compressed_size()); return result; } int FilePNG::read_frame(VFrame *output, VFrame *input) { png_structp png_ptr; png_infop info_ptr; png_infop end_info = 0; int result = 0; int color_type; int color_depth; int colormodel; int size = input->get_compressed_size(); input->set_compressed_size(0); //printf("FilePNG::read_frame 1 %d %d\n", native_cmodel, output->get_color_model()); png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); info_ptr = png_create_info_struct(png_ptr); png_set_read_fn(png_ptr, input, (png_rw_ptr)read_function); png_read_info(png_ptr, info_ptr); int png_color_type = png_get_color_type(png_ptr, info_ptr); if (png_color_type == PNG_COLOR_TYPE_GRAY || png_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); } colormodel = output->get_color_model(); color_type = png_get_color_type(png_ptr, info_ptr); color_depth = png_get_bit_depth(png_ptr,info_ptr); if (((native_cmodel == BC_RGBA16161616)||(native_cmodel == BC_RGB161616)) && ((colormodel == BC_RGBA8888)||(colormodel == BC_RGB888))) { png_set_strip_16(png_ptr); } /* If we're dropping the alpha channel, use the background color of the image otherwise, use black */ if (((native_cmodel == BC_RGBA16161616)||(native_cmodel == BC_RGBA8888)) && ((colormodel == BC_RGB161616)||(colormodel == BC_RGB888))) { png_color_16 my_background; png_color_16p image_background; memset(&my_background,0,sizeof(png_color_16)); if (png_get_bKGD(png_ptr, info_ptr, &image_background)) { png_set_background(png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); } else { png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0); } } /* Little endian */ if ((color_depth == 16) &&((colormodel == BC_RGBA16161616)||(colormodel == BC_RGB161616))) { png_set_swap(png_ptr); } if (!(color_type & PNG_COLOR_MASK_COLOR)) { png_set_gray_to_rgb(png_ptr); } if (color_type & PNG_COLOR_MASK_PALETTE) { png_set_palette_to_rgb(png_ptr); } if (color_depth <= 8) { png_set_expand(png_ptr); } /* read the image */ png_read_image(png_ptr, output->get_rows()); //printf("FilePNG::read_frame 3\n"); png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); input->set_compressed_size(size); //printf("FilePNG::read_frame 4\n"); return result; } FrameWriterUnit* FilePNG::new_writer_unit(FrameWriter *writer) { return new PNGUnit(this, writer); } PNGUnit::PNGUnit(FilePNG *file, FrameWriter *writer) : FrameWriterUnit(writer) { this->file = file; temp_frame = 0; } PNGUnit::~PNGUnit() { if(temp_frame) delete temp_frame; } PNGConfigVideo::PNGConfigVideo(BC_WindowBase *parent_window, Asset *asset) : BC_Window(PROGRAM_NAME ": Video Compression", parent_window->get_abs_cursor_x(1), parent_window->get_abs_cursor_y(1), 200, 100) { this->parent_window = parent_window; this->asset = asset; } PNGConfigVideo::~PNGConfigVideo() { } void PNGConfigVideo::create_objects() { lock_window("PNGConfigVideo::create_objects()"); int x = 10, y = 10; add_subwindow(new PNGUseAlpha(this, x, y)); add_subwindow(new BC_OKButton(this)); show_window(1); unlock_window(); } int PNGConfigVideo::close_event() { set_done(0); return 1; } PNGUseAlpha::PNGUseAlpha(PNGConfigVideo *gui, int x, int y) : BC_CheckBox(x, y, gui->asset->png_use_alpha, _("Use alpha")) { this->gui = gui; } int PNGUseAlpha::handle_event() { gui->asset->png_use_alpha = get_value(); return 1; }