Refactor RasterDrawMethods some more

This commit is contained in:
mniip 2023-02-23 14:09:21 +01:00
parent 7fc046c57d
commit 5daeced716
6 changed files with 88 additions and 161 deletions

View File

@ -25,12 +25,12 @@ struct Vec2
{
}
inline bool operator==(Vec2<T> other) const
constexpr inline bool operator==(Vec2<T> other) const
{
return X == other.X && Y == other.Y;
}
inline bool operator!=(Vec2<T> other) const
constexpr inline bool operator!=(Vec2<T> other) const
{
return X != other.X || Y != other.Y;
}
@ -41,13 +41,13 @@ struct Vec2
return Vec2<decltype(std::declval<T>() + std::declval<S>())>(X + other.X, Y + other.Y);
}
inline Vec2<T> operator-() const
constexpr inline Vec2<T> operator-() const
{
return Vec2<T>(-X, -Y);
}
template<typename S>
inline Vec2<decltype(std::declval<T>() - std::declval<S>())> operator-(Vec2<S> other) const
constexpr inline Vec2<decltype(std::declval<T>() - std::declval<S>())> operator-(Vec2<S> other) const
{
return Vec2<decltype(std::declval<T>() - std::declval<S>())>(X - other.X, Y - other.Y);
}
@ -59,45 +59,45 @@ struct Vec2
}
template<typename S, typename = std::enable_if<std::is_arithmetic_v<S>, void>>
inline Vec2<decltype(std::declval<T>() / std::declval<S>())> operator/(S other) const
constexpr inline Vec2<decltype(std::declval<T>() / std::declval<S>())> operator/(S other) const
{
return Vec2<decltype(std::declval<T>() / std::declval<S>())>(X / other, Y / other);
}
template<typename S>
inline Vec2<T> &operator+=(Vec2<S> other)
constexpr inline Vec2<T> &operator+=(Vec2<S> other)
{
return *this = *this + other;
}
template<typename S>
inline Vec2<T> &operator-=(Vec2<S> other)
constexpr inline Vec2<T> &operator-=(Vec2<S> other)
{
return *this = *this - other;
}
template<typename S>
inline Vec2<T> &operator*=(Vec2<S> other)
constexpr inline Vec2<T> &operator*=(Vec2<S> other)
{
return *this = *this * other;
}
template<typename S>
inline Vec2<T> &operator/=(Vec2<S> other)
constexpr inline Vec2<T> &operator/=(Vec2<S> other)
{
return *this = *this / other;
}
inline Vec2<T> Floor() const
constexpr inline Vec2<T> Floor() const
{
return Vec2<T>(std::floor(X), std::floor(Y));
}
// Return a rectangle starting at origin, whose dimensions match this vector
template<typename = std::enable_if<std::is_integral_v<T>, void>>
inline Rect<T> OriginRect() const
constexpr inline Rect<T> OriginRect() const
{
return RectSized(Vec2<T>::Zero, *this);
return RectSized(Vec2<T>(0, 0), *this);
}
static Vec2<T> Zero;
@ -150,7 +150,7 @@ template<typename T, typename V>
Mat2<T> Mat2<T, V>::CCW = Mat2<T>(0, 1, -1, 0); // reminder: the Y axis points down
template<typename T, typename = std::enable_if<std::is_arithmetic_v<T>, void>>
static inline Rect<T> RectBetween(Vec2<T>, Vec2<T>);
constexpr static inline Rect<T> RectBetween(Vec2<T>, Vec2<T>);
template<typename T, typename>
struct Rect
@ -164,7 +164,7 @@ private:
BottomRight(bottomRight)
{
}
friend Rect<T> RectBetween<T>(Vec2<T>, Vec2<T>);
friend constexpr Rect<T> RectBetween<T>(Vec2<T>, Vec2<T>);
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<T> operator|(Rect<T> other) const
{
return Rect<T>(
@ -210,6 +217,15 @@ public:
);
}
// Return the intersection of two rectangles (possibly empty)
inline Rect<T> operator&(Rect<T> other) const
{
return Rect<T>(
Vec2<T>(std::max(TopLeft.X, other.TopLeft.X), std::max(TopLeft.Y, other.TopLeft.Y)),
Vec2<T>(std::min(BottomRight.X, other.BottomRight.X), std::min(BottomRight.Y, other.BottomRight.Y))
);
}
inline Rect<T> &operator|=(Rect<T> 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<T> Clamp(Vec2<T> point) const
{
return Vec2<T>(std::clamp(point.X, TopLeft.X, BottomRight.X), std::clamp(point.Y, TopLeft.Y, BottomRight.Y));
}
inline Rect<T> Clamp(Rect<T> other) const
{
return Rect<T>(Clamp(other.TopLeft), Clamp(other.BottomRight));
}
template<typename = std::enable_if<std::is_integral_v<T>, void>>
inline Vec2<T> Size() const
{
@ -248,7 +254,7 @@ public:
};
template<typename T, typename>
static inline Rect<T> RectBetween(Vec2<T> topLeft, Vec2<T> bottomRight)
constexpr static inline Rect<T> RectBetween(Vec2<T> topLeft, Vec2<T> bottomRight)
{
return Rect<T>(topLeft, bottomRight);
}
@ -260,7 +266,7 @@ static inline Rect<T> RectAt(Vec2<T> pos)
}
template<typename T, typename = std::enable_if<std::is_integral_v<T>, void>>
static inline Rect<T> RectSized(Vec2<T> topLeft, Vec2<T> dimen)
constexpr static inline Rect<T> RectSized(Vec2<T> topLeft, Vec2<T> dimen)
{
return RectBetween<T>(topLeft, topLeft + dimen - Vec2<T>(1, 1));
}

View File

@ -745,7 +745,7 @@ VideoBuffer Graphics::DumpFrame()
void Graphics::SetClipRect(Rect<int> &rect)
{
auto newRect = RectBetween(WINDOW.OriginRect().Clamp(rect.TopLeft), WINDOW.OriginRect().Clamp(rect.BottomRight));
auto newRect = WINDOW.OriginRect() & rect;
rect = clip;
clip = newRect;
}

View File

@ -73,10 +73,7 @@ class Graphics: public RasterDrawMethods<Graphics>
{
public:
Rect<int> 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;

View File

@ -3,10 +3,15 @@
class VideoBuffer;
// The "Curiously Recurring Template Trick"
// The "Curiously Recurring Template Pattern" trick
template<typename Derived>
struct RasterDrawMethods
class RasterDrawMethods
{
private:
pixel &pixelAt(Vec2<int>);
Rect<int> 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);

View File

@ -4,6 +4,18 @@
#include <cmath>
#include <cstring>
template<typename Derived>
pixel &RasterDrawMethods<Derived>::pixelAt(Vec2<int> pos)
{
return static_cast<Derived *>(this)->vid[pos.X + Derived::VIDXRES * pos.Y];
}
template<typename Derived>
Rect<int> RasterDrawMethods<Derived>::clipRect()
{
return static_cast<Derived *>(this)->clip;
}
template<typename Derived>
int RasterDrawMethods<Derived>::drawtext_outline(int x, int y, const String &s, int r, int g, int b, int a)
{
@ -123,74 +135,40 @@ int RasterDrawMethods<Derived>::addchar(int x, int y, String::value_type c, int
template<typename Derived>
void RasterDrawMethods<Derived>::xor_pixel(int x, int y)
{
int c;
if constexpr (Derived::DoClipCheck)
{
if (!(static_cast<Derived *>(this))->clip.Contains(Vec2<int>(x, y)))
return;
}
if (!clipRect().Contains(Vec2<int>(x, y)))
return;
pixel &c = pixelAt(Vec2<int>(x, y));
if (PIXB(c) + 2 * PIXR(c) + 3 * PIXG(c) < 512)
c = PIXPACK(0xC0C0C0);
else
{
if (!Derived::VIDRES.OriginRect().Contains(Vec2<int>(x, y)))
return;
}
c = (static_cast<Derived *>(this))->vid[y*(Derived::VIDXRES)+x];
c = PIXB(c) + 3*PIXG(c) + 2*PIXR(c);
if (c<512)
(static_cast<Derived *>(this))->vid[y*(Derived::VIDXRES)+x] = PIXPACK(0xC0C0C0);
else
(static_cast<Derived *>(this))->vid[y*(Derived::VIDXRES)+x] = PIXPACK(0x404040);
c = PIXPACK(0x404040);
}
template<typename Derived>
void RasterDrawMethods<Derived>::blendpixel(int x, int y, int r, int g, int b, int a)
{
pixel t;
if constexpr (Derived::DoClipCheck)
if (!clipRect().Contains(Vec2<int>(x, y)))
return;
pixel &t = pixelAt(Vec2<int>(x, y));
if (a != 255)
{
if (!(static_cast<Derived *>(this))->clip.Contains(Vec2<int>(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<int>(x, y)))
return;
}
if (a!=255)
{
t = (static_cast<Derived *>(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<Derived *>(this))->vid[y*(Derived::VIDXRES)+x] = PIXRGB(r,g,b);
t = PIXRGB(r, g, b);
}
template<typename Derived>
void RasterDrawMethods<Derived>::addpixel(int x, int y, int r, int g, int b, int a)
{
pixel t;
if constexpr (Derived::DoClipCheck)
{
if (!(static_cast<Derived *>(this))->clip.Contains(Vec2<int>(x, y)))
return;
}
else
{
if (!Derived::VIDRES.OriginRect().Contains(Vec2<int>(x, y)))
return;
}
t = (static_cast<Derived *>(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<Derived *>(this))->vid[y*(Derived::VIDXRES)+x] = PIXRGB(r,g,b);
if (!clipRect().Contains(Vec2<int>(x, y)))
return;
pixel &t = pixelAt(Vec2<int>(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<typename Derived>
@ -269,94 +247,37 @@ void RasterDrawMethods<Derived>::gradientrect(int x, int y, int width, int heigh
template<typename Derived>
void RasterDrawMethods<Derived>::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<int> rect = RectSized(Vec2<int>(x, y), Vec2<int>(w, h));
if constexpr (Derived::DoClipCheck)
rect = (static_cast<Derived *>(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<int> rect = clipRect() & RectSized(Vec2<int>(x, y), Vec2<int>(w, h));
if (rect.Size().X <= 0 || rect.Size().Y <= 0)
return;
for (i=0; i<h; i++)
memset((static_cast<Derived *>(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<int>(rect.TopLeft.X, y)), rect.Size().X, PIXPACK(0x000000));
}
template<typename Derived>
void RasterDrawMethods<Derived>::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<int> rect = clipRect() & RectSized(Vec2<int>(x, y), Vec2<int>(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<Derived *>(this))->clip.Contains(Vec2<int>(x + i, y + j)))
(static_cast<Derived *>(this))->vid[(y+j)*(Derived::VIDXRES)+(x+i)] = *img;
}
else
{
(static_cast<Derived *>(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<int>(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<typename Derived>

View File

@ -35,10 +35,8 @@ int HeatToColour(float temp);
class Renderer: public RasterDrawMethods<Renderer>
{
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;