From aac416b4b5acfa0a35804eae7b93775250a9f0c3 Mon Sep 17 00:00:00 2001
From: Mattias Andrée <maandree@kth.se>
Date: Sat, 10 Dec 2016 23:56:44 +0100
Subject: misc
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Mattias Andrée <maandree@kth.se>
---
 src/libcolour.c | 808 ++++++++++++++++++++++++++++++++++++++++++--------------
 src/libcolour.h | 156 ++++++++---
 2 files changed, 721 insertions(+), 243 deletions(-)

(limited to 'src')

diff --git a/src/libcolour.c b/src/libcolour.c
index e613280..32ef6c8 100644
--- a/src/libcolour.c
+++ b/src/libcolour.c
@@ -18,6 +18,8 @@
 
 #include <errno.h>
 #include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
 
 
@@ -52,33 +54,72 @@ static void ciexyz_to_rgb(const libcolour_ciexyz_t* restrict from, libcolour_rgb
 static void to_rgb(const libcolour_colour_t* restrict from, libcolour_rgb_t* restrict to)
 {
   int have_gamma = 0, with_gamma = to->with_gamma;
-  libcolour_colour_t tmp;
+  libcolour_ciexyz_t tmp;
   switch (from->model) {
   case LIBCOLOUR_CIEXYZ:
     ciexyz_to_rgb(&from->ciexyz, to);
     break;
   case LIBCOLOUR_RGB:
-    if (!memcmp(from->M, to->M, sizeof(double[3][3]))) {
-      have_gamma = from->with_gamma;
-      *to = *from;
+    if (!memcmp(from->rgb.M, to->M, sizeof(double[3][3]))) {
+      have_gamma = from->rgb.with_gamma;
+      *to = from->rgb;
       break;
     }
     /* fall through */
   default:
     tmp.model = LIBCOLOUR_CIEXYZ;
-    to_ciexyz(from, &tmp.ciexyz);
-    ciexyz_to_rgb(&tmp.ciexyz, to);
+    to_ciexyz(from, &tmp);
+    ciexyz_to_rgb(&tmp, to);
     break;
   }
+
   if (have_gamma != with_gamma) {
     if (with_gamma) {
-      to->R = to->regular_gamma ? pow(to->R, 1 / to->gamma) : (to->to_encoded_red)(to->R);
-      to->G = to->regular_gamma ? pow(to->G, 1 / to->gamma) : (to->to_encoded_green)(to->G);
-      to->B = to->regular_gamma ? pow(to->B, 1 / to->gamma) : (to->to_encoded_blue)(to->B);
+      switch (to->encoding_type) {
+      case LIBCOLOUR_ENCODING_TYPE_LINEAR:
+	break;
+      case LIBCOLOUR_ENCODING_TYPE_SIMPLE:
+	to->R = pow(to->R, 1 / to->gamma);
+	to->G = pow(to->G, 1 / to->gamma);
+	to->B = pow(to->B, 1 / to->gamma);
+	break;
+      case LIBCOLOUR_ENCODING_TYPE_REGULAR:
+	to->R <= to->transition ? to->slope * to->R : (1 + to->offset) * pow(to->R, 1 / to->gamma) - to->offset;
+	to->G <= to->transition ? to->slope * to->G : (1 + to->offset) * pow(to->G, 1 / to->gamma) - to->offset;
+	to->B <= to->transition ? to->slope * to->B : (1 + to->offset) * pow(to->B, 1 / to->gamma) - to->offset;
+	break;
+      case LIBCOLOUR_ENCODING_TYPE_CUSTOM:
+	to->R = (to->to_encoded_red)(to->R);
+	to->G = (to->to_encoded_green)(to->G);
+	to->B = (to->to_encoded_blue)(to->B);
+	break;
+      default:
+	fprintf(stderr, "libcolour: invalid encoding type\n");
+	abort();
+      }
     } else {
-      to->R = to->regular_gamma ? pow(to->R, to->gamma) : (to->to_decoded_red)(to->R);
-      to->G = to->regular_gamma ? pow(to->G, to->gamma) : (to->to_decoded_green)(to->G);
-      to->B = to->regular_gamma ? pow(to->B, to->gamma) : (to->to_decoded_blue)(to->B);
+      switch (to->encoding_type) {
+      case LIBCOLOUR_ENCODING_TYPE_LINEAR:
+	break;
+      case LIBCOLOUR_ENCODING_TYPE_SIMPLE:
+	to->R = pow(to->R, to->gamma);
+	to->G = pow(to->G, to->gamma);
+	to->B = pow(to->B, to->gamma);
+	break;
+      case LIBCOLOUR_ENCODING_TYPE_REGULAR:
+	to->R <= to->transitioninv ? to->R * to->slope : pow((to->R + to->offset) / (1 + to->offset), to->gamma);
+	to->G <= to->transitioninv ? to->G * to->slope : pow((to->G + to->offset) / (1 + to->offset), to->gamma);
+	to->B <= to->transitioninv ? to->B * to->slope : pow((to->B + to->offset) / (1 + to->offset), to->gamma);
+	break;
+      case LIBCOLOUR_ENCODING_TYPE_CUSTOM:
+	to->R = (to->to_decoded_red)(to->R);
+	to->G = (to->to_decoded_green)(to->G);
+	to->B = (to->to_decoded_blue)(to->B);
+	break;
+      default:
+	fprintf(stderr, "libcolour: invalid encoding type\n");
+	abort();
+      }
     }
   }
 }
@@ -97,13 +138,13 @@ static void srgb_to_srgb(const libcolour_srgb_t* restrict from, libcolour_srgb_t
   if (from->with_gamma == to->with_gamma) {
     *to = *from;
   } else if (to->with_gamma) {
-    from->R = libcolour_srgb_encode(to->R);
-    from->G = libcolour_srgb_encode(to->G);
-    from->B = libcolour_srgb_encode(to->B);
+    to->R = libcolour_srgb_encode(from->R);
+    to->G = libcolour_srgb_encode(from->G);
+    to->B = libcolour_srgb_encode(from->B);
   } else {
-    from->R = libcolour_srgb_decode(to->R);
-    from->G = libcolour_srgb_decode(to->G);
-    from->B = libcolour_srgb_decode(to->B);
+    to->R = libcolour_srgb_decode(from->R);
+    to->G = libcolour_srgb_decode(from->G);
+    to->B = libcolour_srgb_decode(from->B);
   }
 }
 
@@ -141,10 +182,10 @@ static void ycgco_to_srgb(const libcolour_ycgco_t* restrict from, libcolour_srgb
 
 static void other_to_srgb(const libcolour_colour_t* restrict from, libcolour_srgb_t* restrict to)
 {
-  libcolour_colour_t tmp;
+  libcolour_ciexyz_t tmp;
   tmp.model = LIBCOLOUR_CIEXYZ;
-  to_ciexyz(from, &tmp.ciexyz);
-  ciexyz_to_srgb(&tmp.ciexyz, to);
+  to_ciexyz(from, &tmp);
+  ciexyz_to_srgb(&tmp, to);
 }
 
 static void to_srgb(const libcolour_colour_t* restrict from, libcolour_srgb_t* restrict to)
@@ -175,7 +216,7 @@ static void to_srgb(const libcolour_colour_t* restrict from, libcolour_srgb_t* r
   }
   if (to->with_gamma) {
     tmp = *to;
-    tmp->with_gamma = 0;
+    tmp.with_gamma = 0;
     srgb_to_srgb(&tmp, to);
   }
 }
@@ -192,39 +233,39 @@ static void ciexyz_to_ciexyy(const libcolour_ciexyz_t* restrict from, libcolour_
     to->x = to->y = 0;
 }
 
+static void other_to_ciexyy(const libcolour_colour_t* restrict from, libcolour_ciexyy_t* restrict to)
+{
+  libcolour_ciexyz_t tmp;
+  tmp.model = LIBCOLOUR_CIEXYZ;
+  to_ciexyz(from, &tmp);
+  ciexyz_to_ciexyy(&tmp, to);
+}
+
 static void srgb_to_ciexyy(const libcolour_srgb_t* restrict from, libcolour_ciexyy_t* restrict to)
 {
-  libcolour_colour_t tmp;
+  libcolour_srgb_t tmp;
   tmp.model = LIBCOLOUR_SRGB;
   if (from->with_gamma) {
-    tmp.srgb.with_gamma = 0;
-    to_srgb((const libcolour_colour_t *)from, &tmp.srgb);
+    tmp.with_gamma = 0;
+    to_srgb((const libcolour_colour_t*)from, &tmp);
   } else {
-    tmp.srgb = *from;
+    tmp = *from;
   }
   if (tmp.R == 0 && tmp.G == 0 && tmp.B == 0) {
     to->x = 0.31272660439158;
     to->y = 0.32902315240275;
     to->Y = 0;
   } else {
-    other_to_ciexyy(&tmp, to);
+    other_to_ciexyy((const libcolour_colour_t*)&tmp, to);
   }
 }
 
-static void other_to_ciexyy(const libcolour_colour_t* restrict from, libcolour_ciexyy_t* restrict to)
-{
-  libcolour_colour_t tmp;
-  tmp.model = LIBCOLOUR_CIEXYZ;
-  to_ciexyz(from, &tmp.ciexyz);
-  ciexyz_to_ciexyy(&tmp.ciexyz, to);
-}
-
 static void to_ciexyy(const libcolour_colour_t* restrict from, libcolour_ciexyy_t* restrict to)
 {
   switch (from->model) {
   case LIBCOLOUR_CIEXYY:
-    *to = *from;
-    return
+    *to = from->ciexyy;
+    return;
   case LIBCOLOUR_CIEXYZ:
     ciexyz_to_ciexyy(&from->ciexyz, to);
     return;
@@ -240,29 +281,29 @@ static void to_ciexyy(const libcolour_colour_t* restrict from, libcolour_ciexyy_
 
 static void rgb_to_ciexyz(const libcolour_rgb_t* restrict from, libcolour_ciexyz_t* restrict to)
 {
-  libcolour_colour_t tmp;
+  libcolour_rgb_t tmp;
   double R, G, B;
   if (from->with_gamma) {
     tmp = *from;
-    tmp.rgb.with_gamma = 0;
-    to_rgb(from, &tmp);
+    tmp.with_gamma = 0;
+    to_rgb((const libcolour_colour_t*)from, &tmp);
     R = tmp.R, G = tmp.G, B = tmp.B;
   } else {
     R = from->R, G = from->G, B = from->B;
   }
-  to->X = to->M[0][0] * R + to->M[0][1] * G + to->M[0][2] * B;
-  to->Y = to->M[1][0] * R + to->M[1][1] * G + to->M[1][2] * B;
-  to->Z = to->M[2][0] * R + to->M[2][1] * G + to->M[2][2] * B;
+  to->X = from->M[0][0] * R + from->M[0][1] * G + from->M[0][2] * B;
+  to->Y = from->M[1][0] * R + from->M[1][1] * G + from->M[1][2] * B;
+  to->Z = from->M[2][0] * R + from->M[2][1] * G + from->M[2][2] * B;
 }
 
 static void srgb_to_ciexyz(const libcolour_srgb_t* restrict from, libcolour_ciexyz_t* restrict to)
 {
-  libcolour_colour_t tmp;
+  libcolour_srgb_t tmp;
   double R, G, B;
   if (from->with_gamma) {
     tmp.model = LIBCOLOUR_SRGB;
-    tmp.srgb.with_gamma = 0;
-    to_srgb(from, &tmp);
+    tmp.with_gamma = 0;
+    to_srgb((const libcolour_colour_t*)from, &tmp);
     R = tmp.R, G = tmp.G, B = tmp.B;
   } else {
     R = from->R, G = from->G, B = from->B;
@@ -281,7 +322,7 @@ static void ciexyy_to_ciexyz(const libcolour_ciexyy_t* restrict from, libcolour_
   } else {
     to->X = x * Yy;
     to->Y = Y;
-    to->Z = (1 - x - y) * Yy
+    to->Z = (1 - x - y) * Yy;
   }
 }
 
@@ -321,19 +362,19 @@ static void cieluv_to_ciexyz(const libcolour_cieluv_t* restrict from, libcolour_
 
 static void cielch_to_ciexyz(const libcolour_cielch_t* restrict from, libcolour_ciexyz_t* restrict to)
 {
-  libcolour_colour_t tmp;
-  tmp.model = LIBCOLOUR_YUV;
-  tmp.yuv.white = from.white;
-  to_yuv((const libcolour_colour_t*)from, &tmp.yuv);
-  yuv_to_ciexyz(&tmp.yuv, to);
+  libcolour_cieluv_t tmp;
+  tmp.model = LIBCOLOUR_CIELUV;
+  tmp.white = from->white;
+  to_cieluv((const libcolour_colour_t*)from, &tmp);
+  cieluv_to_ciexyz(&tmp, to);
 }
 
 static void yuv_to_ciexyz(const libcolour_yuv_t* restrict from, libcolour_ciexyz_t* restrict to)
 {
-  libcolour_colour_t tmp;
+  libcolour_ydbdr_t tmp;
   tmp.model = LIBCOLOUR_YDBDR;
-  to_ydbdr((const libcolour_colour_t*)from, &tmp.ydbdr);
-  ydbdr_to_ciexyz(&tmp.ydbdr, to);
+  to_ydbdr((const libcolour_colour_t*)from, &tmp);
+  to_ciexyz((const libcolour_colour_t*)&tmp, to);
 }
 
 static void cie1960ucs_to_ciexyz(const libcolour_cie1960ucs_t* restrict from, libcolour_ciexyz_t* restrict to)
@@ -346,19 +387,19 @@ static void cie1960ucs_to_ciexyz(const libcolour_cie1960ucs_t* restrict from, li
 
 static void cieuvw_to_ciexyz(const libcolour_cieuvw_t* restrict from, libcolour_ciexyz_t* restrict to)
 {
-  libcolour_colour_t tmp;
-  tmp.model = LIBCOLOUR_CIE1969UCS;
-  to_cie1960ucs((const libcolour_cieuvw_t*)from, &tmp.cie1960ucs);
-  cie1960ucs_to_ciexyz(&tmp.cie1960ucs, to);
+  libcolour_cie1960ucs_t tmp;
+  tmp.model = LIBCOLOUR_CIE1960UCS;
+  to_cie1960ucs((const libcolour_colour_t*)from, &tmp);
+  cie1960ucs_to_ciexyz(&tmp, to);
 }
 
 static void other_to_ciexyz(const libcolour_colour_t* restrict from, libcolour_ciexyz_t* restrict to)
 {
-  libcolour_colour_t tmp;
+  libcolour_srgb_t tmp;
   tmp.model = LIBCOLOUR_SRGB;
-  tmp.srgb.with_gamma = 0;
-  to_srgb(from, &tmp.srgb);
-  srgb_to_ciexyz(&tmp.srgb, to);
+  tmp.with_gamma = 0;
+  to_srgb(from, &tmp);
+  srgb_to_ciexyz(&tmp, to);
 }
 
 static void to_ciexyz(const libcolour_colour_t* restrict from, libcolour_ciexyz_t* restrict to)
@@ -366,7 +407,7 @@ static void to_ciexyz(const libcolour_colour_t* restrict from, libcolour_ciexyz_
   switch (from->model) {
   case LIBCOLOUR_RGB:
     rgb_to_ciexyz(&from->rgb, to);
-    return
+    return;
   case LIBCOLOUR_SRGB:
     srgb_to_ciexyz(&from->srgb, to);
     return;
@@ -374,7 +415,7 @@ static void to_ciexyz(const libcolour_colour_t* restrict from, libcolour_ciexyz_
     ciexyy_to_ciexyz(&from->ciexyy, to);
     return;
   case LIBCOLOUR_CIEXYZ:
-    *to = *from
+    *to = from->ciexyz;
     return;
   case LIBCOLOUR_CIELAB:
     cielab_to_ciexyz(&from->cielab, to);
@@ -419,10 +460,10 @@ static void ciexyz_to_cielab(const libcolour_ciexyz_t* restrict from, libcolour_
 
 static void other_to_cielab(const libcolour_colour_t* restrict from, libcolour_cielab_t* restrict to)
 {
-  libcolour_colour_t tmp;
+  libcolour_ciexyz_t tmp;
   tmp.model = LIBCOLOUR_CIEXYZ;
-  to_ciexyz(from, &tmp.ciexyz);
-  ciexyz_to_cielab(&tmp.ciexyz, to);
+  to_ciexyz(from, &tmp);
+  ciexyz_to_cielab(&tmp, to);
 }
 
 static void to_cielab(const libcolour_colour_t* restrict from, libcolour_cielab_t* restrict to)
@@ -432,7 +473,7 @@ static void to_cielab(const libcolour_colour_t* restrict from, libcolour_cielab_
     ciexyz_to_cielab(&from->ciexyz, to);
     return;
   case LIBCOLOUR_CIELAB:
-    *to = *from;
+    *to = from->cielab;
     return;
   default:
     other_to_cielab(from, to);
@@ -443,21 +484,14 @@ static void to_cielab(const libcolour_colour_t* restrict from, libcolour_cielab_
 
 static void ciexyz_to_cieluv(const libcolour_ciexyz_t* restrict from, libcolour_cieluv_t* restrict to)
 {
-  libcolour_ciexyz_t white;
   double t, u, v, y, y2;
-  if (to->white.model == LIBCOLOUR_CIEXYZ) {
-    white = to->white.ciexyz;
-  } else {
-    white.model = LIBCOLOUR_CIEXYZ;
-    to_ciexyz(&to->white, &white);
-  }
-  t = white.X + 15 * white.Y + 3 * white.Z;
-  u = 4 * white.x / t;
-  v = 9 * white.y / t;
+  t = to->white.X + 15 * to->white.Y + 3 * to->white.Z;
+  u = 4 * to->white.X / t;
+  v = 9 * to->white.Y / t;
   t = from->X + 15 * from->Y + 4 * from->Z;
-  u = 4 * from->x / t - u;
-  v = 9 * from->y / t - v;
-  y = from->Y = white.Y;
+  u = 4 * from->X / t - u;
+  v = 9 * from->Y / t - v;
+  y = from->Y / to->white.Y;
   y2 = y * 24389;
   y = y2 <= 216 ? y2 / 27 : cbrt(y) * 116 - 16;
   to->L = y;
@@ -468,28 +502,16 @@ static void ciexyz_to_cieluv(const libcolour_ciexyz_t* restrict from, libcolour_
 
 static void cielch_to_cieluv(const libcolour_cielch_t* restrict from, libcolour_cieluv_t* restrict to)
 {
-  libcolour_ciexyz_t from_white, to_white;
-  libcolour_colour_t tmp, tmp2;
+  libcolour_ciexyz_t tmp;
+  libcolour_cielch_t tmp2;
   double L, C, h;
-  if (from->white.model == LIBCOLOUR_CIEXYZ) {
-    from_white = from->white.ciexyz;
-  } else {
-    from_white.model = LIBCOLOUR_CIEXYZ;
-    to_ciexyz(&from->white, &from_white);
-  }
-  if (to->white.model == LIBCOLOUR_CIEXYZ) {
-    to_white = to->white.ciexyz;
-  } else {
-    to_white.model = LIBCOLOUR_CIEXYZ;
-    to_ciexyz(&to->white, &to_white);
-  }
-  if (to_white.X != from_white.X || to_white.Y != from_white.Y || to_white.Z != from_white.Z) {
+  if (to->white.X != from->white.X || to->white.Y != from->white.Y || to->white.Z != from->white.Z) {
     tmp.model = LIBCOLOUR_CIEXYZ;
     tmp2.model = LIBCOLOUR_CIELCH;
-    tmp2.cielch.white.ciexyz = to_white;
-    to_ciexyz(from, &tmp.ciexyz);
-    to_cielch(&tmp, &tmp2.cielch);
-    L = tmp2.cielch.L, C = tmp2.cielch.C, h = tmp2.cielch.h;
+    tmp2.white = to->white;
+    to_ciexyz((const libcolour_colour_t*)from, &tmp);
+    to_cielch((const libcolour_colour_t*)&tmp, &tmp2);
+    L = tmp2.L, C = tmp2.C, h = tmp2.h;
   } else {
     L = from->L, C = from->C, h = from->h;
   }
@@ -500,25 +522,23 @@ static void cielch_to_cieluv(const libcolour_cielch_t* restrict from, libcolour_
 
 static void other_to_cieluv(const libcolour_colour_t* restrict from, libcolour_cieluv_t* restrict to)
 {
-  libcolour_colour_t tmp;
+  libcolour_ciexyz_t tmp;
   tmp.model = LIBCOLOUR_CIEXYZ;
-  to_ciexyz(from, &tmp.ciexyz);
-  ciexyz_to_cieluv(&tmp.ciexyz, to);
+  to_ciexyz(from, &tmp);
+  ciexyz_to_cieluv(&tmp, to);
 }
 
 static void to_cieluv(const libcolour_colour_t* restrict from, libcolour_cieluv_t* restrict to)
 {
   switch (from->model) {
   case LIBCOLOUR_CIELCH:
-    cielch_to_cielab(&from->cielch, to);
+    cielch_to_cieluv(&from->cielch, to);
     return;
   case LIBCOLOUR_CIELUV:
-    if (to->cieluv.model == LIBCOLOUR_CIEXYZ &&
-	from->cieluv.model == LIBCOLOUR_CIEXYZ &&
-	to->cieluv.white.X == from->cieluv.white.X &&
-	to->cieluv.white.Y == from->cieluv.white.Y &&
-	to->cieluv.white.Z == from->cieluv.white.Z) {
-      *to = *from;
+    if (to->white.X == from->cieluv.white.X &&
+	to->white.Y == from->cieluv.white.Y &&
+	to->white.Z == from->cieluv.white.Z) {
+      *to = from->cieluv;
       return;
     }
     /* fall through */
@@ -531,25 +551,12 @@ static void to_cieluv(const libcolour_colour_t* restrict from, libcolour_cieluv_
 
 static void cieluv_to_cielch(const libcolour_cieluv_t* restrict from, libcolour_cielch_t* restrict to)
 {
-  libcolour_ciexyz_t from_white, to_white;
   libcolour_cieluv_t tmp;
   double L, u, v;
-  if (from->white.model == LIBCOLOUR_CIEXYZ) {
-    from_white = from->white.ciexyz;
-  } else {
-    from_white.model = LIBCOLOUR_CIEXYZ;
-    to_ciexyz(&from->white, &from_white);
-  }
-  if (to->white.model == LIBCOLOUR_CIEXYZ) {
-    to_white = to->white.ciexyz;
-  } else {
-    to_white.model = LIBCOLOUR_CIEXYZ;
-    to_ciexyz(&to->white, &to_white);
-  }
-  if (to_white.X != from_white.X || to_white.Y != from_white.Y || to_white.Z != from_white.Z) {
+  if (to->white.X != from->white.X || to->white.Y != from->white.Y || to->white.Z != from->white.Z) {
     tmp.model = LIBCOLOUR_CIELUV;
-    tmp.white.ciexyz = to_white;
-    to_cieluv(from, &tmp);
+    tmp.white = to->white;
+    to_cieluv((const libcolour_colour_t*)from, &tmp);
     L = tmp.L, u = tmp.u, v = tmp.v;
   } else {
     L = from->L, u = from->u, v = from->u;
@@ -561,11 +568,11 @@ static void cieluv_to_cielch(const libcolour_cieluv_t* restrict from, libcolour_
 
 static void other_to_cielch(const libcolour_colour_t* restrict from, libcolour_cielch_t* restrict to)
 {
-  libcolour_colour_t tmp;
+  libcolour_cieluv_t tmp;
   tmp.model = LIBCOLOUR_CIELUV;
-  tmp.white = to.white;
-  to_cieluv(from, &tmp.cieluv);
-  cieluv_to_cielch(&tmp.cieluv, to);
+  tmp.white = to->white;
+  to_cieluv(from, &tmp);
+  cieluv_to_cielch(&tmp, to);
 }
 
 static void to_cielch(const libcolour_colour_t* restrict from, libcolour_cielch_t* restrict to)
@@ -575,12 +582,10 @@ static void to_cielch(const libcolour_colour_t* restrict from, libcolour_cielch_
     cieluv_to_cielch(&from->cieluv, to);
     return;
   case LIBCOLOUR_CIELCH:
-    if (to->cielch.model == LIBCOLOUR_CIEXYZ &&
-	from->cielch.model == LIBCOLOUR_CIEXYZ &&
-	to->cielch.white.X == from->cielcg.white.X &&
-	to->cielch.white.Y == from->cielch.white.Y &&
-	to->cielch.white.Z == from->cielch.white.Z) {
-      *to = *from;
+    if (to->white.X == from->cielch.white.X &&
+	to->white.Y == from->cielch.white.Y &&
+	to->white.Z == from->cielch.white.Z) {
+      *to = from->cielch;
       return;
     }
     /* fall through */
@@ -597,16 +602,16 @@ static void to_yiq(const libcolour_colour_t* restrict from, libcolour_yiq_t* res
   libcolour_colour_t tmp = *from;
   switch (from->model) {
   case LIBCOLOUR_YIQ:
-    *to = *from;
+    *to = from->yiq;
     return;
   default:
     tmp.model = LIBCOLOUR_SRGB;
     to_srgb(from, &tmp.srgb);
     /* fall through */
   case LIBCOLOUR_SRGB:
-    r = tmp->srgb.R;
-    g = tmp->srgb.G;
-    b = tmp->srgb.B;
+    r = tmp.srgb.R;
+    g = tmp.srgb.G;
+    b = tmp.srgb.B;
     to->Y = r * 0.29893602129377540 + g * 0.5870430744511212 + b * 0.11402090425510336;
     to->I = r * 0.59594574307079930 - g * 0.2743886357457892 - b * 0.32155710732501010;
     to->Q = r * 0.21149734030682846 - g * 0.5229106903029739 + b * 0.31141334999614540;
@@ -621,25 +626,26 @@ static void to_ydbdr(const libcolour_colour_t* restrict from, libcolour_ydbdr_t*
   libcolour_colour_t tmp = *from;
   switch (from->model) {
   case LIBCOLOUR_YDBDR:
-    *to = *from;
+    *to = from->ydbdr;
     return;
   case LIBCOLOUR_YUV:
-    to->Y  =  tmp->yuv.Y;
-    to->Db =  tmp->yuv.U * 3.069;
-    to->Dr = -tmp->yuv.V * 2.169;
+    to->Y  =  tmp.yuv.Y;
+    to->Db =  tmp.yuv.U * 3.069;
+    to->Dr = -tmp.yuv.V * 2.169;
     return;
   default:
     tmp.model = LIBCOLOUR_SRGB;
     to_srgb(from, &tmp.srgb);
     /* fall through */
   case LIBCOLOUR_SRGB:
-    r = tmp->srgb.R;
-    g = tmp->srgb.G;
-    b = tmp->srgb.B;
+    r = tmp.srgb.R;
+    g = tmp.srgb.G;
+    b = tmp.srgb.B;
     to->Y  =  r * 0.299 + g * 0.587 + b * 0.114;
     to->Db = -r * 0.450 - g * 0.883 + b * 1.333;
     to->Dr = -r * 1.333 + g * 1.116 + b * 0.217;
     return;
+  }
 }
 
 
@@ -648,7 +654,7 @@ static void to_yuv(const libcolour_colour_t* restrict from, libcolour_yuv_t* res
   libcolour_colour_t tmp = *from;
   switch (from->model) {
   case LIBCOLOUR_YUV:
-    *to = *from;
+    *to = from->yuv;
     return;
   default:
     tmp.model = LIBCOLOUR_YDBDR;
@@ -659,6 +665,7 @@ static void to_yuv(const libcolour_colour_t* restrict from, libcolour_yuv_t* res
     to->U =  tmp.ydbdr.Db / 3.069;
     to->V = -tmp.ydbdr.Dr / 2.169;
     return;
+  }
 }
 
 
@@ -667,7 +674,7 @@ static void to_ypbpr(const libcolour_colour_t* restrict from, libcolour_ypbpr_t*
   libcolour_colour_t tmp = *from;
   switch (from->model) {
   case LIBCOLOUR_YPBPR:
-    *to = *from;
+    *to = from->ypbpr;
     return;
   default:
     tmp.model = LIBCOLOUR_SRGB;
@@ -678,6 +685,7 @@ static void to_ypbpr(const libcolour_colour_t* restrict from, libcolour_ypbpr_t*
     to->Pb = tmp.srgb.B - to->Y;
     to->Pr = tmp.srgb.R - to->Y;
     return;
+  }
 }
 
 
@@ -686,7 +694,7 @@ static void to_ycgco(const libcolour_colour_t* restrict from, libcolour_ycgco_t*
   libcolour_colour_t tmp = *from;
   switch (from->model) {
   case LIBCOLOUR_YCGCO:
-    *to = *from;
+    *to = from->ycgco;
     return;
   default:
     tmp.model = LIBCOLOUR_SRGB;
@@ -697,6 +705,7 @@ static void to_ycgco(const libcolour_colour_t* restrict from, libcolour_ycgco_t*
     to->Cg = -tmp.srgb.R / 4 + tmp.srgb.G / 2 - tmp.srgb.B / 4;
     to->Co =  tmp.srgb.R / 2 - tmp.srgb.B / 2;
     return;
+  }
 }
 
 
@@ -706,7 +715,7 @@ static void to_cie1960ucs(const libcolour_colour_t* restrict from, libcolour_cie
   double u, v, w, y;
   switch (from->model) {
   case LIBCOLOUR_CIE1960UCS:
-    *to = *from;
+    *to = from->cie1960ucs;
     return;
   case LIBCOLOUR_CIEUVW:
     u = from->cieuvw.U, v = from->cieuvw.V, w = from->cieuvw.W;
@@ -819,22 +828,22 @@ int libcolour_convert(const libcolour_colour_t* restrict from, libcolour_colour_
 
 double libcolour_srgb_encode(double x)
 {
-  return x <= 0.0031308 ? 12.92 * x : 1.055 * pow(x, 1 / 2.4) - 0.055;
+  return x <= 0.0031306684425217108 ? 12.92 * x : 1.055 * pow(x, 1 / 2.4) - 0.055;
 }
 
 double libcolour_srgb_decode(double x)
 {
-  return x <= 0.04045 ? x / 12.92 : pow((x + 0.055) / 1.055, 2.4);
+  return x <= 0.040448236277380506 ? x / 12.92 : pow((x + 0.055) / 1.055, 2.4);
 }
 
 
 int libcolour_delta_e(const libcolour_colour_t* a, const libcolour_colour_t* b, double* e)
 {
-  libcolour_colour_t u, v;
+  libcolour_cielab_t u, v;
   u.model = v.model = LIBCOLOUR_CIELAB;
-  if (libcolour_convert(a, &u))
+  if (libcolour_convert(a, (libcolour_colour_t*)&u))
     return -1;
-  if (libcolour_convert(b, &v))
+  if (libcolour_convert(b, (libcolour_colour_t*)&v))
     return -1;
   u.L -= v.L, u.L *= u.L;
   u.a -= v.a, u.a *= u.a;
@@ -890,59 +899,35 @@ static int eliminate(double** M, size_t n, size_t m)
 int libcolour_proper(libcolour_colour_t* colour)
 {
   libcolour_colour_t tmp, r, g, b;
-  double m[3][4]
+  double m[3][4];
   switch (colour->model) {
+  case LIBCOLOUR_CIELUV:
+    colour->cieluv.white.model = LIBCOLOUR_CIEXYZ;
+    break;
+  case LIBCOLOUR_CIELCH:
+    colour->cielch.white.model = LIBCOLOUR_CIEXYZ;
+    break;
   case LIBCOLOUR_RGB:
-    if (colour->rgb.red.model != LIBCOLOUR_CIEXYY) {
-      tmp.model = LIBCOLOUR_CIEXYY;
-      libcolour_convert(&colour->rgb.red, &tmp);
-      colour->rgb.red = tmp;
-    }
-    if (colour->rgb.green.model != LIBCOLOUR_CIEXYY) {
-      tmp.model = LIBCOLOUR_CIEXYY;
-      libcolour_convert(&colour->rgb.green, &tmp);
-      colour->rgb.green = tmp;
-    }
-    if (colour->rgb.blue.model != LIBCOLOUR_CIEXYY) {
-      tmp.model = LIBCOLOUR_CIEXYY;
-      libcolour_convert(&colour->rgb.blue, &tmp);
-      colour->rgb.blue = tmp;
-    }
-    if (colour->rgb.white.model != LIBCOLOUR_CIEXYY) {
-      tmp.model = LIBCOLOUR_CIEXYY;
-      libcolour_convert(&colour->rgb.white, &tmp);
-      colour->rgb.white = tmp;
-    }
-    colour->rgb.red.ciexyy.Y = 1;
-    colour->rgb.green.ciexyy.Y = 1;
-    colour->rgb.blue.ciexyy.Y = 1;
+    colour->rgb.red.model   = LIBCOLOUR_CIEXYY;
+    colour->rgb.green.model = LIBCOLOUR_CIEXYY;
+    colour->rgb.blue.model  = LIBCOLOUR_CIEXYY;
+    colour->rgb.white.model = LIBCOLOUR_CIEXYY;
+    colour->rgb.red.Y   = 1;
+    colour->rgb.green.Y = 1;
+    colour->rgb.blue.Y  = 1;
     r.model = g.model = b.model = tmp.model = LIBCOLOUR_CIEXYZ;
-    libcolour_convert(&colour->rgb.red, &r);
-    libcolour_convert(&colour->rgb.green, &g);
-    libcolour_convert(&colour->rgb.blue, &b);
-    libcolour_convert(&colour->rgb.white, &tmp);
+    libcolour_convert((const libcolour_colour_t*)&colour->rgb.red, &r);
+    libcolour_convert((const libcolour_colour_t*)&colour->rgb.green, &g);
+    libcolour_convert((const libcolour_colour_t*)&colour->rgb.blue, &b);
+    libcolour_convert((const libcolour_colour_t*)&colour->rgb.white, &tmp);
     m[0][0] = r.ciexyz.X, m[0][1] = g.ciexyz.X, m[0][2] = b.ciexyz.X, m[0][3] = tmp.ciexyz.X;
     m[1][0] = r.ciexyz.Y, m[1][1] = g.ciexyz.Y, m[1][2] = b.ciexyz.Y, m[1][3] = tmp.ciexyz.Y;
     m[2][0] = r.ciexyz.Z, m[2][1] = g.ciexyz.Z, m[2][2] = b.ciexyz.Z, m[2][3] = tmp.ciexyz.Z;
-    if (eliminate(m, 3, 4))
+    if (eliminate((double**)m, 3, 4))
       return -1;
-    colour->rgb.red.ciexyy.Y = m[0][3];
-    colour->rgb.green.ciexyy.Y = m[1][3];
-    colour->rgb.blue.ciexyy.Y = m[2][3];
-    break;
-  case LIBCOLOUR_CIELUV:
-    if (colour->cieluv.white.model != LIBCOLOUR_CIEXYZ) {
-      tmp.model = LIBCOLOUR_CIEXYZ;
-      libcolour_convert(&colour->cieluv.white, &tmp);
-      colour->cieluv.white = tmp;
-    }
-    break;
-  case LIBCOLOUR_CIELCH:
-    if (colour->cielch.white.model != LIBCOLOUR_CIEXYZ) {
-      tmp.model = LIBCOLOUR_CIEXYZ;
-      libcolour_convert(&colour->cielch.white, &tmp);
-      colour->cielch.white = tmp;
-    }
+    colour->rgb.red.Y   = m[0][3];
+    colour->rgb.green.Y = m[1][3];
+    colour->rgb.blue.Y  = m[2][3];
     break;
   default:
     break;
@@ -950,3 +935,420 @@ int libcolour_proper(libcolour_colour_t* colour)
   return 0;
 }
 
+
+static int get_primaries(libcolour_rgb_t* cs)
+{
+  libcolour_colour_t r, g, b, w;
+  double Sr, Sg, Sb;
+  double M[3][6];
+
+  r.model = g.model = b.model = w.model = LIBCOLOUR_CIEXYZ;
+  r.ciexyz.Y = 1;
+  g.ciexyz.Y = 1;
+  b.ciexyz.Y = 1;
+
+  Sr = cs->M[1][0] * cs->white_r;
+  Sg = cs->M[1][1] * cs->white_g;
+  Sb = cs->M[1][2] * cs->white_b;
+
+  r.ciexyz.X = cs->M[0][0] / Sr, r.ciexyz.Z = cs->M[2][0] / Sr;
+  g.ciexyz.X = cs->M[0][1] / Sg, g.ciexyz.Z = cs->M[2][1] / Sg;
+  b.ciexyz.X = cs->M[0][2] / Sb, b.ciexyz.Z = cs->M[2][2] / Sb;
+
+  M[0][0] = r.ciexyz.X, M[0][1] = g.ciexyz.X, M[0][2] = b.ciexyz.X, M[0][3] = 1, M[0][4] = 0, M[0][5] = 0;
+  M[1][0] = r.ciexyz.Y, M[1][1] = g.ciexyz.Y, M[1][2] = b.ciexyz.Y, M[1][3] = 0, M[1][4] = 1, M[1][5] = 0;
+  M[2][0] = r.ciexyz.Z, M[2][1] = g.ciexyz.Z, M[2][2] = b.ciexyz.Z, M[2][3] = 0, M[2][4] = 0, M[2][5] = 1;
+
+  if (eliminate((double**)M, 3, 6))
+    return -1;
+
+  memcpy(M[0], M[0] + 3, 3 * sizeof(double)), M[0][3] = Sr;
+  memcpy(M[1], M[1] + 3, 3 * sizeof(double)), M[1][3] = Sg;
+  memcpy(M[2], M[2] + 3, 3 * sizeof(double)), M[2][3] = Sb;
+
+  if (eliminate((double**)M, 3, 4))
+    return -1;
+
+  w.ciexyz.X = M[0][3];
+  w.ciexyz.Y = M[1][3];
+  w.ciexyz.Z = M[2][3];
+
+  cs->red.model   = LIBCOLOUR_CIEXYY;
+  cs->green.model = LIBCOLOUR_CIEXYY;
+  cs->blue.model  = LIBCOLOUR_CIEXYY;
+  cs->white.model = LIBCOLOUR_CIEXYY;
+
+  libcolour_convert(&r, (libcolour_colour_t*)&cs->red);
+  libcolour_convert(&g, (libcolour_colour_t*)&cs->green);
+  libcolour_convert(&b, (libcolour_colour_t*)&cs->blue);
+  libcolour_convert(&w, (libcolour_colour_t*)&cs->white);
+
+  return 0;
+}
+
+
+static int get_matrices(libcolour_rgb_t* cs)
+{
+  libcolour_colour_t r, g, b, w;
+  double M[3][6];
+  double Sr, Sg, Sb;
+
+  r.model = g.model = b.model = LIBCOLOUR_CIEXYZ;
+  w.model = LIBCOLOUR_CIEXYY;
+  libcolour_convert((const libcolour_colour_t*)&cs->red,   &w), w.ciexyy.Y = 1, libcolour_convert(&w, &r);
+  libcolour_convert((const libcolour_colour_t*)&cs->green, &w), w.ciexyy.Y = 1, libcolour_convert(&w, &g);
+  libcolour_convert((const libcolour_colour_t*)&cs->blue,  &w), w.ciexyy.Y = 1, libcolour_convert(&w, &b);
+  w.model = LIBCOLOUR_CIEXYZ;
+  libcolour_convert((const libcolour_colour_t*)&cs->white, &w);
+
+  M[0][0] = r.ciexyz.X, M[0][1] = g.ciexyz.X, M[0][2] = b.ciexyz.X, M[0][3] = 1, M[0][4] = 0, M[0][5] = 0;
+  M[1][0] = r.ciexyz.Y, M[1][1] = g.ciexyz.Y, M[1][2] = b.ciexyz.Y, M[1][3] = 0, M[1][4] = 1, M[1][5] = 0;
+  M[2][0] = r.ciexyz.Z, M[2][1] = g.ciexyz.Z, M[2][2] = b.ciexyz.Z, M[2][3] = 0, M[2][4] = 0, M[2][5] = 1;
+
+  if (eliminate((double**)M, 3, 6))
+    return -1;
+
+  Sr = M[0][3] * w.ciexyz.X + M[0][4] * w.ciexyz.Y + M[0][5] * w.ciexyz.Z;
+  Sg = M[1][3] * w.ciexyz.X + M[1][4] * w.ciexyz.Y + M[1][5] * w.ciexyz.Z;
+  Sb = M[2][3] * w.ciexyz.X + M[2][4] * w.ciexyz.Y + M[2][5] * w.ciexyz.Z;
+
+  Sr /= cs->white_r;
+  Sg /= cs->white_g;
+  Sb /= cs->white_b;
+
+  cs->M[0][0] = Sr * r.ciexyz.X, cs->M[0][1] = Sg * g.ciexyz.X, cs->M[0][2] = Sb * b.ciexyz.X;
+  cs->M[1][0] = Sr * r.ciexyz.Y, cs->M[1][1] = Sg * g.ciexyz.Y, cs->M[1][2] = Sb * b.ciexyz.Y;
+  cs->M[2][0] = Sr * r.ciexyz.Z, cs->M[2][1] = Sg * g.ciexyz.Z, cs->M[2][2] = Sb * b.ciexyz.Z;
+
+  memcpy(M[0], cs->M[0], 3 * sizeof(double)), M[0][3] = 1, M[0][4] = 0, M[0][5] = 0;
+  memcpy(M[1], cs->M[1], 3 * sizeof(double)), M[1][3] = 0, M[1][4] = 1, M[1][5] = 0;
+  memcpy(M[2], cs->M[2], 3 * sizeof(double)), M[2][3] = 0, M[2][4] = 0, M[2][5] = 1;
+
+  if (eliminate((double**)M, 3, 6))
+    return -1;
+
+  memcpy(cs->Minv[0], M[0] + 3, 3 * sizeof(double));
+  memcpy(cs->Minv[1], M[1] + 3, 3 * sizeof(double));
+  memcpy(cs->Minv[2], M[2] + 3, 3 * sizeof(double));
+
+  return 0;
+}
+
+
+static int invert(double **Minv, double **M, size_t n)
+{
+  double J[3][6];
+  size_t i;
+  for (i = 0; i < n; i++) {
+    memcpy(J[i], M[i], n * sizeof(double));
+    memset(J[i] + n, 0, n * sizeof(double));
+    J[i][n + i] = 1;
+  }
+  if (eliminate((double**)J, n, 2 * n))
+    return -1;
+  for (i = 0; i < n; i++)
+    memcpy(M[i], J[i] + n, n * sizeof(double));
+  return 0;
+}
+
+
+int libcolour_get_rgb_colour_space(libcolour_colour_t* cs_, libcolour_rgb_colour_space_t space)
+{
+#define XYY(XVALUE, YVALUE)  (libcolour_ciexyy_t){ .model = LIBCOLOUR_CIEXYY, .x = XVALUE, .y = YVALUE, .Y = 1}
+
+  libcolour_rgb_t* cs = &cs_->rgb;
+
+  switch (space) {
+  case LIBCOLOUR_RGB_COLOUR_SPACE_CUSTOM_FROM_MEASUREMENTS:
+    if (get_matrices(cs))
+      return -1;
+    return 0;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_CUSTOM_FROM_MATRIX:
+    if (invert((double**)(cs->Minv), (double**)(cs->M), 3) || get_primaries(cs))
+      return -1;
+    return 0;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_CUSTOM_FROM_INV_MATRIX:
+    if (invert((double**)(cs->M), (double**)(cs->Minv), 3) || get_primaries(cs))
+      return -1;
+    return 0;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_SRGB:
+    cs->red   = XYY(0.6400, 0.3300);
+    cs->green = XYY(0.3000, 0.6000);
+    cs->blue  = XYY(0.1500, 0.0600);
+    cs->white = LIBCOLOUR_ILLUMINANT_D65;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_REGULAR;
+    cs->offset = 0.055;
+    cs->gamma = 2.4;
+    cs->slope = 12.92;
+    cs->transition = 0.0031306684425217108;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_ADOBE_RGB:
+    cs->red   = XYY(0.6400, 0.3300);
+    cs->green = XYY(0.2100, 0.7100);
+    cs->blue  = XYY(0.1500, 0.0600);
+    cs->white = LIBCOLOUR_ILLUMINANT_D65;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_SIMPLE;
+    cs->gamma = 2.2;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_APPLE_RGB:
+    cs->red   = XYY(0.6250, 0.3400);
+    cs->green = XYY(0.2800, 0.5950);
+    cs->blue  = XYY(0.1550, 0.0700);
+    cs->white = LIBCOLOUR_ILLUMINANT_D65;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_SIMPLE;
+    cs->gamma = 1.8;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_BEST_RGB:
+    cs->red   = XYY(0.7347, 0.2653);
+    cs->green = XYY(0.2150, 0.7750);
+    cs->blue  = XYY(0.1300, 0.0350);
+    cs->white = LIBCOLOUR_ILLUMINANT_D50;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_SIMPLE;
+    cs->gamma = 2.2;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_BETA_RGB:
+    cs->red   = XYY(0.6888, 0.3112);
+    cs->green = XYY(0.1986, 0.7551);
+    cs->blue  = XYY(0.1265, 0.0352);
+    cs->white = LIBCOLOUR_ILLUMINANT_D50;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_SIMPLE;
+    cs->gamma = 2.2;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_BRUCE_RGB:
+    cs->red   = XYY(0.6400, 0.3300);
+    cs->green = XYY(0.2800, 0.6500);
+    cs->blue  = XYY(0.1500, 0.0600);
+    cs->white = LIBCOLOUR_ILLUMINANT_D65;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_SIMPLE;
+    cs->gamma = 2.2;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_CIE_RGB:
+    cs->red   = XYY(0.7350, 0.2650);
+    cs->green = XYY(0.2640, 0.7170);
+    cs->blue  = XYY(0.1670, 0.0090);
+    cs->white = LIBCOLOUR_ILLUMINANT_E;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_SIMPLE;
+    cs->gamma = 2.2;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_COLORMATCH_RGB:
+    cs->red   = XYY(0.6300, 0.3400);
+    cs->green = XYY(0.2950, 0.6050);
+    cs->blue  = XYY(0.1500, 0.0750);
+    cs->white = LIBCOLOUR_ILLUMINANT_D50;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_SIMPLE;
+    cs->gamma = 1.8;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_DCI_P3_D65:
+    cs->red   = XYY(0.6380, 0.3200);
+    cs->green = XYY(0.2650, 0.6900);
+    cs->blue  = XYY(0.1500, 0.0600);
+    cs->white = LIBCOLOUR_ILLUMINANT_D65;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_SIMPLE;
+    cs->gamma = 2.6;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_DCI_P3_THEATER:
+    cs->red   = XYY(0.6380, 0.3200);
+    cs->green = XYY(0.2650, 0.6900);
+    cs->blue  = XYY(0.1500, 0.0600);
+    cs->white = XYY(0.314, 0.351);
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_SIMPLE;
+    cs->gamma = 2.6;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_DON_RGB_4:
+    cs->red   = XYY(0.6960, 0.3000);
+    cs->green = XYY(0.2150, 0.7650);
+    cs->blue  = XYY(0.1300, 0.0350);
+    cs->white = LIBCOLOUR_ILLUMINANT_D50;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_SIMPLE;
+    cs->gamma = 2.2;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_ECI_RGB_V2:
+    cs->red   = XYY(0.6700, 0.3300);
+    cs->green = XYY(0.2100, 0.7100);
+    cs->blue  = XYY(0.1400, 0.0800);
+    cs->white = LIBCOLOUR_ILLUMINANT_D50;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_CUSTOM; /* TODO L* */
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_EKTA_SPACE_PS5:
+    cs->red   = XYY(0.6950, 0.3050);
+    cs->green = XYY(0.2600, 0.7000);
+    cs->blue  = XYY(0.1100, 0.0050);
+    cs->white = LIBCOLOUR_ILLUMINANT_D50;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_SIMPLE;
+    cs->gamma = 2.2;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_601_625_LINE:
+    cs->red   = XYY(0.6400, 0.3300);
+    cs->green = XYY(0.2900, 0.6000);
+    cs->blue  = XYY(0.1500, 0.0060);
+    cs->white = LIBCOLOUR_ILLUMINANT_D65;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_REGULAR;
+    cs->gamma = 1 / 0.45;
+    cs->offset = 0.09929682680944;
+    cs->slope = 4.5;
+    cs->transition = 0.018053968510807;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_601_525_LINE:
+    cs->red   = XYY(0.6300, 0.3400);
+    cs->green = XYY(0.3100, 0.5950);
+    cs->blue  = XYY(0.1550, 0.0700);
+    cs->white = LIBCOLOUR_ILLUMINANT_D65;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_REGULAR;
+    cs->gamma = 1 / 0.45;
+    cs->offset = 0.09929682680944;
+    cs->slope = 4.5;
+    cs->transition = 0.018053968510807;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_709:
+    cs->red   = XYY(0.6300, 0.3300);
+    cs->green = XYY(0.3000, 0.6000);
+    cs->blue  = XYY(0.1500, 0.0600);
+    cs->white = LIBCOLOUR_ILLUMINANT_D65;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_REGULAR;
+    cs->gamma = 1 / 0.45;
+    cs->offset = 0.09929682680944;
+    cs->slope = 4.5;
+    cs->transition = 0.018053968510807;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_2020:
+    cs->red   = XYY(0.7080, 0.2920);
+    cs->green = XYY(0.1700, 0.7970);
+    cs->blue  = XYY(0.1310, 0.0460);
+    cs->white = LIBCOLOUR_ILLUMINANT_D65;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_REGULAR;
+    cs->gamma = 1 / 0.45;
+    cs->offset = 0.09929682680944;
+    cs->slope = 4.5;
+    cs->transition = 0.018053968510807;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_2100_EOTF_PQ:
+  case LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_2100_OOTF_PQ:
+  case LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_2100_OETF_PQ:
+  case LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_2100_EOTF_HLG:
+  case LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_2100_OOTF_HLG:
+  case LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_2100_OETF_HLG:
+    cs->red   = XYY(0.7080, 0.2920);
+    cs->green = XYY(0.1700, 0.7970);
+    cs->blue  = XYY(0.1310, 0.0460);
+    cs->white = LIBCOLOUR_ILLUMINANT_D65;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_CUSTOM;
+    /* TODO http://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2100-0-201607-I!!PDF-E.pdf */
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_LIGHTROOM_RGB:
+    cs->red   = XYY(0.7347, 0.2653);
+    cs->green = XYY(0.1596, 0.8404);
+    cs->blue  = XYY(0.0366, 0.0001);
+    cs->white = LIBCOLOUR_ILLUMINANT_D50;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_LINEAR;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_NTSC_RGB:
+    cs->red   = XYY(0.6700, 0.3300);
+    cs->green = XYY(0.2100, 0.7100);
+    cs->blue  = XYY(0.1400, 0.0800);
+    cs->white = LIBCOLOUR_ILLUMINANT_C;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_REGULAR;
+    cs->gamma = 1 / 0.45;
+    cs->offset = 0.09929682680944;
+    cs->slope = 4.5;
+    cs->transition = 0.018053968510807;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_PAL_SECAM_RGB:
+    cs->red   = XYY(0.6400, 0.3300);
+    cs->green = XYY(0.2900, 0.6000);
+    cs->blue  = XYY(0.1500, 0.0600);
+    cs->white = LIBCOLOUR_ILLUMINANT_D65;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_REGULAR;
+    cs->gamma = 1 / 0.45;
+    cs->offset = 0.09929682680944;
+    cs->slope = 4.5;
+    cs->transition = 0.018053968510807;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_PROPHOTO_RGB:
+    cs->red   = XYY(0.7347, 0.2653);
+    cs->green = XYY(0.1596, 0.8404);
+    cs->blue  = XYY(0.0366, 0.0001);
+    cs->white = LIBCOLOUR_ILLUMINANT_D50;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_SIMPLE;
+    cs->gamma = 1.8;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_SGI_RGB:
+    cs->red   = XYY(0.6250, 0.3400);
+    cs->green = XYY(0.2800, 0.5950);
+    cs->blue  = XYY(0.1550, 0.0700);
+    cs->white = LIBCOLOUR_ILLUMINANT_D50;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_SIMPLE;
+    cs->gamma = 1.47;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_SMPTE_240M_RGB:
+    cs->red   = XYY(0.6300, 0.3400);
+    cs->green = XYY(0.3100, 0.5950);
+    cs->blue  = XYY(0.1550, 0.0700);
+    cs->white = LIBCOLOUR_ILLUMINANT_D65;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_REGULAR;
+    cs->gamma = 1 / 0.45;
+    cs->offset = 0.1115721957735072;
+    cs->slope = 4.0;
+    cs->transition = 0.022821585552393633;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_SMPTE_C_RGB:
+    cs->red   = XYY(0.6300, 0.3400);
+    cs->green = XYY(0.3100, 0.5950);
+    cs->blue  = XYY(0.1550, 0.0700);
+    cs->white = LIBCOLOUR_ILLUMINANT_D65;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_REGULAR;
+    cs->gamma = 1 / 0.45;
+    cs->offset = 0.09929682680944;
+    cs->slope = 4.5;
+    cs->transition = 0.018053968510807;
+    break;
+
+  case LIBCOLOUR_RGB_COLOUR_SPACE_WIDE_GAMUT_RGB:
+    cs->red   = XYY(0.7350, 0.2650);
+    cs->green = XYY(0.1150, 0.8260);
+    cs->blue  = XYY(0.1570, 0.0180);
+    cs->white = LIBCOLOUR_ILLUMINANT_D50;
+    cs->encoding_type = LIBCOLOUR_ENCODING_TYPE_SIMPLE;
+    cs->gamma = 2.19921875;
+    break;
+
+  default:
+    errno = EINVAL;
+    return -1;
+  }
+  if (cs->encoding_type == LIBCOLOUR_ENCODING_TYPE_REGULAR)
+    cs->transitioninv = cs->transition * cs->slope;
+  cs->colour_space = space;
+  cs->white_r = cs->white_g = cs->white_b = 1;
+  if (get_matrices(cs) || libcolour_proper(cs_))
+    return -1;
+  return 0;
+
+#undef XYY
+}
diff --git a/src/libcolour.h b/src/libcolour.h
index f579dfb..b780c71 100644
--- a/src/libcolour.h
+++ b/src/libcolour.h
@@ -22,6 +22,28 @@
 union libcolour_colour;
 
 
+#define LIBCOLOUR_ILLUMINANT_A    (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 0.44757, .y = 0.40745, .Y = 1}
+#define LIBCOLOUR_ILLUMINANT_B    (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 0.34842, .y = 0.35161, .Y = 1}
+#define LIBCOLOUR_ILLUMINANT_C    (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 0.31006, .y = 0.31616, .Y = 1}
+#define LIBCOLOUR_ILLUMINANT_D50  (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 0.34567, .y = 0.35850, .Y = 1}
+#define LIBCOLOUR_ILLUMINANT_D55  (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 0.33242, .y = 0.34743, .Y = 1}
+#define LIBCOLOUR_ILLUMINANT_D65  (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 0.31271, .y = 0.32902, .Y = 1}
+#define LIBCOLOUR_ILLUMINANT_D75  (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 0.29902, .y = 0.31485, .Y = 1}
+#define LIBCOLOUR_ILLUMINANT_E    (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 1. / 3,  .y = 1. / 3,  .Y = 1}
+#define LIBCOLOUR_ILLUMINANT_F1   (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 0.31310, .y = 0.33727, .Y = 1}
+#define LIBCOLOUR_ILLUMINANT_F2   (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 0.37208, .y = 0.37529, .Y = 1}
+#define LIBCOLOUR_ILLUMINANT_F3   (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 0.40910, .y = 0.39430, .Y = 1}
+#define LIBCOLOUR_ILLUMINANT_F4   (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 0.44018, .y = 0.40329, .Y = 1}
+#define LIBCOLOUR_ILLUMINANT_F5   (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 0.31379, .y = 0.34531, .Y = 1}
+#define LIBCOLOUR_ILLUMINANT_F6   (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 0.37790, .y = 0.38835, .Y = 1}
+#define LIBCOLOUR_ILLUMINANT_F7   (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 0.31292, .y = 0.32933, .Y = 1}
+#define LIBCOLOUR_ILLUMINANT_F8   (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 0.34588, .y = 0.35875, .Y = 1}
+#define LIBCOLOUR_ILLUMINANT_F9   (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 0.37417, .y = 0.37281, .Y = 1}
+#define LIBCOLOUR_ILLUMINANT_F10  (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 0.34609, .y = 0.35986, .Y = 1}
+#define LIBCOLOUR_ILLUMINANT_F11  (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 0.38052, .y = 0.37713, .Y = 1}
+#define LIBCOLOUR_ILLUMINANT_F12  (libcolour_ciexyy_t){.model = LIBCOLOUR_CIEXYY, .x = 0.43695, .y = 0.40441, .Y = 1}
+
+
 typedef enum libcolour_model {
   LIBCOLOUR_RGB,
   LIBCOLOUR_SRGB,
@@ -39,34 +61,52 @@ typedef enum libcolour_model {
   LIBCOLOUR_CIEUVW
 } libcolour_model_t;
 
+typedef enum libcolour_encoding_type {
+  LIBCOLOUR_ENCODING_TYPE_LINEAR,
+  LIBCOLOUR_ENCODING_TYPE_SIMPLE,
+  LIBCOLOUR_ENCODING_TYPE_REGULAR,
+  LIBCOLOUR_ENCODING_TYPE_CUSTOM
+} libcolour_encoding_type;
+
 
 typedef enum libcolour_rgb_colour_space {
+  LIBCOLOUR_RGB_COLOUR_SPACE_CUSTOM_FROM_MEASUREMENTS,
+  LIBCOLOUR_RGB_COLOUR_SPACE_CUSTOM_FROM_MATRIX,
+  LIBCOLOUR_RGB_COLOUR_SPACE_CUSTOM_FROM_INV_MATRIX,
+  LIBCOLOUR_RGB_COLOUR_SPACE_SRGB,
+  LIBCOLOUR_RGB_COLOUR_SPACE_ADOBE_RGB,
+  LIBCOLOUR_RGB_COLOUR_SPACE_APPLE_RGB,
+  LIBCOLOUR_RGB_COLOUR_SPACE_BEST_RGB,
+  LIBCOLOUR_RGB_COLOUR_SPACE_BETA_RGB,
+  LIBCOLOUR_RGB_COLOUR_SPACE_BRUCE_RGB,
+  LIBCOLOUR_RGB_COLOUR_SPACE_CIE_RGB,
+  LIBCOLOUR_RGB_COLOUR_SPACE_COLORMATCH_RGB,
+  LIBCOLOUR_RGB_COLOUR_SPACE_DCI_P3_D65,
+  LIBCOLOUR_RGB_COLOUR_SPACE_DCI_P3_THEATER,
+  LIBCOLOUR_RGB_COLOUR_SPACE_DON_RGB_4,
+  LIBCOLOUR_RGB_COLOUR_SPACE_ECI_RGB_V2,
+  LIBCOLOUR_RGB_COLOUR_SPACE_EKTA_SPACE_PS5,
+  LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_601_625_LINE,
+  LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_601_525_LINE,
+  LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_709,
+  LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_2020,
+  LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_2100_EOTF_PQ,
+  LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_2100_OOTF_PQ,
+  LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_2100_OETF_PQ,
+  LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_2100_EOTF_HLG,
+  LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_2100_OOTF_HLG,
+  LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_2100_OETF_HLG,
+  LIBCOLOUR_RGB_COLOUR_SPACE_LIGHTROOM_RGB,
+  LIBCOLOUR_RGB_COLOUR_SPACE_NTSC_RGB,
+  LIBCOLOUR_RGB_COLOUR_SPACE_PAL_SECAM_RGB,
+  LIBCOLOUR_RGB_COLOUR_SPACE_PROPHOTO_RGB,
+  LIBCOLOUR_RGB_COLOUR_SPACE_SGI_RGB,
+  LIBCOLOUR_RGB_COLOUR_SPACE_SMPTE_240M_RGB,
+  LIBCOLOUR_RGB_COLOUR_SPACE_SMPTE_C_RGB,
+  LIBCOLOUR_RGB_COLOUR_SPACE_WIDE_GAMUT_RGB
 } libcolour_rgb_colour_space_t;
 
 
-typedef struct libcolour_rgb {
-  enum libcolour_model model;
-  double R;
-  double G;
-  double B;
-  int with_gamma;
-  int regular_gamma;
-  double gamma;
-  union libcolour_colour red;
-  union libcolour_colour green;
-  union libcolour_colour blue;
-  union libcolour_colour white;
-  double (*to_encoded_red)(double);
-  double (*to_decoded_red)(double);
-  double (*to_encoded_green)(double);
-  double (*to_decoded_green)(double);
-  double (*to_encoded_blue)(double);
-  double (*to_decoded_blue)(double);
-  double M[3][3];
-  double Minv[3][3];
-  enum libcolour_rgb_colour_space colour_space;
-} libcolour_srgb_t;
-
 typedef struct libcolour_srgb {
   enum libcolour_model model;
   double R;
@@ -93,25 +133,9 @@ typedef struct libcolour_cielab {
   enum libcolour_model model;
   double L;
   double a;
-  double n;
+  double b;
 } libcolour_cielab_t;
 
-typedef struct libcolour_cieluv {
-  enum libcolour_model model;
-  double L;
-  double u;
-  double v;
-  union libcolour_colour white;
-} libcolour_cieluv_t;
-
-typedef struct libcolour_cielch {
-  enum libcolour_model model;
-  double L;
-  double C;
-  double h;
-  union libcolour_colour white;
-} libcolour_cielch_t;
-
 typedef struct libcolour_yiq {
   enum libcolour_model model;
   double Y;
@@ -163,6 +187,56 @@ typedef struct libcolour_cieuvw {
   double v0;
 } libcolour_cieuvw_t;
 
+typedef struct libcolour_cieluv {
+  enum libcolour_model model;
+  double L;
+  double u;
+  double v;
+  struct libcolour_ciexyz white;
+} libcolour_cieluv_t;
+
+typedef struct libcolour_cielch {
+  enum libcolour_model model;
+  double L;
+  double C;
+  double h;
+  struct libcolour_ciexyz white;
+} libcolour_cielch_t;
+
+typedef struct libcolour_rgb {
+  enum libcolour_model model;
+  double R;
+  double G;
+  double B;
+  int with_gamma;
+  enum libcolour_encoding_type encoding_type;
+  double gamma;
+  double offset;
+  double slope;
+  double transition;
+  double transitioninv;
+  struct libcolour_ciexyy red;
+  struct libcolour_ciexyy green;
+  struct libcolour_ciexyy blue;
+  struct libcolour_ciexyy white;
+  double (*to_encoded_red)(double);
+  double (*to_decoded_red)(double);
+  double (*to_encoded_green)(double);
+  double (*to_decoded_green)(double);
+  double (*to_encoded_blue)(double);
+  double (*to_decoded_blue)(double);
+  double M[3][3];
+  double Minv[3][3];
+  double white_r;
+  double white_g;
+  double white_b;
+  enum libcolour_rgb_colour_space colour_space;
+} libcolour_rgb_t;
+
+/* TODO LMS, L'M'S', ICtCp */
+/* TODO generic additive colour space */
+/* TODO generic subtractive colour space */
+
 
 typedef union libcolour_colour {
   enum libcolour_model model;
@@ -189,6 +263,8 @@ double libcolour_srgb_encode(double);
 double libcolour_srgb_decode(double);
 int libcolour_delta_e(const libcolour_colour_t*, const libcolour_colour_t*, double*);
 int libcolour_proper(libcolour_colour_t*);
+int libcolour_get_rgb_colour_space(libcolour_colour_t*, libcolour_rgb_colour_space_t);
+/* TODO (un)marshal */
 
 
 
-- 
cgit v1.2.3-70-g09d2