backdrop(); $this->painter->paint($canvas, $markPath, $position, $size, $opacity); return $canvas; } private function backdrop(): SimpleImage { $canvas = (new SimpleImage())->fromNew(self::WIDTH, self::HEIGHT, '#1f2937'); // A left-to-right ramp, one column at a time — GD has no gradient // primitive, and 480 lines is imperceptible next to the encode // that follows. for ($x = 0; $x < self::WIDTH; $x++) { $shade = (int) round(24 + ($x / self::WIDTH) * 210); // alpha 1 is *opaque* in SimpleImage's vocabulary — 0 is the // fully transparent one ('transparent' normalizes to alpha 0). // Getting that backwards draws the whole ramp invisibly, which // no assertion about the mark itself would ever have caught. $canvas->line($x, 0, $x, self::HEIGHT, [ 'red' => $shade, 'green' => $shade, 'blue' => $shade, 'alpha' => 1, ]); } // Two blocks at the extremes of the ramp, so every corner and edge // the position picker offers has both a light and a dark // neighbourhood somewhere near it. $this->fill($canvas, 0, 0, (int) (self::WIDTH * 0.28), (int) (self::HEIGHT * 0.34), '#f8fafc'); $this->fill($canvas, (int) (self::WIDTH * 0.68), (int) (self::HEIGHT * 0.62), self::WIDTH, self::HEIGHT, '#0b1120'); return $canvas; } /** * A filled rectangle, drawn as a run of vertical lines. * * `rectangle(..., 'filled')` does exist and would be the obvious call, * but SimpleImage's own docblock types that parameter `integer|array`, * so passing its documented magic string fails static analysis. Lines * cost nothing here and keep the analyser honest instead of teaching * it to ignore a whole category of argument-type error in this file. * * @param string|array $color */ private function fill(SimpleImage $canvas, int $x1, int $y1, int $x2, int $y2, string|array $color): void { for ($x = $x1; $x <= $x2; $x++) { $canvas->line($x, $y1, $x, $y2, $color); } } }