The main goal of these changes are to improve static (i.e. build-time) checks on whether a given data can be allocated and freed with `malloc` and `free` (C-style), or requires proper C++-style construction and destruction (`new` and `delete`). * Add new `MEM_malloc_arrayN_aligned` API. * Make `MEM_freeN` a template function in C++, which does static assert on type triviality. * Add `MEM_SAFE_DELETE`, similar to `MEM_SAFE_FREE` but calling `MEM_delete`. The changes to `MEM_freeN` was painful and useful, as it allowed to fix a bunch of invalid calls in existing codebase already. It also highlighted a fair amount of places where it is called to free incomplete type pointers, which is likely a sign of badly designed code (there should rather be an API to destroy and free these data then, if the data type is not fully publicly exposed). For now, these are 'worked around' by explicitly casting the freed pointers to `void *` in these cases - which also makes them easy to search for. Some of these will be addressed separately (see blender/blender!134765). Finally, MSVC seems to consider structs defining new/delete operators (e.g. by using the `MEM_CXX_CLASS_ALLOC_FUNCS` macro) as non-trivial. This does not seem to follow the definition of type triviality, so for now static type checking in `MEM_freeN` has been disabled for Windows. We'll likely have to do the same with type-safe `MEM_[cm]allocN` API being worked on in blender/blender!134771 Based on ideas from Brecht in blender/blender!134452 Pull Request: https://projects.blender.org/blender/blender/pulls/134463
541 lines
15 KiB
C++
541 lines
15 KiB
C++
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
|
|
#include "BLI_math_color.h"
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "ocio_impl.h"
|
|
|
|
using std::max;
|
|
|
|
#define CONFIG_DEFAULT ((OCIO_ConstConfigRcPtr *)1)
|
|
|
|
enum TransformType {
|
|
TRANSFORM_LINEAR_TO_SRGB,
|
|
TRANSFORM_SRGB_TO_LINEAR,
|
|
TRANSFORM_SCALE,
|
|
TRANSFORM_EXPONENT,
|
|
TRANSFORM_NONE,
|
|
TRANSFORM_UNKNOWN,
|
|
};
|
|
|
|
#define COLORSPACE_LINEAR ((OCIO_ConstColorSpaceRcPtr *)1)
|
|
#define COLORSPACE_SRGB ((OCIO_ConstColorSpaceRcPtr *)2)
|
|
#define COLORSPACE_DATA ((OCIO_ConstColorSpaceRcPtr *)3)
|
|
|
|
struct OCIO_PackedImageDescription {
|
|
float *data;
|
|
long width;
|
|
long height;
|
|
long numChannels;
|
|
long chanStrideBytes;
|
|
long xStrideBytes;
|
|
long yStrideBytes;
|
|
};
|
|
|
|
struct FallbackTransform {
|
|
FallbackTransform() = default;
|
|
virtual ~FallbackTransform() = default;
|
|
|
|
void applyRGB(float *pixel)
|
|
{
|
|
if (type == TRANSFORM_LINEAR_TO_SRGB) {
|
|
pixel[0] *= scale;
|
|
pixel[1] *= scale;
|
|
pixel[2] *= scale;
|
|
|
|
linearrgb_to_srgb_v3_v3(pixel, pixel);
|
|
|
|
pixel[0] = powf(max(0.0f, pixel[0]), exponent);
|
|
pixel[1] = powf(max(0.0f, pixel[1]), exponent);
|
|
pixel[2] = powf(max(0.0f, pixel[2]), exponent);
|
|
}
|
|
else if (type == TRANSFORM_SRGB_TO_LINEAR) {
|
|
srgb_to_linearrgb_v3_v3(pixel, pixel);
|
|
}
|
|
else if (type == TRANSFORM_EXPONENT) {
|
|
pixel[0] = powf(max(0.0f, pixel[0]), exponent);
|
|
pixel[1] = powf(max(0.0f, pixel[1]), exponent);
|
|
pixel[2] = powf(max(0.0f, pixel[2]), exponent);
|
|
}
|
|
else if (type == TRANSFORM_SCALE) {
|
|
pixel[0] *= scale;
|
|
pixel[1] *= scale;
|
|
pixel[2] *= scale;
|
|
}
|
|
}
|
|
|
|
void applyRGBA(float *pixel)
|
|
{
|
|
applyRGB(pixel);
|
|
}
|
|
|
|
bool isNoOp()
|
|
{
|
|
/* Rely on the short-circuiting based on name-space comparison in the IMB_colormanagement. */
|
|
return false;
|
|
}
|
|
|
|
TransformType type = TRANSFORM_UNKNOWN;
|
|
/* Scale transform. */
|
|
float scale = 1.0f;
|
|
/* Exponent transform. */
|
|
float exponent = 1.0f;
|
|
|
|
MEM_CXX_CLASS_ALLOC_FUNCS("FallbackTransform");
|
|
};
|
|
|
|
struct FallbackProcessor {
|
|
FallbackProcessor(const FallbackTransform &transform) : transform(transform) {}
|
|
|
|
void applyRGB(float *pixel)
|
|
{
|
|
transform.applyRGB(pixel);
|
|
}
|
|
|
|
void applyRGBA(float *pixel)
|
|
{
|
|
transform.applyRGBA(pixel);
|
|
}
|
|
|
|
bool isNoOp()
|
|
{
|
|
return transform.isNoOp();
|
|
}
|
|
|
|
FallbackTransform transform;
|
|
|
|
MEM_CXX_CLASS_ALLOC_FUNCS("FallbackProcessor");
|
|
};
|
|
|
|
OCIO_ConstConfigRcPtr *FallbackImpl::getCurrentConfig()
|
|
{
|
|
return CONFIG_DEFAULT;
|
|
}
|
|
|
|
void FallbackImpl::setCurrentConfig(const OCIO_ConstConfigRcPtr * /*config*/) {}
|
|
|
|
OCIO_ConstConfigRcPtr *FallbackImpl::configCreateFromEnv()
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
OCIO_ConstConfigRcPtr *FallbackImpl::configCreateFromFile(const char * /*filename*/)
|
|
{
|
|
return CONFIG_DEFAULT;
|
|
}
|
|
|
|
void FallbackImpl::configRelease(OCIO_ConstConfigRcPtr * /*config*/) {}
|
|
|
|
int FallbackImpl::configGetNumColorSpaces(OCIO_ConstConfigRcPtr * /*config*/)
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
const char *FallbackImpl::configGetColorSpaceNameByIndex(OCIO_ConstConfigRcPtr * /*config*/,
|
|
int index)
|
|
{
|
|
if (index == 0) {
|
|
return "Linear";
|
|
}
|
|
if (index == 1) {
|
|
return "sRGB";
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
OCIO_ConstColorSpaceRcPtr *FallbackImpl::configGetColorSpace(OCIO_ConstConfigRcPtr * /*config*/,
|
|
const char *name)
|
|
{
|
|
if (strcmp(name, "scene_linear") == 0) {
|
|
return COLORSPACE_LINEAR;
|
|
}
|
|
if (strcmp(name, "color_picking") == 0) {
|
|
return COLORSPACE_SRGB;
|
|
}
|
|
if (strcmp(name, "texture_paint") == 0) {
|
|
return COLORSPACE_LINEAR;
|
|
}
|
|
if (strcmp(name, "default_byte") == 0) {
|
|
return COLORSPACE_SRGB;
|
|
}
|
|
if (strcmp(name, "default_float") == 0) {
|
|
return COLORSPACE_LINEAR;
|
|
}
|
|
if (strcmp(name, "default_sequencer") == 0) {
|
|
return COLORSPACE_SRGB;
|
|
}
|
|
if (strcmp(name, "Linear") == 0) {
|
|
return COLORSPACE_LINEAR;
|
|
}
|
|
if (strcmp(name, "sRGB") == 0) {
|
|
return COLORSPACE_SRGB;
|
|
}
|
|
if (strcmp(name, "data") == 0) {
|
|
return COLORSPACE_DATA;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
int FallbackImpl::configGetIndexForColorSpace(OCIO_ConstConfigRcPtr *config, const char *name)
|
|
{
|
|
OCIO_ConstColorSpaceRcPtr *cs = configGetColorSpace(config, name);
|
|
|
|
if (cs == COLORSPACE_LINEAR) {
|
|
return 0;
|
|
}
|
|
if (cs == COLORSPACE_SRGB) {
|
|
return 1;
|
|
}
|
|
if (cs == COLORSPACE_DATA) {
|
|
return 2;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
const char *FallbackImpl::configGetDefaultDisplay(OCIO_ConstConfigRcPtr * /*config*/)
|
|
{
|
|
return "sRGB";
|
|
}
|
|
|
|
int FallbackImpl::configGetNumDisplays(OCIO_ConstConfigRcPtr * /*config*/)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
const char *FallbackImpl::configGetDisplay(OCIO_ConstConfigRcPtr * /*config*/, int index)
|
|
{
|
|
if (index == 0) {
|
|
return "sRGB";
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const char *FallbackImpl::configGetDefaultView(OCIO_ConstConfigRcPtr * /*config*/,
|
|
const char * /*display*/)
|
|
{
|
|
return "Standard";
|
|
}
|
|
|
|
int FallbackImpl::configGetNumViews(OCIO_ConstConfigRcPtr * /*config*/, const char * /*display*/)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
const char *FallbackImpl::configGetView(OCIO_ConstConfigRcPtr * /*config*/,
|
|
const char * /*display*/,
|
|
int index)
|
|
{
|
|
if (index == 0) {
|
|
return "Standard";
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const char *FallbackImpl::configGetDisplayColorSpaceName(OCIO_ConstConfigRcPtr * /*config*/,
|
|
const char * /*display*/,
|
|
const char * /*view*/)
|
|
{
|
|
return "sRGB";
|
|
}
|
|
|
|
void FallbackImpl::configGetDefaultLumaCoefs(OCIO_ConstConfigRcPtr * /*config*/, float *rgb)
|
|
{
|
|
/* Here we simply use the older Blender assumed primaries of
|
|
* ITU-BT.709 / sRGB, or 0.2126729 0.7151522 0.0721750. Brute
|
|
* force stupid, but only plausible option given no color management
|
|
* system in place.
|
|
*/
|
|
|
|
rgb[0] = 0.2126f;
|
|
rgb[1] = 0.7152f;
|
|
rgb[2] = 0.0722f;
|
|
}
|
|
|
|
void FallbackImpl::configGetXYZtoSceneLinear(OCIO_ConstConfigRcPtr * /*config*/,
|
|
float xyz_to_scene_linear[3][3])
|
|
{
|
|
/* Default to ITU-BT.709. */
|
|
memcpy(xyz_to_scene_linear, OCIO_XYZ_TO_REC709, sizeof(OCIO_XYZ_TO_REC709));
|
|
}
|
|
|
|
int FallbackImpl::configGetNumLooks(OCIO_ConstConfigRcPtr * /*config*/)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
const char *FallbackImpl::configGetLookNameByIndex(OCIO_ConstConfigRcPtr * /*config*/,
|
|
int /*index*/)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
OCIO_ConstLookRcPtr *FallbackImpl::configGetLook(OCIO_ConstConfigRcPtr * /*config*/,
|
|
const char * /*name*/)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
const char *FallbackImpl::lookGetProcessSpace(OCIO_ConstLookRcPtr * /*look*/)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
void FallbackImpl::lookRelease(OCIO_ConstLookRcPtr * /*look*/) {}
|
|
|
|
int FallbackImpl::colorSpaceIsInvertible(OCIO_ConstColorSpaceRcPtr * /*cs*/)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
int FallbackImpl::colorSpaceIsData(OCIO_ConstColorSpaceRcPtr * /*cs*/)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void FallbackImpl::colorSpaceIsBuiltin(OCIO_ConstConfigRcPtr * /*config*/,
|
|
OCIO_ConstColorSpaceRcPtr *cs,
|
|
bool &is_scene_linear,
|
|
bool &is_srgb)
|
|
{
|
|
if (cs == COLORSPACE_LINEAR) {
|
|
is_scene_linear = true;
|
|
is_srgb = false;
|
|
}
|
|
else if (cs == COLORSPACE_SRGB) {
|
|
is_scene_linear = false;
|
|
is_srgb = true;
|
|
}
|
|
else {
|
|
is_scene_linear = false;
|
|
is_srgb = false;
|
|
}
|
|
}
|
|
|
|
void FallbackImpl::colorSpaceRelease(OCIO_ConstColorSpaceRcPtr * /*cs*/) {}
|
|
|
|
OCIO_ConstProcessorRcPtr *FallbackImpl::configGetProcessorWithNames(OCIO_ConstConfigRcPtr *config,
|
|
const char *srcName,
|
|
const char *dstName)
|
|
{
|
|
OCIO_ConstColorSpaceRcPtr *cs_src = configGetColorSpace(config, srcName);
|
|
OCIO_ConstColorSpaceRcPtr *cs_dst = configGetColorSpace(config, dstName);
|
|
FallbackTransform transform;
|
|
if (cs_src == COLORSPACE_DATA || cs_dst == COLORSPACE_DATA) {
|
|
transform.type = TRANSFORM_NONE;
|
|
}
|
|
else if (cs_src == COLORSPACE_LINEAR && cs_dst == COLORSPACE_SRGB) {
|
|
transform.type = TRANSFORM_LINEAR_TO_SRGB;
|
|
}
|
|
else if (cs_src == COLORSPACE_SRGB && cs_dst == COLORSPACE_LINEAR) {
|
|
transform.type = TRANSFORM_SRGB_TO_LINEAR;
|
|
}
|
|
else {
|
|
transform.type = TRANSFORM_UNKNOWN;
|
|
}
|
|
return (OCIO_ConstProcessorRcPtr *)new FallbackProcessor(transform);
|
|
}
|
|
|
|
OCIO_ConstCPUProcessorRcPtr *FallbackImpl::processorGetCPUProcessor(
|
|
OCIO_ConstProcessorRcPtr *processor)
|
|
{
|
|
/* Just make a copy of the processor so that we are compatible with OCIO
|
|
* which does need it as a separate object. */
|
|
FallbackProcessor *fallback_processor = (FallbackProcessor *)processor;
|
|
return (OCIO_ConstCPUProcessorRcPtr *)new FallbackProcessor(*fallback_processor);
|
|
}
|
|
|
|
void FallbackImpl::processorRelease(OCIO_ConstProcessorRcPtr *processor)
|
|
{
|
|
delete (FallbackProcessor *)(processor);
|
|
}
|
|
|
|
bool FallbackImpl::cpuProcessorIsNoOp(OCIO_ConstCPUProcessorRcPtr *cpu_processor)
|
|
{
|
|
return ((FallbackProcessor *)cpu_processor)->isNoOp();
|
|
}
|
|
|
|
void FallbackImpl::cpuProcessorApply(OCIO_ConstCPUProcessorRcPtr *cpu_processor,
|
|
OCIO_PackedImageDesc *img)
|
|
{
|
|
/* OCIO_TODO stride not respected, channels must be 3 or 4 */
|
|
OCIO_PackedImageDescription *desc = (OCIO_PackedImageDescription *)img;
|
|
int channels = desc->numChannels;
|
|
float *pixels = desc->data;
|
|
int width = desc->width;
|
|
int height = desc->height;
|
|
int x, y;
|
|
|
|
for (y = 0; y < height; y++) {
|
|
for (x = 0; x < width; x++) {
|
|
float *pixel = pixels + channels * (y * width + x);
|
|
|
|
if (channels == 4) {
|
|
cpuProcessorApplyRGBA(cpu_processor, pixel);
|
|
}
|
|
else if (channels == 3) {
|
|
cpuProcessorApplyRGB(cpu_processor, pixel);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FallbackImpl::cpuProcessorApply_predivide(OCIO_ConstCPUProcessorRcPtr *cpu_processor,
|
|
OCIO_PackedImageDesc *img)
|
|
{
|
|
/* OCIO_TODO stride not respected, channels must be 3 or 4 */
|
|
OCIO_PackedImageDescription *desc = (OCIO_PackedImageDescription *)img;
|
|
int channels = desc->numChannels;
|
|
float *pixels = desc->data;
|
|
int width = desc->width;
|
|
int height = desc->height;
|
|
int x, y;
|
|
|
|
for (y = 0; y < height; y++) {
|
|
for (x = 0; x < width; x++) {
|
|
float *pixel = pixels + channels * (y * width + x);
|
|
|
|
if (channels == 4) {
|
|
cpuProcessorApplyRGBA_predivide(cpu_processor, pixel);
|
|
}
|
|
else if (channels == 3) {
|
|
cpuProcessorApplyRGB(cpu_processor, pixel);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FallbackImpl::cpuProcessorApplyRGB(OCIO_ConstCPUProcessorRcPtr *cpu_processor, float *pixel)
|
|
{
|
|
((FallbackProcessor *)cpu_processor)->applyRGB(pixel);
|
|
}
|
|
|
|
void FallbackImpl::cpuProcessorApplyRGBA(OCIO_ConstCPUProcessorRcPtr *cpu_processor, float *pixel)
|
|
{
|
|
((FallbackProcessor *)cpu_processor)->applyRGBA(pixel);
|
|
}
|
|
|
|
void FallbackImpl::cpuProcessorApplyRGBA_predivide(OCIO_ConstCPUProcessorRcPtr *cpu_processor,
|
|
float *pixel)
|
|
{
|
|
if (pixel[3] == 1.0f || pixel[3] == 0.0f) {
|
|
cpuProcessorApplyRGBA(cpu_processor, pixel);
|
|
}
|
|
else {
|
|
float alpha, inv_alpha;
|
|
|
|
alpha = pixel[3];
|
|
inv_alpha = 1.0f / alpha;
|
|
|
|
pixel[0] *= inv_alpha;
|
|
pixel[1] *= inv_alpha;
|
|
pixel[2] *= inv_alpha;
|
|
|
|
cpuProcessorApplyRGBA(cpu_processor, pixel);
|
|
|
|
pixel[0] *= alpha;
|
|
pixel[1] *= alpha;
|
|
pixel[2] *= alpha;
|
|
}
|
|
}
|
|
|
|
void FallbackImpl::cpuProcessorRelease(OCIO_ConstCPUProcessorRcPtr *cpu_processor)
|
|
{
|
|
delete (FallbackProcessor *)(cpu_processor);
|
|
}
|
|
|
|
const char *FallbackImpl::colorSpaceGetName(OCIO_ConstColorSpaceRcPtr *cs)
|
|
{
|
|
if (cs == COLORSPACE_LINEAR) {
|
|
return "Linear";
|
|
}
|
|
if (cs == COLORSPACE_SRGB) {
|
|
return "sRGB";
|
|
}
|
|
if (cs == COLORSPACE_DATA) {
|
|
return "data";
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const char *FallbackImpl::colorSpaceGetDescription(OCIO_ConstColorSpaceRcPtr * /*cs*/)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
const char *FallbackImpl::colorSpaceGetFamily(OCIO_ConstColorSpaceRcPtr * /*cs*/)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
int FallbackImpl::colorSpaceGetNumAliases(OCIO_ConstColorSpaceRcPtr * /*cs*/)
|
|
{
|
|
return 0;
|
|
}
|
|
const char *FallbackImpl::colorSpaceGetAlias(OCIO_ConstColorSpaceRcPtr * /*cs*/,
|
|
const int /*index*/)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
OCIO_ConstProcessorRcPtr *FallbackImpl::createDisplayProcessor(OCIO_ConstConfigRcPtr * /*config*/,
|
|
const char * /*input*/,
|
|
const char * /*view*/,
|
|
const char * /*display*/,
|
|
const char * /*look*/,
|
|
const float scale,
|
|
const float exponent,
|
|
const float /*temperature*/,
|
|
const float /*tint*/,
|
|
const bool /*use_white_balance*/,
|
|
const bool inverse)
|
|
{
|
|
FallbackTransform transform;
|
|
transform.type = (inverse) ? TRANSFORM_SRGB_TO_LINEAR : TRANSFORM_LINEAR_TO_SRGB;
|
|
transform.scale = (inverse && scale != 0.0f) ? 1.0f / scale : scale;
|
|
transform.exponent = (inverse && exponent != 0.0f) ? 1.0f / exponent : exponent;
|
|
|
|
return (OCIO_ConstProcessorRcPtr *)new FallbackProcessor(transform);
|
|
}
|
|
|
|
OCIO_PackedImageDesc *FallbackImpl::createOCIO_PackedImageDesc(float *data,
|
|
long width,
|
|
long height,
|
|
long numChannels,
|
|
long chanStrideBytes,
|
|
long xStrideBytes,
|
|
long yStrideBytes)
|
|
{
|
|
OCIO_PackedImageDescription *desc = MEM_cnew<OCIO_PackedImageDescription>(
|
|
"OCIO_PackedImageDescription");
|
|
desc->data = data;
|
|
desc->width = width;
|
|
desc->height = height;
|
|
desc->numChannels = numChannels;
|
|
desc->chanStrideBytes = chanStrideBytes;
|
|
desc->xStrideBytes = xStrideBytes;
|
|
desc->yStrideBytes = yStrideBytes;
|
|
return (OCIO_PackedImageDesc *)desc;
|
|
}
|
|
|
|
void FallbackImpl::OCIO_PackedImageDescRelease(OCIO_PackedImageDesc *id)
|
|
{
|
|
MEM_freeN(reinterpret_cast<OCIO_PackedImageDescription *>(id));
|
|
}
|
|
|
|
const char *FallbackImpl::getVersionString()
|
|
{
|
|
return "fallback";
|
|
}
|
|
|
|
int FallbackImpl::getVersionHex()
|
|
{
|
|
return 0;
|
|
}
|