/*
 * Copyright 2021 Collabora, Ltd.
 * Copyright 2021 Advanced Micro Devices, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifndef WESTON_COLOR_H
#define WESTON_COLOR_H

#include <stdbool.h>
#include <stdint.h>
#include <libweston/libweston.h>

#include "backend-drm/drm-kms-enums.h"

enum weston_hdr_metadata_type1_groups {
	/** weston_hdr_metadata_type1::primary is set */
	WESTON_HDR_METADATA_TYPE1_GROUP_PRIMARIES	= 0x01,

	/** weston_hdr_metadata_type1::white is set */
	WESTON_HDR_METADATA_TYPE1_GROUP_WHITE		= 0x02,

	/** weston_hdr_metadata_type1::maxDML is set */
	WESTON_HDR_METADATA_TYPE1_GROUP_MAXDML		= 0x04,

	/** weston_hdr_metadata_type1::minDML is set */
	WESTON_HDR_METADATA_TYPE1_GROUP_MINDML		= 0x08,

	/** weston_hdr_metadata_type1::maxCLL is set */
	WESTON_HDR_METADATA_TYPE1_GROUP_MAXCLL		= 0x10,

	/** weston_hdr_metadata_type1::maxFALL is set */
	WESTON_HDR_METADATA_TYPE1_GROUP_MAXFALL		= 0x20,

	/** all valid bits */
	WESTON_HDR_METADATA_TYPE1_GROUP_ALL_MASK	= 0x3f
};

/** HDR static metadata type 1
 *
 * The fields are defined by CTA-861-G except here they use float encoding.
 *
 * In Weston used only with HDR display modes.
 */
struct weston_hdr_metadata_type1 {
	/** Which fields are valid
	 *
	 * A bitmask of values from enum weston_hdr_metadata_type1_groups.
	 */
	uint32_t group_mask;

	/* EOTF is tracked externally with enum weston_eotf_mode */

	/** Chromaticities of the primaries, in any order */
	struct weston_CIExy primary[3];

	/** White point chromaticity */
	struct weston_CIExy white;

	/** Maximum display mastering luminance, 1 - 65535 cd/m² */
	float maxDML;

	/** Minimum display mastering luminance, 0.0001 - 6.5535 cd/m² */
	float minDML;

	/** Maximum content light level, 1 - 65535 cd/m² */
	float maxCLL;

	/** Maximum frame-average light level, 1 - 65535 cd/m² */
	float maxFALL;
};

/** Output properties derived from its color profile
 *
 * These are constructed by a color manager.
 *
 * A weston_output_color_outcome owns (a reference to) everything it contains.
 *
 * \ingroup output
 * \internal
 */
struct weston_output_color_outcome {
	/** sRGB to output color space transformation */
	struct weston_color_transform *from_sRGB_to_output;

	/** sRGB to blending color space transformation */
	struct weston_color_transform *from_sRGB_to_blend;

	/** Blending to output color space transformation */
	struct weston_color_transform *from_blend_to_output;

	/** HDR Static Metadata Type 1 for WESTON_EOTF_MODE_ST2084 */
	struct weston_hdr_metadata_type1 hdr_meta;
};

/**
 * Represents a color profile description (an ICC color profile)
 *
 * Sub-classed by the color manager that created this.
 */
struct weston_color_profile {
	struct weston_color_manager *cm;
	int ref_count;
	char *description;

	/* Unique id to be used by the CM&HDR protocol extension. Should not
	 * be confused with the protocol object id. */
	uint32_t id;
};

/**
 * Color transfer function
 *
 * Includes any parameter values the enumerated transfer function might need.
 */
struct weston_color_tf {
	/** Encoding transfer characteristic by enumeration; always set. */
	const struct weston_color_tf_info *info;

	/** TF parameters, specific to TF. */
	float params[1];

	char padding[4];
};

/** Parameters that define a parametric color profile */
struct weston_color_profile_params {
	/* Primary color volume; always set. */
	struct weston_color_gamut primaries;

	/* Primary color volume by enumeration; optional, may be NULL. */
	const struct weston_color_primaries_info *primaries_info;

	/* Encoding transfer characteristic by enumeration; always set. */
	struct weston_color_tf tf;

	/* Primary color volume luminance parameters cd/m²; always set. */
	float min_luminance, max_luminance;
	float reference_white_luminance;

	/* Target color volume; always set. */
	struct weston_color_gamut target_primaries;

	/* Target luminance parameters cd/m²; negative when not set */
	float target_min_luminance, target_max_luminance;
	float maxCLL, maxFALL;

	char padding[4];
};

/** Type for parametric curves */
enum weston_color_curve_parametric_type {
	/** Transfer function named LINPOW
	 *
	 * y = (a * x + b) ^ g | x >= d
	 * y = c * x           | 0 <= x < d
	 *
	 * We gave it the name LINPOW because the first operation with the input
	 * x is a linear one, and then the result is raised to g.
	 *
	 * As all parametric curves, this one should be represented using struct
	 * weston_color_curve_parametric. For each color channel RGB we may have
	 * different params, see weston_color_curve_parametric::params.
	 *
	 * For LINPOW, the params g, a, b, c, and d are respectively
	 * params[channel][0], ... , params[channel][4].
	 *
	 * The input for all color channels may be clamped to [0.0, 1.0]. In
	 * such case, weston_color_curve_parametric::clamped_input is true.
	 * If the input is not clamped and LINPOW needs to evaluate a negative
	 * input value, it uses mirroring (i.e. -f(-x)).
	 */
	WESTON_COLOR_CURVE_PARAMETRIC_TYPE_LINPOW,

	/** Transfer function named POWLIN
	 *
	 * y = (a * (x ^ g)) + b | x >= d
	 * y = c * x             | 0 <= x < d
	 *
	 * We gave it the name POWLIN because the first operation with the input
	 * x is an exponential one, and then the result is multiplied by a.
	 *
	 * As all parametric curves, this one should be represented using struct
	 * weston_color_curve_parametric. For each color channel RGB we may have
	 * different params, see weston_color_curve_parametric::params.
	 *
	 * For POWLIN, the params g, a, b, c, and d are respectively
	 * params[channel][0], ... , params[channel][4].
	 *
	 * The input for all color channels may be clamped to [0.0, 1.0]. In
	 * such case, weston_color_curve_parametric::clamped_input is true.
	 * If the input is not clamped and POWLIN needs to evaluate a negative
	 * input value, it uses mirroring (i.e. -f(-x)).
	 */
	WESTON_COLOR_CURVE_PARAMETRIC_TYPE_POWLIN,
};

/** Type or formula for a curve */
enum weston_color_curve_type {
	/** Identity function, no-op */
	WESTON_COLOR_CURVE_TYPE_IDENTITY = 0,

	/** Three-channel, one-dimensional look-up table */
	WESTON_COLOR_CURVE_TYPE_LUT_3x1D,

	/** Enumerated color curve */
	WESTON_COLOR_CURVE_TYPE_ENUM,

	/** Parametric color curve */
	WESTON_COLOR_CURVE_TYPE_PARAMETRIC,
};

/** LUT_3x1D parameters */
struct weston_color_curve_lut_3x1d {
	/**
	 * Approximate a color curve with three 1D LUTs
	 *
	 * A 1D LUT is a mapping from [0.0, 1.0] to arbitrary values. The first
	 * element in the LUT corresponds to input value 0.0, and the last
	 * element corresponds to input value 1.0. The step from one element
	 * to the next in input space is 1.0 / (len - 1). When input value is
	 * between two elements, linear interpolation should be used.
	 *
	 * This function fills in the given array with the LUT values.
	 *
	 * \param xform This color transformation object.
	 * \param len The number of elements in each 1D LUT.
	 * \param values The destination array of length \c len for
	 * receiving the three 1D LUTs.
	 */
	void
	(*fill_in)(struct weston_color_transform *xform,
		   struct weston_vec3f *values, unsigned len);

	/** Optimal 1D LUT length for storage vs. precision */
	unsigned optimal_len;
};

/** Direct or inverse of a tf. */
enum weston_tf_direction {
	WESTON_FORWARD_TF,
	WESTON_INVERSE_TF,
};

/** Enumerated color curve */
struct weston_color_curve_enum {
	struct weston_color_tf tf;

	/* Determines if the direct or inverse of the tf should be used. */
	enum weston_tf_direction tf_direction;
};

/**
 * The only params curve we have are WESTON_COLOR_CURVE_PARAMETRIC_TYPE_LINPOW
 * and WESTON_COLOR_CURVE_PARAMETRIC_TYPE_POWLIN, and they have exactly 5
 * params.
 */
union weston_color_curve_parametric_chan_data {
	struct {
		float g, a, b, c, d;
	};
	float data[5];
};

union weston_color_curve_parametric_data {
	/** Channels in RGB order. */
	union weston_color_curve_parametric_chan_data chan[3];
	float array[15];
};

static_assert(sizeof (union weston_color_curve_parametric_data){}.array ==
	      sizeof (union weston_color_curve_parametric_data){}.chan,
	      "union size error");

/** Parametric color curve parameters */
struct weston_color_curve_parametric {
	enum weston_color_curve_parametric_type type;

	/* For each color channel we may have curves with different params. The
	 * channels are in RGB order. */
	union weston_color_curve_parametric_data params;

	/* The input of the curve should be clamped from 0.0 to 1.0? */
	bool clamped_input;
};

/**
 * A scalar function for color encoding and decoding
 *
 * This object can represent a one-dimensional function that is applied
 * independently to each of the color channels. Depending on the type and
 * parameterization of the curve, all color channels may use the
 * same function or each may have separate parameters.
 *
 * This is usually used for EOTF or EOTF^-1 and to optimize a 3D LUT size
 * without sacrificing precision, both in one step.
 */
struct weston_color_curve {
	/** Which member of 'u' defines the curve. */
	enum weston_color_curve_type type;

	/** Parameters for the curve. */
	union {
		/* identity: no parameters */
		struct weston_color_curve_lut_3x1d lut_3x1d;
		struct weston_color_curve_enum enumerated;
		struct weston_color_curve_parametric parametric;
	} u;
};

/** Type or formula for a color mapping */
enum weston_color_mapping_type {
	/** Identity function, no-op */
	WESTON_COLOR_MAPPING_TYPE_IDENTITY = 0,

	/** matrix */
	WESTON_COLOR_MAPPING_TYPE_MATRIX,
};

/**
 * A 3x3 matrix and data is arranged as column major
 */
struct weston_color_mapping_matrix {
	struct weston_mat3f matrix;
	struct weston_vec3f offset;
};

/**
 * Color mapping function
 *
 * This object can represent a 3D LUT to do a color space conversion
 *
 */
struct weston_color_mapping {
	/** Which member of 'u' defines the color mapping type */
	enum weston_color_mapping_type type;

	/** Parameters for the color mapping function */
	union {
		/* identity: no parameters */
		struct weston_color_mapping_matrix mat;
	} u;
};

/**
 * Describes a color transformation formula
 *
 * Guaranteed unique, de-duplicated.
 *
 * Sub-classed by the color manager that created this.
 *
 * For a renderer to support WESTON_CAP_COLOR_OPS it must implement everything
 * that this structure can represent.
 */
struct weston_color_transform {
	struct weston_color_manager *cm;
	int ref_count;
	uint32_t id; /* For debug */

	/* for renderer or backend to attach their own cached objects */
	struct wl_signal destroy_signal;

	/**
	 * When this is true, users are allowed to use the steps described below
	 * (pre curve, color mapping and post curve) and implement the color
	 * transformation themselves. Otherwise this is forbidden and to_clut()
	 * must be used.
	 */
	bool steps_valid;

	/* Color transform is the series of steps: */

	/** Step 1: color model change */
	/* YCbCr→RGB conversion, but that is done elsewhere */

	/** Step 2: color curve before color mapping */
	struct weston_color_curve pre_curve;

	/** Step 3: color mapping */
	struct weston_color_mapping mapping;

	/** Step 4: color curve after color mapping */
	struct weston_color_curve post_curve;

	/**
	 * Decompose the color transformation into a shaper (3x1D LUT) followed
	 * by a 3D cLUT.
	 *
	 * \param xform_base The color transformation to decompose.
	 * \param len_shaper Number of taps in each of the 1D LUT.
	 * \param shaper Where the shaper is saved, caller's responsibility to
	 * allocate.
	 * \param len_clut The 3D cLUT's length for each dimension.
	 * \param clut Where the 3D cLUT is saved, caller's responsibility to
	 * allocate. Its layout on memory is: clut[B][G][R], i.e. R is the
	 * innermost and its index grow faster, followed by G and then B.
	 * \return True on success, false otherwise.
	 */
	bool
	(*to_clut)(struct weston_color_transform *xform_base,
		   uint32_t len_shaper, float *shaper,
		   uint32_t len_clut, float *clut);
};

struct weston_cvd_correction {
	enum weston_cvd_correction_type type;
	struct weston_mat3f correction;
};

struct weston_output_color_effect {
	struct weston_compositor *compositor;
	struct wl_signal destroy_signal;

	/** Which member of 'u' defines the effect. */
	enum weston_output_color_effect_type type;

	union {
		/* color inversion: no parameters */
		/* color grayscale: no parameters */
		struct weston_cvd_correction cvd;
	} u;
};

/**
 * How content color needs to be transformed
 *
 * This object is specific to the color properties of the weston_surface and
 * weston_output it was created for. It is automatically destroyed if any
 * relevant color properties change.
 *
 * Fundamentally this contains the color transformation from content color
 * space to an output's blending color space. This is stored in field
 * 'transform' with NULL value corresponding to identity transformation.
 *
 * For graphics pipeline optimization purposes, the field 'identity_pipeline'
 * indicates whether the combination of 'transform' here and the output's
 * blending color space to monitor color space transformation total to
 * identity transformation. This helps detecting cases where renderer bypass
 * (direct scanout) is possible.
 */
struct weston_surface_color_transform {
	/** Transformation from source to blending space */
	struct weston_color_transform *transform;

	/** True, if source colorspace is identical to monitor color space */
	bool identity_pipeline;
};

struct cm_image_desc_info;

struct weston_color_manager {
	/** Identifies this CMS component */
	const char *name;

	/** This compositor instance */
	struct weston_compositor *compositor;

	/** Supports the Wayland CM&HDR protocol extension? */
	bool supports_client_protocol;

	/**
	 * Supported color features from Wayland CM&HDR protocol extension.
	 *
	 * If v (v being enum weston_color_feature v) is a supported color
	 * feature, the bit v of this will be set to 1.
	 */
	uint32_t supported_color_features;

	/**
	 * Supported rendering intents from Wayland CM&HDR protocol extension.
	 *
	 * If v (v being enum weston_render_intent v) is a supported rendering
	 * intent, the bit v of this will be set to 1.
	 */
	uint32_t supported_rendering_intents;

	/**
	 * Supported primaries named from Wayland CM&HDR protocol extension.
	 *
	 * If v (v being enum weston_color_primaries v) is a supported
	 * primaries named, the bit v of this will be set to 1.
	 */
	uint32_t supported_primaries_named;

	/**
	 * Supported tf named from Wayland CM&HDR protocol extension.
	 *
	 * If v (v being enum weston_transfer_function v) is a supported
	 * tf named, the bit v of this will be set to 1.
	 */
	uint32_t supported_tf_named;

	/** Initialize color manager */
	bool
	(*init)(struct weston_color_manager *cm);

	/** Destroy color manager */
	void
	(*destroy)(struct weston_color_manager *cm);

	/** Destroy a color profile after refcount fell to zero */
	void
	(*destroy_color_profile)(struct weston_color_profile *cprof);

	/** Print detailed description of the color profile */
	char *
	(*print_color_profile_details)(const struct weston_color_profile *cprof);

	/** Gets a new reference to the stock sRGB color profile
	 *
	 * \param cm The color manager.
	 * \return A new reference to the stock sRGB profile, never returns NULL.
	 */
	struct weston_color_profile *
	(*ref_stock_sRGB_color_profile)(struct weston_color_manager *cm);

	/** Create a color profile from ICC data
	 *
	 * \param cm The color manager.
	 * \param icc_data Pointer to the ICC binary data.
	 * \param icc_len Length of the ICC data in bytes.
	 * \param name_part A string to be used in describing the profile.
	 * \param cprof_out On success, the created object is returned here.
	 * On failure, untouched.
	 * \param errmsg On success, untouched. On failure, a pointer to a
	 * string describing the error is stored here. The string must be
	 * free()'d.
	 * \return True on success, false on failure.
	 *
	 * This may return a new reference to an existing color profile if
	 * that profile is identical to the one that would be created, apart
	 * from name_part.
	 */
	bool
	(*get_color_profile_from_icc)(struct weston_color_manager *cm,
				      const void *icc_data,
				      size_t icc_len,
				      const char *name_part,
				      struct weston_color_profile **cprof_out,
				      char **errmsg);

	/** Create a color profile from parameters
	 *
	 * \param cm The color manager.
	 * \param params The struct weston_color_profile_params with the params.
	 * \param name_part A string to be used in describing the profile.
	 * \param cprof_out On success, the created object is returned here.
	 * On failure, untouched.
	 * \param errmsg On success, untouched. On failure, a pointer to a
	 * string describing the error is stored here. The string must be
	 * free()'d.
	 * \return True on success, false on failure.
	 *
	 * This may return a new reference to an existing color profile if
	 * that profile is identical to the one that would be created, apart
	 * from name_part.
	 */
	bool
	(*get_color_profile_from_params)(struct weston_color_manager *cm,
					 const struct weston_color_profile_params *params,
					 const char *name_part,
					 struct weston_color_profile **cprof_out,
					 char **errmsg);

	/** Send image description to clients.
	 *
	 * \param cm_image_desc_info The image description info object
	 * \param cprof_base The color profile that backs the image description
	 * \return True on success, false on failure
	 *
	 * This should be used only by the CM&HDR protocol extension
	 * implementation.
	 *
	 * The color manager implementing this function should use the helpers
	 * from color-management.c (weston_cm_send_primaries(), etc) to send the
	 * information to clients.
	 */
	bool
	(*send_image_desc_info)(struct cm_image_desc_info *cm_image_desc_info,
				struct weston_color_profile *cprof_base);

	/** Given a color profile, returns a reference to a profile that is
	 * guaranteed to be parametric and which is equivalent to the given
	 * profile.
	 *
	 * \param cprof_base The color profile.
	 * \param errmsg On success, untouched. On failure, a pointer to a
	 * string describing the error is stored here. The string must be
	 * free()'d.
	 * \return A reference to an equivalent parametric color profile, or
	 * NULL on failure.
	 */
	struct weston_color_profile *
	(*get_parametric_color_profile)(struct weston_color_profile *cprof_base,
					char **errmsg);

	/** Destroy a color transform after refcount fell to zero */
	void
	(*destroy_color_transform)(struct weston_color_transform *xform);

	/** Get surface to output's blending space transformation
	 *
	 * \param cm The color manager.
	 * \param surface The surface for the source color space.
	 * \param output The output for the destination blending color space.
	 * \param surf_xform For storing the color transformation and
	 * additional information.
	 *
	 * The callee is responsible for increasing the reference count on the
	 * weston_color_transform it stores into surf_xform.
	 */
	bool
	(*get_surface_color_transform)(struct weston_color_manager *cm,
				       struct weston_surface *surface,
				       struct weston_output *output,
				       struct weston_surface_color_transform *surf_xform);

	/** Compute derived color properties for an output
	 *
	 * \param cm The color manager.
	 * \param output The output.
	 * \return A new color_outcome object on success, NULL on failure.
	 *
	 * The callee (color manager) must inspect the weston_output (color
	 * profile, EOTF mode, etc.) and create a fully populated
	 * weston_output_color_outcome object.
	 */
	struct weston_output_color_outcome *
	(*create_output_color_outcome)(struct weston_color_manager *cm,
				       struct weston_output *output);
};

void
weston_color_profile_init(struct weston_color_profile *cprof,
			  struct weston_color_manager *cm);

char *
weston_color_profile_params_to_str(const struct weston_color_profile_params *params,
				   const char *ident);

bool
weston_color_curve_enum_get_parametric(struct weston_compositor *compositor,
				       const struct weston_color_curve_enum *curve,
				       struct weston_color_curve_parametric *out);

enum weston_color_curve_step {
	WESTON_COLOR_CURVE_STEP_PRE,
	WESTON_COLOR_CURVE_STEP_POST,
};

enum weston_color_precision {
	WESTON_COLOR_PRECISION_CARELESS,
	WESTON_COLOR_PRECISION_CAREFUL,
};

struct weston_vec3f *
weston_color_curve_to_3x1D_LUT(struct weston_compositor *compositor,
			       struct weston_color_transform *xform,
			       enum weston_color_curve_step step,
			       enum weston_color_precision precision_mode,
			       size_t lut_size, char **err_msg);

void
find_neighbors(struct weston_compositor *compositor, uint32_t len, const float *array,
	       float val, uint32_t *neigh_A_index, uint32_t *neigh_B_index);

float
weston_inverse_evaluate_lut1d(struct weston_compositor *compositor,
			      uint32_t len_lut, const float *lut, float input);

struct weston_color_transform *
weston_color_transform_ref(struct weston_color_transform *xform);

void
weston_color_transform_unref(struct weston_color_transform *xform);

void
weston_color_transform_init(struct weston_color_transform *xform,
			    struct weston_color_manager *cm);

char *
weston_color_transform_string(const struct weston_color_transform *xform);

char *
weston_color_transform_details_string(int indent,
				      const struct weston_color_transform *xform);

void
weston_surface_color_transform_copy(struct weston_surface_color_transform *dst,
				    const struct weston_surface_color_transform *src);

void
weston_surface_color_transform_fini(struct weston_surface_color_transform *surf_xform);

struct weston_paint_node;

void
weston_paint_node_ensure_color_transform(struct weston_paint_node *pnode);

struct weston_color_manager *
weston_color_manager_noop_create(struct weston_compositor *compositor);

/* DSO module entrypoint */
struct weston_color_manager *
weston_color_manager_create(struct weston_compositor *compositor);

char *
weston_eotf_mask_to_str(uint32_t eotf_mask);

struct weston_colorimetry_mode_info {
	/** Primary key: the colorimetry mode */
	enum weston_colorimetry_mode mode;

	/** Its name as a string for logging. */
	const char *name;

	/** wdrm equivalent, or WDRM_COLORSPACE__COUNT if none */
	enum wdrm_colorspace wdrm;
};

const struct weston_colorimetry_mode_info *
weston_colorimetry_mode_info_get(enum weston_colorimetry_mode c);

const struct weston_colorimetry_mode_info *
weston_colorimetry_mode_info_get_by_wdrm(enum wdrm_colorspace cs);

char *
weston_colorimetry_mask_to_str(uint32_t colorimetry_mask);

void
weston_output_color_outcome_destroy(struct weston_output_color_outcome **pco);

const char *
weston_color_format_to_str(enum weston_color_format c);

char *
weston_color_format_mask_to_str(uint32_t color_format_mask);

struct weston_vec3f
weston_CIExy_to_XYZ(struct weston_CIExy c);

#endif /* WESTON_COLOR_H */
