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