/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 1999-2001 David Hodson . */ /** \file * \ingroup imbcineon * * Cineon image file format library routines. */ #include "logImageCore.h" #include "cineonlib.h" #include "dpxlib.h" #include "logmemfile.h" #include #include #include #include "BLI_fileops.h" #include "BLI_utildefines.h" #include "IMB_imbuf.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 *logImage, LogImageElement logElement, float *data); static int logImageElementGetData1(LogImageFile *logImage, LogImageElement logElement, float *data); static int logImageElementGetData8(LogImageFile *logImage, LogImageElement logElement, float *data); static int logImageElementGetData10(LogImageFile *logImage, LogImageElement logElement, float *data); static int logImageElementGetData10Packed(LogImageFile *logImage, LogImageElement logElement, float *data); static int logImageElementGetData12(LogImageFile *logImage, LogImageElement logElement, float *data); static int logImageElementGetData12Packed(LogImageFile *logImage, LogImageElement logElement, float *data); static int logImageElementGetData16(LogImageFile *logImage, 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, const uint size) { uint magicNum; if (size < sizeof(magicNum)) { return 0; } magicNum = *(uint *)buffer; return (magicNum == DPX_FILE_MAGIC || magicNum == swap_uint(DPX_FILE_MAGIC, 1)); } int logImageIsCineon(const void *buffer, const uint size) { uint magicNum; if (size < sizeof(magicNum)) { return 0; } magicNum = *(uint *)buffer; return (magicNum == CINEON_FILE_MAGIC || magicNum == swap_uint(CINEON_FILE_MAGIC, 1)); } LogImageFile *logImageOpenFromFile(const char *filepath, int cineon) { uint magicNum; FILE *f = BLI_fopen(filepath, "rb"); (void)cineon; if (f == NULL) { return NULL; } if (fread(&magicNum, sizeof(magicNum), 1, f) != 1) { fclose(f); return NULL; } fclose(f); if (logImageIsDpx(&magicNum, sizeof(magicNum))) { return dpxOpen((const uchar *)filepath, 0, 0); } if (logImageIsCineon(&magicNum, sizeof(magicNum))) { return cineonOpen((const uchar *)filepath, 0, 0); } return NULL; } LogImageFile *logImageOpenFromMemory(const uchar *buffer, uint size) { if (logImageIsDpx(buffer, size)) { return dpxOpen(buffer, 1, size); } if (logImageIsCineon(buffer, size)) { return cineonOpen(buffer, 1, size); } return NULL; } LogImageFile *logImageCreate(const char *filepath, 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(filepath, width, height, bitsPerSample, creator); } return dpxCreate(filepath, width, height, bitsPerSample, isLogarithmic, hasAlpha, referenceWhite, referenceBlack, gamma, creator); return NULL; } void logImageClose(LogImageFile *logImage) { if (logImage != NULL) { if (logImage->file) { fclose(logImage->file); logImage->file = NULL; } MEM_freeN(logImage); } } void logImageGetSize(LogImageFile *logImage, int *width, int *height, int *depth) { *width = logImage->width; *height = logImage->height; *depth = logImage->depth; } /* * Helper */ size_t getRowLength(size_t 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 (ELEM(logElement.packing, 1, 2)) { return ((width * logElement.depth - 1) / 3 + 1) * 4; } break; case 12: if (logElement.packing == 0) { return ((width * logElement.depth * 12 - 1) / 32 + 1) * 4; } else if (ELEM(logElement.packing, 1, 2)) { return width * logElement.depth * 2; } break; case 16: return width * logElement.depth * 2; } return 0; } /* * Data writing */ int logImageSetDataRGBA(LogImageFile *logImage, float *data, int dataIsLinearRGB) { float *elementData; int returnValue; elementData = (float *)imb_alloc_pixels( logImage->width, logImage->height, logImage->depth, sizeof(float), __func__); if (elementData == NULL) { 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) { size_t rowLength = getRowLength(logImage->width, logElement); uchar *row; row = (uchar *)MEM_mallocN(rowLength, __func__); if (row == NULL) { if (verbose) { printf("DPX/Cineon: Cannot allocate row.\n"); } return 1; } memset(row, 0, rowLength); for (size_t y = 0; y < logImage->height; y++) { for (size_t x = 0; x < logImage->width * logImage->depth; x++) { row[x] = (uchar)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) { size_t rowLength = getRowLength(logImage->width, logElement); uint pixel, index; uint *row; row = (uint *)MEM_mallocN(rowLength, __func__); if (row == NULL) { if (verbose) { printf("DPX/Cineon: Cannot allocate row.\n"); } return 1; } for (size_t y = 0; y < logImage->height; y++) { int offset = 22; index = 0; pixel = 0; for (size_t x = 0; x < logImage->width * logImage->depth; x++) { pixel |= (uint)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) { size_t rowLength = getRowLength(logImage->width, logElement); ushort *row; row = (ushort *)MEM_mallocN(rowLength, __func__); if (row == NULL) { if (verbose) { printf("DPX/Cineon: Cannot allocate row.\n"); } return 1; } for (size_t y = 0; y < logImage->height; y++) { for (size_t x = 0; x < logImage->width * logImage->depth; x++) { row[x] = swap_ushort( ((ushort)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) { size_t rowLength = getRowLength(logImage->width, logElement); ushort *row; row = (ushort *)MEM_mallocN(rowLength, __func__); if (row == NULL) { if (verbose) { printf("DPX/Cineon: Cannot allocate row.\n"); } return 1; } for (size_t y = 0; y < logImage->height; y++) { for (size_t x = 0; x < logImage->width * logImage->depth; x++) { row[x] = swap_ushort( (ushort)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; uint 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 uints 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 (!ELEM(logImage->element[i].descriptor, descriptor_Depth, descriptor_Composite)) { /* Allocate memory */ elementData[i] = imb_alloc_pixels( logImage->width, logImage->height, logImage->element[i].depth, sizeof(float), __func__); if (elementData[i] == NULL) { if (verbose) { printf("DPX/Cineon: Cannot allocate memory for elementData[%d]\n.", i); } for (j = 0; j < i; j++) { if (elementData[j] != NULL) { 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] != NULL) { 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, sizeof(int[8])); /* 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 *)imb_alloc_pixels( logImage->width, logImage->height, mergedElement.depth, sizeof(float), __func__); if (mergedData == NULL) { if (verbose) { printf("DPX/Cineon: Cannot allocate mergedData.\n"); } for (i = 0; i < logImage->numElements; i++) { if (elementData[i] != NULL) { 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] != NULL) { 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 (ELEM(logElement.packing, 1, 2)) { return logImageElementGetData10(logImage, logElement, data); } break; case 12: if (logElement.packing == 0) { return logImageElementGetData12Packed(logImage, logElement, data); } else if (ELEM(logElement.packing, 1, 2)) { return logImageElementGetData12(logImage, logElement, data); } break; case 16: return logImageElementGetData16(logImage, logElement, data); } /* format not supported */ return 1; } static int logImageElementGetData1(LogImageFile *logImage, LogImageElement logElement, float *data) { uint pixel; /* 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 (size_t y = 0; y < logImage->height; y++) { for (size_t 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 (int 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) { size_t rowLength = getRowLength(logImage->width, logElement); uchar pixel; /* extract required pixels */ for (size_t 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", (int)(logElement.dataOffset + y * rowLength)); } return 1; } for (size_t 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) { uint 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; } if (logImage->depth == 1 && logImage->srcFormat == format_DPX) { for (size_t y = 0; y < logImage->height; y++) { int offset = 32; for (size_t 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 (size_t y = 0; y < logImage->height; y++) { int offset = -1; for (size_t 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) { size_t rowLength = getRowLength(logImage->width, logElement); uint pixel, oldPixel; /* converting bytes to pixels */ for (size_t 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", (uint)(y * rowLength + logElement.dataOffset)); } return 1; } oldPixel = 0; int offset = 0; int offset2 = 0; for (size_t 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) { uint sampleIndex; uint numSamples = logImage->width * logImage->height * logElement.depth; ushort 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) { size_t rowLength = getRowLength(logImage->width, logElement); uint pixel, oldPixel; /* converting bytes to pixels */ for (size_t 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", (uint)(y * rowLength + logElement.dataOffset)); } return 1; } oldPixel = 0; int offset = 0; int offset2 = 0; for (size_t 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) { uint numSamples = logImage->width * logImage->height * logElement.depth; uint sampleIndex; ushort 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 float *getLinToLogLut(LogImageFile *logImage, LogImageElement logElement) { float *lut; float gain, negativeFilmGamma, offset, step; uint lutsize = (uint)(logElement.maxValue + 1); uint i; lut = MEM_mallocN(sizeof(float) * lutsize, "getLinToLogLut"); 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 < lutsize; i++) { lut[i] = (logImage->referenceWhite + log10f(powf((i + offset) / gain, 1.7f / logImage->gamma)) / (step / negativeFilmGamma)) / logElement.maxValue; } return lut; } static float *getLogToLinLut(LogImageFile *logImage, LogImageElement logElement) { float *lut; float breakPoint, gain, kneeGain, kneeOffset, negativeFilmGamma, offset, step, softClip; /* float filmGamma; unused */ uint lutsize = (uint)(logElement.maxValue + 1); uint i; lut = MEM_mallocN(sizeof(float) * lutsize, "getLogToLinLut"); /* 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 < lutsize; 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; } } return lut; } static float *getLinToSrgbLut(LogImageElement logElement) { float col, *lut; uint lutsize = (uint)(logElement.maxValue + 1); uint i; lut = MEM_mallocN(sizeof(float) * lutsize, "getLogToLinLut"); for (i = 0; i < lutsize; 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; } } return lut; } static float *getSrgbToLinLut(LogImageElement logElement) { float col, *lut; uint lutsize = (uint)(logElement.maxValue + 1); uint i; lut = MEM_mallocN(sizeof(float) * lutsize, "getLogToLinLut"); for (i = 0; i < lutsize; 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); } } return lut; } static int convertRGBA_RGB(float *src, float *dst, LogImageFile *logImage, LogImageElement logElement, int elementIsSource) { uint i; float *src_ptr = src; float *dst_ptr = dst; switch (logElement.transfer) { case transfer_Unspecified: 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: { float *lut; if (elementIsSource == 1) { lut = getLogToLinLut(logImage, logElement); } else { lut = getLinToLogLut(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++; } MEM_freeN(lut); return 0; } default: if (verbose) { printf("DPX/Cineon: Unknown transfer %d.\n", logElement.transfer); } return 1; } } static int convertRGB_RGBA(float *src, float *dst, LogImageFile *logImage, LogImageElement logElement, int elementIsSource) { uint i; float *src_ptr = src; float *dst_ptr = dst; switch (logElement.transfer) { case transfer_Unspecified: 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: { float *lut; if (elementIsSource == 1) { lut = getLogToLinLut(logImage, logElement); } else { lut = getLinToLogLut(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; } MEM_freeN(lut); return 0; } default: if (verbose) { printf("DPX/Cineon: Unknown transfer %d.\n", logElement.transfer); } return 1; } } static int convertRGBA_RGBA(float *src, float *dst, LogImageFile *logImage, LogImageElement logElement, int elementIsSource) { uint i; float *src_ptr = src; float *dst_ptr = dst; switch (logElement.transfer) { case transfer_UserDefined: case transfer_Linear: case transfer_Logarithmic: { memcpy(dst, src, 4 * (size_t)logImage->width * (size_t)logImage->height * sizeof(float)); return 0; } case transfer_PrintingDensity: { float *lut; if (elementIsSource == 1) { lut = getLogToLinLut(logImage, logElement); } else { lut = getLinToLogLut(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++); } MEM_freeN(lut); return 0; } default: return 1; } } static int convertABGR_RGBA(float *src, float *dst, LogImageFile *logImage, LogImageElement logElement, int elementIsSource) { uint i; 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: { float *lut; if (elementIsSource == 1) { lut = getLogToLinLut(logImage, logElement); } else { lut = getLinToLogLut(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; } MEM_freeN(lut); return 0; } default: return 1; } } static int convertCbYCr_RGBA(float *src, float *dst, LogImageFile *logImage, LogImageElement logElement) { uint 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) { uint 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) { uint 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) { uint 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) { uint 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) { uint 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; uint i; float *src_ptr; float *dst_ptr; /* 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; } if (dstIsLinearRGB) { /* convert data from sRGB to Linear RGB via lut */ float *lut = getSrgbToLinLut(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++; } MEM_freeN(lut); } return 0; } static int convertRGBAToLogElement( float *src, float *dst, LogImageFile *logImage, LogImageElement logElement, int srcIsLinearRGB) { uint i; int rvalue; float *srgbSrc; float *srgbSrc_ptr; float *src_ptr = src; float *lut; if (srcIsLinearRGB != 0) { /* we need to convert src to sRGB */ srgbSrc = (float *)imb_alloc_pixels( logImage->width, logImage->height, 4, sizeof(float), __func__); if (srgbSrc == NULL) { return 1; } memcpy(srgbSrc, src, 4 * (size_t)logImage->width * (size_t)logImage->height * sizeof(float)); srgbSrc_ptr = srgbSrc; /* convert data from Linear RGB to sRGB via lut */ lut = getLinToSrgbLut(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++; } MEM_freeN(lut); } 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; break; } if (srcIsLinearRGB != 0) { MEM_freeN(srgbSrc); } return rvalue; }