r851: Merge 2.1:
[cinelerra_cv.git] / cinelerra / fileexr.C
blob6409bf7340f1c6472608562c7be404b6d91de012
1 #include "asset.h"
2 #include "bcsignals.h"
3 #include "clip.h"
4 #include "fileexr.h"
5 #include "filesystem.h"
6 #include "interlacemodes.h"
8 #include <ImathBox.h>
9 #include <ImfChannelList.h>
10 #include <ImfChromaticities.h>
11 #include <ImfCompression.h>
12 #include <ImfIO.h>
13 #include <ImfInputFile.h>
14 #include <ImfOutputFile.h>
15 #include <ImfPixelType.h>
16 #include <ImfRgbaFile.h>
17 #include <ImfRgbaYca.h>
18 #include <ImfVersion.h>
19 #include "mwindow.inc"
20 #include "vframe.h"
23 class EXRIStream : public Imf::IStream
25 public:
26         EXRIStream(char *data, int size);
27         ~EXRIStream();
29         bool read (char c[], int n);
30         Imf::Int64 tellg ();
31         void seekg (Imf::Int64 pos);
32         void clear ();
34 private:
35         char *data;
36         int size;
37         int position;
40 class EXROStream : public Imf::OStream
42 public:
43         EXROStream(VFrame *data);
44         ~EXROStream();
46     virtual void write(const char c[], int n);
47     virtual Imf::Int64 tellp();
48     virtual void seekp(Imf::Int64 pos);
50 private:
51         VFrame *data;
52         int position;
57 EXRIStream::EXRIStream(char *data, int size)
58  : Imf::IStream("mypath")
60         this->data = data;
61         this->size = size;
62         position = 0;
65 EXRIStream::~EXRIStream()
69 bool EXRIStream::read(char c[], int n)
71         int fragment = n;
72         if(position + fragment > size)
73         {
74                 fragment = size - position;
75         }
76         memcpy(c, data + position, fragment);
77         position += fragment;
79         if(n != fragment)
80         {
81                 throw Iex::InputExc ("EXRIStream::read: Unexpected end of file.");
82         }
83         return position >= size;
86 Imf::Int64 EXRIStream::tellg ()
88         return position;
91 void EXRIStream::seekg(Imf::Int64 pos)
93         position = pos;
96 void EXRIStream::clear()
110 EXROStream::EXROStream(VFrame *data)
111  : Imf::OStream("mypath")
113         this->data = data;
114         position = 0;
116 EXROStream::~EXROStream()
120 void EXROStream::write(const char c[], int n)
122         if(position + n > data->get_compressed_allocated())
123                 data->allocate_compressed_data(MAX(position + n, data->get_compressed_allocated() * 2));
125         memcpy(data->get_data() + position, c, n);
126         position += n;
127         data->set_compressed_size(MAX(position, data->get_compressed_size()));
130 Imf::Int64 EXROStream::tellp()
132         return position;
135 void EXROStream::seekp(Imf::Int64 pos)
137         position = pos;
150 FileEXR::FileEXR(Asset *asset, File *file)
151  : FileList(asset, file, "EXRLIST", ".exr", FILE_EXR, FILE_EXR_LIST)
153         native_cmodel = BC_RGB_FLOAT;
154         is_yuv = 0;
155         temp_y = 0;
156         temp_u = 0;
157         temp_v = 0;
160 FileEXR::~FileEXR()
162         if(temp_y) delete [] temp_y;
163         if(temp_u) delete [] temp_u;
164         if(temp_v) delete [] temp_v;
167 char* FileEXR::compression_to_str(int compression)
169         switch(compression)
170         {
171                 case FileEXR::NONE: return "None"; break;
172                 case FileEXR::PIZ: return "PIZ"; break;
173                 case FileEXR::ZIP: return "ZIP"; break;
174                 case FileEXR::ZIPS: return "ZIPS"; break;
175                 case FileEXR::RLE: return "RLE"; break;
176                 case FileEXR::PXR24: return "PXR24"; break;
177         }
178         return "None";
181 int FileEXR::compression_to_exr(int compression)
183         switch(compression)
184         {
185                 case FileEXR::NONE: return (int)Imf::NO_COMPRESSION; break;
186                 case FileEXR::PIZ: return (int)Imf::PIZ_COMPRESSION; break;
187                 case FileEXR::ZIP: return (int)Imf::ZIP_COMPRESSION; break;
188                 case FileEXR::ZIPS: return (int)Imf::ZIPS_COMPRESSION; break;
189                 case FileEXR::RLE: return (int)Imf::RLE_COMPRESSION; break;
190                 case FileEXR::PXR24: return (int)Imf::PXR24_COMPRESSION; break;
191         }
192         return Imf::NO_COMPRESSION;
195 int FileEXR::str_to_compression(char *string)
197         if(!strcmp(compression_to_str(FileEXR::NONE), string))  
198                 return FileEXR::NONE;
199         if(!strcmp(compression_to_str(FileEXR::PIZ), string))   
200                 return FileEXR::PIZ;
201         if(!strcmp(compression_to_str(FileEXR::ZIP), string))   
202                 return FileEXR::ZIP;
203         if(!strcmp(compression_to_str(FileEXR::ZIPS), string))  
204                 return FileEXR::ZIPS;
205         if(!strcmp(compression_to_str(FileEXR::RLE), string))   
206                 return FileEXR::RLE;
207         if(!strcmp(compression_to_str(FileEXR::PXR24), string))         
208                 return PXR24;
209         return FileEXR::NONE;
212 int FileEXR::check_sig(Asset *asset, char *test)
214         if(Imf::isImfMagic(test)) return 1;
215         if(test[0] == 'E' && test[1] == 'X' && test[2] == 'R' && 
216                 test[3] == 'L' && test[4] == 'I' && test[5] == 'S' && test[6] == 'T')
217         {
218                 return 1;
219         }
221         return 0;
224 void FileEXR::get_parameters(BC_WindowBase *parent_window, 
225         Asset *asset, 
226         BC_WindowBase* &format_window,
227         int audio_options,
228         int video_options)
230         if(video_options)
231         {
232                 EXRConfigVideo *window = new EXRConfigVideo(parent_window, asset);
233                 format_window = window;
234                 window->create_objects();
235                 window->run_window();
236                 delete window;
237         }
240 int FileEXR::colormodel_supported(int colormodel)
242         return native_cmodel;
245 int FileEXR::get_best_colormodel(Asset *asset, int driver)
247         if(asset->exr_use_alpha)
248                 return BC_RGBA_FLOAT;
249         else
250                 return BC_RGB_FLOAT;
253 int64_t FileEXR::get_memory_usage()
255         int64_t result = FileList::get_memory_usage();
256         if(temp_y) result += (int64_t)asset->width * asset->height * 3 / 2;
257         return result;
261 int FileEXR::read_frame_header(char *path)
263         int result = 0;
265 // This may have been used by VFS
266 //      FILE *stream;
267 // 
268 //      if(!(stream = fopen(path, "rb")))
269 //      {
270 //              perror("FileEXR::read_frame_header");
271 //              return 1;
272 //      }
273 //      int size = FileSystem::get_size(path);
274 //      char *buffer = new char[size];
275 //      fread(buffer, size, 1, stream);
276 //      fclose(stream);
277 // 
278 //      EXRIStream exr_stream(buffer, size);
279 //      Imf::InputFile file(exr_stream);
282         Imf::InputFile file(path);
284         Imath::Box2i dw = file.header().dataWindow();
285         
286         asset->width = dw.max.x - dw.min.x + 1;
287         asset->height = dw.max.y - dw.min.y + 1;
288         asset->interlace_mode = BC_ILACE_MODE_NOTINTERLACED;
290         const Imf::ChannelList &channels = file.header().channels();
292         if(channels.findChannel("A"))
293                 native_cmodel = BC_RGBA_FLOAT;
294         else
295                 native_cmodel = BC_RGB_FLOAT;
297         if(channels.findChannel("Y"))
298                 is_yuv = 1;
299 // for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
300 // {
301 // printf("%s\n", i.name());
302 // }
304 //      delete [] buffer;
305         return result;
308 int FileEXR::read_frame(VFrame *frame, VFrame *data)
310         EXRIStream exr_stream((char*)data->get_data(), data->get_compressed_size());
311         Imf::InputFile file(exr_stream);
312         Imath::Box2i dw = file.header().dataWindow();
313     int dx = dw.min.x;
314     int dy = dw.min.y;
315         Imf::FrameBuffer framebuffer;
316         float **rows = (float**)frame->get_rows();
317         int components = cmodel_components(frame->get_color_model());
319         if(is_yuv)
320         {
321                 if(!temp_y) temp_y = new float[asset->width * asset->height];
322                 if(!temp_u) temp_u = new float[asset->width * asset->height / 4];
323                 if(!temp_v) temp_v = new float[asset->width * asset->height / 4];
324                 framebuffer.insert("Y", Imf::Slice(Imf::FLOAT, 
325                         (char*)(temp_y - dy * asset->width - dx),
326                         sizeof(float),
327                         sizeof(float) * frame->get_w()));
328                 framebuffer.insert("BY", Imf::Slice(Imf::FLOAT, 
329                         (char*)(temp_u - dy * asset->width / 4 - dx / 2),
330                         sizeof(float),
331                         sizeof(float) * frame->get_w() / 2,
332                         2, 
333                         2));
334                 framebuffer.insert("RY", Imf::Slice(Imf::FLOAT, 
335                         (char*)(temp_v - dy * asset->width / 4 - dx / 2),
336                         sizeof(float),
337                         sizeof(float) * frame->get_w() / 2,
338                         2, 
339                         2));
340         }
341         else
342         {
343                 framebuffer.insert("R", Imf::Slice(Imf::FLOAT, 
344                         (char*)(&rows[-dy][-dx * components]),
345                         sizeof(float) * components,
346                         sizeof(float) * components * frame->get_w()));
347                 framebuffer.insert("G", Imf::Slice(Imf::FLOAT, 
348                         (char*)(&rows[-dy][-dx * components + 1]),
349                         sizeof(float) * components,
350                         sizeof(float) * components * frame->get_w()));
351                 framebuffer.insert("B", Imf::Slice(Imf::FLOAT, 
352                         (char*)(&rows[-dy][-dx * components + 2]),
353                         sizeof(float) * components,
354                         sizeof(float) * components * frame->get_w()));
355         }
357 // Alpha always goes directly to the output frame
358         if(components == 4)
359         {
360                 framebuffer.insert("A", Imf::Slice(Imf::FLOAT, 
361                         (char*)(&rows[-dy][-dx * components + 3]),
362                         sizeof(float) * components,
363                         sizeof(float) * components * frame->get_w()));
364         }
366         file.setFrameBuffer(framebuffer);
367         file.readPixels (dw.min.y, dw.max.y);
371         if(is_yuv)
372         {
373 // Convert to RGB using crazy ILM equations
374                 Imath::V3f yw;
375                 Imf::Chromaticities cr;
376                 yw = Imf::RgbaYca::computeYw(cr);
378                 for(int i = 0; i < asset->height - 1; i += 2)
379                 {
380                         float *y_row1 = temp_y + i * asset->width;
381                         float *y_row2 = temp_y + (i + 1) * asset->width;
382                         float *u_row = temp_u + (i * asset->width / 4);
383                         float *v_row = temp_v + (i * asset->width / 4);
384                         float *out_row1 = rows[i];
385                         float *out_row2 = rows[i + 1];
386                         for(int j = 0; j < asset->width - 1; j += 2)
387                         {
388                                 float v = *u_row++;
389                                 float u = *v_row++;
390                                 float y;
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++;
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++;
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++;
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;
437 int FileEXR::write_frame(VFrame *frame, VFrame *data, FrameWriterUnit *unit)
439         EXRUnit *exr_unit = (EXRUnit*)unit;
440         int result = 0;
442         VFrame *output_frame;
443         data->set_compressed_size(0);
446         int native_cmodel = asset->exr_use_alpha ? BC_RGBA_FLOAT : BC_RGB_FLOAT;
447         int components = cmodel_components(native_cmodel);
449         if(frame->get_color_model() != native_cmodel)
450         {
451                 if(!exr_unit->temp_frame) exr_unit->temp_frame = new VFrame(0,
452                         asset->width,
453                         asset->height,
454                         native_cmodel);
455                 cmodel_transfer(exr_unit->temp_frame->get_rows(), /* Leave NULL if non existent */
456                         frame->get_rows(),
457                         exr_unit->temp_frame->get_y(), /* Leave NULL if non existent */
458                         exr_unit->temp_frame->get_u(),
459                         exr_unit->temp_frame->get_v(),
460                         frame->get_y(), /* Leave NULL if non existent */
461                         frame->get_u(),
462                         frame->get_v(),
463                         0,        /* Dimensions to capture from input frame */
464                         0, 
465                         asset->width, 
466                         asset->height,
467                         0,       /* Dimensions to project on output frame */
468                         0, 
469                         asset->width, 
470                         asset->height,
471                         frame->get_color_model(), 
472                         native_cmodel,
473                         0,         /* When transfering BC_RGBA8888 to non-alpha this is the background color in 0xRRGGBB hex */
474                         asset->width,       /* For planar use the luma rowspan */
475                         asset->height);
476                 output_frame = exr_unit->temp_frame;
477         }
478         else
479                 output_frame = frame;
481         Imf::Header header(output_frame->get_w(), output_frame->get_h());
482         header.compression() = (Imf::Compression)compression_to_exr(
483                 asset->exr_compression);
484         header.channels().insert("R", Imf::Channel(Imf::FLOAT));
485         header.channels().insert("G", Imf::Channel(Imf::FLOAT));
486         header.channels().insert("B", Imf::Channel(Imf::FLOAT));
487         if(asset->exr_use_alpha) header.channels().insert("A", Imf::Channel(Imf::FLOAT));
489         EXROStream exr_stream(data);
490         Imf::OutputFile file(exr_stream, header);
491         Imf::FrameBuffer framebuffer;
492         float **rows = (float**)output_frame->get_rows();
493         framebuffer.insert("R",
494                 Imf::Slice(Imf::FLOAT,
495                         (char*)(rows[0]),
496                         sizeof(float) * components,
497                         sizeof(float) * components * output_frame->get_w()));
498         framebuffer.insert("G",
499                 Imf::Slice(Imf::FLOAT,
500                         (char*)(rows[0] + 1),
501                         sizeof(float) * components,
502                         sizeof(float) * components * output_frame->get_w()));
503         framebuffer.insert("B",
504                 Imf::Slice(Imf::FLOAT,
505                         (char*)(rows[0] + 2),
506                         sizeof(float) * components,
507                         sizeof(float) * components * output_frame->get_w()));
508         if(asset->exr_use_alpha)
509                 framebuffer.insert("A",
510                         Imf::Slice(Imf::FLOAT,
511                                 (char*)(rows[0] + 3),
512                                 sizeof(float) * components,
513                                 sizeof(float) * components * output_frame->get_w()));
514         file.setFrameBuffer(framebuffer);
515         file.writePixels(asset->height);
516         return 0;
519 FrameWriterUnit* FileEXR::new_writer_unit(FrameWriter *writer)
521         return new EXRUnit(this, writer);
535 EXRUnit::EXRUnit(FileEXR *file, FrameWriter *writer)
536  : FrameWriterUnit(writer)
538         this->file = file;
539         temp_frame = 0;
542 EXRUnit::~EXRUnit()
544         if(temp_frame) delete temp_frame;
557 EXRConfigVideo::EXRConfigVideo(BC_WindowBase *parent_window, Asset *asset)
558  : BC_Window(PROGRAM_NAME ": Video Compression",
559         parent_window->get_abs_cursor_x(1),
560         parent_window->get_abs_cursor_y(1),
561         300,
562         BC_OKButton::calculate_h() + 100)
564         this->parent_window = parent_window;
565         this->asset = asset;
568 EXRConfigVideo::~EXRConfigVideo()
572 int EXRConfigVideo::create_objects()
574         int x = 10, y = 10;
575         add_subwindow(new EXRUseAlpha(this, x, y));
576         y += 30;
577         EXRCompression *menu;
578         add_subwindow(new BC_Title(x, y, "Compression:"));
579         x += 110;
580         add_subwindow(menu = new EXRCompression(this, x, y, 100));
581         menu->create_objects();
582         add_subwindow(new BC_OKButton(this));
583         return 0;
586 int EXRConfigVideo::close_event()
588         set_done(0);
589         return 1;
593 EXRUseAlpha::EXRUseAlpha(EXRConfigVideo *gui, int x, int y)
594  : BC_CheckBox(x, y, gui->asset->exr_use_alpha, _("Use alpha"))
596         this->gui = gui;
599 int EXRUseAlpha::handle_event()
601         gui->asset->exr_use_alpha = get_value();
602         return 1;
607 EXRCompression::EXRCompression(EXRConfigVideo *gui, int x, int y, int w)
608  : BC_PopupMenu(x, 
609         y, 
610         w, 
611         FileEXR::compression_to_str(gui->asset->exr_compression))
613         this->gui = gui;
615 void EXRCompression::create_objects()
617         add_item(new EXRCompressionItem(gui, FileEXR::NONE));
618         add_item(new EXRCompressionItem(gui, FileEXR::PIZ));
619         add_item(new EXRCompressionItem(gui, FileEXR::ZIP));
620         add_item(new EXRCompressionItem(gui, FileEXR::ZIPS));
621         add_item(new EXRCompressionItem(gui, FileEXR::RLE));
622         add_item(new EXRCompressionItem(gui, FileEXR::PXR24));
625 int EXRCompression::handle_event()
627         return 1;
630 EXRCompressionItem::EXRCompressionItem(EXRConfigVideo *gui, int value)
631  : BC_MenuItem(FileEXR::compression_to_str(value))
633         this->gui = gui;
634         this->value = value;
637 int EXRCompressionItem::handle_event()
639         gui->asset->exr_compression = value;
640         return 0;