diff options
author | Jacques Lucke <jacques@blender.org> | 2022-06-22 11:56:21 +0300 |
---|---|---|
committer | Jacques Lucke <jacques@blender.org> | 2022-06-22 11:56:21 +0300 |
commit | 2d0dc88209e166159bd0b68dc54103280b09193c (patch) | |
tree | 3875e9554c810d14fa3a490cbdf003472bd7ee3c /source/blender/imbuf/intern | |
parent | c57ed65cc88418e290401599e28d51f1acb5dfd9 (diff) | |
parent | a3d0f77ded1c982da93d61fac6942cfc67c9e599 (diff) |
Merge branch 'master' into deform-curves-with-surface
Diffstat (limited to 'source/blender/imbuf/intern')
29 files changed, 908 insertions, 444 deletions
diff --git a/source/blender/imbuf/intern/IMB_colormanagement_intern.h b/source/blender/imbuf/intern/IMB_colormanagement_intern.h index c89b15480a2..23b3f0191b7 100644 --- a/source/blender/imbuf/intern/IMB_colormanagement_intern.h +++ b/source/blender/imbuf/intern/IMB_colormanagement_intern.h @@ -18,8 +18,12 @@ struct ImBuf; struct OCIO_ConstCPUProcessorRcPtr; extern float imbuf_luma_coefficients[3]; -extern float imbuf_xyz_to_rgb[3][3]; -extern float imbuf_rgb_to_xyz[3][3]; +extern float imbuf_scene_linear_to_xyz[3][3]; +extern float imbuf_xyz_to_scene_linear[3][3]; +extern float imbuf_scene_linear_to_aces[3][3]; +extern float imbuf_aces_to_scene_linear[3][3]; +extern float imbuf_scene_linear_to_rec709[3][3]; +extern float imbuf_rec709_to_scene_linear[3][3]; #define MAX_COLORSPACE_NAME 64 #define MAX_COLORSPACE_DESCRIPTION 512 diff --git a/source/blender/imbuf/intern/IMB_filetype.h b/source/blender/imbuf/intern/IMB_filetype.h index 67d1aefeacb..9a0a6998fab 100644 --- a/source/blender/imbuf/intern/IMB_filetype.h +++ b/source/blender/imbuf/intern/IMB_filetype.h @@ -42,8 +42,8 @@ typedef struct ImFileType { * dimensions of the full-size image in r_width & r_height. */ struct ImBuf *(*load_filepath_thumbnail)(const char *filepath, - const int flags, - const size_t max_thumb_size, + int flags, + size_t max_thumb_size, char colorspace[IM_MAX_SPACE], size_t *r_width, size_t *r_height); @@ -155,8 +155,8 @@ struct ImBuf *imb_load_jpeg(const unsigned char *buffer, int flags, char colorspace[IM_MAX_SPACE]); struct ImBuf *imb_thumbnail_jpeg(const char *filepath, - const int flags, - const size_t max_thumb_size, + int flags, + size_t max_thumb_size, char colorspace[IM_MAX_SPACE], size_t *r_width, size_t *r_height); @@ -240,11 +240,10 @@ void imb_loadtiletiff( /** * Saves a TIFF file. * - * #ImBuf structures with 1, 3 or 4 bytes per pixel (GRAY, RGB, RGBA - * respectively) are accepted, and interpreted correctly. Note that the TIFF - * convention is to use pre-multiplied alpha, which can be achieved within - * Blender by setting "Premul" alpha handling. Other alpha conventions are - * not strictly correct, but are permitted anyhow. + * #ImBuf structures with 1, 3 or 4 bytes per pixel (GRAY, RGB, RGBA respectively) + * are accepted, and interpreted correctly. Note that the TIFF convention is to use + * pre-multiplied alpha, which can be achieved within Blender by setting `premul` alpha handling. + * Other alpha conventions are not strictly correct, but are permitted anyhow. * * \param ibuf: Image buffer. * \param filepath: Name of the TIFF file to create. diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 096089d4c41..0052ce19aa1 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -423,7 +423,7 @@ static int startavi(struct anim *anim) anim->cur_position = 0; # if 0 - printf("x:%d y:%d size:%d interl:%d dur:%d\n", + printf("x:%d y:%d size:%d interlace:%d dur:%d\n", anim->x, anim->y, anim->framesize, diff --git a/source/blender/imbuf/intern/cineon/cineon_dpx.c b/source/blender/imbuf/intern/cineon/cineon_dpx.c index d1cd30cfe84..1a99d2a34d9 100644 --- a/source/blender/imbuf/intern/cineon/cineon_dpx.c +++ b/source/blender/imbuf/intern/cineon/cineon_dpx.c @@ -69,7 +69,7 @@ static struct ImBuf *imb_load_dpx_cineon(const unsigned char *mem, return ibuf; } -static int imb_save_dpx_cineon(ImBuf *ibuf, const char *filename, int use_cineon, int flags) +static int imb_save_dpx_cineon(ImBuf *ibuf, const char *filepath, int use_cineon, int flags) { LogImageFile *logImage; float *fbuf; @@ -86,7 +86,7 @@ static int imb_save_dpx_cineon(ImBuf *ibuf, const char *filename, int use_cineon depth = (ibuf->planes + 7) >> 3; if (depth > 4 || depth < 3) { - printf("DPX/Cineon: unsupported depth: %d for file: '%s'\n", depth, filename); + printf("DPX/Cineon: unsupported depth: %d for file: '%s'\n", depth, filepath); return 0; } @@ -103,7 +103,7 @@ static int imb_save_dpx_cineon(ImBuf *ibuf, const char *filename, int use_cineon bitspersample = 8; } - logImage = logImageCreate(filename, + logImage = logImageCreate(filepath, use_cineon, ibuf->x, ibuf->y, @@ -121,8 +121,8 @@ static int imb_save_dpx_cineon(ImBuf *ibuf, const char *filename, int use_cineon } if (ibuf->rect_float != NULL && bitspersample != 8) { - /* don't use the float buffer to save 8 bpp picture to prevent color banding - * (there's no dithering algorithm behind the logImageSetDataRGBA function) */ + /* Don't use the float buffer to save 8 BPP picture to prevent color banding + * (there's no dithering algorithm behind the #logImageSetDataRGBA function). */ fbuf = (float *)MEM_mallocN(sizeof(float[4]) * ibuf->x * ibuf->y, "fbuf in imb_save_dpx_cineon"); diff --git a/source/blender/imbuf/intern/cineon/cineonlib.c b/source/blender/imbuf/intern/cineon/cineonlib.c index 3bdfcb60292..8312476bda0 100644 --- a/source/blender/imbuf/intern/cineon/cineonlib.c +++ b/source/blender/imbuf/intern/cineon/cineonlib.c @@ -35,7 +35,7 @@ void cineonSetVerbose(int verbosity) static void fillCineonMainHeader(LogImageFile *cineon, CineonMainHeader *header, - const char *filename, + const char *filepath, const char *creator) { time_t fileClock; @@ -57,7 +57,7 @@ static void fillCineonMainHeader(LogImageFile *cineon, getRowLength(cineon->width, cineon->element[0]), cineon->isMSB); strcpy(header->fileHeader.version, "v4.5"); - strncpy(header->fileHeader.file_name, filename, 99); + strncpy(header->fileHeader.file_name, filepath, 99); header->fileHeader.file_name[99] = 0; fileClock = time(NULL); fileTime = localtime(&fileClock); @@ -126,7 +126,7 @@ LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t { CineonMainHeader header; LogImageFile *cineon = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__); - const char *filename = (const char *)byteStuff; + const char *filepath = (const char *)byteStuff; int i; unsigned int dataOffset; @@ -144,11 +144,11 @@ LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t cineon->file = NULL; if (fromMemory == 0) { - /* byteStuff is then the filename */ - cineon->file = BLI_fopen(filename, "rb"); + /* byteStuff is then the filepath */ + cineon->file = BLI_fopen(filepath, "rb"); if (cineon->file == NULL) { if (verbose) { - printf("Cineon: Failed to open file \"%s\".\n", filename); + printf("Cineon: Failed to open file \"%s\".\n", filepath); } logImageClose(cineon); return NULL; @@ -350,7 +350,7 @@ LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t } LogImageFile *cineonCreate( - const char *filename, int width, int height, int bitsPerSample, const char *creator) + const char *filepath, int width, int height, int bitsPerSample, const char *creator) { CineonMainHeader header; const char *shortFilename = NULL; @@ -393,18 +393,18 @@ LogImageFile *cineonCreate( cineon->referenceBlack = 95.0f; cineon->gamma = 1.7f; - shortFilename = strrchr(filename, '/'); + shortFilename = strrchr(filepath, PATHSEP_CHAR); if (shortFilename == NULL) { - shortFilename = filename; + shortFilename = filepath; } else { shortFilename++; } - cineon->file = BLI_fopen(filename, "wb"); + cineon->file = BLI_fopen(filepath, "wb"); if (cineon->file == NULL) { if (verbose) { - printf("cineon: Couldn't open file %s\n", filename); + printf("cineon: Couldn't open file %s\n", filepath); } logImageClose(cineon); return NULL; diff --git a/source/blender/imbuf/intern/cineon/cineonlib.h b/source/blender/imbuf/intern/cineon/cineonlib.h index 37b27d19539..13d40461728 100644 --- a/source/blender/imbuf/intern/cineon/cineonlib.h +++ b/source/blender/imbuf/intern/cineon/cineonlib.h @@ -114,7 +114,7 @@ typedef struct { void cineonSetVerbose(int); LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t bufferSize); LogImageFile *cineonCreate( - const char *filename, int width, int height, int bitsPerSample, const char *creator); + const char *filepath, int width, int height, int bitsPerSample, const char *creator); #ifdef __cplusplus } diff --git a/source/blender/imbuf/intern/cineon/dpxlib.c b/source/blender/imbuf/intern/cineon/dpxlib.c index 2d28a477c8a..28c19116361 100644 --- a/source/blender/imbuf/intern/cineon/dpxlib.c +++ b/source/blender/imbuf/intern/cineon/dpxlib.c @@ -124,7 +124,7 @@ LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t buf { DpxMainHeader header; LogImageFile *dpx = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__); - const char *filename = (const char *)byteStuff; + const char *filepath = (const char *)byteStuff; int i; if (dpx == NULL) { @@ -141,11 +141,11 @@ LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t buf dpx->file = NULL; if (fromMemory == 0) { - /* byteStuff is then the filename */ - dpx->file = BLI_fopen(filename, "rb"); + /* byteStuff is then the filepath */ + dpx->file = BLI_fopen(filepath, "rb"); if (dpx->file == NULL) { if (verbose) { - printf("DPX: Failed to open file \"%s\".\n", filename); + printf("DPX: Failed to open file \"%s\".\n", filepath); } logImageClose(dpx); return NULL; @@ -406,7 +406,7 @@ LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t buf return dpx; } -LogImageFile *dpxCreate(const char *filename, +LogImageFile *dpxCreate(const char *filepath, int width, int height, int bitsPerSample, @@ -502,19 +502,19 @@ LogImageFile *dpxCreate(const char *filename, dpx->gamma = 1.7f; } - shortFilename = strrchr(filename, '/'); + shortFilename = strrchr(filepath, PATHSEP_CHAR); if (shortFilename == NULL) { - shortFilename = filename; + shortFilename = filepath; } else { shortFilename++; } - dpx->file = BLI_fopen(filename, "wb"); + dpx->file = BLI_fopen(filepath, "wb"); if (dpx->file == NULL) { if (verbose) { - printf("DPX: Couldn't open file %s\n", filename); + printf("DPX: Couldn't open file %s\n", filepath); } logImageClose(dpx); return NULL; diff --git a/source/blender/imbuf/intern/cineon/dpxlib.h b/source/blender/imbuf/intern/cineon/dpxlib.h index d8ed5dc6f67..aac424d52d6 100644 --- a/source/blender/imbuf/intern/cineon/dpxlib.h +++ b/source/blender/imbuf/intern/cineon/dpxlib.h @@ -133,7 +133,7 @@ typedef struct { void dpxSetVerbose(int verbosity); LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t bufferSize); -LogImageFile *dpxCreate(const char *filename, +LogImageFile *dpxCreate(const char *filepath, int width, int height, int bitsPerSample, diff --git a/source/blender/imbuf/intern/cineon/logImageCore.c b/source/blender/imbuf/intern/cineon/logImageCore.c index 36e12e07316..e693aa6f891 100644 --- a/source/blender/imbuf/intern/cineon/logImageCore.c +++ b/source/blender/imbuf/intern/cineon/logImageCore.c @@ -101,10 +101,10 @@ int logImageIsCineon(const void *buffer, const unsigned int size) return (magicNum == CINEON_FILE_MAGIC || magicNum == swap_uint(CINEON_FILE_MAGIC, 1)); } -LogImageFile *logImageOpenFromFile(const char *filename, int cineon) +LogImageFile *logImageOpenFromFile(const char *filepath, int cineon) { unsigned int magicNum; - FILE *f = BLI_fopen(filename, "rb"); + FILE *f = BLI_fopen(filepath, "rb"); (void)cineon; @@ -120,10 +120,10 @@ LogImageFile *logImageOpenFromFile(const char *filename, int cineon) fclose(f); if (logImageIsDpx(&magicNum, sizeof(magicNum))) { - return dpxOpen((const unsigned char *)filename, 0, 0); + return dpxOpen((const unsigned char *)filepath, 0, 0); } if (logImageIsCineon(&magicNum, sizeof(magicNum))) { - return cineonOpen((const unsigned char *)filename, 0, 0); + return cineonOpen((const unsigned char *)filepath, 0, 0); } return NULL; @@ -141,7 +141,7 @@ LogImageFile *logImageOpenFromMemory(const unsigned char *buffer, unsigned int s return NULL; } -LogImageFile *logImageCreate(const char *filename, +LogImageFile *logImageCreate(const char *filepath, int cineon, int width, int height, @@ -155,10 +155,10 @@ LogImageFile *logImageCreate(const char *filename, { /* referenceWhite, referenceBlack and gamma values are only supported for DPX file */ if (cineon) { - return cineonCreate(filename, width, height, bitsPerSample, creator); + return cineonCreate(filepath, width, height, bitsPerSample, creator); } - return dpxCreate(filename, + return dpxCreate(filepath, width, height, bitsPerSample, diff --git a/source/blender/imbuf/intern/cineon/logImageCore.h b/source/blender/imbuf/intern/cineon/logImageCore.h index 6875dba3f87..35540497828 100644 --- a/source/blender/imbuf/intern/cineon/logImageCore.h +++ b/source/blender/imbuf/intern/cineon/logImageCore.h @@ -19,6 +19,12 @@ #include "BLI_sys_types.h" #include "BLI_utildefines.h" +#ifdef _WIN32 +# define PATHSEP_CHAR '\\' +#else +# define PATHSEP_CHAR '/' +#endif + #ifdef __cplusplus extern "C" { #endif @@ -169,9 +175,9 @@ void logImageSetVerbose(int verbosity); int logImageIsDpx(const void *buffer, unsigned int size); int logImageIsCineon(const void *buffer, unsigned int size); LogImageFile *logImageOpenFromMemory(const unsigned char *buffer, unsigned int size); -LogImageFile *logImageOpenFromFile(const char *filename, int cineon); +LogImageFile *logImageOpenFromFile(const char *filepath, int cineon); void logImageGetSize(LogImageFile *logImage, int *width, int *height, int *depth); -LogImageFile *logImageCreate(const char *filename, +LogImageFile *logImageCreate(const char *filepath, int cineon, int width, int height, diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index 53aa74edc61..33873b5daa7 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -73,10 +73,12 @@ static int global_tot_looks = 0; /* Luma coefficients and XYZ to RGB to be initialized by OCIO. */ float imbuf_luma_coefficients[3] = {0.0f}; -float imbuf_xyz_to_rgb[3][3] = {{0.0f}}; -float imbuf_rgb_to_xyz[3][3] = {{0.0f}}; -static float imbuf_xyz_to_linear_srgb[3][3] = {{0.0f}}; -static float imbuf_linear_srgb_to_xyz[3][3] = {{0.0f}}; +float imbuf_scene_linear_to_xyz[3][3] = {{0.0f}}; +float imbuf_xyz_to_scene_linear[3][3] = {{0.0f}}; +float imbuf_scene_linear_to_rec709[3][3] = {{0.0f}}; +float imbuf_rec709_to_scene_linear[3][3] = {{0.0f}}; +float imbuf_scene_linear_to_aces[3][3] = {{0.0f}}; +float imbuf_aces_to_scene_linear[3][3] = {{0.0f}}; /* lock used by pre-cached processors getters, so processor wouldn't * be created several times @@ -573,10 +575,16 @@ static void colormanage_load_config(OCIO_ConstConfigRcPtr *config) /* Load luminance coefficients. */ OCIO_configGetDefaultLumaCoefs(config, imbuf_luma_coefficients); - OCIO_configGetXYZtoRGB(config, imbuf_xyz_to_rgb); - invert_m3_m3(imbuf_rgb_to_xyz, imbuf_xyz_to_rgb); - copy_m3_m3(imbuf_xyz_to_linear_srgb, OCIO_XYZ_TO_LINEAR_SRGB); - invert_m3_m3(imbuf_linear_srgb_to_xyz, imbuf_xyz_to_linear_srgb); + + /* Load standard color spaces. */ + OCIO_configGetXYZtoSceneLinear(config, imbuf_xyz_to_scene_linear); + invert_m3_m3(imbuf_scene_linear_to_xyz, imbuf_xyz_to_scene_linear); + + mul_m3_m3m3(imbuf_scene_linear_to_rec709, OCIO_XYZ_TO_REC709, imbuf_scene_linear_to_xyz); + invert_m3_m3(imbuf_rec709_to_scene_linear, imbuf_scene_linear_to_rec709); + + mul_m3_m3m3(imbuf_aces_to_scene_linear, imbuf_xyz_to_scene_linear, OCIO_ACES_TO_XYZ); + invert_m3_m3(imbuf_scene_linear_to_aces, imbuf_aces_to_scene_linear); } static void colormanage_free_config(void) @@ -589,7 +597,7 @@ static void colormanage_free_config(void) while (colorspace) { ColorSpace *colorspace_next = colorspace->next; - /* free precomputer processors */ + /* Free precomputed processors. */ if (colorspace->to_scene_linear) { OCIO_cpuProcessorRelease((OCIO_ConstCPUProcessorRcPtr *)colorspace->to_scene_linear); } @@ -665,7 +673,7 @@ void colormanagement_init(void) #ifdef WIN32 { - /* quite a hack to support loading configuration from path with non-acii symbols */ + /* Quite a hack to support loading configuration from path with non-ACII symbols. */ char short_name[256]; BLI_get_short_name(short_name, configfile); @@ -1412,9 +1420,15 @@ bool IMB_colormanagement_space_name_is_scene_linear(const char *name) return (colorspace && IMB_colormanagement_space_is_scene_linear(colorspace)); } -const float *IMB_colormanagement_get_xyz_to_rgb() +bool IMB_colormanagement_space_name_is_srgb(const char *name) { - return &imbuf_xyz_to_rgb[0][0]; + ColorSpace *colorspace = colormanage_colorspace_get_named(name); + return (colorspace && IMB_colormanagement_space_is_srgb(colorspace)); +} + +const float *IMB_colormanagement_get_xyz_to_scene_linear() +{ + return &imbuf_xyz_to_scene_linear[0][0]; } /** \} */ @@ -2197,21 +2211,14 @@ void IMB_colormanagement_imbuf_to_byte_texture(unsigned char *out_buffer, const int width, const int height, const struct ImBuf *ibuf, - const bool compress_as_srgb, const bool store_premultiplied) { - /* Convert byte buffer for texture storage on the GPU. These have builtin - * support for converting sRGB to linear, which allows us to store textures - * without precision or performance loss at minimal memory usage. */ + /* Byte buffer storage, only for sRGB and data texture since other + * color space conversions can't be done on the GPU. */ BLI_assert(ibuf->rect && ibuf->rect_float == NULL); + BLI_assert(IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace) || + IMB_colormanagement_space_is_data(ibuf->rect_colorspace)); - OCIO_ConstCPUProcessorRcPtr *processor = NULL; - if (compress_as_srgb && ibuf->rect_colorspace && - !IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace)) { - processor = colorspace_to_scene_linear_cpu_processor(ibuf->rect_colorspace); - } - - /* TODO(brecht): make this multi-threaded, or at least process in batches. */ const unsigned char *in_buffer = (unsigned char *)ibuf->rect; const bool use_premultiply = IMB_alpha_affects_rgb(ibuf) && store_premultiplied; @@ -2221,20 +2228,7 @@ void IMB_colormanagement_imbuf_to_byte_texture(unsigned char *out_buffer, const unsigned char *in = in_buffer + in_offset * 4; unsigned char *out = out_buffer + out_offset * 4; - if (processor != NULL) { - /* Convert to scene linear, to sRGB and premultiply. */ - for (int x = 0; x < width; x++, in += 4, out += 4) { - float pixel[4]; - rgba_uchar_to_float(pixel, in); - OCIO_cpuProcessorApplyRGB(processor, pixel); - linearrgb_to_srgb_v3_v3(pixel, pixel); - if (use_premultiply) { - mul_v3_fl(pixel, pixel[3]); - } - rgba_float_to_uchar(out, pixel); - } - } - else if (use_premultiply) { + if (use_premultiply) { /* Premultiply only. */ for (int x = 0; x < width; x++, in += 4, out += 4) { out[0] = (in[0] * in[3]) >> 8; @@ -2265,49 +2259,87 @@ void IMB_colormanagement_imbuf_to_float_texture(float *out_buffer, { /* Float texture are stored in scene linear color space, with premultiplied * alpha depending on the image alpha mode. */ - const float *in_buffer = ibuf->rect_float; - const int in_channels = ibuf->channels; - const bool use_unpremultiply = IMB_alpha_affects_rgb(ibuf) && !store_premultiplied; - - for (int y = 0; y < height; y++) { - const size_t in_offset = (offset_y + y) * ibuf->x + offset_x; - const size_t out_offset = y * width; - const float *in = in_buffer + in_offset * in_channels; - float *out = out_buffer + out_offset * 4; - - if (in_channels == 1) { - /* Copy single channel. */ - for (int x = 0; x < width; x++, in += 1, out += 4) { - out[0] = in[0]; - out[1] = in[0]; - out[2] = in[0]; - out[3] = in[0]; + if (ibuf->rect_float) { + /* Float source buffer. */ + const float *in_buffer = ibuf->rect_float; + const int in_channels = ibuf->channels; + const bool use_unpremultiply = IMB_alpha_affects_rgb(ibuf) && !store_premultiplied; + + for (int y = 0; y < height; y++) { + const size_t in_offset = (offset_y + y) * ibuf->x + offset_x; + const size_t out_offset = y * width; + const float *in = in_buffer + in_offset * in_channels; + float *out = out_buffer + out_offset * 4; + + if (in_channels == 1) { + /* Copy single channel. */ + for (int x = 0; x < width; x++, in += 1, out += 4) { + out[0] = in[0]; + out[1] = in[0]; + out[2] = in[0]; + out[3] = in[0]; + } } - } - else if (in_channels == 3) { - /* Copy RGB. */ - for (int x = 0; x < width; x++, in += 3, out += 4) { - out[0] = in[0]; - out[1] = in[1]; - out[2] = in[2]; - out[3] = 1.0f; + else if (in_channels == 3) { + /* Copy RGB. */ + for (int x = 0; x < width; x++, in += 3, out += 4) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = 1.0f; + } } - } - else if (in_channels == 4) { - /* Copy or convert RGBA. */ - if (use_unpremultiply) { - for (int x = 0; x < width; x++, in += 4, out += 4) { - premul_to_straight_v4_v4(out, in); + else if (in_channels == 4) { + /* Copy or convert RGBA. */ + if (use_unpremultiply) { + for (int x = 0; x < width; x++, in += 4, out += 4) { + premul_to_straight_v4_v4(out, in); + } + } + else { + memcpy(out, in, sizeof(float[4]) * width); } } - else { - memcpy(out, in, sizeof(float[4]) * width); + } + } + else { + /* Byte source buffer. */ + const unsigned char *in_buffer = (unsigned char *)ibuf->rect; + const bool use_premultiply = IMB_alpha_affects_rgb(ibuf) && store_premultiplied; + + /* TODO(brecht): make this multi-threaded, or at least process in batches. */ + OCIO_ConstCPUProcessorRcPtr *processor = (ibuf->rect_colorspace) ? + colorspace_to_scene_linear_cpu_processor( + ibuf->rect_colorspace) : + NULL; + + for (int y = 0; y < height; y++) { + const size_t in_offset = (offset_y + y) * ibuf->x + offset_x; + const size_t out_offset = y * width; + const unsigned char *in = in_buffer + in_offset * 4; + float *out = out_buffer + out_offset * 4; + + /* Convert to scene linear, to sRGB and premultiply. */ + for (int x = 0; x < width; x++, in += 4, out += 4) { + float pixel[4]; + rgba_uchar_to_float(pixel, in); + if (processor) { + OCIO_cpuProcessorApplyRGB(processor, pixel); + } + else { + srgb_to_linearrgb_v3_v3(pixel, pixel); + } + if (use_premultiply) { + mul_v3_fl(pixel, pixel[3]); + } + copy_v4_v4(out, pixel); } } } } -void IMB_colormanagement_scene_linear_to_color_picking_v3(float pixel[3]) +void IMB_colormanagement_scene_linear_to_color_picking_v3(float color_picking[3], + const float scene_linear[3]) { if (!global_color_picking_state.cpu_processor_to && !global_color_picking_state.failed) { /* Create processor if none exists. */ @@ -2329,12 +2361,15 @@ void IMB_colormanagement_scene_linear_to_color_picking_v3(float pixel[3]) BLI_mutex_unlock(&processor_lock); } + copy_v3_v3(color_picking, scene_linear); + if (global_color_picking_state.cpu_processor_to) { - OCIO_cpuProcessorApplyRGB(global_color_picking_state.cpu_processor_to, pixel); + OCIO_cpuProcessorApplyRGB(global_color_picking_state.cpu_processor_to, color_picking); } } -void IMB_colormanagement_color_picking_to_scene_linear_v3(float pixel[3]) +void IMB_colormanagement_color_picking_to_scene_linear_v3(float scene_linear[3], + const float color_picking[3]) { if (!global_color_picking_state.cpu_processor_from && !global_color_picking_state.failed) { /* Create processor if none exists. */ @@ -2356,25 +2391,13 @@ void IMB_colormanagement_color_picking_to_scene_linear_v3(float pixel[3]) BLI_mutex_unlock(&processor_lock); } + copy_v3_v3(scene_linear, color_picking); + if (global_color_picking_state.cpu_processor_from) { - OCIO_cpuProcessorApplyRGB(global_color_picking_state.cpu_processor_from, pixel); + OCIO_cpuProcessorApplyRGB(global_color_picking_state.cpu_processor_from, scene_linear); } } -void IMB_colormanagement_scene_linear_to_srgb_v3(float pixel[3]) -{ - mul_m3_v3(imbuf_rgb_to_xyz, pixel); - mul_m3_v3(imbuf_xyz_to_linear_srgb, pixel); - linearrgb_to_srgb_v3_v3(pixel, pixel); -} - -void IMB_colormanagement_srgb_to_scene_linear_v3(float pixel[3]) -{ - srgb_to_linearrgb_v3_v3(pixel, pixel); - mul_m3_v3(imbuf_linear_srgb_to_xyz, pixel); - mul_m3_v3(imbuf_xyz_to_rgb, pixel); -} - void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], ColorManagedDisplay *display) { OCIO_ConstCPUProcessorRcPtr *processor = display_from_scene_linear_processor(display); @@ -2445,68 +2468,77 @@ void IMB_colormanagement_imbuf_make_display_space( colormanagement_imbuf_make_display_space(ibuf, view_settings, display_settings, false); } +static ImBuf *imbuf_ensure_editable(ImBuf *ibuf, ImBuf *colormanaged_ibuf, bool allocate_result) +{ + if (colormanaged_ibuf != ibuf) { + /* Is already an editable copy. */ + return colormanaged_ibuf; + } + + if (allocate_result) { + /* Copy full image buffer. */ + colormanaged_ibuf = IMB_dupImBuf(ibuf); + IMB_metadata_copy(colormanaged_ibuf, ibuf); + return colormanaged_ibuf; + } + else { + /* Render pipeline is constructing image buffer itself, + * but it's re-using byte and float buffers from render result make copy of this buffers + * here sine this buffers would be transformed to other color space here. */ + if (ibuf->rect && (ibuf->mall & IB_rect) == 0) { + ibuf->rect = MEM_dupallocN(ibuf->rect); + ibuf->mall |= IB_rect; + } + + if (ibuf->rect_float && (ibuf->mall & IB_rectfloat) == 0) { + ibuf->rect_float = MEM_dupallocN(ibuf->rect_float); + ibuf->mall |= IB_rectfloat; + } + + return ibuf; + } +} + ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, bool save_as_render, bool allocate_result, const ImageFormatData *image_format) { ImBuf *colormanaged_ibuf = ibuf; - const bool is_movie = BKE_imtype_is_movie(image_format->imtype); - const bool requires_linear_float = BKE_imtype_requires_linear_float(image_format->imtype); - const bool do_alpha_under = image_format->planes != R_IMF_PLANES_RGBA; + /* Update byte buffer if exists but invalid. */ if (ibuf->rect_float && ibuf->rect && (ibuf->userflags & (IB_DISPLAY_BUFFER_INVALID | IB_RECT_INVALID)) != 0) { IMB_rect_from_float(ibuf); ibuf->userflags &= ~(IB_RECT_INVALID | IB_DISPLAY_BUFFER_INVALID); } - const bool do_colormanagement_display = save_as_render && (is_movie || !requires_linear_float); - const bool do_colormanagement_linear = save_as_render && requires_linear_float && - image_format->linear_colorspace_settings.name[0] && - !IMB_colormanagement_space_name_is_scene_linear( - image_format->linear_colorspace_settings.name); + /* Detect if we are writing to a file format that needs a linear float buffer. */ + const bool linear_float_output = BKE_imtype_requires_linear_float(image_format->imtype); - if (do_colormanagement_display || do_colormanagement_linear || do_alpha_under) { - if (allocate_result) { - colormanaged_ibuf = IMB_dupImBuf(ibuf); - } - else { - /* Render pipeline is constructing image buffer itself, - * but it's re-using byte and float buffers from render result make copy of this buffers - * here sine this buffers would be transformed to other color space here. - */ + /* Detect if we are writing output a byte buffer, which we would need to create + * with color management conversions applied. This may be for either applying the + * display transform for renders, or a user specified color space for the file. */ + const bool byte_output = BKE_image_format_is_byte(image_format); - if (ibuf->rect && (ibuf->mall & IB_rect) == 0) { - ibuf->rect = MEM_dupallocN(ibuf->rect); - ibuf->mall |= IB_rect; - } - - if (ibuf->rect_float && (ibuf->mall & IB_rectfloat) == 0) { - ibuf->rect_float = MEM_dupallocN(ibuf->rect_float); - ibuf->mall |= IB_rectfloat; - } - } - } + BLI_assert(!(byte_output && linear_float_output)); - /* If we're saving from RGBA to RGB buffer then it's not - * so much useful to just ignore alpha -- it leads to bad - * artifacts especially when saving byte images. + /* If we're saving from RGBA to RGB buffer then it's not so much useful to just ignore alpha -- + * it leads to bad artifacts especially when saving byte images. * - * What we do here is we're overlaying our image on top of - * background color (which is currently black). + * What we do here is we're overlaying our image on top of background color (which is currently + * black). This is quite much the same as what Gimp does and it seems to be what artists expects + * from saving. * - * This is quite much the same as what Gimp does and it - * seems to be what artists expects from saving. - * - * Do a conversion here, so image format writers could - * happily assume all the alpha tricks were made already. - * helps keep things locally here, not spreading it to - * all possible image writers we've got. + * Do a conversion here, so image format writers could happily assume all the alpha tricks were + * made already. helps keep things locally here, not spreading it to all possible image writers + * we've got. */ - if (do_alpha_under) { + if (image_format->planes != R_IMF_PLANES_RGBA) { float color[3] = {0, 0, 0}; + colormanaged_ibuf = imbuf_ensure_editable(ibuf, colormanaged_ibuf, allocate_result); + if (colormanaged_ibuf->rect_float && colormanaged_ibuf->channels == 4) { IMB_alpha_under_color_float( colormanaged_ibuf->rect_float, colormanaged_ibuf->x, colormanaged_ibuf->y, color); @@ -2520,69 +2552,95 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, } } - if (do_colormanagement_display) { - /* Color management with display and view transform. */ - bool make_byte = false; + if (save_as_render && !linear_float_output) { + /* Render output: perform conversion to display space using view transform. */ + colormanaged_ibuf = imbuf_ensure_editable(ibuf, colormanaged_ibuf, allocate_result); - /* for proper check whether byte buffer is required by a format or not - * should be pretty safe since this image buffer is supposed to be used for - * saving only and ftype would be overwritten a bit later by BKE_imbuf_write - */ - colormanaged_ibuf->ftype = BKE_imtype_to_ftype(image_format->imtype, - &colormanaged_ibuf->foptions); - - /* if file format isn't able to handle float buffer itself, - * we need to allocate byte buffer and store color managed - * image there - */ - const ImFileType *type = IMB_file_type_from_ibuf(colormanaged_ibuf); - if (type != NULL) { - if ((type->save != NULL) && (type->flag & IM_FTYPE_FLOAT) == 0) { - make_byte = true; - } - } - - /* perform color space conversions */ colormanagement_imbuf_make_display_space(colormanaged_ibuf, &image_format->view_settings, &image_format->display_settings, - make_byte); + byte_output); if (colormanaged_ibuf->rect_float) { - /* float buffer isn't linear anymore, + /* Float buffer isn't linear anymore, * image format write callback should check for this flag and assume - * no space conversion should happen if ibuf->float_colorspace != NULL - */ + * no space conversion should happen if ibuf->float_colorspace != NULL. */ colormanaged_ibuf->float_colorspace = display_transform_get_colorspace( &image_format->view_settings, &image_format->display_settings); + if (byte_output) { + colormanaged_ibuf->rect_colorspace = colormanaged_ibuf->float_colorspace; + } } } - else if (do_colormanagement_linear) { - /* Color management transform to another linear color space. */ - if (!colormanaged_ibuf->rect_float) { - IMB_float_from_rect(colormanaged_ibuf); - imb_freerectImBuf(colormanaged_ibuf); + else { + /* Linear render or regular file output: conversion between two color spaces. */ + + /* Detect which color space we need to convert between. */ + const char *from_colorspace = (ibuf->rect_float && !(byte_output && ibuf->rect)) ? + /* From float buffer. */ + (ibuf->float_colorspace) ? ibuf->float_colorspace->name : + global_role_scene_linear : + /* From byte buffer. */ + (ibuf->rect_colorspace) ? ibuf->rect_colorspace->name : + global_role_default_byte; + + const char *to_colorspace = image_format->linear_colorspace_settings.name; + + /* TODO: can we check with OCIO if color spaces are the same but have different names? */ + if (to_colorspace[0] == '\0' || STREQ(from_colorspace, to_colorspace)) { + /* No conversion needed, but may still need to allocate byte buffer for output. */ + if (byte_output && !ibuf->rect) { + ibuf->rect_colorspace = ibuf->float_colorspace; + IMB_rect_from_float(ibuf); + } } + else { + /* Color space conversion needed. */ + colormanaged_ibuf = imbuf_ensure_editable(ibuf, colormanaged_ibuf, allocate_result); + + if (byte_output) { + colormanaged_ibuf->rect_colorspace = colormanage_colorspace_get_named(to_colorspace); + + if (colormanaged_ibuf->rect) { + /* Byte to byte. */ + IMB_colormanagement_transform_byte_threaded((unsigned char *)colormanaged_ibuf->rect, + colormanaged_ibuf->x, + colormanaged_ibuf->y, + colormanaged_ibuf->channels, + from_colorspace, + to_colorspace); + } + else { + /* Float to byte. */ + IMB_rect_from_float(colormanaged_ibuf); + } + } + else { + if (!colormanaged_ibuf->rect_float) { + /* Byte to float. */ + IMB_float_from_rect(colormanaged_ibuf); + imb_freerectImBuf(colormanaged_ibuf); - if (colormanaged_ibuf->rect_float) { - const char *from_colorspace = (ibuf->float_colorspace) ? ibuf->float_colorspace->name : - global_role_scene_linear; - const char *to_colorspace = image_format->linear_colorspace_settings.name; + /* This conversion always goes to scene linear. */ + from_colorspace = global_role_scene_linear; + } - IMB_colormanagement_transform(colormanaged_ibuf->rect_float, - colormanaged_ibuf->x, - colormanaged_ibuf->y, - colormanaged_ibuf->channels, - from_colorspace, - to_colorspace, - false); + if (colormanaged_ibuf->rect_float) { + /* Float to float. */ + IMB_colormanagement_transform(colormanaged_ibuf->rect_float, + colormanaged_ibuf->x, + colormanaged_ibuf->y, + colormanaged_ibuf->channels, + from_colorspace, + to_colorspace, + false); + + colormanaged_ibuf->float_colorspace = colormanage_colorspace_get_named(to_colorspace); + } + } } } - if (colormanaged_ibuf != ibuf) { - IMB_metadata_copy(colormanaged_ibuf, ibuf); - } - return colormanaged_ibuf; } @@ -4076,3 +4134,170 @@ void IMB_colormanagement_finish_glsl_draw(void) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Rendering Tables + * \{ */ + +/* Calculate color in range 800..12000 using an approximation + * a/x+bx+c for R and G and ((at + b)t + c)t + d) for B + * + * The result of this can be negative to support gamut wider than + * than rec.709, just needs to be clamped. */ + +static const float blackbody_table_r[7][3] = {{1.61919106e+03f, -2.05010916e-03f, 5.02995757e+00f}, + {2.48845471e+03f, -1.11330907e-03f, 3.22621544e+00f}, + {3.34143193e+03f, -4.86551192e-04f, 1.76486769e+00f}, + {4.09461742e+03f, -1.27446582e-04f, 7.25731635e-01f}, + {4.67028036e+03f, 2.91258199e-05f, 1.26703442e-01f}, + {4.59509185e+03f, 2.87495649e-05f, 1.50345020e-01f}, + {3.78717450e+03f, 9.35907826e-06f, 3.99075871e-01f}}; + +static const float blackbody_table_g[7][3] = { + {-4.88999748e+02f, 6.04330754e-04f, -7.55807526e-02f}, + {-7.55994277e+02f, 3.16730098e-04f, 4.78306139e-01f}, + {-1.02363977e+03f, 1.20223470e-04f, 9.36662319e-01f}, + {-1.26571316e+03f, 4.87340896e-06f, 1.27054498e+00f}, + {-1.42529332e+03f, -4.01150431e-05f, 1.43972784e+00f}, + {-1.17554822e+03f, -2.16378048e-05f, 1.30408023e+00f}, + {-5.00799571e+02f, -4.59832026e-06f, 1.09098763e+00f}}; + +static const float blackbody_table_b[7][4] = { + {5.96945309e-11f, -4.85742887e-08f, -9.70622247e-05f, -4.07936148e-03f}, + {2.40430366e-11f, 5.55021075e-08f, -1.98503712e-04f, 2.89312858e-02f}, + {-1.40949732e-11f, 1.89878968e-07f, -3.56632824e-04f, 9.10767778e-02f}, + {-3.61460868e-11f, 2.84822009e-07f, -4.93211319e-04f, 1.56723440e-01f}, + {-1.97075738e-11f, 1.75359352e-07f, -2.50542825e-04f, -2.22783266e-02f}, + {-1.61997957e-13f, -1.64216008e-08f, 3.86216271e-04f, -7.38077418e-01f}, + {6.72650283e-13f, -2.73078809e-08f, 4.24098264e-04f, -7.52335691e-01f}}; + +static void blackbody_temperature_to_rec709(float rec709[3], float t) +{ + if (t >= 12000.0f) { + rec709[0] = 0.8262954810464208f; + rec709[1] = 0.9945080501520986f; + rec709[2] = 1.566307710274283f; + } + else if (t < 800.0f) { + rec709[0] = 5.413294490189271f; + rec709[1] = -0.20319390035873933f; + rec709[2] = -0.0822535242887164f; + } + else { + int i = (t >= 6365.0f) ? 6 : + (t >= 3315.0f) ? 5 : + (t >= 1902.0f) ? 4 : + (t >= 1449.0f) ? 3 : + (t >= 1167.0f) ? 2 : + (t >= 965.0f) ? 1 : + 0; + + const float *r = blackbody_table_r[i]; + const float *g = blackbody_table_g[i]; + const float *b = blackbody_table_b[i]; + + const float t_inv = 1.0f / t; + rec709[0] = r[0] * t_inv + r[1] * t + r[2]; + rec709[1] = g[0] * t_inv + g[1] * t + g[2]; + rec709[2] = ((b[0] * t + b[1]) * t + b[2]) * t + b[3]; + } +} + +void IMB_colormanagement_blackbody_temperature_to_rgb_table(float *r_table, + const int width, + const float min, + const float max) +{ + for (int i = 0; i < width; i++) { + float temperature = min + (max - min) / (float)width * (float)i; + + float rec709[3]; + blackbody_temperature_to_rec709(rec709, temperature); + + float rgb[3]; + IMB_colormanagement_rec709_to_scene_linear(rgb, rec709); + clamp_v3(rgb, 0.0f, FLT_MAX); + + copy_v3_v3(&r_table[i * 4], rgb); + r_table[i * 4 + 3] = 0.0f; + } +} + +/** + * CIE color matching functions `xBar`, `yBar`, and `zBar` for + * wavelengths from 380 through 780 nanometers, every 5 nanometers. + * + * For a wavelength lambda in this range: + * \code{.txt} + * cie_color_match[(lambda - 380) / 5][0] = xBar + * cie_color_match[(lambda - 380) / 5][1] = yBar + * cie_color_match[(lambda - 380) / 5][2] = zBar + * \endcode + */ + +static float cie_colour_match[81][3] = { + {0.0014f, 0.0000f, 0.0065f}, {0.0022f, 0.0001f, 0.0105f}, {0.0042f, 0.0001f, 0.0201f}, + {0.0076f, 0.0002f, 0.0362f}, {0.0143f, 0.0004f, 0.0679f}, {0.0232f, 0.0006f, 0.1102f}, + {0.0435f, 0.0012f, 0.2074f}, {0.0776f, 0.0022f, 0.3713f}, {0.1344f, 0.0040f, 0.6456f}, + {0.2148f, 0.0073f, 1.0391f}, {0.2839f, 0.0116f, 1.3856f}, {0.3285f, 0.0168f, 1.6230f}, + {0.3483f, 0.0230f, 1.7471f}, {0.3481f, 0.0298f, 1.7826f}, {0.3362f, 0.0380f, 1.7721f}, + {0.3187f, 0.0480f, 1.7441f}, {0.2908f, 0.0600f, 1.6692f}, {0.2511f, 0.0739f, 1.5281f}, + {0.1954f, 0.0910f, 1.2876f}, {0.1421f, 0.1126f, 1.0419f}, {0.0956f, 0.1390f, 0.8130f}, + {0.0580f, 0.1693f, 0.6162f}, {0.0320f, 0.2080f, 0.4652f}, {0.0147f, 0.2586f, 0.3533f}, + {0.0049f, 0.3230f, 0.2720f}, {0.0024f, 0.4073f, 0.2123f}, {0.0093f, 0.5030f, 0.1582f}, + {0.0291f, 0.6082f, 0.1117f}, {0.0633f, 0.7100f, 0.0782f}, {0.1096f, 0.7932f, 0.0573f}, + {0.1655f, 0.8620f, 0.0422f}, {0.2257f, 0.9149f, 0.0298f}, {0.2904f, 0.9540f, 0.0203f}, + {0.3597f, 0.9803f, 0.0134f}, {0.4334f, 0.9950f, 0.0087f}, {0.5121f, 1.0000f, 0.0057f}, + {0.5945f, 0.9950f, 0.0039f}, {0.6784f, 0.9786f, 0.0027f}, {0.7621f, 0.9520f, 0.0021f}, + {0.8425f, 0.9154f, 0.0018f}, {0.9163f, 0.8700f, 0.0017f}, {0.9786f, 0.8163f, 0.0014f}, + {1.0263f, 0.7570f, 0.0011f}, {1.0567f, 0.6949f, 0.0010f}, {1.0622f, 0.6310f, 0.0008f}, + {1.0456f, 0.5668f, 0.0006f}, {1.0026f, 0.5030f, 0.0003f}, {0.9384f, 0.4412f, 0.0002f}, + {0.8544f, 0.3810f, 0.0002f}, {0.7514f, 0.3210f, 0.0001f}, {0.6424f, 0.2650f, 0.0000f}, + {0.5419f, 0.2170f, 0.0000f}, {0.4479f, 0.1750f, 0.0000f}, {0.3608f, 0.1382f, 0.0000f}, + {0.2835f, 0.1070f, 0.0000f}, {0.2187f, 0.0816f, 0.0000f}, {0.1649f, 0.0610f, 0.0000f}, + {0.1212f, 0.0446f, 0.0000f}, {0.0874f, 0.0320f, 0.0000f}, {0.0636f, 0.0232f, 0.0000f}, + {0.0468f, 0.0170f, 0.0000f}, {0.0329f, 0.0119f, 0.0000f}, {0.0227f, 0.0082f, 0.0000f}, + {0.0158f, 0.0057f, 0.0000f}, {0.0114f, 0.0041f, 0.0000f}, {0.0081f, 0.0029f, 0.0000f}, + {0.0058f, 0.0021f, 0.0000f}, {0.0041f, 0.0015f, 0.0000f}, {0.0029f, 0.0010f, 0.0000f}, + {0.0020f, 0.0007f, 0.0000f}, {0.0014f, 0.0005f, 0.0000f}, {0.0010f, 0.0004f, 0.0000f}, + {0.0007f, 0.0002f, 0.0000f}, {0.0005f, 0.0002f, 0.0000f}, {0.0003f, 0.0001f, 0.0000f}, + {0.0002f, 0.0001f, 0.0000f}, {0.0002f, 0.0001f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, + {0.0001f, 0.0000f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, {0.0000f, 0.0000f, 0.0000f}}; + +static void wavelength_to_xyz(float xyz[3], float lambda_nm) +{ + float ii = (lambda_nm - 380.0f) * (1.0f / 5.0f); /* Scaled 0..80. */ + int i = (int)ii; + + if (i < 0 || i >= 80) { + xyz[0] = 0.0f; + xyz[1] = 0.0f; + xyz[2] = 0.0f; + } + else { + ii -= (float)i; + const float *c = cie_colour_match[i]; + xyz[0] = c[0] + ii * (c[3] - c[0]); + xyz[1] = c[1] + ii * (c[4] - c[1]); + xyz[2] = c[2] + ii * (c[5] - c[2]); + } +} + +void IMB_colormanagement_wavelength_to_rgb_table(float *r_table, const int width) +{ + for (int i = 0; i < width; i++) { + float temperature = 380 + 400 / (float)width * (float)i; + + float xyz[3]; + wavelength_to_xyz(xyz, temperature); + + float rgb[3]; + IMB_colormanagement_xyz_to_scene_linear(rgb, xyz); + clamp_v3(rgb, 0.0f, FLT_MAX); + + copy_v3_v3(&r_table[i * 4], rgb); + r_table[i * 4 + 3] = 0.0f; + } +} + +/** \} */ diff --git a/source/blender/imbuf/intern/colormanagement_inline.c b/source/blender/imbuf/intern/colormanagement_inline.c index 411cf9af802..668307ec802 100644 --- a/source/blender/imbuf/intern/colormanagement_inline.c +++ b/source/blender/imbuf/intern/colormanagement_inline.c @@ -27,14 +27,46 @@ unsigned char IMB_colormanagement_get_luminance_byte(const unsigned char rgb[3]) return unit_float_to_uchar_clamp(val); } -void IMB_colormanagement_xyz_to_rgb(float rgb[3], const float xyz[3]) +void IMB_colormanagement_xyz_to_scene_linear(float scene_linear[3], const float xyz[3]) { - mul_v3_m3v3(rgb, imbuf_xyz_to_rgb, xyz); + mul_v3_m3v3(scene_linear, imbuf_xyz_to_scene_linear, xyz); } -void IMB_colormanagement_rgb_to_xyz(float xyz[3], const float rgb[3]) +void IMB_colormanagement_scene_linear_to_xyz(float xyz[3], const float scene_linear[3]) { - mul_v3_m3v3(xyz, imbuf_rgb_to_xyz, rgb); + mul_v3_m3v3(xyz, imbuf_scene_linear_to_xyz, scene_linear); +} + +void IMB_colormanagement_rec709_to_scene_linear(float scene_linear[3], const float rec709[3]) +{ + mul_v3_m3v3(scene_linear, imbuf_rec709_to_scene_linear, rec709); +} + +void IMB_colormanagement_scene_linear_to_rec709(float rec709[3], const float scene_linear[3]) +{ + mul_v3_m3v3(rec709, imbuf_scene_linear_to_rec709, scene_linear); +} + +void IMB_colormanagement_scene_linear_to_srgb_v3(float srgb[3], const float scene_linear[3]) +{ + mul_v3_m3v3(srgb, imbuf_scene_linear_to_rec709, scene_linear); + linearrgb_to_srgb_v3_v3(srgb, srgb); +} + +void IMB_colormanagement_srgb_to_scene_linear_v3(float scene_linear[3], const float srgb[3]) +{ + srgb_to_linearrgb_v3_v3(scene_linear, srgb); + mul_m3_v3(imbuf_rec709_to_scene_linear, scene_linear); +} + +void IMB_colormanagement_aces_to_scene_linear(float scene_linear[3], const float aces[3]) +{ + mul_v3_m3v3(scene_linear, imbuf_aces_to_scene_linear, aces); +} + +void IMB_colormanagement_scene_linear_to_aces(float aces[3], const float scene_linear[3]) +{ + mul_v3_m3v3(aces, imbuf_scene_linear_to_aces, scene_linear); } #endif /* __IMB_COLORMANAGEMENT_INLINE_H__ */ diff --git a/source/blender/imbuf/intern/dds/Color.h b/source/blender/imbuf/intern/dds/Color.h index 3918ef31052..5b00333ad77 100644 --- a/source/blender/imbuf/intern/dds/Color.h +++ b/source/blender/imbuf/intern/dds/Color.h @@ -21,9 +21,8 @@ class Color32 { Color32() { } - Color32(const Color32 &c) : u(c.u) - { - } + Color32(const Color32 &) = default; + Color32(unsigned char R, unsigned char G, unsigned char B) { setRGBA(R, G, B, 0xFF); diff --git a/source/blender/imbuf/intern/dds/Stream.cpp b/source/blender/imbuf/intern/dds/Stream.cpp index 34f3654aa3f..566891dac8b 100644 --- a/source/blender/imbuf/intern/dds/Stream.cpp +++ b/source/blender/imbuf/intern/dds/Stream.cpp @@ -45,7 +45,7 @@ unsigned int mem_read(Stream &mem, unsigned long long &i) mem.set_failed(msg_error_seek); return 0; } - memcpy(&i, mem.mem + mem.pos, 8); /* @@ todo: make sure little endian */ + memcpy(&i, mem.mem + mem.pos, 8); /* TODO: make sure little endian. */ mem.pos += 8; return 8; } @@ -56,7 +56,7 @@ unsigned int mem_read(Stream &mem, unsigned int &i) mem.set_failed(msg_error_read); return 0; } - memcpy(&i, mem.mem + mem.pos, 4); /* @@ todo: make sure little endian */ + memcpy(&i, mem.mem + mem.pos, 4); /* TODO: make sure little endian. */ mem.pos += 4; return 4; } @@ -67,7 +67,7 @@ unsigned int mem_read(Stream &mem, unsigned short &i) mem.set_failed(msg_error_read); return 0; } - memcpy(&i, mem.mem + mem.pos, 2); /* @@ todo: make sure little endian */ + memcpy(&i, mem.mem + mem.pos, 2); /* TODO: make sure little endian. */ mem.pos += 2; return 2; } diff --git a/source/blender/imbuf/intern/filetype.c b/source/blender/imbuf/intern/filetype.c index 74042ef75be..92fa980cd7f 100644 --- a/source/blender/imbuf/intern/filetype.c +++ b/source/blender/imbuf/intern/filetype.c @@ -157,7 +157,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_openexr, .load = imb_load_openexr, .load_filepath = NULL, - .load_filepath_thumbnail = NULL, + .load_filepath_thumbnail = imb_load_filepath_thumbnail_openexr, .save = imb_save_openexr, .load_tile = NULL, .flag = IM_FTYPE_FLOAT, diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 1cc91d25d2a..cbc5d984755 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -376,10 +376,10 @@ int IMB_timecode_to_array_index(IMB_Timecode_Type tc) static void get_index_dir(struct anim *anim, char *index_dir, size_t index_dir_len) { if (!anim->index_dir[0]) { - char fname[FILE_MAXFILE]; - BLI_split_dirfile(anim->name, index_dir, fname, index_dir_len, sizeof(fname)); + char filename[FILE_MAXFILE]; + BLI_split_dirfile(anim->name, index_dir, filename, index_dir_len, sizeof(filename)); BLI_path_append(index_dir, index_dir_len, "BL_proxy"); - BLI_path_append(index_dir, index_dir_len, fname); + BLI_path_append(index_dir, index_dir_len, filename); } else { BLI_strncpy(index_dir, anim->index_dir, index_dir_len); @@ -388,14 +388,14 @@ static void get_index_dir(struct anim *anim, char *index_dir, size_t index_dir_l void IMB_anim_get_fname(struct anim *anim, char *file, int size) { - char fname[FILE_MAXFILE]; - BLI_split_dirfile(anim->name, file, fname, size, sizeof(fname)); - BLI_strncpy(file, fname, size); + char filename[FILE_MAXFILE]; + BLI_split_dirfile(anim->name, file, filename, size, sizeof(filename)); + BLI_strncpy(file, filename, size); } -static bool get_proxy_filename(struct anim *anim, +static bool get_proxy_filepath(struct anim *anim, IMB_Proxy_Size preview_size, - char *fname, + char *filepath, bool temp) { char index_dir[FILE_MAXDIR]; @@ -426,11 +426,11 @@ static bool get_proxy_filename(struct anim *anim, return false; } - BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR, index_dir, proxy_name); + BLI_join_dirfile(filepath, FILE_MAXFILE + FILE_MAXDIR, index_dir, proxy_name); return true; } -static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char *fname) +static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char *filepath) { char index_dir[FILE_MAXDIR]; int i = IMB_timecode_to_array_index(tc); @@ -457,7 +457,7 @@ static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char *fname get_index_dir(anim, index_dir, sizeof(index_dir)); - BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR, index_dir, index_name); + BLI_join_dirfile(filepath, FILE_MAXFILE + FILE_MAXDIR, index_dir, index_name); } /* ---------------------------------------------------------------------- @@ -492,18 +492,18 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( { struct proxy_output_ctx *rv = MEM_callocN(sizeof(struct proxy_output_ctx), "alloc_proxy_output"); - char fname[FILE_MAX]; + char filepath[FILE_MAX]; rv->proxy_size = proxy_size; rv->anim = anim; - get_proxy_filename(rv->anim, rv->proxy_size, fname, true); - BLI_make_existing_file(fname); + get_proxy_filepath(rv->anim, rv->proxy_size, filepath, true); + BLI_make_existing_file(filepath); rv->of = avformat_alloc_context(); rv->of->oformat = av_guess_format("avi", NULL, NULL); - rv->of->url = av_strdup(fname); + rv->of->url = av_strdup(filepath); fprintf(stderr, "Starting work on proxy: %s\n", rv->of->url); @@ -577,7 +577,7 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( avcodec_parameters_from_context(rv->st->codecpar, rv->c); - int ret = avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE); + int ret = avio_open(&rv->of->pb, filepath, AVIO_FLAG_WRITE); if (ret < 0) { fprintf(stderr, @@ -723,8 +723,8 @@ static void add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *fr static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback) { - char fname[FILE_MAX]; - char fname_tmp[FILE_MAX]; + char filepath[FILE_MAX]; + char filepath_tmp[FILE_MAX]; if (!ctx) { return; @@ -755,15 +755,15 @@ static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback) av_free(ctx->frame); } - get_proxy_filename(ctx->anim, ctx->proxy_size, fname_tmp, true); + get_proxy_filepath(ctx->anim, ctx->proxy_size, filepath_tmp, true); if (rollback) { - unlink(fname_tmp); + unlink(filepath_tmp); } else { - get_proxy_filename(ctx->anim, ctx->proxy_size, fname, false); - unlink(fname); - BLI_rename(fname_tmp, fname); + get_proxy_filepath(ctx->anim, ctx->proxy_size, filepath, false); + unlink(filepath); + BLI_rename(filepath_tmp, filepath); } MEM_freeN(ctx); @@ -907,11 +907,11 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, for (i = 0; i < num_indexers; i++) { if (tcs_in_use & tc_types[i]) { - char fname[FILE_MAX]; + char filepath[FILE_MAX]; - get_tc_filename(anim, tc_types[i], fname); + get_tc_filename(anim, tc_types[i], filepath); - context->indexer[i] = IMB_index_builder_create(fname); + context->indexer[i] = IMB_index_builder_create(filepath); if (!context->indexer[i]) { tcs_in_use &= ~tc_types[i]; } @@ -1212,7 +1212,7 @@ typedef struct FallbackIndexBuilderContext { } FallbackIndexBuilderContext; static AviMovie *alloc_proxy_output_avi( - struct anim *anim, char *filename, int width, int height, int quality) + struct anim *anim, char *filepath, int width, int height, int quality) { int x, y; AviFormat format; @@ -1233,7 +1233,7 @@ static AviMovie *alloc_proxy_output_avi( format = AVI_FORMAT_MJPEG; - if (AVI_open_compress(filename, avi, 1, format) != AVI_ERROR_NONE) { + if (AVI_open_compress(filepath, avi, 1, format) != AVI_ERROR_NONE) { MEM_freeN(avi); return NULL; } @@ -1275,13 +1275,13 @@ static IndexBuildContext *index_fallback_create_context(struct anim *anim, for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) { if (context->proxy_sizes_in_use & proxy_sizes[i]) { - char fname[FILE_MAX]; + char filepath[FILE_MAX]; - get_proxy_filename(anim, proxy_sizes[i], fname, true); - BLI_make_existing_file(fname); + get_proxy_filepath(anim, proxy_sizes[i], filepath, true); + BLI_make_existing_file(filepath); context->proxy_ctx[i] = alloc_proxy_output_avi( - anim, fname, anim->x * proxy_fac[i], anim->y * proxy_fac[i], quality); + anim, filepath, anim->x * proxy_fac[i], anim->y * proxy_fac[i], quality); } } @@ -1291,8 +1291,8 @@ static IndexBuildContext *index_fallback_create_context(struct anim *anim, static void index_rebuild_fallback_finish(FallbackIndexBuilderContext *context, int stop) { struct anim *anim = context->anim; - char fname[FILE_MAX]; - char fname_tmp[FILE_MAX]; + char filepath[FILE_MAX]; + char filepath_tmp[FILE_MAX]; int i; for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) { @@ -1300,15 +1300,15 @@ static void index_rebuild_fallback_finish(FallbackIndexBuilderContext *context, AVI_close_compress(context->proxy_ctx[i]); MEM_freeN(context->proxy_ctx[i]); - get_proxy_filename(anim, proxy_sizes[i], fname_tmp, true); - get_proxy_filename(anim, proxy_sizes[i], fname, false); + get_proxy_filepath(anim, proxy_sizes[i], filepath_tmp, true); + get_proxy_filepath(anim, proxy_sizes[i], filepath, false); if (stop) { - unlink(fname_tmp); + unlink(filepath_tmp); } else { - unlink(fname); - rename(fname_tmp, fname); + unlink(filepath); + rename(filepath_tmp, filepath); } } } @@ -1388,7 +1388,7 @@ IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, IMB_Proxy_Size proxy_size = proxy_sizes[i]; if (proxy_size & proxy_sizes_to_build) { char filename[FILE_MAX]; - if (get_proxy_filename(anim, proxy_size, filename, false) == false) { + if (get_proxy_filepath(anim, proxy_size, filename, false) == false) { return NULL; } void **filename_key_p; @@ -1411,7 +1411,7 @@ IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, IMB_Proxy_Size proxy_size = proxy_sizes[i]; if (proxy_size & built_proxies) { char filename[FILE_MAX]; - if (get_proxy_filename(anim, proxy_size, filename, false) == false) { + if (get_proxy_filepath(anim, proxy_size, filename, false) == false) { return NULL; } printf("Skipping proxy: %s\n", filename); @@ -1532,7 +1532,7 @@ void IMB_anim_set_index_dir(struct anim *anim, const char *dir) struct anim *IMB_anim_open_proxy(struct anim *anim, IMB_Proxy_Size preview_size) { - char fname[FILE_MAX]; + char filepath[FILE_MAX]; int i = IMB_proxy_size_to_array_index(preview_size); if (i < 0) { @@ -1547,10 +1547,10 @@ struct anim *IMB_anim_open_proxy(struct anim *anim, IMB_Proxy_Size preview_size) return NULL; } - get_proxy_filename(anim, preview_size, fname, false); + get_proxy_filepath(anim, preview_size, filepath, false); /* proxies are generated in the same color space as animation itself */ - anim->proxy_anim[i] = IMB_open_anim(fname, 0, 0, anim->colorspace); + anim->proxy_anim[i] = IMB_open_anim(filepath, 0, 0, anim->colorspace); anim->proxies_tried |= preview_size; @@ -1559,7 +1559,7 @@ struct anim *IMB_anim_open_proxy(struct anim *anim, IMB_Proxy_Size preview_size) struct anim_index *IMB_anim_open_index(struct anim *anim, IMB_Timecode_Type tc) { - char fname[FILE_MAX]; + char filepath[FILE_MAX]; int i = IMB_timecode_to_array_index(tc); if (i < 0) { @@ -1574,9 +1574,9 @@ struct anim_index *IMB_anim_open_index(struct anim *anim, IMB_Timecode_Type tc) return NULL; } - get_tc_filename(anim, tc, fname); + get_tc_filename(anim, tc, filepath); - anim->curr_idx[i] = IMB_indexer_open(fname); + anim->curr_idx[i] = IMB_indexer_open(filepath); anim->indices_tried |= tc; @@ -1602,7 +1602,7 @@ IMB_Proxy_Size IMB_anim_proxy_get_existing(struct anim *anim) for (i = 0; i < num_proxy_sizes; i++) { IMB_Proxy_Size proxy_size = proxy_sizes[i]; char filename[FILE_MAX]; - get_proxy_filename(anim, proxy_size, filename, false); + get_proxy_filepath(anim, proxy_size, filename, false); if (BLI_exists(filename)) { existing |= proxy_size; } diff --git a/source/blender/imbuf/intern/iris.c b/source/blender/imbuf/intern/iris.c index eb0a2c4a47f..a8150fd1648 100644 --- a/source/blender/imbuf/intern/iris.c +++ b/source/blender/imbuf/intern/iris.c @@ -105,7 +105,7 @@ static int expandrow2( float *optr, const float *optr_end, const uchar *iptr, const uchar *iptr_end, int z); static void interleaverow(uchar *lptr, const uchar *cptr, int z, int n); static void interleaverow2(float *lptr, const uchar *cptr, int z, int n); -static int compressrow(uchar *lbuf, uchar *rlebuf, int z, int row_len); +static int compressrow(const uchar *lbuf, uchar *rlebuf, int z, int row_len); static void lumrow(const uchar *rgbptr, uchar *lumptr, int n); /* @@ -779,18 +779,24 @@ fail: } /** - * Copy an array of ints to an iris image file. - * Each int represents one pixel. xsize and ysize specify the dimensions of - * the pixel array. zsize specifies what kind of image file to - * write out. if zsize is 1, the luminance of the pixels are - * calculated, and a single channel black and white image is saved. - * If zsize is 3, an RGB image file is saved. If zsize is 4, an - * RGBA image file is saved. - * - * Added: zbuf write + * \param filepath: The file path to write to. + * \param lptr: an array of integers to an iris image file (each int represents one pixel). + * \param zptr: depth-buffer (optional, may be NULL). + * \param xsize: with width of the pixel-array. + * \param ysize: height of the pixel-array. + * \param zsize: specifies what kind of image file to write out. + * - 1: the luminance of the pixels are calculated, + * and a single channel black and white image is saved. + * - 3: an RGB image file is saved. + * - 4: an RGBA image file is saved. + * - 8: an RGBA image and a Z-buffer (non-null `zptr`). */ - -static bool output_iris(uint *lptr, int xsize, int ysize, int zsize, const char *name, int *zptr) +static bool output_iris(const char *filepath, + const uint *lptr, + const int *zptr, + const int xsize, + const int ysize, + const int zsize) { FILE *outf; IMAGE *image; @@ -801,7 +807,7 @@ static bool output_iris(uint *lptr, int xsize, int ysize, int zsize, const char int rlebuflen, goodwrite; goodwrite = 1; - outf = BLI_fopen(name, "wb"); + outf = BLI_fopen(filepath, "wb"); if (!outf) { return 0; } @@ -837,21 +843,20 @@ static bool output_iris(uint *lptr, int xsize, int ysize, int zsize, const char for (z = 0; z < zsize; z++) { if (zsize == 1) { - lumrow((uchar *)lptr, (uchar *)lumbuf, xsize); - len = compressrow((uchar *)lumbuf, rlebuf, CHANOFFSET(z), xsize); + lumrow((const uchar *)lptr, (uchar *)lumbuf, xsize); + len = compressrow((const uchar *)lumbuf, rlebuf, CHANOFFSET(z), xsize); } else { if (z < 4) { - len = compressrow((uchar *)lptr, rlebuf, CHANOFFSET(z), xsize); + len = compressrow((const uchar *)lptr, rlebuf, CHANOFFSET(z), xsize); } else if (z < 8 && zptr) { - len = compressrow((uchar *)zptr, rlebuf, CHANOFFSET(z - 4), xsize); + len = compressrow((const uchar *)zptr, rlebuf, CHANOFFSET(z - 4), xsize); } } - if (len > rlebuflen) { - fprintf(stderr, "output_iris: rlebuf is too small - bad poop\n"); - exit(1); - } + + BLI_assert_msg(len <= rlebuflen, "The length calculated for 'rlebuflen' was too small!"); + goodwrite *= fwrite(rlebuf, len, 1, outf); starttab[y + z * ysize] = pos; lengthtab[y + z * ysize] = len; @@ -892,9 +897,10 @@ static void lumrow(const uchar *rgbptr, uchar *lumptr, int n) } } -static int compressrow(uchar *lbuf, uchar *rlebuf, int z, int row_len) +static int compressrow(const uchar *lbuf, uchar *rlebuf, const int z, const int row_len) { - uchar *iptr, *ibufend, *sptr, *optr; + const uchar *iptr, *ibufend, *sptr; + uchar *optr; short todo, cc; int count; @@ -964,7 +970,7 @@ bool imb_saveiris(struct ImBuf *ibuf, const char *filepath, int flags) IMB_convert_rgba_to_abgr(ibuf); test_endian_zbuf(ibuf); - const bool ok = output_iris(ibuf->rect, ibuf->x, ibuf->y, zsize, filepath, ibuf->zbuf); + const bool ok = output_iris(filepath, ibuf->rect, ibuf->zbuf, ibuf->x, ibuf->y, zsize); /* restore! Quite clumsy, 2 times a switch... maybe better a malloc ? */ IMB_convert_rgba_to_abgr(ibuf); diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c index 80fcceb0aa7..cffa61977f7 100644 --- a/source/blender/imbuf/intern/jpeg.c +++ b/source/blender/imbuf/intern/jpeg.c @@ -286,8 +286,8 @@ static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, } if (max_size > 0) { - /* libjpeg can more quickly decompress while scaling down to 1/2, 1/4, 1/8, - * while libjpeg-turbo can also do 3/8, 5/8, etc. But max is 1/8. */ + /* `libjpeg` can more quickly decompress while scaling down to 1/2, 1/4, 1/8, + * while `libjpeg-turbo` can also do 3/8, 5/8, etc. But max is 1/8. */ float scale = (float)max_size / MAX2(cinfo->image_width, cinfo->image_height); cinfo->scale_denom = 8; cinfo->scale_num = max_uu(1, min_uu(8, ceill(scale * (float)cinfo->scale_denom))); @@ -520,9 +520,9 @@ struct ImBuf *imb_thumbnail_jpeg(const char *filepath, if ((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI) && (fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_APP1)) { - /* This is a JPEG in Exif format (SOI + APP1), not JFIF (SOI + APP0). */ + /* This is a JPEG in EXIF format (SOI + APP1), not JFIF (SOI + APP0). */ unsigned int i = JPEG_APP1_MAX; - /* All Exif data is within this 64K header segment. Skip ahead until next SOI for thumbnail. */ + /* All EXIF data is within this 64K header segment. Skip ahead until next SOI for thumbnail. */ while (!((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI)) && !feof(infile) && i--) ; diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index 9948aaac5da..0414fa1268d 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -10,6 +10,7 @@ #include <cstddef> #include <cstdio> #include <cstdlib> +#include <fcntl.h> #include <fstream> #include <iostream> #include <set> @@ -43,6 +44,8 @@ #include <OpenEXR/ImfInputFile.h> #include <OpenEXR/ImfOutputFile.h> #include <OpenEXR/ImfPixelType.h> +#include <OpenEXR/ImfPreviewImage.h> +#include <OpenEXR/ImfRgbaFile.h> #include <OpenEXR/ImfStandardAttributes.h> #include <OpenEXR/ImfStringAttribute.h> #include <OpenEXR/ImfVersion.h> @@ -63,6 +66,9 @@ #if defined(WIN32) # include "utfconv.h" +# include <io.h> +#else +# include <unistd.h> #endif #include "MEM_guardedalloc.h" @@ -77,7 +83,9 @@ _CRTIMP void __cdecl _invalid_parameter_noinfo(void) #endif } #include "BLI_blenlib.h" +#include "BLI_fileops.h" #include "BLI_math_color.h" +#include "BLI_mmap.h" #include "BLI_string_utils.h" #include "BLI_threads.h" @@ -151,6 +159,66 @@ class IMemStream : public Imf::IStream { unsigned char *_exrbuf; }; +/* Memory-Mapped Input Stream */ + +class IMMapStream : public Imf::IStream { + public: + IMMapStream(const char *filepath) : IStream(filepath) + { + int file = BLI_open(filepath, O_BINARY | O_RDONLY, 0); + if (file < 0) { + throw IEX_NAMESPACE::InputExc("file not found"); + } + _exrpos = 0; + _exrsize = BLI_file_descriptor_size(file); + imb_mmap_lock(); + _mmap_file = BLI_mmap_open(file); + imb_mmap_unlock(); + if (_mmap_file == nullptr) { + throw IEX_NAMESPACE::InputExc("BLI_mmap_open failed"); + } + close(file); + _exrbuf = (unsigned char *)BLI_mmap_get_pointer(_mmap_file); + } + + ~IMMapStream() override + { + imb_mmap_lock(); + BLI_mmap_free(_mmap_file); + imb_mmap_unlock(); + } + + /* This is implementing regular `read`, not `readMemoryMapped`, because DWAA and DWAB + * decompressors load on unaligned offsets. Therefore we can't avoid the memory copy. */ + + bool read(char c[], int n) override + { + if (_exrpos + n > _exrsize) { + throw Iex::InputExc("Unexpected end of file."); + } + memcpy(c, _exrbuf + _exrpos, n); + _exrpos += n; + + return _exrpos < _exrsize; + } + + exr_file_offset_t tellg() override + { + return _exrpos; + } + + void seekg(exr_file_offset_t pos) override + { + _exrpos = pos; + } + + private: + BLI_mmap_file *_mmap_file; + exr_file_offset_t _exrpos; + exr_file_offset_t _exrsize; + unsigned char *_exrbuf; +}; + /* File Input Stream */ class IFileStream : public Imf::IStream { @@ -363,7 +431,7 @@ static void openexr_header_compression(Header *header, int compression) case R_IMF_EXR_CODEC_B44A: header->compression() = B44A_COMPRESSION; break; -#if OPENEXR_VERSION_MAJOR >= 2 && OPENEXR_VERSION_MINOR >= 2 +#if OPENEXR_VERSION_MAJOR > 2 || (OPENEXR_VERSION_MAJOR >= 2 && OPENEXR_VERSION_MINOR >= 2) case R_IMF_EXR_CODEC_DWAA: header->compression() = DWAA_COMPRESSION; break; @@ -444,7 +512,7 @@ static bool imb_save_openexr_half(ImBuf *ibuf, const char *name, const int flags /* we store first everything in half array */ std::vector<RGBAZ> pixels(height * width); - RGBAZ *to = &pixels[0]; + RGBAZ *to = pixels.data(); int xstride = sizeof(RGBAZ); int ystride = xstride * width; @@ -942,7 +1010,7 @@ void IMB_exrtile_begin_write( /* manually create ofstream, so we can handle utf-8 filepaths on windows */ try { data->ofile_stream = new OFileStream(filepath); - data->mpofile = new MultiPartOutputFile(*(data->ofile_stream), &headers[0], headers.size()); + data->mpofile = new MultiPartOutputFile(*(data->ofile_stream), headers.data(), headers.size()); } catch (const std::exception &) { delete data->mpofile; @@ -1394,12 +1462,10 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa const char *name = echan->m->name.c_str(); const char *end = name + strlen(name); const char *token; - char tokenbuf[EXR_TOT_MAXNAME]; - int len; /* some multilayers have the combined buffer with names A B G R saved */ if (name[1] == 0) { - echan->chan_id = name[0]; + echan->chan_id = BLI_toupper_ascii(name[0]); layname[0] = '\0'; if (ELEM(name[0], 'R', 'G', 'B', 'A')) { @@ -1416,13 +1482,17 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa } /* last token is channel identifier */ - len = imb_exr_split_token(name, end, &token); + size_t len = imb_exr_split_token(name, end, &token); if (len == 0) { printf("multilayer read: bad channel name: %s\n", name); return 0; } + + char channelname[EXR_TOT_MAXNAME]; + BLI_strncpy(channelname, token, std::min(len + 1, sizeof(channelname))); + if (len == 1) { - echan->chan_id = token[0]; + echan->chan_id = BLI_toupper_ascii(channelname[0]); } else if (len > 1) { bool ok = false; @@ -1436,36 +1506,35 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa * * Here we do some magic to distinguish such cases. */ - if (ELEM(token[1], 'X', 'Y', 'Z') || ELEM(token[1], 'R', 'G', 'B') || - ELEM(token[1], 'U', 'V', 'A')) { - echan->chan_id = token[1]; + const char chan_id = BLI_toupper_ascii(channelname[1]); + if (ELEM(chan_id, 'X', 'Y', 'Z', 'R', 'G', 'B', 'U', 'V', 'A')) { + echan->chan_id = chan_id; ok = true; } } - else if (BLI_strcaseeq(token, "red")) { + else if (BLI_strcaseeq(channelname, "red")) { echan->chan_id = 'R'; ok = true; } - else if (BLI_strcaseeq(token, "green")) { + else if (BLI_strcaseeq(channelname, "green")) { echan->chan_id = 'G'; ok = true; } - else if (BLI_strcaseeq(token, "blue")) { + else if (BLI_strcaseeq(channelname, "blue")) { echan->chan_id = 'B'; ok = true; } - else if (BLI_strcaseeq(token, "alpha")) { + else if (BLI_strcaseeq(channelname, "alpha")) { echan->chan_id = 'A'; ok = true; } - else if (BLI_strcaseeq(token, "depth")) { + else if (BLI_strcaseeq(channelname, "depth")) { echan->chan_id = 'Z'; ok = true; } if (ok == false) { - BLI_strncpy(tokenbuf, token, std::min(len + 1, EXR_TOT_MAXNAME)); - printf("multilayer read: unknown channel token: %s\n", tokenbuf); + printf("multilayer read: unknown channel token: %s\n", channelname); return 0; } } @@ -2098,19 +2167,122 @@ struct ImBuf *imb_load_openexr(const unsigned char *mem, } } -void imb_initopenexr(void) +struct ImBuf *imb_load_filepath_thumbnail_openexr(const char *filepath, + const int UNUSED(flags), + const size_t max_thumb_size, + char colorspace[], + size_t *r_width, + size_t *r_height) { - int num_threads = BLI_system_thread_count(); + IStream *stream = nullptr; + Imf::RgbaInputFile *file = nullptr; + + /* OpenExr uses exceptions for error-handling. */ + try { - setGlobalThreadCount(num_threads); + /* The memory-mapped stream is faster, but don't use for huge files as it requires contiguous + * address space and we are processing multiple files at once (typically one per processor + * core). The 100 MB limit here is arbitrary, but seems reasonable and conservative. */ + if (BLI_file_size(filepath) < 100 * 1024 * 1024) { + stream = new IMMapStream(filepath); + } + else { + stream = new IFileStream(filepath); + } + + /* imb_initopenexr() creates a global pool of worker threads. But we thumbnail multiple images + * at once, and by default each file will attempt to use the entire pool for itself, stalling + * the others. So each thumbnail should use a single thread of the pool. */ + file = new RgbaInputFile(*stream, 1); + + if (!file->isComplete()) { + return nullptr; + } + + Imath::Box2i dw = file->dataWindow(); + int source_w = dw.max.x - dw.min.x + 1; + int source_h = dw.max.y - dw.min.y + 1; + *r_width = source_w; + *r_height = source_h; + + /* If there is an embedded thumbnail, return that instead of making a new one. */ + if (file->header().hasPreviewImage()) { + const Imf::PreviewImage &preview = file->header().previewImage(); + ImBuf *ibuf = IMB_allocFromBuffer( + (unsigned int *)preview.pixels(), nullptr, preview.width(), preview.height(), 4); + delete file; + delete stream; + IMB_flipy(ibuf); + return ibuf; + } + + /* Create a new thumbnail. */ + + if (colorspace && colorspace[0]) { + colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT); + } + + float scale_factor = MIN2((float)max_thumb_size / (float)source_w, + (float)max_thumb_size / (float)source_h); + int dest_w = (int)(source_w * scale_factor); + int dest_h = (int)(source_h * scale_factor); + + struct ImBuf *ibuf = IMB_allocImBuf(dest_w, dest_h, 32, IB_rectfloat); + + /* A single row of source pixels. */ + Imf::Array<Imf::Rgba> pixels(source_w); + + /* Loop through destination thumbnail rows. */ + for (int h = 0; h < dest_h; h++) { + + /* Load the single source row that corresponds with destination row. */ + int source_y = (int)((float)h / scale_factor) + dw.min.y; + file->setFrameBuffer(&pixels[0] - dw.min.x - source_y * source_w, 1, source_w); + file->readPixels(source_y); + + for (int w = 0; w < dest_w; w++) { + /* For each destination pixel find single corresponding source pixel. */ + int source_x = (int)(MIN2((w / scale_factor), dw.max.x - 1)); + float *dest_px = &ibuf->rect_float[(h * dest_w + w) * 4]; + dest_px[0] = pixels[source_x].r; + dest_px[1] = pixels[source_x].g; + dest_px[2] = pixels[source_x].b; + dest_px[3] = pixels[source_x].a; + } + } + + if (file->lineOrder() == INCREASING_Y) { + IMB_flipy(ibuf); + } + + delete file; + delete stream; + + return ibuf; + } + + catch (const std::exception &exc) { + std::cerr << exc.what() << std::endl; + delete file; + delete stream; + return nullptr; + } + + return nullptr; +} + +void imb_initopenexr(void) +{ + /* In a multithreaded program, staticInitialize() must be called once during startup, before the + * program accesses any other functions or classes in the IlmImf library. */ + Imf::staticInitialize(); + Imf::setGlobalThreadCount(BLI_system_thread_count()); } void imb_exitopenexr(void) { - /* Tells OpenEXR to free thread pool, also ensures there is no running - * tasks. - */ - setGlobalThreadCount(0); + /* Tells OpenEXR to free thread pool, also ensures there is no running tasks. */ + Imf::setGlobalThreadCount(0); } } /* export "C" */ diff --git a/source/blender/imbuf/intern/openexr/openexr_api.h b/source/blender/imbuf/intern/openexr/openexr_api.h index c02b03dbe6c..40a724c9f42 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.h +++ b/source/blender/imbuf/intern/openexr/openexr_api.h @@ -26,6 +26,13 @@ bool imb_save_openexr(struct ImBuf *ibuf, const char *name, int flags); struct ImBuf *imb_load_openexr(const unsigned char *mem, size_t size, int flags, char *colorspace); +struct ImBuf *imb_load_filepath_thumbnail_openexr(const char *filepath, + int flags, + size_t max_thumb_size, + char colorspace[], + size_t *r_width, + size_t *r_height); + #ifdef __cplusplus } #endif diff --git a/source/blender/imbuf/intern/readimage.c b/source/blender/imbuf/intern/readimage.c index 805a7e8d687..4b433836767 100644 --- a/source/blender/imbuf/intern/readimage.c +++ b/source/blender/imbuf/intern/readimage.c @@ -189,21 +189,21 @@ ImBuf *IMB_loadifffile( return ibuf; } -static void imb_cache_filename(char *filename, const char *name, int flags) +static void imb_cache_filename(char *filepath, const char *name, int flags) { /* read .tx instead if it exists and is not older */ if (flags & IB_tilecache) { - BLI_strncpy(filename, name, IMB_FILENAME_SIZE); - if (!BLI_path_extension_replace(filename, IMB_FILENAME_SIZE, ".tx")) { + BLI_strncpy(filepath, name, IMB_FILENAME_SIZE); + if (!BLI_path_extension_replace(filepath, IMB_FILENAME_SIZE, ".tx")) { return; } - if (BLI_file_older(name, filename)) { + if (BLI_file_older(name, filepath)) { return; } } - BLI_strncpy(filename, name, IMB_FILENAME_SIZE); + BLI_strncpy(filepath, name, IMB_FILENAME_SIZE); } ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]) diff --git a/source/blender/imbuf/intern/stereoimbuf.c b/source/blender/imbuf/intern/stereoimbuf.c index 52756891f21..2a0baaf6172 100644 --- a/source/blender/imbuf/intern/stereoimbuf.c +++ b/source/blender/imbuf/intern/stereoimbuf.c @@ -702,21 +702,21 @@ int *IMB_stereo3d_from_rect(const ImageFormatData *im_format, int *rect_left, int *rect_right) { - int *r_rect; + int *rect_result; Stereo3DData s3d_data = {{NULL}}; size_t width, height; const bool is_float = im_format->depth > 8; IMB_stereo3d_write_dimensions( im_format->stereo3d_format.display_mode, false, x, y, &width, &height); - r_rect = MEM_mallocN(channels * sizeof(int) * width * height, __func__); + rect_result = MEM_mallocN(channels * sizeof(int) * width * height, __func__); imb_stereo3d_data_init( - &s3d_data, is_float, x, y, channels, rect_left, rect_right, r_rect, NULL, NULL, NULL); + &s3d_data, is_float, x, y, channels, rect_left, rect_right, rect_result, NULL, NULL, NULL); imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); - imb_stereo3d_squeeze_rect(r_rect, &im_format->stereo3d_format, x, y, channels); + imb_stereo3d_squeeze_rect(rect_result, &im_format->stereo3d_format, x, y, channels); - return r_rect; + return rect_result; } float *IMB_stereo3d_from_rectf(const ImageFormatData *im_format, @@ -726,21 +726,30 @@ float *IMB_stereo3d_from_rectf(const ImageFormatData *im_format, float *rectf_left, float *rectf_right) { - float *r_rectf; + float *rectf_result; Stereo3DData s3d_data = {{NULL}}; size_t width, height; const bool is_float = im_format->depth > 8; IMB_stereo3d_write_dimensions( im_format->stereo3d_format.display_mode, false, x, y, &width, &height); - r_rectf = MEM_mallocN(channels * sizeof(float) * width * height, __func__); + rectf_result = MEM_mallocN(channels * sizeof(float) * width * height, __func__); - imb_stereo3d_data_init( - &s3d_data, is_float, x, y, channels, NULL, NULL, NULL, rectf_left, rectf_right, r_rectf); + imb_stereo3d_data_init(&s3d_data, + is_float, + x, + y, + channels, + NULL, + NULL, + NULL, + rectf_left, + rectf_right, + rectf_result); imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); - imb_stereo3d_squeeze_rectf(r_rectf, &im_format->stereo3d_format, x, y, channels); + imb_stereo3d_squeeze_rectf(rectf_result, &im_format->stereo3d_format, x, y, channels); - return r_rectf; + return rectf_result; } ImBuf *IMB_stereo3d_ImBuf(const ImageFormatData *im_format, ImBuf *ibuf_left, ImBuf *ibuf_right) diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c index 51951aa9605..6f39009d38d 100644 --- a/source/blender/imbuf/intern/thumbs.c +++ b/source/blender/imbuf/intern/thumbs.c @@ -30,7 +30,6 @@ #include "IMB_thumbs.h" #include <ctype.h> -#include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> @@ -466,27 +465,27 @@ static ImBuf *thumb_create_or_fail(const char *file_path, return img; } -ImBuf *IMB_thumb_create(const char *path, ThumbSize size, ThumbSource source, ImBuf *img) +ImBuf *IMB_thumb_create(const char *filepath, ThumbSize size, ThumbSource source, ImBuf *img) { char uri[URI_MAX] = ""; char thumb_name[40]; - if (!uri_from_filename(path, uri)) { + if (!uri_from_filename(filepath, uri)) { return NULL; } thumbname_from_uri(uri, thumb_name, sizeof(thumb_name)); return thumb_create_ex( - path, uri, thumb_name, false, THUMB_DEFAULT_HASH, NULL, NULL, size, source, img); + filepath, uri, thumb_name, false, THUMB_DEFAULT_HASH, NULL, NULL, size, source, img); } -ImBuf *IMB_thumb_read(const char *path, ThumbSize size) +ImBuf *IMB_thumb_read(const char *filepath, ThumbSize size) { char thumb[FILE_MAX]; char uri[URI_MAX]; ImBuf *img = NULL; - if (!uri_from_filename(path, uri)) { + if (!uri_from_filename(filepath, uri)) { return NULL; } if (thumbpath_from_uri(uri, thumb, sizeof(thumb), size)) { @@ -496,16 +495,16 @@ ImBuf *IMB_thumb_read(const char *path, ThumbSize size) return img; } -void IMB_thumb_delete(const char *path, ThumbSize size) +void IMB_thumb_delete(const char *filepath, ThumbSize size) { char thumb[FILE_MAX]; char uri[URI_MAX]; - if (!uri_from_filename(path, uri)) { + if (!uri_from_filename(filepath, uri)) { return; } if (thumbpath_from_uri(uri, thumb, sizeof(thumb), size)) { - if (BLI_path_ncmp(path, thumb, sizeof(thumb)) == 0) { + if (BLI_path_ncmp(filepath, thumb, sizeof(thumb)) == 0) { return; } if (BLI_exists(thumb)) { @@ -514,7 +513,7 @@ void IMB_thumb_delete(const char *path, ThumbSize size) } } -ImBuf *IMB_thumb_manage(const char *org_path, ThumbSize size, ThumbSource source) +ImBuf *IMB_thumb_manage(const char *filepath, ThumbSize size, ThumbSource source) { char thumb_path[FILE_MAX]; char thumb_name[40]; @@ -526,7 +525,7 @@ ImBuf *IMB_thumb_manage(const char *org_path, ThumbSize size, ThumbSource source ImBuf *img = NULL; char *blen_group = NULL, *blen_id = NULL; - path = file_path = org_path; + path = file_path = filepath; if (source == THB_SOURCE_BLEND) { if (BLO_library_path_explode(path, path_buff, &blen_group, &blen_id)) { if (blen_group) { diff --git a/source/blender/imbuf/intern/thumbs_font.c b/source/blender/imbuf/intern/thumbs_font.c index 7a1c4947c99..c0a33f608a5 100644 --- a/source/blender/imbuf/intern/thumbs_font.c +++ b/source/blender/imbuf/intern/thumbs_font.c @@ -41,7 +41,7 @@ void IMB_thumb_ensure_translations(void) } } -struct ImBuf *IMB_thumb_load_font(const char *filename, unsigned int x, unsigned int y) +struct ImBuf *IMB_thumb_load_font(const char *filepath, unsigned int x, unsigned int y) { const int font_size = y / 4; @@ -60,7 +60,7 @@ struct ImBuf *IMB_thumb_load_font(const char *filename, unsigned int x, unsigned /* draw with full alpha */ font_color[3] = 1.0f; - BLF_thumb_preview(filename, + BLF_thumb_preview(filepath, thumb_str, i18n_thumb_str, ARRAY_SIZE(thumb_str), diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c index 1372aa31713..2f13ef409e3 100644 --- a/source/blender/imbuf/intern/tiff.c +++ b/source/blender/imbuf/intern/tiff.c @@ -8,15 +8,15 @@ * Provides TIFF file loading and saving for Blender, via libtiff. * * The task of loading is complicated somewhat by the fact that Blender has - * already loaded the file into a memory buffer. libtiff is not well + * already loaded the file into a memory buffer. libtiff is not well * configured to handle files in memory, so a client wrapper is written to - * surround the memory and turn it into a virtual file. Currently, reading - * of TIFF files is done using libtiff's RGBAImage support. This is a + * surround the memory and turn it into a virtual file. Currently, reading + * of TIFF files is done using libtiff's RGBAImage support. This is a * high-level routine that loads all images as 32-bit RGBA, handling all the * required conversions between many different TIFF types internally. * * Saving supports RGB, RGBA and BW (gray-scale) images correctly, with - * 8 bits per channel in all cases. The "deflate" compression algorithm is + * 8 bits per channel in all cases. The "deflate" compression algorithm is * used to compress images. */ @@ -151,8 +151,8 @@ static tsize_t imb_tiff_ReadProc(thandle_t handle, tdata_t data, tsize_t n) /** * Writes data to an in-memory TIFF file. * - * NOTE: The current Blender implementation should not need this function. It - * is simply a stub. + * NOTE: The current Blender implementation should not need this function. + * It is simply a stub. */ static tsize_t imb_tiff_WriteProc(thandle_t handle, tdata_t data, tsize_t n) { @@ -176,7 +176,7 @@ static tsize_t imb_tiff_WriteProc(thandle_t handle, tdata_t data, tsize_t n) * error). * * \return Resulting offset location within the file, measured in bytes from - * the beginning of the file. (-1) indicates an error. + * the beginning of the file. (-1) indicates an error. */ static toff_t imb_tiff_SeekProc(thandle_t handle, toff_t ofs, int whence) { @@ -215,8 +215,8 @@ static toff_t imb_tiff_SeekProc(thandle_t handle, toff_t ofs, int whence) * Closes (virtually) an in-memory TIFF file. * * NOTE: All this function actually does is sets the data pointer within the - * TIFF file to NULL. That should trigger assertion errors if attempts - * are made to access the file after that point. However, no such + * TIFF file to NULL. That should trigger assertion errors if attempts + * are made to access the file after that point. However, no such * attempts should ever be made (in theory). * * \param handle: Handle of the TIFF file (pointer to #ImbTIFFMemFile). @@ -734,7 +734,7 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags) int x, y, from_i, to_i, i; int compress_mode = COMPRESSION_NONE; - /* check for a valid number of bytes per pixel. Like the PNG writer, + /* check for a valid number of bytes per pixel. Like the PNG writer, * the TIFF writer supports 1, 3 or 4 bytes per pixel, corresponding * to gray, RGB, RGBA respectively. */ samplesperpixel = (uint16_t)((ibuf->planes + 7) >> 3); @@ -838,7 +838,7 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags) TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); } - /* copy pixel data. While copying, we flip the image vertically. */ + /* copy pixel data. While copying, we flip the image vertically. */ const int channels_in_float = ibuf->channels ? ibuf->channels : 4; for (x = 0; x < ibuf->x; x++) { for (y = 0; y < ibuf->y; y++) { diff --git a/source/blender/imbuf/intern/transform.cc b/source/blender/imbuf/intern/transform.cc index edb47c8c7ce..1499c1071e3 100644 --- a/source/blender/imbuf/intern/transform.cc +++ b/source/blender/imbuf/intern/transform.cc @@ -293,30 +293,37 @@ class Sampler { NumChannels == 4) { const float wrapped_u = uv_wrapper.modify_u(source, u); const float wrapped_v = uv_wrapper.modify_v(source, v); - bilinear_interpolation_color_fl(source, nullptr, &r_sample[0], wrapped_u, wrapped_v); + bilinear_interpolation_color_fl(source, nullptr, r_sample.data(), wrapped_u, wrapped_v); } else if constexpr (Filter == IMB_FILTER_NEAREST && std::is_same_v<StorageType, unsigned char> && NumChannels == 4) { const float wrapped_u = uv_wrapper.modify_u(source, u); const float wrapped_v = uv_wrapper.modify_v(source, v); - nearest_interpolation_color_char(source, &r_sample[0], nullptr, wrapped_u, wrapped_v); + nearest_interpolation_color_char(source, r_sample.data(), nullptr, wrapped_u, wrapped_v); } else if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v<StorageType, unsigned char> && NumChannels == 4) { const float wrapped_u = uv_wrapper.modify_u(source, u); const float wrapped_v = uv_wrapper.modify_v(source, v); - bilinear_interpolation_color_char(source, &r_sample[0], nullptr, wrapped_u, wrapped_v); + bilinear_interpolation_color_char(source, r_sample.data(), nullptr, wrapped_u, wrapped_v); } else if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v<StorageType, float>) { if constexpr (std::is_same_v<UVWrapping, WrapRepeatUV>) { - BLI_bilinear_interpolation_wrap_fl( - source->rect_float, &r_sample[0], source->x, source->y, NumChannels, u, v, true, true); + BLI_bilinear_interpolation_wrap_fl(source->rect_float, + r_sample.data(), + source->x, + source->y, + NumChannels, + u, + v, + true, + true); } else { const float wrapped_u = uv_wrapper.modify_u(source, u); const float wrapped_v = uv_wrapper.modify_v(source, v); BLI_bilinear_interpolation_fl(source->rect_float, - &r_sample[0], + r_sample.data(), source->x, source->y, NumChannels, @@ -390,11 +397,11 @@ class ChannelConverter { BLI_STATIC_ASSERT(SourceNumChannels == 4, "Unsigned chars always have 4 channels."); BLI_STATIC_ASSERT(DestinationNumChannels == 4, "Unsigned chars always have 4 channels."); - copy_v4_v4_uchar(pixel_pointer.get_pointer(), &sample[0]); + copy_v4_v4_uchar(pixel_pointer.get_pointer(), sample.data()); } else if constexpr (std::is_same_v<StorageType, float> && SourceNumChannels == 4 && DestinationNumChannels == 4) { - copy_v4_v4(pixel_pointer.get_pointer(), &sample[0]); + copy_v4_v4(pixel_pointer.get_pointer(), sample.data()); } else if constexpr (std::is_same_v<StorageType, float> && SourceNumChannels == 3 && DestinationNumChannels == 4) { diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c index 45b50c866fe..ffa989a29b4 100644 --- a/source/blender/imbuf/intern/util.c +++ b/source/blender/imbuf/intern/util.c @@ -390,14 +390,3 @@ bool IMB_isanim(const char *filepath) return (type && type != ANIM_SEQUENCE); } - -bool IMB_isfloat(const ImBuf *ibuf) -{ - const ImFileType *type = IMB_file_type_from_ibuf(ibuf); - if (type != NULL) { - if (type->flag & IM_FTYPE_FLOAT) { - return true; - } - } - return false; -} diff --git a/source/blender/imbuf/intern/util_gpu.c b/source/blender/imbuf/intern/util_gpu.c index 5abbc84b0ea..5feb0ceb515 100644 --- a/source/blender/imbuf/intern/util_gpu.c +++ b/source/blender/imbuf/intern/util_gpu.c @@ -28,17 +28,30 @@ static void imb_gpu_get_format(const ImBuf *ibuf, eGPUTextureFormat *r_texture_format) { const bool float_rect = (ibuf->rect_float != NULL); - const bool use_srgb = (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace) && - !IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)); - high_bitdepth = (!(ibuf->flags & IB_halffloat) && high_bitdepth); - - *r_data_format = (float_rect) ? GPU_DATA_FLOAT : GPU_DATA_UBYTE; if (float_rect) { - *r_texture_format = high_bitdepth ? GPU_RGBA32F : GPU_RGBA16F; + /* Float. */ + const bool use_high_bitdepth = (!(ibuf->flags & IB_halffloat) && high_bitdepth); + *r_data_format = GPU_DATA_FLOAT; + *r_texture_format = use_high_bitdepth ? GPU_RGBA32F : GPU_RGBA16F; } else { - *r_texture_format = use_srgb ? GPU_SRGB8_A8 : GPU_RGBA8; + if (IMB_colormanagement_space_is_data(ibuf->rect_colorspace) || + IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)) { + /* Non-color data or scene linear, just store buffer as is. */ + *r_data_format = GPU_DATA_UBYTE; + *r_texture_format = GPU_RGBA8; + } + else if (IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace)) { + /* sRGB, store as byte texture that the GPU can decode directly. */ + *r_data_format = GPU_DATA_UBYTE; + *r_texture_format = GPU_SRGB8_A8; + } + else { + /* Other colorspace, store as half float texture to avoid precision loss. */ + *r_data_format = GPU_DATA_FLOAT; + *r_texture_format = GPU_RGBA16F; + } } } @@ -74,7 +87,6 @@ static bool IMB_gpu_get_compressed_format(const ImBuf *ibuf, eGPUTextureFormat * static void *imb_gpu_get_data(const ImBuf *ibuf, const bool do_rescale, const int rescale_size[2], - const bool compress_as_srgb, const bool store_premultiplied, bool *r_freedata) { @@ -99,14 +111,16 @@ static void *imb_gpu_get_data(const ImBuf *ibuf, } } else { - /* Byte image is in original colorspace from the file. If the file is sRGB - * scene linear, or non-color data no conversion is needed. Otherwise we - * compress as scene linear + sRGB transfer function to avoid precision loss - * in common cases. + /* Byte image is in original colorspace from the file, and may need conversion. * * We must also convert to premultiplied for correct texture interpolation * and consistency with float images. */ - if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { + if (IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { + /* Non-color data, just store buffer as is. */ + } + else if (IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace) || + IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)) { + /* sRGB or scene linear, store as byte texture that the GPU can decode directly. */ data_rect = MEM_mallocN(sizeof(uchar[4]) * ibuf->x * ibuf->y, __func__); *r_freedata = freedata = true; @@ -120,7 +134,24 @@ static void *imb_gpu_get_data(const ImBuf *ibuf, * zero alpha areas, and appears generally closer to what game engines that we * want to be compatible with do. */ IMB_colormanagement_imbuf_to_byte_texture( - (uchar *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, compress_as_srgb, store_premultiplied); + (uchar *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied); + } + else { + /* Other colorspace, store as float texture to avoid precision loss. */ + data_rect = MEM_mallocN(sizeof(float[4]) * ibuf->x * ibuf->y, __func__); + *r_freedata = freedata = true; + + if (data_rect == NULL) { + return NULL; + } + + /* Texture storage of images is defined by the alpha mode of the image. The + * downside of this is that there can be artifacts near alpha edges. However, + * this allows us to use sRGB texture formats and preserves color values in + * zero alpha areas, and appears generally closer to what game engines that we + * want to be compatible with do. */ + IMB_colormanagement_imbuf_to_float_texture( + (float *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied); } } @@ -154,7 +185,7 @@ GPUTexture *IMB_touch_gpu_texture( GPUTexture *tex; if (layers > 0) { - tex = GPU_texture_create_2d_array(name, w, h, layers, 1, tex_format, NULL); + tex = GPU_texture_create_2d_array(name, w, h, layers, 9999, tex_format, NULL); } else { tex = GPU_texture_create_2d(name, w, h, 9999, tex_format, NULL); @@ -181,10 +212,9 @@ void IMB_update_gpu_texture_sub(GPUTexture *tex, eGPUTextureFormat tex_format; imb_gpu_get_format(ibuf, use_high_bitdepth, &data_format, &tex_format); - const bool compress_as_srgb = (tex_format == GPU_SRGB8_A8); bool freebuf = false; - void *data = imb_gpu_get_data(ibuf, do_rescale, size, compress_as_srgb, use_premult, &freebuf); + void *data = imb_gpu_get_data(ibuf, do_rescale, size, use_premult, &freebuf); /* Update Texture. */ GPU_texture_update_sub(tex, data_format, data, x, y, z, w, h, 1); @@ -197,12 +227,10 @@ void IMB_update_gpu_texture_sub(GPUTexture *tex, GPUTexture *IMB_create_gpu_texture(const char *name, ImBuf *ibuf, bool use_high_bitdepth, - bool use_premult, - bool limit_gl_texture_size) + bool use_premult) { GPUTexture *tex = NULL; - int size[2] = {GPU_texture_size_with_limit(ibuf->x, limit_gl_texture_size), - GPU_texture_size_with_limit(ibuf->y, limit_gl_texture_size)}; + int size[2] = {GPU_texture_size_with_limit(ibuf->x), GPU_texture_size_with_limit(ibuf->y)}; bool do_rescale = (ibuf->x != size[0]) || (ibuf->y != size[1]); #ifdef WITH_DDS @@ -240,7 +268,6 @@ GPUTexture *IMB_create_gpu_texture(const char *name, eGPUTextureFormat tex_format; imb_gpu_get_format(ibuf, use_high_bitdepth, &data_format, &tex_format); - const bool compress_as_srgb = (tex_format == GPU_SRGB8_A8); bool freebuf = false; /* Create Texture. */ @@ -252,7 +279,7 @@ GPUTexture *IMB_create_gpu_texture(const char *name, do_rescale = true; } BLI_assert(tex != NULL); - void *data = imb_gpu_get_data(ibuf, do_rescale, size, compress_as_srgb, use_premult, &freebuf); + void *data = imb_gpu_get_data(ibuf, do_rescale, size, use_premult, &freebuf); GPU_texture_update(tex, data_format, data); GPU_texture_anisotropic_filter(tex, true); diff --git a/source/blender/imbuf/intern/writeimage.c b/source/blender/imbuf/intern/writeimage.c index 56c9384a330..d2c0b61c1c5 100644 --- a/source/blender/imbuf/intern/writeimage.c +++ b/source/blender/imbuf/intern/writeimage.c @@ -19,11 +19,6 @@ #include "IMB_colormanagement.h" #include "IMB_colormanagement_intern.h" -static bool prepare_write_imbuf(const ImFileType *type, ImBuf *ibuf) -{ - return IMB_prepare_write_ImBuf((type->flag & IM_FTYPE_FLOAT), ibuf); -} - bool IMB_saveiff(struct ImBuf *ibuf, const char *filepath, int flags) { errno = 0; @@ -36,34 +31,22 @@ bool IMB_saveiff(struct ImBuf *ibuf, const char *filepath, int flags) ibuf->flags = flags; const ImFileType *type = IMB_file_type_from_ibuf(ibuf); - if (type != NULL) { - if (type->save != NULL) { - prepare_write_imbuf(type, ibuf); - return type->save(ibuf, filepath, flags); - } + if (type == NULL || type->save == NULL) { + fprintf(stderr, "Couldn't save picture.\n"); + return false; } - fprintf(stderr, "Couldn't save picture.\n"); - - return false; -} - -bool IMB_prepare_write_ImBuf(const bool isfloat, ImBuf *ibuf) -{ - bool changed = false; - - if (isfloat) { - /* pass */ - } - else { + /* If writing byte image from float buffer, create a byte buffer for writing. + * + * For color managed image writing, IMB_colormanagement_imbuf_for_write should + * have already created this byte buffer. This is a basic fallback for other + * cases where we do not have a specific desired output colorspace. */ + if (!(type->flag & IM_FTYPE_FLOAT)) { if (ibuf->rect == NULL && ibuf->rect_float) { ibuf->rect_colorspace = colormanage_colorspace_get_roled(COLOR_ROLE_DEFAULT_BYTE); IMB_rect_from_float(ibuf); - if (ibuf->rect != NULL) { - changed = true; - } } } - return changed; + return type->save(ibuf, filepath, flags); } |