public function render()
{
$i = 0;
$w = $this->width;
$w2 = $this->width >> 1;
$h = $this->height;
$m = $this->matrix;
$c = $this->colors;
$ESC = chr(27);
ob_start();
for ($y = 0, $cy = 0; $y < $h; $y += 4, $cy++) {
$cx = 0;
$cy0 = $cy * $w2;
$y0 = $y * $w;
$y1 = ($y + 1) * $w;
$y2 = ($y + 2) * $w;
$y3 = ($y + 3) * $w;
for ($x = 0; $x < $w; $x += 2, $cx++) {
$cell = 0;
$x1 = $x + 1;
foreach ([0x1 => $x1 + $y3, 0x2 => $x + $y3, 0x4 => $x1 + $y2, 0x8 => $x + $y2, 0x10 => $x1 + $y1, 0x20 => $x + $y1, 0x40 => $x1 + $y0, 0x80 => $x + $y0] as $bit => $ofs) {
if (!empty($m[$ofs])) {
$cell |= $bit;
}
}
$dots_r = 0x2800;
if ($cell & 0x80) {
$dots_r |= 0x1;
}
if ($cell & 0x40) {
$dots_r |= 0x8;
}
if ($cell & 0x20) {
$dots_r |= 0x2;
}
if ($cell & 0x10) {
$dots_r |= 0x10;
}
if ($cell & 0x8) {
$dots_r |= 0x4;
}
if ($cell & 0x4) {
$dots_r |= 0x20;
}
if ($cell & 0x2) {
$dots_r |= 0x40;
}
if ($cell & 0x1) {
$dots_r |= 0x80;
}
$dots_r_64 = $dots_r % 64;
$dots_r_4096 = $dots_r % 4096;
// Print UTF-8 character and color
echo $ESC . '[' . ($c[$cy0 + $cx] ? '38;5;' . $c[$cy0 + $cx] : 39) . 'm' . chr(224 + ($dots_r - $dots_r_4096 >> 12)) . chr(128 + ($dots_r_4096 - $dots_r_64 >> 6)) . chr(128 + $dots_r_64);
}
echo $ESC . "[0\n";
}
$buffer = ob_get_contents();
ob_end_clean();
return $buffer;
}