Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11468 глав6256 карточек справочника
Дерево руководства

Глава 421 · После первых изображений Gmagick, массивов и объектов

Gmagick: ограниченная палитра и распределение ошибки

Выбрать цвета автоматически или по эталону, увидеть действие dithering и безопасно создать вариант палитры.

Сравнить палитры из четырёх и восьми цветов

Небольшой экран или стилизованная карточка требуют ограничить палитру. Алгоритм сам выбирает представители цветов; заранее задаём только их число и параметры анализа. На плавном исходнике разница между четырьмя и восемью цветами видна как изменение областей и переходов.

Собственный RGB-градиент: 57 502 различных цвета
Собственный RGB-градиент: 57 502 различных цвета — 320 × 200 px.
Четыре выбранных алгоритмом цвета вместо 57 502 входных
Четыре выбранных алгоритмом цвета вместо 57 502 входных — 320 × 200 px.
Восемь цветов и полное дерево depth=8: другая детализация переходов
Восемь цветов и полное дерево depth=8: другая детализация переходов — 320 × 200 px.
<?php
$image = new Gmagick(__DIR__ . '/spectrum.ppm');
$image->quantizeImage(4, Gmagick::COLORSPACE_RGB, 0, false, true);
$image->setImageFormat('PNG');
$image->writeImage(__DIR__ . '/four-colors.png');

Пять аргументов идут строго по порядку:4 цвета, пространствоRGB, автоматическая глубина дерева 0, отключённый dither, включённый расчёт ошибки. true/false — логические значения, не строки. Полный пример рядом делает 8 цветов с глубиной 8 и measureError=false. Вывод метода — объект Gmagick, поэтому присвоить его результат переменной $error и ожидать число было бы ошибкой.

Сравнить палитры из четырёх и восьми цветовСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$source=new Gmagick(__DIR__.'/spectrum.ppm');
$four=clone $source;$four->quantizeImage(4,Gmagick::COLORSPACE_RGB,0,false,true);
$eight=clone $source;$eight->quantizeImage(8,Gmagick::COLORSPACE_RGB,8,false,false);
$four->setImageDepth(8);
$four->setImageFormat('PNG');
$four->writeImage(__DIR__.'/gm99-quantize4.png');
$eight->setImageDepth(8);
$eight->setImageFormat('PNG');
$eight->writeImage(__DIR__.'/gm99-quantize8.png');
$counts=[(new Gmagick(__DIR__.'/gm99-quantize4.png'))->getImageColors(),(new Gmagick(__DIR__.'/gm99-quantize8.png'))->getImageColors()];
$errors=['empty'=>gmTry(fn()=>(new Gmagick())->quantizeImage(4,Gmagick::COLORSPACE_RGB,0,false,false))];

gmReport('gm99-quantize-levels',["gmagick.construct", "gmagick.quantizeimage", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage", "gmagick.getimagecolors"],["gm99-quantize4.png", "gm99-quantize8.png"],
    ['palette_sizes'=>$counts===[4,8],'empty_rejected'=>$errors['empty']['class']==='GmagickException','size_gm99_quantize4'=>gmInspect('gm99-quantize4.png')['width']===320&&gmInspect('gm99-quantize4.png')['height']===200,'size_gm99_quantize8'=>gmInspect('gm99-quantize8.png')['width']===320&&gmInspect('gm99-quantize8.png')['height']===200],
    $errors ?? [], ['saved_colors'=>$counts,'measure_error_returns_scalar'=>false]);
echo "gm99-quantize4.png 320x200 gm99-quantize8.png 320x200\n";
Ожидаемый результат
gm99-quantize4.png 320x200 gm99-quantize8.png 320x200

Архив запускается командой php example.php с Gmagick. Проверенный отдельный runtime: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные входы восстанавливаются из .b64; gm-fixture.php проверяет файлы и результат, изучаемые операции находятся в самой программе. PNG и metrics.json записываются рядом.

Вывод успешного запуска
gm99-quantize4.png 320x200 gm99-quantize8.png 320x200

По официальному описанию 0 или 1 разрешают автоматическую глубину около log₄(N),8 полностью разворачивает дерево; другой выбор может понадобиться при малом разбросе цветов. Это описание алгоритма, не наш замер скорости. Запрос большего числа не обязан создавать отсутствующие цвета. Пустой объект перехватывается как GmagickException. Статистика количества читается из заново открытых PNG, чтобы подтвердить именно сохранённый результат.

Использовать только разрешённые цвета бренда

У бренда допускаются только четыре заданные краски. Автоматический подбор quantizeImage может выбрать другие оттенки, поэтому здесь нужен эталон. Верхняя полоска показывает четыре пикселя palette.ppm в увеличении, нижняя — результат сопоставления градиента этой палитре.

Собственный RGB-градиент: 57 502 различных цвета
Собственный RGB-градиент: 57 502 различных цвета — 320 × 200 px.
Четыре цвета эталона 4×1, увеличенные точечным фильтром для чтения
Четыре цвета эталона 4×1, увеличенные точечным фильтром для чтения — 320 × 80 px.
Все пиксели заменены ближайшими цветами эталона без dithering
Все пиксели заменены ближайшими цветами эталона без dithering — 320 × 200 px.
<?php
$image = new Gmagick(__DIR__ . '/spectrum.ppm');
$palette = new Gmagick(__DIR__ . '/palette.ppm');
$image->mapImage($palette, false);
$image->setImageFormat('PNG');
$image->writeImage(__DIR__ . '/brand.png');

Первый объект содержит изображение, второй — набор допустимых цветов. mapImage получает именно объект $palette, а не путь. false отключает распределение ошибки, поэтому образуются крупные сплошные области. Размер эталона 4×1 не задаёт размер результата:320×200 сохраняются. Увеличение полоски сделано точечным фильтром, чтобы не добавить промежуточных цветов в поясняющий образец.

Использовать только разрешённые цвета брендаСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$image=new Gmagick(__DIR__.'/spectrum.ppm');$palette=new Gmagick(__DIR__.'/palette.ppm');
$referenceSignature=$palette->getImageSignature();$image->mapImage($palette,false);
$paletteUnchanged=$palette->getImageSignature()===$referenceSignature;
$preview=clone $palette;$preview->resizeImage(320,80,Gmagick::FILTER_POINT,1.0);
$preview->setImageDepth(8);
$preview->setImageFormat('PNG');
$preview->writeImage(__DIR__.'/gm99-brand-palette.png');
$image->setImageDepth(8);
$image->setImageFormat('PNG');
$image->writeImage(__DIR__.'/gm99-mapped-brand.png');
$decoded=new Gmagick(__DIR__.'/gm99-mapped-brand.png');$allowed=[];$used=[];
foreach($palette->getImageHistogram()as$pixel)$allowed[]=json_encode($pixel->getColor(true));
foreach($decoded->getImageHistogram()as$pixel)$used[]=json_encode($pixel->getColor(true));
$errors=['map_type'=>gmTry(fn()=>$image->mapImage('not-an-image',false))];

gmReport('gm99-map-palette',["gmagick.construct", "gmagick.getimagesignature", "gmagick.mapimage", "gmagick.resizeimage", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage", "gmagick.getimagehistogram", "gmagickpixel.getcolor"],["gm99-brand-palette.png", "gm99-mapped-brand.png"],
    ['reference_preserved'=>$paletteUnchanged,'only_reference_colors'=>count($used)===4&&array_diff($used,$allowed)===[],'bad_reference'=>$errors['map_type']['class']==='TypeError','size_gm99_brand_palette'=>gmInspect('gm99-brand-palette.png')['width']===320&&gmInspect('gm99-brand-palette.png')['height']===80,'size_gm99_mapped_brand'=>gmInspect('gm99-mapped-brand.png')['width']===320&&gmInspect('gm99-mapped-brand.png')['height']===200],
    $errors ?? [], ['used'=>$used,'allowed'=>$allowed]);
echo "gm99-brand-palette.png 320x80 gm99-mapped-brand.png 320x200\n";
Ожидаемый результат
gm99-brand-palette.png 320x80 gm99-mapped-brand.png 320x200

Архив запускается командой php example.php с Gmagick. Проверенный отдельный runtime: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные входы восстанавливаются из .b64; gm-fixture.php проверяет файлы и результат, изучаемые операции находятся в самой программе. PNG и metrics.json записываются рядом.

Вывод успешного запуска
gm99-brand-palette.png 320x80 gm99-mapped-brand.png 320x200

Полная программа проверяет две вещи: эталон не изменился, а каждый RGB результата присутствует среди четырёх разрешённых значений. Это содержательнее одной проверки «четыре цвета»: четыре произвольных оттенка не выполнили бы требование бренда. Строка вместо объекта вызывает TypeError. Данный метод не является проверкой доступности контраста текста; такую задачу решают отдельно.

Сравнить сплошные области и распределение ошибки

При той же палитре можно улучшить впечатление от плавного перехода с помощью соседства разных точек. Слева mapImage создаёт сплошные области, справа включает dithering. Новых красок нет: меняется пространственный рисунок из разрешённых цветов.

Собственный RGB-градиент: 57 502 различных цвета
Собственный RGB-градиент: 57 502 различных цвета — 320 × 200 px.
mapImage с dither=false: крупные однородные области
mapImage с dither=false: крупные однородные области — 320 × 200 px.
mapImage с dither=true: узор соседних пикселей смягчает восприятие переходов
mapImage с dither=true: узор соседних пикселей смягчает восприятие переходов — 320 × 200 px.
<?php
$image = new Gmagick(__DIR__ . '/spectrum.ppm');
$palette = new Gmagick(__DIR__ . '/palette.ppm');
$image->mapImage($palette, true);
$image->setImageFormat('PNG');
$image->writeImage(__DIR__ . '/dithered.png');

Значение true запрашивает распределение ошибки. Если идеальный цвет недоступен, соседние пиксели могут использовать разные доступные значения, создавая усреднённое зрительное впечатление. Это не увеличение глубины канала и не физическое смешение новых цветов в файле. Обе версии имеют четыре цвета и одинаковые размеры; полная программа дополнительно подтверждает различие декодированных пикселей.

Сравнить сплошные области и распределение ошибкиСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$source=new Gmagick(__DIR__.'/spectrum.ppm');$palette=new Gmagick(__DIR__.'/palette.ppm');
$flat=clone $source;$flat->mapImage($palette,false);
$dither=clone $source;$dither->mapImage($palette,true);
$flat->setImageDepth(8);
$flat->setImageFormat('PNG');
$flat->writeImage(__DIR__.'/gm99-map-flat.png');
$dither->setImageDepth(8);
$dither->setImageFormat('PNG');
$dither->writeImage(__DIR__.'/gm99-map-dither.png');
$readFlat=new Gmagick(__DIR__.'/gm99-map-flat.png');$readDither=new Gmagick(__DIR__.'/gm99-map-dither.png');
$counts=[$readFlat->getImageColors(),$readDither->getImageColors()];

gmReport('gm99-map-dither',["gmagick.construct", "gmagick.mapimage", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage", "gmagick.getimagecolors"],["gm99-map-flat.png", "gm99-map-dither.png"],
    ['four_each'=>$counts===[4,4],'different_pixels'=>$readFlat->getImageSignature()!==$readDither->getImageSignature(),'size_gm99_map_flat'=>gmInspect('gm99-map-flat.png')['width']===320&&gmInspect('gm99-map-flat.png')['height']===200,'size_gm99_map_dither'=>gmInspect('gm99-map-dither.png')['width']===320&&gmInspect('gm99-map-dither.png')['height']===200],
    $errors ?? [], ['saved_colors'=>$counts]);
echo "gm99-map-flat.png 320x200 gm99-map-dither.png 320x200\n";
Ожидаемый результат
gm99-map-flat.png 320x200 gm99-map-dither.png 320x200

Архив запускается командой php example.php с Gmagick. Проверенный отдельный runtime: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные входы восстанавливаются из .b64; gm-fixture.php проверяет файлы и результат, изучаемые операции находятся в самой программе. PNG и metrics.json записываются рядом.

Вывод успешного запуска
gm99-map-flat.png 320x200 gm99-map-dither.png 320x200

Узор может быть полезен на градиенте, но мешать мелкой графике и повышать размер сжатого файла. Нельзя объявлять его всегда более качественным: смотрите результат на целевом размере и устройстве. Важно, что флаг относится к конкретному методу. Следующий пример показывает, почему сходное имя параметра ещё не гарантирует одинаковый видимый эффект во всех операциях.

Проверить действие флага, не обещая различие заранее

До внедрения флага в интерфейс сервиса полезно проверить его на вашем входе. В этом сравнении quantizeImage вызывается с dither=false иtrue при одинаковых остальных аргументах. Оба результата показаны честно: на указанной сборке и этом градиенте они совпали.

Собственный RGB-градиент: 57 502 различных цвета
Собственный RGB-градиент: 57 502 различных цвета — 320 × 200 px.
quantizeImage: запрос четырёх цветов без dithering
quantizeImage: запрос четырёх цветов без dithering — 320 × 200 px.
На этом входе и runtime dither=true дал те же пиксели; это проверено сравнением
На этом входе и runtime dither=true дал те же пиксели; это проверено сравнением — 320 × 200 px.
<?php
$image = new Gmagick(__DIR__ . '/spectrum.ppm');
$image->quantizeImage(4, Gmagick::COLORSPACE_RGB, 0, true, true);
$image->setImageFormat('PNG');
$image->writeImage(__DIR__ . '/requested-dither.png');

Последние два true означают запрос dithering и расчёта ошибки; они не являются обещанием отдельного файла с ошибкой. Полная программа меняет только предпоследний аргумент, сохраняет обе копии и сравнивает подписи заново декодированных изображений. Сравнение подтверждает совпадение пикселей, а не только количество 4. По одному примеру нельзя объявить dither неработающим во всех версиях и на всех данных.

Проверить действие флага, не обещая различие заранееСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$source=new Gmagick(__DIR__.'/spectrum.ppm');
$flat=clone $source;$flat->quantizeImage(4,Gmagick::COLORSPACE_RGB,0,false,true);
$dither=clone $source;$dither->quantizeImage(4,Gmagick::COLORSPACE_RGB,0,true,true);
$flat->setImageDepth(8);
$flat->setImageFormat('PNG');
$flat->writeImage(__DIR__.'/gm99-quantize-no-dither.png');
$dither->setImageDepth(8);
$dither->setImageFormat('PNG');
$dither->writeImage(__DIR__.'/gm99-quantize-requested-dither.png');
$a=new Gmagick(__DIR__.'/gm99-quantize-no-dither.png');$b=new Gmagick(__DIR__.'/gm99-quantize-requested-dither.png');
$samePixels=$a->getImageSignature()===$b->getImageSignature();
$counts=[$a->getImageColors(),$b->getImageColors()];

gmReport('gm99-quantize-switch',["gmagick.construct", "gmagick.quantizeimage", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage", "gmagick.getimagesignature", "gmagick.getimagecolors"],["gm99-quantize-no-dither.png", "gm99-quantize-requested-dither.png"],
    ['four_each'=>$counts===[4,4],'observed_same_result'=>$samePixels,'size_gm99_quantize_no_dither'=>gmInspect('gm99-quantize-no-dither.png')['width']===320&&gmInspect('gm99-quantize-no-dither.png')['height']===200,'size_gm99_quantize_requested_dither'=>gmInspect('gm99-quantize-requested-dither.png')['width']===320&&gmInspect('gm99-quantize-requested-dither.png')['height']===200],
    $errors ?? [], ['same_decoded_pixels'=>$samePixels,'counts'=>$counts]);
echo "gm99-quantize-no-dither.png 320x200 gm99-quantize-requested-dither.png 320x200\n";
Ожидаемый результат
gm99-quantize-no-dither.png 320x200 gm99-quantize-requested-dither.png 320x200

Архив запускается командой php example.php с Gmagick. Проверенный отдельный runtime: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные входы восстанавливаются из .b64; gm-fixture.php проверяет файлы и результат, изучаемые операции находятся в самой программе. PNG и metrics.json записываются рядом.

Вывод успешного запуска
gm99-quantize-no-dither.png 320x200 gm99-quantize-requested-dither.png 320x200

Для задачи с фиксированным эталоном предыдущий mapImage уже показал реальное различие. Если продукт обещает пользователю определённый вид растра, проверяйте именно готовый результат вместо вывода подписи «dithering включён» по одному значению переменной. Причину совпадения здесь не выдаём за доказанный внутренний дефект библиотеки.

Получить вариант цветов и проверить попытку обратного сдвига

Для декоративного оформления можно переставить назначение цветов ограниченной палитры. Ниже сначала сохранён исходник, затем вариант со сдвигом+1, затем результат попытки сдвинуть обратно на−1. Последний не равен исходному: это важная граница проверенного API.

Исходная индексная палитра из шести цветов
Исходная индексная палитра из шести цветов — 320 × 200 px.
Сдвиг таблицы на +1 изменил назначение цветов
Сдвиг таблицы на +1 изменил назначение цветов — 320 × 200 px.
После дополнительного −1 исходник здесь не восстановился: осталось пять цветов
После дополнительного −1 исходник здесь не восстановился: осталось пять цветов — 320 × 200 px.
<?php
$image = new Gmagick(__DIR__ . '/product.ppm');
$image->quantizeImage(6, Gmagick::COLORSPACE_RGB, 0, false, false);
$image->cycleColormapImage(1);
$image->setImageFormat('PNG');
$image->writeImage(__DIR__ . '/shifted-colors.png');

quantizeImage сначала создаёт ограниченную палитру. Аргумент 1 у cycleColormapImage задаёт число позиций, а не градусы оттенка. Геометрия остаётся прежней, изменяются назначенные областям цвета. Полный пример сохраняет независимые снимки до и после, затем проверяет попытку−1. На нашей версии числа уникальных цветов составили 6,6,5; возвращение исходного значения параметра не стало отменой действия.

Получить вариант цветов и проверить попытку обратного сдвигаСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$image=new Gmagick(__DIR__.'/product.ppm');$image->quantizeImage(6,Gmagick::COLORSPACE_RGB,0,false,false);
$before=clone $image;
$before->setImageDepth(8);
$before->setImageFormat('PNG');
$before->writeImage(__DIR__.'/gm99-cycle-before.png');
$image->cycleColormapImage(1);$shifted=clone $image;
$shifted->setImageDepth(8);
$shifted->setImageFormat('PNG');
$shifted->writeImage(__DIR__.'/gm99-cycle-plus1.png');
$image->cycleColormapImage(-1);
$image->setImageDepth(8);
$image->setImageFormat('PNG');
$image->writeImage(__DIR__.'/gm99-cycle-minus1.png');
$a=new Gmagick(__DIR__.'/gm99-cycle-before.png');$b=new Gmagick(__DIR__.'/gm99-cycle-plus1.png');$c=new Gmagick(__DIR__.'/gm99-cycle-minus1.png');
$counts=[$a->getImageColors(),$b->getImageColors(),$c->getImageColors()];

gmReport('gm99-cycle-palette',["gmagick.construct", "gmagick.quantizeimage", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage", "gmagick.cyclecolormapimage", "gmagick.getimagecolors"],["gm99-cycle-before.png", "gm99-cycle-plus1.png", "gm99-cycle-minus1.png"],
    ['counts'=>$counts===[6,6,5],'first_changed'=>$a->getImageSignature()!==$b->getImageSignature(),'not_inverse_here'=>$a->getImageSignature()!==$c->getImageSignature(),'size_gm99_cycle_before'=>gmInspect('gm99-cycle-before.png')['width']===320&&gmInspect('gm99-cycle-before.png')['height']===200,'size_gm99_cycle_plus1'=>gmInspect('gm99-cycle-plus1.png')['width']===320&&gmInspect('gm99-cycle-plus1.png')['height']===200,'size_gm99_cycle_minus1'=>gmInspect('gm99-cycle-minus1.png')['width']===320&&gmInspect('gm99-cycle-minus1.png')['height']===200],
    $errors ?? [], ['saved_color_counts'=>$counts,'inverse_claimed'=>false]);
echo "gm99-cycle-before.png 320x200 gm99-cycle-plus1.png 320x200 gm99-cycle-minus1.png 320x200\n";
Ожидаемый результат
gm99-cycle-before.png 320x200 gm99-cycle-plus1.png 320x200 gm99-cycle-minus1.png 320x200

Архив запускается командой php example.php с Gmagick. Проверенный отдельный runtime: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные входы восстанавливаются из .b64; gm-fixture.php проверяет файлы и результат, изучаемые операции находятся в самой программе. PNG и metrics.json записываются рядом.

Вывод успешного запуска
gm99-cycle-before.png 320x200 gm99-cycle-plus1.png 320x200 gm99-cycle-minus1.png 320x200

Не объясняем это наблюдение недоказанным переполнением или другим механизмом. Для безопасной отмены храните оригинал и создавайте новый вариант из него. Цветовые перестановки подходят декоративной карточке, но могут разрушить смысл схемы, где красный обозначает отказ, а зелёный успех. Значение цвета в предметной области важнее наличия такого эффекта в библиотеке.

Подготовить независимый цветовой вариант карточки

Несколько вариантов карточки должны создаваться независимо, чтобы изменение одного не повреждало исходник. Функция paletteVariant объединяет ограничение палитры и сдвиг, проверяет допустимое для приложения число цветов и возвращает готовую копию.

Исходная индексная палитра из шести цветов
Исходная индексная палитра из шести цветов — 320 × 200 px.
Цветовой вариант через quantizeImage и cycleColormapImage; оригинал сохранён отдельно
Цветовой вариант через quantizeImage и cycleColormapImage; оригинал сохранён отдельно — 320 × 200 px.
<?php
$source = new Gmagick(__DIR__ . '/product.ppm');
$variant = clone $source;
$variant->quantizeImage(6, Gmagick::COLORSPACE_RGB, 0, false, false);
$variant->cycleColormapImage(1);
$variant->setImageFormat('PNG');
$variant->writeImage(__DIR__ . '/variant.png');

Короткий код показывает сам порядок. Полная версия оформляет его в function с тремя аргументами: исходник, число цветов, сдвиг. return возвращает копию; путь записи выбирается снаружи. Условие 2…256 — явная политика нашего сервиса, не исчерпывающий контракт numColors. Передача 1 вызывает InvalidArgumentException до работы библиотеки.

Подготовить независимый цветовой вариант карточкиСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
function paletteVariant(Gmagick $source,int $colors,int $shift):Gmagick {
    if($colors<2||$colors>256)throw new InvalidArgumentException('Choose 2..256 palette colors');
    $copy=clone $source;$copy->quantizeImage($colors,Gmagick::COLORSPACE_RGB,0,false,false);
    $copy->cycleColormapImage($shift);return $copy;
}
$source=new Gmagick(__DIR__.'/product.ppm');$original=$source->getImageSignature();
$variant=paletteVariant($source,6,1);
$errors=['palette_size'=>gmTry(fn()=>paletteVariant($source,1,1))];
$variant->setImageDepth(8);
$variant->setImageFormat('PNG');
$variant->writeImage(__DIR__.'/gm99-palette-variant.png');
$decoded=new Gmagick(__DIR__.'/gm99-palette-variant.png');

gmReport('gm99-palette-function',["gmagick.construct", "gmagick.quantizeimage", "gmagick.cyclecolormapimage", "gmagick.getimagesignature", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage"],["gm99-palette-variant.png"],
    ['original_unchanged'=>$source->getImageSignature()===$original,'six_colors'=>$decoded->getImageColors()===6,'different_variant'=>$decoded->getImageSignature()!==$original,'policy_range'=>$errors['palette_size']['class']==='InvalidArgumentException','size_gm99_palette_variant'=>gmInspect('gm99-palette-variant.png')['width']===320&&gmInspect('gm99-palette-variant.png')['height']===200],
    $errors ?? [], ['palette_limit'=>6,'shift'=>1]);
echo "gm99-palette-variant.png 320x200\n";
Ожидаемый результат
gm99-palette-variant.png 320x200

Архив запускается командой php example.php с Gmagick. Проверенный отдельный runtime: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные входы восстанавливаются из .b64; gm-fixture.php проверяет файлы и результат, изучаемые операции находятся в самой программе. PNG и metrics.json записываются рядом.

Вывод успешного запуска
gm99-palette-variant.png 320x200

Проверки подтверждают неизменность оригинала, шесть цветов и реальное отличие варианта. Для другой позиции сдвига снова вызывайте функцию с оригиналом, а не пытайтесь отменить предыдущую перестановку. Если важны строго заданные фирменные RGB, выбирайте mapImage с эталоном; функция из этого раздела решает задачу декоративных вариантов, а не цветового соответствия бренду.

Упражнение: какой метод нужен для строго заданных четырёх RGB? Ответ:mapImage с эталоном. Вернёт ли quantizeImage(...,true) сумму ошибки как число? Ответ:нет, возвращается Gmagick; measureError только запрашивает расчёт. Можно ли считать cycle(+1),cycle(−1) надёжной отменой? Ответ:нет; сохраняйте оригинал. Для оценки цвета помогают гистограмма и частоты, а выразительный эффект для карточки можно развить в рельефное оформление.

Практический вопрос, из которого вырос сценарий

How to write these `convert` commands in Magick++?: Приложению нужна фиксированная палитра и управляемое распределение ошибки; автор пытается перенести команды другого API в код. Разделяем автоматический подбор quantizeImage и сопоставление mapImage с собственным четырёхцветным эталоном. Показываем реально различающиеся map-результаты и честно совпавшие quantize-результаты. Не обещаем перечисленные в вопросе алгоритмы CLI как дополнительные параметры bool API Gmagick.

Функции и методы этой темы

Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.

Gmagick::cyclecolormapimageСдвинуть назначение цветов палитры

PECL gmagick >= Unknown · gmagick

Gmagick::cyclecolormapimage(int $displace): Gmagick

displace — int, число позиций сдвига таблицы. Возвращается self/Gmagick. Повторные сдвиги могут создавать резко изменённые цветовые варианты; это не физическое изменение освещения и не регулировка оттенка. Перед работой явно создаём ограниченную палитру через quantizeImage. На фиксированной сборке последовательность +1, затем−1 не восстановила исходные пиксели и сократила число цветов с 6 до 5. Причина здесь не объявляется установленной и отрицательное значение не предлагается как гарантированная отмена. Для возврата храните оригинал и создавайте новый вариант из его копии.

Gmagick::cyclecolormapimage(int $displace): Gmagick
АргументТипСмысл
displaceint — обязательныйЧисло позиций сдвига таблицы цветов.

Возврат по PHP.net: Gmagick — изменённый объект изображения.

Официальный отказ: GmagickException при ошибке операции. Неверный PHP-тип может быть отвергнут раньше как TypeError; проверка диапазона в наших функциях даёт отдельную InvalidArgumentException.

Требуется PECL Gmagick. Нижняя граница версии указана PHP.net как Unknown; её не заменяем датой страницы или номером PHP в новостном баннере. Результаты здесь проверяются на фиксированной Q16-сборке, другие версии могут отличаться.

Два применения: Получить вариант цветов и проверить попытку обратного сдвига; Подготовить независимый цветовой вариант карточки.

Gmagick::mapimageПодогнать изображение под заданный эталон цветов

PECL gmagick >= Unknown · gmagick

Gmagick::mapimage(gmagick $gmagick, bool $dither): Gmagick

gmagick — объект изображения-эталона; dither — обязательный bool, возвращается Gmagick. Каждый цвет заменяется ближайшим доступным цветом эталона. В отличие от quantizeImage приложение заранее задаёт допустимую палитру, здесь четыре пикселя собственного palette.ppm. PHP.net в описании dither упоминает ненулевое целое, но тип сигнатуры bool: используем false/true. Распределение ошибки создаёт узор из допустимых цветов, а не новые промежуточные краски. Сохранённый результат проверяется по набору RGB, эталон остаётся неизменным. Строка вместо объекта даёт TypeError.

Gmagick::mapimage(gmagick $gmagick, bool $dither): Gmagick
АргументТипСмысл
gmagickgmagick — обязательныйИзображение-эталон допустимых цветов.
ditherbool — обязательныйfalse отключает, true запрашивает распределение ошибки; результат проверяем.

Возврат по PHP.net: Gmagick — изменённый объект изображения.

Официальный отказ: GmagickException при ошибке операции. Неверный PHP-тип может быть отвергнут раньше как TypeError; проверка диапазона в наших функциях даёт отдельную InvalidArgumentException.

Требуется PECL Gmagick. Нижняя граница версии указана PHP.net как Unknown; её не заменяем датой страницы или номером PHP в новостном баннере. Результаты здесь проверяются на фиксированной Q16-сборке, другие версии могут отличаться.

Два применения: Использовать только разрешённые цвета бренда; Сравнить сплошные области и распределение ошибки.

Gmagick::quantizeimageВыбрать ограниченную палитру по самому изображению

PECL gmagick >= Unknown · gmagick

Gmagick::quantizeimage(int $numColors, int $colorspace, int $treeDepth, bool $dither, bool $measureError): Gmagick

Метод анализирует текущее изображение и подбирает заданное число цветов, стремясь уменьшить различие с оригиналом при разумном времени обработки. numColors — int, желаемое количество; colorspace — пространство сокращения, обычно COLORSPACE_RGB. treeDepth=0 или 1 предлагает алгоритму выбрать глубину около log₄(число цветов), что в официальном описании связано с представлением, памятью и временем;8 разворачивает дерево полностью. Для малой дисперсии может быть уместна другая глубина. dither — bool: распределение ошибки между соседними пикселями по описанной в PHP.net кривой Гильберта. measureError — bool: вычислить сумму квадратов RGB-расстояний между исходными и квантованными пикселями. Возвращается Gmagick, а не вычисленная ошибка как float. Наши true/false dither при четырёх цветах дали одинаковые сохранённые пиксели; это наблюдение данного входа и версии, не отмена параметра. Для видимого сравнения распределения ошибки рядом есть mapImage.

Gmagick::quantizeimage(int $numColors, int $colorspace, int $treeDepth, bool $dither, bool $measureError): Gmagick
АргументТипСмысл
numColorsint — обязательныйЗапрашиваемое число цветов.
colorspaceint — обязательныйКонстанта COLORSPACE_*; в quantizeImage выбирает пространство анализа цветов.
treeDepthint — обязательныйГлубина дерева:0/1 автоматический выбор,8 полное дерево; подробности выше.
ditherbool — обязательныйfalse отключает, true запрашивает распределение ошибки; результат проверяем.
measureErrorbool — обязательныйЗапрос вычисления суммарной ошибки; возвращаемое значение остаётся объектом.

Возврат по PHP.net: Gmagick — изменённый объект изображения.

Официальный отказ: GmagickException при ошибке операции. Неверный PHP-тип может быть отвергнут раньше как TypeError; проверка диапазона в наших функциях даёт отдельную InvalidArgumentException.

Требуется PECL Gmagick. Нижняя граница версии указана PHP.net как Unknown; её не заменяем датой страницы или номером PHP в новостном баннере. Результаты здесь проверяются на фиксированной Q16-сборке, другие версии могут отличаться.

Два применения: Сравнить палитры из четырёх и восьми цветов; Проверить действие флага, не обещая различие заранее.

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.