openMSX
PixelOperations.hh
Go to the documentation of this file.
1#ifndef PIXELOPERATIONS_HH
2#define PIXELOPERATIONS_HH
3
4#include <bit>
5#include <cstdint>
6#include <span>
7
8namespace openmsx {
9
10using Pixel = uint32_t;
11
13{
14public:
15 [[nodiscard]] Pixel getRmask() const { return 0x000000FF; }
16 [[nodiscard]] Pixel getGmask() const { return 0x0000FF00; }
17 [[nodiscard]] Pixel getBmask() const { return 0x00FF0000; }
18 [[nodiscard]] Pixel getAmask() const { return 0xFF000000; }
19
22 [[nodiscard]] unsigned red(Pixel p) const;
23 [[nodiscard]] unsigned green(Pixel p) const;
24 [[nodiscard]] unsigned blue(Pixel p) const;
25 [[nodiscard]] unsigned alpha(Pixel p) const;
26
29 [[nodiscard]] Pixel combine(unsigned r, unsigned g, unsigned b) const;
30
35 template<unsigned w1, unsigned w2>
36 [[nodiscard]] Pixel blend(Pixel p1, Pixel p2) const;
37 template<unsigned w1, unsigned w2, unsigned w3>
38 [[nodiscard]] Pixel blend(Pixel p1, Pixel p2, Pixel p3) const;
39 template<unsigned w1, unsigned w2, unsigned w3, unsigned w4>
40 [[nodiscard]] Pixel blend(Pixel p1, Pixel p2, Pixel p3, Pixel p4) const;
41 template<unsigned w1, unsigned w2, unsigned w3,
42 unsigned w4, unsigned w5, unsigned w6>
43 [[nodiscard]] Pixel blend(Pixel p1, Pixel p2, Pixel p3,
44 Pixel p4, Pixel p5, Pixel p6) const;
45
46 template<unsigned w1, unsigned w2>
47 [[nodiscard]] Pixel blend(std::span<const Pixel, 2> p) const;
48 template<unsigned w1, unsigned w2, unsigned w3>
49 [[nodiscard]] Pixel blend(std::span<const Pixel, 3> p) const;
50 template<unsigned w1, unsigned w2, unsigned w3, unsigned w4>
51 [[nodiscard]] Pixel blend(std::span<const Pixel, 4> p) const;
52 template<unsigned w1, unsigned w2, unsigned w3,
53 unsigned w4, unsigned w5, unsigned w6>
54 [[nodiscard]] Pixel blend(std::span<const Pixel, 6> p) const;
55
64 [[nodiscard]] static Pixel multiply(Pixel p, unsigned x);
65
74 [[nodiscard]] Pixel lerp(Pixel p1, Pixel p2, unsigned x) const;
75
80 [[nodiscard]] Pixel alphaBlend(Pixel p1, Pixel p2) const;
81
82private:
83 [[nodiscard]] Pixel avgDown(Pixel p1, Pixel p2) const;
84 [[nodiscard]] Pixel avgUp (Pixel p1, Pixel p2) const;
85};
86
87
88inline unsigned PixelOperations::red(Pixel p) const
89{
90 return (p >> 0) & 0xFF;
91}
92inline unsigned PixelOperations::green(Pixel p) const
93{
94 return (p >> 8) & 0xFF;
95}
96inline unsigned PixelOperations::blue(Pixel p) const
97{
98 return (p >> 16) & 0xFF;
99}
100inline unsigned PixelOperations::alpha(Pixel p) const
101{
102 return (p >> 24) & 0xFF;
103}
104
106 unsigned r, unsigned g, unsigned b) const
107{
108 return Pixel((r << 0) | (g << 8) | (b << 16) | (0xFF << 24));
109}
110
111inline Pixel PixelOperations::avgDown(Pixel p1, Pixel p2) const
112{
113 // Average can be calculated as:
114 // floor((x + y) / 2.0) = (x & y) + (x ^ y) / 2
115 // see "Average of Integers" on http://aggregate.org/MAGIC/
116 return (p1 & p2) + (((p1 ^ p2) & 0xFEFEFEFE) >> 1);
117}
118inline Pixel PixelOperations::avgUp(Pixel p1, Pixel p2) const
119{
120 // Similar to above, but rounds up
121 // ceil((x + y) / 2.0) = (x | y) - (x ^ y) / 2
122 return (p1 | p2) - (((p1 ^ p2) & 0xFEFEFEFE) >> 1);
123}
124
125template<unsigned w1, unsigned w2>
127{
128 constexpr unsigned total = w1 + w2;
129 if constexpr (w1 == 0) {
130 return p2;
131 } else if constexpr (w1 > w2) {
132 return blend<w2, w1>(p2, p1);
133
134 } else if constexpr (w1 == w2) {
135 // <1,1>
136 return avgDown(p1, p2);
137 } else if constexpr ((3 * w1) == w2) {
138 // <1,3>
139 Pixel p11 = avgDown(p1, p2);
140 return avgUp(p11, p2);
141 } else if constexpr ((7 * w1) == w2) {
142 // <1,7>
143 Pixel p11 = avgDown(p1, p2);
144 Pixel p13 = avgDown(p11, p2);
145 return avgUp(p13, p2);
146 } else if constexpr ((5 * w1) == (3 * w2)) {
147 // <3,5> mix rounding up/down to get a more accurate result
148 Pixel p11 = avgUp (p1, p2);
149 Pixel p13 = avgDown(p11, p2);
150 return avgDown(p11, p13);
151
152 } else if constexpr (!std::has_single_bit(total)) {
153 // approximate with weights that sum to 256
154 // e.g. approximate <1,2> as <85,171> (or <21,43>)
155 // ww1 = round(256 * w1 / total) ww2 = 256 - ww1
156 constexpr unsigned newTotal = 256;
157 constexpr unsigned ww1 = (2 * w1 * newTotal + total) / (2 * total);
158 constexpr unsigned ww2 = 256 - ww1;
159 return blend<ww1, ww2>(p1, p2);
160
161 } else {
162 // 32bpp
163 constexpr unsigned l2 = std::bit_width(total) - 1;
164 unsigned c1 = (((p1 & 0x00FF00FF) * w1 +
165 (p2 & 0x00FF00FF) * w2
166 ) >> l2) & 0x00FF00FF;
167 unsigned c2 = (((p1 & 0xFF00FF00) >> l2) * w1 +
168 ((p2 & 0xFF00FF00) >> l2) * w2
169 ) & 0xFF00FF00;
170 return c1 | c2;
171 }
172}
173
174template<unsigned w1, unsigned w2, unsigned w3>
176{
177 constexpr unsigned total = w1 + w2 + w3;
178 if constexpr (std::has_single_bit(total)) {
179 constexpr unsigned l2 = std::bit_width(total) - 1;
180 unsigned c1 = (((p1 & 0x00FF00FF) * w1 +
181 (p2 & 0x00FF00FF) * w2 +
182 (p3 & 0x00FF00FF) * w3) >> l2) & 0x00FF00FF;
183 unsigned c2 = (((p1 & 0xFF00FF00) >> l2) * w1 +
184 ((p2 & 0xFF00FF00) >> l2) * w2 +
185 ((p3 & 0xFF00FF00) >> l2) * w3) & 0xFF00FF00;
186 return c1 | c2;
187 } else {
188 unsigned r = (red (p1) * w1 + red (p2) * w2 + red (p3) * w3) / total;
189 unsigned g = (green(p1) * w1 + green(p2) * w2 + green(p3) * w3) / total;
190 unsigned b = (blue (p1) * w1 + blue (p2) * w2 + blue (p3) * w3) / total;
191 return combine(r, g, b);
192 }
193}
194
195template<unsigned w1, unsigned w2, unsigned w3, unsigned w4>
197 Pixel p1, Pixel p2, Pixel p3, Pixel p4) const
198{
199 constexpr unsigned total = w1 + w2 + w3 + w4;
200 if constexpr (std::has_single_bit(total)) {
201 constexpr unsigned l2 = std::bit_width(total) - 1;
202 unsigned c1 = (((p1 & 0x00FF00FF) * w1 +
203 (p2 & 0x00FF00FF) * w2 +
204 (p3 & 0x00FF00FF) * w3 +
205 (p4 & 0x00FF00FF) * w4) >> l2) & 0x00FF00FF;
206 unsigned c2 = (((p1 & 0xFF00FF00) >> l2) * w1 +
207 ((p2 & 0xFF00FF00) >> l2) * w2 +
208 ((p3 & 0xFF00FF00) >> l2) * w3 +
209 ((p4 & 0xFF00FF00) >> l2) * w4) & 0xFF00FF00;
210 return c1 | c2;
211 } else {
212 unsigned r = (red (p1) * w1 + red (p2) * w2 +
213 red (p3) * w3 + red (p4) * w4) / total;
214 unsigned g = (green(p1) * w1 + green(p2) * w2 +
215 green(p3) * w3 + green(p4) * w4) / total;
216 unsigned b = (blue (p1) * w1 + blue (p2) * w2 +
217 blue (p3) * w3 + blue (p4) * w4) / total;
218 return combine(r, g, b);
219 }
220}
221
222template<unsigned w1, unsigned w2, unsigned w3,
223 unsigned w4, unsigned w5, unsigned w6>
225 Pixel p1, Pixel p2, Pixel p3, Pixel p4, Pixel p5, Pixel p6) const
226{
227 constexpr unsigned total = w1 + w2 + w3 + w4 + w5 + w6;
228 if constexpr (std::has_single_bit(total)) {
229 constexpr unsigned l2 = std::bit_width(total) - 1;
230 unsigned c1 = (((p1 & 0x00FF00FF) * w1 +
231 (p2 & 0x00FF00FF) * w2 +
232 (p3 & 0x00FF00FF) * w3 +
233 (p4 & 0x00FF00FF) * w4 +
234 (p5 & 0x00FF00FF) * w5 +
235 (p6 & 0x00FF00FF) * w6) >> l2) & 0x00FF00FF;
236 unsigned c2 = (((p1 & 0xFF00FF00) >> l2) * w1 +
237 ((p2 & 0xFF00FF00) >> l2) * w2 +
238 ((p3 & 0xFF00FF00) >> l2) * w3 +
239 ((p4 & 0xFF00FF00) >> l2) * w4 +
240 ((p5 & 0xFF00FF00) >> l2) * w5 +
241 ((p6 & 0xFF00FF00) >> l2) * w6) & 0xFF00FF00;
242 return c1 | c2;
243 } else {
244 unsigned r = (red (p1) * w1 + red (p2) * w2 +
245 red (p3) * w3 + red (p4) * w4 +
246 red (p5) * w5 + red (p6) * w6) / total;
247 unsigned g = (green(p1) * w1 + green(p2) * w2 +
248 green(p3) * w3 + green(p4) * w4 +
249 green(p5) * w5 + green(p6) * w6) / total;
250 unsigned b = (blue (p1) * w1 + blue (p2) * w2 +
251 blue (p3) * w3 + blue (p4) * w4 +
252 blue (p5) * w5 + blue (p6) * w6) / total;
253 return combine(r, g, b);
254 }
255}
256
257
258template<unsigned w1, unsigned w2>
259inline Pixel PixelOperations::blend(std::span<const Pixel, 2> p) const
260{
261 return blend<w1, w2>(p[0], p[1]);
262}
263
264template<unsigned w1, unsigned w2, unsigned w3>
265inline Pixel PixelOperations::blend(std::span<const Pixel, 3> p) const
266{
267 return blend<w1, w2, w3>(p[0], p[1], p[2]);
268}
269
270template<unsigned w1, unsigned w2, unsigned w3, unsigned w4>
271inline Pixel PixelOperations::blend(std::span<const Pixel, 4> p) const
272{
273 return blend<w1, w2, w3, w4>(p[0], p[1], p[2], p[3]);
274}
275
276template<unsigned w1, unsigned w2, unsigned w3,
277 unsigned w4, unsigned w5, unsigned w6>
278inline Pixel PixelOperations::blend(std::span<const Pixel, 6> p) const
279{
280 return blend<w1, w2, w3, w4, w5, w6>(p[0], p[1], p[2], p[3], p[4], p[5]);
281}
282
284{
285 return ((((p & 0x00FF00FF) * x) & 0xFF00FF00) >> 8)
286 | ((((p >> 8) & 0x00FF00FF) * x) & 0xFF00FF00);
287}
288
289inline Pixel PixelOperations::lerp(Pixel p1, Pixel p2, unsigned x) const
290{
291 // 32bpp
292 unsigned rb1 = (p1 >> 0) & 0x00FF00FF;
293 unsigned ag1 = (p1 >> 8) & 0x00FF00FF;
294 unsigned rb2 = (p2 >> 0) & 0x00FF00FF;
295 unsigned ag2 = (p2 >> 8) & 0x00FF00FF;
296
297 // Note: the subtraction for the lower component can 'borrow' from
298 // the higher component. Though in the full calculation this error
299 // magically cancels out.
300 unsigned trb = ((rb2 - rb1) * x) >> 8;
301 unsigned tag = ((ag2 - ag1) * x) >> 0;
302
303 unsigned rb = ((trb + rb1) << 0) & 0x00FF00FF;
304 unsigned ag = (tag + (ag1 << 8)) & 0xFF00FF00;
305
306 return rb | ag;
307}
308
310{
311 unsigned a = alpha(p1);
312 // Note: 'a' is [0..255], while lerp() expects [0..256].
313 // We ignore this small error.
314 return lerp(p2, p1, a);
315}
316
317} // namespace openmsx
318
319#endif
int g
Pixel lerp(Pixel p1, Pixel p2, unsigned x) const
Perform linear interpolation between two pixels.
Pixel blend(Pixel p1, Pixel p2) const
Blend the given colors into a single color.
unsigned green(Pixel p) const
Pixel combine(unsigned r, unsigned g, unsigned b) const
Combine RGB components to a pixel.
unsigned alpha(Pixel p) const
static Pixel multiply(Pixel p, unsigned x)
Perform a component wise multiplication of a pixel with an 8-bit fractional value: result = (pixel * ...
Pixel alphaBlend(Pixel p1, Pixel p2) const
Perform alpha blending of two pixels.
unsigned red(Pixel p) const
Extract RGBA components, each in range [0..255].
unsigned blue(Pixel p) const
mat4 p4(vec4(1, 2, 3, 4), vec4(3, 4, 5, 6), vec4(5, 0, 7, 8), vec4(7, 8, 9, 0))
mat3 p3(vec3(1, 2, 3), vec3(4, 5, 6), vec3(7, 0, 9))
vec3 w3(-1, -2, 2)
vec4 w4(-1, 2, 2, -6)
This file implemented 3 utility functions:
Definition Autofire.cc:11
CharacterConverter::Pixel Pixel