diff --git a/libopendcp/codecs/opendcp_decoder_openexr.c b/libopendcp/codecs/opendcp_decoder_openexr.c index 5842ba7..81a8806 100644 --- a/libopendcp/codecs/opendcp_decoder_openexr.c +++ b/libopendcp/codecs/opendcp_decoder_openexr.c @@ -47,6 +47,7 @@ #include #include #include +#include #include "opendcp.h" #include "opendcp_image.h" @@ -58,11 +59,11 @@ typedef enum { EXR_COMPRESSION_ZIPS = 2, /* zip single line (not supported) */ EXR_COMPRESSION_ZIP = 3, /* zip 16 lines */ EXR_COMPRESSION_PIZ = 4, /* piz (not supported) */ - EXR_COMPRESSION_PXR24 = 5 /* pixar 24 bit (not supported) */ - EXR_COMPRESSION_B44 = 6 /* b44 (not supported) */ - EXR_COMPRESSION_B44A = 7 /* b44a (not supported) */ - EXR_COMPRESSION_DWAA = 8 /* dwaa 32 lines (not supported) */ - EXR_COMPRESSION_DWAB = 9 /* dwab 256 lines (not supported) */ + EXR_COMPRESSION_PXR24 = 5, /* pixar 24 bit (not supported) */ + EXR_COMPRESSION_B44 = 6, /* b44 (not supported) */ + EXR_COMPRESSION_B44A = 7, /* b44a (not supported) */ + EXR_COMPRESSION_DWAA = 8, /* dwaa 32 lines (not supported) */ + EXR_COMPRESSION_DWAB = 9, /* dwab 256 lines (not supported) */ } exr_compression_enum; typedef enum { @@ -73,7 +74,7 @@ typedef enum { typedef struct { char name[255]; /* channel name */ - unsigned char dataType; /* channel data type, int, half, float */ + unsigned char data_type; /* channel data type, int, half, float */ unsigned char non_linear; /* non linear, only use for B44 and B44A compression */ unsigned int sample_x; /* sample x direction, only support == 1 */ unsigned int sample_y; /* sample y direction, only support == 1 */ @@ -98,7 +99,8 @@ typedef struct { typedef struct { exr_channel_list channel_list; /* channel list */ unsigned char compression; /* compression */ - exr_window dataWindow; /* data window */ + exr_window data_window; /* data window */ + exr_window display_window; /* display window */ } exr_attributes; /* exr chunk data */ @@ -340,16 +342,16 @@ exr_attributes read_attributes( FILE *exr_fp ) { else if( !strcmp( "compression", attribute_name ) ) attributes.compression = fgetc( exr_fp ); else if( !strcmp( "dataWindow", attribute_name ) ) { - fread( &(attributes.dataWindow.left), 4, 1, exr_fp ); - fread( &(attributes.dataWindow.bottom), 4, 1, exr_fp ); - fread( &(attributes.dataWindow.right), 4, 1, exr_fp ); - fread( &(attributes.dataWindow.top), 4, 1, exr_fp ); + fread( &(attributes.data_window.left), 4, 1, exr_fp ); + fread( &(attributes.data_window.bottom), 4, 1, exr_fp ); + fread( &(attributes.data_window.right), 4, 1, exr_fp ); + fread( &(attributes.data_window.top), 4, 1, exr_fp ); } else if( !strcmp( "displayWindow", attribute_name ) ) { - fread( &(attributes.displayWindow.left), 4, 1, exr_fp ); - fread( &(attributes.displayWindow.bottom), 4, 1, exr_fp ); - fread( &(attributes.displayWindow.right), 4, 1, exr_fp ); - fread( &(attributes.displayWindow.top), 4, 1, exr_fp ); + fread( &(attributes.display_window.left), 4, 1, exr_fp ); + fread( &(attributes.display_window.bottom), 4, 1, exr_fp ); + fread( &(attributes.display_window.right), 4, 1, exr_fp ); + fread( &(attributes.display_window.top), 4, 1, exr_fp ); } else // ---- skip attribute @@ -367,7 +369,7 @@ exr_attributes read_attributes( FILE *exr_fp ) { exr_chunk_data read_chunk_data( FILE *exr_fp, exr_attributes *attributes ) { exr_chunk_data chunk_data; - unsigned short num_rows = (attributes->dataWindow.top - attributes->dataWindow.bottom) + 1; + unsigned short num_rows = (attributes->data_window.top - attributes->data_window.bottom) + 1; // ---- if EXR_COMPRESSION_ZIP, 16 rows per chunk if( attributes->compression == EXR_COMPRESSION_ZIP ) { // @@ -432,7 +434,8 @@ void unfilter_buffer( unsigned char *buffer, unsigned char *unfilteredBuffer, un } /* uncompress rle - from OpenEXR library */ -void uncompress_rle( unsigned char *compressed_buffer, unsigned int compressed_buffer_length, unsigned char *uncompressed_buffer, unsigned int uncompressed_buffer_length) { +void uncompress_rle( unsigned char *compressed_buffer, unsigned int compressed_buffer_length, + unsigned char *uncompressed_buffer, int uncompressed_buffer_length) { unsigned char *outStart = compressed_buffer; @@ -575,7 +578,7 @@ void copy_float_data( unsigned char *buffer, float *channel_data, unsigned short /* compression no */ void read_data_compression_no( FILE *exr_fp, exr_chunk_data *chunk_data, exr_attributes *attributes, exr_image_data *image_data ) { - unsigned int num_columns = (attributes->dataWindow.right - attributes->dataWindow.left) + 1; + unsigned int num_columns = (attributes->data_window.right - attributes->data_window.left) + 1; unsigned short channel_data_width = attributes->channel_list.data_width; @@ -621,7 +624,7 @@ void read_data_compression_no( FILE *exr_fp, exr_chunk_data *chunk_data, exr_att /* compression RLE */ void read_data_compression_rle( FILE *exr_fp, exr_chunk_data *chunk_data, exr_attributes *attributes, exr_image_data *image_data ) { - unsigned int num_columns = (attributes->dataWindow.right - attributes->dataWindow.left) + 1; + unsigned int num_columns = (attributes->data_window.right - attributes->data_window.left) + 1; unsigned short channel_data_width = attributes->channel_list.data_width; @@ -682,7 +685,7 @@ void read_data_compression_rle( FILE *exr_fp, exr_chunk_data *chunk_data, exr_at /* compression ZIPS an ZIP */ void read_data_compression_zip( FILE *exr_fp, exr_chunk_data *chunk_data, exr_attributes *attributes, exr_image_data *image_data ) { - unsigned int num_columns = (attributes->dataWindow.right - attributes->dataWindow.left) + 1; + unsigned int num_columns = (attributes->data_window.right - attributes->data_window.left) + 1; unsigned char num_rows = 1; unsigned char *compressed_buffer = NULL; unsigned char *uncompressed_buffer = NULL; @@ -785,7 +788,7 @@ int opendcp_decode_exr(opendcp_image_t **image_ptr, const char *sfile) { magicNumber |= fgetc(exr_fp) << 8; magicNumber |= fgetc(exr_fp); - if (readsize != MAGIC_NUMBER_EXR ) { + if ( magicNumber != MAGIC_NUMBER_EXR ) { OPENDCP_LOG(LOG_ERROR,"%-15.15s: failed to read magic number expected 0x%08x read 0x%08x","read_exr", MAGIC_NUMBER_EXR, magicNumber ); OPENDCP_LOG(LOG_ERROR,"%s is not a valid EXR file", sfile); return OPENDCP_FATAL; @@ -800,7 +803,7 @@ int opendcp_decode_exr(opendcp_image_t **image_ptr, const char *sfile) { // ---- file type: normal, deep pixel, multipart (only support normal) unsigned char type = fgetc(exr_fp); - if( type & 0x1a != 0x00 ) { + if( (type & 0x1a) != 0x00 ) { OPENDCP_LOG(LOG_ERROR,"Only support normal scanline exr file, no tile, deep pixel, multipart file"); return OPENDCP_FATAL; } @@ -810,7 +813,7 @@ int opendcp_decode_exr(opendcp_image_t **image_ptr, const char *sfile) { fgetc(exr_fp); // ---- read EXR attritubes need for dcp - exr_attributes attritbute = read_attributes( exr_fp ); + exr_attributes attributes = read_attributes( exr_fp ); // ---- check compression if( attributes.compression > EXR_COMPRESSION_ZIP ) { @@ -829,8 +832,8 @@ int opendcp_decode_exr(opendcp_image_t **image_ptr, const char *sfile) { // ---- for store image data exr_image_data image_data; - image_data.width = attributes.dataWindow.right - attributes.dataWindow.left + 1; - image_data.height = attributes.dataWindow.top - attributes.dataWindow.bottom + 1; + image_data.width = attributes.data_window.right - attributes.data_window.left + 1; + image_data.height = attributes.data_window.top - attributes.data_window.bottom + 1; // ---- create buffers for image data image_data.channel_b = malloc( image_data.width*image_data.height * sizeof( float ) ); image_data.channel_g = malloc( image_data.width*image_data.height * sizeof( float ) ); @@ -848,15 +851,17 @@ int opendcp_decode_exr(opendcp_image_t **image_ptr, const char *sfile) { fclose( exr_fp ); /* create the image (float data) */ - image = opendcp_image_create_float(3, image_data.width, mage_data.height); + opendcp_image_t *image = opendcp_image_create_float(3, image_data.width, image_data.height); - unsigned int image_size = image_data.width * mage_data.height; - for (index = 0; index < image_size; index++) { + unsigned int image_size = image_data.width * image_data.height; + unsigned int index = 0; + while( index < image_size ) { // ---- need copy float data from exr image data to float opendcp image data - // ----- correct channel order? + // ----- correct channel order? Use float data for OpenEXR image->component[0].float_data[index] = image_data.channel_b[index]; image->component[1].float_data[index] = image_data.channel_g[index]; image->component[2].float_data[index] = image_data.channel_r[index]; + index++; } // ---- free chunk table diff --git a/libopendcp/opendcp_image.c b/libopendcp/opendcp_image.c index 1673072..6c28a26 100644 --- a/libopendcp/opendcp_image.c +++ b/libopendcp/opendcp_image.c @@ -30,9 +30,10 @@ (m)<0?0:((m)>max?max:(m)) extern int rgb_to_xyz_calculate(opendcp_image_t *image, int index); +extern int rgb_to_xyz_calculate_float(opendcp_image_t *image, int index); extern int rgb_to_xyz_lut(opendcp_image_t *image, int index); -/* create opendcp image structure for int */ +/* create opendcp image structure (int data) */ opendcp_image_t *opendcp_image_create(int n_components, int w, int h) { int x; opendcp_image_t *image = 00; @@ -81,8 +82,8 @@ opendcp_image_t *opendcp_image_create(int n_components, int w, int h) { return image; } -/* create opendcp image structure for float */ -opendcp_image_t *opendcp_image_float_create(int n_components, int w, int h) { +/* create opendcp image structure (float data) */ +opendcp_image_t *opendcp_image_create_float(int n_components, int w, int h) { int x; opendcp_image_t *image = 00; @@ -102,11 +103,11 @@ opendcp_image_t *opendcp_image_float_create(int n_components, int w, int h) { for (x = 0; x < n_components; x++) { image->component[x].component_number = x; - image->component[x].float_data = (int *)malloc((w * h) * sizeof(int)); + image->component[x].float_data = (float *)malloc((w * h) * sizeof(float)); if (!image->component[x].float_data) { OPENDCP_LOG(LOG_ERROR, "unable to allocate memory for image float components"); - opendcp_image_free(image); + opendcp_image_free_float(image); return NULL; } } @@ -243,7 +244,8 @@ int opendcp_image_readline_float(opendcp_image_t *image, int y, unsigned char *d for (x = 0; x < image->w; x += 2) { i = (x + y + 0) + ((image->w - 1) * y); - /* get componets for two pixels, convert to 12 bit int */ + /* get componets for two pixels, convert to 12 bit int (multiply 4096 for 12 bit integer) */ + /* pixel 0 */ int pixel0_b = (int)4095*image->component[0].float_data[i]; int pixel0_g = (int)4095*image->component[1].float_data[i]; @@ -252,15 +254,49 @@ int opendcp_image_readline_float(opendcp_image_t *image, int y, unsigned char *d int pixel1_b = (int)4095*image->component[0].float_data[i+1]; int pixel1_g = (int)4095*image->component[1].float_data[i+1]; int pixel1_r = (int)4095*image->component[2].float_data[i+1]; + + /* check values before save them, OpenEXR file can have pixel value > 1.0f and < 0.0f */ + /* pixel 0 */ + if( pixel0_b > 4095 ) + pixel0_b = 4095; + else if( pixel0_b < 0 ) + pixel0_b = 0; + + if( pixel0_g > 4095 ) + pixel0_g = 4095; + else if( pixel0_g < 0 ) + pixel0_g = 0; + + if( pixel0_r > 4095 ) + pixel0_r = 4095; + else if( pixel0_r < 0 ) + pixel0_r = 0; + + /* pixel 1 */ + if( pixel1_b > 4095 ) + pixel1_b = 4095; + else if( pixel1_b < 0 ) + pixel1_b = 0; + + if( pixel1_g > 4095 ) + pixel1_g = 4095; + else if( pixel1_g < 0 ) + pixel1_g = 0; + + if( pixel1_r > 4095 ) + pixel1_r = 4095; + else if( pixel1_r < 0 ) + pixel1_r = 0; + /* put pixel data in dbuffer */ dbuffer[d + 0] = pixel0_b >> 4; - dbuffer[d + 1] = (pixel0_b & 0x0f) << 4 ) | ((pixel0_g >> 8) & 0x0f); + dbuffer[d + 1] = ((pixel0_b & 0x0f) << 4 ) | ((pixel0_g >> 8) & 0x0f); dbuffer[d + 2] = pixel0_g; dbuffer[d + 3] = pixel0_r >> 4; - dbuffer[d + 4] = (pixel0_r & 0x0f) << 4 ) | ((pixel1_b >> 8) & 0x0f); + dbuffer[d + 4] = ((pixel0_r & 0x0f) << 4 ) | ((pixel1_b >> 8) & 0x0f); dbuffer[d + 5] = pixel1_b; dbuffer[d + 6] = (pixel1_g >> 4); - dbuffer[d + 7] = (pixel1_g << 4 ) | (pixel1_r >> 8) & 0x0f); + dbuffer[d + 7] = ((pixel1_g & 0x0f) << 4 ) | ((pixel1_r >> 8) & 0x0f); dbuffer[d + 8] = pixel1_r; d += 9; } @@ -315,6 +351,54 @@ int check_image_compliance(int profile, opendcp_image_t *image, char *file) { return OPENDCP_NO_ERROR; } +/* check image compliance (float data) */ +int check_image_compliance_float(int profile, opendcp_image_t *image, char *file) { + int w, h; + int dci_w = MAX_WIDTH_2K; + int dci_h = MAX_HEIGHT_2K; + opendcp_image_t *tmp; + + if (image == NULL) { + OPENDCP_LOG(LOG_DEBUG, "reading file %s", file); + + if (read_image(&tmp, file) == OPENDCP_NO_ERROR) { + h = tmp->h; + w = tmp->w; + opendcp_image_free_float(tmp); + } + else { + opendcp_image_free_float(tmp); + return OPENDCP_ERROR; + } + } + else { + h = image->h; + w = image->w; + } + + if (profile == DCP_CINEMA4K) { + dci_w = dci_w *2; + dci_h = dci_h *2; + } + + if ((w != dci_w) && (h != dci_h)) { + OPENDCP_LOG(LOG_WARN, "image does not match at least one dimension of the DCI container"); + return OPENDCP_ERROR; + } + + if ((w > dci_w) || (h > dci_h)) { + OPENDCP_LOG(LOG_WARN, "image dimension exceeds DCI container"); + return OPENDCP_ERROR; + } + + if ((w % 2) || (h % 2)) { + OPENDCP_LOG(LOG_WARN, "image dimensions are not an even value"); + return OPENDCP_ERROR; + } + + return OPENDCP_NO_ERROR; +} + /* yuv444 to rgb 8888 (int data) */ rgb_pixel_float_t yuv444toRGB888(int y, int cb, int cr) { rgb_pixel_float_t p; @@ -337,7 +421,7 @@ rgb_pixel_float_t yuv444toRGB888_float(float y, float cb, float cr) { return(p); } -/* complex gamma function */ +/* complex gamma function (int data) */ float complex_gamma(float p, float gamma, int index) { float v; @@ -364,6 +448,32 @@ float complex_gamma(float p, float gamma, int index) { return v; } +/* complex gamma function (float data) */ +float complex_gamma_float(float p, float gamma, int index) { + float v; + + if (index) { + if (p > 0.081) { + v = pow((p + 0.099) / 1.099, gamma); + } + else { + v = p / 4.5; + v = pow((p + 0.099) / 1.099, gamma); + } + } + else { + if (p > 0.04045) { + v = pow((p + 0.055) / 1.055, gamma); + } + else { + v = p / 12.92; + } + } + + return v; +} + +/* adjust headroom (int data) */ int adjust_headroom(int p) { if (p < HEADROOM * 3) { return p - HEADROOM; @@ -372,6 +482,17 @@ int adjust_headroom(int p) { return p; } +/* adjust headroom (float data) */ +float adjust_headroom_float(float p) { + float h = HEADROOM/4095.0; + + if (p < h * 3) { + return p - h; + } + + return p; +} + /* dci transfer (int data) */ int dci_transfer(float p) { int v; @@ -384,12 +505,12 @@ int dci_transfer(float p) { } /* dci transfer (floatt data) */ -float dci_transfer(float p) { +float dci_transfer_float(float p) { float v; v = pow((p * DCI_COEFFICENT), DCI_DEGAMMA); // adjust value below full color pixel? - v -= HEADROOM/4096.0f; //<---- assume int data function give 12 bit data + v -= HEADROOM/4095.0f; //<---- assume int data function give 12 bit data return v; } @@ -402,7 +523,7 @@ int dci_transfer_inverse(float p) { } /* dci transfer inverse (float data) */ -float dci_transfer_inverse(float p) { +float dci_transfer_inverse_float(float p) { return (pow(p, 1 / DCI_GAMMA)); } @@ -505,9 +626,9 @@ int rgb_to_xyz_calculate_float(opendcp_image_t *image, int index) { OPENDCP_LOG(LOG_DEBUG, "gamma: %f", GAMMA[index]); for (i = 0; i < size; i++) { - s.r = complex_gamma(image->component[0].float_data[i], GAMMA[index], index); - s.g = complex_gamma(image->component[1].float_data[i], GAMMA[index], index); - s.b = complex_gamma(image->component[2].float_data[i], GAMMA[index], index); + s.r = complex_gamma_float(image->component[0].float_data[i], GAMMA[index], index); + s.g = complex_gamma_float(image->component[1].float_data[i], GAMMA[index], index); + s.b = complex_gamma_float(image->component[2].float_data[i], GAMMA[index], index); d.x = ((s.r * color_matrix[index][0][0]) + (s.g * color_matrix[index][0][1]) + (s.b * color_matrix[index][0][2])); d.y = ((s.r * color_matrix[index][1][0]) + (s.g * color_matrix[index][1][1]) + (s.b * color_matrix[index][1][2])); diff --git a/libopendcp/opendcp_image.h b/libopendcp/opendcp_image.h index db4b6cd..5434613 100644 --- a/libopendcp/opendcp_image.h +++ b/libopendcp/opendcp_image.h @@ -64,7 +64,10 @@ typedef struct { unsigned char use_float; /* flag for use float */ } opendcp_image_t; + int read_image(opendcp_image_t **image, char *file); + + /* int data functions */ void opendcp_image_free(opendcp_image_t *image); int opendcp_image_size(opendcp_image_t *opendcp_image); int opendcp_image_readline(opendcp_image_t *image, int y, unsigned char *data); @@ -72,6 +75,14 @@ int rgb_to_xyz(opendcp_image_t *image, int gamma, int method); int resize(opendcp_image_t **image, int profile, int method); rgb_pixel_float_t yuv444toRGB888(int y, int cb, int cr); opendcp_image_t *opendcp_image_create(int n_components, int w, int h); + +/* float data functions */ +void opendcp_image_free_float(opendcp_image_t *opendcp_image); +int opendcp_image_readline_float(opendcp_image_t *image, int y, unsigned char *dbuffer); +float rgb_to_xyz_float(opendcp_image_t *image, int index); +int resize_float(opendcp_image_t **image, int profile, int method); +rgb_pixel_float_t yuv444toRGB888_float(float y, float cb, float cr); +opendcp_image_t *opendcp_image_create_float(int n_components, int w, int h); #ifdef __cplusplus }