From 762280aa4cfbfe8733d39a0f0d4befa3dee1d46d Mon Sep 17 00:00:00 2001 From: Elias Batek Date: Wed, 24 Apr 2024 02:23:41 +0200 Subject: [PATCH] Implement a couple of drawing functions Copied over from older codebases of mine. --- pixmappaint.d | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) diff --git a/pixmappaint.d b/pixmappaint.d index ad32f0d..1d91e2c 100644 --- a/pixmappaint.d +++ b/pixmappaint.d @@ -115,8 +115,188 @@ struct Pixmap { return (width * int(Pixel.sizeof)); } + /++ + Retrieves a linear slice of the pixmap. + + Returns: + `n` pixels starting at the top-left position `pos`. + +/ + inout(Pixel)[] sliceAt(Point pos, int n) inout { + immutable size_t offset = linearOffset(width, pos); + immutable size_t end = (offset + n); + return data[offset .. end]; + } + /// Clears the buffer’s contents (by setting each pixel to the same color) void clear(Pixel value) { data[] = value; } } + +// Alpha-blending functions +@safe pure nothrow @nogc { + + /// + public void alphaBlend(scope Pixel[] target, scope const Pixel[] source) @trusted + in (source.length == target.length) { + foreach (immutable idx, ref pxtarget; target) { + alphaBlend(pxtarget, source.ptr[idx]); + } + } + + /// + public void alphaBlend(ref Pixel pxTarget, const Pixel pxSource) @trusted { + pragma(inline, true); + + immutable alphaSource = (pxSource.a | (pxSource.a << 8)); + immutable alphaTarget = (0xFFFF - alphaSource); + + foreach (immutable ib, ref px; pxTarget.components) { + immutable d = cast(ubyte)(((px * alphaTarget) + 0x8080) >> 16); + immutable s = cast(ubyte)(((pxSource.components.ptr[ib] * alphaSource) + 0x8080) >> 16); + px = cast(ubyte)(d + s); + } + } +} + +// Drawing functions +@safe pure nothrow @nogc { + + private { + struct OriginRectangle { + Size size; + + @safe pure nothrow @nogc: + + int left() const => 0; + int top() const => 0; + int right() const => size.width; + int bottom() const => size.height; + + bool intersect(const Rectangle b) const { + // dfmt off + return ( + (b.right > 0 ) && + (b.left < this.right ) && + (b.bottom > 0 ) && + (b.top < this.bottom) + ); + // dfmt on + } + } + + Point pos(Rectangle r) => r.upperLeft; + } + + /++ + Draws a single pixel + +/ + void drawPixel(Pixmap target, Point pos, Pixel color) { + immutable size_t offset = linearOffset(target.width, pos); + target.data[offset] = color; + } + + /++ + Draws a rectangle + +/ + void drawRectangle(Pixmap target, Rectangle rectangle, Pixel color) { + alias r = rectangle; + + immutable tRect = OriginRectangle( + Size(target.width, target.height), + ); + + // out of bounds? + if (!tRect.intersect(r)) { + return; + } + + immutable drawingTarget = Point( + (r.pos.x >= 0) ? r.pos.x : 0, + (r.pos.y >= 0) ? r.pos.y : 0, + ); + + immutable drawingEnd = Point( + (r.right < tRect.right) ? r.right : tRect.right, + (r.bottom < tRect.bottom) ? r.bottom : tRect.bottom, + ); + + immutable int drawingWidth = drawingEnd.x - drawingTarget.x; + + foreach (y; drawingTarget.y .. drawingEnd.y) { + target.sliceAt(Point(drawingTarget.x, y), drawingWidth)[] = color; + } + } + + /++ + Draws a line + +/ + void drawLine(Pixmap target, Point a, Point b, Pixel color) { + import std.math : round, sqrt; + + float deltaX = b.x - a.x; + float deltaY = b.y - a.y; + int steps = sqrt(deltaX * deltaX + deltaY * deltaY).typeCast!int; + + float[2] step = [ + (deltaX / steps), + (deltaY / steps), + ]; + + foreach (i; 0 .. steps) { + // dfmt off + immutable Point p = a + Point( + round(step[0] * i).typeCast!int, + round(step[1] * i).typeCast!int, + ); + // dfmt on + + immutable offset = linearOffset(p, target.width); + target.data[offset] = color; + } + + immutable offsetEnd = linearOffset(b, target.width); + target.data[offsetEnd] = color; + } + + /++ + Draws an image (a source pixmap) on a target pixmap + + Params: + target = target pixmap to draw on + image = source pixmap + pos = top-left destination position (on the target pixmap) + +/ + void drawPixmap(Pixmap target, Pixmap image, Point pos) { + alias source = image; + + immutable tRect = OriginRectangle( + Size(target.width, target.height), + ); + + immutable sRect = Rectangle(pos, source.size); + + // out of bounds? + if (!tRect.intersect(sRect)) { + return; + } + + immutable drawingTarget = Point( + (pos.x >= 0) ? pos.x : 0, + (pos.y >= 0) ? pos.y : 0, + ); + + immutable drawingEnd = Point( + (sRect.right < tRect.right) ? sRect.right : tRect.right, + (sRect.bottom < tRect.bottom) ? sRect.bottom : tRect.bottom, + ); + + immutable drawingSource = Point(drawingTarget.x, 0) - Point(sRect.pos.x, sRect.pos.y); + immutable int drawingWidth = drawingEnd.x - drawingTarget.x; + + foreach (y; drawingTarget.y .. drawingEnd.y) { + target.sliceAt(Point(drawingTarget.x, y), drawingWidth)[] = + source.sliceAt(Point(drawingSource.x, y + drawingSource.y), drawingWidth); + } + } +}