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