diff --git a/src/graphics/Graphics.h b/src/graphics/Graphics.h index 8be76f293..aca5fb2bf 100644 --- a/src/graphics/Graphics.h +++ b/src/graphics/Graphics.h @@ -72,8 +72,9 @@ public: class Graphics: public RasterDrawMethods { public: + constexpr static auto VIDXRES = WINDOW.X; + Rect clip; - constexpr static int VIDXRES = WINDOWW; pixel *vid; int sdl_scale; diff --git a/src/graphics/Pixel.h b/src/graphics/Pixel.h index 5224069e9..99d422b9a 100644 --- a/src/graphics/Pixel.h +++ b/src/graphics/Pixel.h @@ -1,8 +1,12 @@ #pragma once +#include #include #include #include +#include +// This is always packed with the least significant byte being blue, +// then green, then red, then 0. typedef uint32_t pixel; constexpr int PIXELCHANNELS = 3; @@ -29,28 +33,97 @@ constexpr int PIXB(pixel x) } template, void>> -struct RGB +class RGBA; + +template, void>> +struct alignas(std::min(alignof(uint32_t), alignof(T))) RGB { - T Red, Green, Blue; + T Blue, Green, Red; constexpr RGB(T r, T g, T b): - Red(r), + Blue(b), Green(g), - Blue(b) + Red(r) { } + + template // Avoid referring to the non-intuitive order of components + RGB(std::initializer_list) = delete; + + template, void>> + inline RGB Blend(RGBA other) const + { + if (other.Alpha == 0xFF) + return other.NoAlpha(); + return RGB( + // Technically should divide by 0xFF, but >> 8 is close enough + (other.Alpha * other.Red + (0xFF - other.Alpha) * Red ) >> 8, + (other.Alpha * other.Green + (0xFF - other.Alpha) * Green) >> 8, + (other.Alpha * other.Blue + (0xFF - other.Alpha) * Blue ) >> 8 + ); + } + + template, void>> + inline RGB Add(RGBA other) const + { + return RGB( + std::min(0xFF, (other.Alpha * other.Red + 0xFF * Red ) >> 8), + std::min(0xFF, (other.Alpha * other.Green + 0xFF * Green) >> 8), + std::min(0xFF, (other.Alpha * other.Blue + 0xFF * Blue ) >> 8) + ); + } + + template, void>> + inline RGB Inverse() const + { + return RGB(0xFF - Red, 0xFF - Green, 0xFF - Blue); + } + + inline RGBA WithAlpha(T a) const + { + return RGBA(Red, Green, Blue, a); + } + + template, void>> + inline pixel Pack() const + { + return PIXRGB(Red, Green, Blue); + } + + template, void>> + static inline RGB Unpack(pixel px) + { + return RGB(PIXR(px), PIXG(px), PIXB(px)); + } }; -template, void>> -struct RGBA +template +struct alignas(std::min(alignof(uint32_t), alignof(T))) RGBA { - T Red, Green, Blue, Alpha; + T Blue, Green, Red, Alpha; - constexpr RGBA(T r, T g, T b, T a = std::numeric_limits::max()): - Red(r), - Green(g), + constexpr RGBA(T r, T g, T b, T a): Blue(b), + Green(g), + Red(r), Alpha(a) { } + + template, void>> + RGBA(T r, T g, T b): + Blue(b), + Green(g), + Red(r), + Alpha(0xFF) + { + } + + template // Avoid referring to the non-intuitive order of components + RGBA(std::initializer_list) = delete; + + RGB NoAlpha() const + { + return RGB(Red, Green, Blue); + } }; diff --git a/src/graphics/RasterDrawMethodsImpl.h b/src/graphics/RasterDrawMethodsImpl.h index 924fea896..3d0d2e252 100644 --- a/src/graphics/RasterDrawMethodsImpl.h +++ b/src/graphics/RasterDrawMethodsImpl.h @@ -7,7 +7,7 @@ template pixel &RasterDrawMethods::pixelAt(Vec2 pos) { - return static_cast(this)->vid[pos.X + Derived::VIDXRES * pos.Y]; + return static_cast(this)->vid[pos.X + static_cast(this)->VIDXRES * pos.Y]; } template @@ -29,14 +29,16 @@ int RasterDrawMethods::drawtext_outline(int x, int y, const String &s, } template -int RasterDrawMethods::drawtext(int x, int y, const String &str, int r, int g, int b, int a) +int RasterDrawMethods::drawtext(int x, int y, const String &str, int r, int g, int b, int alpha) { if(!str.size()) return 0; + auto colour = RGB(r, g, b); + bool underline = false; - int invert = 0; - int oR = r, oG = g, oB = b; + bool invert = false; + auto origColour = colour; int characterX = x, characterY = y; int startX = characterX; for (size_t i = 0; i < str.length(); i++) @@ -50,62 +52,48 @@ int RasterDrawMethods::drawtext(int x, int y, const String &str, int r, { if (str.length() <= i+3) break; - oR = r; - oG = g; - oB = b; - r = (unsigned char)str[i + 1]; - g = (unsigned char)str[i + 2]; - b = (unsigned char)str[i + 3]; + origColour = colour; // ??? + colour.Red = str[i + 1]; + colour.Green = str[i + 2]; + colour.Blue = str[i + 3]; i += 3; } else if (str[i] == '\x0E') { - r = oR; - g = oG; - b = oB; + colour = origColour; } else if (str[i] == '\x01') { invert = !invert; - r = 255-r; - g = 255-g; - b = 255-b; + colour = colour.Inverse(); } else if (str[i] == '\b') { if (str.length() <= i + 1) break; - auto colorCode = false; + bool colourCode = true; switch (str[i + 1]) { - case 'U': underline = !underline; break; - case 'w': r = 255; g = 255; b = 255; colorCode = true; break; - case 'g': r = 192; g = 192; b = 192; colorCode = true; break; - case 'o': r = 255; g = 216; b = 32; colorCode = true; break; - case 'r': r = 255; g = 0; b = 0; colorCode = true; break; - case 'l': r = 255; g = 75; b = 75; colorCode = true; break; - case 'b': r = 0; g = 0; b = 255; colorCode = true; break; - case 't': b = 255; g = 170; r = 32; colorCode = true; break; - case 'u': r = 147; g = 83; b = 211; colorCode = true; break; - } - if (colorCode && invert) - { - r = 255-r; - g = 255-g; - b = 255-b; + case 'U': underline = !underline; colourCode = false; break; + case 'w': colour = RGB(0xFF, 0xFF, 0xFF); break; + case 'g': colour = RGB(0xC0, 0xC0, 0xC0); break; + case 'o': colour = RGB(0xFF, 0xD8, 0x20); break; + case 'r': colour = RGB(0xFF, 0x00, 0x00); break; + case 'l': colour = RGB(0xFF, 0x4B, 0x4B); break; + case 'b': colour = RGB(0x00, 0x00, 0xFF); break; + case 't': colour = RGB(0xFF, 0xAA, 0x20); break; + case 'u': colour = RGB(0x93, 0x53, 0xD3); break; } + if (colourCode && invert) + colour = colour.Inverse(); i++; } else { - auto newCharacterX = drawchar(characterX, characterY, str[i], r, g, b, a); + auto newCharacterX = drawchar(characterX, characterY, str[i], colour.Red, colour.Green, colour.Blue, alpha); if (underline) - { for (int i = characterX; i < newCharacterX; ++i) - { - blendpixel(i, y + FONT_H, r, g, b, a); - } - } + blendpixel(i, y + FONT_H, colour.Red, colour.Green, colour.Blue, alpha); characterX = newCharacterX; } } @@ -150,13 +138,7 @@ void RasterDrawMethods::blendpixel(int x, int y, int r, int g, int b, i if (!clipRect().Contains(Vec2(x, y))) return; pixel &t = pixelAt(Vec2(x, y)); - if (a != 255) - { - r = (a * r + (255 - a) * PIXR(t)) >> 8; - g = (a * g + (255 - a) * PIXG(t)) >> 8; - b = (a * b + (255 - a) * PIXB(t)) >> 8; - } - t = PIXRGB(r, g, b); + t = RGB::Unpack(t).Blend(RGBA(r, g, b, a)).Pack(); } template @@ -165,10 +147,7 @@ void RasterDrawMethods::addpixel(int x, int y, int r, int g, int b, int if (!clipRect().Contains(Vec2(x, y))) return; pixel &t = pixelAt(Vec2(x, y)); - r = std::min(255, (a * r + 255 * PIXR(t)) >> 8); - g = std::min(255, (a * g + 255 * PIXG(t)) >> 8); - b = std::min(255, (a * b + 255 * PIXB(t)) >> 8); - t = PIXRGB(r, g, b); + t = RGB::Unpack(t).Add(RGBA(r, g, b, a)).Pack(); } template