aboutsummaryrefslogtreecommitdiffstats
path: root/firmware/color.c
blob: f0f3ed64ffcc38bfb8e9bfc34fbf7675470459bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/*
 * Color space model conversions.
 *
 * Copyright (c) 2020 Michael Buesch <m@bues.ch>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "compat.h"
#include "color.h"
#include "util.h"

#ifndef USE_FLOAT
# define USE_FLOAT	0
#endif
#ifndef USE_64BIT_MUL
# define USE_64BIT_MUL	0
#endif

/* Fractions of the value range. */
#if USE_FLOAT
  typedef float		intermediate_t;
# define f0_x		((float)(0.0f))
# define f1_1		((float)(1.0f))
# define f1_6		((float)(1.0f / 6.0f))
# define f1_3		((float)(1.0f / 3.0f))
# define f1_2		((float)(1.0f / 2.0f))
# define f2_3		((float)(2.0f / 3.0f))
# define f6_1		((float)(6.0f))
#else
  typedef int32_t	intermediate_t;
# define SCALE		16
# define FRACT_MASK	((uint16_t)(((uint32_t)1u << SCALE) - 1u))
# define f0_x		((int32_t)0)
# define f1_1		((int32_t)UINT16_MAX + 1)
# define f1_6		(f1_1 / 6)
# define f1_3		(f1_1 / 3)
# define f1_2		(f1_1 / 2)
# define f2_3		((f1_1 * 2) / 3)
# define f6_1		(f1_1 * 6)
#endif


#if USE_FLOAT
static float multiply(float a, float b)
{
	return a * b;
}
#endif

#if !USE_FLOAT && !USE_64BIT_MUL
static noinline int32_t neg32(int32_t a)
{
	return -a;
}
#endif

#if !USE_FLOAT && !USE_64BIT_MUL
static noinline uint32_t abs32(int32_t a)
{
	return (uint32_t)((a < 0) ? neg32(a) : a);
}
#endif

#if !USE_FLOAT && !USE_64BIT_MUL
static noinline uint32_t mulstep(uint32_t res, int8_t scaledir, uint16_t a, uint16_t b)
{
	uint32_t tmp;

	tmp = (uint32_t)a * (uint32_t)b;
	if (scaledir != 0) {
		if (scaledir < 0)
			tmp >>= SCALE;
		else
			tmp <<= SCALE;
	}
	tmp += res;

	return tmp;
}
#endif

#if !USE_FLOAT
static noinline int32_t multiply(int32_t a, int32_t b)
{
#if USE_64BIT_MUL
	int64_t x;

	x = (int64_t)a * (int64_t)b;	/* multiply */
	x += (int64_t)1 << (16 - 1);	/* round */
	x >>= 16;			/* scale */

	return (int32_t)x;
#else
	uint32_t res;
	uint32_t a_abs, b_abs;
	uint16_t a_count, a_fract;
	uint16_t b_count, b_fract;
	uint8_t is_neg;

	is_neg = ((uint8_t)((uint32_t)a >> 24) ^ (uint8_t)((uint32_t)b >> 24)) & 0x80u;

	a_abs = abs32(a);
	a_count = (uint16_t)(a_abs >> SCALE);
	a_fract = (uint16_t)(a_abs & FRACT_MASK);
	b_abs = abs32(b);
	b_count = (uint16_t)(b_abs >> SCALE);
	b_fract = (uint16_t)(b_abs & FRACT_MASK);

	res = 0u;
	res = mulstep(res, 1, a_count, b_count);
	res = mulstep(res, 0, a_fract, b_count);
	res = mulstep(res, 0, a_count, b_fract);
	res = mulstep(res, -1, a_fract, b_fract);

	if (is_neg)
		return neg32((int32_t)res);
	return (int32_t)res;
#endif
}
#endif /* USE_FLOAT */

static intermediate_t scale_input(uint16_t value)
{
#if USE_FLOAT
	return multiply((float)value, (f1_1 / (float)UINT16_MAX));
#else
	return (int32_t)value;
#endif
}

static uint16_t scale_output(intermediate_t value)
{
#if USE_FLOAT
	if (value < f0_x)
		return 0u;
	if (value > f1_1)
		return UINT16_MAX;
	return (uint16_t)multiply(value, (float)UINT16_MAX);
#else
	return lim_u16(value);
#endif
}

static uint16_t h2rgb(intermediate_t x, intermediate_t y, intermediate_t h)
{
	intermediate_t ret;

	/* modulo */
	if (h < f0_x)
		h += f1_1;
	if (h > f1_1)
		h -= f1_1;

	if (h < f1_6)
		ret = x + multiply((y - x), multiply(h, f6_1));
	else if (h < f1_2)
		ret = y;
	else if (h < f2_3)
		ret = x + multiply((y - x), multiply((f2_3 - h), f6_1));
	else
		ret = x;

	return scale_output(ret);
}

/* Convert from HSL color model to RGB. */
void hsl2rgb(uint16_t *r, uint16_t *g, uint16_t *b,
	     uint16_t h, uint16_t s, uint16_t l)
{
	intermediate_t hh, ss, ll, x, y;

	hh = scale_input(h);
	ss = scale_input(s);
	ll = scale_input(l);

	if (s == 0u) {
		*r = *g = *b = l;
	} else {
		if (ll <= f1_2)
			y = ll + multiply(ll, ss);
		else
			y = (ll + ss) - multiply(ll, ss);
		x = (ll + ll) - y;

		*r = h2rgb(x, y, hh + f1_3);
		*g = h2rgb(x, y, hh);
		*b = h2rgb(x, y, hh - f1_3);
	}
}
bues.ch cgit interface