![]() |
Ansel 0.0
A darktable fork - bloat + design vision
|
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_t * | dt_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. | |
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.
cmsCreateTransform) and application is not. Build one in commit_params(), apply it in process(), free it in cleanup_pipe(). Never build one per tile.Definition in file conversion.h.
| #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.
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.
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.
| in | source pixel, 4 floats. |
| out | destination pixel, 4 floats. May alias in. |
Definition at line 190 of file conversion.h.
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.
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().
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.
Definition at line 116 of file conversion.h.
| 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()).
| conversion | prepared conversion. NULL is a no-op, leaving out untouched. |
| in | source, 4 floats per pixel, 16-byte aligned. |
| out | destination, 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. |
| width | pixels per row. |
| height | rows. |
Definition at line 616 of file conversion.c.
References dt_colorspaces_apply_conversion_hooked(), height, L, out, and width.
Referenced by process().
| 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.
| hook | applied 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().
| 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.
| matrix | filled with the matrix. Untouched, and FALSE returned, when the conversion has no clipping stage. |
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().
| 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().
| 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.
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().
| 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.
| matrix | filled with the matrix. Left untouched, and FALSE returned, on the lcms2 branch or for a NULL conversion. |
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().
| 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.
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().
| 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.
| channel | 0, 1 or 2. |
Definition at line 658 of file conversion.c.
References IS_NULL_PTR, L, and dt_colorspaces_conversion_t::lut_source.
Referenced by process_cl().
| 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.
| matrix | filled 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". |
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().
| 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().
| 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().
| void dt_colorspaces_free_conversion | ( | dt_colorspaces_conversion_t ** | conversion | ) |
Release a conversion and NULL the caller's pointer.
| conversion | address 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_conversion_t * dt_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.
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.
| from | source space. Must not be NULL. |
| to | target space. Must not be NULL. |
| clip | optional 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. |
| proof | optional 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. |
| intent | rendering intent for the lcms2 branch. The matrix branch has no intent. |
| flags | see dt_colorspaces_conversion_flags_t. |
cmsCreateTransform refused the pair). Release it with dt_colorspaces_free_conversion(). 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().