/* * Cineon image file format library routines. * * Copyright 1999,2000,2001 David Hodson * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Contributor(s): Julien Enche. * */ /** \file blender/imbuf/intern/cineon/logImageCore.c * \ingroup imbcineon */ #include "logmemfile.h" #include "logImageCore.h" #include "dpxlib.h" #include "cineonlib.h" #include #include #include #include "BLI_fileops.h" #include "BLI_utildefines.h" #include "MEM_guardedalloc.h" /* * Declaration of static functions */ static int logImageSetData8(LogImageFile *logImage, LogImageElement logElement, float *data); static int logImageSetData10(LogImageFile *logImage, LogImageElement logElement, float *data); static int logImageSetData12(LogImageFile *logImage, LogImageElement logElement, float *data); static int logImageSetData16(LogImageFile *logImage, LogImageElement logElement, float *data); static int logImageElementGetData(LogImageFile *dpx, LogImageElement logElement, float *data); static int logImageElementGetData1(LogImageFile *dpx, LogImageElement logElement, float *data); static int logImageElementGetData8(LogImageFile *dpx, LogImageElement logElement, float *data); static int logImageElementGetData10(LogImageFile *dpx, LogImageElement logElement, float *data); static int logImageElementGetData10Packed(LogImageFile *dpx, LogImageElement logElement, float *data); static int logImageElementGetData12(LogImageFile *dpx, LogImageElement logElement, float *data); static int logImageElementGetData12Packed(LogImageFile *dpx, LogImageElement logElement, float *data); static int logImageElementGetData16(LogImageFile *dpx, LogImageElement logElement, float *data); static int convertLogElementToRGBA(float *src, float *dst, LogImageFile *logImage, LogImageElement logElement, int dstIsLinearRGB); static int convertRGBAToLogElement(float *src, float *dst, LogImageFile *logImage, LogImageElement logElement, int srcIsLinearRGB); /* * For debug purpose */ static int verbose = 0; void logImageSetVerbose(int verbosity) { verbose = verbosity; cineonSetVerbose(verbosity); dpxSetVerbose(verbosity); } /* * IO stuff */ int logImageIsDpx(const void *buffer) { unsigned int magicNum = *(unsigned int *)buffer; return (magicNum == DPX_FILE_MAGIC || magicNum == swap_uint(DPX_FILE_MAGIC, 1)); } int logImageIsCineon(const void *buffer) { unsigned int magicNum = *(unsigned int *)buffer; return (magicNum == CINEON_FILE_MAGIC || magicNum == swap_uint(CINEON_FILE_MAGIC, 1)); } LogImageFile *logImageOpenFromFile(const char *filename, int cineon) { unsigned int magicNum; FILE *f = BLI_fopen(filename, "rb"); (void)cineon; if (f == 0) return 0; if (fread(&magicNum, sizeof(unsigned int), 1, f) != 1) { fclose(f); return 0; } fclose(f); if (logImageIsDpx(&magicNum)) return dpxOpen((const unsigned char *)filename, 0, 0); else if (logImageIsCineon(&magicNum)) return cineonOpen((const unsigned char *)filename, 0, 0); return 0; } LogImageFile *logImageOpenFromMemory(const unsigned char *buffer, unsigned int size) { if (logImageIsDpx(buffer)) return dpxOpen(buffer, 1, size); else if (logImageIsCineon(buffer)) return cineonOpen(buffer, 1, size); return 0; } LogImageFile *logImageCreate(const char *filename, int cineon, int width, int height, int bitsPerSample, int isLogarithmic, int hasAlpha, int referenceWhite, int referenceBlack, float gamma, const char *creator) { /* referenceWhite, referenceBlack and gamma values are only supported for DPX file */ if (cineon) return cineonCreate(filename, width, height, bitsPerSample, creator); else return dpxCreate(filename, width, height, bitsPerSample, isLogarithmic, hasAlpha, referenceWhite, referenceBlack, gamma, creator); return 0; } void logImageClose(LogImageFile *logImage) { if (logImage != 0) { if (logImage->file) { fclose(logImage->file); logImage->file = 0; } MEM_freeN(logImage); } } void logImageGetSize(LogImageFile *logImage, int *width, int *height, int *depth) { *width = logImage->width; *height = logImage->height; *depth = logImage->depth; } /* * Helper */ unsigned int getRowLength(int width, LogImageElement logElement) { /* return the row length in bytes according to width and packing method */ switch (logElement.bitsPerSample) { case 1: return ((width * logElement.depth - 1) / 32 + 1) * 4; case 8: return ((width * logElement.depth - 1) / 4 + 1) * 4; case 10: if (logElement.packing == 0) return ((width * logElement.depth * 10 - 1) / 32 + 1) * 4; else if (logElement.packing == 1 || logElement.packing == 2) return ((width * logElement.depth - 1) / 3 + 1) * 4; case 12: if (logElement.packing == 0) return ((width * logElement.depth * 12 - 1) / 32 + 1) * 4; else if (logElement.packing == 1 || logElement.packing == 2) return width * logElement.depth * 2; case 16: return width * logElement.depth * 2; default: return 0; } } /* * Data writing */ int logImageSetDataRGBA(LogImageFile *logImage, float *data, int dataIsLinearRGB) { float *elementData; int returnValue; elementData = (float *)MEM_mallocN(logImage->width * logImage->height * logImage->depth * sizeof(float), __func__); if (elementData == 0) return 1; if (convertRGBAToLogElement(data, elementData, logImage, logImage->element[0], dataIsLinearRGB) != 0) { MEM_freeN(elementData); return 1; } switch (logImage->element[0].bitsPerSample) { case 8: returnValue = logImageSetData8(logImage, logImage->element[0], elementData); break; case 10: returnValue = logImageSetData10(logImage, logImage->element[0], elementData); break; case 12: returnValue = logImageSetData12(logImage, logImage->element[0], elementData); break; case 16: returnValue = logImageSetData16(logImage, logImage->element[0], elementData); break; default: returnValue = 1; break; } MEM_freeN(elementData); return returnValue; } static int logImageSetData8(LogImageFile *logImage, LogImageElement logElement, float *data) { unsigned int rowLength = getRowLength(logImage->width, logElement); unsigned char *row; int x, y; row = (unsigned char *)MEM_mallocN(rowLength, __func__); if (row == 0) { if (verbose) printf("DPX/Cineon: Cannot allocate row.\n"); return 1; } memset(row, 0, rowLength); for (y = 0; y < logImage->height; y++) { for (x = 0; x < logImage->width * logImage->depth; x++) row[x] = (unsigned char)float_uint(data[y * logImage->width * logImage->depth + x], 255); if (logimage_fwrite(row, rowLength, 1, logImage) == 0) { if (verbose) printf("DPX/Cineon: Error while writing file.\n"); MEM_freeN(row); return 1; } } MEM_freeN(row); return 0; } static int logImageSetData10(LogImageFile *logImage, LogImageElement logElement, float *data) { unsigned int rowLength = getRowLength(logImage->width, logElement); unsigned int pixel, index; unsigned int *row; int x, y, offset; row = (unsigned int *)MEM_mallocN(rowLength, __func__); if (row == 0) { if (verbose) printf("DPX/Cineon: Cannot allocate row.\n"); return 1; } for (y = 0; y < logImage->height; y++) { offset = 22; index = 0; pixel = 0; for (x = 0; x < logImage->width * logImage->depth; x++) { pixel |= (unsigned int)float_uint(data[y * logImage->width * logImage->depth + x], 1023) << offset; offset -= 10; if (offset < 0) { row[index] = swap_uint(pixel, logImage->isMSB); index++; pixel = 0; offset = 22; } } if (pixel != 0) row[index] = swap_uint(pixel, logImage->isMSB); if (logimage_fwrite(row, rowLength, 1, logImage) == 0) { if (verbose) printf("DPX/Cineon: Error while writing file.\n"); { MEM_freeN(row); return 1; } } } MEM_freeN(row); return 0; } static int logImageSetData12(LogImageFile *logImage, LogImageElement logElement, float *data) { unsigned int rowLength = getRowLength(logImage->width, logElement); unsigned short *row; int x, y; row = (unsigned short *)MEM_mallocN(rowLength, __func__); if (row == 0) { if (verbose) printf("DPX/Cineon: Cannot allocate row.\n"); return 1; } for (y = 0; y < logImage->height; y++) { for (x = 0; x < logImage->width * logImage->depth; x++) row[x] = swap_ushort(((unsigned short)float_uint(data[y * logImage->width * logImage->depth + x], 4095)) << 4, logImage->isMSB); if (logimage_fwrite(row, rowLength, 1, logImage) == 0) { if (verbose) printf("DPX/Cineon: Error while writing file.\n"); MEM_freeN(row); return 1; } } MEM_freeN(row); return 0; } static int logImageSetData16(LogImageFile *logImage, LogImageElement logElement, float *data) { unsigned int rowLength = getRowLength(logImage->width, logElement); unsigned short *row; int x, y; row = (unsigned short *)MEM_mallocN(rowLength, __func__); if (row == 0) { if (verbose) printf("DPX/Cineon: Cannot allocate row.\n"); return 1; } for (y = 0; y < logImage->height; y++) { for (x = 0; x < logImage->width * logImage->depth; x++) row[x] = swap_ushort((unsigned short)float_uint(data[y * logImage->width * logImage->depth + x], 65535), logImage->isMSB); if (logimage_fwrite(row, rowLength, 1, logImage) == 0) { if (verbose) printf("DPX/Cineon: Error while writing file.\n"); MEM_freeN(row); return 1; } } MEM_freeN(row); return 0; } /* * Data reading */ int logImageGetDataRGBA(LogImageFile *logImage, float *data, int dataIsLinearRGB) { /* Fills data with 32 bits float RGBA values */ int i, j, returnValue, sortedElementData[8], hasAlpha; float *elementData[8]; float *elementData_ptr[8]; float *mergedData; unsigned int sampleIndex; LogImageElement mergedElement; /* Determine the depth of the picture and if there's a separate alpha element. If the element is supported, load it into an unsigned ints array. */ memset(&elementData, 0, 8 * sizeof(float *)); hasAlpha = 0; for (i = 0; i < logImage->numElements; i++) { /* descriptor_Depth and descriptor_Composite are not supported */ if (logImage->element[i].descriptor != descriptor_Depth && logImage->element[i].descriptor != descriptor_Composite) { /* Allocate memory */ elementData[i] = (float *)MEM_mallocN(logImage->width * logImage->height * logImage->element[i].depth * sizeof(float), __func__); if (elementData[i] == 0) { if (verbose) printf("DPX/Cineon: Cannot allocate memory for elementData[%d]\n.", i); for (j = 0; j < i; j++) if (elementData[j] != 0) MEM_freeN(elementData[j]); return 1; } elementData_ptr[i] = elementData[i]; /* Load data */ if (logImageElementGetData(logImage, logImage->element[i], elementData[i]) != 0) { if (verbose) printf("DPX/Cineon: Cannot read elementData[%d]\n.", i); for (j = 0; j < i; j++) if (elementData[j] != 0) MEM_freeN(elementData[j]); return 1; } } if (logImage->element[i].descriptor == descriptor_Alpha) hasAlpha = 1; } /* only one element, easy case, no need to do anything */ if (logImage->numElements == 1) { returnValue = convertLogElementToRGBA(elementData[0], data, logImage, logImage->element[0], dataIsLinearRGB); MEM_freeN(elementData[0]); } else { /* The goal here is to merge every elements into only one * to recreate a classic 16 bits RGB, RGBA or YCbCr element. * Unsupported elements are skipped (depth, composite) */ memcpy(&mergedElement, &logImage->element[0], sizeof(LogImageElement)); mergedElement.descriptor = -1; mergedElement.depth = logImage->depth; memset(&sortedElementData, -1, 8 * sizeof(int)); /* Try to know how to assemble the elements */ for (i = 0; i < logImage->numElements; i++) { switch (logImage->element[i].descriptor) { case descriptor_Red: case descriptor_RGB: if (hasAlpha == 0) mergedElement.descriptor = descriptor_RGB; else mergedElement.descriptor = descriptor_RGBA; sortedElementData[0] = i; break; case descriptor_Green: if (hasAlpha == 0) mergedElement.descriptor = descriptor_RGB; else mergedElement.descriptor = descriptor_RGBA; sortedElementData[1] = i; break; case descriptor_Blue: if (hasAlpha == 0) mergedElement.descriptor = descriptor_RGB; else mergedElement.descriptor = descriptor_RGBA; sortedElementData[2] = i; break; case descriptor_Alpha: /* Alpha component is always the last one */ sortedElementData[mergedElement.depth - 1] = i; break; case descriptor_Luminance: if (mergedElement.descriptor == -1) if (hasAlpha == 0) mergedElement.descriptor = descriptor_Luminance; else mergedElement.descriptor = descriptor_YA; else if (mergedElement.descriptor == descriptor_Chrominance) { if (mergedElement.depth == 2) mergedElement.descriptor = descriptor_CbYCrY; else if (mergedElement.depth == 3) if (hasAlpha == 0) mergedElement.descriptor = descriptor_CbYCr; else mergedElement.descriptor = descriptor_CbYACrYA; else if (mergedElement.depth == 4) mergedElement.descriptor = descriptor_CbYCrA; } /* Y component always in 1 except if it's alone or with alpha */ if (mergedElement.depth == 1 || (mergedElement.depth == 2 && hasAlpha == 1)) sortedElementData[0] = i; else sortedElementData[1] = i; break; case descriptor_Chrominance: if (mergedElement.descriptor == -1) mergedElement.descriptor = descriptor_Chrominance; else if (mergedElement.descriptor == descriptor_Luminance) { if (mergedElement.depth == 2) mergedElement.descriptor = descriptor_CbYCrY; else if (mergedElement.depth == 3) if (hasAlpha == 0) mergedElement.descriptor = descriptor_CbYCr; else mergedElement.descriptor = descriptor_CbYACrYA; else if (mergedElement.depth == 4) mergedElement.descriptor = descriptor_CbYCrA; } /* Cb and Cr always in 0 or 2 */ if (sortedElementData[0] == -1) sortedElementData[0] = i; else sortedElementData[2] = i; break; case descriptor_CbYCr: if (hasAlpha == 0) mergedElement.descriptor = descriptor_CbYCr; else mergedElement.descriptor = descriptor_CbYCrA; sortedElementData[0] = i; break; case descriptor_RGBA: case descriptor_ABGR: case descriptor_CbYACrYA: case descriptor_CbYCrY: case descriptor_CbYCrA: /* I don't think these ones can be seen in a planar image */ mergedElement.descriptor = logImage->element[i].descriptor; sortedElementData[0] = i; break; case descriptor_Depth: case descriptor_Composite: /* Not supported */ break; } } mergedData = (float *)MEM_mallocN(logImage->width * logImage->height * mergedElement.depth * sizeof(float), __func__); if (mergedData == 0) { if (verbose) printf("DPX/Cineon: Cannot allocate mergedData.\n"); for (i = 0; i < logImage->numElements; i++) if (elementData[i] != 0) MEM_freeN(elementData[i]); return 1; } sampleIndex = 0; while (sampleIndex < logImage->width * logImage->height * mergedElement.depth) { for (i = 0; i < logImage->numElements; i++) for (j = 0; j < logImage->element[sortedElementData[i]].depth; j++) mergedData[sampleIndex++] = *(elementData_ptr[sortedElementData[i]]++); } /* Done with elements data, clean-up */ for (i = 0; i < logImage->numElements; i++) if (elementData[i] != 0) MEM_freeN(elementData[i]); returnValue = convertLogElementToRGBA(mergedData, data, logImage, mergedElement, dataIsLinearRGB); MEM_freeN(mergedData); } return returnValue; } static int logImageElementGetData(LogImageFile *logImage, LogImageElement logElement, float *data) { switch (logElement.bitsPerSample) { case 1: return logImageElementGetData1(logImage, logElement, data); case 8: return logImageElementGetData8(logImage, logElement, data); case 10: if (logElement.packing == 0) return logImageElementGetData10Packed(logImage, logElement, data); else if (logElement.packing == 1 || logElement.packing == 2) return logImageElementGetData10(logImage, logElement, data); case 12: if (logElement.packing == 0) return logImageElementGetData12Packed(logImage, logElement, data); else if (logElement.packing == 1 || logElement.packing == 2) return logImageElementGetData12(logImage, logElement, data); case 16: return logImageElementGetData16(logImage, logElement, data); default: /* format not supported */ return 1; } } static int logImageElementGetData1(LogImageFile *logImage, LogImageElement logElement, float *data) { unsigned int pixel; int x, y, offset; /* seek at the right place */ if (logimage_fseek(logImage, logElement.dataOffset, SEEK_SET) != 0) { if (verbose) printf("DPX/Cineon: Couldn't seek at %d\n", logElement.dataOffset); return 1; } /* read 1 bit data padded to 32 bits */ for (y = 0; y < logImage->height; y++) { for (x = 0; x < logImage->width * logElement.depth; x += 32) { if (logimage_read_uint(&pixel, logImage) != 0) { if (verbose) printf("DPX/Cineon: EOF reached\n"); return 1; } pixel = swap_uint(pixel, logImage->isMSB); for (offset = 0; offset < 32 && x + offset < logImage->width; offset++) data[y * logImage->width * logElement.depth + x + offset] = (float)((pixel >> offset) & 0x01); } } return 0; } static int logImageElementGetData8(LogImageFile *logImage, LogImageElement logElement, float *data) { unsigned int rowLength = getRowLength(logImage->width, logElement); unsigned char pixel; int x, y; /* extract required pixels */ for (y = 0; y < logImage->height; y++) { /* 8 bits are 32-bits padded so we need to seek at each row */ if (logimage_fseek(logImage, logElement.dataOffset + y * rowLength, SEEK_SET) != 0) { if (verbose) printf("DPX/Cineon: Couldn't seek at %d\n", logElement.dataOffset + y * rowLength); return 1; } for (x = 0; x < logImage->width * logElement.depth; x++) { if (logimage_read_uchar(&pixel, logImage) != 0) { if (verbose) printf("DPX/Cineon: EOF reached\n"); return 1; } data[y * logImage->width * logElement.depth + x] = (float)pixel / 255.0f; } } return 0; } static int logImageElementGetData10(LogImageFile *logImage, LogImageElement logElement, float *data) { unsigned int pixel; int x, y, offset; /* seek to data */ if (logimage_fseek(logImage, logElement.dataOffset, SEEK_SET) != 0) { if (verbose) printf("DPX/Cineon: Couldn't seek at %d\n", logElement.dataOffset); return 1; } if (logImage->depth == 1 && logImage->srcFormat == format_DPX) { for (y = 0; y < logImage->height; y++) { offset = 32; for (x = 0; x < logImage->width * logElement.depth; x++) { /* we need to read the next long */ if (offset >= 30) { if (logElement.packing == 1) offset = 2; else if (logElement.packing == 2) offset = 0; if (logimage_read_uint(&pixel, logImage) != 0) { if (verbose) printf("DPX/Cineon: EOF reached\n"); return 1; } pixel = swap_uint(pixel, logImage->isMSB); } data[y * logImage->width * logElement.depth + x] = (float)((pixel >> offset) & 0x3ff) / 1023.0f; offset += 10; } } } else { for (y = 0; y < logImage->height; y++) { offset = -1; for (x = 0; x < logImage->width * logElement.depth; x++) { /* we need to read the next long */ if (offset < 0) { if (logElement.packing == 1) offset = 22; else if (logElement.packing == 2) offset = 20; if (logimage_read_uint(&pixel, logImage) != 0) { if (verbose) printf("DPX/Cineon: EOF reached\n"); return 1; } pixel = swap_uint(pixel, logImage->isMSB); } data[y * logImage->width * logElement.depth + x] = (float)((pixel >> offset) & 0x3ff) / 1023.0f; offset -= 10; } } } return 0; } static int logImageElementGetData10Packed(LogImageFile *logImage, LogImageElement logElement, float *data) { unsigned int rowLength = getRowLength(logImage->width, logElement); unsigned int pixel, oldPixel; int offset, offset2, x, y; /* converting bytes to pixels */ for (y = 0; y < logImage->height; y++) { /* seek to data */ if (logimage_fseek(logImage, y * rowLength + logElement.dataOffset, SEEK_SET) != 0) { if (verbose) printf("DPX/Cineon: Couldn't seek at %u\n", y * rowLength + logElement.dataOffset); return 1; } oldPixel = 0; offset = 0; offset2 = 0; for (x = 0; x < logImage->width * logElement.depth; x++) { if (offset2 != 0) { offset = 10 - offset2; offset2 = 0; oldPixel = 0; } else if (offset == 32) { offset = 0; } else if (offset + 10 > 32) { /* next pixel is on two different longs */ oldPixel = (pixel >> offset); offset2 = 32 - offset; offset = 0; } if (offset == 0) { /* we need to read the next long */ if (logimage_read_uint(&pixel, logImage) != 0) { if (verbose) printf("DPX/Cineon: EOF reached\n"); return 1; } pixel = swap_uint(pixel, logImage->isMSB); } data[y * logImage->width * logElement.depth + x] = (float)((((pixel << offset2) >> offset) & 0x3ff) | oldPixel) / 1023.0f; offset += 10; } } return 0; } static int logImageElementGetData12(LogImageFile *logImage, LogImageElement logElement, float *data) { unsigned int sampleIndex; unsigned int numSamples = logImage->width * logImage->height * logElement.depth; unsigned short pixel; /* seek to data */ if (logimage_fseek(logImage, logElement.dataOffset, SEEK_SET) != 0) { if (verbose) printf("DPX/Cineon: Couldn't seek at %d\n", logElement.dataOffset); return 1; } /* convert bytes to pixels */ sampleIndex = 0; for (sampleIndex = 0; sampleIndex < numSamples; sampleIndex++) { if (logimage_read_ushort(&pixel, logImage) != 0) { if (verbose) printf("DPX/Cineon: EOF reached\n"); return 1; } pixel = swap_ushort(pixel, logImage->isMSB); if (logElement.packing == 1) /* padded to the right */ data[sampleIndex] = (float)(pixel >> 4) / 4095.0f; else if (logElement.packing == 2) /* padded to the left */ data[sampleIndex] = (float)pixel / 4095.0f; } return 0; } static int logImageElementGetData12Packed(LogImageFile *logImage, LogImageElement logElement, float *data) { unsigned int rowLength = getRowLength(logImage->width, logElement); unsigned int pixel, oldPixel; int offset, offset2, x, y; /* converting bytes to pixels */ for (y = 0; y < logImage->height; y++) { /* seek to data */ if (logimage_fseek(logImage, y * rowLength + logElement.dataOffset, SEEK_SET) != 0) { if (verbose) printf("DPX/Cineon: Couldn't seek at %u\n", y * rowLength + logElement.dataOffset); return 1; } oldPixel = 0; offset = 0; offset2 = 0; for (x = 0; x < logImage->width * logElement.depth; x++) { if (offset2 != 0) { offset = 12 - offset2; offset2 = 0; oldPixel = 0; } else if (offset == 32) { offset = 0; } else if (offset + 12 > 32) { /* next pixel is on two different longs */ oldPixel = (pixel >> offset); offset2 = 32 - offset; offset = 0; } if (offset == 0) { /* we need to read the next long */ if (logimage_read_uint(&pixel, logImage) != 0) { if (verbose) printf("DPX/Cineon: EOF reached\n"); return 1; } pixel = swap_uint(pixel, logImage->isMSB); } data[y * logImage->width * logElement.depth + x] = (float)((((pixel << offset2) >> offset) & 0xfff) | oldPixel) / 4095.0f; offset += 12; } } return 0; } static int logImageElementGetData16(LogImageFile *logImage, LogImageElement logElement, float *data) { unsigned int numSamples = logImage->width * logImage->height * logElement.depth; unsigned int sampleIndex; unsigned short pixel; /* seek to data */ if (logimage_fseek(logImage, logElement.dataOffset, SEEK_SET) != 0) { if (verbose) printf("DPX/Cineon: Couldn't seek at %d\n", logElement.dataOffset); return 1; } for (sampleIndex = 0; sampleIndex < numSamples; sampleIndex++) { if (logimage_read_ushort(&pixel, logImage) != 0) { if (verbose) printf("DPX/Cineon: EOF reached\n"); return 1; } pixel = swap_ushort(pixel, logImage->isMSB); data[sampleIndex] = (float)pixel / 65535.0f; } return 0; } /* * Color conversion */ static int getYUVtoRGBMatrix(float *matrix, LogImageElement logElement) { float scaleY, scaleCbCr; float refHighData = (float)logElement.refHighData / logElement.maxValue; float refLowData = (float)logElement.refLowData / logElement.maxValue; scaleY = 1.0f / (refHighData - refLowData); scaleCbCr = scaleY * ((940.0f - 64.0f) / (960.0f - 64.0f)); switch (logElement.transfer) { case 2: /* linear */ matrix[0] = 1.0f * scaleY; matrix[1] = 1.0f * scaleCbCr; matrix[2] = 1.0f * scaleCbCr; matrix[3] = 1.0f * scaleY; matrix[4] = 1.0f * scaleCbCr; matrix[5] = 1.0f * scaleCbCr; matrix[6] = 1.0f * scaleY; matrix[7] = 1.0f * scaleCbCr; matrix[8] = 1.0f * scaleCbCr; return 0; case 5: /* SMPTE 240M */ matrix[0] = 1.0000f * scaleY; matrix[1] = 0.0000f * scaleCbCr; matrix[2] = 1.5756f * scaleCbCr; matrix[3] = 1.0000f * scaleY; matrix[4] = -0.2253f * scaleCbCr; matrix[5] = -0.5000f * scaleCbCr; matrix[6] = 1.0000f * scaleY; matrix[7] = 1.8270f * scaleCbCr; matrix[8] = 0.0000f * scaleCbCr; return 0; case 6: /* CCIR 709-1 */ matrix[0] = 1.000000f * scaleY; matrix[1] = 0.000000f * scaleCbCr; matrix[2] = 1.574800f * scaleCbCr; matrix[3] = 1.000000f * scaleY; matrix[4] = -0.187324f * scaleCbCr; matrix[5] = -0.468124f * scaleCbCr; matrix[6] = 1.000000f * scaleY; matrix[7] = 1.855600f * scaleCbCr; matrix[8] = 0.000000f * scaleCbCr; return 0; case 7: /* CCIR 601 */ case 8: /* I'm not sure 7 and 8 should share the same matrix */ matrix[0] = 1.000000f * scaleY; matrix[1] = 0.000000f * scaleCbCr; matrix[2] = 1.402000f * scaleCbCr; matrix[3] = 1.000000f * scaleY; matrix[4] = -0.344136f * scaleCbCr; matrix[5] = -0.714136f * scaleCbCr; matrix[6] = 1.000000f * scaleY; matrix[7] = 1.772000f * scaleCbCr; matrix[8] = 0.000000f * scaleCbCr; return 0; default: return 1; } } static void getLinToLogLut(float *lut, LogImageFile *logImage, LogImageElement logElement) { float gain, negativeFilmGamma, offset, step; unsigned int i; negativeFilmGamma = 0.6; step = logElement.refHighQuantity / logElement.maxValue; gain = logElement.maxValue / (1.0f - powf(10, (logImage->referenceBlack - logImage->referenceWhite) * step / negativeFilmGamma * logImage->gamma / 1.7f)); offset = gain - logElement.maxValue; for (i = 0; i < (int)(logElement.maxValue + 1); i++) lut[i] = (logImage->referenceWhite + log10f(powf((i + offset) / gain, 1.7f / logImage->gamma)) / (step / negativeFilmGamma)) / logElement.maxValue; } static void getLogToLinLut(float *lut, LogImageFile *logImage, LogImageElement logElement) { float breakPoint, gain, kneeGain, kneeOffset, negativeFilmGamma, offset, step, softClip; /* float filmGamma; unused */ unsigned int i; /* Building the Log -> Lin LUT */ step = logElement.refHighQuantity / logElement.maxValue; negativeFilmGamma = 0.6; /* these are default values */ /* filmGamma = 2.2f; unused */ softClip = 0; breakPoint = logImage->referenceWhite - softClip; gain = logElement.maxValue / (1.0f - powf(10, (logImage->referenceBlack - logImage->referenceWhite) * step / negativeFilmGamma * logImage->gamma / 1.7f)); offset = gain - logElement.maxValue; kneeOffset = powf(10, (breakPoint - logImage->referenceWhite) * step / negativeFilmGamma * logImage->gamma / 1.7f) * gain - offset; kneeGain = (logElement.maxValue - kneeOffset) / powf(5 * softClip, softClip / 100); for (i = 0; i < (int)(logElement.maxValue + 1); i++) { if (i < logImage->referenceBlack) lut[i] = 0.0f; else if (i > breakPoint) lut[i] = (powf(i - breakPoint, softClip / 100) * kneeGain + kneeOffset) / logElement.maxValue; else lut[i] = (powf(10, ((float)i - logImage->referenceWhite) * step / negativeFilmGamma * logImage->gamma / 1.7f) * gain - offset) / logElement.maxValue; } } static void getLinToSrgbLut(float *lut, LogImageElement logElement) { unsigned int i; float col; for (i = 0; i < (int)(logElement.maxValue + 1); i++) { col = (float)i / logElement.maxValue; if (col < 0.0031308f) lut[i] = (col < 0.0f) ? 0.0f : col * 12.92f; else lut[i] = 1.055f * powf(col, 1.0f / 2.4f) - 0.055f; } } static void getSrgbToLinLut(float *lut, LogImageElement logElement) { unsigned int i; float col; for (i = 0; i < (int)(logElement.maxValue + 1); i++) { col = (float)i / logElement.maxValue; if (col < 0.04045f) lut[i] = (col < 0.0f) ? 0.0f : col * (1.0f / 12.92f); else lut[i] = powf((col + 0.055f) * (1.0f / 1.055f), 2.4f); } } static int convertRGBA_RGB(float *src, float *dst, LogImageFile *logImage, LogImageElement logElement, int elementIsSource) { unsigned int i; float lut[65536]; float *src_ptr = src; float *dst_ptr = dst; switch (logElement.transfer) { case transfer_UserDefined: case transfer_Linear: case transfer_Logarithmic: for (i = 0; i < logImage->width * logImage->height; i++) { *(dst_ptr++) = *(src_ptr++); *(dst_ptr++) = *(src_ptr++); *(dst_ptr++) = *(src_ptr++); src_ptr++; } return 0; case transfer_PrintingDensity: if (elementIsSource == 1) getLogToLinLut((float *)&lut, logImage, logElement); else getLinToLogLut((float *)&lut, logImage, logElement); for (i = 0; i < logImage->width * logImage->height; i++) { *(dst_ptr++) = lut[float_uint(*(src_ptr++), logElement.maxValue)]; *(dst_ptr++) = lut[float_uint(*(src_ptr++), logElement.maxValue)]; *(dst_ptr++) = lut[float_uint(*(src_ptr++), logElement.maxValue)]; src_ptr++; } return 0; default: return 1; } } static int convertRGB_RGBA(float *src, float *dst, LogImageFile *logImage, LogImageElement logElement, int elementIsSource) { unsigned int i; float lut[65536]; float *src_ptr = src; float *dst_ptr = dst; switch (logElement.transfer) { case transfer_UserDefined: case transfer_Linear: case transfer_Logarithmic: for (i = 0; i < logImage->width * logImage->height; i++) { *(dst_ptr++) = *(src_ptr++); *(dst_ptr++) = *(src_ptr++); *(dst_ptr++) = *(src_ptr++); *(dst_ptr++) = 1.0f; } return 0; case transfer_PrintingDensity: if (elementIsSource == 1) getLogToLinLut((float *)&lut, logImage, logElement); else getLinToLogLut((float *)&lut, logImage, logElement); for (i = 0; i < logImage->width * logImage->height; i++) { *(dst_ptr++) = lut[float_uint(*(src_ptr++), logElement.maxValue)]; *(dst_ptr++) = lut[float_uint(*(src_ptr++), logElement.maxValue)]; *(dst_ptr++) = lut[float_uint(*(src_ptr++), logElement.maxValue)]; *(dst_ptr++) = 1.0f; } return 0; default: return 1; } } static int convertRGBA_RGBA(float *src, float *dst, LogImageFile *logImage, LogImageElement logElement, int elementIsSource) { unsigned int i; float lut[65536]; float *src_ptr = src; float *dst_ptr = dst; switch (logElement.transfer) { case transfer_UserDefined: case transfer_Linear: case transfer_Logarithmic: memcpy(dst, src, 4 * logImage->width * logImage->height * sizeof(float)); return 0; case transfer_PrintingDensity: if (elementIsSource == 1) getLogToLinLut((float *)&lut, logImage, logElement); else getLinToLogLut((float *)&lut, logImage, logElement); for (i = 0; i < logImage->width * logImage->height; i++) { *(dst_ptr++) = lut[float_uint(*(src_ptr++), logElement.maxValue)]; *(dst_ptr++) = lut[float_uint(*(src_ptr++), logElement.maxValue)]; *(dst_ptr++) = lut[float_uint(*(src_ptr++), logElement.maxValue)]; *(dst_ptr++) = *(src_ptr++); } return 0; default: return 1; } } static int convertABGR_RGBA(float *src, float *dst, LogImageFile *logImage, LogImageElement logElement, int elementIsSource) { unsigned int i; float lut[65536]; float *src_ptr = src; float *dst_ptr = dst; switch (logElement.transfer) { case transfer_UserDefined: case transfer_Linear: case transfer_Logarithmic: for (i = 0; i < logImage->width * logImage->height; i++) { src_ptr += 4; *(dst_ptr++) = *(src_ptr--); *(dst_ptr++) = *(src_ptr--); *(dst_ptr++) = *(src_ptr--); *(dst_ptr++) = *(src_ptr--); src_ptr += 4; } return 0; case transfer_PrintingDensity: if (elementIsSource == 1) getLogToLinLut((float *)&lut, logImage, logElement); else getLinToLogLut((float *)&lut, logImage, logElement); for (i = 0; i < logImage->width * logImage->height; i++) { src_ptr += 4; *(dst_ptr++) = lut[float_uint(*(src_ptr--), logElement.maxValue)]; *(dst_ptr++) = lut[float_uint(*(src_ptr--), logElement.maxValue)]; *(dst_ptr++) = lut[float_uint(*(src_ptr--), logElement.maxValue)]; *(dst_ptr++) = *(src_ptr--); src_ptr += 4; } return 0; default: return 1; } } static int convertCbYCr_RGBA(float *src, float *dst, LogImageFile *logImage, LogImageElement logElement) { unsigned int i; float conversionMatrix[9], refLowData, y, cb, cr; float *src_ptr = src; float *dst_ptr = dst; if (getYUVtoRGBMatrix((float *)&conversionMatrix, logElement) != 0) return 1; refLowData = (float)logElement.refLowData / logElement.maxValue; for (i = 0; i < logImage->width * logImage->height; i++) { cb = *(src_ptr++) - 0.5f; y = *(src_ptr++) - refLowData; cr = *(src_ptr++) - 0.5f; *(dst_ptr++) = clamp_float(y * conversionMatrix[0] + cb * conversionMatrix[1] + cr * conversionMatrix[2], 0.0f, 1.0f); *(dst_ptr++) = clamp_float(y * conversionMatrix[3] + cb * conversionMatrix[4] + cr * conversionMatrix[5], 0.0f, 1.0f); *(dst_ptr++) = clamp_float(y * conversionMatrix[6] + cb * conversionMatrix[7] + cr * conversionMatrix[8], 0.0f, 1.0f); *(dst_ptr++) = 1.0f; } return 0; } static int convertCbYCrA_RGBA(float *src, float *dst, LogImageFile *logImage, LogImageElement logElement) { unsigned int i; float conversionMatrix[9], refLowData, y, cb, cr, a; float *src_ptr = src; float *dst_ptr = dst; if (getYUVtoRGBMatrix((float *)&conversionMatrix, logElement) != 0) return 1; refLowData = (float)logElement.refLowData / logElement.maxValue; for (i = 0; i < logImage->width * logImage->height; i++) { cb = *(src_ptr++) - 0.5f; y = *(src_ptr++) - refLowData; cr = *(src_ptr++) - 0.5f; a = *(src_ptr++); *(dst_ptr++) = clamp_float(y * conversionMatrix[0] + cb * conversionMatrix[1] + cr * conversionMatrix[2], 0.0f, 1.0f); *(dst_ptr++) = clamp_float(y * conversionMatrix[3] + cb * conversionMatrix[4] + cr * conversionMatrix[5], 0.0f, 1.0f); *(dst_ptr++) = clamp_float(y * conversionMatrix[6] + cb * conversionMatrix[7] + cr * conversionMatrix[8], 0.0f, 1.0f); *(dst_ptr++) = a; } return 0; } static int convertCbYCrY_RGBA(float *src, float *dst, LogImageFile *logImage, LogImageElement logElement) { unsigned int i; float conversionMatrix[9], refLowData, y1, y2, cb, cr; float *src_ptr = src; float *dst_ptr = dst; if (getYUVtoRGBMatrix((float *)&conversionMatrix, logElement) != 0) return 1; refLowData = (float)logElement.refLowData / logElement.maxValue; for (i = 0; i < logImage->width * logImage->height / 2; i++) { cb = *(src_ptr++) - 0.5f; y1 = *(src_ptr++) - refLowData; cr = *(src_ptr++) - 0.5f; y2 = *(src_ptr++) - refLowData; *(dst_ptr++) = clamp_float(y1 * conversionMatrix[0] + cb * conversionMatrix[1] + cr * conversionMatrix[2], 0.0f, 1.0f); *(dst_ptr++) = clamp_float(y1 * conversionMatrix[3] + cb * conversionMatrix[4] + cr * conversionMatrix[5], 0.0f, 1.0f); *(dst_ptr++) = clamp_float(y1 * conversionMatrix[6] + cb * conversionMatrix[7] + cr * conversionMatrix[8], 0.0f, 1.0f); *(dst_ptr++) = 1.0f; *(dst_ptr++) = clamp_float(y2 * conversionMatrix[0] + cb * conversionMatrix[1] + cr * conversionMatrix[2], 0.0f, 1.0f); *(dst_ptr++) = clamp_float(y2 * conversionMatrix[3] + cb * conversionMatrix[4] + cr * conversionMatrix[5], 0.0f, 1.0f); *(dst_ptr++) = clamp_float(y2 * conversionMatrix[6] + cb * conversionMatrix[7] + cr * conversionMatrix[8], 0.0f, 1.0f); *(dst_ptr++) = 1.0f; } return 0; } static int convertCbYACrYA_RGBA(float *src, float *dst, LogImageFile *logImage, LogImageElement logElement) { unsigned int i; float conversionMatrix[9], refLowData, y1, y2, cb, cr, a1, a2; float *src_ptr = src; float *dst_ptr = dst; if (getYUVtoRGBMatrix((float *)&conversionMatrix, logElement) != 0) return 1; refLowData = (float)logElement.refLowData / logElement.maxValue; for (i = 0; i < logImage->width * logImage->height / 2; i++) { cb = *(src_ptr++) - 0.5f; y1 = *(src_ptr++) - refLowData; a1 = *(src_ptr++); cr = *(src_ptr++) - 0.5f; y2 = *(src_ptr++) - refLowData; a2 = *(src_ptr++); *(dst_ptr++) = clamp_float(y1 * conversionMatrix[0] + cb * conversionMatrix[1] + cr * conversionMatrix[2], 0.0f, 1.0f); *(dst_ptr++) = clamp_float(y1 * conversionMatrix[3] + cb * conversionMatrix[4] + cr * conversionMatrix[5], 0.0f, 1.0f); *(dst_ptr++) = clamp_float(y1 * conversionMatrix[6] + cb * conversionMatrix[7] + cr * conversionMatrix[8], 0.0f, 1.0f); *(dst_ptr++) = a1; *(dst_ptr++) = clamp_float(y2 * conversionMatrix[0] + cb * conversionMatrix[1] + cr * conversionMatrix[2], 0.0f, 1.0f); *(dst_ptr++) = clamp_float(y2 * conversionMatrix[3] + cb * conversionMatrix[4] + cr * conversionMatrix[5], 0.0f, 1.0f); *(dst_ptr++) = clamp_float(y2 * conversionMatrix[6] + cb * conversionMatrix[7] + cr * conversionMatrix[8], 0.0f, 1.0f); *(dst_ptr++) = a2; } return 0; } static int convertLuminance_RGBA(float *src, float *dst, LogImageFile *logImage, LogImageElement logElement) { unsigned int i; float conversionMatrix[9], value, refLowData; float *src_ptr = src; float *dst_ptr = dst; if (getYUVtoRGBMatrix((float *)&conversionMatrix, logElement) != 0) return 1; refLowData = (float)logElement.refLowData / logElement.maxValue; for (i = 0; i < logImage->width * logImage->height; i++) { value = clamp_float((*(src_ptr++) - refLowData) * conversionMatrix[0], 0.0f, 1.0f); *(dst_ptr++) = value; *(dst_ptr++) = value; *(dst_ptr++) = value; *(dst_ptr++) = 1.0f; } return 0; } static int convertYA_RGBA(float *src, float *dst, LogImageFile *logImage, LogImageElement logElement) { unsigned int i; float conversionMatrix[9], value, refLowData; float *src_ptr = src; float *dst_ptr = dst; if (getYUVtoRGBMatrix((float *)&conversionMatrix, logElement) != 0) return 1; refLowData = (float)logElement.refLowData / logElement.maxValue; for (i = 0; i < logImage->width * logImage->height; i++) { value = clamp_float((*(src_ptr++) - refLowData) * conversionMatrix[0], 0.0f, 1.0f); *(dst_ptr++) = value; *(dst_ptr++) = value; *(dst_ptr++) = value; *(dst_ptr++) = *(src_ptr++); } return 0; } static int convertLogElementToRGBA(float *src, float *dst, LogImageFile *logImage, LogImageElement logElement, int dstIsLinearRGB) { int rvalue; unsigned int i; float *src_ptr; float *dst_ptr; float lut[65536]; /* Convert data in src to linear RGBA in dst */ switch (logElement.descriptor) { case descriptor_RGB: rvalue = convertRGB_RGBA(src, dst, logImage, logElement, 1); break; case descriptor_RGBA: rvalue = convertRGBA_RGBA(src, dst, logImage, logElement, 1); break; case descriptor_ABGR: rvalue = convertABGR_RGBA(src, dst, logImage, logElement, 1); break; case descriptor_Luminance: rvalue = convertLuminance_RGBA(src, dst, logImage, logElement); break; case descriptor_CbYCr: rvalue = convertCbYCr_RGBA(src, dst, logImage, logElement); break; case descriptor_CbYCrY: rvalue = convertCbYCrY_RGBA(src, dst, logImage, logElement); break; case descriptor_CbYACrYA: rvalue = convertCbYACrYA_RGBA(src, dst, logImage, logElement); break; case descriptor_CbYCrA: rvalue = convertCbYCrA_RGBA(src, dst, logImage, logElement); break; case descriptor_YA: /* this descriptor is for internal use only */ rvalue = convertYA_RGBA(src, dst, logImage, logElement); break; default: return 1; } if (rvalue == 1) return 1; else if (dstIsLinearRGB) { /* convert data from sRGB to Linear RGB via lut */ getSrgbToLinLut((float *)&lut, logElement); src_ptr = dst; // no error here dst_ptr = dst; for (i = 0; i < logImage->width * logImage->height; i++) { *(dst_ptr++) = lut[float_uint(*(src_ptr++), logElement.maxValue)]; *(dst_ptr++) = lut[float_uint(*(src_ptr++), logElement.maxValue)]; *(dst_ptr++) = lut[float_uint(*(src_ptr++), logElement.maxValue)]; dst_ptr++; src_ptr++; } } return 0; } static int convertRGBAToLogElement(float *src, float *dst, LogImageFile *logImage, LogImageElement logElement, int srcIsLinearRGB) { unsigned int i; int rvalue; float *srgbSrc; float *srgbSrc_ptr; float *src_ptr = src; float lut[65536]; if (srcIsLinearRGB != 0) { /* we need to convert src to sRGB */ srgbSrc = (float *)MEM_mallocN(4 * logImage->width * logImage->height * sizeof(float), __func__); if (srgbSrc == 0) return 1; memcpy(srgbSrc, src, 4 * logImage->width * logImage->height * sizeof(float)); srgbSrc_ptr = srgbSrc; /* convert data from Linear RGB to sRGB via lut */ getLinToSrgbLut((float *)&lut, logElement); for (i = 0; i < logImage->width * logImage->height; i++) { *(srgbSrc_ptr++) = lut[float_uint(*(src_ptr++), logElement.maxValue)]; *(srgbSrc_ptr++) = lut[float_uint(*(src_ptr++), logElement.maxValue)]; *(srgbSrc_ptr++) = lut[float_uint(*(src_ptr++), logElement.maxValue)]; srgbSrc_ptr++; src_ptr++; } } else srgbSrc = src; /* Convert linear RGBA data in src to format described by logElement in dst */ switch (logElement.descriptor) { case descriptor_RGB: rvalue = convertRGBA_RGB(srgbSrc, dst, logImage, logElement, 0); break; case descriptor_RGBA: rvalue = convertRGBA_RGBA(srgbSrc, dst, logImage, logElement, 0); break; /* these ones are not supported for the moment */ case descriptor_ABGR: case descriptor_Luminance: case descriptor_CbYCr: case descriptor_CbYCrY: case descriptor_CbYACrYA: case descriptor_CbYCrA: case descriptor_YA: /* this descriptor is for internal use only */ default: rvalue = 1; } if (srcIsLinearRGB != 0) { MEM_freeN(srgbSrc); } return rvalue; }