Fix second part #32377: saving/loading exr files to file paths with non-ascii
[blender.git] / source / blender / imbuf / intern / openexr / openexr_api.cpp
1 /*
2  * ***** BEGIN GPLLICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Copyright by Gernot Ziegler <gz@lysator.liu.se>.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Austin Benesh, Ton Roosendaal (float, half, speedup, cleanup...).
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/imbuf/intern/openexr/openexr_api.cpp
29  *  \ingroup openexr
30  */
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <stddef.h>
35 #include <fstream>
36 #include <string>
37 #include <set>
38
39
40 #include <openexr_api.h>
41
42 #if defined (WIN32) && !defined(FREE_WINDOWS)
43 #include "utfconv.h"
44 #endif
45
46 extern "C"
47 {
48
49 // The following prevents a linking error in debug mode for MSVC using the libs in CVS
50 #if defined(WITH_OPENEXR) && defined(_WIN32) && defined(_DEBUG) && !defined(__MINGW32__) && !defined(__CYGWIN__)
51 _CRTIMP void __cdecl _invalid_parameter_noinfo(void)
52 {
53 }
54 #endif
55
56 #include "MEM_guardedalloc.h"
57
58 #include "BLI_blenlib.h"
59 #include "BLI_math_color.h"
60 #include "BLI_threads.h"
61
62 #include "IMB_imbuf_types.h"
63 #include "IMB_imbuf.h"
64 #include "IMB_allocimbuf.h"
65 #include "IMB_metadata.h"
66
67 #include "openexr_multi.h"
68 }
69
70 #include <iostream>
71
72 #if defined(_WIN32) && !defined(FREE_WINDOWS)
73 #include <half.h>
74 #include <IlmImf/ImfVersion.h>
75 #include <IlmImf/ImfArray.h>
76 #include <IlmImf/ImfIO.h>
77 #include <IlmImf/ImfChannelList.h>
78 #include <IlmImf/ImfPixelType.h>
79 #include <IlmImf/ImfInputFile.h>
80 #include <IlmImf/ImfOutputFile.h>
81 #include <IlmImf/ImfCompression.h>
82 #include <IlmImf/ImfCompressionAttribute.h>
83 #include <IlmImf/ImfStringAttribute.h>
84 #include <IlmImf/ImfStdIO.h>
85 #include <Imath/ImathBox.h>
86 #else
87 #include <half.h>
88 #include <ImfVersion.h>
89 #include <ImathBox.h>
90 #include <ImfArray.h>
91 #include <ImfIO.h>
92 #include <ImfChannelList.h>
93 #include <ImfPixelType.h>
94 #include <ImfInputFile.h>
95 #include <ImfOutputFile.h>
96 #include <ImfCompression.h>
97 #include <ImfCompressionAttribute.h>
98 #include <ImfStringAttribute.h>
99 #include <ImfStdIO.h>
100 #endif
101
102 using namespace Imf;
103 using namespace Imath;
104
105 class Mem_IStream : public Imf::IStream
106 {
107 public:
108
109         Mem_IStream (unsigned char *exrbuf, size_t exrsize) :
110                 IStream("dummy"), _exrpos(0), _exrsize(exrsize) {
111                 _exrbuf = exrbuf;
112         }
113
114         virtual bool    read(char c[], int n);
115         virtual Int64   tellg();
116         virtual void    seekg(Int64 pos);
117         virtual void    clear();
118         //virtual ~Mem_IStream() {}; // unused
119
120 private:
121
122         Int64 _exrpos;
123         Int64 _exrsize;
124         unsigned char *_exrbuf;
125 };
126
127 bool Mem_IStream::read(char c[], int n)
128 {
129         if (n + _exrpos <= _exrsize) {
130                 memcpy(c, (void *)(&_exrbuf[_exrpos]), n);
131                 _exrpos += n;
132                 return true;
133         }
134         else
135                 return false;
136 }
137
138 Int64 Mem_IStream::tellg()
139 {
140         return _exrpos;
141 }
142
143 void Mem_IStream::seekg(Int64 pos)
144 {
145         _exrpos = pos;
146 }
147
148 void Mem_IStream::clear()
149 {
150 }
151
152 struct _RGBAZ {
153         half r;
154         half g;
155         half b;
156         half a;
157         half z;
158 };
159
160 typedef struct _RGBAZ RGBAZ;
161
162 extern "C"
163 {
164
165 int imb_is_a_openexr(unsigned char *mem)
166 {
167         return Imf::isImfMagic((const char *)mem);
168 }
169
170 static void openexr_header_compression(Header *header, int compression)
171 {
172         switch (compression) {
173                 case 0:
174                         header->compression() = NO_COMPRESSION;
175                         break;
176                 case 1:
177                         header->compression() = PXR24_COMPRESSION;
178                         break;
179                 case 2:
180                         header->compression() = ZIP_COMPRESSION;
181                         break;
182                 case 3:
183                         header->compression() = PIZ_COMPRESSION;
184                         break;
185                 case 4:
186                         header->compression() = RLE_COMPRESSION;
187                         break;
188                 default:
189                         header->compression() = ZIP_COMPRESSION;
190                         break;
191         }
192 }
193
194 static void openexr_header_metadata(Header *header, struct ImBuf *ibuf)
195 {
196         ImMetaData *info;
197
198         for (info = ibuf->metadata; info; info = info->next)
199                 header->insert(info->key, StringAttribute(info->value));
200 }
201
202 static void openexr_ofstream_open(std::ofstream& ofs, const char *name)
203 {
204 #if defined (WIN32) && !defined(FREE_WINDOWS)
205         wchar_t *wname = alloc_utf16_from_8(name, 0);
206         ofs.open(wname, std::ios_base::binary);
207         free(wname);
208 #else
209         ofs.open(name, std::ios_base::binary);
210 #endif
211 }
212
213 static void openexr_ifstream_open(std::ifstream& ifs, const char *name)
214 {
215 #if defined (WIN32) && !defined(FREE_WINDOWS)
216         wchar_t *wname = alloc_utf16_from_8(name, 0);
217         ifs.open(wname, std::ios_base::binary);
218         free(wname);
219 #else
220         ifs.open(name, std::ios_base::binary);
221 #endif
222 }
223
224 static int imb_save_openexr_half(struct ImBuf *ibuf, const char *name, int flags)
225 {
226         const int channels = ibuf->channels;
227         const int is_alpha = (channels >= 4) && (ibuf->planes == 32);
228         const int is_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != NULL; /* summarize */
229         const int width = ibuf->x;
230         const int height = ibuf->y;
231
232         try
233         {
234                 Header header(width, height);
235
236                 openexr_header_compression(&header, ibuf->ftype & OPENEXR_COMPRESS);
237                 openexr_header_metadata(&header, ibuf);
238
239                 header.channels().insert("R", Channel(HALF));
240                 header.channels().insert("G", Channel(HALF));
241                 header.channels().insert("B", Channel(HALF));
242                 if (is_alpha)
243                         header.channels().insert("A", Channel(HALF));
244                 if (is_zbuf)     // z we do as float always
245                         header.channels().insert("Z", Channel(Imf::FLOAT));
246
247                 FrameBuffer frameBuffer;
248
249                 /* manually create ofstream, so we can handle utf-8 filepaths on windows */
250                 std::ofstream std_ostream;
251                 openexr_ofstream_open(std_ostream, name);
252                 StdOFStream exr_ostream(std_ostream, name);
253
254                 OutputFile file(exr_ostream, header);
255
256                 /* we store first everything in half array */
257                 RGBAZ *pixels = new RGBAZ[height * width];
258                 RGBAZ *to = pixels;
259                 int xstride = sizeof(RGBAZ);
260                 int ystride = xstride * width;
261
262                 /* indicate used buffers */
263                 frameBuffer.insert("R", Slice(HALF,  (char *) &pixels[0].r, xstride, ystride));
264                 frameBuffer.insert("G", Slice(HALF,  (char *) &pixels[0].g, xstride, ystride));
265                 frameBuffer.insert("B", Slice(HALF,  (char *) &pixels[0].b, xstride, ystride));
266                 if (is_alpha)
267                         frameBuffer.insert("A", Slice(HALF, (char *) &pixels[0].a, xstride, ystride));
268                 if (is_zbuf)
269                         frameBuffer.insert("Z", Slice(Imf::FLOAT, (char *)(ibuf->zbuf_float + (height - 1) * width),
270                                                       sizeof(float), sizeof(float) * -width));
271                 if (ibuf->rect_float) {
272                         float *from;
273
274                         for (int i = ibuf->y - 1; i >= 0; i--) {
275                                 from = ibuf->rect_float + channels * i * width;
276
277                                 for (int j = ibuf->x; j > 0; j--) {
278                                         to->r = from[0];
279                                         to->g = from[1];
280                                         to->b = from[2];
281                                         to->a = (channels >= 4) ? from[3] : 1.0f;
282                                         to++; from += 4;
283                                 }
284                         }
285                 }
286                 else {
287                         unsigned char *from;
288
289                         if (ibuf->profile == IB_PROFILE_LINEAR_RGB) {
290                                 for (int i = ibuf->y - 1; i >= 0; i--) {
291                                         from = (unsigned char *)ibuf->rect + channels * i * width;
292
293                                         for (int j = ibuf->x; j > 0; j--) {
294                                                 to->r = (float)(from[0]) / 255.0f;
295                                                 to->g = (float)(from[1]) / 255.0f;
296                                                 to->b = (float)(from[2]) / 255.0f;
297                                                 to->a = (float)(channels >= 4) ? from[3] / 255.0f : 1.0f;
298                                                 to++; from += 4;
299                                         }
300                                 }
301                         }
302                         else {
303                                 for (int i = ibuf->y - 1; i >= 0; i--) {
304                                         from = (unsigned char *)ibuf->rect + channels * i * width;
305
306                                         for (int j = ibuf->x; j > 0; j--) {
307                                                 to->r = srgb_to_linearrgb((float)from[0] / 255.0f);
308                                                 to->g = srgb_to_linearrgb((float)from[1] / 255.0f);
309                                                 to->b = srgb_to_linearrgb((float)from[2] / 255.0f);
310                                                 to->a = channels >= 4 ? (float)from[3] / 255.0f : 1.0f;
311                                                 to++; from += 4;
312                                         }
313                                 }
314                         }
315                 }
316
317 //              printf("OpenEXR-save: Writing OpenEXR file of height %d.\n", height);
318
319                 file.setFrameBuffer(frameBuffer);
320                 file.writePixels(height);
321
322                 delete[] pixels;
323         }
324         catch (const std::exception &exc)
325         {
326                 printf("OpenEXR-save: ERROR: %s\n", exc.what());
327                 if (ibuf) IMB_freeImBuf(ibuf);
328
329                 return (0);
330         }
331
332         return (1);
333 }
334
335 static int imb_save_openexr_float(struct ImBuf *ibuf, const char *name, int flags)
336 {
337         const int channels = ibuf->channels;
338         const int is_alpha = (channels >= 4) && (ibuf->planes == 32);
339         const int is_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != NULL; /* summarize */
340         const int width = ibuf->x;
341         const int height = ibuf->y;
342
343         try
344         {
345                 Header header(width, height);
346
347                 openexr_header_compression(&header, ibuf->ftype & OPENEXR_COMPRESS);
348                 openexr_header_metadata(&header, ibuf);
349
350                 header.channels().insert("R", Channel(Imf::FLOAT));
351                 header.channels().insert("G", Channel(Imf::FLOAT));
352                 header.channels().insert("B", Channel(Imf::FLOAT));
353                 if (is_alpha)
354                         header.channels().insert("A", Channel(Imf::FLOAT));
355                 if (is_zbuf)
356                         header.channels().insert("Z", Channel(Imf::FLOAT));
357
358                 FrameBuffer frameBuffer;
359
360                 /* manually create ofstream, so we can handle utf-8 filepaths on windows */
361                 std::ofstream std_ostream;
362                 openexr_ofstream_open(std_ostream, name);
363                 StdOFStream exr_ostream(std_ostream, name);
364
365                 OutputFile file(exr_ostream, header);
366
367                 int xstride = sizeof(float) * channels;
368                 int ystride = -xstride * width;
369                 float *rect[4] = {NULL, NULL, NULL, NULL};
370
371                 /* last scanline, stride negative */
372                 rect[0] = ibuf->rect_float + channels * (height - 1) * width;
373                 rect[1] = rect[0] + 1;
374                 rect[2] = rect[0] + 2;
375                 rect[3] = (channels >= 4) ? rect[0] + 3 : rect[0]; /* red as alpha, is this needed since alpha isn't written? */
376
377                 frameBuffer.insert("R", Slice(Imf::FLOAT,  (char *)rect[0], xstride, ystride));
378                 frameBuffer.insert("G", Slice(Imf::FLOAT,  (char *)rect[1], xstride, ystride));
379                 frameBuffer.insert("B", Slice(Imf::FLOAT,  (char *)rect[2], xstride, ystride));
380                 if (is_alpha)
381                         frameBuffer.insert("A", Slice(Imf::FLOAT,  (char *)rect[3], xstride, ystride));
382                 if (is_zbuf)
383                         frameBuffer.insert("Z", Slice(Imf::FLOAT, (char *) (ibuf->zbuf_float + (height - 1) * width),
384                                                       sizeof(float), sizeof(float) * -width));
385                 file.setFrameBuffer(frameBuffer);
386                 file.writePixels(height);
387         }
388         catch (const std::exception &exc)
389         {
390                 printf("OpenEXR-save: ERROR: %s\n", exc.what());
391                 if (ibuf) IMB_freeImBuf(ibuf);
392
393                 return (0);
394         }
395
396         return (1);
397         //      printf("OpenEXR-save: Done.\n");
398 }
399
400
401 int imb_save_openexr(struct ImBuf *ibuf, const char *name, int flags)
402 {
403         if (flags & IB_mem) {
404                 printf("OpenEXR-save: Create EXR in memory CURRENTLY NOT SUPPORTED !\n");
405                 imb_addencodedbufferImBuf(ibuf);
406                 ibuf->encodedsize = 0;
407                 return(0);
408         }
409
410         if (ibuf->ftype & OPENEXR_HALF)
411                 return imb_save_openexr_half(ibuf, name, flags);
412         else {
413                 /* when no float rect, we save as half (16 bits is sufficient) */
414                 if (ibuf->rect_float == NULL)
415                         return imb_save_openexr_half(ibuf, name, flags);
416                 else
417                         return imb_save_openexr_float(ibuf, name, flags);
418         }
419 }
420
421 /* ********************* Nicer API, MultiLayer and with Tile file support ************************************ */
422
423 /* naming rules:
424  * - parse name from right to left
425  * - last character is channel ID, 1 char like 'A' 'R' 'G' 'B' 'X' 'Y' 'Z' 'W' 'U' 'V'
426  * - separated with a dot; the Pass name (like "Depth", "Color", "Diffuse" or "Combined")
427  * - separated with a dot: the Layer name (like "Lamp1" or "Walls" or "Characters")
428  */
429
430 static ListBase exrhandles = {NULL, NULL};
431
432 typedef struct ExrHandle {
433         struct ExrHandle *next, *prev;
434
435         std::ifstream *std_istream;
436         StdIFStream *exr_istream;
437         InputFile *ifile;
438
439         std::ofstream *std_ostream;
440         StdOFStream *exr_ostream;
441         TiledOutputFile *tofile;
442         OutputFile *ofile;
443
444         int tilex, tiley;
445         int width, height;
446         int mipmap;
447
448         ListBase channels;  /* flattened out, ExrChannel */
449         ListBase layers;    /* hierarchical, pointing in end to ExrChannel */
450 } ExrHandle;
451
452 /* flattened out channel */
453 typedef struct ExrChannel {
454         struct ExrChannel *next, *prev;
455
456         char name[EXR_TOT_MAXNAME + 1];  /* full name of layer+pass */
457         int xstride, ystride;            /* step to next pixel, to next scanline */
458         float *rect;                     /* first pointer to write in */
459         char chan_id;                    /* quick lookup of channel char */
460 } ExrChannel;
461
462
463 /* hierarchical; layers -> passes -> channels[] */
464 typedef struct ExrPass {
465         struct ExrPass *next, *prev;
466         char name[EXR_PASS_MAXNAME];
467         int totchan;
468         float *rect;
469         struct ExrChannel *chan[EXR_PASS_MAXCHAN];
470         char chan_id[EXR_PASS_MAXCHAN];
471 } ExrPass;
472
473 typedef struct ExrLayer {
474         struct ExrLayer *next, *prev;
475         char name[EXR_LAY_MAXNAME + 1];
476         ListBase passes;
477 } ExrLayer;
478
479 /* ********************** */
480
481 void *IMB_exr_get_handle(void)
482 {
483         ExrHandle *data = (ExrHandle *)MEM_callocN(sizeof(ExrHandle), "exr handle");
484         BLI_addtail(&exrhandles, data);
485         return data;
486 }
487
488 /* adds flattened ExrChannels */
489 /* xstride, ystride and rect can be done in set_channel too, for tile writing */
490 void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect)
491 {
492         ExrHandle *data = (ExrHandle *)handle;
493         ExrChannel *echan;
494
495         echan = (ExrChannel *)MEM_callocN(sizeof(ExrChannel), "exr tile channel");
496
497         if (layname) {
498                 char lay[EXR_LAY_MAXNAME + 1], pass[EXR_PASS_MAXNAME + 1];
499                 BLI_strncpy(lay, layname, EXR_LAY_MAXNAME);
500                 BLI_strncpy(pass, passname, EXR_PASS_MAXNAME);
501
502                 BLI_snprintf(echan->name, sizeof(echan->name), "%s.%s", lay, pass);
503         }
504         else {
505                 BLI_strncpy(echan->name, passname, EXR_TOT_MAXNAME - 1);
506         }
507
508         echan->xstride = xstride;
509         echan->ystride = ystride;
510         echan->rect = rect;
511
512         // printf("added channel %s\n", echan->name);
513         BLI_addtail(&data->channels, echan);
514 }
515
516 /* only used for writing temp. render results (not image files) */
517 int IMB_exr_begin_write(void *handle, const char *filename, int width, int height, int compress)
518 {
519         ExrHandle *data = (ExrHandle *)handle;
520         Header header(width, height);
521         ExrChannel *echan;
522
523         data->width = width;
524         data->height = height;
525
526         for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next)
527                 header.channels().insert(echan->name, Channel(Imf::FLOAT));
528
529         openexr_header_compression(&header, compress);
530         // openexr_header_metadata(&header, ibuf); // no imbuf. cant write
531         /* header.lineOrder() = DECREASING_Y; this crashes in windows for file read! */
532
533         header.insert("BlenderMultiChannel", StringAttribute("Blender V2.55.1 and newer"));
534
535         /* avoid crash/abort when we don't have permission to write here */
536         /* manually create ofstream, so we can handle utf-8 filepaths on windows */
537         try {
538                 data->std_ostream = new std::ofstream();
539                 openexr_ofstream_open(*(data->std_ostream), filename);
540
541                 data->exr_ostream = new StdOFStream(*(data->std_ostream), filename);
542                 data->ofile = new OutputFile(*(data->exr_ostream), header);
543         }
544         catch (const std::exception &exc) {
545                 std::cerr << "IMB_exr_begin_write: ERROR: " << exc.what() << std::endl;
546
547                 delete data->ofile;
548                 delete data->exr_ostream;
549                 delete data->std_ostream;
550
551                 data->ofile = NULL;
552                 data->exr_ostream = NULL;
553                 data->std_ostream = NULL;
554         }
555
556         return (data->ofile != NULL);
557 }
558
559 void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley)
560 {
561         ExrHandle *data = (ExrHandle *)handle;
562         Header header(width, height);
563         ExrChannel *echan;
564
565         data->tilex = tilex;
566         data->tiley = tiley;
567         data->width = width;
568         data->height = height;
569         data->mipmap = mipmap;
570
571         for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next)
572                 header.channels().insert(echan->name, Channel(Imf::FLOAT));
573
574         header.setTileDescription(TileDescription(tilex, tiley, (mipmap) ? MIPMAP_LEVELS : ONE_LEVEL));
575         header.lineOrder() = RANDOM_Y;
576         header.compression() = RLE_COMPRESSION;
577
578         header.insert("BlenderMultiChannel", StringAttribute("Blender V2.43"));
579
580         /* avoid crash/abort when we don't have permission to write here */
581         /* manually create ofstream, so we can handle utf-8 filepaths on windows */
582         try {
583                 data->std_ostream = new std::ofstream();
584                 openexr_ofstream_open(*(data->std_ostream), filename);
585
586                 data->exr_ostream = new StdOFStream(*(data->std_ostream), filename);
587                 data->tofile = new TiledOutputFile(*(data->exr_ostream), header);
588         }
589         catch (const std::exception &exc) {
590                 delete data->tofile;
591                 delete data->exr_ostream;
592                 delete data->std_ostream;
593
594                 data->tofile = NULL;
595                 data->exr_ostream = NULL;
596                 data->std_ostream = NULL;
597         }
598 }
599
600 /* read from file */
601 int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height)
602 {
603         ExrHandle *data = (ExrHandle *)handle;
604
605         if (BLI_exists(filename) && BLI_file_size(filename) > 32) {   /* 32 is arbitrary, but zero length files crashes exr */
606                 /* avoid crash/abort when we don't have permission to write here */
607                 /* manually create ofstream, so we can handle utf-8 filepaths on windows */
608                 try {
609                         data->std_istream = new std::ifstream();
610                         openexr_ifstream_open(*(data->std_istream), filename);
611
612                         data->exr_istream = new StdIFStream(*(data->std_istream), filename);
613                         data->ifile = new InputFile(*(data->exr_istream));
614                 }
615                 catch (const std::exception &exc) {
616                         delete data->ifile;
617                         delete data->exr_istream;
618                         delete data->std_istream;
619
620                         data->ifile = NULL;
621                         data->exr_istream = NULL;
622                         data->std_istream = NULL;
623                 }
624
625                 if (data->ifile) {
626                         Box2i dw = data->ifile->header().dataWindow();
627                         data->width = *width  = dw.max.x - dw.min.x + 1;
628                         data->height = *height = dw.max.y - dw.min.y + 1;
629
630                         const ChannelList &channels = data->ifile->header().channels();
631
632                         for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
633                                 IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL);
634
635                         return 1;
636                 }
637         }
638         return 0;
639 }
640
641 /* still clumsy name handling, layers/channels can be ordered as list in list later */
642 void IMB_exr_set_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect)
643 {
644         ExrHandle *data = (ExrHandle *)handle;
645         ExrChannel *echan;
646         char name[EXR_TOT_MAXNAME + 1];
647
648         if (layname) {
649                 char lay[EXR_LAY_MAXNAME + 1], pass[EXR_PASS_MAXNAME + 1];
650                 BLI_strncpy(lay, layname, EXR_LAY_MAXNAME);
651                 BLI_strncpy(pass, passname, EXR_PASS_MAXNAME);
652
653                 BLI_snprintf(name, sizeof(name), "%s.%s", lay, pass);
654         }
655         else
656                 BLI_strncpy(name, passname, EXR_TOT_MAXNAME - 1);
657
658         echan = (ExrChannel *)BLI_findstring(&data->channels, name, offsetof(ExrChannel, name));
659
660         if (echan) {
661                 echan->xstride = xstride;
662                 echan->ystride = ystride;
663                 echan->rect = rect;
664         }
665         else
666                 printf("IMB_exrtile_set_channel error %s\n", name);
667 }
668
669 void IMB_exrtile_clear_channels(void *handle)
670 {
671         ExrHandle *data = (ExrHandle *)handle;
672         BLI_freelistN(&data->channels);
673 }
674
675 void IMB_exrtile_write_channels(void *handle, int partx, int party, int level)
676 {
677         ExrHandle *data = (ExrHandle *)handle;
678         FrameBuffer frameBuffer;
679         ExrChannel *echan;
680
681         for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
682                 float *rect = echan->rect - echan->xstride * partx - echan->ystride * party;
683
684                 frameBuffer.insert(echan->name, Slice(Imf::FLOAT,  (char *)rect,
685                                                       echan->xstride * sizeof(float), echan->ystride * sizeof(float)));
686         }
687
688         data->tofile->setFrameBuffer(frameBuffer);
689
690         try {
691                 // printf("write tile %d %d\n", partx/data->tilex, party/data->tiley);
692                 data->tofile->writeTile(partx / data->tilex, party / data->tiley, level);
693         }
694         catch (const std::exception &exc) {
695                 std::cerr << "OpenEXR-writeTile: ERROR: " << exc.what() << std::endl;
696         }
697 }
698
699 void IMB_exr_write_channels(void *handle)
700 {
701         ExrHandle *data = (ExrHandle *)handle;
702         FrameBuffer frameBuffer;
703         ExrChannel *echan;
704
705         if (data->channels.first) {
706                 for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
707                         /* last scanline, stride negative */
708                         float *rect = echan->rect + echan->xstride * (data->height - 1) * data->width;
709
710                         frameBuffer.insert(echan->name, Slice(Imf::FLOAT,  (char *)rect,
711                                                               echan->xstride * sizeof(float), -echan->ystride * sizeof(float)));
712                 }
713
714                 data->ofile->setFrameBuffer(frameBuffer);
715                 try {
716                         data->ofile->writePixels(data->height);
717                 }
718                 catch (const std::exception &exc) {
719                         std::cerr << "OpenEXR-writePixels: ERROR: " << exc.what() << std::endl;
720                 }
721         }
722         else {
723                 printf("Error: attempt to save MultiLayer without layers.\n");
724         }
725 }
726
727 void IMB_exr_read_channels(void *handle)
728 {
729         ExrHandle *data = (ExrHandle *)handle;
730         FrameBuffer frameBuffer;
731         ExrChannel *echan;
732
733         /* check if exr was saved with previous versions of blender which flipped images */
734         const StringAttribute *ta = data->ifile->header().findTypedAttribute <StringAttribute> ("BlenderMultiChannel");
735         short flip = (ta && strncmp(ta->value().c_str(), "Blender V2.43", 13) == 0); /* 'previous multilayer attribute, flipped */
736
737         for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
738
739                 if (echan->rect) {
740                         if (flip)
741                                 frameBuffer.insert(echan->name, Slice(Imf::FLOAT,  (char *)echan->rect,
742                                                                       echan->xstride * sizeof(float), echan->ystride * sizeof(float)));
743                         else
744                                 frameBuffer.insert(echan->name, Slice(Imf::FLOAT,  (char *)(echan->rect + echan->xstride * (data->height - 1) * data->width),
745                                                                       echan->xstride * sizeof(float), -echan->ystride * sizeof(float)));
746                 }
747                 else
748                         printf("warning, channel with no rect set %s\n", echan->name);
749         }
750
751         data->ifile->setFrameBuffer(frameBuffer);
752
753         try {
754                 data->ifile->readPixels(0, data->height - 1);
755         }
756         catch (const std::exception &exc) {
757                 std::cerr << "OpenEXR-readPixels: ERROR: " << exc.what() << std::endl;
758         }
759 }
760
761 void IMB_exr_multilayer_convert(void *handle, void *base,
762                                 void * (*addlayer)(void *base, const char *str),
763                                 void (*addpass)(void *base, void *lay, const char *str,
764                                                 float *rect, int totchan, const char *chan_id))
765 {
766         ExrHandle *data = (ExrHandle *)handle;
767         ExrLayer *lay;
768         ExrPass *pass;
769
770         if (data->layers.first == NULL) {
771                 printf("cannot convert multilayer, no layers in handle\n");
772                 return;
773         }
774
775         for (lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) {
776                 void *laybase = addlayer(base, lay->name);
777                 if (laybase) {
778                         for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) {
779                                 addpass(base, laybase, pass->name, pass->rect, pass->totchan, pass->chan_id);
780                                 pass->rect = NULL;
781                         }
782                 }
783         }
784 }
785
786
787 void IMB_exr_close(void *handle)
788 {
789         ExrHandle *data = (ExrHandle *)handle;
790         ExrLayer *lay;
791         ExrPass *pass;
792
793         delete data->ifile;
794         delete data->exr_istream;
795         delete data->std_istream;
796
797         delete data->ofile;
798         delete data->tofile;
799         delete data->exr_ostream;
800         delete data->std_ostream;
801
802         data->ifile = NULL;
803         data->exr_istream = NULL;
804         data->std_istream = NULL;
805
806         data->ofile = NULL;
807         data->tofile = NULL;
808         data->exr_ostream = NULL;
809         data->std_ostream = NULL;
810
811         BLI_freelistN(&data->channels);
812
813         for (lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) {
814                 for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next)
815                         if (pass->rect)
816                                 MEM_freeN(pass->rect);
817                 BLI_freelistN(&lay->passes);
818         }
819         BLI_freelistN(&data->layers);
820
821         BLI_remlink(&exrhandles, data);
822         MEM_freeN(data);
823 }
824
825 /* ********* */
826
827 /* get a substring from the end of the name, separated by '.' */
828 static int imb_exr_split_token(const char *str, const char *end, const char **token)
829 {
830         int maxlen = end - str;
831         int len = 0;
832         while (len < maxlen && *(end - len - 1) != '.') {
833                 len++;
834         }
835
836         *token = end - len;
837         return len;
838 }
839
840 static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *passname)
841 {
842         const char *name = echan->name;
843         const char *end = name + strlen(name);
844         const char *token;
845         char tokenbuf[EXR_TOT_MAXNAME];
846         int len;
847
848         /* last token is single character channel identifier */
849         len = imb_exr_split_token(name, end, &token);
850         if (len == 0) {
851                 printf("multilayer read: bad channel name: %s\n", name);
852                 return 0;
853         }
854         else if (len > 1) {
855                 BLI_strncpy(tokenbuf, token, len);
856                 printf("multilayer read: channel token too long: %s\n", tokenbuf);
857                 return 0;
858         }
859         echan->chan_id = token[0];
860         end -= len + 1; /* +1 to skip '.' separator */
861
862         /* second token is pass name */
863         len = imb_exr_split_token(name, end, &token);
864         if (len == 0) {
865                 printf("multilayer read: bad channel name: %s\n", name);
866                 return 0;
867         }
868         BLI_strncpy(passname, token, len + 1);
869         end -= len + 1; /* +1 to skip '.' separator */
870
871         /* all preceding tokens combined as layer name */
872         if (end > name)
873                 BLI_strncpy(layname, name, (int)(end - name) + 1);
874         else
875                 layname[0] = '\0';
876
877         return 1;
878 }
879
880 static ExrLayer *imb_exr_get_layer(ListBase *lb, char *layname)
881 {
882         ExrLayer *lay = (ExrLayer *)BLI_findstring(lb, layname, offsetof(ExrLayer, name));
883
884         if (lay == NULL) {
885                 lay = (ExrLayer *)MEM_callocN(sizeof(ExrLayer), "exr layer");
886                 BLI_addtail(lb, lay);
887                 BLI_strncpy(lay->name, layname, EXR_LAY_MAXNAME);
888         }
889
890         return lay;
891 }
892
893 static ExrPass *imb_exr_get_pass(ListBase *lb, char *passname)
894 {
895         ExrPass *pass = (ExrPass *)BLI_findstring(lb, passname, offsetof(ExrPass, name));
896
897         if (pass == NULL) {
898                 pass = (ExrPass *)MEM_callocN(sizeof(ExrPass), "exr pass");
899
900                 if (strcmp(passname, "Combined") == 0)
901                         BLI_addhead(lb, pass);
902                 else
903                         BLI_addtail(lb, pass);
904         }
905
906         BLI_strncpy(pass->name, passname, EXR_LAY_MAXNAME);
907
908         return pass;
909 }
910
911 /* creates channels, makes a hierarchy and assigns memory to channels */
912 static ExrHandle *imb_exr_begin_read_mem(InputFile *file, int width, int height)
913 {
914         ExrLayer *lay;
915         ExrPass *pass;
916         ExrChannel *echan;
917         ExrHandle *data = (ExrHandle *)IMB_exr_get_handle();
918         int a;
919         char layname[EXR_TOT_MAXNAME], passname[EXR_TOT_MAXNAME];
920
921         data->ifile = file;
922         data->width = width;
923         data->height = height;
924
925         const ChannelList &channels = data->ifile->header().channels();
926
927         for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
928                 IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL);
929
930         /* now try to sort out how to assign memory to the channels */
931         /* first build hierarchical layer list */
932         for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
933                 if (imb_exr_split_channel_name(echan, layname, passname) ) {
934                         ExrLayer *lay = imb_exr_get_layer(&data->layers, layname);
935                         ExrPass *pass = imb_exr_get_pass(&lay->passes, passname);
936
937                         pass->chan[pass->totchan] = echan;
938                         pass->totchan++;
939                         if (pass->totchan >= EXR_PASS_MAXCHAN)
940                                 break;
941                 }
942         }
943         if (echan) {
944                 printf("error, too many channels in one pass: %s\n", echan->name);
945                 IMB_exr_close(data);
946                 return NULL;
947         }
948
949         /* with some heuristics, try to merge the channels in buffers */
950         for (lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) {
951                 for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) {
952                         if (pass->totchan) {
953                                 pass->rect = (float *)MEM_mapallocN(width * height * pass->totchan * sizeof(float), "pass rect");
954                                 if (pass->totchan == 1) {
955                                         echan = pass->chan[0];
956                                         echan->rect = pass->rect;
957                                         echan->xstride = 1;
958                                         echan->ystride = width;
959                                         pass->chan_id[0] = echan->chan_id;
960                                 }
961                                 else {
962                                         char lookup[256];
963
964                                         memset(lookup, 0, sizeof(lookup));
965
966                                         /* we can have RGB(A), XYZ(W), UVA */
967                                         if (pass->totchan == 3 || pass->totchan == 4) {
968                                                 if (pass->chan[0]->chan_id == 'B' || pass->chan[1]->chan_id == 'B' ||  pass->chan[2]->chan_id == 'B') {
969                                                         lookup[(unsigned int)'R'] = 0;
970                                                         lookup[(unsigned int)'G'] = 1;
971                                                         lookup[(unsigned int)'B'] = 2;
972                                                         lookup[(unsigned int)'A'] = 3;
973                                                 }
974                                                 else if (pass->chan[0]->chan_id == 'Y' || pass->chan[1]->chan_id == 'Y' ||  pass->chan[2]->chan_id == 'Y') {
975                                                         lookup[(unsigned int)'X'] = 0;
976                                                         lookup[(unsigned int)'Y'] = 1;
977                                                         lookup[(unsigned int)'Z'] = 2;
978                                                         lookup[(unsigned int)'W'] = 3;
979                                                 }
980                                                 else {
981                                                         lookup[(unsigned int)'U'] = 0;
982                                                         lookup[(unsigned int)'V'] = 1;
983                                                         lookup[(unsigned int)'A'] = 2;
984                                                 }
985                                                 for (a = 0; a < pass->totchan; a++) {
986                                                         echan = pass->chan[a];
987                                                         echan->rect = pass->rect + lookup[(unsigned int)echan->chan_id];
988                                                         echan->xstride = pass->totchan;
989                                                         echan->ystride = width * pass->totchan;
990                                                         pass->chan_id[(unsigned int)lookup[(unsigned int)echan->chan_id]] = echan->chan_id;
991                                                 }
992                                         }
993                                         else { /* unknown */
994                                                 for (a = 0; a < pass->totchan; a++) {
995                                                         echan = pass->chan[a];
996                                                         echan->rect = pass->rect + a;
997                                                         echan->xstride = pass->totchan;
998                                                         echan->ystride = width * pass->totchan;
999                                                         pass->chan_id[a] = echan->chan_id;
1000                                                 }
1001                                         }
1002                                 }
1003                         }
1004                 }
1005         }
1006
1007         return data;
1008 }
1009
1010
1011 /* ********************************************************* */
1012
1013 /* debug only */
1014 static void exr_print_filecontents(InputFile *file)
1015 {
1016         const ChannelList &channels = file->header().channels();
1017
1018         for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
1019                 const Channel &channel = i.channel();
1020                 printf("OpenEXR-load: Found channel %s of type %d\n", i.name(), channel.type);
1021         }
1022 }
1023
1024 /* for non-multilayer, map  R G B A channel names to something that's in this file */
1025 static const char *exr_rgba_channelname(InputFile *file, const char *chan)
1026 {
1027         const ChannelList &channels = file->header().channels();
1028
1029         for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
1030                 /* const Channel &channel = i.channel(); */ /* Not used yet */
1031                 const char *str = i.name();
1032                 int len = strlen(str);
1033                 if (len) {
1034                         if (BLI_strcasecmp(chan, str + len - 1) == 0) {
1035                                 return str;
1036                         }
1037                 }
1038         }
1039         return chan;
1040 }
1041
1042 static int exr_has_zbuffer(InputFile *file)
1043 {
1044         return !(file->header().channels().findChannel("Z") == NULL);
1045 }
1046
1047 static int exr_has_alpha(InputFile *file)
1048 {
1049         return !(file->header().channels().findChannel("A") == NULL);
1050 }
1051
1052 static int exr_is_multilayer(InputFile *file)
1053 {
1054         const StringAttribute *comments = file->header().findTypedAttribute<StringAttribute>("BlenderMultiChannel");
1055         const ChannelList &channels = file->header().channels();
1056         std::set <std::string> layerNames;
1057
1058         channels.layers(layerNames);
1059
1060         if (comments || layerNames.size() > 1)
1061                 return 1;
1062
1063         return 0;
1064 }
1065
1066 struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags)
1067 {
1068         struct ImBuf *ibuf = NULL;
1069         InputFile *file = NULL;
1070
1071         if (imb_is_a_openexr(mem) == 0) return(NULL);
1072
1073         try
1074         {
1075                 Mem_IStream *membuf = new Mem_IStream(mem, size);
1076                 int is_multi;
1077                 file = new InputFile(*membuf);
1078
1079                 Box2i dw = file->header().dataWindow();
1080                 const int width  = dw.max.x - dw.min.x + 1;
1081                 const int height = dw.max.y - dw.min.y + 1;
1082
1083                 //printf("OpenEXR-load: image data window %d %d %d %d\n",
1084                 //         dw.min.x, dw.min.y, dw.max.x, dw.max.y);
1085
1086                 if (0) // debug
1087                         exr_print_filecontents(file);
1088
1089                 is_multi = exr_is_multilayer(file);
1090
1091                 /* do not make an ibuf when */
1092                 if (is_multi && !(flags & IB_test) && !(flags & IB_multilayer)) {
1093                         printf("Error: can't process EXR multilayer file\n");
1094                 }
1095                 else {
1096                         const int is_alpha = exr_has_alpha(file);
1097
1098                         ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, 0);
1099                         ibuf->ftype = OPENEXR;
1100
1101                         /* openEXR is linear as per EXR spec */
1102                         ibuf->profile = IB_PROFILE_LINEAR_RGB;
1103
1104                         if (!(flags & IB_test)) {
1105                                 if (is_multi) { /* only enters with IB_multilayer flag set */
1106                                         /* constructs channels for reading, allocates memory in channels */
1107                                         ExrHandle *handle = imb_exr_begin_read_mem(file, width, height);
1108                                         if (handle) {
1109                                                 IMB_exr_read_channels(handle);
1110                                                 ibuf->userdata = handle;         /* potential danger, the caller has to check for this! */
1111                                         }
1112                                 }
1113                                 else {
1114                                         FrameBuffer frameBuffer;
1115                                         float *first;
1116                                         int xstride = sizeof(float) * 4;
1117                                         int ystride = -xstride * width;
1118
1119                                         imb_addrectfloatImBuf(ibuf);
1120
1121                                         /* inverse correct first pixel for datawindow coordinates (- dw.min.y because of y flip) */
1122                                         first = ibuf->rect_float - 4 * (dw.min.x - dw.min.y * width);
1123                                         /* but, since we read y-flipped (negative y stride) we move to last scanline */
1124                                         first += 4 * (height - 1) * width;
1125
1126                                         frameBuffer.insert(exr_rgba_channelname(file, "R"),
1127                                                            Slice(Imf::FLOAT,  (char *) first, xstride, ystride));
1128                                         frameBuffer.insert(exr_rgba_channelname(file, "G"),
1129                                                            Slice(Imf::FLOAT,  (char *) (first + 1), xstride, ystride));
1130                                         frameBuffer.insert(exr_rgba_channelname(file, "B"),
1131                                                            Slice(Imf::FLOAT,  (char *) (first + 2), xstride, ystride));
1132
1133                                         /* 1.0 is fill value, this still neesd to be assigned even when (is_alpha == 0) */
1134                                         frameBuffer.insert(exr_rgba_channelname(file, "A"),
1135                                                            Slice(Imf::FLOAT,  (char *) (first + 3), xstride, ystride, 1, 1, 1.0f));
1136
1137                                         if (exr_has_zbuffer(file)) {
1138                                                 float *firstz;
1139
1140                                                 addzbuffloatImBuf(ibuf);
1141                                                 firstz = ibuf->zbuf_float - (dw.min.x - dw.min.y * width);
1142                                                 firstz += (height - 1) * width;
1143                                                 frameBuffer.insert("Z", Slice(Imf::FLOAT,  (char *)firstz, sizeof(float), -width * sizeof(float)));
1144                                         }
1145
1146                                         file->setFrameBuffer(frameBuffer);
1147                                         file->readPixels(dw.min.y, dw.max.y);
1148
1149                                         // XXX, ImBuf has no nice way to deal with this.
1150                                         // ideally IM_rect would be used when the caller wants a rect BUT
1151                                         // at the moment all functions use IM_rect.
1152                                         // Disabling this is ok because all functions should check if a rect exists and create one on demand.
1153                                         //
1154                                         // Disabling this because the sequencer frees immediate.
1155                                         //
1156                                         // if (flag & IM_rect)
1157                                         //     IMB_rect_from_float(ibuf);
1158
1159                                         /* file is no longer needed */
1160                                         delete file;
1161                                 }
1162                         }
1163                 }
1164                 return(ibuf);
1165         }
1166         catch (const std::exception &exc)
1167         {
1168                 std::cerr << exc.what() << std::endl;
1169                 if (ibuf) IMB_freeImBuf(ibuf);
1170                 delete file;
1171
1172                 return (0);
1173         }
1174
1175 }
1176
1177 void imb_initopenexr(void)
1178 {
1179         int num_threads = BLI_system_thread_count();
1180
1181         setGlobalThreadCount(num_threads);
1182 }
1183
1184 } // export "C"