svn merge -r 12937:13095 https://svn.blender.org/svnroot/bf-blender/trunk/blender
[blender.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  */
21
22 #include "dpxfile.h"
23 #include "dpxlib.h"
24
25 #include <stdio.h>
26 #include <math.h>
27 #include <stdlib.h>
28 #include <time.h>                                /* strftime() */
29 #include <sys/types.h>
30 #ifdef WIN32
31 #include <winsock.h>
32 #else
33 #include <netinet/in.h>  /* htonl() */
34 #endif
35 #include <string.h>                      /* memset */
36 #include "cin_debug_stuff.h"
37 #include "logmemfile.h"
38
39 static void
40 fillDpxChannelInfo(DpxFile* dpx, DpxChannelInformation* chan, int des) {
41
42         chan->signage = 0;
43         chan->ref_low_data = htonl(0);
44         chan->ref_low_quantity = htonf(0.0);
45         chan->ref_high_data = htonl(1023);
46         chan->ref_high_quantity = htonf(2.046);
47         chan->designator1 = des;
48         chan->transfer_characteristics = 0;
49         chan->colourimetry = 0;
50         chan->bits_per_pixel = 10;
51         chan->packing = htons(1);
52         chan->encoding = 0;
53         chan->data_offset = 0;
54         chan->line_padding = htonl(0);
55         chan->channel_padding = htonl(0);
56         chan->description[0] = 0;
57 }
58
59 static void
60 dumpDpxChannelInfo(DpxChannelInformation* chan) {
61         d_printf("      Signage %ld", (long)ntohl(chan->signage));
62         d_printf("      Ref low data %ld\n", (long)ntohl(chan->ref_low_data));
63         d_printf("      Ref low quantity %f\n", ntohf(chan->ref_low_quantity));
64         d_printf("      Ref high data %ld\n", (long)ntohl(chan->ref_high_data));
65         d_printf("      Ref high quantity %f\n", ntohf(chan->ref_high_quantity));
66         d_printf("      Designator1: %d,", chan->designator1);
67         d_printf("      Bits per pixel %d\n", chan->bits_per_pixel);
68         d_printf("      Packing: %d,", ntohs(chan->packing));
69         d_printf("      Data Offset: %ld,", (long)ntohl(chan->data_offset));
70 }
71
72 static void
73 fillDpxFileInfo(
74         DpxFile* dpx, DpxFileInformation* fileInfo, const char* filename) {
75
76         time_t fileClock;
77         struct tm* fileTime;
78
79         /* Note: always write files in network order */
80         /* By the spec, it shouldn't matter, but ... */
81
82         fileInfo->magic_num = htonl(DPX_FILE_MAGIC);
83         fileInfo->offset = htonl(dpx->imageOffset);
84         strcpy(fileInfo->vers, "v1.0");
85         fileInfo->file_size = htonl(dpx->imageOffset +
86                 pixelsToLongs(dpx->height * dpx->width * dpx->depth) * 4);
87         fileInfo->ditto_key = 0;
88         fileInfo->gen_hdr_size = htonl(
89                 sizeof(DpxFileInformation) +
90                 sizeof(DpxImageInformation) +
91                 sizeof(DpxOriginationInformation));
92         fileInfo->ind_hdr_size = htonl(sizeof(DpxMPIInformation));
93         fileInfo->user_data_size = 0;
94         strncpy(fileInfo->file_name, filename, 99);
95         fileInfo->file_name[99] = 0;
96
97         fileClock = time(0);
98         fileTime = localtime(&fileClock);
99         strftime(fileInfo->create_date, 24, "%Y:%m:%d:%H:%M:%S%Z", fileTime);
100         /* Question: is %Z in strftime guaranteed to return 3 chars? */
101         fileInfo->create_date[23] = 0;
102
103         strcpy(fileInfo->creator, "David's DPX writer");
104         fileInfo->project[0] = 0;
105         fileInfo->copyright[0] = 0;
106         fileInfo->key = 0xFFFFFFFF; /* same in any byte order */
107 }
108
109 static void
110 dumpDpxFileInfo(DpxFileInformation* fileInfo) {
111         d_printf("\n--File Information--\n");
112         d_printf("Magic: %8.8lX\n", (unsigned long)ntohl(fileInfo->magic_num));
113         d_printf("Image Offset %ld\n", (long)ntohl(fileInfo->offset));
114         d_printf("Version \"%s\"\n", fileInfo->vers);
115         d_printf("File size %ld\n", (long)ntohl(fileInfo->file_size));
116         d_printf("Ditto key %ld\n", (long)ntohl(fileInfo->ditto_key));
117         d_printf("Generic Header size %ld\n", (long)ntohl(fileInfo->gen_hdr_size));
118         d_printf("Industry Header size %ld\n", (long)ntohl(fileInfo->ind_hdr_size));
119         d_printf("User Data size %ld\n", (long)ntohl(fileInfo->user_data_size));
120         d_printf("File name \"%s\"\n", fileInfo->file_name);
121         d_printf("Creation date \"%s\"\n", fileInfo->create_date);
122         d_printf("Creator \"%s\"\n", fileInfo->creator);
123         d_printf("Project \"%s\"\n", fileInfo->project);
124         d_printf("Copyright \"%s\"\n", fileInfo->copyright);
125         d_printf("Key %ld\n", (long)ntohl(fileInfo->key));
126 }
127
128 static void
129 fillDpxImageInfo(
130         DpxFile* dpx, DpxImageInformation* imageInfo) {
131         imageInfo->orientation = 0;
132         imageInfo->channels_per_image = htons(1);
133         imageInfo->pixels_per_line = htonl(dpx->width);
134         imageInfo->lines_per_image = htonl(dpx->height);
135
136         if (dpx->depth == 1) {
137                 fillDpxChannelInfo(dpx, &imageInfo->channel[0], 0);
138
139         } else if (dpx->depth == 3) {
140                 fillDpxChannelInfo(dpx, &imageInfo->channel[0], 50);
141         }
142 }
143
144 static void
145 dumpDpxImageInfo(DpxImageInformation* imageInfo) {
146
147         int n;
148         int i;
149         d_printf("\n--Image Information--\n");
150         d_printf("Image orientation %d,", ntohs(imageInfo->orientation));
151         n = ntohs(imageInfo->channels_per_image);
152         d_printf("Channels %d\n", n);
153         d_printf("Pixels per line %ld\n", (long)ntohl(imageInfo->pixels_per_line));
154         d_printf("Lines per image %ld\n", (long)ntohl(imageInfo->lines_per_image));
155         for (i = 0; i < n; ++i) {
156                 d_printf("      --Channel %d--\n", i);
157                 dumpDpxChannelInfo(&imageInfo->channel[i]);
158         }
159 }
160
161 static void
162 fillDpxOriginationInfo(
163         DpxFile* dpx, DpxOriginationInformation* originInfo, DpxFileInformation* fileInfo) {
164 }
165
166 static void
167 dumpDpxOriginationInfo(DpxOriginationInformation* originInfo) {
168         d_printf("\n--Origination Information--\n");
169         d_printf("X offset %ld\n", (long)ntohl(originInfo->x_offset));
170         d_printf("Y offset %ld\n", (long)ntohl(originInfo->y_offset));
171         d_printf("X centre %f\n", ntohf(originInfo->x_centre));
172         d_printf("Y centre %f\n", ntohf(originInfo->y_centre));
173         d_printf("Original X %ld\n", (long)ntohl(originInfo->x_original_size));
174         d_printf("Original Y %ld\n", (long)ntohl(originInfo->y_original_size));
175         d_printf("File name \"%s\"\n", originInfo->file_name);
176         d_printf("Creation time \"%s\"\n", originInfo->creation_time);
177         d_printf("Input device \"%s\"\n", originInfo->input_device);
178         d_printf("Serial number \"%s\"\n", originInfo->input_serial_number);
179 }
180
181 static void
182 initDpxMainHeader(DpxFile* dpx, DpxMainHeader* header, const char* shortFilename) {
183         memset(header, 0, sizeof(DpxMainHeader));
184         fillDpxFileInfo(dpx, &header->fileInfo, shortFilename);
185         fillDpxImageInfo(dpx, &header->imageInfo);
186         fillDpxOriginationInfo(dpx, &header->originInfo, &header->fileInfo);
187 #if 0
188         fillDpxMPIInfo(dpx, &header->filmHeader);
189 #endif
190 }
191
192 static void
193 dumpDpxMainHeader(DpxMainHeader* header) {
194         dumpDpxFileInfo(&header->fileInfo);
195         dumpDpxImageInfo(&header->imageInfo);
196         dumpDpxOriginationInfo(&header->originInfo);
197 #if 0
198         dumpDpxMPIInformation(&header->filmHeader);
199 #endif
200 }
201
202 static int verbose = 0;
203 void
204 dpxSetVerbose(int verbosity) {
205         verbose = verbosity;
206 }
207
208 static void
209 verboseMe(DpxFile* dpx) {
210
211         d_printf("size %d x %d x %d\n", dpx->width, dpx->height, dpx->depth);
212         d_printf("ImageStart %d, lineBufferLength %d, implied length %d\n",
213                 dpx->imageOffset, dpx->lineBufferLength * 4,
214                 dpx->imageOffset + pixelsToLongs(dpx->width * dpx->depth * dpx->height) * 4);
215 }
216
217 int
218 dpxGetRowBytes(DpxFile* dpx, unsigned short* row, int y) {
219
220         /* Note: this code is bizarre because DPX files can wrap */
221         /* packed longwords across line boundaries!!!! */
222
223         size_t readLongs;
224         unsigned int longIndex;
225         int numPixels = dpx->width * dpx->depth;
226         int pixelIndex;
227
228         /* only seek if not reading consecutive lines */
229         /* this is not quite right yet, need to account for leftovers */
230         if (y != dpx->fileYPos) {
231                 int lineOffset = pixelsToLongs(y * dpx->width * dpx->depth) * 4;
232                 if (verbose) d_printf("Seek in getRowBytes\n");
233                 if (logimage_fseek(dpx, dpx->imageOffset + lineOffset, SEEK_SET) != 0) {
234                         if (verbose) d_printf("Couldn't seek to line %d at %d\n", y, dpx->imageOffset + lineOffset);
235                         return 1;
236                 }
237                 dpx->fileYPos = y;
238         }
239
240         /* read enough longwords */
241         readLongs = pixelsToLongs(numPixels - dpx->pixelBufferUsed);
242         if (logimage_fread(dpx->lineBuffer, 4, readLongs, dpx) != readLongs) {
243                 if (verbose) d_printf("Couldn't read line %d length %d\n", y, readLongs * 4);
244                 return 1;
245         }
246         ++dpx->fileYPos;
247
248         /* convert longwords to pixels */
249         pixelIndex = dpx->pixelBufferUsed;
250
251         /* this is just strange */
252         if (dpx->depth == 1) {
253                 for (longIndex = 0; longIndex < readLongs; ++longIndex) {
254                         unsigned int t = ntohl(dpx->lineBuffer[longIndex]);
255                         dpx->pixelBuffer[pixelIndex] = t & 0x3ff;
256                         t = t >> 10;
257                         dpx->pixelBuffer[pixelIndex+1] = t & 0x3ff;
258                         t = t >> 10;
259                         dpx->pixelBuffer[pixelIndex+2] = t & 0x3ff;
260                         pixelIndex += 3;
261                 }
262         } else /* if (dpx->depth == 3) */ {
263                 for (longIndex = 0; longIndex < readLongs; ++longIndex) {
264                         unsigned int t = ntohl(dpx->lineBuffer[longIndex]);
265                         t = t >> 2;
266                         dpx->pixelBuffer[pixelIndex+2] = t & 0x3ff;
267                         t = t >> 10;
268                         dpx->pixelBuffer[pixelIndex+1] = t & 0x3ff;
269                         t = t >> 10;
270                         dpx->pixelBuffer[pixelIndex] = t & 0x3ff;
271                         pixelIndex += 3;
272                 }
273         }
274         dpx->pixelBufferUsed = pixelIndex;
275
276         /* extract required pixels */
277         for (pixelIndex = 0; pixelIndex < numPixels; ++pixelIndex) {
278                 /* row[pixelIndex] = dpx->lut10[dpx->pixelBuffer[pixelIndex]]; */
279                 row[pixelIndex] = dpx->pixelBuffer[pixelIndex] << 6;
280         }
281
282         /* save remaining pixels */
283         while (pixelIndex < dpx->pixelBufferUsed) {
284                 dpx->pixelBuffer[pixelIndex - numPixels] = dpx->pixelBuffer[pixelIndex];
285                 ++pixelIndex;
286         }
287         dpx->pixelBufferUsed -= numPixels;
288
289         /* done! */
290         return 0;
291 }
292
293 int
294 dpxSetRowBytes(DpxFile* dpx, const unsigned short* row, int y) {
295
296         /* Note: this code is bizarre because DPX files can wrap */
297         /* packed longwords across line boundaries!!!! */
298
299         size_t writeLongs;
300         int longIndex;
301         int numPixels = dpx->width * dpx->depth;
302         int pixelIndex;
303         int pixelIndex2;
304
305         /* only seek if not reading consecutive lines */
306         /* this is not quite right yet */
307         if (y != dpx->fileYPos) {
308                 int lineOffset = pixelsToLongs(y * dpx->width * dpx->depth) * 4;
309                 if (verbose) d_printf("Seek in getRowBytes\n");
310                 if (logimage_fseek(dpx, dpx->imageOffset + lineOffset, SEEK_SET) != 0) {
311                         if (verbose) d_printf("Couldn't seek to line %d at %d\n", y, dpx->imageOffset + lineOffset);
312                         return 1;
313                 }
314                 dpx->fileYPos = y;
315         }
316
317         /* put new pixels into pixelBuffer */
318         for (pixelIndex = 0; pixelIndex < numPixels; ++pixelIndex) {
319                 /* dpx->pixelBuffer[dpx->pixelBufferUsed + pixelIndex] = dpx->lut8[row[pixelIndex]]; */
320                 dpx->pixelBuffer[dpx->pixelBufferUsed + pixelIndex] = row[pixelIndex] >> 6;
321         }
322         dpx->pixelBufferUsed += numPixels;
323
324         /* pack into longwords */
325         writeLongs = dpx->pixelBufferUsed / 3;
326         /* process whole line at image end */
327         if (dpx->fileYPos == (dpx->height - 1)) {
328                 writeLongs = pixelsToLongs(dpx->pixelBufferUsed);
329         }
330         pixelIndex = 0;
331         if (dpx->depth == 1) {
332                 for (longIndex = 0; longIndex < writeLongs; ++longIndex) {
333                         unsigned int t = dpx->pixelBuffer[pixelIndex] |
334                                         (dpx->pixelBuffer[pixelIndex+1] << 10) |
335                                         (dpx->pixelBuffer[pixelIndex+2] << 20);
336                         dpx->lineBuffer[longIndex] = htonl(t);
337                         pixelIndex += 3;
338                 }
339         } else {
340                 for (longIndex = 0; longIndex < writeLongs; ++longIndex) {
341                         unsigned int t = dpx->pixelBuffer[pixelIndex+2] << 2 |
342                                         (dpx->pixelBuffer[pixelIndex+1] << 12) |
343                                         (dpx->pixelBuffer[pixelIndex] << 22);
344                         dpx->lineBuffer[longIndex] = htonl(t);
345                         pixelIndex += 3;
346                 }
347         }
348
349         /* write them */
350         if (fwrite(dpx->lineBuffer, 4, writeLongs, dpx->file) != writeLongs) {
351                 if (verbose) d_printf("Couldn't write line %d length %d\n", y, writeLongs * 4);
352                 return 1;
353         }
354         ++dpx->fileYPos;
355
356         /* save remaining pixels */
357         pixelIndex2 = 0;
358         while (pixelIndex < dpx->pixelBufferUsed) {
359                 dpx->pixelBuffer[pixelIndex2] = dpx->pixelBuffer[pixelIndex];
360                 ++pixelIndex;
361                 ++pixelIndex2;
362         }
363         dpx->pixelBufferUsed = pixelIndex2;
364
365         return 0;
366 }
367
368 #define LFMEMFILE       0
369 #define LFREALFILE      1
370
371 static DpxFile* 
372 intern_dpxOpen(int mode, const char* bytestuff, int bufsize) {
373
374         DpxMainHeader header;
375         const char *filename = bytestuff;
376         DpxFile* dpx = (DpxFile*)malloc(sizeof(DpxFile));
377         
378         if (dpx == 0) {
379                 if (verbose) d_printf("Failed to malloc dpx file structure.\n");
380                 return 0;
381         }
382
383         /* for close routine */
384         dpx->file = 0;
385         dpx->lineBuffer = 0;
386         dpx->pixelBuffer = 0;
387
388         if (mode == LFREALFILE) {
389                 filename = bytestuff;
390                 dpx->file = fopen(filename, "rb");
391                 if (dpx->file == 0) {   
392                         if (verbose) d_printf("Failed to open file \"%s\".\n", filename);
393                         dpxClose(dpx);
394                         return 0;
395                 }
396                 dpx->membuffer = 0;
397                 dpx->memcursor = 0;
398                 dpx->membuffersize = 0;
399         } else if (mode == LFMEMFILE) {
400                 dpx->membuffer = (unsigned char *)bytestuff;
401                 dpx->memcursor = (unsigned char *)bytestuff;
402                 dpx->membuffersize = bufsize;
403         }
404         
405         dpx->reading = 1;
406
407         if (logimage_fread(&header, sizeof(header), 1, dpx) == 0) {
408                 if (verbose) d_printf("Not enough data for header in \"%s\".\n", filename);
409                 dpxClose(dpx);
410                 return 0;
411         }
412
413         /* let's assume dpx files are always network order */
414         if (header.fileInfo.magic_num != ntohl(DPX_FILE_MAGIC)) {
415                 if (verbose) d_printf("Bad magic number %8.8lX in \"%s\".\n",
416                         (unsigned long)ntohl(header.fileInfo.magic_num), filename);
417                 dpxClose(dpx);
418                 return 0;
419         }
420
421         if (ntohs(header.imageInfo.channel[0].packing) != 1) {
422                 if (verbose) d_printf("Unknown packing %d\n", header.imageInfo.channel[0].packing);
423                 dpxClose(dpx);
424                 return 0;
425         }
426
427
428         dpx->width = ntohl(header.imageInfo.pixels_per_line);
429         dpx->height = ntohl(header.imageInfo.lines_per_image);
430         dpx->depth = ntohs(header.imageInfo.channels_per_image);
431         /* Another DPX vs Cineon wierdness */
432         if (dpx->depth == 1) {
433                 switch (header.imageInfo.channel[0].designator1) {
434                 case 50: dpx->depth = 3; break;
435                 case 51: dpx->depth = 4; break;
436                 case 52: dpx->depth = 4; break;
437                 default: break;
438                 }
439         }
440         dpx->bitsPerPixel = 10;
441         /* dpx->bitsPerPixel = header.imageInfo.channel[0].bits_per_pixel; */
442         dpx->imageOffset = ntohl(header.fileInfo.offset);
443
444         dpx->lineBufferLength = pixelsToLongs(dpx->width * dpx->depth);
445         dpx->lineBuffer = malloc(dpx->lineBufferLength * 4);
446         if (dpx->lineBuffer == 0) {
447                 if (verbose) d_printf("Couldn't malloc line buffer of size %d\n", dpx->lineBufferLength * 4);
448                 dpxClose(dpx);
449                 return 0;
450         }
451
452         /* could have 2 pixels left over */
453         dpx->pixelBuffer = malloc((dpx->lineBufferLength * 3 + 2) * sizeof(unsigned short));
454         if (dpx->pixelBuffer == 0) {
455                 if (verbose) d_printf("Couldn't malloc pixel buffer of size %d\n",
456                                 (dpx->width * dpx->depth + 2 + 2) * sizeof(unsigned short));
457                 dpxClose(dpx);
458                 return 0;
459         }
460         dpx->pixelBufferUsed = 0;
461
462         if (logimage_fseek(dpx, dpx->imageOffset, SEEK_SET) != 0) {
463                 if (verbose) d_printf("Couldn't seek to image data start at %d\n", dpx->imageOffset);
464                 dpxClose(dpx);
465                 return 0;
466         }
467         dpx->fileYPos = 0;
468
469         logImageGetByteConversionDefaults(&dpx->params);
470         setupLut(dpx);
471
472         dpx->getRow = &dpxGetRowBytes;
473         dpx->setRow = 0;
474         dpx->close = &dpxClose;
475
476         if (verbose) {
477                 verboseMe(dpx);
478         }
479
480         return dpx;
481 }
482
483 DpxFile* 
484 dpxOpen(const char *filename) {
485         return intern_dpxOpen(LFREALFILE, filename, 0);
486 }
487
488 DpxFile* 
489 dpxOpenFromMem(unsigned char *buffer, unsigned int size) {
490         return intern_dpxOpen(LFMEMFILE, (const char *) buffer, size);
491 }
492
493 int 
494 dpxIsMemFileCineon(void *buffer) {
495         int magicnum = 0;
496         magicnum = *((int*)buffer);
497         if (magicnum == ntohl(DPX_FILE_MAGIC)) return 1;
498         else return 0;
499 }
500
501 DpxFile*
502 dpxCreate(const char* filename, int width, int height, int depth) {
503
504         /* Note: always write files in network order */
505         /* By the spec, it shouldn't matter, but ... */
506
507         DpxMainHeader header;
508         const char* shortFilename = 0;
509
510         DpxFile* dpx = (DpxFile*)malloc(sizeof(DpxFile));
511         if (dpx == 0) {
512                 if (verbose) d_printf("Failed to malloc dpx file structure.\n");
513                 return 0;
514         }
515
516         memset(&header, 0, sizeof(header));
517
518         /* for close routine */
519         dpx->file = 0;
520         dpx->lineBuffer = 0;
521         dpx->pixelBuffer = 0;
522
523         dpx->file = fopen(filename, "wb");
524         if (dpx->file == 0) {
525                 if (verbose) d_printf("Couldn't open file %s\n", filename);
526                 dpxClose(dpx);
527                 return 0;
528         }
529         dpx->reading = 0;
530
531         dpx->width = width;
532         dpx->height = height;
533         dpx->depth = depth;
534         dpx->bitsPerPixel = 10;
535         dpx->imageOffset = sizeof(DpxMainHeader);
536
537         dpx->lineBufferLength = pixelsToLongs(dpx->width * dpx->depth);
538         dpx->lineBuffer = malloc(dpx->lineBufferLength * 4);
539         if (dpx->lineBuffer == 0) {
540                 if (verbose) d_printf("Couldn't malloc line buffer of size %d\n", dpx->lineBufferLength * 4);
541                 dpxClose(dpx);
542                 return 0;
543         }
544
545         dpx->pixelBuffer = malloc((dpx->lineBufferLength * 3 + 2) * sizeof(unsigned short));
546         if (dpx->pixelBuffer == 0) {
547                 if (verbose) d_printf("Couldn't malloc pixel buffer of size %d\n",
548                                 (dpx->width * dpx->depth + 2 + 2) * sizeof(unsigned short));
549                 dpxClose(dpx);
550                 return 0;
551         }
552         dpx->pixelBufferUsed = 0;
553
554         /* find trailing part of filename */
555         shortFilename = strrchr(filename, '/');
556         if (shortFilename == 0) {
557                 shortFilename = filename;
558         } else {
559                 ++shortFilename;
560         }
561         initDpxMainHeader(dpx, &header, shortFilename);
562
563         if (fwrite(&header, sizeof(header), 1, dpx->file) == 0) {
564                 if (verbose) d_printf("Couldn't write image header\n");
565                 dpxClose(dpx);
566                 return 0;
567         }
568         dpx->fileYPos = 0;
569
570         logImageGetByteConversionDefaults(&dpx->params);
571         setupLut(dpx);
572
573         dpx->getRow = 0;
574         dpx->setRow = &dpxSetRowBytes;
575         dpx->close = &dpxClose;
576
577         return dpx;
578 }
579
580 void
581 dpxClose(DpxFile* dpx) {
582
583         if (dpx == 0) {
584                 return;
585         }
586
587         if (dpx->file) {
588                 fclose(dpx->file);
589                 dpx->file = 0;
590         }
591
592         if (dpx->lineBuffer) {
593                 free(dpx->lineBuffer);
594                 dpx->lineBuffer = 0;
595         }
596
597         if (dpx->pixelBuffer) {
598                 free(dpx->pixelBuffer);
599                 dpx->pixelBuffer = 0;
600         }
601
602         free(dpx);
603 }
604
605 void
606 dpxDump(const char* filename) {
607
608         DpxMainHeader header;
609         FILE* file;
610
611         file = fopen(filename, "rb");
612         if (file == 0) {
613                 d_printf("Failed to open file \"%s\".\n", filename);
614                 return;
615         }
616
617         if (fread(&header, sizeof(header), 1, file) == 0) {
618                 d_printf("Not enough data for header in \"%s\".\n", filename);
619                 fclose(file);
620                 return;
621         }
622
623         fclose(file);
624         dumpDpxMainHeader(&header);
625 }