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

Дерево руководства
Глава 428 · После первых изображений Gmagick, объектов, массивов и циклов
Gmagick: палитра серии, области изменений и метки сцен
Ограничить цвета по всей серии, сохранить области изменений и отличить scene от индекса.
Согласовать восемь цветов по всей серии
Для серии с плавным цветным фоном нужна согласованная палитра: иначе каждый кадр может подобрать свои оттенки. quantizeImages рассматривает всю последовательность. Сравним исходные градиенты с восемью цветами и сохраним как GIF, так и обычные PNG кадров.





<?php
$sequence = new Gmagick(__DIR__ . '/ramp-views.ppm');
$sequence->quantizeImages(8, Gmagick::COLORSPACE_RGB, 0, false, true);
$sequence->setImageIndex(0);
do { $sequence->setImageFormat('GIF'); $sequence->setImageDelay(30); } while ($sequence->nextImage());
$sequence->setImageIndex(0);
$sequence->writeImage(__DIR__ . '/eight-colors.gif', true);Первый аргумент ограничивает число цветов, второй задаёт RGB-пространство,0 даёт автоматическую глубину дерева. false отключает распределение ошибки; true в последнем аргументе запрашивает её измерение. Метод возвращает объект, а не число ошибки. Полный пример строит серию из исходных PPM без предварительной потери цвета в GIF.
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$sequence=new Gmagick();
$sequence->readImageBlob(file_get_contents(__DIR__.'/ramp-views.ppm'));
$sequence->setImageIndex(0);
do{$sequence->setImageDelay(30);$sequence->setImageDispose(Gmagick::DISPOSE_NONE);$sequence->setImageIterations(0);}while($sequence->nextImage());
$sequence->setImageIndex(0);
$sequence->setImageIndex(0);
$sequence->setImageDepth(8);
$sequence->setImageFormat('PNG');
$sequence->writeImage(__DIR__.'/gm100-gradient-source.png',false);
$same=$sequence->quantizeImages(8,Gmagick::COLORSPACE_RGB,0,false,true)===$sequence;
$counts=[];
foreach([0,1,2] as $i){$sequence->setImageIndex($i);$sequence->setImageDepth(8);$sequence->setImageFormat('PNG');$name='gm100-palette'.$i.'.png';$sequence->writeImage(__DIR__.'/'.$name,false);$counts[]=(new Gmagick(__DIR__.'/'.$name))->getImageColors();}
$sequence->setImageIndex(0);
do{$sequence->setImageFormat('GIF');}while($sequence->nextImage());
$sequence->setImageIndex(0);$sequence->writeImage(__DIR__.'/gm100-palette8.gif',true);
gmReport('gm100-palette-shared',["gmagick.construct", "gmagick.readimageblob", "gmagick.setimageindex", "gmagick.setimagedelay", "gmagick.setimagedispose", "gmagick.setimageiterations", "gmagick.nextimage", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage", "gmagick.quantizeimages", "gmagick.getimagecolors"],["gm100-gradient-source.png", "gm100-palette0.png", "gm100-palette1.png", "gm100-palette2.png"],
['eight_each'=>$counts===[8,8,8],'same_object'=>$same,'size_gm100_gradient_source'=>gmInspect('gm100-gradient-source.png')['width']===320&&gmInspect('gm100-gradient-source.png')['height']===200,'size_gm100_palette0'=>gmInspect('gm100-palette0.png')['width']===320&&gmInspect('gm100-palette0.png')['height']===200,'size_gm100_palette1'=>gmInspect('gm100-palette1.png')['width']===320&&gmInspect('gm100-palette1.png')['height']===200,'size_gm100_palette2'=>gmInspect('gm100-palette2.png')['width']===320&&gmInspect('gm100-palette2.png')['height']===200],
$errors ?? [], ['saved_color_counts'=>$counts,'measure_error_returns_scalar'=>false]);
echo "gm100-gradient-source.png 320x200 gm100-palette0.png 320x200 gm100-palette1.png 320x200 gm100-palette2.png 320x200\n";
Ожидаемый результат
gm100-gradient-source.png 320x200 gm100-palette0.png 320x200 gm100-palette1.png 320x200 gm100-palette2.png 320x200
Самостоятельный ZIP: php example.php с Gmagick. Окружение: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные PPM и готовый views.gif восстанавливаются из .b64. gm-fixture.php проверяет файлы и записывает metrics.json; изучаемые операции видны в самой программе. PNG и GIF сохраняются рядом.
Вывод успешного запуска
gm100-gradient-source.png 320x200 gm100-palette0.png 320x200 gm100-palette1.png 320x200 gm100-palette2.png 320x200
Восемь цветов для градиента — намеренно заметное ограничение: крупные ступени показывают работу алгоритма. Общая палитра не гарантирует хороший вид при любом слишком малом числе цветов. Сначала сравните изображение, затем оценивайте размер файла.
Сравнить запросы распределения ошибки
Узор dithering может скрывать ступени ограниченной палитры. Сохраним два варианта одной серии с одинаковым пределом цветов: без распределения ошибки и с ним. Результаты показываются рядом, а не оцениваются по одному булеву флагу.





<?php
$sequence = new Gmagick(__DIR__ . '/ramp-views.ppm');
$sequence->quantizeImages(8, Gmagick::COLORSPACE_RGB, 8, true, false);
$sequence->setImageIndex(0);
do { $sequence->setImageFormat('GIF'); $sequence->setImageDelay(30); } while ($sequence->nextImage());
$sequence->setImageIndex(0);
$sequence->writeImage(__DIR__ . '/dither.gif', true);Глубина8 раскрывает дерево полностью. Dither=true распределяет ошибку выбранного цвета по соседям; новые оттенки могут казаться присутствующими из-за пространственного узора, хотя палитра ограничена. MeasureError=false не запрашивает расчёт общей ошибки.
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$counts=[];
foreach([false,true] as $dither){
$sequence=new Gmagick();$sequence->readImageBlob(file_get_contents(__DIR__.'/ramp-views.ppm'));$sequence->setImageIndex(0);
do{$sequence->setImageDelay(30);$sequence->setImageDispose(Gmagick::DISPOSE_NONE);$sequence->setImageIterations(0);}while($sequence->nextImage());
$sequence->quantizeImages(8,Gmagick::COLORSPACE_RGB,$dither?8:0,$dither,false);
$sequence->setImageIndex(1);$tag=$dither?'on':'off';$sequence->setImageDepth(8);$sequence->setImageFormat('PNG');
$sequence->writeImage(__DIR__.'/gm100-dither-'.$tag.'.png',false);
$counts[]=(new Gmagick(__DIR__.'/gm100-dither-'.$tag.'.png'))->getImageColors();
$sequence->setImageIndex(0);do{$sequence->setImageFormat('GIF');}while($sequence->nextImage());$sequence->setImageIndex(0);$sequence->writeImage(__DIR__.'/gm100-dither-'.$tag.'.gif',true);
}
$off=new Gmagick(__DIR__.'/gm100-dither-off.png');$on=new Gmagick(__DIR__.'/gm100-dither-on.png');
$samePixels=$off->getImageSignature()===$on->getImageSignature();
gmReport('gm100-palette-dither',["gmagick.construct", "gmagick.readimageblob", "gmagick.setimageindex", "gmagick.setimagedelay", "gmagick.setimagedispose", "gmagick.setimageiterations", "gmagick.nextimage", "gmagick.quantizeimages", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage", "gmagick.getimagecolors", "gmagick.getimagesignature"],["gm100-dither-off.png", "gm100-dither-on.png"],
['eight_each'=>$counts===[8,8],'observed_same_pixels'=>$samePixels,'size_gm100_dither_off'=>gmInspect('gm100-dither-off.png')['width']===320&&gmInspect('gm100-dither-off.png')['height']===200,'size_gm100_dither_on'=>gmInspect('gm100-dither-on.png')['width']===320&&gmInspect('gm100-dither-on.png')['height']===200],
$errors ?? [], ['saved_counts'=>$counts,'observed_equal_pixels'=>$samePixels,'scope'=>'this input and fixed runtime; no general equivalence claim']);
echo "gm100-dither-off.png 320x200 gm100-dither-on.png 320x200\n";
Ожидаемый результат
gm100-dither-off.png 320x200 gm100-dither-on.png 320x200
Самостоятельный ZIP: php example.php с Gmagick. Окружение: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные PPM и готовый views.gif восстанавливаются из .b64. gm-fixture.php проверяет файлы и записывает metrics.json; изучаемые операции видны в самой программе. PNG и GIF сохраняются рядом.
Вывод успешного запуска
gm100-dither-off.png 320x200 gm100-dither-on.png 320x200
На этом входе и фиксированной сборке декодированные пиксели всех трёх кадров совпали для запросов false/depth0 и true/depth8, хотя байты файлов GIF различаются. Обе картинки имеют зернистые переходы. Здесь нельзя подписывать один результат «гладкий», а другой «с шумом», или выводить универсальное правило о флаге. Полная программа сравнивает подписи пикселей среднего PNG; причину равенства не приписываем непроверенному внутреннему механизму.
Сохранить только области изменений
У неподвижного фона изменяется только товар между двумя позициями. Посмотрим, какие области действительно требуются следующему кадру. Новый объект deconstructImages содержит первый полный кадр и ограничивающие прямоугольники изменений; исходная последовательность остаётся доступной.




<?php
$sequence = new Gmagick(__DIR__ . '/views.gif');
$patches = $sequence->deconstructImages();
$patches->setImageIndex(1);
$width = $patches->getImageWidth();
$height = $patches->getImageHeight();
$patches->setImageFormat('PNG');
$patches->writeImage(__DIR__ . '/difference.png', false);Переменная $patches содержит новый Gmagick. Ширина и высота описывают сохранённый фрагмент, а не весь холст320×200. В область отличий входят и место, откуда предмет ушёл, и место, куда пришёл: надо восстановить старый фон и нарисовать новое положение.
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$sequence=new Gmagick();
$sequence->readImageBlob(file_get_contents(__DIR__.'/views.ppm'));
$sequence->setImageIndex(0);
do{$sequence->setImageDelay(30);$sequence->setImageDispose(Gmagick::DISPOSE_NONE);$sequence->setImageIterations(0);}while($sequence->nextImage());
$sequence->setImageIndex(0);
$patches=$sequence->deconstructImages();$newObject=$patches!==$sequence;
$geometries=[];
foreach([0,1,2] as $i){$patches->setImageIndex($i);$geometries[]=[$patches->getImageWidth(),$patches->getImageHeight(),$patches->getImagePage()];}
$patches->setImageIndex(1);
$patches->setImageDepth(8);
$patches->setImageFormat('PNG');
$patches->writeImage(__DIR__.'/gm100-difference1.png',false);
$patches->setImageIndex(2);
$patches->setImageDepth(8);
$patches->setImageFormat('PNG');
$patches->writeImage(__DIR__.'/gm100-difference2.png',false);
$patches->setImageIndex(0);
do{$patches->setImageFormat('GIF');}while($patches->nextImage());
$patches->setImageIndex(0);$patches->writeImage(__DIR__.'/gm100-differences.gif',true);
gmReport('gm100-palette-differences',["gmagick.construct", "gmagick.readimageblob", "gmagick.setimageindex", "gmagick.setimagedelay", "gmagick.setimagedispose", "gmagick.setimageiterations", "gmagick.nextimage", "gmagick.deconstructimages", "gmagick.getimagewidth", "gmagick.getimageheight", "gmagick.getimagepage", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage"],["gm100-difference1.png", "gm100-difference2.png"],
['new_object'=>$newObject,'source_full'=>$sequence->getImageWidth()===320&&$sequence->getImageHeight()===200,'patch_geometry'=>$geometries[1][0]===160&&$geometries[1][1]===80&&$geometries[1][2]['x']===30&&$geometries[2][2]['x']===130,'size_gm100_difference1'=>gmInspect('gm100-difference1.png')['width']===160&&gmInspect('gm100-difference1.png')['height']===80,'size_gm100_difference2'=>gmInspect('gm100-difference2.png')['width']===160&&gmInspect('gm100-difference2.png')['height']===80],
$errors ?? [], ['patch_geometry'=>$geometries,'original_unchanged_geometry'=>true]);
echo "gm100-difference1.png 160x80 gm100-difference2.png 160x80\n";
Ожидаемый результат
gm100-difference1.png 160x80 gm100-difference2.png 160x80
Самостоятельный ZIP: php example.php с Gmagick. Окружение: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные PPM и готовый views.gif восстанавливаются из .b64. gm-fixture.php проверяет файлы и записывает metrics.json; изучаемые операции видны в самой программе. PNG и GIF сохраняются рядом.
Вывод успешного запуска
gm100-difference1.png 160x80 gm100-difference2.png 160x80
Размер прямоугольника — не единственный критерий качества. Сравните воспроизведение полного GIF и сокращённого GIF, а также их реальные байты. Не обещаем, что любой вход после deconstructImages уменьшится или сохранит правильность при произвольном dispose.
Для этих кадров получены области320×200,160×80 и160×80; смещения последних —(30,60) и(130,60). Исходный полный GIF в исследованном варианте занимал2342 байта, сокращённый1760. Это измерение конкретных файлов, не обещание коэффициента сжатия. Смещение задаёт место фрагмента на холсте, а размеры подписи PNG относятся к самому фрагменту.
Отделить метку сцены от позиции списка
Номера кадров во внешней системе могут быть10,20,30, хотя позиции списка остаются0,1,2. Назначим метки scene и проверим это различие. Видимый средний ракурс останется средним после назначения ему метки20.


<?php
$sequence = new Gmagick(__DIR__ . '/views.gif');
$sequence->setImageIndex(1);
$sequence->setImageScene(20);
$scene = $sequence->getImageScene();
$index = $sequence->getImageIndex();
echo $index, ':', $scene, "\n";getImageIndex сообщает положение указателя, getImageScene — метаданные выбранного кадра. Setter scene не сортирует последовательность. Полная программа присваивает метки всем кадрам, сохраняет GIF и отдельно показывает метки после чтения файла.
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$sequence=new Gmagick();
$sequence->readImageBlob(file_get_contents(__DIR__.'/views.ppm'));
$sequence->setImageIndex(0);
do{$sequence->setImageDelay(30);$sequence->setImageDispose(Gmagick::DISPOSE_NONE);$sequence->setImageIterations(0);}while($sequence->nextImage());
$sequence->setImageIndex(0);
$before=[];
foreach([10,20,30] as $i=>$scene){$sequence->setImageIndex($i);$sequence->setImageScene($scene);$before[]=[$sequence->getImageIndex(),$sequence->getImageScene()];}
$sequence->setImageIndex(1);
$sequence->setImageDepth(8);
$sequence->setImageFormat('PNG');
$sequence->writeImage(__DIR__.'/gm100-scene20.png',false);
$sequence->setImageIndex(0);
do{$sequence->setImageFormat('GIF');}while($sequence->nextImage());
$sequence->setImageIndex(0);$sequence->writeImage(__DIR__.'/gm100-scene-tags.gif',true);
$read=new Gmagick(__DIR__.'/gm100-scene-tags.gif');$after=[];$read->setImageIndex(0);
do{$after[]=[$read->getImageIndex(),$read->getImageScene()];}while($read->nextImage());
gmReport('gm100-palette-scenes',["gmagick.construct", "gmagick.readimageblob", "gmagick.setimageindex", "gmagick.setimagedelay", "gmagick.setimagedispose", "gmagick.setimageiterations", "gmagick.nextimage", "gmagick.setimagescene", "gmagick.getimageindex", "gmagick.getimagescene", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage"],["gm100-scene20.png"],
['memory_tags'=>$before===[[0,10],[1,20],[2,30]],'gif_scenes_renumbered'=>$after===[[0,0],[1,1],[2,2]],'size_gm100_scene20'=>gmInspect('gm100-scene20.png')['width']===320&&gmInspect('gm100-scene20.png')['height']===200],
$errors ?? [], ['before'=>$before,'gif_readback'=>$after]);
echo "gm100-scene20.png 320x200\n";
Ожидаемый результат
gm100-scene20.png 320x200
Самостоятельный ZIP: php example.php с Gmagick. Окружение: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные PPM и готовый views.gif восстанавливаются из .b64. gm-fixture.php проверяет файлы и записывает metrics.json; изучаемые операции видны в самой программе. PNG и GIF сохраняются рядом.
Вывод успешного запуска
gm100-scene20.png 320x200
Не рассчитывайте на перенос произвольной scene-метки через любой формат. Для устойчивого соответствия внешним ID используйте отдельные данные приложения. Для выбора позиции нужен setImageIndex, а не setImageScene.
В нашем GIF метки10,20,30 после чтения стали0,1,2. Это наблюдаемый контракт выбранного кодера: произвольная метка памяти не переносится этим способом. Порядок картинок при этом остался прежним.
Найти кадр по метке приложения
В каталоге просят кадр с меткой200, а не «второй файл». Функция поиска обходит последовательность и сравнивает getImageScene. После обнаружения сохраняем выбранный ракурс; отсутствие нужной метки становится понятной ошибкой приложения.

<?php
$sequence = new Gmagick(__DIR__ . '/views.gif');
$sequence->setImageIndex(1);
$sequence->setImageScene(200);
$label = $sequence->getImageScene();
$sequence->setImageFormat('PNG');
$sequence->writeImage(__DIR__ . '/scene-200.png', false);Короткий фрагмент показывает назначение и чтение метки. В полной функции do/while проходит все кадры, а if сравнивает метку с искомой. Возвращаемая позиция затем явно выбирается перед записью PNG. Метки формируются в памяти из наших входных данных, а не извлекаются из неподтверждённых метаданных GIF.
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$sequence=new Gmagick();
$sequence->readImageBlob(file_get_contents(__DIR__.'/views.ppm'));
$sequence->setImageIndex(0);
do{$sequence->setImageDelay(30);$sequence->setImageDispose(Gmagick::DISPOSE_NONE);$sequence->setImageIterations(0);}while($sequence->nextImage());
$sequence->setImageIndex(0);
foreach([100,200,300] as $i=>$scene){$sequence->setImageIndex($i);$sequence->setImageScene($scene);}
function sceneIndex(Gmagick $sequence,int $wanted):int{
$sequence->setImageIndex(0);
do{if($sequence->getImageScene()===$wanted)return $sequence->getImageIndex();}while($sequence->nextImage());
throw new OutOfBoundsException('Scene label not found');
}
$found=sceneIndex($sequence,200);$sequence->setImageIndex($found);$scene=$sequence->getImageScene();
$sequence->setImageDepth(8);
$sequence->setImageFormat('PNG');
$sequence->writeImage(__DIR__.'/gm100-found-scene.png',false);
$errors=['missing'=>gmTry(fn()=>sceneIndex($sequence,999))];
gmReport('gm100-palette-find-scene',["gmagick.construct", "gmagick.readimageblob", "gmagick.setimageindex", "gmagick.setimagedelay", "gmagick.setimagedispose", "gmagick.setimageiterations", "gmagick.nextimage", "gmagick.setimagescene", "gmagick.getimagescene", "gmagick.getimageindex", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage"],["gm100-found-scene.png"],
['selected_label'=>$found===1&&$scene===200,'missing_rejected'=>$errors['missing']['class']==='OutOfBoundsException','size_gm100_found_scene'=>gmInspect('gm100-found-scene.png')['width']===320&&gmInspect('gm100-found-scene.png')['height']===200],
$errors ?? [], ['selected_index'=>$found,'selected_scene'=>$scene]);
echo "gm100-found-scene.png 320x200\n";
Ожидаемый результат
gm100-found-scene.png 320x200
Самостоятельный ZIP: php example.php с Gmagick. Окружение: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные PPM и готовый views.gif восстанавливаются из .b64. gm-fixture.php проверяет файлы и записывает metrics.json; изучаемые операции видны в самой программе. PNG и GIF сохраняются рядом.
Вывод успешного запуска
gm100-found-scene.png 320x200
Если метки могут повторяться, определите правило: эта функция возвращает первое совпадение. Она не выдаёт случайный текущий кадр, когда ничего не найдено. Для массового каталога соответствие ID и файлов удобно держать отдельно от визуального контейнера.
Подготовить компактную анимацию и обложку
Завершим путь публикации: общий набор цветов, области изменений, заметная задержка и явная запись всей последовательности. Сохраним анимированный файл и отдельную статическую обложку, чтобы один результат подходил и для просмотра движения, и для неподвижной карточки.



<?php
$sequence = new Gmagick(__DIR__ . '/ramp-views.ppm');
$sequence->quantizeImages(16, Gmagick::COLORSPACE_RGB, 0, false, false);
$compact = $sequence->deconstructImages();
$compact->setImageIndex(0);
do { $compact->setImageFormat('GIF'); $compact->setImageDelay(30); } while ($compact->nextImage());
$compact->setImageIndex(0);
$compact->writeImage(__DIR__ . '/catalog-compact.gif', true);Сначала выбираются цвета по полным кадрам, затем сравниваются уже приведённые к ним изображения. Обратный порядок может включить в анализ цвета только обрезанных областей. Полная функция явно назначает время и dispose, получает новый объект областей и сохраняет оба вида результата.
<?php
declare(strict_types=1);
require __DIR__ . '/gm-fixture.php';
gmRestore();
$sequence=new Gmagick();
$sequence->readImageBlob(file_get_contents(__DIR__.'/ramp-views.ppm'));
$sequence->setImageIndex(0);
do{$sequence->setImageDelay(30);$sequence->setImageDispose(Gmagick::DISPOSE_NONE);$sequence->setImageIterations(0);}while($sequence->nextImage());
$sequence->setImageIndex(0);
function compactCatalog(Gmagick $sequence,int $colors):Gmagick{
if($colors<2||$colors>256)throw new InvalidArgumentException('Palette must be2..256 colors');
$sequence->quantizeImages($colors,Gmagick::COLORSPACE_RGB,0,false,false);
$sequence->setImageIndex(0);do{$sequence->setImageDelay(40);$sequence->setImageDispose(Gmagick::DISPOSE_NONE);$sequence->setImageIterations(0);}while($sequence->nextImage());
return $sequence->deconstructImages();
}
$compact=compactCatalog($sequence,16);$sequence->setImageIndex(1);
$sequence->setImageDepth(8);
$sequence->setImageFormat('PNG');
$sequence->writeImage(__DIR__.'/gm100-catalog-cover.png',false);
$compact->setImageIndex(0);
do{$compact->setImageFormat('GIF');}while($compact->nextImage());
$compact->setImageIndex(0);$compact->writeImage(__DIR__.'/gm100-catalog-compact.gif',true);
$geometry=[];$compact->setImageIndex(0);do{$geometry[]=[$compact->getImageWidth(),$compact->getImageHeight()];}while($compact->nextImage());
$errors=['colors'=>gmTry(fn()=>compactCatalog($sequence,1))];
gmReport('gm100-palette-publish',["gmagick.construct", "gmagick.readimageblob", "gmagick.setimageindex", "gmagick.setimagedelay", "gmagick.setimagedispose", "gmagick.setimageiterations", "gmagick.nextimage", "gmagick.quantizeimages", "gmagick.deconstructimages", "gmagick.setimagedepth", "gmagick.setimageformat", "gmagick.writeimage", "gmagick.getimagewidth", "gmagick.getimageheight"],["gm100-catalog-cover.png"],
['compact_geometry'=>$geometry===[[320,200],[256,200],[192,192]],'palette_limit'=>(new Gmagick(__DIR__.'/gm100-catalog-cover.png'))->getImageColors()===16,'range_rejected'=>$errors['colors']['class']==='InvalidArgumentException','size_gm100_catalog_cover'=>gmInspect('gm100-catalog-cover.png')['width']===320&&gmInspect('gm100-catalog-cover.png')['height']===200],
$errors ?? [], ['patch_geometry'=>$geometry,'source'=>'full frames; shared palette before difference extraction']);
echo "gm100-catalog-cover.png 320x200\n";
Ожидаемый результат
gm100-catalog-cover.png 320x200
Самостоятельный ZIP: php example.php с Gmagick. Окружение: PHP8.3.6 / Gmagick2.0.6RC1 / GraphicsMagick1.3.42 Q16. Собственные PPM и готовый views.gif восстанавливаются из .b64. gm-fixture.php проверяет файлы и записывает metrics.json; изучаемые операции видны в самой программе. PNG и GIF сохраняются рядом.
Вывод успешного запуска
gm100-catalog-cover.png 320x200
Это воспроизводимый маршрут для наших непрозрачных кадров с общим холстом. Для произвольной загруженной анимации сначала надо восстановить отображаемые кадры с учётом области и dispose, а не просто прогнать этот фрагмент вслепую. Дальше проверьте имя, формат, запись и освобождение ресурсов в связанных главах.
Упражнение: возврат quantizeImages — число ошибки или объект? Ответ:объект. Совпадает ли scene200 с индексом200? Ответ:нет. Почему видимый фон может увеличивать область различий после квантизации? Ответ:пиксели фона тоже могут различаться; нужно измерять результат. В нашем маршруте16 цветов дали области320×200,256×200 и192×192, а не прямоугольники только коробки. Дальнейшие шаги: имена и состояние файла, реальное кодирование экспорта и состояние и очистка объектов. Для одиночной картинки сравните quantizeImage и mapImage.
Практический вопрос и наш ответ
Effective gif/image color quantization?: При создании GIF сокращение цветов меняет заметные оттенки, а разные кадры дают цветовое мерцание; автор ищет более подходящий алгоритм. Мы рассматриваем всю последовательность одним quantizeImages, сохраняем исходный градиент и каждый результат, сравниваем ограничения палитры и фактическое действие параметров. На нашей сборке два запроса dither дали одинаковые пиксели; это наблюдение явно отделено от описанного назначения аргумента и не превращено в универсальное правило.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
Gmagick::deconstructimagesСравнивает соседние изображения последовательности и возвращает новый Gmagick с ограничивающими областями обнаруженных различий
PECL gmagick >= Unknown · gmagick
Gmagick::deconstructimages(): GmagickСравнивает соседние изображения последовательности и возвращает новый Gmagick с ограничивающими областями обнаруженных различий. Это геометрия изменений, а не универсальный оптимизатор любого GIF. Сохраняйте положение области на общем холсте; один обрезанный PNG ещё не является полным кадром анимации.
Gmagick::deconstructimages(): GmagickАргументов нет. Getter или навигация относятся к текущему изображению и списку, как описано выше.
Возврат: Новый объект Gmagick с областями отличий; исходный объект не заменяется автоматически.
PHP.net указывает GmagickException при ошибке. Неверный тип аргумента может быть отклонён PHP как TypeError до выполнения операции. Проверки диапазона в функциях примеров — отдельные правила приложения. Для nextImage/previousImage обычная граница обхода также выражается false.
Доступность: PECL Gmagick; нижняя граница на этих официальных страницах указана Unknown. Это не означает определённую версию PHP. Примеры привязаны к указанной Q16-сборке, ограничения которой разобраны в уроке.
Два применения: Сохранить только области изменений; Подготовить компактную анимацию и обложку.
Gmagick::getimagesceneЧитает целую метку сцены активного изображения
PECL gmagick >= Unknown · gmagick
Gmagick::getimagescene(): intЧитает целую метку сцены активного изображения. Она позволяет сопоставить кадр с внешним номером съёмки, но не заменяет индекс обхода. Если нужен устойчивый идентификатор во внешнем формате, сохраняйте соответствие отдельно.
Gmagick::getimagescene(): intАргументов нет. Getter или навигация относятся к текущему изображению и списку, как описано выше.
Возврат: Целое число с указанным выше смыслом.
PHP.net указывает GmagickException при ошибке. Неверный тип аргумента может быть отклонён PHP как TypeError до выполнения операции. Проверки диапазона в функциях примеров — отдельные правила приложения. Для nextImage/previousImage обычная граница обхода также выражается false.
Доступность: PECL Gmagick; нижняя граница на этих официальных страницах указана Unknown. Это не означает определённую версию PHP. Примеры привязаны к указанной Q16-сборке, ограничения которой разобраны в уроке.
Два применения: Отделить метку сцены от позиции списка; Найти кадр по метке приложения.
Gmagick::quantizeimagesАнализирует цвета последовательности и выбирает фиксированное ограниченное множество представительных цветов, стремясь уменьшить цветовую ошибку и время обработки
PECL gmagick >= Unknown · gmagick
Gmagick::quantizeimages(int $numColors, int $colorspace, int $treeDepth, bool $dither, bool $measureError): GmagickАнализирует цвета последовательности и выбирает фиксированное ограниченное множество представительных цветов, стремясь уменьшить цветовую ошибку и время обработки. В отличие от quantizeImage, единицей анализа служит вся серия. Число цветов в сохранённых кадрах и рисунок градиента проверяем отдельно.
Gmagick::quantizeimages(int $numColors, int $colorspace, int $treeDepth, bool $dither, bool $measureError): Gmagick| Аргумент | Тип | Смысл |
|---|---|---|
numColors | int — обязательный | Максимальное желаемое число цветов, выбираемых по всей последовательности. Это ограничение палитры, а не число кадров; фактически использованных цветов может быть меньше. |
colorspace | int — обязательный | Пространство, в котором выполняется сокращение цветов; обычный вариант — Gmagick::COLORSPACE_RGB. |
treeDepth | int — обязательный | Глубина дерева:0 или1 поручают алгоритму выбрать примерно log4(numColors), обычно с хорошим балансом памяти, скорости и представления цветов. Для малой дисперсии цветов иной уровень может быть полезнее.8 раскрывает дерево полностью. |
dither | bool — обязательный | Булев флаг распределения ошибки: ненулевое значение передаёт разницу между исходным и выбранным цветом соседям по кривой Гильберта. Узор может маскировать полосы градиента; новых цветов сверх палитры он не создаёт. |
measureError | bool — обязательный | Булев запрос расчёта общей ошибки квантизации: сумма квадратов RGB-расстояний исходных и полученных пикселей. Возвратом метода остаётся Gmagick, а не вычисленная сумма. |
Возврат: Изменённый Gmagick; объект можно использовать для следующей операции.
PHP.net указывает GmagickException при ошибке. Неверный тип аргумента может быть отклонён PHP как TypeError до выполнения операции. Проверки диапазона в функциях примеров — отдельные правила приложения. Для nextImage/previousImage обычная граница обхода также выражается false.
Доступность: PECL Gmagick; нижняя граница на этих официальных страницах указана Unknown. Это не означает определённую версию PHP. Примеры привязаны к указанной Q16-сборке, ограничения которой разобраны в уроке.
Два применения: Согласовать восемь цветов по всей серии; Сравнить запросы распределения ошибки.
Gmagick::setimagesceneНазначает целую метку сцены текущему изображению и возвращает Gmagick
PECL gmagick >= Unknown · gmagick
Gmagick::setimagescene(int $scene): GmagickНазначает целую метку сцены текущему изображению и возвращает Gmagick. Метка не означает перемещение изображения в такую позицию. После записи GIF проверяем, что именно сохранил формат; номер в памяти не является обещанием произвольных метаданных в файле.
Gmagick::setimagescene(int $scene): Gmagick| Аргумент | Тип | Смысл |
|---|---|---|
scene | int — обязательный | Целая метка сцены у активного изображения. Её изменение само по себе не переставляет кадры и не передвигает указатель. Сохранение такой метки зависит от формата. |
Возврат: Изменённый Gmagick; объект можно использовать для следующей операции.
PHP.net указывает GmagickException при ошибке. Неверный тип аргумента может быть отклонён PHP как TypeError до выполнения операции. Проверки диапазона в функциях примеров — отдельные правила приложения. Для nextImage/previousImage обычная граница обхода также выражается false.
Доступность: PECL Gmagick; нижняя граница на этих официальных страницах указана Unknown. Это не означает определённую версию PHP. Примеры привязаны к указанной Q16-сборке, ограничения которой разобраны в уроке.
Два применения: Отделить метку сцены от позиции списка; Найти кадр по метке приложения.
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


