1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * Copyright 1999,2000,2001 David Hodson <hodsond@acm.org>
17  */
18 
19 /** \file
20  * \ingroup imbcineon
21  *
22  * Cineon image file format library routines.
23  */
24 
25 #include "cineonlib.h"
26 #include "logmemfile.h"
27 
28 #include <math.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/types.h>
33 #include <time.h>
34 
35 #include "BLI_fileops.h"
36 #include "BLI_utildefines.h"
37 
38 #include "MEM_guardedalloc.h"
39 
40 /*
41  * For debug purpose
42  */
43 
44 static int verbose = 0;
45 
cineonSetVerbose(int verbosity)46 void cineonSetVerbose(int verbosity)
47 {
48   verbose = verbosity;
49 }
50 
fillCineonMainHeader(LogImageFile * cineon,CineonMainHeader * header,const char * filename,const char * creator)51 static void fillCineonMainHeader(LogImageFile *cineon,
52                                  CineonMainHeader *header,
53                                  const char *filename,
54                                  const char *creator)
55 {
56   time_t fileClock;
57   struct tm *fileTime;
58   int i;
59 
60   memset(header, 0, sizeof(CineonMainHeader));
61 
62   /* --- File header --- */
63   header->fileHeader.magic_num = swap_uint(CINEON_FILE_MAGIC, cineon->isMSB);
64   header->fileHeader.offset = swap_uint(cineon->element[0].dataOffset, cineon->isMSB);
65   header->fileHeader.gen_hdr_size = swap_uint(
66       sizeof(CineonFileHeader) + sizeof(CineonImageHeader) + sizeof(CineonOriginationHeader),
67       cineon->isMSB);
68   header->fileHeader.ind_hdr_size = 0;
69   header->fileHeader.user_data_size = 0;
70   header->fileHeader.file_size = swap_uint(cineon->element[0].dataOffset +
71                                                cineon->height *
72                                                    getRowLength(cineon->width, cineon->element[0]),
73                                            cineon->isMSB);
74   strcpy(header->fileHeader.version, "v4.5");
75   strncpy(header->fileHeader.file_name, filename, 99);
76   header->fileHeader.file_name[99] = 0;
77   fileClock = time(NULL);
78   fileTime = localtime(&fileClock);
79   strftime(header->fileHeader.creation_date, 12, "%Y:%m:%d", fileTime);
80   strftime(header->fileHeader.creation_time, 12, "%H:%M:%S%Z", fileTime);
81   header->fileHeader.creation_time[11] = 0;
82 
83   /* --- Image header --- */
84   header->imageHeader.orientation = 0;
85   header->imageHeader.elements_per_image = cineon->depth;
86 
87   for (i = 0; i < 3; i++) {
88     header->imageHeader.element[i].descriptor1 = 0;
89     header->imageHeader.element[i].descriptor2 = i;
90     header->imageHeader.element[i].bits_per_sample = cineon->element[0].bitsPerSample;
91     header->imageHeader.element[i].pixels_per_line = swap_uint(cineon->width, cineon->isMSB);
92     header->imageHeader.element[i].lines_per_image = swap_uint(cineon->height, cineon->isMSB);
93     header->imageHeader.element[i].ref_low_data = swap_uint(cineon->element[0].refLowData,
94                                                             cineon->isMSB);
95     header->imageHeader.element[i].ref_low_quantity = swap_float(cineon->element[0].refLowQuantity,
96                                                                  cineon->isMSB);
97     header->imageHeader.element[i].ref_high_data = swap_uint(cineon->element[0].refHighData,
98                                                              cineon->isMSB);
99     header->imageHeader.element[i].ref_high_quantity = swap_float(
100         cineon->element[0].refHighQuantity, cineon->isMSB);
101   }
102 
103   header->imageHeader.white_point_x = swap_float(0.0f, cineon->isMSB);
104   header->imageHeader.white_point_y = swap_float(0.0f, cineon->isMSB);
105   header->imageHeader.red_primary_x = swap_float(0.0f, cineon->isMSB);
106   header->imageHeader.red_primary_y = swap_float(0.0f, cineon->isMSB);
107   header->imageHeader.green_primary_x = swap_float(0.0f, cineon->isMSB);
108   header->imageHeader.green_primary_y = swap_float(0.0f, cineon->isMSB);
109   header->imageHeader.blue_primary_x = swap_float(0.0f, cineon->isMSB);
110   header->imageHeader.blue_primary_y = swap_float(0.0f, cineon->isMSB);
111   strncpy(header->imageHeader.label, creator, 199);
112   header->imageHeader.label[199] = 0;
113   header->imageHeader.interleave = 0;
114   header->imageHeader.data_sign = 0;
115   header->imageHeader.sense = 0;
116   header->imageHeader.line_padding = swap_uint(0, cineon->isMSB);
117   header->imageHeader.element_padding = swap_uint(0, cineon->isMSB);
118 
119   switch (cineon->element[0].packing) {
120     case 0:
121       header->imageHeader.packing = 0;
122       break;
123 
124     case 1:
125       header->imageHeader.packing = 5;
126       break;
127 
128     case 2:
129       header->imageHeader.packing = 6;
130       break;
131   }
132 
133   /* --- Origination header --- */
134   /* we leave it blank */
135 
136   /* --- Film header --- */
137   /* we leave it blank */
138 }
139 
cineonOpen(const unsigned char * byteStuff,int fromMemory,size_t bufferSize)140 LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t bufferSize)
141 {
142   CineonMainHeader header;
143   LogImageFile *cineon = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__);
144   const char *filename = (const char *)byteStuff;
145   int i;
146   unsigned int dataOffset;
147 
148   if (cineon == NULL) {
149     if (verbose) {
150       printf("Cineon: Failed to malloc cineon file structure.\n");
151     }
152     return NULL;
153   }
154 
155   /* zero the header */
156   memset(&header, 0, sizeof(CineonMainHeader));
157 
158   /* for close routine */
159   cineon->file = NULL;
160 
161   if (fromMemory == 0) {
162     /* byteStuff is then the filename */
163     cineon->file = BLI_fopen(filename, "rb");
164     if (cineon->file == NULL) {
165       if (verbose) {
166         printf("Cineon: Failed to open file \"%s\".\n", filename);
167       }
168       logImageClose(cineon);
169       return NULL;
170     }
171     /* not used in this case */
172     cineon->memBuffer = NULL;
173     cineon->memCursor = NULL;
174     cineon->memBufferSize = 0;
175   }
176   else {
177     cineon->memBuffer = (unsigned char *)byteStuff;
178     cineon->memCursor = (unsigned char *)byteStuff;
179     cineon->memBufferSize = bufferSize;
180   }
181 
182   if (logimage_fread(&header, sizeof(header), 1, cineon) == 0) {
183     if (verbose) {
184       printf("Cineon: Not enough data for header in \"%s\".\n", byteStuff);
185     }
186     logImageClose(cineon);
187     return NULL;
188   }
189 
190   /* endianness determination */
191   if (header.fileHeader.magic_num == swap_uint(CINEON_FILE_MAGIC, 1)) {
192     cineon->isMSB = 1;
193     if (verbose) {
194       printf("Cineon: File is MSB.\n");
195     }
196   }
197   else if (header.fileHeader.magic_num == CINEON_FILE_MAGIC) {
198     cineon->isMSB = 0;
199     if (verbose) {
200       printf("Cineon: File is LSB.\n");
201     }
202   }
203   else {
204     if (verbose) {
205       printf("Cineon: Bad magic number %lu in \"%s\".\n",
206              (unsigned long)header.fileHeader.magic_num,
207              byteStuff);
208     }
209     logImageClose(cineon);
210     return NULL;
211   }
212 
213   cineon->width = swap_uint(header.imageHeader.element[0].pixels_per_line, cineon->isMSB);
214   cineon->height = swap_uint(header.imageHeader.element[0].lines_per_image, cineon->isMSB);
215 
216   if (cineon->width == 0 || cineon->height == 0) {
217     if (verbose) {
218       printf("Cineon: Wrong image dimension: %dx%d\n", cineon->width, cineon->height);
219     }
220     logImageClose(cineon);
221     return NULL;
222   }
223 
224   cineon->depth = header.imageHeader.elements_per_image;
225   cineon->srcFormat = format_Cineon;
226 
227   if (header.imageHeader.interleave == 0) {
228     cineon->numElements = 1;
229   }
230   else if (header.imageHeader.interleave == 2) {
231     cineon->numElements = header.imageHeader.elements_per_image;
232   }
233   else {
234     if (verbose) {
235       printf("Cineon: Data interleave not supported: %d\n", header.imageHeader.interleave);
236     }
237     logImageClose(cineon);
238     return NULL;
239   }
240 
241   if (cineon->depth == 1) {
242     /* Grayscale image */
243     cineon->element[0].descriptor = descriptor_Luminance;
244     cineon->element[0].transfer = transfer_Linear;
245     cineon->element[0].depth = 1;
246   }
247   else if (cineon->depth == 3) {
248     /* RGB image */
249     if (cineon->numElements == 1) {
250       cineon->element[0].descriptor = descriptor_RGB;
251       cineon->element[0].transfer = transfer_PrintingDensity;
252       cineon->element[0].depth = 3;
253     }
254     else if (cineon->numElements == 3) {
255       cineon->element[0].descriptor = descriptor_Red;
256       cineon->element[0].transfer = transfer_PrintingDensity;
257       cineon->element[0].depth = 1;
258       cineon->element[1].descriptor = descriptor_Green;
259       cineon->element[1].transfer = transfer_PrintingDensity;
260       cineon->element[1].depth = 1;
261       cineon->element[2].descriptor = descriptor_Blue;
262       cineon->element[2].transfer = transfer_PrintingDensity;
263       cineon->element[2].depth = 1;
264     }
265   }
266   else {
267     if (verbose) {
268       printf("Cineon: Cineon image depth unsupported: %d\n", cineon->depth);
269     }
270     logImageClose(cineon);
271     return NULL;
272   }
273 
274   dataOffset = swap_uint(header.fileHeader.offset, cineon->isMSB);
275 
276   for (i = 0; i < cineon->numElements; i++) {
277     cineon->element[i].bitsPerSample = header.imageHeader.element[i].bits_per_sample;
278     cineon->element[i].maxValue = powf(2, cineon->element[i].bitsPerSample) - 1.0f;
279     cineon->element[i].refLowData = swap_uint(header.imageHeader.element[i].ref_low_data,
280                                               cineon->isMSB);
281     cineon->element[i].refLowQuantity = swap_float(header.imageHeader.element[i].ref_low_quantity,
282                                                    cineon->isMSB);
283     cineon->element[i].refHighData = swap_uint(header.imageHeader.element[i].ref_high_data,
284                                                cineon->isMSB);
285     cineon->element[i].refHighQuantity = swap_float(
286         header.imageHeader.element[i].ref_high_quantity, cineon->isMSB);
287 
288     switch (header.imageHeader.packing) {
289       case 0:
290         cineon->element[i].packing = 0;
291         break;
292 
293       case 5:
294         cineon->element[i].packing = 1;
295         break;
296 
297       case 6:
298         cineon->element[i].packing = 2;
299         break;
300 
301       default:
302         /* Not supported */
303         if (verbose) {
304           printf("Cineon: packing unsupported: %d\n", header.imageHeader.packing);
305         }
306         logImageClose(cineon);
307         return NULL;
308     }
309 
310     if (cineon->element[i].refLowData == CINEON_UNDEFINED_U32) {
311       cineon->element[i].refLowData = 0;
312     }
313 
314     if (cineon->element[i].refHighData == CINEON_UNDEFINED_U32) {
315       cineon->element[i].refHighData = (unsigned int)cineon->element[i].maxValue;
316     }
317 
318     if (cineon->element[i].refLowQuantity == CINEON_UNDEFINED_R32 ||
319         isnan(cineon->element[i].refLowQuantity)) {
320       cineon->element[i].refLowQuantity = 0.0f;
321     }
322 
323     if (cineon->element[i].refHighQuantity == CINEON_UNDEFINED_R32 ||
324         isnan(cineon->element[i].refHighQuantity)) {
325       if (cineon->element[i].transfer == transfer_PrintingDensity) {
326         cineon->element[i].refHighQuantity = 2.048f;
327       }
328       else {
329         cineon->element[i].refHighQuantity = cineon->element[i].maxValue;
330       }
331     }
332 
333     cineon->element[i].dataOffset = dataOffset;
334     dataOffset += cineon->height * getRowLength(cineon->width, cineon->element[i]);
335   }
336 
337   cineon->referenceBlack = 95.0f / 1023.0f * cineon->element[0].maxValue;
338   cineon->referenceWhite = 685.0f / 1023.0f * cineon->element[0].maxValue;
339   cineon->gamma = 1.7f;
340 
341   if (verbose) {
342     printf("size %d x %d x %d elements\n", cineon->width, cineon->height, cineon->numElements);
343     for (i = 0; i < cineon->numElements; i++) {
344       printf(" Element %d:\n", i);
345       printf("  Bits per sample: %d\n", cineon->element[i].bitsPerSample);
346       printf("  Depth: %d\n", cineon->element[i].depth);
347       printf("  Transfer characteristics: %d\n", cineon->element[i].transfer);
348       printf("  Packing: %d\n", cineon->element[i].packing);
349       printf("  Descriptor: %d\n", cineon->element[i].descriptor);
350       printf("  Data offset: %d\n", cineon->element[i].dataOffset);
351       printf("  Reference low data: %u\n", cineon->element[i].refLowData);
352       printf("  Reference low quantity: %f\n", cineon->element[i].refLowQuantity);
353       printf("  Reference high data: %u\n", cineon->element[i].refHighData);
354       printf("  Reference high quantity: %f\n", cineon->element[i].refHighQuantity);
355       printf("\n");
356     }
357 
358     printf("Gamma: %f\n", cineon->gamma);
359     printf("Reference black: %f\n", cineon->referenceBlack);
360     printf("Reference white: %f\n", cineon->referenceWhite);
361     printf("Orientation: %d\n", header.imageHeader.orientation);
362     printf("----------------------------\n");
363   }
364   return cineon;
365 }
366 
cineonCreate(const char * filename,int width,int height,int bitsPerSample,const char * creator)367 LogImageFile *cineonCreate(
368     const char *filename, int width, int height, int bitsPerSample, const char *creator)
369 {
370   CineonMainHeader header;
371   const char *shortFilename = NULL;
372   /* unsigned char pad[6044]; */
373 
374   LogImageFile *cineon = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__);
375   if (cineon == NULL) {
376     if (verbose) {
377       printf("cineon: Failed to malloc cineon file structure.\n");
378     }
379     return NULL;
380   }
381 
382   /* Only 10 bits Cineon are supported */
383   if (bitsPerSample != 10) {
384     if (verbose) {
385       printf("cineon: Only 10 bits Cineon are supported.\n");
386     }
387     logImageClose(cineon);
388     return NULL;
389   }
390 
391   cineon->width = width;
392   cineon->height = height;
393   cineon->element[0].bitsPerSample = 10;
394   cineon->element[0].dataOffset = sizeof(CineonMainHeader);
395   cineon->element[0].maxValue = 1023;
396   cineon->isMSB = 1;
397   cineon->numElements = 1;
398   cineon->element[0].packing = 1;
399   cineon->depth = 3;
400   cineon->element[0].depth = 3;
401   cineon->element[0].descriptor = descriptor_RGB;
402   cineon->element[0].transfer = transfer_PrintingDensity;
403   cineon->element[0].refHighQuantity = 2.048f;
404   cineon->element[0].refLowQuantity = 0;
405   cineon->element[0].refLowData = 0;
406   cineon->element[0].refHighData = cineon->element[0].maxValue;
407   cineon->referenceWhite = 685.0f;
408   cineon->referenceBlack = 95.0f;
409   cineon->gamma = 1.7f;
410 
411   shortFilename = strrchr(filename, '/');
412   if (shortFilename == NULL) {
413     shortFilename = filename;
414   }
415   else {
416     shortFilename++;
417   }
418 
419   cineon->file = BLI_fopen(filename, "wb");
420   if (cineon->file == NULL) {
421     if (verbose) {
422       printf("cineon: Couldn't open file %s\n", filename);
423     }
424     logImageClose(cineon);
425     return NULL;
426   }
427 
428   fillCineonMainHeader(cineon, &header, shortFilename, creator);
429 
430   if (fwrite(&header, sizeof(header), 1, cineon->file) == 0) {
431     if (verbose) {
432       printf("cineon: Couldn't write image header\n");
433     }
434     logImageClose(cineon);
435     return NULL;
436   }
437 
438   return cineon;
439 }
440