Skip to content

Commit

Permalink
Merge pull request #5 from kellegous/drawing
Browse files Browse the repository at this point in the history
added basic drawing
  • Loading branch information
kellegous authored May 2, 2024
2 parents 984e9e2 + df11a7f commit 7b81b6d
Show file tree
Hide file tree
Showing 10 changed files with 475 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/php.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: PHP Composer
name: Validation

on:
push:
Expand Down
17 changes: 14 additions & 3 deletions bin/test.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,19 @@

declare(strict_types=1);

use Kellegous\Algs4\Graphics\Destination;
use Kellegous\Algs4\Graphics\Drawing;

require __DIR__ . '/../vendor/autoload.php';

var_dump(
['3' => 1]
);
$m = new Drawing(800, 600);
$m->floodFill(400, 300, $m->colorFromRGB(0xff, 0xff, 0xff));
$m->ellipse(400, 300, 200, 150)
->fill($m->colorFromRGBA(0, 0, 0, 200));
$m->writePNG(Destination::toFile('test.png'));

$a = $m->colorFromRGB(0xff, 0xff, 0xff);
var_dump($a->alpha());
printf("%x\n", $a->getID());
$b = $m->colorFromRGBA(0xff, 0xff, 0xff, 127);
printf("alpha = %x\n", $b->alpha());
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
],
"require": {
"php": "^8.3",
"ext-gd": "*",
"symfony/console": "^7.0"
},
"require-dev": {
Expand Down
5 changes: 3 additions & 2 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

114 changes: 114 additions & 0 deletions src/Graphics/Color.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<?php

declare(strict_types=1);

namespace Kellegous\Algs4\Graphics;

/**
* Representation of an RGBA color.
*/
final readonly class Color
{
/**
* @param int $id
*/
private function __construct(
private int $id
) {
}

/**
* Create a new opaque color from the given RGB values.
* @param Drawing $drawing
* @param int<0,255> $r
* @param int<0,255> $g
* @param int<0,255> $b
* @return self
* @throws Exception
* @see Drawing::colorFromRGB()
*/
public static function fromRGB(
Drawing $drawing,
int $r,
int $g,
int $b,
): self {
$c = imagecolorallocate($drawing->getImage(), $r, $g, $b);
if ($c === false) {
throw new Exception('unable to allocate color');
}
return new self($c);
}

/**
* Create a new opaque color from the given RGB values.
* @param Drawing $drawing
* @param int<0,255> $r
* @param int<0,255> $g
* @param int<0,255> $b
* @param int<0,255> $a
* @return self
* @throws Exception
*/
public static function fromRGBA(
Drawing $drawing,
int $r,
int $g,
int $b,
int $a,
): self {
$c = imagecolorallocatealpha($drawing->getImage(), $r, $g, $b, $a >> 1);
if ($c === false) {
throw new Exception('unable to allocate color');
}
return new self($c);
}

/**
* Get the GD color index. This will be the color as an integer.
* @return int
* @internal
*/
public function getID(): int
{
return $this->id;
}

/**
* Get the red component of the color.
* @return int<0,255>
*/
public function red(): int
{
return ($this->id >> 16) & 0xff;
}

/**
* Get the green component of the color.
* @return int<0,255>
*/
public function green(): int
{
return ($this->id >> 8) & 0xff;
}

/**
* Get the blue component of the color.
* @return int<0,255>
*/
public function blue(): int
{
return $this->id & 0xff;
}

/**
* Get the alpha component of the color.
* @return int<0,255>
*/
public function alpha(): int
{
$a = (($this->id >> 24) & 0xff) << 1;
assert($a >= 0 && $a <= 255);
return $a;
}
}
47 changes: 47 additions & 0 deletions src/Graphics/Destination.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace Kellegous\Algs4\Graphics;

/**
* An ouput destination for a drawing.
*/
final readonly class Destination
{
/**
* @param resource|string $destination
*/
private function __construct(private mixed $destination)
{
}

/**
* Create a destination that writes to the given stream.
* @param resource $destination
* @return self
*/
public static function toStream(mixed $destination): self
{
return new self($destination);
}

/**
* Create a destination that writes to the given file.
* @param string $path
* @return self
*/
public static function toFile(string $path): self
{
return new self($path);
}

/**
* @return resource|string
* @internal
*/
public function get(): mixed
{
return $this->destination;
}
}
150 changes: 150 additions & 0 deletions src/Graphics/Drawing.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<?php

declare(strict_types=1);

namespace Kellegous\Algs4\Graphics;

use GdImage;

/**
*
*/
final readonly class Drawing
{
/**
* @var GdImage
*/
private GdImage $im;

/**
* @param int $width
* @param int $height
* @param bool $with_alpha_blending
* @param bool $with_antialiasing
* @throws Exception
*/
public function __construct(
int $width,
int $height,
bool $with_alpha_blending = true,
bool $with_antialiasing = true
) {
$im = imagecreatetruecolor($width, $height);
if ($im === false) {
throw new Exception('unable to create image');
}

if (!imagealphablending($im, $with_alpha_blending)) {
throw new Exception('unable to set alpha blending');
}

if (!imageantialias($im, $with_antialiasing)) {
throw new Exception('unable to set antialiasing');
}
$this->im = $im;
}

/**
* @return GdImage
* @internal
*/
public function getImage(): GdImage
{
return $this->im;
}

/**
* @param Destination $dest
* @param int $quality
* @param int $filters
* @return void
* @throws Exception
*/
public function writePNG(
Destination $dest,
int $quality = -1,
int $filters = -1,
): void {
if (!imagepng($this->im, $dest->get(), $quality, $filters)) {
throw new Exception('unable to write PNG');
}
}

/**
* @param Destination $dest
* @param int $quality
* @return void
* @throws Exception
*/
public function writeJPEG(
Destination $dest,
int $quality = -1,
): void {
if (!imagejpeg($this->im, $dest->get(), $quality)) {
throw new Exception('unable to write JPEG');
}
}

/**
* @param int<0,255> $r
* @param int<0,255> $g
* @param int<0,255> $b
* @return Color
* @throws Exception
*/
public function colorFromRGB(int $r, int $g, int $b): Color
{
return Color::fromRGB($this, $r, $g, $b);
}

/**
* @param int<0,255> $r
* @param int<0,255> $g
* @param int<0,255> $b
* @param int<0,255> $a
* @return Color
* @throws Exception
*/
public function colorFromRGBA(int $r, int $g, int $b, int $a): Color
{
return Color::fromRGBA($this, $r, $g, $b, $a);
}

/**
* @param int $x1
* @param int $y1
* @param int $x2
* @param int $y2
* @return Rectangle
*/
public function rectangle(int $x1, int $y1, int $x2, int $y2): Rectangle
{
return new Rectangle($this, $x1, $y1, $x2, $y2);
}

/**
* @param int $center_x
* @param int $center_y
* @param int $width
* @param int $height
* @return Ellipse
*/
public function ellipse(int $center_x, int $center_y, int $width, int $height): Ellipse
{
return new Ellipse($this, $center_x, $center_y, $width, $height);
}

/**
* @param int $x
* @param int $y
* @param Color $color
* @return void
* @throws Exception
*/
public function floodFill(int $x, int $y, Color $color): void
{
if (!imagefill($this->im, $x, $y, $color->getID())) {
throw new Exception('unable to flood fill');
}
}
}
Loading

0 comments on commit 7b81b6d

Please sign in to comment.