Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
conversion.h File Reference

A PREPARED CONVERSION: everything needed to turn pixels in one colour space into pixels in another, built once and applied many times. More...

#include <glib.h>
#include <stddef.h>
#include "colorprofiles/profile_types.h"
#include "math/matrices.h"
+ Include dependency graph for conversion.h:
+ This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  dt_colorspaces_endpoint_t
 One end of a conversion: a profile, named either by identity or by handing over one this module already built for a single image. More...
 

Macros

#define DT_CONVERSION_LUT_SAMPLES   0x10000
 Entries per tone curve. Callers that upload the curves to a device need this to size the buffer; the OpenCL paths in iop/colorin.c and iop/colorout.c upload it as a 256 x 256 float image, which is exactly this many samples.
 

Typedefs

typedef struct dt_colorspaces_conversion_t dt_colorspaces_conversion_t
 A prepared conversion. Opaque: its layout is the module's business, and the two sentinel encodings that used to drive dispatch from the outside (isnan(matrix[0][0]), lut[c][0] < 0) are no longer anyone else's to read.
 
typedef struct dt_colorspaces_endpoint_t dt_colorspaces_endpoint_t
 One end of a conversion: a profile, named either by identity or by handing over one this module already built for a single image.
 
typedef enum dt_colorspaces_conversion_flags_t dt_colorspaces_conversion_flags_t
 What the caller wants, and what the caller can consume.
 
typedef void(* dt_colorspaces_conversion_hook_t) (const float *const in, float *const out)
 A per-pixel hook run between the source curves and the colour conversion proper.
 

Enumerations

enum  dt_colorspaces_conversion_flags_t {
  DT_CONVERSION_NONE = 0 ,
  DT_CONVERSION_FORCE_LCMS2 = 1 << 0 ,
  DT_CONVERSION_SOURCE_CURVES = 1 << 1 ,
  DT_CONVERSION_TARGET_CURVES = 1 << 2 ,
  DT_CONVERSION_GAMUTCHECK = 1 << 3
}
 What the caller wants, and what the caller can consume. More...
 

Functions

dt_colorspaces_conversion_tdt_colorspaces_prepare_conversion (const dt_colorspaces_endpoint_t *const from, const dt_colorspaces_endpoint_t *const to, const dt_colorspaces_endpoint_t *const clip, const dt_colorspaces_endpoint_t *const proof, const dt_iop_color_intent_t intent, const dt_colorspaces_conversion_flags_t flags)
 Build a conversion from from to to. The expensive call; do it once.
 
void dt_colorspaces_free_conversion (dt_colorspaces_conversion_t **conversion)
 Release a conversion and NULL the caller's pointer.
 
void dt_colorspaces_apply_conversion (const dt_colorspaces_conversion_t *const conversion, const float *const in, float *const out, const size_t width, const size_t height)
 Convert a 4-channel float image through a prepared conversion.
 
void dt_colorspaces_apply_conversion_hooked (const dt_colorspaces_conversion_t *const conversion, const float *const in, float *const out, const size_t width, const size_t height, const dt_colorspaces_conversion_hook_t hook)
 dt_colorspaces_apply_conversion() with a per-pixel hook. See dt_colorspaces_conversion_hook_t for why this exists and why it should stay at one caller.
 
gboolean dt_colorspaces_conversion_is_matrix (const dt_colorspaces_conversion_t *const conversion)
 Whether the conversion reduced to matrices and curves, and can therefore be run by a device kernel at all.
 
gboolean dt_colorspaces_conversion_has_clipping (const dt_colorspaces_conversion_t *const conversion)
 Whether the conversion has a gamut-clipping stage, i.e. whether a clip endpoint was given AND survived preparation. Selects between a caller's clipping and non-clipping kernels.
 
gboolean dt_colorspaces_conversion_matrix (const dt_colorspaces_conversion_t *const conversion, dt_colormatrix_t matrix)
 The composed source-to-target matrix, row-major.
 
gboolean dt_colorspaces_conversion_source_matrix (const dt_colorspaces_conversion_t *const conversion, dt_colormatrix_t matrix)
 The SOURCE profile's own RGB -> XYZ (D50) matrix, before composition.
 
gboolean dt_colorspaces_conversion_clip_matrix (const dt_colorspaces_conversion_t *const conversion, dt_colormatrix_t matrix)
 The clip-to-target matrix, the second leg of a clipping conversion.
 
const float * dt_colorspaces_conversion_source_curve (const dt_colorspaces_conversion_t *const conversion, const int channel)
 One channel of the source decoding curves, DT_CONVERSION_LUT_SAMPLES entries.
 
const float * dt_colorspaces_conversion_target_curve (const dt_colorspaces_conversion_t *const conversion, const int channel)
 One channel of the target encoding curves. Same contract as dt_colorspaces_conversion_source_curve().
 
const float * dt_colorspaces_conversion_source_coeffs (const dt_colorspaces_conversion_t *const conversion)
 The 3x3 power-law fits extrapolating the source curves past white, as one flat array of 9 floats in channel-major order – the layout the kernels upload verbatim.
 
const float * dt_colorspaces_conversion_target_coeffs (const dt_colorspaces_conversion_t *const conversion)
 The same fits for the target curves. Same contract.
 

Detailed Description

A PREPARED CONVERSION: everything needed to turn pixels in one colour space into pixels in another, built once and applied many times.

This is the "prepare" half of colour management, and it exists so that no module outside src/colorprofiles/ ever has to do it. Preparing a conversion by hand means resolving two profiles, pinning them for exactly as long as the derivation takes, deciding whether they reduce to matrices and tone curves or need lcms2, extracting six 65536-entry curves, fitting the power laws that carry values above white, building a cmsHTRANSFORM with the right flags when they do not, and closing whichever handles turned out to be owned rather than borrowed. iop/colorin.c and iop/colorout.c each open-coded that, differently, and between them held two of the bugs this module was closed to prevent: a profile handle read without a lock across the window in which a monitor change replaces it, and a transform built from settings that were re-read, field by field, after they had been hashed.

The object below is what both of them were re-implementing:

source RGB --[source curves]--> linear --[matrix]--> linear --[target curves]--> target

with an optional clamp to [0,1] in a third space's primaries part-way through (gamut clipping), and a whole-pipeline lcms2 fallback for the profiles that do not reduce to that form. Which branch runs is decided here, from the profiles, and callers neither choose nor see it – exactly as dt_colorspaces_apply_profile() already does for the RGB<->Lab leg.

Note
Preparation is expensive (two 65536-entry curve extractions, or a 2.2-38 ms cmsCreateTransform) and application is not. Build one in commit_params(), apply it in process(), free it in cleanup_pipe(). Never build one per tile.
See also
colorprofiles/colorspaces.h for the profile list and the CRUDE metadata queries.
colorprofiles/iop_profile.h for dt_colorspaces_apply_profile(), the RGB<->Lab leg, which is the same idea against a single profile rather than a pair.

Definition in file conversion.h.

Macro Definition Documentation

◆ DT_CONVERSION_LUT_SAMPLES

#define DT_CONVERSION_LUT_SAMPLES   0x10000

Entries per tone curve. Callers that upload the curves to a device need this to size the buffer; the OpenCL paths in iop/colorin.c and iop/colorout.c upload it as a 256 x 256 float image, which is exactly this many samples.

Definition at line 66 of file conversion.h.

Typedef Documentation

◆ dt_colorspaces_conversion_flags_t

What the caller wants, and what the caller can consume.

The two *_CURVES bits are not preferences, they are declarations of what the caller's own OpenCL kernel can execute: a conversion that needs a curve stage the caller cannot run has to fall back to lcms2 rather than silently drop the curve. On the CPU both stages are always available, so a caller that only ever runs dt_colorspaces_apply_conversion() may set both.

◆ dt_colorspaces_conversion_hook_t

typedef void(* dt_colorspaces_conversion_hook_t) (const float *const in, float *const out)

A per-pixel hook run between the source curves and the colour conversion proper.

Exists for exactly one caller: iop/colorin.c's "blue mapping", a legacy per-pixel tweak that only old edits carry (nothing sets it for a new one). It is placed where that module has always placed it – after decoding, before the matrix on the matrix branch; before cmsDoTransform on the lcms2 branch, which decodes internally. Do not add callers: a function call per pixel defeats the vectorisation of the loop it sits in.

Parameters
insource pixel, 4 floats.
outdestination pixel, 4 floats. May alias in.

Definition at line 190 of file conversion.h.

◆ dt_colorspaces_conversion_t

A prepared conversion. Opaque: its layout is the module's business, and the two sentinel encodings that used to drive dispatch from the outside (isnan(matrix[0][0]), lut[c][0] < 0) are no longer anyone else's to read.

Definition at line 75 of file conversion.h.

◆ dt_colorspaces_endpoint_t

One end of a conversion: a profile, named either by identity or by handing over one this module already built for a single image.

Most endpoints are registered profiles and are named by identity – {type, filename}, the same pair every preset and conf key stores. The exception is the image-derived family (DT_COLORSPACE_EMBEDDED_ICC through DT_COLORSPACE_ALTERNATE_MATRIX, and an ICC embedded in the file being exported): those are not in the profile list and cannot be resolved by identity at all, because their matrices come from one image's own camera data. For those, resolve the image's profile first (dt_image_get_input_profile() / dt_image_get_output_profile() in imageio/imageio_profile.h) and pass the container here as ::resolved. The conversion borrows it: it must outlive the conversion, and the caller still frees it with dt_colorspaces_free_image_profile().

Enumeration Type Documentation

◆ dt_colorspaces_conversion_flags_t

What the caller wants, and what the caller can consume.

The two *_CURVES bits are not preferences, they are declarations of what the caller's own OpenCL kernel can execute: a conversion that needs a curve stage the caller cannot run has to fall back to lcms2 rather than silently drop the curve. On the CPU both stages are always available, so a caller that only ever runs dt_colorspaces_apply_conversion() may set both.

Enumerator
DT_CONVERSION_NONE 
DT_CONVERSION_FORCE_LCMS2 

Never take the matrix path, even when both profiles reduce to matrices. Backs the plugins/lighttable/export/force_lcms2 conf key.

DT_CONVERSION_SOURCE_CURVES 

The caller can apply the SOURCE profile's decoding curves before the matrix. Without this, a non-linear source profile forces the lcms2 fallback.

DT_CONVERSION_TARGET_CURVES 

The caller can apply the TARGET profile's encoding curves after the matrix. Without this, a non-linear target profile forces the lcms2 fallback.

DT_CONVERSION_GAMUTCHECK 

Mark out-of-gamut pixels rather than merely proofing them. Requires a soft-proof endpoint, and forces the lcms2 fallback because there is no matrix form of it.

Definition at line 116 of file conversion.h.

Function Documentation

◆ dt_colorspaces_apply_conversion()

void dt_colorspaces_apply_conversion ( const dt_colorspaces_conversion_t *const  conversion,
const float *const  in,
float *const  out,
const size_t  width,
const size_t  height 
)

Convert a 4-channel float image through a prepared conversion.

THE apply entry point. Runs whichever branch dt_colorspaces_prepare_conversion() settled on, over the whole buffer, parallelised. The 4th channel is not colour data and is not preserved – the matrix zeroes it and lcms2 leaves it undefined – which is why callers that carry a mask in it copy it back afterwards (dt_iop_alpha_copy()).

Parameters
conversionprepared conversion. NULL is a no-op, leaving out untouched.
insource, 4 floats per pixel, 16-byte aligned.
outdestination, same layout. Must NOT alias in: the matrix branch uses non-temporal stores, and the clipping and curve stages read a pixel after writing earlier ones.
widthpixels per row.
heightrows.

Definition at line 616 of file conversion.c.

References dt_colorspaces_apply_conversion_hooked(), height, L, out, and width.

Referenced by process().

◆ dt_colorspaces_apply_conversion_hooked()

void dt_colorspaces_apply_conversion_hooked ( const dt_colorspaces_conversion_t *const  conversion,
const float *const  in,
float *const  out,
const size_t  width,
const size_t  height,
const dt_colorspaces_conversion_hook_t  hook 
)

dt_colorspaces_apply_conversion() with a per-pixel hook. See dt_colorspaces_conversion_hook_t for why this exists and why it should stay at one caller.

Parameters
hookapplied to every pixel between decoding and conversion. NULL is exactly dt_colorspaces_apply_conversion().

Definition at line 604 of file conversion.c.

References _apply_lcms2(), _apply_matrix(), height, dt_colorspaces_conversion_t::is_matrix, IS_NULL_PTR, L, out, and width.

Referenced by dt_colorspaces_apply_conversion(), and process().

◆ dt_colorspaces_conversion_clip_matrix()

gboolean dt_colorspaces_conversion_clip_matrix ( const dt_colorspaces_conversion_t *const  conversion,
dt_colormatrix_t  matrix 
)

The clip-to-target matrix, the second leg of a clipping conversion.

Parameters
matrixfilled with the matrix. Untouched, and FALSE returned, when the conversion has no clipping stage.
Returns
TRUE when matrix was written.

Definition at line 650 of file conversion.c.

References dt_colorspaces_conversion_t::clip_matrix, FALSE, dt_colorspaces_conversion_t::has_clipping, dt_colorspaces_conversion_t::is_matrix, IS_NULL_PTR, L, matrix, and TRUE.

Referenced by process_cl().

◆ dt_colorspaces_conversion_has_clipping()

gboolean dt_colorspaces_conversion_has_clipping ( const dt_colorspaces_conversion_t *const  conversion)

Whether the conversion has a gamut-clipping stage, i.e. whether a clip endpoint was given AND survived preparation. Selects between a caller's clipping and non-clipping kernels.

Definition at line 629 of file conversion.c.

References dt_colorspaces_conversion_t::has_clipping, and IS_NULL_PTR.

Referenced by process_cl().

◆ dt_colorspaces_conversion_is_matrix()

gboolean dt_colorspaces_conversion_is_matrix ( const dt_colorspaces_conversion_t *const  conversion)

Whether the conversion reduced to matrices and curves, and can therefore be run by a device kernel at all.

Returns
TRUE for the matrix branch, FALSE for the lcms2 fallback (which is host-only, so the caller must clear piece->process_cl_ready). FALSE for a NULL conversion.

Definition at line 624 of file conversion.c.

References dt_colorspaces_conversion_t::is_matrix, and IS_NULL_PTR.

Referenced by commit_params().

◆ dt_colorspaces_conversion_matrix()

gboolean dt_colorspaces_conversion_matrix ( const dt_colorspaces_conversion_t *const  conversion,
dt_colormatrix_t  matrix 
)

The composed source-to-target matrix, row-major.

With a clipping stage this is the source-to-CLIP matrix, and dt_colorspaces_conversion_clip_matrix() is the second leg – which is the argument pair the colorin_clipping kernel already takes.

Parameters
matrixfilled with the matrix. Left untouched, and FALSE returned, on the lcms2 branch or for a NULL conversion.
Returns
TRUE when matrix was written.

Definition at line 634 of file conversion.c.

References FALSE, dt_colorspaces_conversion_t::is_matrix, IS_NULL_PTR, L, dt_colorspaces_conversion_t::matrix, matrix, and TRUE.

Referenced by process_cl().

◆ dt_colorspaces_conversion_source_coeffs()

const float * dt_colorspaces_conversion_source_coeffs ( const dt_colorspaces_conversion_t *const  conversion)

The 3x3 power-law fits extrapolating the source curves past white, as one flat array of 9 floats in channel-major order – the layout the kernels upload verbatim.

Returns
The coefficients, or NULL when the conversion has no source curve stage.
See also
dt_ioppr_eval_trc(), which is what evaluates them.

Definition at line 672 of file conversion.c.

References dt_colorspaces_conversion_t::coeffs_source, IS_NULL_PTR, L, and dt_colorspaces_conversion_t::lut_source.

Referenced by process_cl().

◆ dt_colorspaces_conversion_source_curve()

const float * dt_colorspaces_conversion_source_curve ( const dt_colorspaces_conversion_t *const  conversion,
const int  channel 
)

One channel of the source decoding curves, DT_CONVERSION_LUT_SAMPLES entries.

Parameters
channel0, 1 or 2.
Returns
The curve, or NULL on the lcms2 branch or when DT_CONVERSION_SOURCE_CURVES was not asked for. Present-but-linear is NOT reported as NULL: a curve whose first entry is negative marks that channel linear, which is the convention both the CPU path and the kernels read, so a caller that declared it consumes this side always gets a buffer it can upload. Valid for the life of the conversion.

Definition at line 658 of file conversion.c.

References IS_NULL_PTR, L, and dt_colorspaces_conversion_t::lut_source.

Referenced by process_cl().

◆ dt_colorspaces_conversion_source_matrix()

gboolean dt_colorspaces_conversion_source_matrix ( const dt_colorspaces_conversion_t *const  conversion,
dt_colormatrix_t  matrix 
)

The SOURCE profile's own RGB -> XYZ (D50) matrix, before composition.

Not for converting anything – for describing the source space to something else. iop/colorin.c hands it to the pipe as part of the input-profile record, which downstream modules read to know what the buffer they receive is in.

Parameters
matrixfilled with the matrix. Untouched, and FALSE returned, when the source profile does not reduce to a colorant matrix (a CLUT profile, say), which is the same answer as "there is no such matrix to report".
Returns
TRUE when matrix was written. Available on both branches: a conversion that runs through lcms2 can still have a perfectly good source matrix, and the reason it fell back may have been the target profile.

Definition at line 642 of file conversion.c.

References FALSE, dt_colorspaces_conversion_t::have_source_matrix, IS_NULL_PTR, L, matrix, dt_colorspaces_conversion_t::source_matrix, and TRUE.

Referenced by commit_params().

◆ dt_colorspaces_conversion_target_coeffs()

const float * dt_colorspaces_conversion_target_coeffs ( const dt_colorspaces_conversion_t *const  conversion)

The same fits for the target curves. Same contract.

Definition at line 678 of file conversion.c.

References dt_colorspaces_conversion_t::coeffs_target, IS_NULL_PTR, L, and dt_colorspaces_conversion_t::lut_target.

Referenced by process_cl().

◆ dt_colorspaces_conversion_target_curve()

const float * dt_colorspaces_conversion_target_curve ( const dt_colorspaces_conversion_t *const  conversion,
const int  channel 
)

One channel of the target encoding curves. Same contract as dt_colorspaces_conversion_source_curve().

Definition at line 665 of file conversion.c.

References IS_NULL_PTR, L, and dt_colorspaces_conversion_t::lut_target.

Referenced by process_cl().

◆ dt_colorspaces_free_conversion()

void dt_colorspaces_free_conversion ( dt_colorspaces_conversion_t **  conversion)

Release a conversion and NULL the caller's pointer.

Parameters
conversionaddress OF the caller's pointer. A NULL address, or an address holding NULL, is a no-op. Closes whichever profile handles the conversion owns (never the borrowed ones) and deletes its transform.

Definition at line 377 of file conversion.c.

References _free_curves(), dt_colorspaces_cleanup_profile(), IS_NULL_PTR, k, and L.

Referenced by cleanup_pipe(), commit_params(), and dt_colorspaces_prepare_conversion().

◆ dt_colorspaces_prepare_conversion()

Build a conversion from from to to. The expensive call; do it once.

Resolves both endpoints (holding each profile's own lock across the derivation, because the display profile's handle is replaced on a monitor change), then decides the branch: if neither soft-proofing nor DT_CONVERSION_FORCE_LCMS2 is asked for, and both profiles reduce to a colorant matrix, and every curve stage the profiles need is one the caller declared it can run, the result is a composed matrix plus at most two curve sets. Otherwise it is a cmsHTRANSFORM. Either way dt_colorspaces_apply_conversion() runs it.

Parameters
fromsource space. Must not be NULL.
totarget space. Must not be NULL.
clipoptional third space whose primaries bound the result: the conversion becomes source -> clip, clamp each channel to [0,1], clip -> target. Only the primaries are used, never the tone curves – this is a gamut clamp, not a round trip. NULL for the ordinary direct conversion.
proofoptional soft-proof space. Non-NULL builds a proofing transform with black point compensation, which always means the lcms2 branch. The profile is quantised first (several built-ins carry a parametric TRC that lcms2 would round-trip exactly, making the proof a no-op); if that quantisation fails, proofing is silently dropped and an ordinary transform is built, which is the pre-existing behaviour.
intentrendering intent for the lcms2 branch. The matrix branch has no intent.
flagssee dt_colorspaces_conversion_flags_t.
Returns
A new conversion, or NULL if neither branch could be built (both endpoints unresolvable, or cmsCreateTransform refused the pair). Release it with dt_colorspaces_free_conversion().
Note
Never call this from process(). It costs two 65536-entry curve extractions on the matrix branch and 2.2-38 ms on the lcms2 one.

Definition at line 173 of file conversion.c.

References _allocate_curves(), _format_for(), _free_curves(), _quantise_profile(), _resolve_endpoint(), dt_colorspaces_conversion_t::clip_matrix, dt_colorspaces_conversion_t::clip_xform, dt_colorspaces_conversion_t::coeffs_source, dt_colorspaces_conversion_t::coeffs_target, dt_colormatrix_mul(), dt_colorspaces_free_conversion(), dt_colorspaces_get_matrix_from_input_profile(), dt_colorspaces_get_matrix_from_output_profile(), dt_colorspaces_lock_profile(), dt_colorspaces_unlock_profile(), DT_CONVERSION_FORCE_LCMS2, DT_CONVERSION_GAMUTCHECK, DT_CONVERSION_LUT_SAMPLES, DT_CONVERSION_SOURCE_CURVES, DT_CONVERSION_TARGET_CURVES, dt_ioppr_init_unbounded_coeffs(), FALSE, flags, dt_colorspaces_conversion_t::from_type, dt_colorspaces_conversion_t::gamutcheck, dt_colorspaces_conversion_t::has_clipping, dt_colorspaces_conversion_t::have_source_matrix, dt_colorspaces_conversion_t::is_matrix, IS_NULL_PTR, L, dt_colorspaces_conversion_t::lut_source, dt_colorspaces_conversion_t::lut_target, dt_colorspaces_conversion_t::matrix, dt_colorspaces_conversion_t::n_owned, dt_colorspaces_conversion_t::nonlinear_source, dt_colorspaces_conversion_t::nonlinear_target, dt_colorspaces_conversion_t::owned, dt_colorspaces_conversion_t::source_matrix, dt_colorspaces_conversion_t::to_type, TRUE, and dt_colorspaces_conversion_t::xform.

Referenced by commit_params().