code cleanup: use NULL rather then 0 for pointers, and make vars static where possible.
[blender-staging.git] / source / blender / imbuf / intern / cineon / cineonlib.c
1 /*
2  * Cineon image file format library routines.
3  *
4  * Copyright 1999,2000,2001 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/cineonlib.c
25  *  \ingroup imbcineon
26  */
27
28
29 #include "cineonlib.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 "logImageLib.h"
44
45 #include "MEM_guardedalloc.h"
46
47 /*
48  * For debug purpose
49  */
50
51 static int verbose = 0;
52
53 void cineonSetVerbose(int verbosity)
54 {
55         verbose = verbosity;
56 }
57
58 static void fillCineonMainHeader(LogImageFile *cineon, CineonMainHeader *header,
59                                  const char *filename, const char *creator)
60 {
61         time_t fileClock;
62         struct tm *fileTime;
63         int i;
64
65         memset(header, 0, sizeof(CineonMainHeader));
66
67         /* --- File header --- */
68         header->fileHeader.magic_num = swap_uint(CINEON_FILE_MAGIC, cineon->isMSB);
69         header->fileHeader.offset = swap_uint(cineon->element[0].dataOffset, cineon->isMSB);
70         header->fileHeader.gen_hdr_size = swap_uint(sizeof(CineonFileHeader) + sizeof(CineonImageHeader) +
71                                                     sizeof(CineonOriginationHeader), cineon->isMSB);
72         header->fileHeader.ind_hdr_size = 0;
73         header->fileHeader.user_data_size = 0;
74         header->fileHeader.file_size = swap_uint(cineon->element[0].dataOffset + cineon->height * getRowLength(cineon->width, cineon->element[0]), cineon->isMSB);
75         strcpy(header->fileHeader.version, "v4.5");
76         strncpy(header->fileHeader.file_name, filename, 99);
77         header->fileHeader.file_name[99] = 0;
78         fileClock = time(NULL);
79         fileTime = localtime(&fileClock);
80         strftime(header->fileHeader.creation_date, 12, "%Y:%m:%d", fileTime);
81         strftime(header->fileHeader.creation_time, 12, "%H:%M:%S%Z", fileTime);
82         header->fileHeader.creation_time[11] = 0;
83
84         /* --- Image header --- */
85         header->imageHeader.orientation = 0;
86         header->imageHeader.elements_per_image = cineon->depth;
87
88         for (i = 0; i < 3; i++) {
89                 header->imageHeader.element[i].descriptor1 = 0;
90                 header->imageHeader.element[i].descriptor2 = i;
91                 header->imageHeader.element[i].bits_per_sample = cineon->element[0].bitsPerSample;
92                 header->imageHeader.element[i].pixels_per_line = swap_uint(cineon->width, cineon->isMSB);
93                 header->imageHeader.element[i].lines_per_image = swap_uint(cineon->height, cineon->isMSB);
94                 header->imageHeader.element[i].ref_low_data = swap_uint(cineon->element[0].refLowData, cineon->isMSB);
95                 header->imageHeader.element[i].ref_low_quantity = swap_float(cineon->element[0].refLowQuantity, cineon->isMSB);
96                 header->imageHeader.element[i].ref_high_data = swap_uint(cineon->element[0].refHighData, cineon->isMSB);
97                 header->imageHeader.element[i].ref_high_quantity = swap_float(cineon->element[0].refHighQuantity, cineon->isMSB);
98         }
99
100         header->imageHeader.white_point_x = swap_float(0.0f, cineon->isMSB);
101         header->imageHeader.white_point_y = swap_float(0.0f, cineon->isMSB);
102         header->imageHeader.red_primary_x = swap_float(0.0f, cineon->isMSB);
103         header->imageHeader.red_primary_y = swap_float(0.0f, cineon->isMSB);
104         header->imageHeader.green_primary_x = swap_float(0.0f, cineon->isMSB);
105         header->imageHeader.green_primary_y = swap_float(0.0f, cineon->isMSB);
106         header->imageHeader.blue_primary_x = swap_float(0.0f, cineon->isMSB);
107         header->imageHeader.blue_primary_y = swap_float(0.0f, cineon->isMSB);
108         strncpy(header->imageHeader.label, creator, 199);
109         header->imageHeader.label[199] = 0;
110         header->imageHeader.interleave = 0;
111         header->imageHeader.data_sign = 0;
112         header->imageHeader.sense = 0;
113         header->imageHeader.line_padding = swap_uint(0, cineon->isMSB);
114         header->imageHeader.element_padding = swap_uint(0, cineon->isMSB);
115
116         switch (cineon->element[0].packing) {
117                 case 0:
118                         header->imageHeader.packing = 0;
119                         break;
120
121                 case 1:
122                         header->imageHeader.packing = 5;
123                         break;
124
125                 case 2:
126                         header->imageHeader.packing = 6;
127                         break;
128         }
129
130         /* --- Origination header --- */
131         /* we leave it blank */
132
133         /* --- Film header --- */
134         /* we leave it blank */
135 }
136
137 LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t bufferSize)
138 {
139         CineonMainHeader header;
140         LogImageFile *cineon = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__);
141         char *filename = (char *)byteStuff;
142         int i;
143         unsigned int dataOffset;
144
145         if (cineon == NULL) {
146                 if (verbose) printf("Cineon: Failed to malloc cineon file structure.\n");
147                 return NULL;
148         }
149
150         /* zero the header */
151         memset(&header, 0, sizeof(CineonMainHeader));
152
153         /* for close routine */
154         cineon->file = NULL;
155
156         if (fromMemory == 0) {
157                 /* byteStuff is then the filename */
158                 cineon->file = BLI_fopen(filename, "rb");
159                 if (cineon->file == NULL) {
160                         if (verbose) printf("Cineon: Failed to open file \"%s\".\n", filename);
161                         logImageClose(cineon);
162                         return NULL;
163                 }
164                 /* not used in this case */
165                 cineon->memBuffer = NULL;
166                 cineon->memCursor = NULL;
167                 cineon->memBufferSize = 0;
168         }
169         else {
170                 cineon->memBuffer = (unsigned char *)byteStuff;
171                 cineon->memCursor = (unsigned char *)byteStuff;
172                 cineon->memBufferSize = bufferSize;
173         }
174
175         if (logimage_fread(&header, sizeof(header), 1, cineon) == 0) {
176                 if (verbose) printf("Cineon: Not enough data for header in \"%s\".\n", byteStuff);
177                 logImageClose(cineon);
178                 return NULL;
179         }
180
181         /* endianness determination */
182         if (header.fileHeader.magic_num == swap_uint(CINEON_FILE_MAGIC, 1)) {
183                 cineon->isMSB = 1;
184                 if (verbose) printf("Cineon: File is MSB.\n");
185         }
186         else if (header.fileHeader.magic_num == CINEON_FILE_MAGIC) {
187                 cineon->isMSB = 0;
188                 if (verbose) printf("Cineon: File is LSB.\n");
189         }
190         else {
191                 if (verbose) printf("Cineon: Bad magic number %lu in \"%s\".\n",
192                                         (unsigned long)header.fileHeader.magic_num, byteStuff);
193                 logImageClose(cineon);
194                 return NULL;
195         }
196
197         cineon->width = swap_uint(header.imageHeader.element[0].pixels_per_line, cineon->isMSB);
198         cineon->height = swap_uint(header.imageHeader.element[0].lines_per_image, cineon->isMSB);
199
200         if (cineon->width == 0 || cineon->height == 0) {
201                 if (verbose) printf("Cineon: Wrong image dimension: %dx%d\n", cineon->width, cineon->height);
202                 logImageClose(cineon);
203                 return NULL;
204         }
205
206         cineon->depth = header.imageHeader.elements_per_image;
207         cineon->srcFormat = format_Cineon;
208
209         if (header.imageHeader.interleave == 0)
210                 cineon->numElements = 1;
211         else if (header.imageHeader.interleave == 2)
212                 cineon->numElements = header.imageHeader.elements_per_image;
213         else {
214                 if (verbose) printf("Cineon: Data interleave not supported: %d\n", header.imageHeader.interleave);
215                 logImageClose(cineon);
216                 return NULL;
217         }
218
219         if (cineon->depth == 1) {
220                 /* Grayscale image */
221                 cineon->element[0].descriptor = descriptor_Luminance;
222                 cineon->element[0].transfer = transfer_Linear;
223                 cineon->element[0].depth = 1;
224         }
225         else if (cineon->depth == 3) {
226                 /* RGB image */
227                 if (cineon->numElements == 1) {
228                         cineon->element[0].descriptor = descriptor_RGB;
229                         cineon->element[0].transfer = transfer_PrintingDensity;
230                         cineon->element[0].depth = 3;
231                 }
232                 else if (cineon->numElements == 3) {
233                         cineon->element[0].descriptor = descriptor_Red;
234                         cineon->element[0].transfer = transfer_PrintingDensity;
235                         cineon->element[0].depth = 1;
236                         cineon->element[1].descriptor = descriptor_Green;
237                         cineon->element[1].transfer = transfer_PrintingDensity;
238                         cineon->element[1].depth = 1;
239                         cineon->element[2].descriptor = descriptor_Blue;
240                         cineon->element[2].transfer = transfer_PrintingDensity;
241                         cineon->element[2].depth = 1;
242                 }
243         }
244         else {
245                 if (verbose) printf("Cineon: Cineon image depth unsupported: %d\n", cineon->depth);
246                 logImageClose(cineon);
247                 return NULL;
248         }
249
250         dataOffset = swap_uint(header.fileHeader.offset, cineon->isMSB);
251
252         for (i = 0; i < cineon->numElements; i++) {
253                 cineon->element[i].bitsPerSample = header.imageHeader.element[i].bits_per_sample;
254                 cineon->element[i].maxValue = powf(2, cineon->element[i].bitsPerSample) - 1.0f;
255                 cineon->element[i].refLowData = swap_uint(header.imageHeader.element[i].ref_low_data, cineon->isMSB);
256                 cineon->element[i].refLowQuantity = swap_float(header.imageHeader.element[i].ref_low_quantity, cineon->isMSB);
257                 cineon->element[i].refHighData = swap_uint(header.imageHeader.element[i].ref_high_data, cineon->isMSB);
258                 cineon->element[i].refHighQuantity = swap_float(header.imageHeader.element[i].ref_high_quantity, cineon->isMSB);
259
260                 switch (header.imageHeader.packing) {
261                         case 0:
262                                 cineon->element[i].packing = 0;
263                                 break;
264
265                         case 5:
266                                 cineon->element[i].packing = 1;
267                                 break;
268
269                         case 6:
270                                 cineon->element[i].packing = 2;
271                                 break;
272
273                         default:
274                                 /* Not supported */
275                                 if (verbose) printf("Cineon: packing unsupported: %d\n", header.imageHeader.packing);
276                                 logImageClose(cineon);
277                                 return NULL;
278                 }
279
280                 if (cineon->element[i].refLowData == CINEON_UNDEFINED_U32 || isnan(cineon->element[i].refLowData))
281                         cineon->element[i].refLowData = 0;
282
283                 if (cineon->element[i].refHighData == CINEON_UNDEFINED_U32 || isnan(cineon->element[i].refHighData))
284                         cineon->element[i].refHighData = (unsigned int)cineon->element[i].maxValue;
285
286                 if (cineon->element[i].refLowQuantity == CINEON_UNDEFINED_R32 || isnan(cineon->element[i].refLowQuantity))
287                         cineon->element[i].refLowQuantity = 0.0f;
288
289                 if (cineon->element[i].refHighQuantity == CINEON_UNDEFINED_R32 || isnan(cineon->element[i].refHighQuantity)) {
290                         if (cineon->element[i].transfer == transfer_PrintingDensity)
291                                 cineon->element[i].refHighQuantity = 2.048f;
292                         else
293                                 cineon->element[i].refHighQuantity = cineon->element[i].maxValue;
294                 }
295
296                 cineon->element[i].dataOffset = dataOffset;
297                 dataOffset += cineon->height * getRowLength(cineon->width, cineon->element[i]);
298         }
299
300         cineon->referenceBlack = 95.0f / 1023.0f * cineon->element[0].maxValue;
301         cineon->referenceWhite = 685.0f / 1023.0f * cineon->element[0].maxValue;
302         cineon->gamma = 1.7f;
303
304         if (verbose) {
305                 printf("size %d x %d x %d elements\n", cineon->width, cineon->height, cineon->numElements);
306                 for (i = 0; i < cineon->numElements; i++) {
307                         printf(" Element %d:\n", i);
308                         printf("  Bits per sample: %d\n", cineon->element[i].bitsPerSample);
309                         printf("  Depth: %d\n", cineon->element[i].depth);
310                         printf("  Transfer characteristics: %d\n", cineon->element[i].transfer);
311                         printf("  Packing: %d\n", cineon->element[i].packing);
312                         printf("  Descriptor: %d\n", cineon->element[i].descriptor);
313                         printf("  Data offset: %u\n", cineon->element[i].dataOffset);
314                         printf("  Reference low data: %u\n", cineon->element[i].refLowData);
315                         printf("  Reference low quantity: %f\n", cineon->element[i].refLowQuantity);
316                         printf("  Reference high data: %u\n", cineon->element[i].refHighData);
317                         printf("  Reference high quantity: %f\n", cineon->element[i].refHighQuantity);
318                         printf("\n");
319                 }
320
321                 printf("Gamma: %f\n", cineon->gamma);
322                 printf("Reference black: %f\n", cineon->referenceBlack);
323                 printf("Reference white: %f\n", cineon->referenceWhite);
324                 printf("----------------------------\n");
325         }
326         return cineon;
327 }
328
329 LogImageFile *cineonCreate(const char *filename, int width, int height, int bitsPerSample, const char *creator)
330 {
331         CineonMainHeader header;
332         const char *shortFilename = NULL;
333         /* unsigned char pad[6044]; */
334
335         LogImageFile *cineon = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__);
336         if (cineon == NULL) {
337                 if (verbose) printf("cineon: Failed to malloc cineon file structure.\n");
338                 return NULL;
339         }
340
341         /* Only 10 bits Cineon are supported */
342         if (bitsPerSample != 10) {
343                 if (verbose) printf("cineon: Only 10 bits Cineon are supported.\n");
344                 logImageClose(cineon);
345                 return NULL;
346         }
347
348         cineon->width = width;
349         cineon->height = height;
350         cineon->element[0].bitsPerSample = 10;
351         cineon->element[0].dataOffset = sizeof(CineonMainHeader);
352         cineon->element[0].maxValue = 1023;
353         cineon->isMSB = 1;
354         cineon->numElements = 1;
355         cineon->element[0].packing = 1;
356         cineon->depth = 3;
357         cineon->element[0].depth = 3;
358         cineon->element[0].descriptor = descriptor_RGB;
359         cineon->element[0].transfer = transfer_PrintingDensity;
360         cineon->element[0].refHighQuantity = 2.048f;
361         cineon->element[0].refLowQuantity = 0;
362         cineon->element[0].refLowData = 0;
363         cineon->element[0].refHighData = cineon->element[0].maxValue;
364         cineon->referenceWhite = 685.0f;
365         cineon->referenceBlack = 95.0f;
366         cineon->gamma = 1.7f;
367
368         shortFilename = strrchr(filename, '/');
369         if (shortFilename == NULL)
370                 shortFilename = filename;
371         else
372                 shortFilename++;
373
374         cineon->file = BLI_fopen(filename, "wb");
375         if (cineon->file == NULL) {
376                 if (verbose) printf("cineon: Couldn't open file %s\n", filename);
377                 logImageClose(cineon);
378                 return NULL;
379         }
380
381         fillCineonMainHeader(cineon, &header, shortFilename, creator);
382
383         if (fwrite(&header, sizeof(header), 1, cineon->file) == 0) {
384                 if (verbose) printf("cineon: Couldn't write image header\n");
385                 logImageClose(cineon);
386                 return NULL;
387         }
388
389         return cineon;
390 }