frame-capture.dhover×150all
#!/usr/bin/env dub
/+ dub.sdl:
    name "manim_frame_capture"
    targetPath "build"
+/
/**
 * The native output pipeline in miniature: rasterise a frame into an RGBA
 * buffer, read the pixels back, and reduce them to a checksum — the exact
 * shape of the "render → framebuffer readback → encode" path every native
 * Manim-class engine runs, minus the GPU and the codec.
 *
 * The *output & encoding* axis of the analysis spine. A real backend fills
 * this buffer for you: Cairo hands back `cairo_image_surface_get_data`,
 * raylib exposes it through `TakeScreenshot` / an offscreen `RenderTexture`
 * (already exercised in this repo's `apps/terminal/src/app.d`), and the raw
 * RGBA bytes are then piped to ffmpeg's stdin (`-f rawvideo -pix_fmt rgba`,
 * ManimGL's exact command) or handed to libav. This probe stands in a pure
 * software rasteriser so it compiles and runs with zero dependencies and no
 * display, while grounding the claim that a frame is *just an addressable
 * RGBA buffer* an encoder consumes — the interface the proposal's renderer
 * `readback()` capability returns.
 *
 * It rasterises a background clear plus a filled disc (analytic coverage AA
 * on the boundary — the same anti-aliasing concern §axis 3 raises), reads the
 * buffer back, counts non-background pixels, and prints a deterministic
 * FNV-1a checksum. Determinism of this checksum across runs is precisely what
 * makes per-`play()` content-hash caching (§axis 8) correct.
 *
 * Companion to docs/research/manim/rendering-backends/gpu-vector.md
 *   § "Framebuffer readback" and docs/research/manim/video-encoding.md
 *   § "The raw-RGBA pipe".
 * Run with: dub run --single frame-capture.d
 *
 * Portability: pure software rasterisation, no GPU / display / external
 * dependency — deterministic on every host (unlike a live raylib window,
 * which would need a display and is covered by apps/terminal instead).
 */
module 
(module) manim_frame_capture

The native output pipeline in miniature: rasterise a frame into an RGBA buffer, read the pixels back, and reduce them to a checksum — the exact shape of the "render → framebuffer readback → encode" path every native Manim-class engine runs, minus the GPU and the codec.

The output & encoding axis of the analysis spine. A real backend fills this buffer for you: Cairo hands back cairo_image_surface_get_data, raylib exposes it through TakeScreenshot / an offscreen RenderTexture (already exercised in this repo's apps/terminal/src/app.d), and the raw RGBA bytes are then piped to ffmpeg's stdin (-f rawvideo -pix_fmt rgba, ManimGL's exact command) or handed to libav. This probe stands in a pure software rasteriser so it compiles and runs with zero dependencies and no display, while grounding the claim that a frame is just an addressable RGBA buffer an encoder consumes — the interface the proposal's renderer readback() capability returns.

It rasterises a background clear plus a filled disc (analytic coverage AA on the boundary — the same anti-aliasing concern §axis 3 raises), reads the buffer back, counts non-background pixels, and prints a deterministic FNV-1a checksum. Determinism of this checksum across runs is precisely what makes per-play() content-hash caching (§axis 8) correct.

Companion to docs/research/manim/rendering-backends/gpu-vector.md § "Framebuffer readback" and docs/research/manim/video-encoding.md § "The raw-RGBA pipe". Run with: dub run --single frame-capture.d

Portability

pure software rasterisation, no GPU / display / external dependency — deterministic on every host (unlike a live raylib window, which would need a display and is covered by apps/terminal instead).

manim_frame_capture
;
import
(package) std
std
.
(module) std.math

Contains the elementary mathematical functions (powers, roots, and trigonometric functions), and low-level floating-point operations. Mathematical special functions are available in std.mathspecial.

Category Members
Constants E PI PI_2 PI4 M1_PI M2_PI M2_SQRTPI LN10 LN2 LOG2 LOG2E LOG2T LOG10E SQRT2 SQRT1_2
Algebraic abs fabs sqrt cbrt hypot poly nextPow2 truncPow2
Trigonometry sin cos tan asin acos atan atan2 sinh cosh tanh asinh acosh atanh
Rounding ceil floor round lround trunc rint lrint nearbyint rndtol quantize
Exponentiation & Logarithms pow powmod exp exp2 expm1 ldexp frexp log log2 log10 logb ilogb log1p scalbn
Remainder fmod modf remainder remquo
Floating-point operations approxEqual feqrel fdim fmax fmin fma isClose nextDown nextUp nextafter NaN getNaNPayload cmp
Introspection isFinite isIdentical isInfinity isNaN isNormal isSubnormal signbit sgn copysign isPowerOf2
Hardware Control IeeeFlags ieeeFlags resetIeeeFlags FloatingPointControl

The functionality closely follows the IEEE754-2008 standard for floating-point arithmetic, including the use of camelCase names rather than C99-style lower case names. All of these functions behave correctly when presented with an infinity or NaN.

The following IEEE 'real' formats are currently supported:

  • 64 bit Big-endian 'double' (eg PowerPC)

  • 128 bit Big-endian 'quadruple' (eg SPARC)

  • 64 bit Little-endian 'double' (eg x86-SSE2)

  • 80 bit Little-endian, with implied bit 'real80' (eg x87, Itanium)

  • 128 bit Little-endian 'quadruple' (not implemented on any known processor!)

  • Non-IEEE 128 bit Big-endian 'doubledouble' (eg PowerPC) has partial support

Unlike C, there is no global 'errno' variable. Consequently, almost all of these functions are pure nothrow.

Source

std/math/package.d

@copyrightCopyright The D Language Foundation 2000 - 2011. D implementations of tan, atan, atan2, exp, expm1, exp2, log, log10, log1p, log2, floor, ceil and lrint functions are based on the CEPHES math library, which is Copyright (C) 2001 Stephen L. Moshier <steve@moshier.net> and are incorporated herein by permission of the author. The author reserves the right to distribute this material elsewhere under different copying permissions. These modifications are distributed here under the following terms:@licenseBoost License 1.0.@authorsWalter Bright, Don Clugston, Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger
math
:
(alias) manim_frame_capture.sqrt = float std.math.algebraic.sqrt(float x) pure nothrow @nogc @safe

Compute square root of x.

x sqrt(x) invalid?
-0.0 -0.0 no
<0.0 yes
+ + no
sqrt
;
import
(package) std
std
.
(module) std.stdio
Category Symbols
File handles _popen File isFileHandle openNetwork stderr stdin stdout
Reading chunks lines readf readfln readln
Writing toFile write writef writefln writeln
Misc KeepTerminator LockType StdioException

Standard I/O functions that extend core.stdc.stdio. core.stdc.stdio is publically imported when importing std.stdio.

There are three layers of I/O:

  1. The lowest layer is the operating system layer. The two main schemes are Windows and Posix.

  2. C's stdio.h which unifies the two operating system schemes.

  3. std.stdio, this module, unifies the various stdio.h implementations into a high level package for D programs.

Source

std/stdio.d

@copyrightCopyright The D Language Foundation 2007-.@licenseBoost License 1.0.@authorsWalter Bright, Andrei Alexandrescu, Alex Rønne Petersen
stdio
:
(alias template) manim_frame_capture.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))

Equivalent to writef(fmt, args, '\n').

writefln
,
(alias template) manim_frame_capture.writeln = std.stdio.writeln(T...)(T args)

Equivalent to write(args, '\n'). Calling writeln without arguments is valid and just prints a newline to the standard output.

Example

Reads stdin and writes it to stdout with an argument counter.

import std.stdio;

void main()
{
    string line;

    for (size_t count = 0; (line = readln) !is null; count++)
    {
         writeln("Input ", count, ": ", line);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
;
enum
(constant) int manim_frame_capture.W = 64
W
= 64,
(constant) int manim_frame_capture.H = 48
H
= 48;
struct
(struct) manim_frame_capture.Framebuffer
Framebuffer
{ ubyte[
(constant) int manim_frame_capture.W = 64
W
*
(constant) int manim_frame_capture.H = 48
H
* 4]
(field) ubyte[12288] manim_frame_capture.Framebuffer.px
px
; // RGBA8, row-major
void
void manim_frame_capture.Framebuffer.clear(ubyte r, ubyte g, ubyte b, ubyte a) pure nothrow @nogc @safe
clear
(ubyte
(parameter) ubyte r
r
, ubyte
(parameter) ubyte g
g
, ubyte
(parameter) ubyte b
b
, ubyte
(parameter) ubyte a
a
) @safe pure nothrow @nogc
{ foreach (
(local variable) int i
i
; 0 ..
(constant) int manim_frame_capture.W = 64
W
*
(constant) int manim_frame_capture.H = 48
H
)
{
(field) ubyte[12288] manim_frame_capture.Framebuffer.px
px
[
(local variable) int i
i
* 4 + 0] =
(parameter) ubyte r
r
;
(field) ubyte[12288] manim_frame_capture.Framebuffer.px
px
[
(local variable) int i
i
* 4 + 1] =
(parameter) ubyte g
g
;
(field) ubyte[12288] manim_frame_capture.Framebuffer.px
px
[
(local variable) int i
i
* 4 + 2] =
(parameter) ubyte b
b
;
(field) ubyte[12288] manim_frame_capture.Framebuffer.px
px
[
(local variable) int i
i
* 4 + 3] =
(parameter) ubyte a
a
;
} } /// Alpha-composite a color over pixel (x,y) with coverage in [0,1]. void
void manim_frame_capture.Framebuffer.blend(int x, int y, ubyte r, ubyte g, ubyte b, double cov) pure nothrow @nogc @safe

Alpha-composite a color over pixel (x,y) with coverage in 0,1.

blend
(int
(parameter) int x
x
, int
(parameter) int y
y
, ubyte
(parameter) ubyte r
r
, ubyte
(parameter) ubyte g
g
, ubyte
(parameter) ubyte b
b
, double
(parameter) double cov
cov
) @safe pure nothrow @nogc
{ if (
(parameter) int x
x
< 0 ||
(parameter) int x
x
>=
(constant) int manim_frame_capture.W = 64
W
||
(parameter) int y
y
< 0 ||
(parameter) int y
y
>=
(constant) int manim_frame_capture.H = 48
H
||
(parameter) double cov
cov
<= 0)
return; const
(local variable) const(int) i
i
= (
(parameter) int y
y
*
(constant) int manim_frame_capture.W = 64
W
+
(parameter) int x
x
) * 4;
void
void manim_frame_capture.Framebuffer.blend.over(ulong k, ubyte src) pure nothrow @nogc @safe
over
(
(alias) object.size_t = ulong
size_t
(parameter) ulong k
k
, ubyte
(parameter) ubyte src
src
)
{
(field) ubyte[12288] manim_frame_capture.Framebuffer.px
px
[
(local variable) const(int) i
i
+
(parameter) ulong k
k
] = cast(ubyte)(
(parameter) ubyte src
src
*
(parameter) double cov
cov
+
(field) ubyte[12288] manim_frame_capture.Framebuffer.px
px
[
(local variable) const(int) i
i
+
(parameter) ulong k
k
] * (1 -
(parameter) double cov
cov
) + 0.5);
}
void manim_frame_capture.Framebuffer.blend.over(ulong k, ubyte src) pure nothrow @nogc @safe
over
(0,
(parameter) ubyte r
r
);
void manim_frame_capture.Framebuffer.blend.over(ulong k, ubyte src) pure nothrow @nogc @safe
over
(1,
(parameter) ubyte g
g
);
void manim_frame_capture.Framebuffer.blend.over(ulong k, ubyte src) pure nothrow @nogc @safe
over
(2,
(parameter) ubyte b
b
);
} /// Filled disc with 1px analytic-coverage anti-aliased edge. void
void manim_frame_capture.Framebuffer.disc(double cx, double cy, double rad, ubyte r, ubyte g, ubyte b) pure nothrow @nogc @safe

Filled disc with 1px analytic-coverage anti-aliased edge.

disc
(double
(parameter) double cx
cx
, double
(parameter) double cy
cy
, double
(parameter) double rad
rad
, ubyte
(parameter) ubyte r
r
, ubyte
(parameter) ubyte g
g
, ubyte
(parameter) ubyte b
b
) @safe pure nothrow @nogc
{ foreach (
(local variable) int y
y
; 0 ..
(constant) int manim_frame_capture.H = 48
H
)
foreach (
(local variable) int x
x
; 0 ..
(constant) int manim_frame_capture.W = 64
W
)
{ const
(local variable) const(double) d
d
=
double std.math.algebraic.sqrt(double x) pure nothrow @nogc @safe

Compute square root of x.

x sqrt(x) invalid?
-0.0 -0.0 no
<0.0 yes
+ + no
sqrt
((
(local variable) int x
x
+ 0.5 -
(parameter) double cx
cx
) ^^ 2 + (
(local variable) int y
y
+ 0.5 -
(parameter) double cy
cy
) ^^ 2);
const
(local variable) const(double) cov
cov
=
(local variable) const(double) d
d
<=
(parameter) double rad
rad
- 0.5 ? 1.0 : (
(local variable) const(double) d
d
>=
(parameter) double rad
rad
+ 0.5 ? 0.0 :
(parameter) double rad
rad
+ 0.5 -
(local variable) const(double) d
d
);
void manim_frame_capture.Framebuffer.blend(int x, int y, ubyte r, ubyte g, ubyte b, double cov) pure nothrow @nogc @safe

Alpha-composite a color over pixel (x,y) with coverage in 0,1.

blend
(
(local variable) int x
x
,
(local variable) int y
y
,
(parameter) ubyte r
r
,
(parameter) ubyte g
g
,
(parameter) ubyte b
b
,
(local variable) const(double) cov
cov
);
} } } /// FNV-1a over the whole readback buffer — a stand-in for a content hash. ulong
ulong manim_frame_capture.fnv1a(in ubyte[] bytes) pure nothrow @nogc @safe

FNV-1a over the whole readback buffer — a stand-in for a content hash.

fnv1a
(in ubyte[]
(parameter) const(ubyte[]) bytes
bytes
) @safe pure nothrow @nogc
{ ulong
(local variable) ulong h
h
= 0xcbf29ce484222325;
foreach (
(parameter) const(ubyte) b
b
;
(parameter) const(ubyte[]) bytes
bytes
)
{
(local variable) ulong h
h
^=
(local variable) const(ubyte) b
b
;
(local variable) ulong h
h
*= 0x100000001b3;
} return
(local variable) ulong h
h
;
} int
int D main() @safe
main
() @safe
{
(struct) manim_frame_capture.Framebuffer
Framebuffer
(local variable) manim_frame_capture.Framebuffer fb
fb
;
(local variable) manim_frame_capture.Framebuffer fb
fb
.
void manim_frame_capture.Framebuffer.clear(ubyte r, ubyte g, ubyte b, ubyte a) pure nothrow @nogc @safe
clear
(16, 16, 24, 255); // dark background
(local variable) manim_frame_capture.Framebuffer fb
fb
.
void manim_frame_capture.Framebuffer.disc(double cx, double cy, double rad, ubyte r, ubyte g, ubyte b) pure nothrow @nogc @safe

Filled disc with 1px analytic-coverage anti-aliased edge.

disc
(
(constant) int manim_frame_capture.W = 64
W
/ 2.0,
(constant) int manim_frame_capture.H = 48
H
/ 2.0, 16, 240, 120, 40); // one filled, AA-edged disc
// "Read back" the buffer — exactly what an encoder is handed. const
(local variable) const(ubyte[]) bytes
bytes
=
(local variable) manim_frame_capture.Framebuffer fb
fb
.
(field) ubyte[12288] manim_frame_capture.Framebuffer.px
px
[];
(alias) object.size_t = ulong
size_t
(local variable) ulong nonBg
nonBg
;
foreach (
(local variable) int i
i
; 0 ..
(constant) int manim_frame_capture.W = 64
W
*
(constant) int manim_frame_capture.H = 48
H
)
if (!(
(local variable) const(ubyte[]) bytes
bytes
[
(local variable) int i
i
* 4] == 16 &&
(local variable) const(ubyte[]) bytes
bytes
[
(local variable) int i
i
* 4 + 1] == 16 &&
(local variable) const(ubyte[]) bytes
bytes
[
(local variable) int i
i
* 4 + 2] == 24))
(local variable) ulong nonBg
nonBg
++;
void std.stdio.writeln!string(string __param_0) @safe

Equivalent to write(args, '\n'). Calling writeln without arguments is valid and just prints a newline to the standard output.

Example

Reads stdin and writes it to stdout with an argument counter.

import std.stdio;

void main()
{
    string line;

    for (size_t count = 0; (line = readln) !is null; count++)
    {
         writeln("Input ", count, ": ", line);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
("== software frame capture (stand-in for Cairo/raylib readback) ==");
void std.stdio.writefln!(char, int, int, ulong)(in char[] fmt, int __param_1, int __param_2, ulong __param_3) @safe

Equivalent to writef(fmt, args, '\n').

writefln
(" frame : %d x %d RGBA8 (%d bytes)",
(constant) int manim_frame_capture.W = 64
W
,
(constant) int manim_frame_capture.H = 48
H
,
(local variable) const(ubyte[]) bytes
bytes
.
(field) ulong const(ubyte[]).length
length
);
void std.stdio.writefln!(char, ulong, int)(in char[] fmt, ulong __param_1, int __param_2) @safe

Equivalent to writef(fmt, args, '\n').

writefln
(" drawn pixels : %d / %d (disc + AA edge over background)",
(local variable) ulong nonBg
nonBg
,
(constant) int manim_frame_capture.W = 64
W
*
(constant) int manim_frame_capture.H = 48
H
);
void std.stdio.writefln!(char, ulong)(in char[] fmt, ulong __param_1) @safe

Equivalent to writef(fmt, args, '\n').

writefln
(" readback checksum: 0x%016x (FNV-1a — deterministic across runs)",
ulong manim_frame_capture.fnv1a(in ubyte[] bytes) pure nothrow @nogc @safe

FNV-1a over the whole readback buffer — a stand-in for a content hash.

fnv1a
(
(local variable) const(ubyte[]) bytes
bytes
));
void std.stdio.writeln!string(string __param_0) @safe

Equivalent to write(args, '\n'). Calling writeln without arguments is valid and just prints a newline to the standard output.

Example

Reads stdin and writes it to stdout with an argument counter.

import std.stdio;

void main()
{
    string line;

    for (size_t count = 0; (line = readln) !is null; count++)
    {
         writeln("Input ", count, ": ", line);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
(" → these raw RGBA bytes are the input to `ffmpeg -f rawvideo -pix_fmt rgba`");
void std.stdio.writeln!string(string __param_0) @safe

Equivalent to write(args, '\n'). Calling writeln without arguments is valid and just prints a newline to the standard output.

Example

Reads stdin and writes it to stdout with an argument counter.

import std.stdio;

void main()
{
    string line;

    for (size_t count = 0; (line = readln) !is null; count++)
    {
         writeln("Input ", count, ": ", line);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
(" or libav; the stable checksum is what makes play()-level caching correct.");
return 0; }