Connexion
FR
games/xencenter-xenadmin-xcp-jumphp / src / Vnc / Framebuffer.php · commit fb1b7c9
<?phpnamespace App\Vnc;/** * The screen of a VM as the VNC server sent it: one string per row, 4 bytes a pixel (B, G, R, 0: the pixel format asked with * SetPixelFormat, which is also a 32-bit BMP's), and the parts changed since the last picture was taken (dirty()). * Rows apart: changing a rectangle copies only its rows (a single string of 3 MB would be copied for each of them). */final class Framebuffer{    /** @var list<string> */    public array $rows = [];    /** @var array{0: int, 1: int, 2: int, 3: int}|null x1, y1, x2, y2 changed */    private ?array $dirty = null;    public function __construct(public int $width = 0, public int $height = 0)    {        $this->resize($width, $height);    }    public function resize(int $width, int $height): void    {        $this->width = max(0, $width);        $this->height = max(0, $height);        $this->rows = array_fill(0, $this->height, str_repeat("\0\0\0\0", $this->width));        $this->dirty = $this->width > 0 && $this->height > 0 ? [0, 0, $this->width, $this->height] : null;    }    /** Raw pixels of a rectangle (w * h * 4 bytes, rows from the top). */    public function put(int $x, int $y, int $w, int $h, string $pixels): void    {        $stride = $w * 4;              // the rows of $pixels: the rectangle's own width        $skip = max(0, -$x);        $x = max(0, $x);        $w = min($w - $skip, $this->width - $x);        for ($row = 0; $row < $h; $row++) {            $line = $y + $row;            if ($line < 0 || $line >= $this->height || $w <= 0) {                continue;            }            $this->rows[$line] = substr_replace($this->rows[$line], substr($pixels, $row * $stride + $skip * 4, $w * 4), $x * 4, $w * 4);        }        $this->mark($x, $y, $w, $h);    }    /** CopyRect: a rectangle moved from (sx, sy) (a window dragged, a terminal scrolled). */    public function copy(int $sx, int $sy, int $x, int $y, int $w, int $h): void    {        $pieces = [];        for ($row = 0; $row < $h; $row++) {            $pieces[] = substr($this->rows[$sy + $row] ?? '', $sx * 4, $w * 4);        }        $this->put($x, $y, $w, $h, implode('', array_map(fn ($p) => str_pad($p, $w * 4, "\0"), $pieces)));    }    public function fill(int $x, int $y, int $w, int $h, string $pixel): void    {        $this->put($x, $y, $w, $h, str_repeat($pixel, max(0, $w * $h)));    }    public function mark(int $x, int $y, int $w, int $h): void    {        $x2 = min($this->width, $x + $w);        $y2 = min($this->height, $y + $h);        $x = max(0, $x);        $y = max(0, $y);        if ($x2 <= $x || $y2 <= $y) {            return;        }        $this->dirty = $this->dirty === null ? [$x, $y, $x2, $y2]            : [min($this->dirty[0], $x), min($this->dirty[1], $y), max($this->dirty[2], $x2), max($this->dirty[3], $y2)];    }    /** @return array{0: int, 1: int, 2: int, 3: int}|null the changed rectangle (x, y, w, h), forgotten once asked */    public function takeDirty(): ?array    {        $d = $this->dirty;        $this->dirty = null;        return $d === null ? null : [$d[0], $d[1], $d[2] - $d[0], $d[3] - $d[1]];    }    /** A rectangle as a 32-bit BMP file (top-down): SDL_LoadBMP_RW reads it without a copy of each pixel in PHP. */    public function bmp(int $x, int $y, int $w, int $h): string    {        $data = '';        for ($row = $y; $row < $y + $h; $row++) {            $data .= substr($this->rows[$row], $x * 4, $w * 4);        }        return self::bmpHeader($w, $h, strlen($data)) . $data;    }    public static function bmpHeader(int $w, int $h, int $size): string    {        // the height negative = rows from the top; written as an unsigned 32-bit value (the same bytes on 32 and 64-bit PHP)        return 'BM' . pack('VvvV', 54 + $size, 0, 0, 54) . pack('VV', 40, $w) . pack('v', (-$h) & 0xffff) . pack('v', 0xffff)            . pack('vvVVVVVV', 1, 32, 0, $size, 2835, 2835, 0, 0);    }    /** The colour of a pixel (tests): [r, g, b]. */    public function pixel(int $x, int $y): array    {        $p = substr($this->rows[$y] ?? '', $x * 4, 4);        return strlen($p) === 4 ? [ord($p[2]), ord($p[1]), ord($p[0])] : [0, 0, 0];    }}