arsd.pixmappaint

Pixmap image manipulation

Early Technology Preview.
This module is work in progress. API is subject to changes until further notice.

Pixmap refers to raster graphics, a subset of “bitmap” graphics. A pixmap is an array of pixels and the corresponding meta data to describe how an image if formed from those pixels. In the case of this library, a “width” field is used to map a specified number of pixels to a row of an image.

pixels := [ 0, 1, 2, 3 ]
width  := 2

pixmap(pixels, width)
	=> [
		[ 0, 1 ]
		[ 2, 3 ]
	]
pixels := [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]
width  := 3

pixmap(pixels, width)
	=> [
		[ 0,  1,  2 ]
		[ 3,  4,  5 ]
		[ 6,  7,  8 ]
		[ 9, 10, 11 ]
	]
pixels := [ 0, 1, 2, 3, 4, 5, 6, 7 ]
width  := 4

pixmap(pixels, width)
	=> [
		[ 0, 1, 2, 3 ]
		[ 4, 5, 6, 7 ]
	]

Members

Aliases

Blend
alias Blend = BlendMode
BlendFn
alias BlendFn = ubyte function(const ubyte background, const ubyte foreground) pure nothrow @(nogc)
Color
alias Color = arsd.color.Color
ColorF
alias ColorF = arsd.color.ColorF
Pixel
alias Pixel = Color
Point
alias Point = arsd.color.Point
Rectangle
alias Rectangle = arsd.color.Rectangle
Size
alias Size = arsd.color.Size

Enums

BlendAccuracy
enum BlendAccuracy

Alpha-blending accuracy level

BlendMode
enum BlendMode

Blend modes

Functions

alphaBlendRGB
void alphaBlendRGB(Pixel[] target, Pixel[] source)
void alphaBlendRGB(Pixel pxTarget, Pixel pxSource)

Blends source into target with respect to the opacity of the source image (as stored in the alpha channel).

alphaBlendRGBA
void alphaBlendRGBA(Pixel[] target, Pixel[] source)
void alphaBlendRGBA(Pixel pxTarget, Pixel pxSource)

Blends source into target with respect to the opacity of the source image (as stored in the alpha channel).

blendPixels
void blendPixels(Pixel[] target, Pixel[] source)
void blendPixels(Pixel[] target, Pixel[] source, BlendMode mode)
void blendPixels(Pixel[] target, Pixel[] source, BlendMode mode, BlendAccuracy accuracy)

Blends the pixel data of source into target using the requested blending mode.

clamp255
ubyte clamp255(Tint value)

Limits a value to a maximum of 0xFF (= 255).

crop
Pixmap crop(Pixmap source, Pixmap target, Size cropToSize, Point offset)
cropCalcDims
PixmapBlueprint cropCalcDims(Size cropToSize)
cropInPlace
Pixmap cropInPlace(Pixmap source, Size cropToSize, Point offset)
cropNew
Pixmap cropNew(Pixmap source, Size cropToSize, Point offset)

Crops an image to the provided size with the requested offset.

cropTo
void cropTo(Pixmap source, Pixmap target, Point offset)

Crops an image and stores the result in the provided target Pixmap.

decreaseOpacity
Pixel decreaseOpacity(Pixel source, ubyte opacityPercentage)

Lowers the opacity of a Pixel.

decreaseOpacity
Pixmap decreaseOpacity(Pixmap source, Pixmap target, ubyte opacityPercentage)
decreaseOpacityCalcDims
PixmapBlueprint decreaseOpacityCalcDims(Pixmap source)

Lowers the opacity of a Pixmap.

decreaseOpacityF
Pixel decreaseOpacityF(Pixel source, float opacityPercentage)

Lowers the opacity of a Pixel.

decreaseOpacityF
Pixmap decreaseOpacityF(Pixmap source, Pixmap target, float opacityPercentage)
PixmapBlueprint decreaseOpacityF(Pixmap source)
decreaseOpacityFInPlace
void decreaseOpacityFInPlace(Pixmap source, float opacityPercentage)
decreaseOpacityFNew
Pixmap decreaseOpacityFNew(Pixmap source, float opacityPercentage)

Adjusts the opacity of a Pixmap.

decreaseOpacityInPlace
void decreaseOpacityInPlace(Pixmap source, ubyte opacityPercentage)
decreaseOpacityNew
Pixmap decreaseOpacityNew(Pixmap source, ubyte opacityPercentage)

Lowers the opacity of a Pixmap.

drawLine
void drawLine(Pixmap target, Point a, Point b, Pixel color)

Draws a line

drawPixel
void drawPixel(Pixmap target, Point pos, Pixel color)

Draws a single pixel

drawPixmap
void drawPixmap(Pixmap target, Pixmap image, Point pos, Blend blend)

Draws an image (a source pixmap) on a target pixmap

drawPixmap
void drawPixmap(Pixmap target, SubPixmap image, Point pos, Blend blend)

Draws an image (a subimage from a source pixmap) on a target pixmap

drawRectangle
void drawRectangle(Pixmap target, Rectangle rectangle, Pixel color)

Draws a rectangle

drawSprite
void drawSprite(Pixmap target, SpriteSheet sheet, int spriteIndex, Point pos, Blend blend)

Draws a sprite from a spritesheet

flipHorizontally
Pixmap flipHorizontally(Pixmap source, Pixmap target)
flipHorizontallyCalcDims
PixmapBlueprint flipHorizontallyCalcDims(Pixmap source)
flipHorizontallyInPlace
void flipHorizontallyInPlace(Pixmap source)
flipHorizontallyNew
Pixmap flipHorizontallyNew(Pixmap source)

Flips an image horizontally.

flipVertically
Pixmap flipVertically(Pixmap source, Pixmap target)
flipVerticallyCalcDims
PixmapBlueprint flipVerticallyCalcDims(Pixmap source)
flipVerticallyInPlace
void flipVerticallyInPlace(Pixmap source)
flipVerticallyNew
Pixmap flipVerticallyNew(Pixmap source)

Flips an image vertically.

intNormalizedSqrt
ubyte intNormalizedSqrt(ubyte value)

Calculates the square root of the normalized value representated by the input integer number.

intSqrt
ubyte intSqrt(ubyte value)

Calculates the square root of an integer number as an integer number.

invert
Pixel invert(Pixel color)

Inverts a color (to its negative color).

invert
Pixmap invert(Pixmap source, Pixmap target)
invertCalcDims
PixmapBlueprint invertCalcDims(Pixmap source)
invertInPlace
void invertInPlace(Pixmap pixmap)
invertNew
Pixmap invertNew(Pixmap source)

Inverts all colors to produce a negative image.

n255thsOf
ubyte n255thsOf(ubyte nPercentage, ubyte value)

Fast 8-bit “percentage” function

percentageDecimalToUInt8
ubyte percentageDecimalToUInt8(float decimal)
percentageUInt8ToDecimal
float percentageUInt8ToDecimal(ubyte n255ths)
rgb
Pixel rgb(ubyte r, ubyte g, ubyte b)
rgba
Pixel rgba(ubyte r, ubyte g, ubyte b, ubyte a)
rgba
Pixel rgba(ubyte r, ubyte g, ubyte b, float aPct)
rotate180deg
Pixmap rotate180deg(Pixmap source, Pixmap target)

Rotates an image by 180°.

rotate180degCalcDims
PixmapBlueprint rotate180degCalcDims(Pixmap source)
rotate180degInPlace
void rotate180degInPlace(Pixmap source)
rotate180degNew
Pixmap rotate180degNew(Pixmap source)

Rotates an image by 180°.

rotateClockwise
Pixmap rotateClockwise(Pixmap source, Pixmap target)
rotateClockwiseCalcDims
PixmapBlueprint rotateClockwiseCalcDims(Pixmap source)
rotateClockwiseNew
Pixmap rotateClockwiseNew(Pixmap source)

Rotates an image by 90° clockwise.

rotateCounterClockwise
Pixmap rotateCounterClockwise(Pixmap source, Pixmap target)
rotateCounterClockwiseCalcDims
PixmapBlueprint rotateCounterClockwiseCalcDims(Pixmap source)
rotateCounterClockwiseNew
Pixmap rotateCounterClockwiseNew(Pixmap source)

Rotates an image by 90° counter-clockwise.

transpose
Pixmap transpose(Pixmap source, Pixmap target)
transposeCalcDims
PixmapBlueprint transposeCalcDims(Pixmap source)
transposeNew
Pixmap transposeNew(Pixmap source)

Transposes an image.

Structs

Pixmap
struct Pixmap

Pixel data container

PixmapBlueprint
struct PixmapBlueprint

Advanced functionality.

PixmapScanner
struct PixmapScanner

Advanced functionality.

PixmapScannerRW
struct PixmapScannerRW

Advanced functionality.

SpriteSheet
struct SpriteSheet
SubPixmap
struct SubPixmap

A subpixmap represents a subimage of a Pixmap.

SubPixmapScanner
struct SubPixmapScanner

Advanced functionality.

SubPixmapScannerRW
struct SubPixmapScannerRW

Advanced functionality.

Templates

alphaBlend
template alphaBlend(BlendFn blend = null, BlendAccuracy accuracy = BlendAccuracy.rgba)
template alphaBlend(BlendAccuracy accuracy, BlendFn blend = null)

Blends source into target with respect to the opacity of the source image (as stored in the alpha channel).

blendPixel
template blendPixel(BlendMode mode, BlendAccuracy accuracy = BlendAccuracy.rgba)

Blends pixel source into pixel target using the requested blending mode.

Detailed Description

Colors

Colors are stored in an RGBA format with 8 bit per channel. See Pixel for details.

The coordinate system

The top left corner of a pixmap is its origin (0,0).

The $(horizontal axis) is called x. Its corresponding length/dimension is known as width.

The letter y is used to describe the vertical axis. Its corresponding length/dimension is known as height.

0 → x
↓
y

Furthermore, length refers to the areal size of a pixmap. It represents the total number of pixels in a pixmap. It follows from the foregoing that the term long usually refers to the length (not the width).

Pixmaps

A Pixmap consist of two fields:

  • a slice (of an array of Pixels)
  • a width

This design comes with many advantages. First and foremost it brings simplicity.

Pixel data buffers can be reused across pixmaps, even when those have different sizes. Simply slice the buffer to fit just enough pixels for the new pixmap.

Memory management can also happen outside of the pixmap. It is possible to use a buffer allocated elsewhere. (Such a one shouldn’t be mixed with the built-in memory management facilities of the pixmap type. Otherwise one will end up with GC-allocated copies.)

The most important downside is that it makes pixmaps basically a partial reference type.

Copying a pixmap creates a shallow copy still poiting to the same pixel data that is also used by the source pixmap. This implies that manipulating the source pixels also manipulates the pixels of the copy – and vice versa.

The issues implied by this become an apparent when one of the references modifies the pixel data in a way that also affects the dimensions of the image; such as cropping.

Pixmaps describe how pixel data stored in a 1-dimensional memory space is meant to be interpreted as a 2-dimensional image.

A notable implication of this 1D ↔ 2D mapping is, that slicing the 1D data leads to non-sensical results in the 2D space when the 1D-slice is reinterpreted as 2D-image.

Especially slicing across scanlines (→ horizontal rows of an image) is prone to such errors.

(Slicing of the 1D array data can actually be utilized to cut off the bottom part of an image. Any other naiv cropping operations will run into the aforementioned issues.)

Image manipulation

The term “image manipulation function” here refers to functions that manipulate (e.g. transform) an image as a whole.

Image manipulation functions in this library are provided in up to three flavors:

  • a “source to target” function
  • a “source to newly allocated target” wrapper
  • optionally an “in-place” adaption

Additionally, a “compute dimensions of target” function is provided.

Source to Target

The regular “source to target” function takes (at least) two parameters: A source Pixmap and a target Pixmap.

(Additional operation-specific arguments may be required as well.)

The target pixmap usually needs to be able to fit at least the same number of pixels as the source holds. Use the corresponding “compute size of target function” to calculate the required size when needed. (A notable exception would be cropping, where to target pixmap must be only at least long enough to hold the area of the size to crop to.)

The data stored in the buffer of the target pixmap is overwritten by the operation.

A modified Pixmap structure with adjusted dimensions is returned.

These functions are named plain and simple after the respective operation they perform; e.g. flipHorizontally or crop.

// Allocate a new target Pixmap.
Pixmap target = Pixmap.makeNew(
	flipHorizontallyCalcDims(sourceImage)
);

// Flip the image horizontally and store the updated structure.
// (Note: As a horizontal flip does not affect the dimensions of a Pixmap,
//        storing the updated structure would not be necessary
//        in this specific scenario.)
target = sourceImage.flipHorizontally(target);
const cropOffset = Point(0, 0);
const cropSize = Size(100, 100);

// Allocate a new target Pixmap.
Pixmap target = Pixmap.makeNew(
	cropCalcDims(sourceImage, cropSize, cropOffset)
);

// Crop the Pixmap.
target = sourceImage.crop(target, cropSize, cropOffset);
“Source to target” functions do not work in place. Do not attempt to pass Pixmaps sharing the same buffer for both source and target. Such would lead to bad results with heavy artifacts.

Use the “in-place” variant of the operation instead.

Moreover: Do not use the artifacts produced by this as a creative effect. Those are an implementation detail (and may change at any point).

Source to New Target

The “source to newly allocated target” wrapper allocates a new buffer to hold the manipulated target.

These wrappers are provided for user convenience.

They are identified by the suffix -New that is appended to the name of the corresponding “source to target” function; e.g. flipHorizontallyNew or cropNew.

// Create a new flipped Pixmap.
Pixmap target = sourceImage.flipHorizontallyNew();
const cropOffset = Point(0, 0);
const cropSize = Size(100, 100);

// Create a new cropped Pixmap.
Pixmap target = sourceImage.cropNew(cropSize, cropOffset);

In-Place

For selected image manipulation functions a special adaption is provided that stores the result in the source pixel data buffer.

Depending on the operation, implementing in-place transformations can be either straightforward or a major undertaking (and topic of research). This library focuses and the former and leaves out cases where the latter applies. In particular, algorithms that require allocating further buffers to store temporary results or auxiliary data will probably not get implemented.

Furthermore, operations where to result is longer than the source cannot be performed in-place.

Certain in-place manipulation functions return a shallow-copy of the source structure with dimensions adjusted accordingly. This is behavior is not streamlined consistently as the lack of an in-place option for certain operations makes them a special case anyway.

These function are suffixed with -InPlace; e.g. flipHorizontallyInPlace or cropInPlace.

Manipulating the source image directly can lead to unexpected results when the source image is used in multiple places.
Users are usually better off to utilize the regular “source to target” functions with a reused pixel data buffer.

These functions do not serve as a performance optimization. Some of them might perform significantly worse than their regular variant. Always benchmark and profile.

image.flipHorizontallyInPlace();
const cropOffset = Point(0, 0);
const cropSize = Size(100, 100);

image = image.cropInPlace(cropSize, cropOffset);

Compute size of target

Functions to “compute (the) dimensions of (a) target” are primarily meant to be utilized to calculate the size for allocating new pixmaps to be used as a target for manipulation functions.

They are provided for all manipulation functions even in cases where they are provide little to no benefit. This is for consistency and to ease development.

Such functions are identified by a -CalcDims suffix; e.g. flipHorizontallyCalcDims or cropCalcDims.

They usually take the same parameters as their corresponding “source to new target” function. This does not apply in cases where certain parameters are irrelevant for the computation of the target size.

Meta