openMSX
PixelOperations.hh
Go to the documentation of this file.
1#ifndef PIXELOPERATIONS_HH
2#define PIXELOPERATIONS_HH
3
4#include "PixelFormat.hh"
5#include "narrow.hh"
6#include "unreachable.hh"
7#include "build-info.hh"
8#include <bit>
9#include <concepts>
10#include <span>
11
12namespace openmsx {
13
14template<std::unsigned_integral Pixel> class PixelOpBase
15{
16public:
17 explicit PixelOpBase(const PixelFormat& format_)
18 : format(format_)
19 , blendMask(calcBlendMask())
20 {
21 }
22
23 [[nodiscard]] const PixelFormat& getPixelFormat() const { return format; }
24
25 [[nodiscard]] inline unsigned getRmask() const { return format.getRmask(); }
26 [[nodiscard]] inline unsigned getGmask() const { return format.getGmask(); }
27 [[nodiscard]] inline unsigned getBmask() const { return format.getBmask(); }
28 [[nodiscard]] inline unsigned getAmask() const { return format.getAmask(); }
29 [[nodiscard]] inline unsigned getRshift() const { return format.getRshift(); }
30 [[nodiscard]] inline unsigned getGshift() const { return format.getGshift(); }
31 [[nodiscard]] inline unsigned getBshift() const { return format.getBshift(); }
32 [[nodiscard]] inline unsigned getAshift() const { return format.getAshift(); }
33 [[nodiscard]] inline unsigned getRloss() const { return format.getRloss(); }
34 [[nodiscard]] inline unsigned getGloss() const { return format.getGloss(); }
35 [[nodiscard]] inline unsigned getBloss() const { return format.getBloss(); }
36 [[nodiscard]] inline unsigned getAloss() const { return format.getAloss(); }
37
45 [[nodiscard]] inline Pixel getBlendMask() const { return blendMask; }
46
51 static constexpr bool IS_RGB565 = false;
52
53private:
54 [[nodiscard]] inline Pixel calcBlendMask() const
55 {
56 auto rBit = ~(getRmask() << 1) & getRmask();
57 auto gBit = ~(getGmask() << 1) & getGmask();
58 auto bBit = ~(getBmask() << 1) & getBmask();
59 return ~(rBit | gBit | bBit);
60 }
61
62private:
63 const PixelFormat& format;
64
71 const Pixel blendMask;
72};
73
74// Specialization for 32bpp
75// No need to store 'blendMask' in a member variable.
76template<> class PixelOpBase<unsigned>
77{
78public:
79 explicit PixelOpBase(const PixelFormat& format_)
80 : format(format_) {}
81
82 [[nodiscard]] const PixelFormat& getPixelFormat() const { return format; }
83
84 [[nodiscard]] inline unsigned getRmask() const { return format.getRmask(); }
85 [[nodiscard]] inline unsigned getGmask() const { return format.getGmask(); }
86 [[nodiscard]] inline unsigned getBmask() const { return format.getBmask(); }
87 [[nodiscard]] inline unsigned getAmask() const { return format.getAmask(); }
88 [[nodiscard]] inline unsigned getRshift() const { return format.getRshift(); }
89 [[nodiscard]] inline unsigned getGshift() const { return format.getGshift(); }
90 [[nodiscard]] inline unsigned getBshift() const { return format.getBshift(); }
91 [[nodiscard]] inline unsigned getAshift() const { return format.getAshift(); }
92 [[nodiscard]] inline unsigned getRloss() const { return 0; }
93 [[nodiscard]] inline unsigned getGloss() const { return 0; }
94 [[nodiscard]] inline unsigned getBloss() const { return 0; }
95 [[nodiscard]] inline unsigned getAloss() const { return 0; }
96
97 [[nodiscard]] inline unsigned getBlendMask() const { return 0xFEFEFEFE; }
98
99 static constexpr bool IS_RGB565 = false;
100
101private:
102 const PixelFormat& format;
103};
104
105
106#if PLATFORM_DINGUX
107// Specialization for dingoo (16bpp)
108// We know the exact pixel format for this platform. No need for any
109// members in this class. All values can also be compile-time constant.
110template<> class PixelOpBase<uint16_t>
111{
112public:
113 explicit PixelOpBase(const PixelFormat& /*format*/) {}
114
115 [[nodiscard]] const PixelFormat& getPixelFormat() const
116 {
117 static PixelFormat format(16,
118 0x001F, 0, 3,
119 0x07E0, 5, 2,
120 0xF800, 11, 3,
121 0x0000, 0, 8);
122 return format;
123 }
124
125 [[nodiscard]] inline unsigned getRmask() const { return 0x001F; }
126 [[nodiscard]] inline unsigned getGmask() const { return 0x07E0; }
127 [[nodiscard]] inline unsigned getBmask() const { return 0xF800; }
128 [[nodiscard]] inline unsigned getAmask() const { return 0x0000; }
129 [[nodiscard]] inline unsigned getRshift() const { return 0; }
130 [[nodiscard]] inline unsigned getGshift() const { return 5; }
131 [[nodiscard]] inline unsigned getBshift() const { return 11; }
132 [[nodiscard]] inline unsigned getAshift() const { return 0; }
133 [[nodiscard]] inline unsigned getRloss() const { return 3; }
134 [[nodiscard]] inline unsigned getGloss() const { return 2; }
135 [[nodiscard]] inline unsigned getBloss() const { return 3; }
136 [[nodiscard]] inline unsigned getAloss() const { return 8; }
137
138 [[nodiscard]] inline uint16_t getBlendMask() const { return 0xF7DE; }
139
140 static constexpr bool IS_RGB565 = true;
141};
142#endif
143
144
145
146template<std::unsigned_integral Pixel> class PixelOperations : public PixelOpBase<Pixel>
147{
148public:
164
165 explicit PixelOperations(const PixelFormat& format);
166
169 [[nodiscard]] inline unsigned red(Pixel p) const;
170 [[nodiscard]] inline unsigned green(Pixel p) const;
171 [[nodiscard]] inline unsigned blue(Pixel p) const;
172 [[nodiscard]] inline unsigned alpha(Pixel p) const;
173
174 // alpha is maximum
175 [[nodiscard]] inline bool isFullyOpaque(Pixel p) const;
176 // alpha is minimum
177 [[nodiscard]] inline bool isFullyTransparent(Pixel p) const;
178
181 [[nodiscard]] inline unsigned red256(Pixel p) const;
182 [[nodiscard]] inline unsigned green256(Pixel p) const;
183 [[nodiscard]] inline unsigned blue256(Pixel p) const;
184
187 [[nodiscard]] inline Pixel combine(unsigned r, unsigned g, unsigned b) const;
188 [[nodiscard]] inline Pixel combine256(unsigned r, unsigned g, unsigned b) const;
189
192 [[nodiscard]] inline unsigned getMaxRed() const;
193 [[nodiscard]] inline unsigned getMaxGreen() const;
194 [[nodiscard]] inline unsigned getMaxBlue() const;
195
200 template<unsigned w1, unsigned w2>
201 [[nodiscard]] inline Pixel blend(Pixel p1, Pixel p2) const;
202 template<unsigned w1, unsigned w2, unsigned w3>
203 [[nodiscard]] inline Pixel blend(Pixel p1, Pixel p2, Pixel p3) const;
204 template<unsigned w1, unsigned w2, unsigned w3, unsigned w4>
205 [[nodiscard]] inline Pixel blend(Pixel p1, Pixel p2, Pixel p3, Pixel p4) const;
206 template<unsigned w1, unsigned w2, unsigned w3,
207 unsigned w4, unsigned w5, unsigned w6>
208 [[nodiscard]] inline Pixel blend(Pixel p1, Pixel p2, Pixel p3,
209 Pixel p4, Pixel p5, Pixel p6) const;
210
211 template<unsigned w1, unsigned w2>
212 [[nodiscard]] inline Pixel blend(std::span<const Pixel, 2> p) const;
213 template<unsigned w1, unsigned w2, unsigned w3>
214 [[nodiscard]] inline Pixel blend(std::span<const Pixel, 3> p) const;
215 template<unsigned w1, unsigned w2, unsigned w3, unsigned w4>
216 [[nodiscard]] inline Pixel blend(std::span<const Pixel, 4> p) const;
217 template<unsigned w1, unsigned w2, unsigned w3,
218 unsigned w4, unsigned w5, unsigned w6>
219 [[nodiscard]] inline Pixel blend(std::span<const Pixel, 6> p) const;
220
229 [[nodiscard]] static inline Pixel multiply(Pixel p, unsigned x);
230
239 [[nodiscard]] inline Pixel lerp(Pixel p1, Pixel p2, unsigned x) const;
240
245 [[nodiscard]] inline Pixel alphaBlend(Pixel p1, Pixel p2) const;
246
247private:
248 [[nodiscard]] inline Pixel avgDown(Pixel p1, Pixel p2) const;
249 [[nodiscard]] inline Pixel avgUp (Pixel p1, Pixel p2) const;
250};
251
252
253template<std::unsigned_integral Pixel>
255 : PixelOpBase<Pixel>(format_)
256{
257}
258
259template<std::unsigned_integral Pixel>
260inline unsigned PixelOperations<Pixel>::red(Pixel p) const
261{
262 if constexpr (sizeof(Pixel) == 4) {
263 return (p >> getRshift()) & 0xFF;
264 } else {
265 return (p & getRmask()) >> getRshift();
266 }
267}
268template<std::unsigned_integral Pixel>
269inline unsigned PixelOperations<Pixel>::green(Pixel p) const
270{
271 if constexpr (sizeof(Pixel) == 4) {
272 return (p >> getGshift()) & 0xFF;
273 } else {
274 return (p & getGmask()) >> getGshift();
275 }
276}
277template<std::unsigned_integral Pixel>
278inline unsigned PixelOperations<Pixel>::blue(Pixel p) const
279{
280 if constexpr (sizeof(Pixel) == 4) {
281 return (p >> getBshift()) & 0xFF;
282 } else {
283 return (p & getBmask()) >> getBshift();
284 }
285}
286template<std::unsigned_integral Pixel>
287inline unsigned PixelOperations<Pixel>::alpha(Pixel p) const
288{
289 if constexpr (sizeof(Pixel) == 4) {
290 return (p >> getAshift()) & 0xFF;
291 } else {
292 UNREACHABLE; return 0;
293 //return (p & getAmask()) >> getAshift();
294 }
295}
296
297template<std::unsigned_integral Pixel>
299{
300 if constexpr (sizeof(Pixel) == 4) {
301 return alpha(p) == 255;
302 } else {
303 return p != 0x0001;
304 }
305}
306template<std::unsigned_integral Pixel>
308{
309 if constexpr (sizeof(Pixel) == 4) {
310 return alpha(p) == 0;
311 } else {
312 return p == 0x0001;
313 }
314}
315
316template<std::unsigned_integral Pixel>
317inline unsigned PixelOperations<Pixel>::red256(Pixel p) const
318{
319 if constexpr (sizeof(Pixel) == 4) {
320 return (p >> getRshift()) & 0xFF;
321 } else {
322 return ((p >> getRshift()) << getRloss()) & 0xFF;
323 }
324}
325template<std::unsigned_integral Pixel>
327{
328 if constexpr (sizeof(Pixel) == 4) {
329 return (p >> getGshift()) & 0xFF;
330 } else {
331 return ((p >> getGshift()) << getGloss()) & 0xFF;
332 }
333}
334template<std::unsigned_integral Pixel>
336{
337 if constexpr (sizeof(Pixel) == 4) {
338 return (p >> getBshift()) & 0xFF;
339 } else {
340 return ((p >> getBshift()) << getBloss()) & 0xFF;
341 }
342}
343
344template<std::unsigned_integral Pixel>
346 unsigned r, unsigned g, unsigned b) const
347{
348 return Pixel((r << getRshift()) |
349 (g << getGshift()) |
350 (b << getBshift()));
351}
352
353template<std::unsigned_integral Pixel>
355 unsigned r, unsigned g, unsigned b) const
356{
357 if constexpr (sizeof(Pixel) == 4) {
358 return Pixel((r << getRshift()) |
359 (g << getGshift()) |
360 (b << getBshift()));
361 } else {
362 return Pixel(((r >> getRloss()) << getRshift()) |
363 ((g >> getGloss()) << getGshift()) |
364 ((b >> getBloss()) << getBshift()));
365 }
366}
367
368template<std::unsigned_integral Pixel>
370{
371 if constexpr (sizeof(Pixel) == 4) {
372 return 255;
373 } else {
374 return 255 >> getRloss();
375 }
376}
377template<std::unsigned_integral Pixel>
379{
380 if constexpr (sizeof(Pixel) == 4) {
381 return 255;
382 } else {
383 return 255 >> getGloss();
384 }
385}
386template<std::unsigned_integral Pixel>
388{
389 if constexpr (sizeof(Pixel) == 4) {
390 return 255;
391 } else {
392 return 255 >> getBloss();
393 }
394}
395
396template<std::unsigned_integral Pixel>
398{
399 // Average can be calculated as:
400 // floor((x + y) / 2.0) = (x & y) + (x ^ y) / 2
401 // see "Average of Integers" on http://aggregate.org/MAGIC/
402 return (p1 & p2) + (((p1 ^ p2) & getBlendMask()) >> 1);
403}
404template<std::unsigned_integral Pixel>
405inline Pixel PixelOperations<Pixel>::avgUp(Pixel p1, Pixel p2) const
406{
407 // Similar to above, but rounds up
408 // ceil((x + y) / 2.0) = (x | y) - (x ^ y) / 2
409 return (p1 | p2) - (((p1 ^ p2) & getBlendMask()) >> 1);
410}
411
412template<std::unsigned_integral Pixel>
413template<unsigned w1, unsigned w2>
415{
416 constexpr unsigned total = w1 + w2;
417 if constexpr (w1 == 0) {
418 return p2;
419 } else if constexpr (w1 > w2) {
420 return blend<w2, w1>(p2, p1);
421
422 } else if constexpr (w1 == w2) {
423 // <1,1>
424 return avgDown(p1, p2);
425 } else if constexpr ((3 * w1) == w2) {
426 // <1,3>
427 Pixel p11 = avgDown(p1, p2);
428 return avgUp(p11, p2);
429 } else if constexpr ((7 * w1) == w2) {
430 // <1,7>
431 Pixel p11 = avgDown(p1, p2);
432 Pixel p13 = avgDown(p11, p2);
433 return avgUp(p13, p2);
434 } else if constexpr ((5 * w1) == (3 * w2)) {
435 // <3,5> mix rounding up/down to get a more accurate result
436 Pixel p11 = avgUp (p1, p2);
437 Pixel p13 = avgDown(p11, p2);
438 return avgDown(p11, p13);
439
440 } else if constexpr (!std::has_single_bit(total)) {
441 // approximate with weights that sum to 256 (or 64)
442 // e.g. approximate <1,2> as <85,171> (or <21,43>)
443 // ww1 = round(256 * w1 / total) ww2 = 256 - ww1
444 constexpr unsigned newTotal = IS_RGB565 ? 64 : 256;
445 constexpr unsigned ww1 = (2 * w1 * newTotal + total) / (2 * total);
446 constexpr unsigned ww2 = 256 - ww1;
447 return blend<ww1, ww2>(p1, p2);
448
449 } else if constexpr (sizeof(Pixel) == 4) {
450 constexpr unsigned l2 = std::bit_width(total) - 1;
451 unsigned c1 = (((p1 & 0x00FF00FF) * w1 +
452 (p2 & 0x00FF00FF) * w2
453 ) >> l2) & 0x00FF00FF;
454 unsigned c2 = (((p1 & 0xFF00FF00) >> l2) * w1 +
455 ((p2 & 0xFF00FF00) >> l2) * w2
456 ) & 0xFF00FF00;
457 return c1 | c2;
458
459 } else if constexpr (IS_RGB565) {
460 if constexpr (total > 64) {
461 // reduce to maximum 6-bit
462 // note: DIV64 only exists to work around a
463 // division by zero in dead code
464 constexpr unsigned DIV64 = (total > 64) ? 64 : 1;
465 constexpr unsigned factor = total / DIV64;
466 constexpr unsigned round = factor / 2;
467 constexpr unsigned ww1 = (w1 + round) / factor;
468 constexpr unsigned ww2 = 64 - ww1;
469 return blend<ww1, ww2>(p1, p2);
470 } else {
471 unsigned l2 = std::bit_width(total) - 1;
472 unsigned c1 = (((unsigned(p1) & 0xF81F) * w1) +
473 ((unsigned(p2) & 0xF81F) * w2)) & (0xF81F << l2);
474 unsigned c2 = (((unsigned(p1) & 0x07E0) * w1) +
475 ((unsigned(p2) & 0x07E0) * w2)) & (0x07E0 << l2);
476 return (c1 | c2) >> l2;
477 }
478
479 } else {
480 // generic version
481 unsigned r = (red (p1) * w1 + red (p2) * w2) / total;
482 unsigned g = (green(p1) * w1 + green(p2) * w2) / total;
483 unsigned b = (blue (p1) * w1 + blue (p2) * w2) / total;
484 return combine(r, g, b);
485 }
486}
487
488template<std::unsigned_integral Pixel>
489template<unsigned w1, unsigned w2, unsigned w3>
491{
492 constexpr unsigned total = w1 + w2 + w3;
493 if constexpr ((sizeof(Pixel) == 4) && std::has_single_bit(total)) {
494 constexpr unsigned l2 = std::bit_width(total) - 1;
495 unsigned c1 = (((p1 & 0x00FF00FF) * w1 +
496 (p2 & 0x00FF00FF) * w2 +
497 (p3 & 0x00FF00FF) * w3) >> l2) & 0x00FF00FF;
498 unsigned c2 = (((p1 & 0xFF00FF00) >> l2) * w1 +
499 ((p2 & 0xFF00FF00) >> l2) * w2 +
500 ((p3 & 0xFF00FF00) >> l2) * w3) & 0xFF00FF00;
501 return c1 | c2;
502 } else {
503 unsigned r = (red (p1) * w1 + red (p2) * w2 + red (p3) * w3) / total;
504 unsigned g = (green(p1) * w1 + green(p2) * w2 + green(p3) * w3) / total;
505 unsigned b = (blue (p1) * w1 + blue (p2) * w2 + blue (p3) * w3) / total;
506 return combine(r, g, b);
507 }
508}
509
510template<std::unsigned_integral Pixel>
511template<unsigned w1, unsigned w2, unsigned w3, unsigned w4>
513 Pixel p1, Pixel p2, Pixel p3, Pixel p4) const
514{
515 constexpr unsigned total = w1 + w2 + w3 + w4;
516 if constexpr ((sizeof(Pixel) == 4) && std::has_single_bit(total)) {
517 constexpr unsigned l2 = std::bit_width(total) - 1;
518 unsigned c1 = (((p1 & 0x00FF00FF) * w1 +
519 (p2 & 0x00FF00FF) * w2 +
520 (p3 & 0x00FF00FF) * w3 +
521 (p4 & 0x00FF00FF) * w4) >> l2) & 0x00FF00FF;
522 unsigned c2 = (((p1 & 0xFF00FF00) >> l2) * w1 +
523 ((p2 & 0xFF00FF00) >> l2) * w2 +
524 ((p3 & 0xFF00FF00) >> l2) * w3 +
525 ((p4 & 0xFF00FF00) >> l2) * w4) & 0xFF00FF00;
526 return c1 | c2;
527 } else {
528 unsigned r = (red (p1) * w1 + red (p2) * w2 +
529 red (p3) * w3 + red (p4) * w4) / total;
530 unsigned g = (green(p1) * w1 + green(p2) * w2 +
531 green(p3) * w3 + green(p4) * w4) / total;
532 unsigned b = (blue (p1) * w1 + blue (p2) * w2 +
533 blue (p3) * w3 + blue (p4) * w4) / total;
534 return combine(r, g, b);
535 }
536}
537
538template<std::unsigned_integral Pixel>
539template<unsigned w1, unsigned w2, unsigned w3,
540 unsigned w4, unsigned w5, unsigned w6>
542 Pixel p1, Pixel p2, Pixel p3, Pixel p4, Pixel p5, Pixel p6) const
543{
544 constexpr unsigned total = w1 + w2 + w3 + w4 + w5 + w6;
545 if constexpr ((sizeof(Pixel) == 4) && std::has_single_bit(total)) {
546 constexpr unsigned l2 = std::bit_width(total) - 1;
547 unsigned c1 = (((p1 & 0x00FF00FF) * w1 +
548 (p2 & 0x00FF00FF) * w2 +
549 (p3 & 0x00FF00FF) * w3 +
550 (p4 & 0x00FF00FF) * w4 +
551 (p5 & 0x00FF00FF) * w5 +
552 (p6 & 0x00FF00FF) * w6) >> l2) & 0x00FF00FF;
553 unsigned c2 = (((p1 & 0xFF00FF00) >> l2) * w1 +
554 ((p2 & 0xFF00FF00) >> l2) * w2 +
555 ((p3 & 0xFF00FF00) >> l2) * w3 +
556 ((p4 & 0xFF00FF00) >> l2) * w4 +
557 ((p5 & 0xFF00FF00) >> l2) * w5 +
558 ((p6 & 0xFF00FF00) >> l2) * w6) & 0xFF00FF00;
559 return c1 | c2;
560 } else {
561 unsigned r = (red (p1) * w1 + red (p2) * w2 +
562 red (p3) * w3 + red (p4) * w4 +
563 red (p5) * w5 + red (p6) * w6) / total;
564 unsigned g = (green(p1) * w1 + green(p2) * w2 +
565 green(p3) * w3 + green(p4) * w4 +
566 green(p5) * w5 + green(p6) * w6) / total;
567 unsigned b = (blue (p1) * w1 + blue (p2) * w2 +
568 blue (p3) * w3 + blue (p4) * w4 +
569 blue (p5) * w5 + blue (p6) * w6) / total;
570 return combine(r, g, b);
571 }
572}
573
574
575template<std::unsigned_integral Pixel>
576template<unsigned w1, unsigned w2>
577inline Pixel PixelOperations<Pixel>::blend(std::span<const Pixel, 2> p) const
578{
579 return blend<w1, w2>(p[0], p[1]);
580}
581
582template<std::unsigned_integral Pixel>
583template<unsigned w1, unsigned w2, unsigned w3>
584inline Pixel PixelOperations<Pixel>::blend(std::span<const Pixel, 3> p) const
585{
586 return blend<w1, w2, w3>(p[0], p[1], p[2]);
587}
588
589template<std::unsigned_integral Pixel>
590template<unsigned w1, unsigned w2, unsigned w3, unsigned w4>
591inline Pixel PixelOperations<Pixel>::blend(std::span<const Pixel, 4> p) const
592{
593 return blend<w1, w2, w3, w4>(p[0], p[1], p[2], p[3]);
594}
595
596template<std::unsigned_integral Pixel>
597template<unsigned w1, unsigned w2, unsigned w3,
598 unsigned w4, unsigned w5, unsigned w6>
599inline Pixel PixelOperations<Pixel>::blend(std::span<const Pixel, 6> p) const
600{
601 return blend<w1, w2, w3, w4, w5, w6>(p[0], p[1], p[2], p[3], p[4], p[5]);
602}
603
604template<std::unsigned_integral Pixel>
606{
607 if constexpr (sizeof(Pixel) == 4) {
608 return ((((p & 0x00FF00FF) * x) & 0xFF00FF00) >> 8)
609 | ((((p >> 8) & 0x00FF00FF) * x) & 0xFF00FF00);
610 } else {
611 UNREACHABLE; return 0;
612 }
613}
614
615template<std::unsigned_integral Pixel>
616inline Pixel PixelOperations<Pixel>::lerp(Pixel p1, Pixel p2, unsigned x) const
617{
618 if constexpr (sizeof(Pixel) == 4) { // 32 bpp
619 unsigned rb1 = (p1 >> 0) & 0x00FF00FF;
620 unsigned ag1 = (p1 >> 8) & 0x00FF00FF;
621 unsigned rb2 = (p2 >> 0) & 0x00FF00FF;
622 unsigned ag2 = (p2 >> 8) & 0x00FF00FF;
623
624 // Note: the subtraction for the lower component can 'borrow' from
625 // the higher component. Though in the full calculation this error
626 // magically cancels out.
627 unsigned trb = ((rb2 - rb1) * x) >> 8;
628 unsigned tag = ((ag2 - ag1) * x) >> 0;
629
630 unsigned rb = ((trb + rb1) << 0) & 0x00FF00FF;
631 unsigned ag = (tag + (ag1 << 8)) & 0xFF00FF00;
632
633 return rb | ag;
634
635 } else if constexpr (IS_RGB565) {
636 unsigned rb1 = p1 & 0xF81F;
637 unsigned rb2 = p2 & 0xF81F;
638 unsigned g1 = p1 & 0x07E0;
639 unsigned g2 = p2 & 0x07E0;
640
641 x >>= 2;
642 unsigned trb = ((rb2 - rb1) * x) >> 6;
643 unsigned tg = ((g2 - g1 ) * x) >> 6;
644
645 unsigned rb = (trb + rb1) & 0xF81F;
646 unsigned g = (tg + g1 ) & 0x07E0;
647
648 return rb | g;
649
650 } else {
651 int r1 = red(p1), r2 = red(p2);
652 int g1 = green(p1), g2 = green(p2);
653 int b1 = blue(p1), b2 = blue(p2);
654
655 // note: '/ 256' is not the same as '>> 8' for signed numbers
656 auto r = narrow<unsigned>(((r2 - r1) * narrow<int>(x)) / 256 + r1);
657 auto g = narrow<unsigned>(((g2 - g1) * narrow<int>(x)) / 256 + g1);
658 auto b = narrow<unsigned>(((b2 - b1) * narrow<int>(x)) / 256 + b1);
659
660 return combine(r, g, b);
661 }
662}
663
664template<std::unsigned_integral Pixel>
666{
667 if constexpr (sizeof(Pixel) == 2) {
668 // TODO keep magic value in sync with OutputSurface::getKeyColor()
669 return (p1 == 0x0001) ? p2 : p1;
670 } else {
671 unsigned a = alpha(p1);
672 // Note: 'a' is [0..255], while lerp() expects [0..256].
673 // We ignore this small error.
674 return lerp(p2, p1, a);
675 }
676}
677
678} // namespace openmsx
679
680#endif
int g
unsigned getAshift() const
Definition: PixelFormat.hh:33
unsigned getGloss() const
Definition: PixelFormat.hh:36
unsigned getBshift() const
Definition: PixelFormat.hh:32
unsigned getBloss() const
Definition: PixelFormat.hh:37
unsigned getRshift() const
Definition: PixelFormat.hh:30
unsigned getGmask() const
Definition: PixelFormat.hh:26
unsigned getAmask() const
Definition: PixelFormat.hh:28
unsigned getAloss() const
Definition: PixelFormat.hh:38
unsigned getGshift() const
Definition: PixelFormat.hh:31
unsigned getRmask() const
Definition: PixelFormat.hh:25
unsigned getBmask() const
Definition: PixelFormat.hh:27
unsigned getRloss() const
Definition: PixelFormat.hh:35
PixelOpBase(const PixelFormat &format_)
const PixelFormat & getPixelFormat() const
unsigned getAloss() const
unsigned getGloss() const
unsigned getBmask() const
unsigned getRmask() const
unsigned getRloss() const
unsigned getGshift() const
unsigned getBshift() const
static constexpr bool IS_RGB565
Return true if it's statically known that the pixelformat has a 5-6-5 format (not specified which com...
unsigned getRshift() const
unsigned getAshift() const
unsigned getBloss() const
const PixelFormat & getPixelFormat() const
PixelOpBase(const PixelFormat &format_)
unsigned getGmask() const
Pixel getBlendMask() const
Returns a constant that is useful to calculate the average of two pixel values.
unsigned getAmask() const
Pixel blend(Pixel p1, Pixel p2, Pixel p3, Pixel p4) const
Pixel blend(std::span< const Pixel, 6 > 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 blend(std::span< const Pixel, 2 > p) const
unsigned getMaxRed() const
Get maximum component value.
unsigned blue256(Pixel p) const
Pixel blend(Pixel p1, Pixel p2, Pixel p3, Pixel p4, Pixel p5, Pixel p6) const
unsigned blue(Pixel p) const
unsigned getMaxGreen() const
Pixel blend(Pixel p1, Pixel p2) const
Blend the given colors into a single color.
Pixel blend(std::span< const Pixel, 4 > p) const
unsigned getMaxBlue() const
Pixel blend(Pixel p1, Pixel p2, Pixel p3) const
unsigned red256(Pixel p) const
Same as above, but result is scaled to [0..255].
bool isFullyTransparent(Pixel p) const
unsigned alpha(Pixel p) const
unsigned green256(Pixel p) const
Pixel combine256(unsigned r, unsigned g, unsigned b) const
Pixel combine(unsigned r, unsigned g, unsigned b) const
Combine RGB components to a pixel.
unsigned red(Pixel p) const
Extract RGB components.
PixelOperations(const PixelFormat &format)
Pixel lerp(Pixel p1, Pixel p2, unsigned x) const
Perform linear interpolation between two pixels.
bool isFullyOpaque(Pixel p) const
Pixel blend(std::span< const Pixel, 3 > p) const
unsigned green(Pixel p) const
Pixel alphaBlend(Pixel p1, Pixel p2) const
Perform alpha blending of two pixels.
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)
KeyCode combine(KeyCode key, KeyCode modifier)
Convenience method to create key combinations (hides ugly casts).
Definition: Keys.hh:234
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint32_t Pixel
#define UNREACHABLE
Definition: unreachable.hh:38