10c90b1b891c4bc6f588f84d64ab06ab379b86d4
[blender-staging.git] / source / blender / imbuf / intern / cineon / dpxlib.c
1 /*
2  * Dpx image file format library routines.
3  *
4  * Copyright 1999 - 2002 David Hodson <hodsond@acm.org>
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation; either version 2 of the License, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * Contributor(s): Julien Enche.
21  *
22  */
23
24 /** \file blender/imbuf/intern/cineon/dpxlib.c
25  *  \ingroup imbcineon
26  */
27
28
29 #include "dpxlib.h"
30 #include "logmemfile.h"
31
32 #include <stdio.h>
33 #include <math.h>
34 #include <stdlib.h>
35 #include <time.h>
36 #include <sys/types.h>
37 #include <string.h>
38
39 #include "BLI_fileops.h"
40 #include "BLI_math_base.h"
41 #include "BLI_utildefines.h"
42
43 #include "MEM_guardedalloc.h"
44
45 /*
46  * For debug purpose
47  */
48
49 static int verbose = 0;
50
51 void dpxSetVerbose(int verbosity)
52 {
53         verbose = verbosity;
54 }
55
56
57 /*
58  * Headers
59  */
60
61 static void fillDpxMainHeader(LogImageFile *dpx, DpxMainHeader *header, const char *filename, const char *creator)
62 {
63         time_t fileClock;
64         struct tm *fileTime;
65
66         memset(header, 0, sizeof(DpxMainHeader));
67
68         /* --- File header --- */
69         header->fileHeader.magic_num = swap_uint(DPX_FILE_MAGIC, dpx->isMSB);
70         header->fileHeader.offset = swap_uint(dpx->element[0].dataOffset, dpx->isMSB);
71         strcpy(header->fileHeader.version, "v2.0");
72         header->fileHeader.file_size = swap_uint(dpx->element[0].dataOffset + dpx->height * getRowLength(dpx->width, dpx->element[0]), dpx->isMSB);
73         header->fileHeader.ditto_key = 0;
74         header->fileHeader.gen_hdr_size = swap_uint(sizeof(DpxFileHeader) + sizeof(DpxImageHeader) + sizeof(DpxOrientationHeader), dpx->isMSB);
75         header->fileHeader.ind_hdr_size = swap_uint(sizeof(DpxFilmHeader) + sizeof(DpxTelevisionHeader), dpx->isMSB);
76         header->fileHeader.user_data_size = DPX_UNDEFINED_U32;
77         strncpy(header->fileHeader.file_name, filename, 99);
78         header->fileHeader.file_name[99] = 0;
79         fileClock = time(0);
80         fileTime = localtime(&fileClock);
81         strftime(header->fileHeader.creation_date, 24, "%Y:%m:%d:%H:%M:%S%Z", fileTime);
82         header->fileHeader.creation_date[23] = 0;
83         strncpy(header->fileHeader.creator, creator, 99);
84         header->fileHeader.creator[99] = 0;
85         header->fileHeader.project[0] = 0;
86         header->fileHeader.copyright[0] = 0;
87         header->fileHeader.key = 0xFFFFFFFF;
88
89         /* --- Image header --- */
90         header->imageHeader.orientation = 0;
91         header->imageHeader.elements_per_image = swap_ushort(1, dpx->isMSB);
92         header->imageHeader.pixels_per_line = swap_uint(dpx->width, dpx->isMSB);
93         header->imageHeader.lines_per_element = swap_uint(dpx->height, dpx->isMSB);
94
95         /* Fills element */
96         header->imageHeader.element[0].data_sign = 0;
97         header->imageHeader.element[0].ref_low_data = swap_uint(dpx->element[0].refLowData, dpx->isMSB);
98         header->imageHeader.element[0].ref_low_quantity = swap_float(dpx->element[0].refLowQuantity, dpx->isMSB);
99         header->imageHeader.element[0].ref_high_data = swap_uint(dpx->element[0].refHighData, dpx->isMSB);
100         header->imageHeader.element[0].ref_high_quantity = swap_float(dpx->element[0].refHighQuantity, dpx->isMSB);
101         header->imageHeader.element[0].descriptor = dpx->element[0].descriptor;
102         header->imageHeader.element[0].transfer = dpx->element[0].transfer;
103         header->imageHeader.element[0].colorimetric = 0;
104         header->imageHeader.element[0].bits_per_sample = dpx->element[0].bitsPerSample;
105         header->imageHeader.element[0].packing = swap_ushort(dpx->element[0].packing, dpx->isMSB);
106         header->imageHeader.element[0].encoding = 0;
107         header->imageHeader.element[0].data_offset = swap_uint(dpx->element[0].dataOffset, dpx->isMSB);
108         header->imageHeader.element[0].line_padding = 0;
109         header->imageHeader.element[0].element_padding = 0;
110         header->imageHeader.element[0].description[0] = 0;
111
112         /* --- Orientation header --- */
113         /* we leave it blank */
114
115         /* --- Television header --- */
116         header->televisionHeader.time_code = DPX_UNDEFINED_U32;
117         header->televisionHeader.user_bits = DPX_UNDEFINED_U32;
118         header->televisionHeader.interlace = DPX_UNDEFINED_U8;
119         header->televisionHeader.field_number = DPX_UNDEFINED_U8;
120         header->televisionHeader.video_signal = DPX_UNDEFINED_U8;
121         header->televisionHeader.padding = DPX_UNDEFINED_U8;
122         header->televisionHeader.horizontal_sample_rate = DPX_UNDEFINED_R32;
123         header->televisionHeader.vertical_sample_rate = DPX_UNDEFINED_R32;
124         header->televisionHeader.frame_rate = DPX_UNDEFINED_R32;
125         header->televisionHeader.time_offset = DPX_UNDEFINED_R32;
126         header->televisionHeader.gamma = swap_float(dpx->gamma, dpx->isMSB);
127         header->televisionHeader.black_level = swap_float(dpx->referenceBlack, dpx->isMSB);
128         header->televisionHeader.black_gain = DPX_UNDEFINED_R32;
129         header->televisionHeader.breakpoint = DPX_UNDEFINED_R32;
130         header->televisionHeader.white_level = swap_float(dpx->referenceWhite, dpx->isMSB);
131         header->televisionHeader.integration_times = DPX_UNDEFINED_R32;
132 }
133
134 LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t bufferSize)
135 {
136         DpxMainHeader header;
137         LogImageFile *dpx = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__);
138         char *filename = (char *)byteStuff;
139         int i;
140
141         if (dpx == 0) {
142                 if (verbose) printf("DPX: Failed to malloc dpx file structure.\n");
143                 return 0;
144         }
145
146         /* zero the header */
147         memset(&header, 0, sizeof(DpxMainHeader));
148
149         /* for close routine */
150         dpx->file = 0;
151
152         if (fromMemory == 0) {
153                 /* byteStuff is then the filename */
154                 dpx->file = BLI_fopen(filename, "rb");
155                 if (dpx->file == 0) {
156                         if (verbose) printf("DPX: Failed to open file \"%s\".\n", filename);
157                         logImageClose(dpx);
158                         return 0;
159                 }
160                 /* not used in this case */
161                 dpx->memBuffer = 0;
162                 dpx->memCursor = 0;
163                 dpx->memBufferSize = 0;
164         }
165         else {
166                 dpx->memBuffer = (unsigned char *)byteStuff;
167                 dpx->memCursor = (unsigned char *)byteStuff;
168                 dpx->memBufferSize = bufferSize;
169         }
170
171         if (logimage_fread(&header, sizeof(header), 1, dpx) == 0) {
172                 if (verbose) printf("DPX: Not enough data for header in \"%s\".\n", byteStuff);
173                 logImageClose(dpx);
174                 return 0;
175         }
176
177         /* endianness determination */
178         if (header.fileHeader.magic_num == swap_uint(DPX_FILE_MAGIC, 1)) {
179                 dpx->isMSB = 1;
180                 if (verbose) printf("DPX: File is MSB.\n");
181         }
182         else if (header.fileHeader.magic_num == DPX_FILE_MAGIC) {
183                 dpx->isMSB = 0;
184                 if (verbose) printf("DPX: File is LSB.\n");
185         }
186         else {
187                 if (verbose) printf("DPX: Bad magic number %lu in \"%s\".\n",
188                                     (uintptr_t)header.fileHeader.magic_num, byteStuff);
189                 logImageClose(dpx);
190                 return 0;
191         }
192
193         dpx->srcFormat = format_DPX;
194         dpx->numElements = swap_ushort(header.imageHeader.elements_per_image, dpx->isMSB);
195         if (dpx->numElements == 0) {
196                 if (verbose) printf("DPX: Wrong number of elements: %d\n", dpx->numElements);
197                 logImageClose(dpx);
198                 return 0;
199         }
200
201         dpx->width = swap_uint(header.imageHeader.pixels_per_line, dpx->isMSB);
202         dpx->height = swap_uint(header.imageHeader.lines_per_element, dpx->isMSB);
203
204         if (dpx->width == 0 || dpx->height == 0) {
205                 if (verbose) printf("DPX: Wrong image dimension: %dx%d\n", dpx->width, dpx->height);
206                 logImageClose(dpx);
207                 return 0;
208         }
209
210         dpx->depth = 0;
211
212         for (i = 0; i < dpx->numElements; i++) {
213                 dpx->element[i].descriptor = header.imageHeader.element[i].descriptor;
214
215                 switch (dpx->element[i].descriptor) {
216                         case descriptor_Red:
217                         case descriptor_Green:
218                         case descriptor_Blue:
219                         case descriptor_Alpha:
220                         case descriptor_Luminance:
221                         case descriptor_Chrominance:
222                                 dpx->depth++;
223                                 dpx->element[i].depth = 1;
224                                 break;
225
226                         case descriptor_CbYCrY:
227                                 dpx->depth += 2;
228                                 dpx->element[i].depth = 2;
229                                 break;
230
231                         case descriptor_RGB:
232                         case descriptor_CbYCr:
233                         case descriptor_CbYACrYA:
234                                 dpx->depth += 3;
235                                 dpx->element[i].depth = 3;
236                                 break;
237
238                         case descriptor_RGBA:
239                         case descriptor_ABGR:
240                         case descriptor_CbYCrA:
241                                 dpx->depth += 4;
242                                 dpx->element[i].depth = 4;
243                                 break;
244
245                         case descriptor_Depth:
246                         case descriptor_Composite:
247                                 /* unsupported */
248                                 break;
249                 }
250
251                 if (dpx->depth == 0 || dpx->depth > 4) {
252                         if (verbose) printf("DPX: Unsupported image depth: %d\n", dpx->depth);
253                         logImageClose(dpx);
254                         return 0;
255                 }
256
257                 dpx->element[i].bitsPerSample = header.imageHeader.element[i].bits_per_sample;
258                 if (dpx->element[i].bitsPerSample != 1 && dpx->element[i].bitsPerSample != 8 &&
259                         dpx->element[i].bitsPerSample != 10 && dpx->element[i].bitsPerSample != 12 &&
260                         dpx->element[i].bitsPerSample != 16)
261                 {
262                         if (verbose) printf("DPX: Unsupported bitsPerSample for elements %d: %d\n", i, dpx->element[i].bitsPerSample);
263                         logImageClose(dpx);
264                         return 0;
265                 }
266
267                 dpx->element[i].maxValue = powf(2, dpx->element[i].bitsPerSample) - 1.0f;
268
269                 dpx->element[i].packing = swap_ushort(header.imageHeader.element[i].packing, dpx->isMSB);
270                 if (dpx->element[i].packing > 2) {
271                         if (verbose) printf("DPX: Unsupported packing for element %d: %d\n", i, dpx->element[i].packing);
272                         logImageClose(dpx);
273                         return 0;
274                 }
275
276                 /* Sometimes, the offset is not set correctly in the header */
277                 dpx->element[i].dataOffset = swap_uint(header.imageHeader.element[i].data_offset, dpx->isMSB);
278                 if (dpx->element[i].dataOffset == 0 && dpx->numElements == 1)
279                         dpx->element[i].dataOffset = swap_uint(header.fileHeader.offset, dpx->isMSB);
280
281                 if (dpx->element[i].dataOffset == 0) {
282                         if (verbose) printf("DPX: Image header is corrupted.\n");
283                         logImageClose(dpx);
284                         return 0;
285                 }
286
287                 dpx->element[i].transfer = header.imageHeader.element[i].transfer;
288
289                 /* if undefined, assign default */
290                 dpx->element[i].refLowData = swap_uint(header.imageHeader.element[i].ref_low_data, dpx->isMSB);
291                 dpx->element[i].refLowQuantity = swap_float(header.imageHeader.element[i].ref_low_quantity, dpx->isMSB);
292                 dpx->element[i].refHighData = swap_uint(header.imageHeader.element[i].ref_high_data, dpx->isMSB);
293                 dpx->element[i].refHighQuantity = swap_float(header.imageHeader.element[i].ref_high_quantity, dpx->isMSB);
294
295                 switch (dpx->element[i].descriptor) {
296                         case descriptor_Red:
297                         case descriptor_Green:
298                         case descriptor_Blue:
299                         case descriptor_Alpha:
300                         case descriptor_RGB:
301                         case descriptor_RGBA:
302                         case descriptor_ABGR:
303                                 if (dpx->element[i].refLowData == DPX_UNDEFINED_U32 || isnan(dpx->element[i].refLowData))
304                                         dpx->element[i].refLowData = 0;
305
306                                 if (dpx->element[i].refHighData == DPX_UNDEFINED_U32 || isnan(dpx->element[i].refHighData))
307                                         dpx->element[i].refHighData = (unsigned int)dpx->element[i].maxValue;
308
309                                 if (dpx->element[i].refLowQuantity == DPX_UNDEFINED_R32 || isnan(dpx->element[i].refLowQuantity))
310                                         dpx->element[i].refLowQuantity = 0.0f;
311
312                                 if (dpx->element[i].refHighQuantity == DPX_UNDEFINED_R32 || isnan(dpx->element[i].refHighQuantity)) {
313                                         if (dpx->element[i].transfer == transfer_PrintingDensity || dpx->element[i].transfer == transfer_Logarithmic)
314                                                 dpx->element[i].refHighQuantity = 2.048f;
315                                         else
316                                                 dpx->element[i].refHighQuantity = dpx->element[i].maxValue;
317                                 }
318
319                                 break;
320
321                         case descriptor_Luminance:
322                         case descriptor_Chrominance:
323                         case descriptor_CbYCrY:
324                         case descriptor_CbYCr:
325                         case descriptor_CbYACrYA:
326                         case descriptor_CbYCrA:
327                                 if (dpx->element[i].refLowData == DPX_UNDEFINED_U32 || isnan(dpx->element[i].refLowData))
328                                         dpx->element[i].refLowData = 16.0f / 255.0f * dpx->element[i].maxValue;
329
330                                 if (dpx->element[i].refHighData == DPX_UNDEFINED_U32 || isnan(dpx->element[i].refHighData))
331                                         dpx->element[i].refHighData = 235.0f / 255.0f * dpx->element[i].maxValue;
332
333                                 if (dpx->element[i].refLowQuantity == DPX_UNDEFINED_R32 || isnan(dpx->element[i].refLowQuantity))
334                                         dpx->element[i].refLowQuantity = 0.0f;
335
336                                 if (dpx->element[i].refHighQuantity == DPX_UNDEFINED_R32 || isnan(dpx->element[i].refHighQuantity))
337                                         dpx->element[i].refHighQuantity = 0.7f;
338
339                                 break;
340
341                         default:
342                                 break;
343                 }
344         }
345
346         dpx->referenceBlack = swap_float(header.televisionHeader.black_level, dpx->isMSB);
347         dpx->referenceWhite = swap_float(header.televisionHeader.white_level, dpx->isMSB);
348         dpx->gamma = swap_float(header.televisionHeader.gamma, dpx->isMSB);
349
350         if ((dpx->referenceBlack == DPX_UNDEFINED_R32 || isnan(dpx->referenceBlack)) ||
351             (dpx->referenceWhite == DPX_UNDEFINED_R32 || dpx->referenceWhite <= dpx->referenceBlack || isnan(dpx->referenceWhite)) ||
352             (dpx->gamma == DPX_UNDEFINED_R32 || dpx->gamma <= 0 || isnan(dpx->gamma)))
353         {
354                 dpx->referenceBlack = 95.0f / 1023.0f * dpx->element[0].maxValue;
355                 dpx->referenceWhite = 685.0f / 1023.0f * dpx->element[0].maxValue;
356                 dpx->gamma = 1.7f;
357         }
358
359         if (verbose) {
360                 printf("size %d x %d x %d elements\n", dpx->width, dpx->height, dpx->numElements);
361                 for (i = 0; i < dpx->numElements; i++) {
362                         printf(" Element %d:\n", i);
363                         printf("  Bits per sample: %d\n", dpx->element[i].bitsPerSample);
364                         printf("  Depth: %d\n", dpx->element[i].depth);
365                         printf("  Transfer characteristics: %d\n", dpx->element[i].transfer);
366                         printf("  Packing: %d\n", dpx->element[i].packing);
367                         printf("  Descriptor: %d\n", dpx->element[i].descriptor);
368                         printf("  Data offset: %u\n", dpx->element[i].dataOffset);
369                         printf("  Reference low data: %u\n", dpx->element[i].refLowData);
370                         printf("  Reference low quantity: %f\n", dpx->element[i].refLowQuantity);
371                         printf("  Reference high data: %u\n", dpx->element[i].refHighData);
372                         printf("  Reference high quantity: %f\n", dpx->element[i].refHighQuantity);
373                         printf("\n");
374                 }
375
376                 printf("Gamma: %f\n", dpx->gamma);
377                 printf("Reference black: %f\n", dpx->referenceBlack);
378                 printf("Reference white: %f\n", dpx->referenceWhite);
379                 printf("----------------------------\n");
380         }
381         return dpx;
382 }
383
384 LogImageFile *dpxCreate(const char *filename, int width, int height, int bitsPerSample, int hasAlpha,
385                         int isLogarithmic, int referenceWhite, int referenceBlack, float gamma,
386                         const char *creator)
387 {
388         DpxMainHeader header;
389         const char *shortFilename = 0;
390         unsigned char pad[6044];
391
392         LogImageFile *dpx = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__);
393         if (dpx == 0) {
394                 if (verbose) printf("DPX: Failed to malloc dpx file structure.\n");
395                 return 0;
396         }
397
398         dpx->width = width;
399         dpx->height = height;
400         dpx->element[0].bitsPerSample = bitsPerSample;
401         dpx->element[0].dataOffset = 8092;
402         dpx->element[0].maxValue = powf(2, dpx->element[0].bitsPerSample) - 1.0f;
403         dpx->isMSB = 1;
404         dpx->numElements = 1;
405
406         switch (bitsPerSample) {
407                 case 8:
408                 case 16:
409                         dpx->element[0].packing = 0;
410                         break;
411
412                 case 10:
413                 case 12:
414                         /* Packed Type A padding is the most common 10/12 bits format */
415                         dpx->element[0].packing = 1;
416                         break;
417
418                 default:
419                         if (verbose) printf("DPX: bitsPerSample not supported: %d\n", bitsPerSample);
420                         logImageClose(dpx);
421                         return 0;
422         }
423
424         if (hasAlpha == 0) {
425                 dpx->depth = 3;
426                 dpx->element[0].depth = 3;
427                 dpx->element[0].descriptor = descriptor_RGB;
428         }
429         else {
430                 dpx->depth = 4;
431                 dpx->element[0].depth = 4;
432                 dpx->element[0].descriptor = descriptor_RGBA;
433         }
434
435         if (isLogarithmic == 0) {
436                 dpx->element[0].transfer = transfer_Linear;
437                 dpx->element[0].refHighQuantity = dpx->element[0].maxValue;
438         }
439         else {
440                 dpx->element[0].transfer = transfer_PrintingDensity;
441                 dpx->element[0].refHighQuantity = 2.048f;
442
443         }
444
445         dpx->element[0].refLowQuantity = 0;
446         dpx->element[0].refLowData = 0;
447         dpx->element[0].refHighData = dpx->element[0].maxValue;
448
449         if (referenceWhite > 0)
450                 dpx->referenceWhite = referenceWhite;
451         else
452                 dpx->referenceWhite = 685.0f / 1023.0f * dpx->element[0].maxValue;
453
454         if (referenceBlack > 0)
455                 dpx->referenceBlack = referenceBlack;
456         else
457                 dpx->referenceBlack = 95.0f / 1023.0f * dpx->element[0].maxValue;
458
459         if (gamma > 0.0f)
460                 dpx->gamma = gamma;
461         else
462                 dpx->gamma = 1.7f;
463
464
465         shortFilename = strrchr(filename, '/');
466         if (shortFilename == 0)
467                 shortFilename = filename;
468         else
469                 shortFilename++;
470
471         dpx->file = BLI_fopen(filename, "wb");
472
473         if (dpx->file == 0) {
474                 if (verbose) printf("DPX: Couldn't open file %s\n", filename);
475                 logImageClose(dpx);
476                 return 0;
477         }
478
479         fillDpxMainHeader(dpx, &header, shortFilename, creator);
480
481         if (fwrite(&header, sizeof(header), 1, dpx->file) == 0) {
482                 if (verbose) printf("DPX: Couldn't write image header\n");
483                 logImageClose(dpx);
484                 return 0;
485         }
486
487         /* Header should be rounded to next 8k block
488          * 6044 = 8092 - sizeof(DpxMainHeader) */
489         memset(&pad, 0, 6044);
490         if (fwrite(&pad, 6044, 1, dpx->file) == 0) {
491                 if (verbose) printf("DPX: Couldn't write image header\n");
492                 logImageClose(dpx);
493                 return 0;
494         }
495
496         return dpx;
497 }