add hsluv.h

This commit is contained in:
nakst 2021-09-29 09:17:47 +01:00
parent fe5b3adb7b
commit 079667b97e
11 changed files with 585 additions and 13 deletions

View File

@ -24,6 +24,7 @@ This project also include the work of other projects.
All trademarks are registered trademarks of their respective owners. All trademarks are registered trademarks of their respective owners.
They licenses may be found in the following files: They licenses may be found in the following files:
- util/nanosvg.h - util/nanosvg.h
- util/hsluv.h
- shared/stb_image.h, shared/stb_sprintf.h, shared/stb_ds.h and util/stb_truetype.h - shared/stb_image.h, shared/stb_sprintf.h, shared/stb_ds.h and util/stb_truetype.h
- res/Fonts/Hack License.txt, res/Fonts/Inter License.txt - res/Fonts/Hack License.txt, res/Fonts/Inter License.txt
- res/Icons/elementary Icons License.txt - res/Icons/elementary Icons License.txt

View File

@ -2,4 +2,4 @@
name=Converter name=Converter
[build] [build]
source=apps/converter.cpp source=apps/samples/converter.cpp

View File

@ -0,0 +1,85 @@
#include <essence.h>
EsInstance *instance;
EsElement *canvas;
float spriteX;
float spriteY;
bool isLeftHeld;
bool isRightHeld;
bool isUpHeld;
bool isDownHeld;
void GameRender(EsPainter *painter, EsRectangle bounds) {
// Fill with black.
EsDrawBlock(painter, bounds, 0xFF000000);
// Draw the sprite.
EsRectangle spriteBounds = ES_RECT_4PD(spriteX, spriteY, 30, 30);
EsDrawRectangle(painter, EsRectangleTranslate(spriteBounds, bounds), 0xFFFF0000, 0xFF800000, ES_RECT_1(2));
}
void GameUpdate(float deltaMs) {
// Move the sprite if an arrow key is held.
float directionX = isLeftHeld ? -1 : isRightHeld ? 1 : 0;
spriteX += directionX * deltaMs * 0.25f;
float directionY = isUpHeld ? -1 : isDownHeld ? 1 : 0;
spriteY += directionY * deltaMs * 0.25f;
}
int CanvasMessage(EsElement *, EsMessage *message) {
if (message->type == ES_MSG_PAINT) {
// Get the bounds we should draw on.
EsRectangle bounds = EsPainterBoundsInset(message->painter);
// Render the game.
GameRender(message->painter, bounds);
} else if (message->type == ES_MSG_ANIMATE) {
// Keep sending animation messages.
message->animate.complete = false;
// Update the game using the time delta (milliseconds) provided.
GameUpdate(message->animate.deltaMs);
// Ask to repaint the canvas.
EsElementRepaint(canvas, nullptr);
} else if (message->type == ES_MSG_KEY_DOWN || message->type == ES_MSG_KEY_UP) {
// Track which keys are being held.
if (message->keyboard.scancode == ES_SCANCODE_LEFT_ARROW) {
isLeftHeld = message->type == ES_MSG_KEY_DOWN;
} else if (message->keyboard.scancode == ES_SCANCODE_RIGHT_ARROW) {
isRightHeld = message->type == ES_MSG_KEY_DOWN;
} else if (message->keyboard.scancode == ES_SCANCODE_UP_ARROW) {
isUpHeld = message->type == ES_MSG_KEY_DOWN;
} else if (message->keyboard.scancode == ES_SCANCODE_DOWN_ARROW) {
isDownHeld = message->type == ES_MSG_KEY_DOWN;
}
}
return 0;
}
void _start() {
_init();
while (true) {
EsMessage *message = EsMessageReceive();
if (message->type == ES_MSG_INSTANCE_CREATE) {
instance = EsInstanceCreate(message, "Game Loop");
// Create a canvas to draw on, and make it fill the window.
canvas = EsCustomElementCreate(instance->window, ES_CELL_FILL | ES_ELEMENT_FOCUSABLE, ES_STYLE_PANEL_WINDOW_DIVIDER);
// Set the message callback.
canvas->messageUser = CanvasMessage;
// Send animation messages to the canvas.
EsElementStartAnimating(canvas);
// Focus the canvas to receive keyboard messages.
EsElementFocus(canvas);
}
}
}

View File

@ -0,0 +1,5 @@
[general]
name=Game Loop
[build]
source=apps/samples/game_loop.cpp

View File

@ -2,4 +2,4 @@
name=Hello name=Hello
[build] [build]
source=apps/hello.c source=apps/samples/hello.c

View File

@ -2221,13 +2221,17 @@ function bool EsUTF8IsValid(const char *input, ptrdiff_t bytes); // Does not che
function int EsCRTabs(int n); function int EsCRTabs(int n);
function float EsCRTacosf(float x); function float EsCRTacosf(float x);
function float EsCRTasinf(float x); function float EsCRTasinf(float x);
function double EsCRTatan2(double y, double x);
function float EsCRTatan2f(float y, float x); function float EsCRTatan2f(float y, float x);
function float EsCRTatanf(float x); function float EsCRTatanf(float x);
function int EsCRTatoi(const char *string); function int EsCRTatoi(const char *string);
function void *EsCRTbsearch(const void *key, const void *base, size_t num, size_t size, EsCRTComparisonCallback compar); function void *EsCRTbsearch(const void *key, const void *base, size_t num, size_t size, EsCRTComparisonCallback compar);
function void *EsCRTcalloc(size_t num, size_t size); function void *EsCRTcalloc(size_t num, size_t size);
function double EsCRTcbrt(double x);
function float EsCRTcbrtf(float x);
function double EsCRTceil(double x); function double EsCRTceil(double x);
function float EsCRTceilf(float x); function float EsCRTceilf(float x);
function double EsCRTcos(double x);
function float EsCRTcosf(float x); function float EsCRTcosf(float x);
function double EsCRTexp(double x); function double EsCRTexp(double x);
function float EsCRTexp2f(float x); function float EsCRTexp2f(float x);
@ -2235,6 +2239,7 @@ function double EsCRTfabs(double x);
function float EsCRTfabsf(float x); function float EsCRTfabsf(float x);
function double EsCRTfloor(double x); function double EsCRTfloor(double x);
function float EsCRTfloorf(float x); function float EsCRTfloorf(float x);
function double EsCRTfmod(double x, double y);
function float EsCRTfmodf(float x, float y); function float EsCRTfmodf(float x, float y);
function void EsCRTfree(void *ptr); function void EsCRTfree(void *ptr);
function char *EsCRTgetenv(const char *name); function char *EsCRTgetenv(const char *name);
@ -2252,10 +2257,12 @@ function int EsCRTmemcmp(const void *s1, const void *s2, size_t n);
function void *EsCRTmemcpy(void *dest, const void *src, size_t n); function void *EsCRTmemcpy(void *dest, const void *src, size_t n);
function void *EsCRTmemmove(void *dest, const void *src, size_t n); function void *EsCRTmemmove(void *dest, const void *src, size_t n);
function void *EsCRTmemset(void *s, int c, size_t n); function void *EsCRTmemset(void *s, int c, size_t n);
function double EsCRTpow(double x, double y);
function float EsCRTpowf(float x, float y); function float EsCRTpowf(float x, float y);
function void EsCRTqsort(void *_base, size_t nmemb, size_t size, EsCRTComparisonCallback compar); function void EsCRTqsort(void *_base, size_t nmemb, size_t size, EsCRTComparisonCallback compar);
function int EsCRTrand(); function int EsCRTrand();
function void *EsCRTrealloc(void *ptr, size_t size); function void *EsCRTrealloc(void *ptr, size_t size);
function double EsCRTsin(double x);
function float EsCRTsinf(float x); function float EsCRTsinf(float x);
function int EsCRTsnprintf(char *buffer, size_t bufferSize, const char *format, ...); function int EsCRTsnprintf(char *buffer, size_t bufferSize, const char *format, ...);
function int EsCRTsprintf(char *buffer, const char *format, ...); function int EsCRTsprintf(char *buffer, const char *format, ...);

View File

@ -326,13 +326,17 @@ extern "C" void *EsBufferWrite(EsBuffer *buffer, const void *source, size_t writ
#define acosf EsCRTacosf #define acosf EsCRTacosf
#define asinf EsCRTasinf #define asinf EsCRTasinf
#define assert EsCRTassert #define assert EsCRTassert
#define atan2 EsCRTatan2
#define atan2f EsCRTatan2f #define atan2f EsCRTatan2f
#define atanf EsCRTatanf #define atanf EsCRTatanf
#define atoi EsCRTatoi #define atoi EsCRTatoi
#define bsearch EsCRTbsearch #define bsearch EsCRTbsearch
#define calloc EsCRTcalloc #define calloc EsCRTcalloc
#define cbrt EsCRTcbrt
#define cbrtf EsCRTcbrtf
#define ceil EsCRTceil #define ceil EsCRTceil
#define ceilf EsCRTceilf #define ceilf EsCRTceilf
#define cos EsCRTcos
#define cosf EsCRTcosf #define cosf EsCRTcosf
#define exp EsCRTexp #define exp EsCRTexp
#define exp2f EsCRTexp2f #define exp2f EsCRTexp2f
@ -340,6 +344,7 @@ extern "C" void *EsBufferWrite(EsBuffer *buffer, const void *source, size_t writ
#define fabsf EsCRTfabsf #define fabsf EsCRTfabsf
#define floor EsCRTfloor #define floor EsCRTfloor
#define floorf EsCRTfloorf #define floorf EsCRTfloorf
#define fmod EsCRTfmod
#define fmodf EsCRTfmodf #define fmodf EsCRTfmodf
#define free EsCRTfree #define free EsCRTfree
#define getenv EsCRTgetenv #define getenv EsCRTgetenv
@ -355,10 +360,12 @@ extern "C" void *EsBufferWrite(EsBuffer *buffer, const void *source, size_t writ
#define memcpy EsCRTmemcpy #define memcpy EsCRTmemcpy
#define memmove EsCRTmemmove #define memmove EsCRTmemmove
#define memset EsCRTmemset #define memset EsCRTmemset
#define pow EsCRTpow
#define powf EsCRTpowf #define powf EsCRTpowf
#define qsort EsCRTqsort #define qsort EsCRTqsort
#define rand EsCRTrand #define rand EsCRTrand
#define realloc EsCRTrealloc #define realloc EsCRTrealloc
#define sin EsCRTsin
#define sinf EsCRTsinf #define sinf EsCRTsinf
#define snprintf EsCRTsnprintf #define snprintf EsCRTsnprintf
#define sprintf EsCRTsprintf #define sprintf EsCRTsprintf

View File

@ -836,6 +836,14 @@ float EsCRTpowf(float x, float y) {
return EsCRTexp2f(y * EsCRTlog2f(x)); return EsCRTexp2f(y * EsCRTlog2f(x));
} }
double EsCRTcbrt(double x) {
return EsCRTpow(x, 1.0 / 3.0);
}
float EsCRTcbrtf(float x) {
return EsCRTpowf(x, 1.0f / 3.0f);
}
double EsCRTexp(double x) { double EsCRTexp(double x) {
return EsCRTexp2(x * 1.4426950408889634073); return EsCRTexp2(x * 1.4426950408889634073);
} }

View File

@ -363,6 +363,7 @@ EsWindowAddSizeAlternative=361
EsMenuAddCommandsFromToolbar=362 EsMenuAddCommandsFromToolbar=362
EsPanelStartMovementAnimation=363 EsPanelStartMovementAnimation=363
EsElementStartTransition=364 EsElementStartTransition=364
EsCRTatan2=365
EsFileWriteAllFromHandle=366 EsFileWriteAllFromHandle=366
EsFileWriteAllGatherFromHandle=367 EsFileWriteAllGatherFromHandle=367
EsButtonSetIconFromBits=368 EsButtonSetIconFromBits=368
@ -464,3 +465,9 @@ EsDialogShow=463
EsInstanceSetModified=464 EsInstanceSetModified=464
EsFileMenuAddToToolbar=465 EsFileMenuAddToToolbar=465
EsFileMenuCreate=466 EsFileMenuCreate=466
EsCRTcos=467
EsCRTsin=468
EsCRTfmod=469
EsCRTpow=470
EsCRTcbrt=471
EsCRTcbrtf=472

View File

@ -5,6 +5,7 @@
#include <stdio.h> #include <stdio.h>
#include <math.h> #include <math.h>
#endif #endif
#include "hsluv.h"
// x86_64-w64-mingw32-gcc -O3 -o bin/designer2.exe -D UI_WINDOWS util/designer2.cpp -DUNICODE -lgdi32 -luser32 -lkernel32 -Wl,--subsystem,windows -fno-exceptions -fno-rtti // x86_64-w64-mingw32-gcc -O3 -o bin/designer2.exe -D UI_WINDOWS util/designer2.cpp -DUNICODE -lgdi32 -luser32 -lkernel32 -Wl,--subsystem,windows -fno-exceptions -fno-rtti
@ -16,12 +17,10 @@
// TODO Import and reorganize old theming data. // TODO Import and reorganize old theming data.
// Additional features: // Additional features:
// TODO Better color modification space -- HSLuv.
// TODO Selecting multiple objects. // TODO Selecting multiple objects.
// TODO Resizing objects? // TODO Resizing objects?
// TODO Find object in graph by name. // TODO Find object in graph by name.
// TODO Prototyping display. (Multiple instances of each object can be placed, resized and interacted with). // TODO Prototyping display. (Multiple instances of each object can be placed, resized and interacted with).
// TODO Running on Essence: x86_64-essence-g++ -o root/designer2 -D UI_ESSENCE util/designer2.cpp
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
@ -1123,8 +1122,10 @@ void InspectorPopulate() {
InspectorAddLink(object, "Color:", "color"); InspectorAddLink(object, "Color:", "color");
} else if (object->type == OBJ_MOD_COLOR) { } else if (object->type == OBJ_MOD_COLOR) {
InspectorAddLink(object, "Base color:", "base"); InspectorAddLink(object, "Base color:", "base");
UILabelCreate(0, 0, "Brightness:", -1); UILabelCreate(0, 0, "Brightness (%):", -1);
InspectorBind(&UITextboxCreate(0, UI_ELEMENT_H_FILL)->e, object->id, "brightness", INSPECTOR_INTEGER_TEXTBOX); InspectorBind(&UITextboxCreate(0, UI_ELEMENT_H_FILL)->e, object->id, "brightness", INSPECTOR_INTEGER_TEXTBOX);
UILabelCreate(0, 0, "Hue shift (deg):", -1);
InspectorBind(&UITextboxCreate(0, UI_ELEMENT_H_FILL)->e, object->id, "hueShift", INSPECTOR_INTEGER_TEXTBOX);
} else if (object->type == OBJ_MOD_MULTIPLY) { } else if (object->type == OBJ_MOD_MULTIPLY) {
InspectorAddLink(object, "Base integer:", "base"); InspectorAddLink(object, "Base integer:", "base");
UILabelCreate(0, 0, "Factor (%):", -1); UILabelCreate(0, 0, "Factor (%):", -1);
@ -1239,13 +1240,15 @@ uint32_t CanvasGetColorFromPaint(Object *object, int depth = 0) {
uint32_t base = CanvasGetColorFromPaint(ObjectFind(property ? property->object : 0), depth + 1); uint32_t base = CanvasGetColorFromPaint(ObjectFind(property ? property->object : 0), depth + 1);
uint32_t alpha = base & 0xFF000000; uint32_t alpha = base & 0xFF000000;
int32_t brightness = PropertyReadInt32(object, "brightness"); int32_t brightness = PropertyReadInt32(object, "brightness");
float hue, saturation, value; int32_t hueShift = PropertyReadInt32(object, "hueShift");
UIColorToHSV(base, &hue, &saturation, &value); double hue, saturation, luminosity, red, green, blue;
value += brightness / 100.0f; rgb2hsluv(UI_COLOR_RED_F(base), UI_COLOR_GREEN_F(base), UI_COLOR_BLUE_F(base), &hue, &saturation, &luminosity);
if (value < 0) value = 0; luminosity += luminosity * brightness / 100.0f;
if (value > 1) value = 1; hue = fmod(hue + hueShift, 360.0);
UIColorToRGB(hue, saturation, value, &base); if (luminosity < 0.0) luminosity = 0.0;
return base | alpha; if (luminosity > 100.0) luminosity = 100.0;
hsluv2rgb(hue, saturation, luminosity, &red, &green, &blue);
return UI_COLOR_FROM_FLOAT(red, green, blue) | alpha;
} else { } else {
return 0; return 0;
} }
@ -1530,7 +1533,7 @@ int main() {
#else #else
UIButtonCreate(0, 0, "Save", -1)->invoke = DocumentSave; UIButtonCreate(0, 0, "Save", -1)->invoke = DocumentSave;
#endif #endif
UIButtonCreate(0, 0, "Add object", -1)->invoke = ObjectAddCommand; UIButtonCreate(0, 0, "Add object...", -1)->invoke = ObjectAddCommand;
UISpacerCreate(0, 0, 15, 0); UISpacerCreate(0, 0, 15, 0);
labelMessage = UILabelCreate(0, UI_ELEMENT_H_FILL, 0, 0); labelMessage = UILabelCreate(0, UI_ELEMENT_H_FILL, 0, 0);
UIParentPop(); UIParentPop();

449
util/hsluv.h Normal file
View File

@ -0,0 +1,449 @@
/*
* HSLuv-C: Human-friendly HSL
* <https://github.com/hsluv/hsluv-c>
* <https://www.hsluv.org/>
*
* Copyright (c) 2015 Alexei Boronine (original idea, JavaScript implementation)
* Copyright (c) 2015 Roger Tallada (Obj-C implementation)
* Copyright (c) 2017 Martin Mitas (C implementation, based on Obj-C implementation)
*
* 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 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.
*/
#include <float.h>
typedef struct Triplet_tag Triplet;
struct Triplet_tag {
double a;
double b;
double c;
};
/* for RGB */
static const Triplet m[3] = {
{ 3.24096994190452134377, -1.53738317757009345794, -0.49861076029300328366 },
{ -0.96924363628087982613, 1.87596750150772066772, 0.04155505740717561247 },
{ 0.05563007969699360846, -0.20397695888897656435, 1.05697151424287856072 }
};
/* for XYZ */
static const Triplet m_inv[3] = {
{ 0.41239079926595948129, 0.35758433938387796373, 0.18048078840183428751 },
{ 0.21263900587151035754, 0.71516867876775592746, 0.07219231536073371500 },
{ 0.01933081871559185069, 0.11919477979462598791, 0.95053215224966058086 }
};
static const double ref_u = 0.19783000664283680764;
static const double ref_v = 0.46831999493879100370;
static const double kappa = 903.29629629629629629630;
static const double epsilon = 0.00885645167903563082;
typedef struct Bounds_tag Bounds;
struct Bounds_tag {
double a;
double b;
};
static void
get_bounds(double l, Bounds bounds[6])
{
double tl = l + 16.0;
double sub1 = (tl * tl * tl) / 1560896.0;
double sub2 = (sub1 > epsilon ? sub1 : (l / kappa));
int channel;
int t;
for(channel = 0; channel < 3; channel++) {
double m1 = m[channel].a;
double m2 = m[channel].b;
double m3 = m[channel].c;
for (t = 0; t < 2; t++) {
double top1 = (284517.0 * m1 - 94839.0 * m3) * sub2;
double top2 = (838422.0 * m3 + 769860.0 * m2 + 731718.0 * m1) * l * sub2 - 769860.0 * t * l;
double bottom = (632260.0 * m3 - 126452.0 * m2) * sub2 + 126452.0 * t;
bounds[channel * 2 + t].a = top1 / bottom;
bounds[channel * 2 + t].b = top2 / bottom;
}
}
}
static double
intersect_line_line(const Bounds* line1, const Bounds* line2)
{
return (line1->b - line2->b) / (line2->a - line1->a);
}
static double
dist_from_pole_squared(double x, double y)
{
return x * x + y * y;
}
static double
ray_length_until_intersect(double theta, const Bounds* line)
{
return line->b / (sin(theta) - line->a * cos(theta));
}
static double
max_safe_chroma_for_l(double l)
{
double min_len_squared = DBL_MAX;
Bounds bounds[6];
int i;
get_bounds(l, bounds);
for(i = 0; i < 6; i++) {
double m1 = bounds[i].a;
double b1 = bounds[i].b;
/* x where line intersects with perpendicular running though (0, 0) */
Bounds line2 = { -1.0 / m1, 0.0 };
double x = intersect_line_line(&bounds[i], &line2);
double distance = dist_from_pole_squared(x, b1 + x * m1);
if(distance < min_len_squared)
min_len_squared = distance;
}
return sqrt(min_len_squared);
}
static double
max_chroma_for_lh(double l, double h)
{
double min_len = DBL_MAX;
double hrad = h * 0.01745329251994329577; /* (2 * pi / 360) */
Bounds bounds[6];
int i;
get_bounds(l, bounds);
for(i = 0; i < 6; i++) {
double len = ray_length_until_intersect(hrad, &bounds[i]);
if(len >= 0 && len < min_len)
min_len = len;
}
return min_len;
}
static double
dot_product(const Triplet* t1, const Triplet* t2)
{
return (t1->a * t2->a + t1->b * t2->b + t1->c * t2->c);
}
/* Used for rgb conversions */
static double
from_linear(double c)
{
if(c <= 0.0031308)
return 12.92 * c;
else
return 1.055 * pow(c, 1.0 / 2.4) - 0.055;
}
static double
to_linear(double c)
{
if (c > 0.04045)
return pow((c + 0.055) / 1.055, 2.4);
else
return c / 12.92;
}
static void
xyz2rgb(Triplet* in_out)
{
double r = from_linear(dot_product(&m[0], in_out));
double g = from_linear(dot_product(&m[1], in_out));
double b = from_linear(dot_product(&m[2], in_out));
in_out->a = r;
in_out->b = g;
in_out->c = b;
}
static void
rgb2xyz(Triplet* in_out)
{
Triplet rgbl = { to_linear(in_out->a), to_linear(in_out->b), to_linear(in_out->c) };
double x = dot_product(&m_inv[0], &rgbl);
double y = dot_product(&m_inv[1], &rgbl);
double z = dot_product(&m_inv[2], &rgbl);
in_out->a = x;
in_out->b = y;
in_out->c = z;
}
/* https://en.wikipedia.org/wiki/CIELUV
* In these formulas, Yn refers to the reference white point. We are using
* illuminant D65, so Yn (see refY in Maxima file) equals 1. The formula is
* simplified accordingly.
*/
static double
y2l(double y)
{
if(y <= epsilon)
return y * kappa;
else
return 116.0 * cbrt(y) - 16.0;
}
static double
l2y(double l)
{
if(l <= 8.0) {
return l / kappa;
} else {
double x = (l + 16.0) / 116.0;
return (x * x * x);
}
}
static void
xyz2luv(Triplet* in_out)
{
double var_u = (4.0 * in_out->a) / (in_out->a + (15.0 * in_out->b) + (3.0 * in_out->c));
double var_v = (9.0 * in_out->b) / (in_out->a + (15.0 * in_out->b) + (3.0 * in_out->c));
double l = y2l(in_out->b);
double u = 13.0 * l * (var_u - ref_u);
double v = 13.0 * l * (var_v - ref_v);
in_out->a = l;
if(l < 0.00000001) {
in_out->b = 0.0;
in_out->c = 0.0;
} else {
in_out->b = u;
in_out->c = v;
}
}
static void
luv2xyz(Triplet* in_out)
{
if(in_out->a <= 0.00000001) {
/* Black will create a divide-by-zero error. */
in_out->a = 0.0;
in_out->b = 0.0;
in_out->c = 0.0;
return;
}
double var_u = in_out->b / (13.0 * in_out->a) + ref_u;
double var_v = in_out->c / (13.0 * in_out->a) + ref_v;
double y = l2y(in_out->a);
double x = -(9.0 * y * var_u) / ((var_u - 4.0) * var_v - var_u * var_v);
double z = (9.0 * y - (15.0 * var_v * y) - (var_v * x)) / (3.0 * var_v);
in_out->a = x;
in_out->b = y;
in_out->c = z;
}
static void
luv2lch(Triplet* in_out)
{
double l = in_out->a;
double u = in_out->b;
double v = in_out->c;
double h;
double c = sqrt(u * u + v * v);
/* Grays: disambiguate hue */
if(c < 0.00000001) {
h = 0;
} else {
h = atan2(v, u) * 57.29577951308232087680; /* (180 / pi) */
if(h < 0.0)
h += 360.0;
}
in_out->a = l;
in_out->b = c;
in_out->c = h;
}
static void
lch2luv(Triplet* in_out)
{
double hrad = in_out->c * 0.01745329251994329577; /* (pi / 180.0) */
double u = cos(hrad) * in_out->b;
double v = sin(hrad) * in_out->b;
in_out->b = u;
in_out->c = v;
}
static void
hsluv2lch(Triplet* in_out)
{
double h = in_out->a;
double s = in_out->b;
double l = in_out->c;
double c;
/* White and black: disambiguate chroma */
if(l > 99.9999999 || l < 0.00000001)
c = 0.0;
else
c = max_chroma_for_lh(l, h) / 100.0 * s;
/* Grays: disambiguate hue */
if (s < 0.00000001)
h = 0.0;
in_out->a = l;
in_out->b = c;
in_out->c = h;
}
static void
lch2hsluv(Triplet* in_out)
{
double l = in_out->a;
double c = in_out->b;
double h = in_out->c;
double s;
/* White and black: disambiguate saturation */
if(l > 99.9999999 || l < 0.00000001)
s = 0.0;
else
s = c / max_chroma_for_lh(l, h) * 100.0;
/* Grays: disambiguate hue */
if (c < 0.00000001)
h = 0.0;
in_out->a = h;
in_out->b = s;
in_out->c = l;
}
static void
hpluv2lch(Triplet* in_out)
{
double h = in_out->a;
double s = in_out->b;
double l = in_out->c;
double c;
/* White and black: disambiguate chroma */
if(l > 99.9999999 || l < 0.00000001)
c = 0.0;
else
c = max_safe_chroma_for_l(l) / 100.0 * s;
/* Grays: disambiguate hue */
if (s < 0.00000001)
h = 0.0;
in_out->a = l;
in_out->b = c;
in_out->c = h;
}
static void
lch2hpluv(Triplet* in_out)
{
double l = in_out->a;
double c = in_out->b;
double h = in_out->c;
double s;
/* White and black: disambiguate saturation */
if (l > 99.9999999 || l < 0.00000001)
s = 0.0;
else
s = c / max_safe_chroma_for_l(l) * 100.0;
/* Grays: disambiguate hue */
if (c < 0.00000001)
h = 0.0;
in_out->a = h;
in_out->b = s;
in_out->c = l;
}
void
hsluv2rgb(double h, double s, double l, double* pr, double* pg, double* pb)
{
Triplet tmp = { h, s, l };
hsluv2lch(&tmp);
lch2luv(&tmp);
luv2xyz(&tmp);
xyz2rgb(&tmp);
*pr = tmp.a;
*pg = tmp.b;
*pb = tmp.c;
}
void
hpluv2rgb(double h, double s, double l, double* pr, double* pg, double* pb)
{
Triplet tmp = { h, s, l };
hpluv2lch(&tmp);
lch2luv(&tmp);
luv2xyz(&tmp);
xyz2rgb(&tmp);
*pr = tmp.a;
*pg = tmp.b;
*pb = tmp.c;
}
void
rgb2hsluv(double r, double g, double b, double* ph, double* ps, double* pl)
{
Triplet tmp = { r, g, b };
rgb2xyz(&tmp);
xyz2luv(&tmp);
luv2lch(&tmp);
lch2hsluv(&tmp);
*ph = tmp.a;
*ps = tmp.b;
*pl = tmp.c;
}
void
rgb2hpluv(double r, double g, double b, double* ph, double* ps, double* pl)
{
Triplet tmp = { r, g, b };
rgb2xyz(&tmp);
xyz2luv(&tmp);
luv2lch(&tmp);
lch2hpluv(&tmp);
*ph = tmp.a;
*ps = tmp.b;
*pl = tmp.c;
}