From 5daeced71617373a3302817f48060ca2e3974be0 Mon Sep 17 00:00:00 2001 From: mniip Date: Thu, 23 Feb 2023 14:09:21 +0100 Subject: [PATCH] Refactor RasterDrawMethods some more --- src/common/Geometry.h | 58 +++++---- src/graphics/Graphics.cpp | 2 +- src/graphics/Graphics.h | 3 - src/graphics/RasterDrawMethods.h | 9 +- src/graphics/RasterDrawMethodsImpl.h | 171 +++++++-------------------- src/graphics/Renderer.h | 6 +- 6 files changed, 88 insertions(+), 161 deletions(-) diff --git a/src/common/Geometry.h b/src/common/Geometry.h index c04b8201e..2c5d3091b 100644 --- a/src/common/Geometry.h +++ b/src/common/Geometry.h @@ -25,12 +25,12 @@ struct Vec2 { } - inline bool operator==(Vec2 other) const + constexpr inline bool operator==(Vec2 other) const { return X == other.X && Y == other.Y; } - inline bool operator!=(Vec2 other) const + constexpr inline bool operator!=(Vec2 other) const { return X != other.X || Y != other.Y; } @@ -41,13 +41,13 @@ struct Vec2 return Vec2() + std::declval())>(X + other.X, Y + other.Y); } - inline Vec2 operator-() const + constexpr inline Vec2 operator-() const { return Vec2(-X, -Y); } template - inline Vec2() - std::declval())> operator-(Vec2 other) const + constexpr inline Vec2() - std::declval())> operator-(Vec2 other) const { return Vec2() - std::declval())>(X - other.X, Y - other.Y); } @@ -59,45 +59,45 @@ struct Vec2 } template, void>> - inline Vec2() / std::declval())> operator/(S other) const + constexpr inline Vec2() / std::declval())> operator/(S other) const { return Vec2() / std::declval())>(X / other, Y / other); } template - inline Vec2 &operator+=(Vec2 other) + constexpr inline Vec2 &operator+=(Vec2 other) { return *this = *this + other; } template - inline Vec2 &operator-=(Vec2 other) + constexpr inline Vec2 &operator-=(Vec2 other) { return *this = *this - other; } template - inline Vec2 &operator*=(Vec2 other) + constexpr inline Vec2 &operator*=(Vec2 other) { return *this = *this * other; } template - inline Vec2 &operator/=(Vec2 other) + constexpr inline Vec2 &operator/=(Vec2 other) { return *this = *this / other; } - inline Vec2 Floor() const + constexpr inline Vec2 Floor() const { return Vec2(std::floor(X), std::floor(Y)); } // Return a rectangle starting at origin, whose dimensions match this vector template, void>> - inline Rect OriginRect() const + constexpr inline Rect OriginRect() const { - return RectSized(Vec2::Zero, *this); + return RectSized(Vec2(0, 0), *this); } static Vec2 Zero; @@ -150,7 +150,7 @@ template Mat2 Mat2::CCW = Mat2(0, 1, -1, 0); // reminder: the Y axis points down template, void>> -static inline Rect RectBetween(Vec2, Vec2); +constexpr static inline Rect RectBetween(Vec2, Vec2); template struct Rect @@ -164,7 +164,7 @@ private: BottomRight(bottomRight) { } - friend Rect RectBetween(Vec2, Vec2); + friend constexpr Rect RectBetween(Vec2, Vec2); struct iterator { @@ -202,6 +202,13 @@ private: }; public: + inline operator bool() const + { + return BottomRight.X >= TopLeft.X || BottomRight.Y >= TopLeft.Y; + } + + // Return the smallest rectangle that contains both input rectangles, + // **assuming neither are empty** inline Rect operator|(Rect other) const { return Rect( @@ -210,6 +217,15 @@ public: ); } + // Return the intersection of two rectangles (possibly empty) + inline Rect operator&(Rect other) const + { + return Rect( + Vec2(std::max(TopLeft.X, other.TopLeft.X), std::max(TopLeft.Y, other.TopLeft.Y)), + Vec2(std::min(BottomRight.X, other.BottomRight.X), std::min(BottomRight.Y, other.BottomRight.Y)) + ); + } + inline Rect &operator|=(Rect other) { return *this = *this | other; @@ -220,16 +236,6 @@ public: return point.X >= TopLeft.X && point.X <= BottomRight.X && point.Y >= TopLeft.Y && point.Y <= BottomRight.Y; } - inline Vec2 Clamp(Vec2 point) const - { - return Vec2(std::clamp(point.X, TopLeft.X, BottomRight.X), std::clamp(point.Y, TopLeft.Y, BottomRight.Y)); - } - - inline Rect Clamp(Rect other) const - { - return Rect(Clamp(other.TopLeft), Clamp(other.BottomRight)); - } - template, void>> inline Vec2 Size() const { @@ -248,7 +254,7 @@ public: }; template -static inline Rect RectBetween(Vec2 topLeft, Vec2 bottomRight) +constexpr static inline Rect RectBetween(Vec2 topLeft, Vec2 bottomRight) { return Rect(topLeft, bottomRight); } @@ -260,7 +266,7 @@ static inline Rect RectAt(Vec2 pos) } template, void>> -static inline Rect RectSized(Vec2 topLeft, Vec2 dimen) +constexpr static inline Rect RectSized(Vec2 topLeft, Vec2 dimen) { return RectBetween(topLeft, topLeft + dimen - Vec2(1, 1)); } diff --git a/src/graphics/Graphics.cpp b/src/graphics/Graphics.cpp index a533edf17..322232b11 100644 --- a/src/graphics/Graphics.cpp +++ b/src/graphics/Graphics.cpp @@ -745,7 +745,7 @@ VideoBuffer Graphics::DumpFrame() void Graphics::SetClipRect(Rect &rect) { - auto newRect = RectBetween(WINDOW.OriginRect().Clamp(rect.TopLeft), WINDOW.OriginRect().Clamp(rect.BottomRight)); + auto newRect = WINDOW.OriginRect() & rect; rect = clip; clip = newRect; } diff --git a/src/graphics/Graphics.h b/src/graphics/Graphics.h index 55f32a2e7..8be76f293 100644 --- a/src/graphics/Graphics.h +++ b/src/graphics/Graphics.h @@ -73,10 +73,7 @@ class Graphics: public RasterDrawMethods { public: Rect clip; - constexpr static bool DoClipCheck = true; constexpr static int VIDXRES = WINDOWW; - constexpr static int VIDYRES = WINDOWH; - constexpr static auto VIDRES = WINDOW; pixel *vid; int sdl_scale; diff --git a/src/graphics/RasterDrawMethods.h b/src/graphics/RasterDrawMethods.h index 0938fd673..5a69cf1d1 100644 --- a/src/graphics/RasterDrawMethods.h +++ b/src/graphics/RasterDrawMethods.h @@ -3,10 +3,15 @@ class VideoBuffer; -// The "Curiously Recurring Template Trick" +// The "Curiously Recurring Template Pattern" trick template -struct RasterDrawMethods +class RasterDrawMethods { +private: + pixel &pixelAt(Vec2); + Rect clipRect(); + +public: int drawtext_outline(int x, int y, const String &s, int r, int g, int b, int a); int drawtext(int x, int y, const String &str, int r, int g, int b, int a); int drawchar(int x, int y, String::value_type c, int r, int g, int b, int a); diff --git a/src/graphics/RasterDrawMethodsImpl.h b/src/graphics/RasterDrawMethodsImpl.h index 1e491550b..924fea896 100644 --- a/src/graphics/RasterDrawMethodsImpl.h +++ b/src/graphics/RasterDrawMethodsImpl.h @@ -4,6 +4,18 @@ #include #include +template +pixel &RasterDrawMethods::pixelAt(Vec2 pos) +{ + return static_cast(this)->vid[pos.X + Derived::VIDXRES * pos.Y]; +} + +template +Rect RasterDrawMethods::clipRect() +{ + return static_cast(this)->clip; +} + template int RasterDrawMethods::drawtext_outline(int x, int y, const String &s, int r, int g, int b, int a) { @@ -123,74 +135,40 @@ int RasterDrawMethods::addchar(int x, int y, String::value_type c, int template void RasterDrawMethods::xor_pixel(int x, int y) { - int c; - if constexpr (Derived::DoClipCheck) - { - if (!(static_cast(this))->clip.Contains(Vec2(x, y))) - return; - } + if (!clipRect().Contains(Vec2(x, y))) + return; + pixel &c = pixelAt(Vec2(x, y)); + if (PIXB(c) + 2 * PIXR(c) + 3 * PIXG(c) < 512) + c = PIXPACK(0xC0C0C0); else - { - if (!Derived::VIDRES.OriginRect().Contains(Vec2(x, y))) - return; - } - c = (static_cast(this))->vid[y*(Derived::VIDXRES)+x]; - c = PIXB(c) + 3*PIXG(c) + 2*PIXR(c); - if (c<512) - (static_cast(this))->vid[y*(Derived::VIDXRES)+x] = PIXPACK(0xC0C0C0); - else - (static_cast(this))->vid[y*(Derived::VIDXRES)+x] = PIXPACK(0x404040); + c = PIXPACK(0x404040); } template void RasterDrawMethods::blendpixel(int x, int y, int r, int g, int b, int a) { - pixel t; - if constexpr (Derived::DoClipCheck) + if (!clipRect().Contains(Vec2(x, y))) + return; + pixel &t = pixelAt(Vec2(x, y)); + if (a != 255) { - if (!(static_cast(this))->clip.Contains(Vec2(x, y))) - return; + r = (a * r + (255 - a) * PIXR(t)) >> 8; + g = (a * g + (255 - a) * PIXG(t)) >> 8; + b = (a * b + (255 - a) * PIXB(t)) >> 8; } - else - { - if (!Derived::VIDRES.OriginRect().Contains(Vec2(x, y))) - return; - } - if (a!=255) - { - t = (static_cast(this))->vid[y*(Derived::VIDXRES)+x]; - r = (a*r + (255-a)*PIXR(t)) >> 8; - g = (a*g + (255-a)*PIXG(t)) >> 8; - b = (a*b + (255-a)*PIXB(t)) >> 8; - } - (static_cast(this))->vid[y*(Derived::VIDXRES)+x] = PIXRGB(r,g,b); + t = PIXRGB(r, g, b); } template void RasterDrawMethods::addpixel(int x, int y, int r, int g, int b, int a) { - pixel t; - if constexpr (Derived::DoClipCheck) - { - if (!(static_cast(this))->clip.Contains(Vec2(x, y))) - return; - } - else - { - if (!Derived::VIDRES.OriginRect().Contains(Vec2(x, y))) - return; - } - t = (static_cast(this))->vid[y*(Derived::VIDXRES)+x]; - r = (a*r + 255*PIXR(t)) >> 8; - g = (a*g + 255*PIXG(t)) >> 8; - b = (a*b + 255*PIXB(t)) >> 8; - if (r>255) - r = 255; - if (g>255) - g = 255; - if (b>255) - b = 255; - (static_cast(this))->vid[y*(Derived::VIDXRES)+x] = PIXRGB(r,g,b); + 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); } template @@ -269,94 +247,37 @@ void RasterDrawMethods::gradientrect(int x, int y, int width, int heigh template void RasterDrawMethods::clearrect(int x, int y, int w, int h) { - int i; - // TODO: change calls to clearrect to use sensible meanings of x, y, w, h then remove these 4 lines x += 1; y += 1; w -= 1; h -= 1; - Rect rect = RectSized(Vec2(x, y), Vec2(w, h)); - if constexpr (Derived::DoClipCheck) - rect = (static_cast(this))->clip.Clamp(rect); - else - rect = Derived::VIDRES.OriginRect().Clamp(rect); - - x = rect.TopLeft.X; - y = rect.TopLeft.Y; - w = rect.Size().X; - h = rect.Size().Y; - - if (w<0 || h<0) + Rect rect = clipRect() & RectSized(Vec2(x, y), Vec2(w, h)); + if (rect.Size().X <= 0 || rect.Size().Y <= 0) return; - - for (i=0; i(this))->vid+(x+(Derived::VIDXRES)*(y+i)), 0, PIXELSIZE*w); + for (int y = rect.TopLeft.Y; y <= rect.BottomRight.Y; y++) + std::fill_n(&pixelAt(Vec2(rect.TopLeft.X, y)), rect.Size().X, PIXPACK(0x000000)); } template void RasterDrawMethods::draw_image(const pixel *img, int x, int y, int w, int h, int a) { - int startX = 0; if (!img) return; - // Adjust height to prevent drawing off the bottom - if (y + h > Derived::VIDYRES) - h = ((Derived::VIDYRES)-y)-1; - // Too big - if (x + w > Derived::VIDXRES) + Rect rect = clipRect() & RectSized(Vec2(x, y), Vec2(w, h)); + if (rect.Size().X <= 0 || rect.Size().Y <= 0) return; - - // Starts off the top of the screen, adjust - if (y < 0 && -y < h) - { - img += -y*w; - h += y; - y = 0; - } - // Starts off the left side of the screen, adjust - if (x < 0 && -x < w) - { - startX = -x; - } - - if (!h || y < 0 || !w) - return; - if (a >= 255) - for (int j = 0; j < h; j++) - { - img += startX; - for (int i = startX; i < w; i++) - { - if constexpr (Derived::DoClipCheck) - { - if ((static_cast(this))->clip.Contains(Vec2(x + i, y + j))) - (static_cast(this))->vid[(y+j)*(Derived::VIDXRES)+(x+i)] = *img; - } - else - { - (static_cast(this))->vid[(y+j)*(Derived::VIDXRES)+(x+i)] = *img; - } - img++; - } - } + if (a == 255) + for (int outY = rect.TopLeft.Y; outY <= rect.BottomRight.Y; outY++) + std::copy_n(&img[(rect.TopLeft.X - x) + (outY - y) * w], rect.Size().X, &pixelAt(Vec2(rect.TopLeft.X, outY))); else - { - int r, g, b; - for (int j = 0; j < h; j++) + for (auto pos : rect) { - img += startX; - for (int i = startX; i < w; i++) - { - r = PIXR(*img); - g = PIXG(*img); - b = PIXB(*img); - blendpixel(x+i, y+j, r, g, b, a); - img++; - } + pixel px = img[(pos.X - x) + (pos.Y - y) * w]; + // TODO: ensure this doesn't redo a bounds check + blendpixel(pos.X, pos.Y, PIXR(px), PIXG(px), PIXB(px), a); } - } } template diff --git a/src/graphics/Renderer.h b/src/graphics/Renderer.h index 1100452b4..080598c9b 100644 --- a/src/graphics/Renderer.h +++ b/src/graphics/Renderer.h @@ -35,10 +35,8 @@ int HeatToColour(float temp); class Renderer: public RasterDrawMethods { public: - constexpr static bool DoClipCheck = false; - constexpr static auto VIDRES = WINDOW; - constexpr static auto VIDXRES = VIDRES.X; - constexpr static auto VIDYRES = VIDRES.Y; + constexpr static auto clip = WINDOW.OriginRect(); + constexpr static auto VIDXRES = WINDOW.X; Simulation * sim; Graphics * g;