Credit Andrew - fix vorbis audio which was scratchy and ensure aging plugin does...
[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  * Copyright (C) 2003-2016 Cinelerra CV contributors
6  *
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.
11  *
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.
16  *
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
20  *
21  */
22
23 #ifdef HAVE_OPENEXR
24
25 #include "asset.h"
26 #include "bcsignals.h"
27 #include "clip.h"
28 #include "file.h"
29 #include "fileexr.h"
30 #include "filesystem.h"
31 #include "interlacemodes.h"
32
33 #include "mwindow.inc"
34 #include "vframe.h"
35
36 #include "ImfChannelList.h"
37 #include "ImfChromaticities.h"
38 #include "ImfCompression.h"
39 #include "ImfIO.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"
46
47 class EXRIStream : public Imf::IStream
48 {
49 public:
50         EXRIStream(char *data, int size);
51         ~EXRIStream();
52
53         bool read (char c[], int n);
54         uint64_t tellg ();
55         void seekg (uint64_t pos);
56         void clear ();
57
58 private:
59         char *data;
60         int size;
61         int position;
62 };
63
64 class EXROStream : public Imf::OStream
65 {
66 public:
67         EXROStream(VFrame *data);
68         ~EXROStream();
69
70     virtual void write(const char c[], int n);
71     virtual uint64_t tellp();
72     virtual void seekp(uint64_t pos);
73
74 private:
75         VFrame *data;
76         int position;
77 };
78
79
80
81 EXRIStream::EXRIStream(char *data, int size)
82  : Imf::IStream("mypath")
83 {
84         this->data = data;
85         this->size = size;
86         position = 0;
87 }
88
89 EXRIStream::~EXRIStream()
90 {
91 }
92
93 bool EXRIStream::read(char c[], int n)
94 {
95         int fragment = n;
96         if(position + fragment > size)
97         {
98                 fragment = size - position;
99         }
100         memcpy(c, data + position, fragment);
101         position += fragment;
102
103         if(n != fragment)
104         {
105                 throw Iex::InputExc ("EXRIStream::read: Unexpected end of file.");
106         }
107         return position >= size;
108 }
109
110 uint64_t EXRIStream::tellg ()
111 {
112         return position;
113 }
114
115 void EXRIStream::seekg(uint64_t pos)
116 {
117         position = pos;
118 }
119
120 void EXRIStream::clear()
121 {
122 }
123
124
125 EXROStream::EXROStream(VFrame *data)
126  : Imf::OStream("mypath")
127 {
128         this->data = data;
129         position = 0;
130 }
131 EXROStream::~EXROStream()
132 {
133 }
134
135 void EXROStream::write(const char c[], int n)
136 {
137         if(position + n > data->get_compressed_allocated())
138                 data->allocate_compressed_data(MAX(position + n, data->get_compressed_allocated() * 2));
139
140         memcpy(data->get_data() + position, c, n);
141         position += n;
142         data->set_compressed_size(MAX(position, data->get_compressed_size()));
143 }
144
145 uint64_t EXROStream::tellp()
146 {
147         return position;
148 }
149
150 void EXROStream::seekp(uint64_t pos)
151 {
152         position = pos;
153 }
154
155
156 FileEXR::FileEXR(Asset *asset, File *file)
157  : FileList(asset, file, "EXRLIST", ".exr", FILE_EXR, FILE_EXR_LIST)
158 {
159         native_cmodel = BC_RGB_FLOAT;
160         is_yuv = 0;
161         temp_y = 0;
162         temp_u = 0;
163         temp_v = 0;
164 }
165
166 FileEXR::~FileEXR()
167 {
168         delete [] temp_y;
169         delete [] temp_u;
170         delete [] temp_v;
171 }
172
173 const char* FileEXR::compression_to_str(int compression)
174 {
175         switch( compression ) {
176         case NONE:      break;
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";
186         }
187         return _("None");
188 }
189
190 int FileEXR::compression_to_exr(int compression)
191 {
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;
203         }
204         return Imf::NO_COMPRESSION;
205 }
206
207 int FileEXR::str_to_compression(char *string)
208 {
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;
219         return NONE;
220 }
221
222 int FileEXR::check_sig(Asset *asset, char *test)
223 {
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')
227         {
228                 return 1;
229         }
230
231         return 0;
232 }
233
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)
237 {
238         if( video_options ) {
239                 EXRConfigVideo *window = new EXRConfigVideo(parent_window, asset);
240                 format_window = window;
241                 window->create_objects();
242                 window->run_window();
243                 delete window;
244         }
245 }
246
247 int FileEXR::colormodel_supported(int colormodel)
248 {
249         return native_cmodel;
250 }
251
252 int FileEXR::get_best_colormodel(Asset *asset, int driver)
253 {
254         return asset->exr_use_alpha ? BC_RGBA_FLOAT : BC_RGB_FLOAT;
255 }
256
257 int64_t FileEXR::get_memory_usage()
258 {
259         int64_t result = FileList::get_memory_usage();
260         if(temp_y) result += (int64_t)asset->width * asset->height * 3 / 2;
261         return result;
262 }
263
264
265 int FileEXR::read_frame_header(char *path)
266 {
267         int result = 0;
268
269 // This may have been used by VFS
270 //      FILE *stream;
271 //
272 //      if(!(stream = fopen(path, "rb")))
273 //      {
274 //              perror("FileEXR::read_frame_header");
275 //              return 1;
276 //      }
277 //      int size = FileSystem::get_size(path);
278 //      char *buffer = new char[size];
279 //      fread(buffer, size, 1, stream);
280 //      fclose(stream);
281 //
282 //      EXRIStream exr_stream(buffer, size);
283 //      Imf::InputFile file(exr_stream);
284
285         if( access(path, R_OK) ) return 1;
286         Imf::InputFile file(path);
287
288         Imath::Box2i dw = file.header().dataWindow();
289
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;
293
294         const Imf::ChannelList &channels = file.header().channels();
295
296         if(channels.findChannel("A"))
297                 native_cmodel = BC_RGBA_FLOAT;
298         else
299                 native_cmodel = BC_RGB_FLOAT;
300         asset->exr_use_alpha = BC_CModels::has_alpha(native_cmodel) ? 1 : 0;
301
302         if(channels.findChannel("Y"))
303                 is_yuv = 1;
304 // for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
305 // {
306 // printf("%s\n", i.name());
307 // }
308
309 //      delete [] buffer;
310         return result;
311 }
312
313 int FileEXR::read_frame(VFrame *frame, VFrame *data)
314 {
315
316     try {
317
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());
326
327         if( is_yuv ) {
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),
333                         sizeof(float),
334                         sizeof(float) * frame->get_w()));
335                 framebuffer.insert("BY", Imf::Slice(Imf::FLOAT,
336                         (char*)(temp_u - dy * asset->width / 4 - dx / 2),
337                         sizeof(float),
338                         sizeof(float) * frame->get_w() / 2,
339                         2,
340                         2));
341                 framebuffer.insert("RY", Imf::Slice(Imf::FLOAT,
342                         (char*)(temp_v - dy * asset->width / 4 - dx / 2),
343                         sizeof(float),
344                         sizeof(float) * frame->get_w() / 2,
345                         2,
346                         2));
347         }
348         else {
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()));
361         }
362
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()));
369         }
370
371         file.setFrameBuffer(framebuffer);
372         file.readPixels (dw.min.y, dw.max.y);
373
374         if( is_yuv ) {
375 // Convert to RGB using crazy ILM equations
376                 Imath::V3f yw;
377                 Imf::Chromaticities cr;
378                 yw = Imf::RgbaYca::computeYw(cr);
379
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 ) {
388                                 float v = *u_row++;
389                                 float u = *v_row++;
390                                 float y;
391
392                                 float r, g, b;
393                                 y = *y_row1++;
394                                 r = (u + 1) * y;
395                                 b = (v + 1) * y;
396                                 g = (y - r * yw.x - b * yw.z) / yw.y;
397                                 *out_row1++ = r;
398                                 *out_row1++ = g;
399                                 *out_row1++ = b;
400                                 if(components == 4) out_row1++;
401
402                                 y = *y_row1++;
403                                 r = (u + 1) * y;
404                                 b = (v + 1) * y;
405                                 g = (y - r * yw.x - b * yw.z) / yw.y;
406                                 *out_row1++ = r;
407                                 *out_row1++ = g;
408                                 *out_row1++ = b;
409                                 if(components == 4) out_row1++;
410
411                                 y = *y_row2++;
412                                 r = (u + 1) * y;
413                                 b = (v + 1) * y;
414                                 g = (y - r * yw.x - b * yw.z) / yw.y;
415                                 *out_row2++ = r;
416                                 *out_row2++ = g;
417                                 *out_row2++ = b;
418                                 if(components == 4) out_row1++;
419
420                                 y = *y_row2++;
421                                 r = (u + 1) * y;
422                                 b = (v + 1) * y;
423                                 g = (y - r * yw.x - b * yw.z) / yw.y;
424                                 *out_row2++ = r;
425                                 *out_row2++ = g;
426                                 *out_row2++ = b;
427                                 if(components == 4) out_row1++;
428                         }
429                 }
430         }
431         return 0;
432     } catch (const std::exception &e) {
433     std::cerr << "error reading EXR image file:" << e.what() << std::endl;
434     return 1;
435     }
436 }
437
438
439 int FileEXR::write_frame(VFrame *frame, VFrame *data, FrameWriterUnit *unit)
440 {
441         EXRUnit *exr_unit = (EXRUnit*)unit;
442
443         VFrame *output_frame;
444         data->set_compressed_size(0);
445         Imf::setGlobalThreadCount(file->cpus);
446
447         int native_cmodel = asset->exr_use_alpha ? BC_RGBA_FLOAT : BC_RGB_FLOAT;
448         int components = BC_CModels::components(native_cmodel);
449
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 */
454                         frame->get_rows(),
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 */
459                         frame->get_u(),
460                         frame->get_v(),
461                         0,        /* Dimensions to capture from input frame */
462                         0,
463                         asset->width,
464                         asset->height,
465                         0,       /* Dimensions to project on output frame */
466                         0,
467                         asset->width,
468                         asset->height,
469                         frame->get_color_model(),
470                         native_cmodel,
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 */
473                         asset->height);
474                 output_frame = exr_unit->temp_frame;
475         }
476         else
477                 output_frame = frame;
478
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));
486
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,
493                         (char*)(rows[0]),
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);
514         return 0;
515 }
516
517 int FileEXR::can_copy_from(Asset *asset, int64_t position)
518 {
519         if(asset->format == FILE_EXR ||
520                 asset->format == FILE_EXR_LIST)
521                 return 1;
522
523         return 0;
524 }
525
526 FrameWriterUnit* FileEXR::new_writer_unit(FrameWriter *writer)
527 {
528         return new EXRUnit(this, writer);
529 }
530
531
532 EXRUnit::EXRUnit(FileEXR *file, FrameWriter *writer)
533  : FrameWriterUnit(writer)
534 {
535         this->file = file;
536         temp_frame = 0;
537 }
538
539 EXRUnit::~EXRUnit()
540 {
541         if(temp_frame) delete temp_frame;
542 }
543
544
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))
549 {
550         this->parent_window = parent_window;
551         this->asset = asset;
552 // *** CONTEXT_HELP ***
553         context_help_set_keyword("Single File Rendering");
554 }
555
556 EXRConfigVideo::~EXRConfigVideo()
557 {
558 }
559
560 void EXRConfigVideo::create_objects()
561 {
562         lock_window("EXRConfigVideo::create_objects");
563         int x = xS(10), y = yS(10);
564         add_subwindow(new EXRUseAlpha(this, x, y));
565         y += yS(30);
566         EXRCompression *menu;
567         add_subwindow(new BC_Title(x, y, _("Compression:")));
568         x += xS(110);
569         add_subwindow(menu = new EXRCompression(this, x, y, xS(100)));
570         menu->create_objects();
571         add_subwindow(new BC_OKButton(this));
572         show_window(1);
573         unlock_window();
574 }
575
576 int EXRConfigVideo::close_event()
577 {
578         set_done(0);
579         return 1;
580 }
581
582
583 EXRUseAlpha::EXRUseAlpha(EXRConfigVideo *gui, int x, int y)
584  : BC_CheckBox(x, y, gui->asset->exr_use_alpha, _("Use alpha"))
585 {
586         this->gui = gui;
587 }
588
589 int EXRUseAlpha::handle_event()
590 {
591         gui->asset->exr_use_alpha = get_value();
592         return 1;
593 }
594
595
596
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))
600 {
601         this->gui = gui;
602 }
603 void EXRCompression::create_objects()
604 {
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));
615 }
616
617 int EXRCompression::handle_event()
618 {
619         return 1;
620 }
621
622 EXRCompressionItem::EXRCompressionItem(EXRConfigVideo *gui, int value)
623  : BC_MenuItem(FileEXR::compression_to_str(value))
624 {
625         this->gui = gui;
626         this->value = value;
627 }
628
629 int EXRCompressionItem::handle_event()
630 {
631         gui->asset->exr_compression = value;
632         return 0;
633 }
634
635 #endif