2a54f3ed7ad668a7de36e682336a3f3c0f40b4fd
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / fileexr.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 #ifdef HAVE_OPENEXR
23
24 #include "asset.h"
25 #include "bcsignals.h"
26 #include "clip.h"
27 #include "file.h"
28 #include "fileexr.h"
29 #include "filesystem.h"
30 #include "interlacemodes.h"
31
32 #include "mwindow.inc"
33 #include "vframe.h"
34
35 #include "ImfChannelList.h"
36 #include "ImfChromaticities.h"
37 #include "ImfCompression.h"
38 #include "ImfIO.h"
39 #include "ImfInputFile.h"
40 #include "ImfOutputFile.h"
41 #include "ImfPixelType.h"
42 #include "ImfRgbaFile.h"
43 #include "ImfRgbaYca.h"
44 #include "ImfVersion.h"
45
46 class EXRIStream : public Imf::IStream
47 {
48 public:
49         EXRIStream(char *data, int size);
50         ~EXRIStream();
51
52         bool read (char c[], int n);
53         Imf::Int64 tellg ();
54         void seekg (Imf::Int64 pos);
55         void clear ();
56
57 private:
58         char *data;
59         int size;
60         int position;
61 };
62
63 class EXROStream : public Imf::OStream
64 {
65 public:
66         EXROStream(VFrame *data);
67         ~EXROStream();
68
69     virtual void write(const char c[], int n);
70     virtual Imf::Int64 tellp();
71     virtual void seekp(Imf::Int64 pos);
72
73 private:
74         VFrame *data;
75         int position;
76 };
77
78
79
80 EXRIStream::EXRIStream(char *data, int size)
81  : Imf::IStream("mypath")
82 {
83         this->data = data;
84         this->size = size;
85         position = 0;
86 }
87
88 EXRIStream::~EXRIStream()
89 {
90 }
91
92 bool EXRIStream::read(char c[], int n)
93 {
94         int fragment = n;
95         if(position + fragment > size)
96         {
97                 fragment = size - position;
98         }
99         memcpy(c, data + position, fragment);
100         position += fragment;
101
102         if(n != fragment)
103         {
104                 throw Iex::InputExc ("EXRIStream::read: Unexpected end of file.");
105         }
106         return position >= size;
107 }
108
109 Imf::Int64 EXRIStream::tellg ()
110 {
111         return position;
112 }
113
114 void EXRIStream::seekg(Imf::Int64 pos)
115 {
116         position = pos;
117 }
118
119 void EXRIStream::clear()
120 {
121 }
122
123
124 EXROStream::EXROStream(VFrame *data)
125  : Imf::OStream("mypath")
126 {
127         this->data = data;
128         position = 0;
129 }
130 EXROStream::~EXROStream()
131 {
132 }
133
134 void EXROStream::write(const char c[], int n)
135 {
136         if(position + n > data->get_compressed_allocated())
137                 data->allocate_compressed_data(MAX(position + n, data->get_compressed_allocated() * 2));
138
139         memcpy(data->get_data() + position, c, n);
140         position += n;
141         data->set_compressed_size(MAX(position, data->get_compressed_size()));
142 }
143
144 Imf::Int64 EXROStream::tellp()
145 {
146         return position;
147 }
148
149 void EXROStream::seekp(Imf::Int64 pos)
150 {
151         position = pos;
152 }
153
154
155 FileEXR::FileEXR(Asset *asset, File *file)
156  : FileList(asset, file, "EXRLIST", ".exr", FILE_EXR, FILE_EXR_LIST)
157 {
158         native_cmodel = BC_RGB_FLOAT;
159         is_yuv = 0;
160         temp_y = 0;
161         temp_u = 0;
162         temp_v = 0;
163 }
164
165 FileEXR::~FileEXR()
166 {
167         delete [] temp_y;
168         delete [] temp_u;
169         delete [] temp_v;
170 }
171
172 const char* FileEXR::compression_to_str(int compression)
173 {
174         switch( compression ) {
175         case NONE:      break;
176         case PIZ:       return "PIZ";
177         case ZIP:       return "ZIP";
178         case ZIPS:      return "ZIPS";
179         case RLE:       return "RLE";
180         case PXR24:     return "PXR24";
181         case B44:       return "B44";
182         case B44A:      return "B44A";
183         case DWAB:      return "DWAB";
184         case DWAA:      return "DWAA";
185         }
186         return _("None");
187 }
188
189 int FileEXR::compression_to_exr(int compression)
190 {
191         switch( compression ) {
192         case NONE:      return Imf::NO_COMPRESSION;
193         case PIZ:       return Imf::PIZ_COMPRESSION;
194         case ZIP:       return Imf::ZIP_COMPRESSION;
195         case ZIPS:      return Imf::ZIPS_COMPRESSION;
196         case RLE:       return Imf::RLE_COMPRESSION;
197         case PXR24:     return Imf::PXR24_COMPRESSION;
198         case B44:       return Imf::B44_COMPRESSION;
199         case B44A:      return Imf::B44A_COMPRESSION;
200         case DWAA:      return Imf::DWAA_COMPRESSION;
201         case DWAB:      return Imf::DWAB_COMPRESSION;
202         }
203         return Imf::NO_COMPRESSION;
204 }
205
206 int FileEXR::str_to_compression(char *string)
207 {
208         if( !strcmp(compression_to_str(NONE), string) ) return NONE;
209         if( !strcmp(compression_to_str(PIZ), string)  ) return PIZ;
210         if( !strcmp(compression_to_str(ZIP), string)  ) return ZIP;
211         if( !strcmp(compression_to_str(ZIPS), string) ) return ZIPS;
212         if( !strcmp(compression_to_str(RLE), string)  ) return RLE;
213         if( !strcmp(compression_to_str(RLE), string)  ) return B44;
214         if( !strcmp(compression_to_str(RLE), string)  ) return B44A;
215         if( !strcmp(compression_to_str(RLE), string)  ) return DWAA;
216         if( !strcmp(compression_to_str(RLE), string)  ) return DWAB;
217         if( !strcmp(compression_to_str(PXR24),string) ) return PXR24;
218         return NONE;
219 }
220
221 int FileEXR::check_sig(Asset *asset, char *test)
222 {
223         if(Imf::isImfMagic(test)) return 1;
224         if(test[0] == 'E' && test[1] == 'X' && test[2] == 'R' &&
225                 test[3] == 'L' && test[4] == 'I' && test[5] == 'S' && test[6] == 'T')
226         {
227                 return 1;
228         }
229
230         return 0;
231 }
232
233 void FileEXR::get_parameters(BC_WindowBase *parent_window,
234         Asset *asset, BC_WindowBase* &format_window,
235         int audio_options, int video_options, EDL *edl)
236 {
237         if( video_options ) {
238                 EXRConfigVideo *window = new EXRConfigVideo(parent_window, asset);
239                 format_window = window;
240                 window->create_objects();
241                 window->run_window();
242                 delete window;
243         }
244 }
245
246 int FileEXR::colormodel_supported(int colormodel)
247 {
248         return native_cmodel;
249 }
250
251 int FileEXR::get_best_colormodel(Asset *asset, int driver)
252 {
253         return asset->exr_use_alpha ? BC_RGBA_FLOAT : BC_RGB_FLOAT;
254 }
255
256 int64_t FileEXR::get_memory_usage()
257 {
258         int64_t result = FileList::get_memory_usage();
259         if(temp_y) result += (int64_t)asset->width * asset->height * 3 / 2;
260         return result;
261 }
262
263
264 int FileEXR::read_frame_header(char *path)
265 {
266         int result = 0;
267
268 // This may have been used by VFS
269 //      FILE *stream;
270 //
271 //      if(!(stream = fopen(path, "rb")))
272 //      {
273 //              perror("FileEXR::read_frame_header");
274 //              return 1;
275 //      }
276 //      int size = FileSystem::get_size(path);
277 //      char *buffer = new char[size];
278 //      fread(buffer, size, 1, stream);
279 //      fclose(stream);
280 //
281 //      EXRIStream exr_stream(buffer, size);
282 //      Imf::InputFile file(exr_stream);
283
284         if( access(path, R_OK) ) return 1;
285         Imf::InputFile file(path);
286
287         Imath::Box2i dw = file.header().dataWindow();
288
289         asset->width = dw.max.x - dw.min.x + 1;
290         asset->height = dw.max.y - dw.min.y + 1;
291         asset->interlace_mode = ILACE_MODE_NOTINTERLACED;
292
293         const Imf::ChannelList &channels = file.header().channels();
294
295         if(channels.findChannel("A"))
296                 native_cmodel = BC_RGBA_FLOAT;
297         else
298                 native_cmodel = BC_RGB_FLOAT;
299         asset->exr_use_alpha = BC_CModels::has_alpha(native_cmodel) ? 1 : 0;
300
301         if(channels.findChannel("Y"))
302                 is_yuv = 1;
303 // for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
304 // {
305 // printf("%s\n", i.name());
306 // }
307
308 //      delete [] buffer;
309         return result;
310 }
311
312 int FileEXR::read_frame(VFrame *frame, VFrame *data)
313 {
314         Imf::setGlobalThreadCount(file->cpus);
315         EXRIStream exr_stream((char*)data->get_data(), data->get_compressed_size());
316         Imf::InputFile file(exr_stream);
317         Imath::Box2i dw = file.header().dataWindow();
318         int dx = dw.min.x, dy = dw.min.y;
319         Imf::FrameBuffer framebuffer;
320         float **rows = (float**)frame->get_rows();
321         int components = BC_CModels::components(frame->get_color_model());
322
323         if( is_yuv ) {
324                 if( !temp_y ) temp_y = new float[asset->width * asset->height];
325                 if( !temp_u ) temp_u = new float[asset->width * asset->height / 4];
326                 if( !temp_v ) temp_v = new float[asset->width * asset->height / 4];
327                 framebuffer.insert("Y", Imf::Slice(Imf::FLOAT,
328                         (char*)(temp_y - dy * asset->width - dx),
329                         sizeof(float),
330                         sizeof(float) * frame->get_w()));
331                 framebuffer.insert("BY", Imf::Slice(Imf::FLOAT,
332                         (char*)(temp_u - dy * asset->width / 4 - dx / 2),
333                         sizeof(float),
334                         sizeof(float) * frame->get_w() / 2,
335                         2,
336                         2));
337                 framebuffer.insert("RY", Imf::Slice(Imf::FLOAT,
338                         (char*)(temp_v - dy * asset->width / 4 - dx / 2),
339                         sizeof(float),
340                         sizeof(float) * frame->get_w() / 2,
341                         2,
342                         2));
343         }
344         else {
345                 framebuffer.insert("R", Imf::Slice(Imf::FLOAT,
346                         (char*)(&rows[-dy][-dx * components]),
347                         sizeof(float) * components,
348                         sizeof(float) * components * frame->get_w()));
349                 framebuffer.insert("G", Imf::Slice(Imf::FLOAT,
350                         (char*)(&rows[-dy][-dx * components + 1]),
351                         sizeof(float) * components,
352                         sizeof(float) * components * frame->get_w()));
353                 framebuffer.insert("B", Imf::Slice(Imf::FLOAT,
354                         (char*)(&rows[-dy][-dx * components + 2]),
355                         sizeof(float) * components,
356                         sizeof(float) * components * frame->get_w()));
357         }
358
359 // Alpha always goes directly to the output frame
360         if( components == 4 ) {
361                 framebuffer.insert("A", Imf::Slice(Imf::FLOAT,
362                         (char*)(&rows[-dy][-dx * components + 3]),
363                         sizeof(float) * components,
364                         sizeof(float) * components * frame->get_w()));
365         }
366
367         file.setFrameBuffer(framebuffer);
368         file.readPixels (dw.min.y, dw.max.y);
369
370         if( is_yuv ) {
371 // Convert to RGB using crazy ILM equations
372                 Imath::V3f yw;
373                 Imf::Chromaticities cr;
374                 yw = Imf::RgbaYca::computeYw(cr);
375
376                 for( int i=0; i<asset->height-1; i+=2 ) {
377                         float *y_row1 = temp_y + i * asset->width;
378                         float *y_row2 = temp_y + (i + 1) * asset->width;
379                         float *u_row = temp_u + (i * asset->width / 4);
380                         float *v_row = temp_v + (i * asset->width / 4);
381                         float *out_row1 = rows[i];
382                         float *out_row2 = rows[i + 1];
383                         for( int j=0; j<asset->width-1; j+=2 ) {
384                                 float v = *u_row++;
385                                 float u = *v_row++;
386                                 float y;
387
388                                 float r, g, b;
389                                 y = *y_row1++;
390                                 r = (u + 1) * y;
391                                 b = (v + 1) * y;
392                                 g = (y - r * yw.x - b * yw.z) / yw.y;
393                                 *out_row1++ = r;
394                                 *out_row1++ = g;
395                                 *out_row1++ = b;
396                                 if(components == 4) out_row1++;
397
398                                 y = *y_row1++;
399                                 r = (u + 1) * y;
400                                 b = (v + 1) * y;
401                                 g = (y - r * yw.x - b * yw.z) / yw.y;
402                                 *out_row1++ = r;
403                                 *out_row1++ = g;
404                                 *out_row1++ = b;
405                                 if(components == 4) out_row1++;
406
407                                 y = *y_row2++;
408                                 r = (u + 1) * y;
409                                 b = (v + 1) * y;
410                                 g = (y - r * yw.x - b * yw.z) / yw.y;
411                                 *out_row2++ = r;
412                                 *out_row2++ = g;
413                                 *out_row2++ = b;
414                                 if(components == 4) out_row1++;
415
416                                 y = *y_row2++;
417                                 r = (u + 1) * y;
418                                 b = (v + 1) * y;
419                                 g = (y - r * yw.x - b * yw.z) / yw.y;
420                                 *out_row2++ = r;
421                                 *out_row2++ = g;
422                                 *out_row2++ = b;
423                                 if(components == 4) out_row1++;
424                         }
425                 }
426         }
427         return 0;
428 }
429
430
431 int FileEXR::write_frame(VFrame *frame, VFrame *data, FrameWriterUnit *unit)
432 {
433         EXRUnit *exr_unit = (EXRUnit*)unit;
434
435         VFrame *output_frame;
436         data->set_compressed_size(0);
437         Imf::setGlobalThreadCount(file->cpus);
438
439         int native_cmodel = asset->exr_use_alpha ? BC_RGBA_FLOAT : BC_RGB_FLOAT;
440         int components = BC_CModels::components(native_cmodel);
441
442         if( frame->get_color_model() != native_cmodel ) {
443                 if( !exr_unit->temp_frame ) exr_unit->temp_frame =
444                         new VFrame(asset->width, asset->height, native_cmodel, 0);
445                 BC_CModels::transfer(exr_unit->temp_frame->get_rows(), /* Leave NULL if non existent */
446                         frame->get_rows(),
447                         exr_unit->temp_frame->get_y(), /* Leave NULL if non existent */
448                         exr_unit->temp_frame->get_u(),
449                         exr_unit->temp_frame->get_v(),
450                         frame->get_y(), /* Leave NULL if non existent */
451                         frame->get_u(),
452                         frame->get_v(),
453                         0,        /* Dimensions to capture from input frame */
454                         0,
455                         asset->width,
456                         asset->height,
457                         0,       /* Dimensions to project on output frame */
458                         0,
459                         asset->width,
460                         asset->height,
461                         frame->get_color_model(),
462                         native_cmodel,
463                         0,         /* When transfering BC_RGBA8888 to non-alpha this is the background color in 0xRRGGBB hex */
464                         asset->width,       /* For planar use the luma rowspan */
465                         asset->height);
466                 output_frame = exr_unit->temp_frame;
467         }
468         else
469                 output_frame = frame;
470
471         Imf::Header header(output_frame->get_w(), output_frame->get_h());
472         header.compression() = (Imf::Compression)compression_to_exr(
473                 asset->exr_compression);
474         header.channels().insert("R", Imf::Channel(Imf::FLOAT));
475         header.channels().insert("G", Imf::Channel(Imf::FLOAT));
476         header.channels().insert("B", Imf::Channel(Imf::FLOAT));
477         if(asset->exr_use_alpha) header.channels().insert("A", Imf::Channel(Imf::FLOAT));
478
479         EXROStream exr_stream(data);
480         Imf::OutputFile file(exr_stream, header);
481         Imf::FrameBuffer framebuffer;
482         float **rows = (float**)output_frame->get_rows();
483         framebuffer.insert("R",
484                 Imf::Slice(Imf::FLOAT,
485                         (char*)(rows[0]),
486                         sizeof(float) * components,
487                         sizeof(float) * components * output_frame->get_w()));
488         framebuffer.insert("G",
489                 Imf::Slice(Imf::FLOAT,
490                         (char*)(rows[0] + 1),
491                         sizeof(float) * components,
492                         sizeof(float) * components * output_frame->get_w()));
493         framebuffer.insert("B",
494                 Imf::Slice(Imf::FLOAT,
495                         (char*)(rows[0] + 2),
496                         sizeof(float) * components,
497                         sizeof(float) * components * output_frame->get_w()));
498         if(asset->exr_use_alpha)
499                 framebuffer.insert("A",
500                         Imf::Slice(Imf::FLOAT,
501                                 (char*)(rows[0] + 3),
502                                 sizeof(float) * components,
503                                 sizeof(float) * components * output_frame->get_w()));
504         file.setFrameBuffer(framebuffer);
505         file.writePixels(asset->height);
506         return 0;
507 }
508
509 FrameWriterUnit* FileEXR::new_writer_unit(FrameWriter *writer)
510 {
511         return new EXRUnit(this, writer);
512 }
513
514
515 EXRUnit::EXRUnit(FileEXR *file, FrameWriter *writer)
516  : FrameWriterUnit(writer)
517 {
518         this->file = file;
519         temp_frame = 0;
520 }
521
522 EXRUnit::~EXRUnit()
523 {
524         if(temp_frame) delete temp_frame;
525 }
526
527
528 EXRConfigVideo::EXRConfigVideo(BC_WindowBase *parent_window, Asset *asset)
529  : BC_Window(_(PROGRAM_NAME ": Video Compression"),
530         parent_window->get_abs_cursor_x(1), parent_window->get_abs_cursor_y(1),
531         xS(300), BC_OKButton::calculate_h() + yS(100))
532 {
533         this->parent_window = parent_window;
534         this->asset = asset;
535 }
536
537 EXRConfigVideo::~EXRConfigVideo()
538 {
539 }
540
541 void EXRConfigVideo::create_objects()
542 {
543         lock_window("EXRConfigVideo::create_objects");
544         int x = xS(10), y = yS(10);
545         add_subwindow(new EXRUseAlpha(this, x, y));
546         y += yS(30);
547         EXRCompression *menu;
548         add_subwindow(new BC_Title(x, y, _("Compression:")));
549         x += xS(110);
550         add_subwindow(menu = new EXRCompression(this, x, y, xS(100)));
551         menu->create_objects();
552         add_subwindow(new BC_OKButton(this));
553         show_window(1);
554         unlock_window();
555 }
556
557 int EXRConfigVideo::close_event()
558 {
559         set_done(0);
560         return 1;
561 }
562
563
564 EXRUseAlpha::EXRUseAlpha(EXRConfigVideo *gui, int x, int y)
565  : BC_CheckBox(x, y, gui->asset->exr_use_alpha, _("Use alpha"))
566 {
567         this->gui = gui;
568 }
569
570 int EXRUseAlpha::handle_event()
571 {
572         gui->asset->exr_use_alpha = get_value();
573         return 1;
574 }
575
576
577
578 EXRCompression::EXRCompression(EXRConfigVideo *gui, int x, int y, int w)
579  : BC_PopupMenu(x, y, w,
580         FileEXR::compression_to_str(gui->asset->exr_compression))
581 {
582         this->gui = gui;
583 }
584 void EXRCompression::create_objects()
585 {
586         add_item(new EXRCompressionItem(gui, FileEXR::NONE));
587         add_item(new EXRCompressionItem(gui, FileEXR::PIZ));
588         add_item(new EXRCompressionItem(gui, FileEXR::ZIP));
589         add_item(new EXRCompressionItem(gui, FileEXR::ZIPS));
590         add_item(new EXRCompressionItem(gui, FileEXR::RLE));
591         add_item(new EXRCompressionItem(gui, FileEXR::PXR24));
592         add_item(new EXRCompressionItem(gui, FileEXR::B44));
593         add_item(new EXRCompressionItem(gui, FileEXR::B44A));
594         add_item(new EXRCompressionItem(gui, FileEXR::DWAA));
595         add_item(new EXRCompressionItem(gui, FileEXR::DWAB));
596 }
597
598 int EXRCompression::handle_event()
599 {
600         return 1;
601 }
602
603 EXRCompressionItem::EXRCompressionItem(EXRConfigVideo *gui, int value)
604  : BC_MenuItem(FileEXR::compression_to_str(value))
605 {
606         this->gui = gui;
607         this->value = value;
608 }
609
610 int EXRCompressionItem::handle_event()
611 {
612         gui->asset->exr_compression = value;
613         return 0;
614 }
615
616 #endif