4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5 * Copyright (C) 2003-2016 Cinelerra CV contributors
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.
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.
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
26 #include "bcsignals.h"
30 #include "filesystem.h"
31 #include "interlacemodes.h"
33 #include "mwindow.inc"
36 #include "ImfChannelList.h"
37 #include "ImfChromaticities.h"
38 #include "ImfCompression.h"
40 #include "ImfInputFile.h"
41 #include "ImfOutputFile.h"
42 #include "ImfPixelType.h"
43 #include "ImfRgbaFile.h"
44 #include "ImfRgbaYca.h"
45 #include "ImfVersion.h"
47 class EXRIStream : public Imf::IStream
50 EXRIStream(char *data, int size);
53 bool read (char c[], int n);
55 void seekg (uint64_t pos);
64 class EXROStream : public Imf::OStream
67 EXROStream(VFrame *data);
70 virtual void write(const char c[], int n);
71 virtual uint64_t tellp();
72 virtual void seekp(uint64_t pos);
81 EXRIStream::EXRIStream(char *data, int size)
82 : Imf::IStream("mypath")
89 EXRIStream::~EXRIStream()
93 bool EXRIStream::read(char c[], int n)
96 if(position + fragment > size)
98 fragment = size - position;
100 memcpy(c, data + position, fragment);
101 position += fragment;
105 throw Iex::InputExc ("EXRIStream::read: Unexpected end of file.");
107 return position >= size;
110 uint64_t EXRIStream::tellg ()
115 void EXRIStream::seekg(uint64_t pos)
120 void EXRIStream::clear()
125 EXROStream::EXROStream(VFrame *data)
126 : Imf::OStream("mypath")
131 EXROStream::~EXROStream()
135 void EXROStream::write(const char c[], int n)
137 if(position + n > data->get_compressed_allocated())
138 data->allocate_compressed_data(MAX(position + n, data->get_compressed_allocated() * 2));
140 memcpy(data->get_data() + position, c, n);
142 data->set_compressed_size(MAX(position, data->get_compressed_size()));
145 uint64_t EXROStream::tellp()
150 void EXROStream::seekp(uint64_t pos)
156 FileEXR::FileEXR(Asset *asset, File *file)
157 : FileList(asset, file, "EXRLIST", ".exr", FILE_EXR, FILE_EXR_LIST)
159 native_cmodel = BC_RGB_FLOAT;
173 const char* FileEXR::compression_to_str(int compression)
175 switch( compression ) {
177 case PIZ: return "PIZ";
178 case ZIP: return "ZIP";
179 case ZIPS: return "ZIPS";
180 case RLE: return "RLE";
181 case PXR24: return "PXR24";
182 case B44: return "B44";
183 case B44A: return "B44A";
184 case DWAB: return "DWAB";
185 case DWAA: return "DWAA";
190 int FileEXR::compression_to_exr(int compression)
192 switch( compression ) {
193 case NONE: return Imf::NO_COMPRESSION;
194 case PIZ: return Imf::PIZ_COMPRESSION;
195 case ZIP: return Imf::ZIP_COMPRESSION;
196 case ZIPS: return Imf::ZIPS_COMPRESSION;
197 case RLE: return Imf::RLE_COMPRESSION;
198 case PXR24: return Imf::PXR24_COMPRESSION;
199 case B44: return Imf::B44_COMPRESSION;
200 case B44A: return Imf::B44A_COMPRESSION;
201 case DWAA: return Imf::DWAA_COMPRESSION;
202 case DWAB: return Imf::DWAB_COMPRESSION;
204 return Imf::NO_COMPRESSION;
207 int FileEXR::str_to_compression(char *string)
209 if( !strcmp(compression_to_str(NONE), string) ) return NONE;
210 if( !strcmp(compression_to_str(PIZ), string) ) return PIZ;
211 if( !strcmp(compression_to_str(ZIP), string) ) return ZIP;
212 if( !strcmp(compression_to_str(ZIPS), string) ) return ZIPS;
213 if( !strcmp(compression_to_str(RLE), string) ) return RLE;
214 if( !strcmp(compression_to_str(RLE), string) ) return B44;
215 if( !strcmp(compression_to_str(RLE), string) ) return B44A;
216 if( !strcmp(compression_to_str(RLE), string) ) return DWAA;
217 if( !strcmp(compression_to_str(RLE), string) ) return DWAB;
218 if( !strcmp(compression_to_str(PXR24),string) ) return PXR24;
222 int FileEXR::check_sig(Asset *asset, char *test)
224 if(Imf::isImfMagic(test)) return 1;
225 if(test[0] == 'E' && test[1] == 'X' && test[2] == 'R' &&
226 test[3] == 'L' && test[4] == 'I' && test[5] == 'S' && test[6] == 'T')
234 void FileEXR::get_parameters(BC_WindowBase *parent_window,
235 Asset *asset, BC_WindowBase* &format_window,
236 int audio_options, int video_options, EDL *edl)
238 if( video_options ) {
239 EXRConfigVideo *window = new EXRConfigVideo(parent_window, asset);
240 format_window = window;
241 window->create_objects();
242 window->run_window();
247 int FileEXR::colormodel_supported(int colormodel)
249 return native_cmodel;
252 int FileEXR::get_best_colormodel(Asset *asset, int driver)
254 return asset->exr_use_alpha ? BC_RGBA_FLOAT : BC_RGB_FLOAT;
257 int64_t FileEXR::get_memory_usage()
259 int64_t result = FileList::get_memory_usage();
260 if(temp_y) result += (int64_t)asset->width * asset->height * 3 / 2;
265 int FileEXR::read_frame_header(char *path)
269 // This may have been used by VFS
272 // if(!(stream = fopen(path, "rb")))
274 // perror("FileEXR::read_frame_header");
277 // int size = FileSystem::get_size(path);
278 // char *buffer = new char[size];
279 // fread(buffer, size, 1, stream);
282 // EXRIStream exr_stream(buffer, size);
283 // Imf::InputFile file(exr_stream);
285 if( access(path, R_OK) ) return 1;
286 Imf::InputFile file(path);
288 Imath::Box2i dw = file.header().dataWindow();
290 asset->width = dw.max.x - dw.min.x + 1;
291 asset->height = dw.max.y - dw.min.y + 1;
292 asset->interlace_mode = ILACE_MODE_NOTINTERLACED;
294 const Imf::ChannelList &channels = file.header().channels();
296 if(channels.findChannel("A"))
297 native_cmodel = BC_RGBA_FLOAT;
299 native_cmodel = BC_RGB_FLOAT;
300 asset->exr_use_alpha = BC_CModels::has_alpha(native_cmodel) ? 1 : 0;
302 if(channels.findChannel("Y"))
304 // for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
306 // printf("%s\n", i.name());
313 int FileEXR::read_frame(VFrame *frame, VFrame *data)
318 Imf::setGlobalThreadCount(file->cpus);
319 EXRIStream exr_stream((char*)data->get_data(), data->get_compressed_size());
320 Imf::InputFile file(exr_stream);
321 Imath::Box2i dw = file.header().dataWindow();
322 int dx = dw.min.x, dy = dw.min.y;
323 Imf::FrameBuffer framebuffer;
324 float **rows = (float**)frame->get_rows();
325 int components = BC_CModels::components(frame->get_color_model());
328 if( !temp_y ) temp_y = new float[asset->width * asset->height];
329 if( !temp_u ) temp_u = new float[asset->width * asset->height / 4];
330 if( !temp_v ) temp_v = new float[asset->width * asset->height / 4];
331 framebuffer.insert("Y", Imf::Slice(Imf::FLOAT,
332 (char*)(temp_y - dy * asset->width - dx),
334 sizeof(float) * frame->get_w()));
335 framebuffer.insert("BY", Imf::Slice(Imf::FLOAT,
336 (char*)(temp_u - dy * asset->width / 4 - dx / 2),
338 sizeof(float) * frame->get_w() / 2,
341 framebuffer.insert("RY", Imf::Slice(Imf::FLOAT,
342 (char*)(temp_v - dy * asset->width / 4 - dx / 2),
344 sizeof(float) * frame->get_w() / 2,
349 framebuffer.insert("R", Imf::Slice(Imf::FLOAT,
350 (char*)(&rows[-dy][-dx * components]),
351 sizeof(float) * components,
352 sizeof(float) * components * frame->get_w()));
353 framebuffer.insert("G", Imf::Slice(Imf::FLOAT,
354 (char*)(&rows[-dy][-dx * components + 1]),
355 sizeof(float) * components,
356 sizeof(float) * components * frame->get_w()));
357 framebuffer.insert("B", Imf::Slice(Imf::FLOAT,
358 (char*)(&rows[-dy][-dx * components + 2]),
359 sizeof(float) * components,
360 sizeof(float) * components * frame->get_w()));
363 // Alpha always goes directly to the output frame
364 if( components == 4 ) {
365 framebuffer.insert("A", Imf::Slice(Imf::FLOAT,
366 (char*)(&rows[-dy][-dx * components + 3]),
367 sizeof(float) * components,
368 sizeof(float) * components * frame->get_w()));
371 file.setFrameBuffer(framebuffer);
372 file.readPixels (dw.min.y, dw.max.y);
375 // Convert to RGB using crazy ILM equations
377 Imf::Chromaticities cr;
378 yw = Imf::RgbaYca::computeYw(cr);
380 for( int i=0; i<asset->height-1; i+=2 ) {
381 float *y_row1 = temp_y + i * asset->width;
382 float *y_row2 = temp_y + (i + 1) * asset->width;
383 float *u_row = temp_u + (i * asset->width / 4);
384 float *v_row = temp_v + (i * asset->width / 4);
385 float *out_row1 = rows[i];
386 float *out_row2 = rows[i + 1];
387 for( int j=0; j<asset->width-1; j+=2 ) {
396 g = (y - r * yw.x - b * yw.z) / yw.y;
400 if(components == 4) out_row1++;
405 g = (y - r * yw.x - b * yw.z) / yw.y;
409 if(components == 4) out_row1++;
414 g = (y - r * yw.x - b * yw.z) / yw.y;
418 if(components == 4) out_row1++;
423 g = (y - r * yw.x - b * yw.z) / yw.y;
427 if(components == 4) out_row1++;
432 } catch (const std::exception &e) {
433 std::cerr << "error reading EXR image file:" << e.what() << std::endl;
439 int FileEXR::write_frame(VFrame *frame, VFrame *data, FrameWriterUnit *unit)
441 EXRUnit *exr_unit = (EXRUnit*)unit;
443 VFrame *output_frame;
444 data->set_compressed_size(0);
445 Imf::setGlobalThreadCount(file->cpus);
447 int native_cmodel = asset->exr_use_alpha ? BC_RGBA_FLOAT : BC_RGB_FLOAT;
448 int components = BC_CModels::components(native_cmodel);
450 if( frame->get_color_model() != native_cmodel ) {
451 if( !exr_unit->temp_frame ) exr_unit->temp_frame =
452 new VFrame(asset->width, asset->height, native_cmodel, 0);
453 BC_CModels::transfer(exr_unit->temp_frame->get_rows(), /* Leave NULL if non existent */
455 exr_unit->temp_frame->get_y(), /* Leave NULL if non existent */
456 exr_unit->temp_frame->get_u(),
457 exr_unit->temp_frame->get_v(),
458 frame->get_y(), /* Leave NULL if non existent */
461 0, /* Dimensions to capture from input frame */
465 0, /* Dimensions to project on output frame */
469 frame->get_color_model(),
471 0, /* When transfering BC_RGBA8888 to non-alpha this is the background color in 0xRRGGBB hex */
472 asset->width, /* For planar use the luma rowspan */
474 output_frame = exr_unit->temp_frame;
477 output_frame = frame;
479 Imf::Header header(output_frame->get_w(), output_frame->get_h());
480 header.compression() = (Imf::Compression)compression_to_exr(
481 asset->exr_compression);
482 header.channels().insert("R", Imf::Channel(Imf::FLOAT));
483 header.channels().insert("G", Imf::Channel(Imf::FLOAT));
484 header.channels().insert("B", Imf::Channel(Imf::FLOAT));
485 if(asset->exr_use_alpha) header.channels().insert("A", Imf::Channel(Imf::FLOAT));
487 EXROStream exr_stream(data);
488 Imf::OutputFile file(exr_stream, header);
489 Imf::FrameBuffer framebuffer;
490 float **rows = (float**)output_frame->get_rows();
491 framebuffer.insert("R",
492 Imf::Slice(Imf::FLOAT,
494 sizeof(float) * components,
495 sizeof(float) * components * output_frame->get_w()));
496 framebuffer.insert("G",
497 Imf::Slice(Imf::FLOAT,
498 (char*)(rows[0] + 1),
499 sizeof(float) * components,
500 sizeof(float) * components * output_frame->get_w()));
501 framebuffer.insert("B",
502 Imf::Slice(Imf::FLOAT,
503 (char*)(rows[0] + 2),
504 sizeof(float) * components,
505 sizeof(float) * components * output_frame->get_w()));
506 if(asset->exr_use_alpha)
507 framebuffer.insert("A",
508 Imf::Slice(Imf::FLOAT,
509 (char*)(rows[0] + 3),
510 sizeof(float) * components,
511 sizeof(float) * components * output_frame->get_w()));
512 file.setFrameBuffer(framebuffer);
513 file.writePixels(asset->height);
517 int FileEXR::can_copy_from(Asset *asset, int64_t position)
519 if(asset->format == FILE_EXR ||
520 asset->format == FILE_EXR_LIST)
526 FrameWriterUnit* FileEXR::new_writer_unit(FrameWriter *writer)
528 return new EXRUnit(this, writer);
532 EXRUnit::EXRUnit(FileEXR *file, FrameWriter *writer)
533 : FrameWriterUnit(writer)
541 if(temp_frame) delete temp_frame;
545 EXRConfigVideo::EXRConfigVideo(BC_WindowBase *parent_window, Asset *asset)
546 : BC_Window(_(PROGRAM_NAME ": Video Compression"),
547 parent_window->get_abs_cursor_x(1), parent_window->get_abs_cursor_y(1),
548 xS(300), BC_OKButton::calculate_h() + yS(100))
550 this->parent_window = parent_window;
552 // *** CONTEXT_HELP ***
553 context_help_set_keyword("Single File Rendering");
556 EXRConfigVideo::~EXRConfigVideo()
560 void EXRConfigVideo::create_objects()
562 lock_window("EXRConfigVideo::create_objects");
563 int x = xS(10), y = yS(10);
564 add_subwindow(new EXRUseAlpha(this, x, y));
566 EXRCompression *menu;
567 add_subwindow(new BC_Title(x, y, _("Compression:")));
569 add_subwindow(menu = new EXRCompression(this, x, y, xS(100)));
570 menu->create_objects();
571 add_subwindow(new BC_OKButton(this));
576 int EXRConfigVideo::close_event()
583 EXRUseAlpha::EXRUseAlpha(EXRConfigVideo *gui, int x, int y)
584 : BC_CheckBox(x, y, gui->asset->exr_use_alpha, _("Use alpha"))
589 int EXRUseAlpha::handle_event()
591 gui->asset->exr_use_alpha = get_value();
597 EXRCompression::EXRCompression(EXRConfigVideo *gui, int x, int y, int w)
598 : BC_PopupMenu(x, y, w,
599 FileEXR::compression_to_str(gui->asset->exr_compression))
603 void EXRCompression::create_objects()
605 add_item(new EXRCompressionItem(gui, FileEXR::NONE));
606 add_item(new EXRCompressionItem(gui, FileEXR::PIZ));
607 add_item(new EXRCompressionItem(gui, FileEXR::ZIP));
608 add_item(new EXRCompressionItem(gui, FileEXR::ZIPS));
609 add_item(new EXRCompressionItem(gui, FileEXR::RLE));
610 add_item(new EXRCompressionItem(gui, FileEXR::PXR24));
611 add_item(new EXRCompressionItem(gui, FileEXR::B44));
612 add_item(new EXRCompressionItem(gui, FileEXR::B44A));
613 add_item(new EXRCompressionItem(gui, FileEXR::DWAA));
614 add_item(new EXRCompressionItem(gui, FileEXR::DWAB));
617 int EXRCompression::handle_event()
622 EXRCompressionItem::EXRCompressionItem(EXRConfigVideo *gui, int value)
623 : BC_MenuItem(FileEXR::compression_to_str(value))
629 int EXRCompressionItem::handle_event()
631 gui->asset->exr_compression = value;