ghul.raster 0.4.1

Prefix Reserved
dotnet add package ghul.raster --version 0.4.1
                    
NuGet\Install-Package ghul.raster -Version 0.4.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ghul.raster" Version="0.4.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ghul.raster" Version="0.4.1" />
                    
Directory.Packages.props
<PackageReference Include="ghul.raster" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ghul.raster --version 0.4.1
                    
#r "nuget: ghul.raster, 0.4.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ghul.raster@0.4.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ghul.raster&version=0.4.1
                    
Install as a Cake Addin
#tool nuget:?package=ghul.raster&version=0.4.1
                    
Install as a Cake Tool

ghul-raster

Drawing into a PNG, from ghūl, with no dependency on a graphics stack.

System.Drawing.Common is GDI+ and throws on Linux; SkiaSharp needs a native library; neither runs under WebAssembly. This library allocates a buffer, draws strokes into it, and writes a PNG, all in managed code, so it runs wherever ghūl does.

use Raster.IMAGE

let image = IMAGE(640, 400)

image.view(-0.5D, -1.4D, 6.8D, 1.4D)

image.colour(20ub, 60ub, 200ub)
image.stroke(2.0D)

image.line(x0, System.Math.sin(x0), x1, System.Math.sin(x1))

image.text(0.1D, 1.15D, 18.0D, "sin x")

image.write("plot.png")

Coordinates

view maps a rectangle of your own coordinates onto the whole image, with y increasing upwards as it does in mathematics rather than downwards as it does in an image. A program then works in the space its problem is stated in - the complex plane, a unit square, the range its data covers - and says nothing about pixels.

The rectangle is mapped exactly, so it stretches where its proportions differ from the image's: a circle drawn in a square rectangle on a 640 by 320 image comes out an ellipse. Choose proportions that match, or accept the stretch.

Without a view, positions are already pixels.

Sizes are always in pixels, whatever the view. A label is as tall as it is asked to be and a stroke as wide, because a label that stretched with the axes would be unreadable on any plot whose axes differ in scale.

Strokes

Everything is drawn as a stroke, and coverage is computed from the distance to the line segment. Ends and joins are round, and the edges are antialiased. stroke sets the width in pixels; colour sets what is laid down.

Shapes

line takes a sequence of points as well as two, and draws through each in turn with the joins rounded the way a single stroke's ends are. It leaves the shape open, so to close one, repeat the first point.

fill takes the same sequence and fills the polygon it encloses, joining the last point to the first. Edges are antialiased by sampling within each row, and a boundary crossed twice leaves a hole rather than a second layer.

flood_fill recolours every pixel joined to the one it is given and the colour that one is. It takes pixels rather than user coordinates, as set_pixel does: the region is a property of the image rather than of what the view describes.

Pixels

set_pixel writes a pixel outright and pixel reads one back. blend lays part of the current colour over what is already there, which is what a program drawing its own shapes needs to antialias them. A position outside the image is ignored by both writers.

Reading a PNG

IMAGE.read decodes a PNG file into an image, and IMAGE.decode does the same from bytes already in hand. Eight bits a channel and not interlaced, which is what anything writing a PNG for a program to read produces: grey, truecolour and indexed, each with or without alpha. An image carries no alpha of its own, so a transparent pixel is composited onto the white it starts as.

A turtle

TURTLE is a pen on an image, at a position and a heading, told how far to go rather than where to go: forward, turn, face, move_to, pen_up and pen_down. A curve stated as a sequence of turns and steps is written that way directly.

Angles are in degrees and follow the image's coordinates, so with a view set a positive turn is counter-clockwise and without one it is the other way about.

Text

text draws with its baseline at the position given and the height in pixels from baseline to the top of a capital. text_width says how wide that text will be, for placing it. The weight follows the height unless it is given.

The face is a Hershey stroke font, so text is drawn with the same strokes as everything else, and scales without a rasteriser.

Saying where an image belongs

A program that draws has two outputs, and nothing in its text says where the pictures go.

image.write("plot.png")
image.show("plot.png")

show writes a line naming the image:

<<image plot.png>>

That is a contract with whatever displays the output, not an instruction to anything in particular. A publisher can turn it into a reference its destination understands and put the file where that reference points; a console that can display images can display it in place; a terminal shows the line, which names a file the reader was told about anyway.

Nothing here knows where the output is going, and nothing that reads the marker needs to know how the image was drawn.

The font

The Hershey Fonts were originally created by Dr. A. V. Hershey while working at the U. S. National Bureau of Standards. The format of the font data was originally created by James Hurt, Cognition, Inc.

fonts/futural.jhf is the face and fonts/hershey.txt is the notice its distribution terms require to accompany it. build/embed-font.sh writes the face into src/font-data.ghul, which is what the library reads: the compiler emits no embedded resources (degory/ghul#2477), and a source string works under WebAssembly and single-file publish, where a file beside the assembly does not.

Tests

dotnet test unit-tests
dotnet ghul-test --use-dotnet-build integration-tests

The unit tests cover the font and the geometry. The integration test draws a plot and compares it against plot.png.expected, which is the assertion that matters: it is the whole picture, and it notices a stroke a fifth of a pixel too thin.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.4.1 0 9/10/2026
0.4.0 421 9/9/2026
0.3.0 36 9/9/2026
0.2.0 78 9/9/2026
0.1.0 33 9/9/2026
0.0.2 39 9/9/2026
0.0.1 44 9/9/2026