mirror of https://gitlab.com/nakst/essence
add hsluv.h
This commit is contained in:
parent
fe5b3adb7b
commit
079667b97e
|
@ -24,6 +24,7 @@ This project also include the work of other projects.
|
|||
All trademarks are registered trademarks of their respective owners.
|
||||
They licenses may be found in the following files:
|
||||
- util/nanosvg.h
|
||||
- util/hsluv.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/Icons/elementary Icons License.txt
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
name=Converter
|
||||
|
||||
[build]
|
||||
source=apps/converter.cpp
|
||||
source=apps/samples/converter.cpp
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
[general]
|
||||
name=Game Loop
|
||||
|
||||
[build]
|
||||
source=apps/samples/game_loop.cpp
|
|
@ -2,4 +2,4 @@
|
|||
name=Hello
|
||||
|
||||
[build]
|
||||
source=apps/hello.c
|
||||
source=apps/samples/hello.c
|
||||
|
|
|
@ -2221,13 +2221,17 @@ function bool EsUTF8IsValid(const char *input, ptrdiff_t bytes); // Does not che
|
|||
function int EsCRTabs(int n);
|
||||
function float EsCRTacosf(float x);
|
||||
function float EsCRTasinf(float x);
|
||||
function double EsCRTatan2(double y, double x);
|
||||
function float EsCRTatan2f(float y, float x);
|
||||
function float EsCRTatanf(float x);
|
||||
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 *EsCRTcalloc(size_t num, size_t size);
|
||||
function double EsCRTcbrt(double x);
|
||||
function float EsCRTcbrtf(float x);
|
||||
function double EsCRTceil(double x);
|
||||
function float EsCRTceilf(float x);
|
||||
function double EsCRTcos(double x);
|
||||
function float EsCRTcosf(float x);
|
||||
function double EsCRTexp(double x);
|
||||
function float EsCRTexp2f(float x);
|
||||
|
@ -2235,6 +2239,7 @@ function double EsCRTfabs(double x);
|
|||
function float EsCRTfabsf(float x);
|
||||
function double EsCRTfloor(double x);
|
||||
function float EsCRTfloorf(float x);
|
||||
function double EsCRTfmod(double x, double y);
|
||||
function float EsCRTfmodf(float x, float y);
|
||||
function void EsCRTfree(void *ptr);
|
||||
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 *EsCRTmemmove(void *dest, const void *src, 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 void EsCRTqsort(void *_base, size_t nmemb, size_t size, EsCRTComparisonCallback compar);
|
||||
function int EsCRTrand();
|
||||
function void *EsCRTrealloc(void *ptr, size_t size);
|
||||
function double EsCRTsin(double x);
|
||||
function float EsCRTsinf(float x);
|
||||
function int EsCRTsnprintf(char *buffer, size_t bufferSize, const char *format, ...);
|
||||
function int EsCRTsprintf(char *buffer, const char *format, ...);
|
||||
|
|
|
@ -326,13 +326,17 @@ extern "C" void *EsBufferWrite(EsBuffer *buffer, const void *source, size_t writ
|
|||
#define acosf EsCRTacosf
|
||||
#define asinf EsCRTasinf
|
||||
#define assert EsCRTassert
|
||||
#define atan2 EsCRTatan2
|
||||
#define atan2f EsCRTatan2f
|
||||
#define atanf EsCRTatanf
|
||||
#define atoi EsCRTatoi
|
||||
#define bsearch EsCRTbsearch
|
||||
#define calloc EsCRTcalloc
|
||||
#define cbrt EsCRTcbrt
|
||||
#define cbrtf EsCRTcbrtf
|
||||
#define ceil EsCRTceil
|
||||
#define ceilf EsCRTceilf
|
||||
#define cos EsCRTcos
|
||||
#define cosf EsCRTcosf
|
||||
#define exp EsCRTexp
|
||||
#define exp2f EsCRTexp2f
|
||||
|
@ -340,6 +344,7 @@ extern "C" void *EsBufferWrite(EsBuffer *buffer, const void *source, size_t writ
|
|||
#define fabsf EsCRTfabsf
|
||||
#define floor EsCRTfloor
|
||||
#define floorf EsCRTfloorf
|
||||
#define fmod EsCRTfmod
|
||||
#define fmodf EsCRTfmodf
|
||||
#define free EsCRTfree
|
||||
#define getenv EsCRTgetenv
|
||||
|
@ -355,10 +360,12 @@ extern "C" void *EsBufferWrite(EsBuffer *buffer, const void *source, size_t writ
|
|||
#define memcpy EsCRTmemcpy
|
||||
#define memmove EsCRTmemmove
|
||||
#define memset EsCRTmemset
|
||||
#define pow EsCRTpow
|
||||
#define powf EsCRTpowf
|
||||
#define qsort EsCRTqsort
|
||||
#define rand EsCRTrand
|
||||
#define realloc EsCRTrealloc
|
||||
#define sin EsCRTsin
|
||||
#define sinf EsCRTsinf
|
||||
#define snprintf EsCRTsnprintf
|
||||
#define sprintf EsCRTsprintf
|
||||
|
|
|
@ -836,6 +836,14 @@ float EsCRTpowf(float x, float y) {
|
|||
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) {
|
||||
return EsCRTexp2(x * 1.4426950408889634073);
|
||||
}
|
||||
|
|
|
@ -363,6 +363,7 @@ EsWindowAddSizeAlternative=361
|
|||
EsMenuAddCommandsFromToolbar=362
|
||||
EsPanelStartMovementAnimation=363
|
||||
EsElementStartTransition=364
|
||||
EsCRTatan2=365
|
||||
EsFileWriteAllFromHandle=366
|
||||
EsFileWriteAllGatherFromHandle=367
|
||||
EsButtonSetIconFromBits=368
|
||||
|
@ -464,3 +465,9 @@ EsDialogShow=463
|
|||
EsInstanceSetModified=464
|
||||
EsFileMenuAddToToolbar=465
|
||||
EsFileMenuCreate=466
|
||||
EsCRTcos=467
|
||||
EsCRTsin=468
|
||||
EsCRTfmod=469
|
||||
EsCRTpow=470
|
||||
EsCRTcbrt=471
|
||||
EsCRTcbrtf=472
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#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
|
||||
|
||||
|
@ -16,12 +17,10 @@
|
|||
// TODO Import and reorganize old theming data.
|
||||
|
||||
// Additional features:
|
||||
// TODO Better color modification space -- HSLuv.
|
||||
// TODO Selecting multiple objects.
|
||||
// TODO Resizing objects?
|
||||
// TODO Find object in graph by name.
|
||||
// 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");
|
||||
} else if (object->type == OBJ_MOD_COLOR) {
|
||||
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);
|
||||
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) {
|
||||
InspectorAddLink(object, "Base integer:", "base");
|
||||
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 alpha = base & 0xFF000000;
|
||||
int32_t brightness = PropertyReadInt32(object, "brightness");
|
||||
float hue, saturation, value;
|
||||
UIColorToHSV(base, &hue, &saturation, &value);
|
||||
value += brightness / 100.0f;
|
||||
if (value < 0) value = 0;
|
||||
if (value > 1) value = 1;
|
||||
UIColorToRGB(hue, saturation, value, &base);
|
||||
return base | alpha;
|
||||
int32_t hueShift = PropertyReadInt32(object, "hueShift");
|
||||
double hue, saturation, luminosity, red, green, blue;
|
||||
rgb2hsluv(UI_COLOR_RED_F(base), UI_COLOR_GREEN_F(base), UI_COLOR_BLUE_F(base), &hue, &saturation, &luminosity);
|
||||
luminosity += luminosity * brightness / 100.0f;
|
||||
hue = fmod(hue + hueShift, 360.0);
|
||||
if (luminosity < 0.0) luminosity = 0.0;
|
||||
if (luminosity > 100.0) luminosity = 100.0;
|
||||
hsluv2rgb(hue, saturation, luminosity, &red, &green, &blue);
|
||||
return UI_COLOR_FROM_FLOAT(red, green, blue) | alpha;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
@ -1530,7 +1533,7 @@ int main() {
|
|||
#else
|
||||
UIButtonCreate(0, 0, "Save", -1)->invoke = DocumentSave;
|
||||
#endif
|
||||
UIButtonCreate(0, 0, "Add object", -1)->invoke = ObjectAddCommand;
|
||||
UIButtonCreate(0, 0, "Add object...", -1)->invoke = ObjectAddCommand;
|
||||
UISpacerCreate(0, 0, 15, 0);
|
||||
labelMessage = UILabelCreate(0, UI_ELEMENT_H_FILL, 0, 0);
|
||||
UIParentPop();
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue