4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
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.
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.
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
25 #include "bcsignals.h"
28 #include "filesystem.h"
29 #include "interlacemodes.h"
31 #include "mwindow.inc"
34 #include "ImfChannelList.h"
35 #include "ImfChromaticities.h"
36 #include "ImfCompression.h"
38 #include "ImfInputFile.h"
39 #include "ImfOutputFile.h"
40 #include "ImfPixelType.h"
41 #include "ImfRgbaFile.h"
42 #include "ImfRgbaYca.h"
43 #include "ImfVersion.h"
45 class EXRIStream : public Imf::IStream
48 EXRIStream(char *data, int size);
51 bool read (char c[], int n);
53 void seekg (Imf::Int64 pos);
62 class EXROStream : public Imf::OStream
65 EXROStream(VFrame *data);
68 virtual void write(const char c[], int n);
69 virtual Imf::Int64 tellp();
70 virtual void seekp(Imf::Int64 pos);
79 EXRIStream::EXRIStream(char *data, int size)
80 : Imf::IStream("mypath")
87 EXRIStream::~EXRIStream()
91 bool EXRIStream::read(char c[], int n)
94 if(position + fragment > size)
96 fragment = size - position;
98 memcpy(c, data + position, fragment);
103 throw Iex::InputExc ("EXRIStream::read: Unexpected end of file.");
105 return position >= size;
108 Imf::Int64 EXRIStream::tellg ()
113 void EXRIStream::seekg(Imf::Int64 pos)
118 void EXRIStream::clear()
132 EXROStream::EXROStream(VFrame *data)
133 : Imf::OStream("mypath")
138 EXROStream::~EXROStream()
142 void EXROStream::write(const char c[], int n)
144 if(position + n > data->get_compressed_allocated())
145 data->allocate_compressed_data(MAX(position + n, data->get_compressed_allocated() * 2));
147 memcpy(data->get_data() + position, c, n);
149 data->set_compressed_size(MAX(position, data->get_compressed_size()));
152 Imf::Int64 EXROStream::tellp()
157 void EXROStream::seekp(Imf::Int64 pos)
172 FileEXR::FileEXR(Asset *asset, File *file)
173 : FileList(asset, file, "EXRLIST", ".exr", FILE_EXR, FILE_EXR_LIST)
175 native_cmodel = BC_RGB_FLOAT;
184 if(temp_y) delete [] temp_y;
185 if(temp_u) delete [] temp_u;
186 if(temp_v) delete [] temp_v;
189 const char* FileEXR::compression_to_str(int compression)
193 case FileEXR::NONE: return "None"; break;
194 case FileEXR::PIZ: return "PIZ"; break;
195 case FileEXR::ZIP: return "ZIP"; break;
196 case FileEXR::ZIPS: return "ZIPS"; break;
197 case FileEXR::RLE: return "RLE"; break;
198 case FileEXR::PXR24: return "PXR24"; break;
203 int FileEXR::compression_to_exr(int compression)
207 case FileEXR::NONE: return (int)Imf::NO_COMPRESSION; break;
208 case FileEXR::PIZ: return (int)Imf::PIZ_COMPRESSION; break;
209 case FileEXR::ZIP: return (int)Imf::ZIP_COMPRESSION; break;
210 case FileEXR::ZIPS: return (int)Imf::ZIPS_COMPRESSION; break;
211 case FileEXR::RLE: return (int)Imf::RLE_COMPRESSION; break;
212 case FileEXR::PXR24: return (int)Imf::PXR24_COMPRESSION; break;
214 return Imf::NO_COMPRESSION;
217 int FileEXR::str_to_compression(char *string)
219 if(!strcmp(compression_to_str(FileEXR::NONE), string))
220 return FileEXR::NONE;
221 if(!strcmp(compression_to_str(FileEXR::PIZ), string))
223 if(!strcmp(compression_to_str(FileEXR::ZIP), string))
225 if(!strcmp(compression_to_str(FileEXR::ZIPS), string))
226 return FileEXR::ZIPS;
227 if(!strcmp(compression_to_str(FileEXR::RLE), string))
229 if(!strcmp(compression_to_str(FileEXR::PXR24), string))
231 return FileEXR::NONE;
234 int FileEXR::check_sig(Asset *asset, char *test)
236 if(Imf::isImfMagic(test)) return 1;
237 if(test[0] == 'E' && test[1] == 'X' && test[2] == 'R' &&
238 test[3] == 'L' && test[4] == 'I' && test[5] == 'S' && test[6] == 'T')
246 void FileEXR::get_parameters(BC_WindowBase *parent_window,
247 Asset *asset, BC_WindowBase* &format_window,
248 int audio_options, int video_options, EDL *edl)
252 EXRConfigVideo *window = new EXRConfigVideo(parent_window, asset);
253 format_window = window;
254 window->create_objects();
255 window->run_window();
260 int FileEXR::colormodel_supported(int colormodel)
262 return native_cmodel;
265 int FileEXR::get_best_colormodel(Asset *asset, int driver)
267 if(asset->exr_use_alpha)
268 return BC_RGBA_FLOAT;
273 int64_t FileEXR::get_memory_usage()
275 int64_t result = FileList::get_memory_usage();
276 if(temp_y) result += (int64_t)asset->width * asset->height * 3 / 2;
281 int FileEXR::read_frame_header(char *path)
285 // This may have been used by VFS
288 // if(!(stream = fopen(path, "rb")))
290 // perror("FileEXR::read_frame_header");
293 // int size = FileSystem::get_size(path);
294 // char *buffer = new char[size];
295 // fread(buffer, size, 1, stream);
298 // EXRIStream exr_stream(buffer, size);
299 // Imf::InputFile file(exr_stream);
302 Imf::InputFile file(path);
304 Imath::Box2i dw = file.header().dataWindow();
306 asset->width = dw.max.x - dw.min.x + 1;
307 asset->height = dw.max.y - dw.min.y + 1;
308 asset->interlace_mode = ILACE_MODE_NOTINTERLACED;
310 const Imf::ChannelList &channels = file.header().channels();
312 if(channels.findChannel("A"))
313 native_cmodel = BC_RGBA_FLOAT;
315 native_cmodel = BC_RGB_FLOAT;
316 asset->exr_use_alpha = BC_CModels::has_alpha(native_cmodel) ? 1 : 0;
318 if(channels.findChannel("Y"))
320 // for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
322 // printf("%s\n", i.name());
329 int FileEXR::read_frame(VFrame *frame, VFrame *data)
331 EXRIStream exr_stream((char*)data->get_data(), data->get_compressed_size());
332 Imf::InputFile file(exr_stream);
333 Imath::Box2i dw = file.header().dataWindow();
336 Imf::FrameBuffer framebuffer;
337 float **rows = (float**)frame->get_rows();
338 int components = BC_CModels::components(frame->get_color_model());
342 if(!temp_y) temp_y = new float[asset->width * asset->height];
343 if(!temp_u) temp_u = new float[asset->width * asset->height / 4];
344 if(!temp_v) temp_v = new float[asset->width * asset->height / 4];
345 framebuffer.insert("Y", Imf::Slice(Imf::FLOAT,
346 (char*)(temp_y - dy * asset->width - dx),
348 sizeof(float) * frame->get_w()));
349 framebuffer.insert("BY", Imf::Slice(Imf::FLOAT,
350 (char*)(temp_u - dy * asset->width / 4 - dx / 2),
352 sizeof(float) * frame->get_w() / 2,
355 framebuffer.insert("RY", Imf::Slice(Imf::FLOAT,
356 (char*)(temp_v - dy * asset->width / 4 - dx / 2),
358 sizeof(float) * frame->get_w() / 2,
364 framebuffer.insert("R", Imf::Slice(Imf::FLOAT,
365 (char*)(&rows[-dy][-dx * components]),
366 sizeof(float) * components,
367 sizeof(float) * components * frame->get_w()));
368 framebuffer.insert("G", Imf::Slice(Imf::FLOAT,
369 (char*)(&rows[-dy][-dx * components + 1]),
370 sizeof(float) * components,
371 sizeof(float) * components * frame->get_w()));
372 framebuffer.insert("B", Imf::Slice(Imf::FLOAT,
373 (char*)(&rows[-dy][-dx * components + 2]),
374 sizeof(float) * components,
375 sizeof(float) * components * frame->get_w()));
378 // Alpha always goes directly to the output frame
381 framebuffer.insert("A", Imf::Slice(Imf::FLOAT,
382 (char*)(&rows[-dy][-dx * components + 3]),
383 sizeof(float) * components,
384 sizeof(float) * components * frame->get_w()));
387 file.setFrameBuffer(framebuffer);
388 file.readPixels (dw.min.y, dw.max.y);
392 // Convert to RGB using crazy ILM equations
394 Imf::Chromaticities cr;
395 yw = Imf::RgbaYca::computeYw(cr);
397 for(int i = 0; i < asset->height - 1; i += 2)
399 float *y_row1 = temp_y + i * asset->width;
400 float *y_row2 = temp_y + (i + 1) * asset->width;
401 float *u_row = temp_u + (i * asset->width / 4);
402 float *v_row = temp_v + (i * asset->width / 4);
403 float *out_row1 = rows[i];
404 float *out_row2 = rows[i + 1];
405 for(int j = 0; j < asset->width - 1; j += 2)
415 g = (y - r * yw.x - b * yw.z) / yw.y;
419 if(components == 4) out_row1++;
424 g = (y - r * yw.x - b * yw.z) / yw.y;
428 if(components == 4) out_row1++;
433 g = (y - r * yw.x - b * yw.z) / yw.y;
437 if(components == 4) out_row1++;
442 g = (y - r * yw.x - b * yw.z) / yw.y;
446 if(components == 4) out_row1++;
456 int FileEXR::write_frame(VFrame *frame, VFrame *data, FrameWriterUnit *unit)
458 EXRUnit *exr_unit = (EXRUnit*)unit;
460 VFrame *output_frame;
461 data->set_compressed_size(0);
464 int native_cmodel = asset->exr_use_alpha ? BC_RGBA_FLOAT : BC_RGB_FLOAT;
465 int components = BC_CModels::components(native_cmodel);
467 if(frame->get_color_model() != native_cmodel)
469 if(!exr_unit->temp_frame) exr_unit->temp_frame =
470 new VFrame(asset->width, asset->height, native_cmodel, 0);
471 BC_CModels::transfer(exr_unit->temp_frame->get_rows(), /* Leave NULL if non existent */
473 exr_unit->temp_frame->get_y(), /* Leave NULL if non existent */
474 exr_unit->temp_frame->get_u(),
475 exr_unit->temp_frame->get_v(),
476 frame->get_y(), /* Leave NULL if non existent */
479 0, /* Dimensions to capture from input frame */
483 0, /* Dimensions to project on output frame */
487 frame->get_color_model(),
489 0, /* When transfering BC_RGBA8888 to non-alpha this is the background color in 0xRRGGBB hex */
490 asset->width, /* For planar use the luma rowspan */
492 output_frame = exr_unit->temp_frame;
495 output_frame = frame;
497 Imf::Header header(output_frame->get_w(), output_frame->get_h());
498 header.compression() = (Imf::Compression)compression_to_exr(
499 asset->exr_compression);
500 header.channels().insert("R", Imf::Channel(Imf::FLOAT));
501 header.channels().insert("G", Imf::Channel(Imf::FLOAT));
502 header.channels().insert("B", Imf::Channel(Imf::FLOAT));
503 if(asset->exr_use_alpha) header.channels().insert("A", Imf::Channel(Imf::FLOAT));
505 EXROStream exr_stream(data);
506 Imf::OutputFile file(exr_stream, header);
507 Imf::FrameBuffer framebuffer;
508 float **rows = (float**)output_frame->get_rows();
509 framebuffer.insert("R",
510 Imf::Slice(Imf::FLOAT,
512 sizeof(float) * components,
513 sizeof(float) * components * output_frame->get_w()));
514 framebuffer.insert("G",
515 Imf::Slice(Imf::FLOAT,
516 (char*)(rows[0] + 1),
517 sizeof(float) * components,
518 sizeof(float) * components * output_frame->get_w()));
519 framebuffer.insert("B",
520 Imf::Slice(Imf::FLOAT,
521 (char*)(rows[0] + 2),
522 sizeof(float) * components,
523 sizeof(float) * components * output_frame->get_w()));
524 if(asset->exr_use_alpha)
525 framebuffer.insert("A",
526 Imf::Slice(Imf::FLOAT,
527 (char*)(rows[0] + 3),
528 sizeof(float) * components,
529 sizeof(float) * components * output_frame->get_w()));
530 file.setFrameBuffer(framebuffer);
531 file.writePixels(asset->height);
535 FrameWriterUnit* FileEXR::new_writer_unit(FrameWriter *writer)
537 return new EXRUnit(this, writer);
551 EXRUnit::EXRUnit(FileEXR *file, FrameWriter *writer)
552 : FrameWriterUnit(writer)
560 if(temp_frame) delete temp_frame;
573 EXRConfigVideo::EXRConfigVideo(BC_WindowBase *parent_window, Asset *asset)
574 : BC_Window(_(PROGRAM_NAME ": Video Compression"),
575 parent_window->get_abs_cursor_x(1), parent_window->get_abs_cursor_y(1),
576 xS(300), BC_OKButton::calculate_h() + yS(100))
578 this->parent_window = parent_window;
582 EXRConfigVideo::~EXRConfigVideo()
586 void EXRConfigVideo::create_objects()
588 lock_window("EXRConfigVideo::create_objects");
589 int x = xS(10), y = yS(10);
590 add_subwindow(new EXRUseAlpha(this, x, y));
592 EXRCompression *menu;
593 add_subwindow(new BC_Title(x, y, _("Compression:")));
595 add_subwindow(menu = new EXRCompression(this, x, y, xS(100)));
596 menu->create_objects();
597 add_subwindow(new BC_OKButton(this));
602 int EXRConfigVideo::close_event()
609 EXRUseAlpha::EXRUseAlpha(EXRConfigVideo *gui, int x, int y)
610 : BC_CheckBox(x, y, gui->asset->exr_use_alpha, _("Use alpha"))
615 int EXRUseAlpha::handle_event()
617 gui->asset->exr_use_alpha = get_value();
623 EXRCompression::EXRCompression(EXRConfigVideo *gui, int x, int y, int w)
624 : BC_PopupMenu(x, y, w,
625 FileEXR::compression_to_str(gui->asset->exr_compression))
629 void EXRCompression::create_objects()
631 add_item(new EXRCompressionItem(gui, FileEXR::NONE));
632 add_item(new EXRCompressionItem(gui, FileEXR::PIZ));
633 add_item(new EXRCompressionItem(gui, FileEXR::ZIP));
634 add_item(new EXRCompressionItem(gui, FileEXR::ZIPS));
635 add_item(new EXRCompressionItem(gui, FileEXR::RLE));
636 add_item(new EXRCompressionItem(gui, FileEXR::PXR24));
639 int EXRCompression::handle_event()
644 EXRCompressionItem::EXRCompressionItem(EXRConfigVideo *gui, int value)
645 : BC_MenuItem(FileEXR::compression_to_str(value))
651 int EXRCompressionItem::handle_event()
653 gui->asset->exr_compression = value;