Skip to content

Commit 677f822

Browse files
Optimize Tensor -> Image conversion for GD
1 parent 50b5f15 commit 677f822

6 files changed

Lines changed: 114 additions & 91 deletions

File tree

examples/image-test.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,6 @@ function fromTensorTest(ImageDriver $imageDriver, Tensor $tensor) : Image
5454
$image = fromTensorTest(ImageDriver::IMAGICK, $tensor);
5555
$image = fromTensorTest(ImageDriver::GD, $tensor);
5656
//$image = fromTensorTest(ImageDriver::VIPS, $tensor);
57+
58+
// Save the image
59+
$image->save('images/butterfly-converted.jpg');

examples/pipelines/image-to-text.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@
99
require_once './bootstrap.php';
1010

1111
ini_set('memory_limit', -1);
12-
//$captioner = pipeline('image-to-text', 'Xenova/vit-gpt2-image-captioning');
13-
$captioner = pipeline('image-to-text', 'Xenova/trocr-small-handwritten');
12+
$captioner = pipeline('image-to-text', 'Xenova/vit-gpt2-image-captioning');
13+
//$captioner = pipeline('image-to-text', 'Xenova/trocr-small-handwritten');
1414

1515
//$streamer = StdOutStreamer::make($captioner->tokenizer);
1616

17-
//$url = __DIR__ . '/../images/beach.png';
17+
$url = __DIR__ . '/../images/beach.png';
1818
//$url = __DIR__. '/../images/handwriting.jpg';
1919
//$url = __DIR__. '/../images/handwriting3.png';
20-
$url = __DIR__ . '/../images/handwriting4.jpeg';
20+
//$url = __DIR__ . '/../images/handwriting4.jpeg';
2121

2222
$output = $captioner($url);
2323

src/Pipelines/ImageToTextPipeline.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Codewithkyrian\Transformers\Utils\Tensor;
1010
use function Codewithkyrian\Transformers\Utils\camelCaseToSnakeCase;
1111
use function Codewithkyrian\Transformers\Utils\prepareImages;
12+
use function Codewithkyrian\Transformers\Utils\timeUsage;
1213

1314
/**
1415
* Image To Text pipeline using a `AutoModelForVision2Seq`. This pipeline predicts a caption for a given image.
@@ -58,7 +59,7 @@ public function __invoke(array|string $inputs, ...$args): array
5859
$toReturn = [];
5960

6061
foreach ($pixelValues as $batch) {
61-
$batch = Tensor::fromArray($batch, shape: [1, ...$batch->shape()]);
62+
$batch = $batch->reshape([1, ...$batch->shape()]);
6263

6364
$output = $this->model->generate($batch, generationConfig: $generationConfig, streamer: $streamer);
6465

src/Pipelines/QuestionAnsweringPipeline.php

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public function __invoke(array|string $inputs, ...$args): array
4747
$s1 = array_filter(
4848
array_map(
4949
fn($x) => [$x[0], $x[1]],
50-
array_map(null, $startLogits->softmax(), range(0, count($startLogits) - 1))
50+
array_map(null, $startLogits->softmax()->toArray(), range(0, count($startLogits) - 1))
5151
),
5252
fn($x) => $x[1] > $sepIndex
5353
);
@@ -56,7 +56,7 @@ public function __invoke(array|string $inputs, ...$args): array
5656
$e1 = array_filter(
5757
array_map(
5858
fn($x) => [$x[0], $x[1]],
59-
array_map(null, $endLogits->softmax(), range(0, count($endLogits) - 1))
59+
array_map(null, $endLogits->softmax()->toArray(), range(0, count($endLogits) - 1))
6060
),
6161
fn($x) => $x[1] > $sepIndex
6262
);
@@ -65,19 +65,13 @@ public function __invoke(array|string $inputs, ...$args): array
6565
$product = Math::product($s1, $e1);
6666

6767
// Filter options and compute values
68-
$options = array_filter($product, function ($x) {
69-
return $x[0][1] <= $x[1][1];
70-
});
68+
$options = array_filter($product, fn($x) => $x[0][1] <= $x[1][1]);
7169

7270
// Map options to desired format and sort
73-
$options = array_map(function ($x) {
74-
return [$x[0][1], $x[1][1], $x[0][0] * $x[1][0]];
75-
}, $options);
71+
$options = array_map(fn($x) => [$x[0][1], $x[1][1], $x[0][0] * $x[1][0]], $options);
7672

7773
// Sort by score
78-
usort($options, function ($a, $b) {
79-
return $b[2] <=> $a[2];
80-
});
74+
usort($options, fn($a, $b) => $b[2] <=> $a[2]);
8175

8276
$minLength = min(count($options), $topK);
8377

src/Utils/Image.php

Lines changed: 94 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,60 @@ class Image
2121
{
2222
public static AbstractImagine $imagine;
2323

24+
public const CHR = [
25+
"\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\x09", "\x0A", "\x0B", "\x0C", "\x0D", "\x0E", "\x0F",
26+
"\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", "\x18", "\x19", "\x1A", "\x1B", "\x1C", "\x1D", "\x1E", "\x1F",
27+
"\x20", "\x21", "\x22", "\x23", "\x24", "\x25", "\x26", "\x27", "\x28", "\x29", "\x2A", "\x2B", "\x2C", "\x2D", "\x2E", "\x2F",
28+
"\x30", "\x31", "\x32", "\x33", "\x34", "\x35", "\x36", "\x37", "\x38", "\x39", "\x3A", "\x3B", "\x3C", "\x3D", "\x3E", "\x3F",
29+
"\x40", "\x41", "\x42", "\x43", "\x44", "\x45", "\x46", "\x47", "\x48", "\x49", "\x4A", "\x4B", "\x4C", "\x4D", "\x4E", "\x4F",
30+
"\x50", "\x51", "\x52", "\x53", "\x54", "\x55", "\x56", "\x57", "\x58", "\x59", "\x5A", "\x5B", "\x5C", "\x5D", "\x5E", "\x5F",
31+
"\x60", "\x61", "\x62", "\x63", "\x64", "\x65", "\x66", "\x67", "\x68", "\x69", "\x6A", "\x6B", "\x6C", "\x6D", "\x6E", "\x6F",
32+
"\x70", "\x71", "\x72", "\x73", "\x74", "\x75", "\x76", "\x77", "\x78", "\x79", "\x7A", "\x7B", "\x7C", "\x7D", "\x7E", "\x7F",
33+
"\x80", "\x81", "\x82", "\x83", "\x84", "\x85", "\x86", "\x87", "\x88", "\x89", "\x8A", "\x8B", "\x8C", "\x8D", "\x8E", "\x8F",
34+
"\x90", "\x91", "\x92", "\x93", "\x94", "\x95", "\x96", "\x97", "\x98", "\x99", "\x9A", "\x9B", "\x9C", "\x9D", "\x9E", "\x9F",
35+
"\xA0", "\xA1", "\xA2", "\xA3", "\xA4", "\xA5", "\xA6", "\xA7", "\xA8", "\xA9", "\xAA", "\xAB", "\xAC", "\xAD", "\xAE", "\xAF",
36+
"\xB0", "\xB1", "\xB2", "\xB3", "\xB4", "\xB5", "\xB6", "\xB7", "\xB8", "\xB9", "\xBA", "\xBB", "\xBC", "\xBD", "\xBE", "\xBF",
37+
"\xC0", "\xC1", "\xC2", "\xC3", "\xC4", "\xC5", "\xC6", "\xC7", "\xC8", "\xC9", "\xCA", "\xCB", "\xCC", "\xCD", "\xCE", "\xCF",
38+
"\xD0", "\xD1", "\xD2", "\xD3", "\xD4", "\xD5", "\xD6", "\xD7", "\xD8", "\xD9", "\xDA", "\xDB", "\xDC", "\xDD", "\xDE", "\xDF",
39+
"\xE0", "\xE1", "\xE2", "\xE3", "\xE4", "\xE5", "\xE6", "\xE7", "\xE8", "\xE9", "\xEA", "\xEB", "\xEC", "\xED", "\xEE", "\xEF",
40+
"\xF0", "\xF1", "\xF2", "\xF3", "\xF4", "\xF5", "\xF6", "\xF7", "\xF8", "\xF9", "\xFA", "\xFB", "\xFC", "\xFD", "\xFE", "\xFF",
41+
]; // Lookup for chr($x): much faster.
42+
43+
public const ALPHA_LOOKUP = [
44+
0x00000000 => "\xff", 0x01000000 => "\xfd", 0x02000000 => "\xfb", 0x03000000 => "\xf9",
45+
0x04000000 => "\xf7", 0x05000000 => "\xf5", 0x06000000 => "\xf3", 0x07000000 => "\xf1",
46+
0x08000000 => "\xef", 0x09000000 => "\xed", 0x0a000000 => "\xeb", 0x0b000000 => "\xe9",
47+
0x0c000000 => "\xe7", 0x0d000000 => "\xe5", 0x0e000000 => "\xe3", 0x0f000000 => "\xe1",
48+
0x10000000 => "\xdf", 0x11000000 => "\xdd", 0x12000000 => "\xdb", 0x13000000 => "\xd9",
49+
0x14000000 => "\xd7", 0x15000000 => "\xd5", 0x16000000 => "\xd3", 0x17000000 => "\xd1",
50+
0x18000000 => "\xcf", 0x19000000 => "\xcd", 0x1a000000 => "\xcb", 0x1b000000 => "\xc9",
51+
0x1c000000 => "\xc7", 0x1d000000 => "\xc5", 0x1e000000 => "\xc3", 0x1f000000 => "\xc1",
52+
0x20000000 => "\xbf", 0x21000000 => "\xbd", 0x22000000 => "\xbb", 0x23000000 => "\xb9",
53+
0x24000000 => "\xb7", 0x25000000 => "\xb5", 0x26000000 => "\xb3", 0x27000000 => "\xb1",
54+
0x28000000 => "\xaf", 0x29000000 => "\xad", 0x2a000000 => "\xab", 0x2b000000 => "\xa9",
55+
0x2c000000 => "\xa7", 0x2d000000 => "\xa5", 0x2e000000 => "\xa3", 0x2f000000 => "\xa1",
56+
0x30000000 => "\x9f", 0x31000000 => "\x9d", 0x32000000 => "\x9b", 0x33000000 => "\x99",
57+
0x34000000 => "\x97", 0x35000000 => "\x95", 0x36000000 => "\x93", 0x37000000 => "\x91",
58+
0x38000000 => "\x8f", 0x39000000 => "\x8d", 0x3a000000 => "\x8b", 0x3b000000 => "\x89",
59+
0x3c000000 => "\x87", 0x3d000000 => "\x85", 0x3e000000 => "\x83", 0x3f000000 => "\x81",
60+
0x40000000 => "\x7f", 0x41000000 => "\x7d", 0x42000000 => "\x7b", 0x43000000 => "\x79",
61+
0x44000000 => "\x77", 0x45000000 => "\x75", 0x46000000 => "\x73", 0x47000000 => "\x71",
62+
0x48000000 => "\x6f", 0x49000000 => "\x6d", 0x4a000000 => "\x6b", 0x4b000000 => "\x69",
63+
0x4c000000 => "\x67", 0x4d000000 => "\x65", 0x4e000000 => "\x63", 0x4f000000 => "\x61",
64+
0x50000000 => "\x5f", 0x51000000 => "\x5d", 0x52000000 => "\x5b", 0x53000000 => "\x59",
65+
0x54000000 => "\x57", 0x55000000 => "\x55", 0x56000000 => "\x53", 0x57000000 => "\x51",
66+
0x58000000 => "\x4f", 0x59000000 => "\x4d", 0x5a000000 => "\x4b", 0x5b000000 => "\x49",
67+
0x5c000000 => "\x47", 0x5d000000 => "\x45", 0x5e000000 => "\x43", 0x5f000000 => "\x41",
68+
0x60000000 => "\x3f", 0x61000000 => "\x3d", 0x62000000 => "\x3b", 0x63000000 => "\x39",
69+
0x64000000 => "\x37", 0x65000000 => "\x35", 0x66000000 => "\x33", 0x67000000 => "\x31",
70+
0x68000000 => "\x2f", 0x69000000 => "\x2d", 0x6a000000 => "\x2b", 0x6b000000 => "\x29",
71+
0x6c000000 => "\x27", 0x6d000000 => "\x25", 0x6e000000 => "\x23", 0x6f000000 => "\x21",
72+
0x70000000 => "\x1f", 0x71000000 => "\x1d", 0x72000000 => "\x1b", 0x73000000 => "\x19",
73+
0x74000000 => "\x17", 0x75000000 => "\x15", 0x76000000 => "\x13", 0x77000000 => "\x11",
74+
0x78000000 => "\x0f", 0x79000000 => "\x0d", 0x7a000000 => "\x0b", 0x7b000000 => "\x09",
75+
0x7c000000 => "\x07", 0x7d000000 => "\x05", 0x7e000000 => "\x03", 0x7f000000 => "\x00"
76+
]; // Lookup table for chr(255-(($x >> 23) & 0x7f)).
77+
2478
public function __construct(public ImageInterface $image, public int $channels = 4)
2579
{
2680
if ($this->image instanceof \Imagine\Vips\Image) {
@@ -263,6 +317,9 @@ public static function fromTensor(Tensor $tensor, string $channelFormat = 'CHW')
263317
// Make sure the tensor is the right data type
264318
$tensor = $tensor->to(Tensor::uint8);
265319

320+
/**
321+
* Handle Vips images
322+
*/
266323
if ($image instanceof \Imagine\Vips\Image) {
267324
$vipImage = $image->getVips()::newFromMemory($tensor->buffer()->dump(), $width, $height, $channels, 'uchar');
268325

@@ -271,6 +328,9 @@ public static function fromTensor(Tensor $tensor, string $channelFormat = 'CHW')
271328
return new self($image, $channels);
272329
}
273330

331+
/**
332+
* Handle Imagick images
333+
*/
274334
if ($image instanceof \Imagine\Imagick\Image) {
275335
$map = match ($channels) {
276336
1 => 'I',
@@ -287,17 +347,38 @@ public static function fromTensor(Tensor $tensor, string $channelFormat = 'CHW')
287347
return new self($image, $channels);
288348
}
289349

290-
$pixels = $tensor->reshape([$width * $height, $channels])->toArray();
350+
/**
351+
* Handle GD images
352+
*/
353+
354+
355+
$gdImage = $image->getGdResource();
356+
$imageData = $tensor->buffer()->dump();
357+
358+
$reverseCHR = array_flip(self::CHR);
359+
$reverseAlphaLookup = array_flip(self::ALPHA_LOOKUP);
291360

361+
$j = 0;
362+
// Iterate through each pixel's worth of string data
292363
for ($y = 0; $y < $height; $y++) {
293364
for ($x = 0; $x < $width; $x++) {
294-
$index = $y * $width + $x;
295-
296-
$color = $channels === 1 ? $pixels[$index][0] : $pixels[$index];
365+
$argb = 0;
297366

298-
$color = $image->palette()->color([$color[0], $color[1], $color[2]], $color[3] ?? null);
367+
if ($channels >= 1) {
368+
$argb |= ($reverseCHR[$imageData[$j++]] << 16); // R
369+
}
370+
if ($channels >= 2) {
371+
$argb |= ($reverseCHR[$imageData[$j++]] << 8); // G
372+
}
373+
if ($channels >= 3) {
374+
$argb |= $reverseCHR[$imageData[$j++]]; // B
375+
}
376+
if ($channels >= 4) {
377+
$argb |= $reverseAlphaLookup[$imageData[$j++]]; // A
378+
}
299379

300-
$image->draw()->dot(new Point($x, $y), $color);
380+
// Set the pixel at (x, y) to the ARGB value
381+
imagesetpixel($gdImage, $x, $y, $argb);
301382
}
302383
}
303384

@@ -356,87 +437,27 @@ public function getPixelData(): string
356437
* brute-force method, suggested by @DewiMorgan on StackOverflow: https://stackoverflow.com/a/30136602/11209184.
357438
* It's tons faster than other methods I tried, and rivals the speed of the Imagick method, so I'll keep it for now.
358439
*/
359-
$alphaLookup = [
360-
0x00000000 => "\xff", 0x01000000 => "\xfd", 0x02000000 => "\xfb", 0x03000000 => "\xf9",
361-
0x04000000 => "\xf7", 0x05000000 => "\xf5", 0x06000000 => "\xf3", 0x07000000 => "\xf1",
362-
0x08000000 => "\xef", 0x09000000 => "\xed", 0x0a000000 => "\xeb", 0x0b000000 => "\xe9",
363-
0x0c000000 => "\xe7", 0x0d000000 => "\xe5", 0x0e000000 => "\xe3", 0x0f000000 => "\xe1",
364-
0x10000000 => "\xdf", 0x11000000 => "\xdd", 0x12000000 => "\xdb", 0x13000000 => "\xd9",
365-
0x14000000 => "\xd7", 0x15000000 => "\xd5", 0x16000000 => "\xd3", 0x17000000 => "\xd1",
366-
0x18000000 => "\xcf", 0x19000000 => "\xcd", 0x1a000000 => "\xcb", 0x1b000000 => "\xc9",
367-
0x1c000000 => "\xc7", 0x1d000000 => "\xc5", 0x1e000000 => "\xc3", 0x1f000000 => "\xc1",
368-
0x20000000 => "\xbf", 0x21000000 => "\xbd", 0x22000000 => "\xbb", 0x23000000 => "\xb9",
369-
0x24000000 => "\xb7", 0x25000000 => "\xb5", 0x26000000 => "\xb3", 0x27000000 => "\xb1",
370-
0x28000000 => "\xaf", 0x29000000 => "\xad", 0x2a000000 => "\xab", 0x2b000000 => "\xa9",
371-
0x2c000000 => "\xa7", 0x2d000000 => "\xa5", 0x2e000000 => "\xa3", 0x2f000000 => "\xa1",
372-
0x30000000 => "\x9f", 0x31000000 => "\x9d", 0x32000000 => "\x9b", 0x33000000 => "\x99",
373-
0x34000000 => "\x97", 0x35000000 => "\x95", 0x36000000 => "\x93", 0x37000000 => "\x91",
374-
0x38000000 => "\x8f", 0x39000000 => "\x8d", 0x3a000000 => "\x8b", 0x3b000000 => "\x89",
375-
0x3c000000 => "\x87", 0x3d000000 => "\x85", 0x3e000000 => "\x83", 0x3f000000 => "\x81",
376-
0x40000000 => "\x7f", 0x41000000 => "\x7d", 0x42000000 => "\x7b", 0x43000000 => "\x79",
377-
0x44000000 => "\x77", 0x45000000 => "\x75", 0x46000000 => "\x73", 0x47000000 => "\x71",
378-
0x48000000 => "\x6f", 0x49000000 => "\x6d", 0x4a000000 => "\x6b", 0x4b000000 => "\x69",
379-
0x4c000000 => "\x67", 0x4d000000 => "\x65", 0x4e000000 => "\x63", 0x4f000000 => "\x61",
380-
0x50000000 => "\x5f", 0x51000000 => "\x5d", 0x52000000 => "\x5b", 0x53000000 => "\x59",
381-
0x54000000 => "\x57", 0x55000000 => "\x55", 0x56000000 => "\x53", 0x57000000 => "\x51",
382-
0x58000000 => "\x4f", 0x59000000 => "\x4d", 0x5a000000 => "\x4b", 0x5b000000 => "\x49",
383-
0x5c000000 => "\x47", 0x5d000000 => "\x45", 0x5e000000 => "\x43", 0x5f000000 => "\x41",
384-
0x60000000 => "\x3f", 0x61000000 => "\x3d", 0x62000000 => "\x3b", 0x63000000 => "\x39",
385-
0x64000000 => "\x37", 0x65000000 => "\x35", 0x66000000 => "\x33", 0x67000000 => "\x31",
386-
0x68000000 => "\x2f", 0x69000000 => "\x2d", 0x6a000000 => "\x2b", 0x6b000000 => "\x29",
387-
0x6c000000 => "\x27", 0x6d000000 => "\x25", 0x6e000000 => "\x23", 0x6f000000 => "\x21",
388-
0x70000000 => "\x1f", 0x71000000 => "\x1d", 0x72000000 => "\x1b", 0x73000000 => "\x19",
389-
0x74000000 => "\x17", 0x75000000 => "\x15", 0x76000000 => "\x13", 0x77000000 => "\x11",
390-
0x78000000 => "\x0f", 0x79000000 => "\x0d", 0x7a000000 => "\x0b", 0x7b000000 => "\x09",
391-
0x7c000000 => "\x07", 0x7d000000 => "\x05", 0x7e000000 => "\x03", 0x7f000000 => "\x00"
392-
]; // Lookup table for chr(255-(($x >> 23) & 0x7f)).
393-
394-
$chr = [
395-
"\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\x09", "\x0A", "\x0B", "\x0C", "\x0D", "\x0E", "\x0F",
396-
"\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", "\x18", "\x19", "\x1A", "\x1B", "\x1C", "\x1D", "\x1E", "\x1F",
397-
"\x20", "\x21", "\x22", "\x23", "\x24", "\x25", "\x26", "\x27", "\x28", "\x29", "\x2A", "\x2B", "\x2C", "\x2D", "\x2E", "\x2F",
398-
"\x30", "\x31", "\x32", "\x33", "\x34", "\x35", "\x36", "\x37", "\x38", "\x39", "\x3A", "\x3B", "\x3C", "\x3D", "\x3E", "\x3F",
399-
"\x40", "\x41", "\x42", "\x43", "\x44", "\x45", "\x46", "\x47", "\x48", "\x49", "\x4A", "\x4B", "\x4C", "\x4D", "\x4E", "\x4F",
400-
"\x50", "\x51", "\x52", "\x53", "\x54", "\x55", "\x56", "\x57", "\x58", "\x59", "\x5A", "\x5B", "\x5C", "\x5D", "\x5E", "\x5F",
401-
"\x60", "\x61", "\x62", "\x63", "\x64", "\x65", "\x66", "\x67", "\x68", "\x69", "\x6A", "\x6B", "\x6C", "\x6D", "\x6E", "\x6F",
402-
"\x70", "\x71", "\x72", "\x73", "\x74", "\x75", "\x76", "\x77", "\x78", "\x79", "\x7A", "\x7B", "\x7C", "\x7D", "\x7E", "\x7F",
403-
"\x80", "\x81", "\x82", "\x83", "\x84", "\x85", "\x86", "\x87", "\x88", "\x89", "\x8A", "\x8B", "\x8C", "\x8D", "\x8E", "\x8F",
404-
"\x90", "\x91", "\x92", "\x93", "\x94", "\x95", "\x96", "\x97", "\x98", "\x99", "\x9A", "\x9B", "\x9C", "\x9D", "\x9E", "\x9F",
405-
"\xA0", "\xA1", "\xA2", "\xA3", "\xA4", "\xA5", "\xA6", "\xA7", "\xA8", "\xA9", "\xAA", "\xAB", "\xAC", "\xAD", "\xAE", "\xAF",
406-
"\xB0", "\xB1", "\xB2", "\xB3", "\xB4", "\xB5", "\xB6", "\xB7", "\xB8", "\xB9", "\xBA", "\xBB", "\xBC", "\xBD", "\xBE", "\xBF",
407-
"\xC0", "\xC1", "\xC2", "\xC3", "\xC4", "\xC5", "\xC6", "\xC7", "\xC8", "\xC9", "\xCA", "\xCB", "\xCC", "\xCD", "\xCE", "\xCF",
408-
"\xD0", "\xD1", "\xD2", "\xD3", "\xD4", "\xD5", "\xD6", "\xD7", "\xD8", "\xD9", "\xDA", "\xDB", "\xDC", "\xDD", "\xDE", "\xDF",
409-
"\xE0", "\xE1", "\xE2", "\xE3", "\xE4", "\xE5", "\xE6", "\xE7", "\xE8", "\xE9", "\xEA", "\xEB", "\xEC", "\xED", "\xEE", "\xEF",
410-
"\xF0", "\xF1", "\xF2", "\xF3", "\xF4", "\xF5", "\xF6", "\xF7", "\xF8", "\xF9", "\xFA", "\xFB", "\xFC", "\xFD", "\xFE", "\xFF",
411-
]; // Lookup for chr($x): much faster.
412-
413-
$imageData = match ($this->channels) {
414-
1 => str_repeat("\x00", $width * $height),
415-
2 => str_repeat("\x00\x00", $width * $height),
416-
3 => str_repeat("\x00\x00\x00", $width * $height),
417-
4 => str_repeat("\x00\x00\x00\x00", $width * $height),
418-
default => throw new Exception("Unsupported number of channels: $this->channels"),
419-
};
420-
440+
$imageData = str_repeat("\x00", $width * $height * $this->channels);
441+
$gdImage = $this->image->getGdResource();
421442

422443
// Loop over each single pixel.
423444
$j = 0;
424445
for ($y = 0; $y < $height; $y++) {
425446
for ($x = 0; $x < $width; $x++) {
426447
// Grab the pixel data.
427-
$argb = imagecolorat($this->image->getGdResource(), $x, $y);
448+
$argb = imagecolorat($gdImage, $x, $y);
428449

429450
if ($this->channels >= 1) {
430-
$imageData[$j++] = $chr[($argb >> 16) & 0xFF]; // R
451+
$imageData[$j++] = self::CHR[($argb >> 16) & 0xFF]; // R
431452
}
432453
if ($this->channels >= 2) {
433-
$imageData[$j++] = $chr[($argb >> 8) & 0xFF]; // G
454+
$imageData[$j++] = self::CHR[($argb >> 8) & 0xFF]; // G
434455
}
435456
if ($this->channels >= 3) {
436-
$imageData[$j++] = $chr[$argb & 0xFF]; // B
457+
$imageData[$j++] = self::CHR[$argb & 0xFF]; // B
437458
}
438459
if ($this->channels >= 4) {
439-
$imageData[$j++] = $alphaLookup[$argb & 0x7f000000]; // A
460+
$imageData[$j++] = self::ALPHA_LOOKUP[$argb & 0x7f000000]; // A
440461
}
441462
}
442463
}

0 commit comments

Comments
 (0)