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

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

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

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

Глава 381 · После объектов, клонирования, альфа-канала и исключений

Imagick: встроенные контуры 8BIM и явная маска отдельного слоя

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

Использовать сохранённый контур и сразу увидеть полезный результат

В каталоге нужно изменить только выбранную область изображения, оставив остальное прежним. Если файл уже содержит сохранённый контур, его можно использовать повторно. Сначала посмотрите конкретный результат: исходник, изменение внутри учебного окна и изменение снаружи. Окно занимает правую часть рисунка; это заранее заданный контур, а не автоматическое распознавание товара.

Исходник 400 × 320, отрицательные цвета внутри окна и снаружи: настоящий результат отдельного слоя, ограниченного извлечённой маской
Исходник 400 × 320, отрицательные цвета внутри окна и снаружи: настоящий результат отдельного слоя, ограниченного извлечённой маской

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

Называя что-либо «обрезкой», легко смешать разные действия. Здесь речь о контуре из профиля 8BIM, который библиотека превращает в маску записи. Сам выбор пути не означает, что размер картинки уменьшился, пиксели вокруг товара удалились или альфа-канал стал прозрачным. В нашей проверенной сборке прямое продолжение clip → negate не ограничило эффект. Поэтому сначала увидим саму извлечённую маску, а затем применим её явно к отдельному слою.

ЗадачаЧто она действительно меняет
Выбрать встроенный путь 8BIMclipImage/clipPathImage создают маску из существующего профиля. Реальный эффект следующей операции проверяется отдельно.
Вырезать прямоугольникcropImage меняет геометрию растровой области; это другая задача.
Сделать часть картинки прозрачнойНужна осмысленная работа с альфа-каналом и подходящий выходной формат. Выбор пути сам этого не обещает.
Ограничить отдельное рисованиеИменованный clip path в ImagickDraw относится к состоянию рисования, а не автоматически к профилю 8BIM.
Найти контур товара на фотографииЭто сегментация/выделение объекта. Данные методы используют готовый путь вместо поиска по содержимому.
$image->clipImage();
$image->clipPathImage("product-window", true);
$image->clipPathImage("#1", false);

$image — переменная с объектом изображения. Стрелка -> вызывает его метод. clipImage() не принимает аргументов и выбирает первый встроенный путь. В clipPathImage первая строка задаёт имя или номер пути, запятая отделяет обязательный флаг inside. true и false — логические значения, их не заключают в кавычки. Точка с запятой завершает инструкцию. Это три независимые формы вызова для объяснения, а не предложение последовательно менять одно и то же состояние без необходимости.

В извлечённой здесь маске true даёт белое внутри окна и чёрное снаружи, false — наоборот. Для нашего явного CopyAlpha белая интенсивность станет непрозрачной выбранной областью слоя, чёрная — прозрачной. Это конкретная интерпретация извлечённой маски в композиции, а не универсальное правило разрешения записи всех операций ImageMagick. Объекты, логические значения и операторы, отдельная копия через clone и ограничение рисования — связанные темы.

Самый короткий вызов выбирает уже существующий путь. Чтобы увидеть его работу, следующий фрагмент получает маску как отдельное изображение. Здесь $mask — новый объект, а не строка с именем пути; присваивание сохраняет результат метода.

$image->clipImage();
$mask = $image->getImageMask(Imagick::PIXELMASK_WRITE);

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

Получить изменение внутри и снаружи через отдельную маску слоя

На трёх панелях исходник, изменение внутри учебного окна и изменение снаружи. Вызов clip только создал маску и сохранил прежние RGB. Видимый локальный эффект собран явно: извлечённая маска задаёт альфа-канал отдельного изменённого слоя, затем слой накладывается OVER на чистую копию исходника.

Получить изменение внутри и снаружи через отдельную маску слояПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/clipping-fixture.php';
clippingRestore();
$base = clippingParts();
$profile = (string)file_get_contents(__DIR__.'/profile.8bim');
$before = clippingHash($base);
$sourceInside = clippingPoint($base,300,160);
$sourceOutside = clippingPoint($base,100,160);
clippingView($base,'source-preview8.png');
$results = []; $cases = [];
foreach ([true,false] as $inside) {
    $selection = clone $base;
    $selection->setImageProfile('8bim',$profile);
    $ok = $selection->clipPathImage('product-window',$inside);
    $selectionUnchanged = clippingHash($selection)===$before;
    $mask = $selection->getImageMask(Imagick::PIXELMASK_WRITE);
    if (!$mask instanceof Imagick) { throw new RuntimeException('Expected a real extracted mask'); }
    // CopyAlpha must copy grayscale intensity, not a pre-existing opaque alpha.
    $mask->setImageAlphaChannel(Imagick::ALPHACHANNEL_OFF);
    $layer = clone $base; // This layer has NEVER received a clipping mask.
    $layer->negateImage(false);
    $layer->setImageAlphaChannel(Imagick::ALPHACHANNEL_SET);
    $layer->compositeImage($mask,Imagick::COMPOSITE_COPYALPHA,0,0);
    $result = clone $base;
    $result->compositeImage($layer,Imagick::COMPOSITE_OVER,0,0);
    $name = $inside?'inside':'outside';
    clippingView($mask,$name.'-mask.png');
    clippingView($result,$name.'-effect.png');
    $results[]=$result;
    $cases[]=['inside'=>$inside,'return_type'=>get_debug_type($ok),'return_value'=>$ok,
        'clip_RGB_unchanged'=>$selectionUnchanged,
        'mask_inside'=>clippingPoint($mask,300,160),'mask_outside'=>clippingPoint($mask,100,160),
        'result_inside'=>clippingPoint($result,300,160),'result_outside'=>clippingPoint($result,100,160)];
}
$rects=clippingCompare([$base,...$results],'embedded-first.png');
clippingDiagnostic('embedded-clipping-first',['executed_api'=>'clipPathImage',
    'profile_sha256'=>hash('sha256',$profile),'cases'=>$cases,
    'source_inside'=>$sourceInside,'source_outside'=>$sourceOutside,
    'source_unchanged'=>clippingHash($base)===$before,'panel_geometry'=>$rects,
    'checks'=>['both_calls_true'=>array_column($cases,'return_value')===[true,true],
        'clip_calls_keep_RGB'=>array_column($cases,'clip_RGB_unchanged')===[true,true],
        'inside_mask_white'=>$cases[0]['mask_inside']===[255,255,255]&&$cases[0]['mask_outside']===[0,0,0],
        'outside_mask_inverse'=>$cases[1]['mask_inside']===[0,0,0]&&$cases[1]['mask_outside']===[255,255,255],
        'inside_effect_local'=>$cases[0]['result_inside']!==$sourceInside&&$cases[0]['result_outside']===$sourceOutside,
        'outside_effect_local'=>$cases[1]['result_inside']===$sourceInside&&$cases[1]['result_outside']!==$sourceOutside,
        'source_unchanged'=>clippingHash($base)===$before]]);
echo "embedded-first.png\nsource-preview8.png\ninside-mask.png\noutside-mask.png\ninside-effect.png\noutside-effect.png\nmetrics.json\nclip-keeps-RGB=1 extracted-mask-to-alpha=1 inside-and-outside-effects=1\n";

На трёх панелях исходник, изменение внутри учебного окна и изменение снаружи. Вызов clip только создал маску и сохранил прежние RGB. Видимый локальный эффект собран явно: извлечённая маска задаёт альфа-канал отдельного изменённого слоя, затем слой накладывается OVER на чистую копию исходника. Архив содержит example.php и три текстовых fixture-файла: source.png.b64, profile.8bim.b64, clipping-fixture.php. Сохраните рядом и выполните php example.php с Imagick, поддержкой PNG/8BIM/SVG и правом записи. Helper восстановит точный собственный рисунок и настоящий бинарный профиль по SHA. Изучаемые clip-вызовы, извлечение маски и композиция остаются в программе; helper только готовит вход, сохраняет просмотры и диагностику.

Ожидаемый вывод
embedded-first.png
source-preview8.png
inside-mask.png
outside-mask.png
inside-effect.png
outside-effect.png
metrics.json
clip-keeps-RGB=1 extracted-mask-to-alpha=1 inside-and-outside-effects=1

Проследить путь из профиля в маску, альфа-канал и результат

Для воспроизводимого урока используется собственный рисунок 400 × 320 и собственный бинарный ресурс 8BIM с именем product-window. В нём замкнутый прямоугольный путь от (225, 100) до (375, 220): это учебное окно вокруг правой части рисунка, а не автоматически распознанная граница товара. Его точки заданы точными нормированными координатами signed8.24, которые формат связывает с шириной и высотой изображения. Такой профиль позволяет проверить механизм без чужого коммерческого TIFF, но не выдаётся за файл, экспортированный Photoshop.

Ресурс 8BIM — метаданные, а не сам PNG. Путь рисования, строка SVG и ресурс профиля не становятся взаимозаменяемыми от того, что все они описывают похожую фигуру. Профиль можно передать в setImageProfile("8bim", $bytes), после чего метод ищет путь именно там. Наличие любого профиля с именем 8bim ещё не гарантирует наличие пути с нужным именем. Извлечённый библиотекой SVG нужен для проверки происхождения контура, а не для подмены отсутствующего профиля вручную нарисованной маской.

Реальные маски одного 8BIM-контура: true — белое внутри, false — белое снаружи; затем интенсивность явно копируется в альфа-канал отдельного слоя
Реальные маски одного 8BIM-контура: true — белое внутри, false — белое снаружи; затем интенсивность явно копируется в альфа-канал отдельного слоя

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

Объект или значениеЧто находится внутри и куда идёт дальше
$baseЧистый исходник 400 × 320 без добавленной маски; остаётся для сравнения и финальной композиции.
$selectionНезависимая копия с бинарным профилем и выбранным путём. RGB после clip прежние.
$maskНастоящий результат getImageMask: белая выбранная область и чёрная противоположная. Его собственный альфа-канал отключаем перед CopyAlpha.
$layerЕщё одна копия исходника, которая никогда не получала clip. Цвета меняются на всём этом слое, затем маска ограничивает его видимую часть.
$resultЧистая копия исходника плюс слой через COMPOSITE_OVER. Вне непрозрачной области слоя остаются исходные пиксели.
$mask->setImageAlphaChannel(Imagick::ALPHACHANNEL_OFF);
$layer = clone $base;
$layer->negateImage(false);
$layer->setImageAlphaChannel(Imagick::ALPHACHANNEL_SET);
$layer->compositeImage($mask, Imagick::COMPOSITE_COPYALPHA, 0, 0);
$result = clone $base;
$result->compositeImage($layer, Imagick::COMPOSITE_OVER, 0, 0);

clone создаёт независимую копию. negateImage(false) меняет цвет каждого пикселя слоя; false не означает «снаружи пути», это собственный аргумент negate. ALPHACHANNEL_SET включает альфа-канал слоя. CopyAlpha берёт альфа-канал источника, если он есть, иначе его серую интенсивность. Поэтому у маски заранее выполняется ALPHACHANNEL_OFF: белое должно дать видимый слой, чёрное — прозрачный, а не скопировать сплошную непрозрачность. Два нуля размещают маску и слой без смещения. OVER накладывает видимые пиксели слоя на чистый исходник.

Ни одна из этих стадий не уменьшает размер 400 × 320. Если нужен файл меньшего размера, это отдельный crop и виртуальная страница. Если нужна прозрачность в итоговом PNG, спроектируйте прозрачную основу отдельно: здесь итог намеренно непрозрачен и показывает локальное изменение цвета.

Запустить каждый из трёх методов на настоящем встроенном пути

Три настоящих PNG-маски: первый путь, внешняя область по #1 и именованный путь через псевдоним
Три настоящих PNG-маски: первый путь, внешняя область по #1 и именованный путь через псевдоним

Разберём три варианта по отдельности. Каждый архив самостоятельный: в нём тот же собственный рисунок и тот же настоящий 8BIM-профиль. Сначала профиль читается как байты и присоединяется к копии, затем выполняется именно указанный метод. Сохранённый PNG показывает маску, а диагностика подтверждает неизменность RGB исходника.

$bytes = file_get_contents(__DIR__ . "/profile.8bim");
$image->setImageProfile("8bim", $bytes);
$ok = $image->clipImage();

__DIR__ — каталог текущего PHP-файла; точка соединяет строки пути. file_get_contents читает содержимое файла, а не сам контур как отдельную картинку. setImageProfile помещает эти байты в профиль текущего изображения. $ok получает результат clipImage; это не объект маски. Маска получается отдельным getImageMask. В скачиваемом примере helper сначала проверяет точные входные байты по SHA, поэтому ошибка отсутствующего учебного файла не маскируется случайным входом.

Увидеть настоящую маску первого встроенного пути

clipImage без аргументов выбрал первый встроенный путь. RGB исходника не изменились. Сохранённая реальная маска белая внутри учебного окна, чёрная снаружи; это конкретный видимый результат вызова и извлечения маски.

Увидеть настоящую маску первого встроенного путиПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/clipping-fixture.php';
clippingRestore();
$base = clippingParts();
$profile = (string)file_get_contents(__DIR__.'/profile.8bim');
$image = clone $base;
$image->setImageProfile('8bim',$profile);
$before = clippingHash($image);
$status = $image->clipImage();
$unchanged = $before===clippingHash($image);
$mask = $image->getImageMask(Imagick::PIXELMASK_WRITE);
if (!$mask instanceof Imagick) { throw new RuntimeException('Expected a real extracted mask'); }
$mask->setImageAlphaChannel(Imagick::ALPHACHANNEL_OFF);
clippingView($mask,'embedded-clipimage-mask.png');
$declared = (string)(new ReflectionMethod(Imagick::class,'clipImage'))->getReturnType();
$inside = clippingPoint($mask,300,160); $outside = clippingPoint($mask,100,160);
clippingDiagnostic('embedded-clipping-clipimage-minimal',['executed_api'=>'clipImage',
    'profile_sha256'=>hash('sha256',$profile), 'return_type'=>get_debug_type($status),
    'return_value'=>$status,'declared_return_type'=>$declared,'clip_RGB_unchanged'=>$unchanged,
    'mask_inside'=>$inside,'mask_outside'=>$outside,
    'checks'=>['actual_return_true'=>$status===true,'clip_keeps_RGB'=>$unchanged,
        'mask_inside_expected'=>$inside===[255,255,255],'mask_outside_expected'=>$outside===[0,0,0],
        'declared_type_observed'=>$declared==='bool']]);
echo "embedded-clipimage-mask.png
metrics.json
";
printf("clipImage return=%s:%s reflected=%s RGB-changed=%d mask-inside=%d mask-outside=%d\n",
    get_debug_type($status),$status?'true':'false',$declared,(int)!$unchanged,$inside[0],$outside[0]);

clipImage без аргументов выбрал первый встроенный путь. RGB исходника не изменились. Сохранённая реальная маска белая внутри учебного окна, чёрная снаружи; это конкретный видимый результат вызова и извлечения маски. Архив содержит example.php и три текстовых fixture-файла: source.png.b64, profile.8bim.b64, clipping-fixture.php. Сохраните рядом и выполните php example.php с Imagick, поддержкой PNG/8BIM/SVG и правом записи. Helper восстановит точный собственный рисунок и настоящий бинарный профиль по SHA. Изучаемые clip-вызовы, извлечение маски и композиция остаются в программе; helper только готовит вход, сохраняет просмотры и диагностику.

Ожидаемый вывод
embedded-clipimage-mask.png
metrics.json
clipImage return=bool:true reflected=bool RGB-changed=0 mask-inside=255 mask-outside=0

Выбрать внешнюю область пути по номеру #1

clipPathImage("#1",false) выбрал противоположную область первого пути. Извлечённая маска чёрная внутри окна и белая снаружи. Размер изображения остаётся 400 × 320, никакой прямоугольный crop не выполнен.

Выбрать внешнюю область пути по номеру #1Пример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/clipping-fixture.php';
clippingRestore();
$base = clippingParts();
$profile = (string)file_get_contents(__DIR__.'/profile.8bim');
$image = clone $base;
$image->setImageProfile('8bim',$profile);
$before = clippingHash($image);
$status = $image->clipPathImage('#1',false);
$unchanged = $before===clippingHash($image);
$mask = $image->getImageMask(Imagick::PIXELMASK_WRITE);
if (!$mask instanceof Imagick) { throw new RuntimeException('Expected a real extracted mask'); }
$mask->setImageAlphaChannel(Imagick::ALPHACHANNEL_OFF);
clippingView($mask,'embedded-clippathimage-mask.png');
$declared = (string)(new ReflectionMethod(Imagick::class,'clipPathImage'))->getReturnType();
$inside = clippingPoint($mask,300,160); $outside = clippingPoint($mask,100,160);
clippingDiagnostic('embedded-clipping-clippathimage-minimal',['executed_api'=>'clipPathImage',
    'profile_sha256'=>hash('sha256',$profile), 'return_type'=>get_debug_type($status),
    'return_value'=>$status,'declared_return_type'=>$declared,'clip_RGB_unchanged'=>$unchanged,
    'mask_inside'=>$inside,'mask_outside'=>$outside,
    'checks'=>['actual_return_true'=>$status===true,'clip_keeps_RGB'=>$unchanged,
        'mask_inside_expected'=>$inside===[0,0,0],'mask_outside_expected'=>$outside===[255,255,255],
        'declared_type_observed'=>$declared==='bool']]);
echo "embedded-clippathimage-mask.png
metrics.json
";
printf("clipPathImage return=%s:%s reflected=%s RGB-changed=%d mask-inside=%d mask-outside=%d\n",
    get_debug_type($status),$status?'true':'false',$declared,(int)!$unchanged,$inside[0],$outside[0]);

clipPathImage("#1",false) выбрал противоположную область первого пути. Извлечённая маска чёрная внутри окна и белая снаружи. Размер изображения остаётся 400 × 320, никакой прямоугольный crop не выполнен. Архив содержит example.php и три текстовых fixture-файла: source.png.b64, profile.8bim.b64, clipping-fixture.php. Сохраните рядом и выполните php example.php с Imagick, поддержкой PNG/8BIM/SVG и правом записи. Helper восстановит точный собственный рисунок и настоящий бинарный профиль по SHA. Изучаемые clip-вызовы, извлечение маски и композиция остаются в программе; helper только готовит вход, сохраняет просмотры и диагностику.

Ожидаемый вывод
embedded-clippathimage-mask.png
metrics.json
clipPathImage return=bool:true reflected=bool RGB-changed=0 mask-inside=0 mask-outside=255

Проверить псевдоним и фактическое возвращаемое значение

clipImagePath исполнил тот же механизм именованного пути. В проверенной сборке наблюдается bool true, хотя Reflection объявляет void; маска совпадает с внутренней областью. Для нового кода предпочтительнее ясная запись clipPathImage с корректно описанным bool-параметром.

Проверить псевдоним и фактическое возвращаемое значениеПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/clipping-fixture.php';
clippingRestore();
$base = clippingParts();
$profile = (string)file_get_contents(__DIR__.'/profile.8bim');
$image = clone $base;
$image->setImageProfile('8bim',$profile);
$before = clippingHash($image);
$status = $image->clipImagePath('product-window',true);
$unchanged = $before===clippingHash($image);
$mask = $image->getImageMask(Imagick::PIXELMASK_WRITE);
if (!$mask instanceof Imagick) { throw new RuntimeException('Expected a real extracted mask'); }
$mask->setImageAlphaChannel(Imagick::ALPHACHANNEL_OFF);
clippingView($mask,'embedded-clipimagepath-mask.png');
$declared = (string)(new ReflectionMethod(Imagick::class,'clipImagePath'))->getReturnType();
$inside = clippingPoint($mask,300,160); $outside = clippingPoint($mask,100,160);
clippingDiagnostic('embedded-clipping-clipimagepath-minimal',['executed_api'=>'clipImagePath',
    'profile_sha256'=>hash('sha256',$profile), 'return_type'=>get_debug_type($status),
    'return_value'=>$status,'declared_return_type'=>$declared,'clip_RGB_unchanged'=>$unchanged,
    'mask_inside'=>$inside,'mask_outside'=>$outside,
    'checks'=>['actual_return_true'=>$status===true,'clip_keeps_RGB'=>$unchanged,
        'mask_inside_expected'=>$inside===[255,255,255],'mask_outside_expected'=>$outside===[0,0,0],
        'declared_type_observed'=>$declared==='void']]);
echo "embedded-clipimagepath-mask.png
metrics.json
";
printf("clipImagePath return=%s:%s reflected=%s RGB-changed=%d mask-inside=%d mask-outside=%d\n",
    get_debug_type($status),$status?'true':'false',$declared,(int)!$unchanged,$inside[0],$outside[0]);

clipImagePath исполнил тот же механизм именованного пути. В проверенной сборке наблюдается bool true, хотя Reflection объявляет void; маска совпадает с внутренней областью. Для нового кода предпочтительнее ясная запись clipPathImage с корректно описанным bool-параметром. Архив содержит example.php и три текстовых fixture-файла: source.png.b64, profile.8bim.b64, clipping-fixture.php. Сохраните рядом и выполните php example.php с Imagick, поддержкой PNG/8BIM/SVG и правом записи. Helper восстановит точный собственный рисунок и настоящий бинарный профиль по SHA. Изучаемые clip-вызовы, извлечение маски и композиция остаются в программе; helper только готовит вход, сохраняет просмотры и диагностику.

Ожидаемый вывод
embedded-clipimagepath-mask.png
metrics.json
clipImagePath return=bool:true reflected=void RGB-changed=0 mask-inside=255 mask-outside=0

clipImage выбирает первый путь без аргументов. clipPathImage("#1", false) выбирает тот же путь по номеру с противоположной областью. clipImagePath("product-window", true) — имя псевдонима, который в точной Imagick 3.8.1 направлен на реализацию clipPathImage. В этой сборке он фактически вернул bool true, хотя Reflection объявляет void. Для переносимого нового кода используйте основное имя clipPathImage и не стройте логику на расхождении метаданных псевдонима. Ни одна минимальная программа не выдаёт возврат true за готовую прозрачную картинку.

Собрать рабочее ограничение слоя и обработать отсутствие контура

Фактический negate после clip изменил весь рисунок; явная композиция ограничила эффект; после трёх ошибок исходник сохранён
Фактический negate после clip изменил весь рисунок; явная композиция ограничила эффект; после трёх ошибок исходник сохранён

В обработчике каталога нужно получить локальное изменение и сохранить оригинал даже при плохом входе. Программа ниже успешно исполняет все три имени на независимых копиях и сравнивает реальные маски. Затем специально показывает неудачное предположение: negate после clip здесь меняет обе контрольные точки. Рабочий вариант создаёт отдельный слой без clip, переносит в его альфа-канал интенсивность извлечённой маски и накладывает его на чистую копию.

Показать фактическую границу clip→negate и собрать рабочее ограничение

В первой панели настоящий negate после clip изменил весь рисунок в данной сборке. Во второй отдельный слой с CopyAlpha ограничил изменение выбранной областью. Третья показывает сохранённый исходник после ошибок: нет профиля, нет имени пути и пустое изображение. Ошибки пойманы как ImagickException.

Показать фактическую границу clip→negate и собрать рабочее ограничениеПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/clipping-fixture.php';
clippingRestore();
$base = clippingParts();
$profile = (string)file_get_contents(__DIR__.'/profile.8bim');
$before=clippingHash($base);
// Each method executes on its own carrier. The original stays free of masks.
$selections=[]; $methodResults=[]; $maskHashes=[];
foreach (['clipImage','clipPathImage','clipImagePath'] as $method) {
    $carrier=clone $base; $carrier->setImageProfile('8bim',$profile);
    $value=$method==='clipImage' ? $carrier->clipImage()
        : ($method==='clipPathImage' ? $carrier->clipPathImage('product-window',true)
            : $carrier->clipImagePath('product-window',true));
    $actualMask=$carrier->getImageMask(Imagick::PIXELMASK_WRITE);
    if (!$actualMask instanceof Imagick) { throw new RuntimeException('Expected a real mask'); }
    $actualMask->setImageAlphaChannel(Imagick::ALPHACHANNEL_OFF);
    $selections[$method]=$carrier; $maskHashes[$method]=clippingHash($actualMask);
    $methodResults[$method]=['return_type'=>get_debug_type($value),'return_value'=>$value,
        'clip_RGB_unchanged'=>clippingHash($carrier)===$before];
}
$selection=$selections['clipImage']; $ok=$methodResults['clipImage']['return_value'];
$clipKeepsRGB=clippingHash($selection)===$before;
$mask=$selection->getImageMask(Imagick::PIXELMASK_WRITE);
if (!$mask instanceof Imagick) { throw new RuntimeException('Expected a real extracted mask'); }
$mask->setImageAlphaChannel(Imagick::ALPHACHANNEL_OFF);
// The direct sequence does NOT localise negate in the exact tested runtime.
$direct=clone $selection;
$direct->negateImage(false);
clippingView($direct,'direct-negate.png');
// Use a separate, never-clipped layer and the extracted mask's intensity.
$layer=clone $base; $layer->negateImage(false);
$layer->setImageAlphaChannel(Imagick::ALPHACHANNEL_SET);
$layer->compositeImage($mask,Imagick::COMPOSITE_COPYALPHA,0,0);
$result=clone $base;
$result->compositeImage($layer,Imagick::COMPOSITE_OVER,0,0);
clippingView($result,'masked-inside.png');
$errors=[];
$missing=clone $base;
try { $missing->clipImage(); $errors['missing_profile']='not caught'; }
catch (ImagickException $error) { $errors['missing_profile']=get_class($error); }
$unknown=clone $base; $unknown->setImageProfile('8bim',$profile);
try { $unknown->clipPathImage('no-such-path',true); $errors['unknown_name']='not caught'; }
catch (ImagickException $error) { $errors['unknown_name']=get_class($error); }
try { (new Imagick())->clipImagePath('product-window',true); $errors['empty_image']='not caught'; }
catch (ImagickException $error) { $errors['empty_image']=get_class($error); }
clippingView($base,'after-errors-source.png');
clippingView($mask,'workflow-mask.png');
$sourceInside=clippingPoint($base,300,160); $sourceOutside=clippingPoint($base,100,160);
$directInside=clippingPoint($direct,300,160); $directOutside=clippingPoint($direct,100,160);
$resultInside=clippingPoint($result,300,160); $resultOutside=clippingPoint($result,100,160);
$unchanged=clippingHash($base)===$before&&clippingHash($missing)===$before&&clippingHash($unknown)===$before;
$rects=clippingCompare([$direct,$result,$base],'embedded-workflow.png');
clippingDiagnostic('embedded-clipping-workflow',['executed_api'=>'clipImage',
    'executed_apis'=>array_keys($methodResults),'method_results'=>$methodResults,
    'mask_hashes'=>$maskHashes,
    'profile_sha256'=>hash('sha256',$profile),'return_value'=>$ok,'clip_RGB_unchanged'=>$clipKeepsRGB,
    'source_inside'=>$sourceInside,'source_outside'=>$sourceOutside,
    'direct_inside'=>$directInside,'direct_outside'=>$directOutside,
    'result_inside'=>$resultInside,'result_outside'=>$resultOutside,
    'errors'=>$errors,'source_unchanged'=>$unchanged,'panel_geometry'=>$rects,
    'checks'=>['clip_true'=>$ok===true,'clip_keeps_RGB'=>$clipKeepsRGB,
        'three_real_methods_true'=>array_column($methodResults,'return_value')===[true,true,true],
        'all_three_keep_RGB'=>array_column($methodResults,'clip_RGB_unchanged')===[true,true,true],
        'all_three_masks_equal'=>count(array_unique($maskHashes))===1,
        'direct_negate_changes_both_points'=>$directInside!==$sourceInside&&$directOutside!==$sourceOutside,
        'separate_layer_localises_effect'=>$resultInside!==$sourceInside&&$resultOutside===$sourceOutside,
        'three_errors_caught'=>array_values($errors)===array_fill(0,3,'ImagickException'),
        'source_kept_after_errors'=>$unchanged]]);
echo "embedded-workflow.png\ndirect-negate.png\nmasked-inside.png\nafter-errors-source.png\nworkflow-mask.png\nmetrics.json\ndirect-negate-changes-both=1 separate-layer-local=1 caught-errors=3 source-unchanged=1\n";

В первой панели настоящий negate после clip изменил весь рисунок в данной сборке. Во второй отдельный слой с CopyAlpha ограничил изменение выбранной областью. Третья показывает сохранённый исходник после ошибок: нет профиля, нет имени пути и пустое изображение. Ошибки пойманы как ImagickException. Архив содержит example.php и три текстовых fixture-файла: source.png.b64, profile.8bim.b64, clipping-fixture.php. Сохраните рядом и выполните php example.php с Imagick, поддержкой PNG/8BIM/SVG и правом записи. Helper восстановит точный собственный рисунок и настоящий бинарный профиль по SHA. Изучаемые clip-вызовы, извлечение маски и композиция остаются в программе; helper только готовит вход, сохраняет просмотры и диагностику.

Ожидаемый вывод
embedded-workflow.png
direct-negate.png
masked-inside.png
after-errors-source.png
workflow-mask.png
metrics.json
direct-negate-changes-both=1 separate-layer-local=1 caught-errors=3 source-unchanged=1

Внутренняя контрольная точка (300, 160) находится в учебном окне, внешняя (100, 160) — за его пределами. У корректного внутреннего результата первая меняется, вторая остаётся прежней. Это проверка реальных пикселей результата, а не только успешного возврата true. Для других эффектов вместо negate можно подготовить иной отдельный слой, сохранив явные этапы маски и композиции; сначала проверьте их на собственном входе.

Неудачный входЧто произошло и как использовать в сервисе
Нет профиля с путёмclipImage вызвал ImagickException. Сообщите пользователю, что нужен сохранённый контур, либо предложите отдельное ручное выделение.
Профиль есть, имя no-such-path отсутствуетclipPathImage вызвал ImagickException. Не подменяйте его успехом или случайным первым путём.
Объект Imagick пустclipImagePath вызвал ImagickException. Сначала загрузите допустимое изображение и убедитесь, что оно существует.

try обозначает участок, где допускаем ожидаемую ошибку; catch (ImagickException $error) перехватывает именно эту ошибку библиотеки. Сохранённый исходник в третьей панели не получал масок и не изменился. В рабочем веб-приложении отдельно ограничьте размер входа, поддерживаемые форматы и право записи, а пользователю возвращайте понятное сообщение. Текст диагностики и PNG следует отдавать разными ответами: обработка исключений и композиция изображений помогают собрать такой обработчик.

Сохранить все официальные правила и различать метаданные, маску и прозрачность

Официальная запись без сокращения

Imagick::clipImage(): bool

clipImage выбирает первый сохранённый контур из профиля 8BIM текущего изображения. Аргументов нет: имя и номер пути не передаются. Официальный контракт возвращает true при успехе и объявляет ImagickException при ошибке. В точном Core этот вызов направляется к выбору пути #1 с inside=true. Если подходящего встроенного пути нет, метод не угадывает границу объекта по пикселям.

Официальная доступность: PECL imagick 2, PECL imagick 3. Все три имени присутствуют в точных исходниках Imagick 3.8.1 без исключающего их IM7-условия. Пустой объект, неизвестное имя или отсутствие требуемого встроенного контура могут закончиться ImagickException. Проверка расширения и чтение исходного файла выполняются до выбора пути.

Наблюдение в PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46: все три вызова успешно создают настоящую маску и сохраняют прежние RGB, но negateImage(false) после clip меняет и внутреннюю, и внешнюю контрольную точку. Поэтому локальный эффект ниже построен явно через извлечение маски, альфа-канал отдельного слоя и OVER; поведение другой операции или версии нужно проверять на её реальном результате.

Полный официальный XML: описание, параметры, результат, предупреждения и ошибки
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<refentry xmlns="http://docbook.org/ns/docbook" xml:id="imagick.clipimage">
 <refnamediv>
  <refname>Imagick::clipImage</refname>
  <refpurpose>Clips along the first path from the 8BIM profile</refpurpose>
 </refnamediv>

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <modifier>public</modifier> <type>bool</type><methodname>Imagick::clipImage</methodname>
   <void/>
  </methodsynopsis>
  <para>
   Clips along the first path from the 8BIM profile, if present.
  </para>
 </refsect1>

 <refsect1 role="parameters">
  &reftitle.parameters;
  &no.function.parameters;
 </refsect1>

 <refsect1 role="returnvalues">
  &reftitle.returnvalues;
  <para>
   &imagick.return.success;
  </para>
 </refsect1>

 <refsect1 role="errors">
  &reftitle.errors;
  <para>
   &imagick.imagick.throws;
  </para>
 </refsect1>

</refentry>

<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->

Официальная запись без сокращения

Imagick::clipPathImage(string $pathname, bool $inside): bool

clipPathImage выбирает встроенный контур по имени. Строка "#1" означает первый путь по порядку, а не файл с таким именем. Официальное описание допускает и другие номера с префиксом #. Параметр inside обязателен: по официальному замыслу true ограничивает последующую обработку внутренней областью, false — внешней. Ниже отдельно показано фактическое поведение следующей операции в точной проверенной сборке. Официальный результат при успехе — true; при ошибке объявлена ImagickException.

АргументЧто он означает
$pathname: stringИмя сохранённого пути, например product-window, или номер с #, например #1. Это не путь к файлу и не строка команд SVG.
$inside: boolОриентация выбираемой области: true — внутри контура, false — снаружи. Это не команда немедленно сделать фон прозрачным и не доказательство локальности любой следующей операции.

Официальная доступность: PECL imagick 2, PECL imagick 3. Все три имени присутствуют в точных исходниках Imagick 3.8.1 без исключающего их IM7-условия. Пустой объект, неизвестное имя или отсутствие требуемого встроенного контура могут закончиться ImagickException. Проверка расширения и чтение исходного файла выполняются до выбора пути.

Наблюдение в PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46: все три вызова успешно создают настоящую маску и сохраняют прежние RGB, но negateImage(false) после clip меняет и внутреннюю, и внешнюю контрольную точку. Поэтому локальный эффект ниже построен явно через извлечение маски, альфа-канал отдельного слоя и OVER; поведение другой операции или версии нужно проверять на её реальном результате.

Полный официальный XML: описание, параметры, результат, предупреждения и ошибки
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<refentry xmlns="http://docbook.org/ns/docbook" xml:id="imagick.clippathimage">
 <refnamediv>
  <refname>Imagick::clipPathImage</refname>
  <refpurpose>Clips along the named paths from the 8BIM profile</refpurpose>
 </refnamediv>

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <modifier>public</modifier> <type>bool</type><methodname>Imagick::clipPathImage</methodname>
   <methodparam><type>string</type><parameter>pathname</parameter></methodparam>
   <methodparam><type>bool</type><parameter>inside</parameter></methodparam>
  </methodsynopsis>
  <para>
   Clips along the named paths from the 8BIM profile, if
   present. Later operations take effect inside the path.
   It may be a number if preceded with #, to work on a
   numbered path, e.g., "#1" to use the first path.
  </para>
 </refsect1>

 <refsect1 role="parameters">
  &reftitle.parameters;
  <para>
   <variablelist>
    <varlistentry>
     <term><parameter>pathname</parameter></term>
     <listitem>
      <para>
       The name of the path
      </para>
     </listitem>
    </varlistentry>
    <varlistentry>
     <term><parameter>inside</parameter></term>
     <listitem>
      <para>
       If &true; later operations take effect inside clipping path.
       Otherwise later operations take effect outside clipping path.
      </para>
     </listitem>
    </varlistentry>
   </variablelist>
  </para>

 </refsect1>
 <refsect1 role="returnvalues">
  &reftitle.returnvalues;
  <para>
   &imagick.return.success;
  </para>
 </refsect1>

 <refsect1 role="errors">
  &reftitle.errors;
  <para>
   &imagick.imagick.throws;
  </para>
 </refsect1>

</refentry>

<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->

Официальная запись без сокращения

Imagick::clipImagePath(string $pathname, string $inside): void

clipImagePath описан на отдельной официальной странице как выбор именованного пути 8BIM. Страница также допускает номер с префиксом #, например "#1" для первого пути, и говорит о действии последующих операций внутри контура; наблюдаемая граница этого обещания показана отдельно ниже. В исходном XML есть предупреждение о недокументированной функции; пояснения параметров и возвращаемого значения пусты. Сигнатура этой страницы указывает string для inside и void для результата. Эти исходные части сохранены ниже, чтобы дополнение не скрывало состояние официальной базы.

Уточнение по точной Imagick 3.8.1

public function clipImagePath(string $pathname, bool $inside): void

Точный stub использует bool для inside. Метод зарегистрирован через ZEND_MALIAS и выполняет реализацию clipPathImage; отдельная информация о возвращаемом типе объявляет void. Это не основание передавать строку вместо логического значения или автоматически объявлять наблюдаемое возвращаемое значение. Его фактическое поведение в проверенной сборке фиксируется отдельно.

Фактическая проверка в этой сборке: успешный clipImagePath вернул bool true; Reflection показывает void. Это наблюдение точной сборки, а не исправление исходного XML и не обещание всех версий. Минимальная программа и общий сценарий действительно вызывают сам псевдоним.

Официальная доступность: PECL imagick 2 >= 2.3.0, PECL imagick 3. Все три имени присутствуют в точных исходниках Imagick 3.8.1 без исключающего их IM7-условия. Пустой объект, неизвестное имя или отсутствие требуемого встроенного контура могут закончиться ImagickException. Проверка расширения и чтение исходного файла выполняются до выбора пути.

Наблюдение в PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46: все три вызова успешно создают настоящую маску и сохраняют прежние RGB, но negateImage(false) после clip меняет и внутреннюю, и внешнюю контрольную точку. Поэтому локальный эффект ниже построен явно через извлечение маски, альфа-канал отдельного слоя и OVER; поведение другой операции или версии нужно проверять на её реальном результате.

Полный официальный XML: описание, параметры, результат, предупреждения и ошибки
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->

<refentry xml:id="imagick.clipimagepath" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
 <refnamediv>
  <refname>Imagick::clipImagePath</refname>
  <refpurpose>Clips along the named paths from the 8BIM profile, if present</refpurpose>
 </refnamediv>

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <modifier>public</modifier> <type>void</type><methodname>Imagick::clipImagePath</methodname>
   <methodparam><type>string</type><parameter>pathname</parameter></methodparam>
   <methodparam><type>string</type><parameter>inside</parameter></methodparam>
  </methodsynopsis>
  <para>
   Clips along the named paths from the 8BIM profile, if present. Later operations take effect inside the path.  Id may be a number if preceded with #, to work on a numbered path, e.g., "#1" to use the first path.
  </para>

  &warn.undocumented.func;

 </refsect1>

 <refsect1 role="parameters">
  &reftitle.parameters;
  <variablelist>
   <varlistentry>
    <term><parameter>pathname</parameter></term>
    <listitem>
     <para>
      
     </para>
    </listitem>
   </varlistentry>
   <varlistentry>
    <term><parameter>inside</parameter></term>
    <listitem>
     <para>
      
     </para>
    </listitem>
   </varlistentry>
  </variablelist>
 </refsect1>

 <refsect1 role="returnvalues">
  &reftitle.returnvalues;
  <para>
   
  </para>
 </refsect1>


</refentry>

<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->

В этих официальных XML нет исполняемых примеров, которые можно было бы скрыто заменить нашими; полный исходный текст сохранён выше, включая пустые описания и предупреждение псевдонима. Наши пять программ добавлены отдельно и используют собственный воспроизводимый профиль. Они не утверждают, что любой путь из любого PSD/TIFF/JPEG будет автоматически импортирован одинаково: проверяются конкретные входные метаданные, имя пути и поддержка кодеров в вашей сборке.

Для реального файла сначала читайте его в Imagick и выясняйте, есть ли нужный встроенный путь. Произвольный SVG-контур или ImagickDraw-clipPath не является доказательством наличия ресурса 8BIM. Если контур отсутствует, эти методы не восстановят его по форме товара. Экспорт JPEG также не становится прозрачным от наличия clip: прозрачность и сохранение метаданных — разные задачи.

Полный helper собственного входа и PNG-панелей

<?php
declare(strict_types=1);
function clippingRestore():void {
    foreach (['source.png'=>'f51e5780445e0f5130a7f23112b2a00618035afd5545b8e3042c7ccff09b617c', 'profile.8bim'=>'fa22988a6cc778fbfcb7e42445ad7470a32aad93247e8cf4ed0f431d49f7d102'] as $name=>$expected) {
        $raw=base64_decode((string)file_get_contents(__DIR__.'/'.$name.'.b64'),true);
        if ($raw===false || hash('sha256',$raw)!==$expected || file_put_contents(__DIR__.'/'.$name,$raw)===false) {
            throw new RuntimeException('Own source/profile missing or changed');
        }
    }
}
function clippingHash(Imagick $image):string {
    return hash('sha256',json_encode($image->exportImagePixels(0,0,$image->getImageWidth(),$image->getImageHeight(),'RGBA',Imagick::PIXEL_CHAR),JSON_THROW_ON_ERROR));
}
function clippingPoint(Imagick $image,int $x,int $y):array { return $image->exportImagePixels($x,$y,1,1,'RGB',Imagick::PIXEL_CHAR); }
function clippingParts():Imagick {
    $source=new Imagick(__DIR__.'/source.png');
    $base=new Imagick(); $base->newImage(400,320,'white');
    $base->compositeImage($source,Imagick::COMPOSITE_OVER,0,0);
    $base->setImagePage(0,0,0,0);
    return $base;
}
function clippingOpaque(Imagick $image):Imagick {
    $view=new Imagick(); $view->newImage($image->getImageWidth(),$image->getImageHeight(),'white');
    $view->compositeImage($image,Imagick::COMPOSITE_OVER,0,0); $view->setImageType(Imagick::IMGTYPE_TRUECOLOR);
    return $view;
}
function clippingView(Imagick $image,string $filename):void {
    $view=clippingOpaque($image); $view->setImageFormat('png'); $view->setImageDepth(8);
    $view->setOption('png:bit-depth','8'); $view->setOption('png:color-type','2');
    $view->setOption('png:exclude-chunk','all');
    if(!$view->writeImage(__DIR__.'/'.$filename)){throw new RuntimeException('PNG preview write failed');}
}
function clippingCompare(array $images,string $filename):array {
    $board=new Imagick(); $board->newImage(400*count($images),320,'white'); $rects=[];
    foreach($images as $i=>$image){
        if($image->getImageWidth()!==400||$image->getImageHeight()!==320){throw new RuntimeException('Expected a complete400x320 panel');}
        $board->compositeImage($image,Imagick::COMPOSITE_OVER,400*$i,0);
        $rects[]=['x'=>400*$i,'y'=>0,'width'=>400,'height'=>320];
    }
    clippingView($board,$filename); return $rects;
}
function clippingDiagnostic(string $id,array $data):void {
    $doc=['schema'=>'aen-embedded-clipping-program-diagnostics-v1','example_id'=>$id]+$data;
    if(file_put_contents(__DIR__.'/metrics.json',json_encode($doc,JSON_PRETTY_PRINT|JSON_PRESERVE_ZERO_FRACTION|JSON_THROW_ON_ERROR)."\n")===false){throw new RuntimeException('Diagnostic write failed');}
}

Helper восстанавливает исходный PNG и отдельный бинарный профиль, готовит белую непрозрачную основу и сохраняет PNG/JSON. Изучаемые clip-вызовы, getImageMask, CopyAlpha и OVER остаются в программах. Профиль не является изображением и не имеет собственного размера PNG. Упражнение: замените true на false и получите внешнюю область тем же явным слоем; ожидайте инверсию маски, прежний размер 400 × 320 и неизменённый исходник. Следующий шаг: области изображения, контуры рисования и независимые копии.

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

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

Imagick::clipImageСохранённый 8BIM-контур, настоящая маска и явное ограничение отдельного слоя

PECL imagick 2, PECL imagick 3 · imagick

Imagick::clipImage(): bool

Официальная запись без сокращения

Imagick::clipImage(): bool

clipImage выбирает первый сохранённый контур из профиля 8BIM текущего изображения. Аргументов нет: имя и номер пути не передаются. Официальный контракт возвращает true при успехе и объявляет ImagickException при ошибке. В точном Core этот вызов направляется к выбору пути #1 с inside=true. Если подходящего встроенного пути нет, метод не угадывает границу объекта по пикселям.

Официальная доступность: PECL imagick 2, PECL imagick 3. Все три имени присутствуют в точных исходниках Imagick 3.8.1 без исключающего их IM7-условия. Пустой объект, неизвестное имя или отсутствие требуемого встроенного контура могут закончиться ImagickException. Проверка расширения и чтение исходного файла выполняются до выбора пути.

Наблюдение в PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46: все три вызова успешно создают настоящую маску и сохраняют прежние RGB, но negateImage(false) после clip меняет и внутреннюю, и внешнюю контрольную точку. Поэтому локальный эффект ниже построен явно через извлечение маски, альфа-канал отдельного слоя и OVER; поведение другой операции или версии нужно проверять на её реальном результате.

Полный официальный XML: описание, параметры, результат, предупреждения и ошибки
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<refentry xmlns="http://docbook.org/ns/docbook" xml:id="imagick.clipimage">
 <refnamediv>
  <refname>Imagick::clipImage</refname>
  <refpurpose>Clips along the first path from the 8BIM profile</refpurpose>
 </refnamediv>

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <modifier>public</modifier> <type>bool</type><methodname>Imagick::clipImage</methodname>
   <void/>
  </methodsynopsis>
  <para>
   Clips along the first path from the 8BIM profile, if present.
  </para>
 </refsect1>

 <refsect1 role="parameters">
  &reftitle.parameters;
  &no.function.parameters;
 </refsect1>

 <refsect1 role="returnvalues">
  &reftitle.returnvalues;
  <para>
   &imagick.return.success;
  </para>
 </refsect1>

 <refsect1 role="errors">
  &reftitle.errors;
  <para>
   &imagick.imagick.throws;
  </para>
 </refsect1>

</refentry>

<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->

Увидеть настоящую маску первого встроенного пути

clipImage без аргументов выбрал первый встроенный путь. RGB исходника не изменились. Сохранённая реальная маска белая внутри учебного окна, чёрная снаружи; это конкретный видимый результат вызова и извлечения маски.

Увидеть настоящую маску первого встроенного путиПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/clipping-fixture.php';
clippingRestore();
$base = clippingParts();
$profile = (string)file_get_contents(__DIR__.'/profile.8bim');
$image = clone $base;
$image->setImageProfile('8bim',$profile);
$before = clippingHash($image);
$status = $image->clipImage();
$unchanged = $before===clippingHash($image);
$mask = $image->getImageMask(Imagick::PIXELMASK_WRITE);
if (!$mask instanceof Imagick) { throw new RuntimeException('Expected a real extracted mask'); }
$mask->setImageAlphaChannel(Imagick::ALPHACHANNEL_OFF);
clippingView($mask,'embedded-clipimage-mask.png');
$declared = (string)(new ReflectionMethod(Imagick::class,'clipImage'))->getReturnType();
$inside = clippingPoint($mask,300,160); $outside = clippingPoint($mask,100,160);
clippingDiagnostic('embedded-clipping-clipimage-minimal',['executed_api'=>'clipImage',
    'profile_sha256'=>hash('sha256',$profile), 'return_type'=>get_debug_type($status),
    'return_value'=>$status,'declared_return_type'=>$declared,'clip_RGB_unchanged'=>$unchanged,
    'mask_inside'=>$inside,'mask_outside'=>$outside,
    'checks'=>['actual_return_true'=>$status===true,'clip_keeps_RGB'=>$unchanged,
        'mask_inside_expected'=>$inside===[255,255,255],'mask_outside_expected'=>$outside===[0,0,0],
        'declared_type_observed'=>$declared==='bool']]);
echo "embedded-clipimage-mask.png
metrics.json
";
printf("clipImage return=%s:%s reflected=%s RGB-changed=%d mask-inside=%d mask-outside=%d\n",
    get_debug_type($status),$status?'true':'false',$declared,(int)!$unchanged,$inside[0],$outside[0]);

clipImage без аргументов выбрал первый встроенный путь. RGB исходника не изменились. Сохранённая реальная маска белая внутри учебного окна, чёрная снаружи; это конкретный видимый результат вызова и извлечения маски. Архив содержит example.php и три текстовых fixture-файла: source.png.b64, profile.8bim.b64, clipping-fixture.php. Сохраните рядом и выполните php example.php с Imagick, поддержкой PNG/8BIM/SVG и правом записи. Helper восстановит точный собственный рисунок и настоящий бинарный профиль по SHA. Изучаемые clip-вызовы, извлечение маски и композиция остаются в программе; helper только готовит вход, сохраняет просмотры и диагностику.

Ожидаемый вывод
embedded-clipimage-mask.png
metrics.json
clipImage return=bool:true reflected=bool RGB-changed=0 mask-inside=255 mask-outside=0

Показать фактическую границу clip→negate и собрать рабочее ограничение

В первой панели настоящий negate после clip изменил весь рисунок в данной сборке. Во второй отдельный слой с CopyAlpha ограничил изменение выбранной областью. Третья показывает сохранённый исходник после ошибок: нет профиля, нет имени пути и пустое изображение. Ошибки пойманы как ImagickException.

Показать фактическую границу clip→negate и собрать рабочее ограничениеПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/clipping-fixture.php';
clippingRestore();
$base = clippingParts();
$profile = (string)file_get_contents(__DIR__.'/profile.8bim');
$before=clippingHash($base);
// Each method executes on its own carrier. The original stays free of masks.
$selections=[]; $methodResults=[]; $maskHashes=[];
foreach (['clipImage','clipPathImage','clipImagePath'] as $method) {
    $carrier=clone $base; $carrier->setImageProfile('8bim',$profile);
    $value=$method==='clipImage' ? $carrier->clipImage()
        : ($method==='clipPathImage' ? $carrier->clipPathImage('product-window',true)
            : $carrier->clipImagePath('product-window',true));
    $actualMask=$carrier->getImageMask(Imagick::PIXELMASK_WRITE);
    if (!$actualMask instanceof Imagick) { throw new RuntimeException('Expected a real mask'); }
    $actualMask->setImageAlphaChannel(Imagick::ALPHACHANNEL_OFF);
    $selections[$method]=$carrier; $maskHashes[$method]=clippingHash($actualMask);
    $methodResults[$method]=['return_type'=>get_debug_type($value),'return_value'=>$value,
        'clip_RGB_unchanged'=>clippingHash($carrier)===$before];
}
$selection=$selections['clipImage']; $ok=$methodResults['clipImage']['return_value'];
$clipKeepsRGB=clippingHash($selection)===$before;
$mask=$selection->getImageMask(Imagick::PIXELMASK_WRITE);
if (!$mask instanceof Imagick) { throw new RuntimeException('Expected a real extracted mask'); }
$mask->setImageAlphaChannel(Imagick::ALPHACHANNEL_OFF);
// The direct sequence does NOT localise negate in the exact tested runtime.
$direct=clone $selection;
$direct->negateImage(false);
clippingView($direct,'direct-negate.png');
// Use a separate, never-clipped layer and the extracted mask's intensity.
$layer=clone $base; $layer->negateImage(false);
$layer->setImageAlphaChannel(Imagick::ALPHACHANNEL_SET);
$layer->compositeImage($mask,Imagick::COMPOSITE_COPYALPHA,0,0);
$result=clone $base;
$result->compositeImage($layer,Imagick::COMPOSITE_OVER,0,0);
clippingView($result,'masked-inside.png');
$errors=[];
$missing=clone $base;
try { $missing->clipImage(); $errors['missing_profile']='not caught'; }
catch (ImagickException $error) { $errors['missing_profile']=get_class($error); }
$unknown=clone $base; $unknown->setImageProfile('8bim',$profile);
try { $unknown->clipPathImage('no-such-path',true); $errors['unknown_name']='not caught'; }
catch (ImagickException $error) { $errors['unknown_name']=get_class($error); }
try { (new Imagick())->clipImagePath('product-window',true); $errors['empty_image']='not caught'; }
catch (ImagickException $error) { $errors['empty_image']=get_class($error); }
clippingView($base,'after-errors-source.png');
clippingView($mask,'workflow-mask.png');
$sourceInside=clippingPoint($base,300,160); $sourceOutside=clippingPoint($base,100,160);
$directInside=clippingPoint($direct,300,160); $directOutside=clippingPoint($direct,100,160);
$resultInside=clippingPoint($result,300,160); $resultOutside=clippingPoint($result,100,160);
$unchanged=clippingHash($base)===$before&&clippingHash($missing)===$before&&clippingHash($unknown)===$before;
$rects=clippingCompare([$direct,$result,$base],'embedded-workflow.png');
clippingDiagnostic('embedded-clipping-workflow',['executed_api'=>'clipImage',
    'executed_apis'=>array_keys($methodResults),'method_results'=>$methodResults,
    'mask_hashes'=>$maskHashes,
    'profile_sha256'=>hash('sha256',$profile),'return_value'=>$ok,'clip_RGB_unchanged'=>$clipKeepsRGB,
    'source_inside'=>$sourceInside,'source_outside'=>$sourceOutside,
    'direct_inside'=>$directInside,'direct_outside'=>$directOutside,
    'result_inside'=>$resultInside,'result_outside'=>$resultOutside,
    'errors'=>$errors,'source_unchanged'=>$unchanged,'panel_geometry'=>$rects,
    'checks'=>['clip_true'=>$ok===true,'clip_keeps_RGB'=>$clipKeepsRGB,
        'three_real_methods_true'=>array_column($methodResults,'return_value')===[true,true,true],
        'all_three_keep_RGB'=>array_column($methodResults,'clip_RGB_unchanged')===[true,true,true],
        'all_three_masks_equal'=>count(array_unique($maskHashes))===1,
        'direct_negate_changes_both_points'=>$directInside!==$sourceInside&&$directOutside!==$sourceOutside,
        'separate_layer_localises_effect'=>$resultInside!==$sourceInside&&$resultOutside===$sourceOutside,
        'three_errors_caught'=>array_values($errors)===array_fill(0,3,'ImagickException'),
        'source_kept_after_errors'=>$unchanged]]);
echo "embedded-workflow.png\ndirect-negate.png\nmasked-inside.png\nafter-errors-source.png\nworkflow-mask.png\nmetrics.json\ndirect-negate-changes-both=1 separate-layer-local=1 caught-errors=3 source-unchanged=1\n";

В первой панели настоящий negate после clip изменил весь рисунок в данной сборке. Во второй отдельный слой с CopyAlpha ограничил изменение выбранной областью. Третья показывает сохранённый исходник после ошибок: нет профиля, нет имени пути и пустое изображение. Ошибки пойманы как ImagickException. Архив содержит example.php и три текстовых fixture-файла: source.png.b64, profile.8bim.b64, clipping-fixture.php. Сохраните рядом и выполните php example.php с Imagick, поддержкой PNG/8BIM/SVG и правом записи. Helper восстановит точный собственный рисунок и настоящий бинарный профиль по SHA. Изучаемые clip-вызовы, извлечение маски и композиция остаются в программе; helper только готовит вход, сохраняет просмотры и диагностику.

Ожидаемый вывод
embedded-workflow.png
direct-negate.png
masked-inside.png
after-errors-source.png
workflow-mask.png
metrics.json
direct-negate-changes-both=1 separate-layer-local=1 caught-errors=3 source-unchanged=1
Imagick::clipPathImageСохранённый 8BIM-контур, настоящая маска и явное ограничение отдельного слоя

PECL imagick 2, PECL imagick 3 · imagick

Imagick::clipPathImage(string $pathname, bool $inside): bool

Официальная запись без сокращения

Imagick::clipPathImage(string $pathname, bool $inside): bool

clipPathImage выбирает встроенный контур по имени. Строка "#1" означает первый путь по порядку, а не файл с таким именем. Официальное описание допускает и другие номера с префиксом #. Параметр inside обязателен: по официальному замыслу true ограничивает последующую обработку внутренней областью, false — внешней. Ниже отдельно показано фактическое поведение следующей операции в точной проверенной сборке. Официальный результат при успехе — true; при ошибке объявлена ImagickException.

АргументЧто он означает
$pathname: stringИмя сохранённого пути, например product-window, или номер с #, например #1. Это не путь к файлу и не строка команд SVG.
$inside: boolОриентация выбираемой области: true — внутри контура, false — снаружи. Это не команда немедленно сделать фон прозрачным и не доказательство локальности любой следующей операции.

Официальная доступность: PECL imagick 2, PECL imagick 3. Все три имени присутствуют в точных исходниках Imagick 3.8.1 без исключающего их IM7-условия. Пустой объект, неизвестное имя или отсутствие требуемого встроенного контура могут закончиться ImagickException. Проверка расширения и чтение исходного файла выполняются до выбора пути.

Наблюдение в PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46: все три вызова успешно создают настоящую маску и сохраняют прежние RGB, но negateImage(false) после clip меняет и внутреннюю, и внешнюю контрольную точку. Поэтому локальный эффект ниже построен явно через извлечение маски, альфа-канал отдельного слоя и OVER; поведение другой операции или версии нужно проверять на её реальном результате.

Полный официальный XML: описание, параметры, результат, предупреждения и ошибки
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<refentry xmlns="http://docbook.org/ns/docbook" xml:id="imagick.clippathimage">
 <refnamediv>
  <refname>Imagick::clipPathImage</refname>
  <refpurpose>Clips along the named paths from the 8BIM profile</refpurpose>
 </refnamediv>

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <modifier>public</modifier> <type>bool</type><methodname>Imagick::clipPathImage</methodname>
   <methodparam><type>string</type><parameter>pathname</parameter></methodparam>
   <methodparam><type>bool</type><parameter>inside</parameter></methodparam>
  </methodsynopsis>
  <para>
   Clips along the named paths from the 8BIM profile, if
   present. Later operations take effect inside the path.
   It may be a number if preceded with #, to work on a
   numbered path, e.g., "#1" to use the first path.
  </para>
 </refsect1>

 <refsect1 role="parameters">
  &reftitle.parameters;
  <para>
   <variablelist>
    <varlistentry>
     <term><parameter>pathname</parameter></term>
     <listitem>
      <para>
       The name of the path
      </para>
     </listitem>
    </varlistentry>
    <varlistentry>
     <term><parameter>inside</parameter></term>
     <listitem>
      <para>
       If &true; later operations take effect inside clipping path.
       Otherwise later operations take effect outside clipping path.
      </para>
     </listitem>
    </varlistentry>
   </variablelist>
  </para>

 </refsect1>
 <refsect1 role="returnvalues">
  &reftitle.returnvalues;
  <para>
   &imagick.return.success;
  </para>
 </refsect1>

 <refsect1 role="errors">
  &reftitle.errors;
  <para>
   &imagick.imagick.throws;
  </para>
 </refsect1>

</refentry>

<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->

Выбрать внешнюю область пути по номеру #1

clipPathImage("#1",false) выбрал противоположную область первого пути. Извлечённая маска чёрная внутри окна и белая снаружи. Размер изображения остаётся 400 × 320, никакой прямоугольный crop не выполнен.

Выбрать внешнюю область пути по номеру #1Пример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/clipping-fixture.php';
clippingRestore();
$base = clippingParts();
$profile = (string)file_get_contents(__DIR__.'/profile.8bim');
$image = clone $base;
$image->setImageProfile('8bim',$profile);
$before = clippingHash($image);
$status = $image->clipPathImage('#1',false);
$unchanged = $before===clippingHash($image);
$mask = $image->getImageMask(Imagick::PIXELMASK_WRITE);
if (!$mask instanceof Imagick) { throw new RuntimeException('Expected a real extracted mask'); }
$mask->setImageAlphaChannel(Imagick::ALPHACHANNEL_OFF);
clippingView($mask,'embedded-clippathimage-mask.png');
$declared = (string)(new ReflectionMethod(Imagick::class,'clipPathImage'))->getReturnType();
$inside = clippingPoint($mask,300,160); $outside = clippingPoint($mask,100,160);
clippingDiagnostic('embedded-clipping-clippathimage-minimal',['executed_api'=>'clipPathImage',
    'profile_sha256'=>hash('sha256',$profile), 'return_type'=>get_debug_type($status),
    'return_value'=>$status,'declared_return_type'=>$declared,'clip_RGB_unchanged'=>$unchanged,
    'mask_inside'=>$inside,'mask_outside'=>$outside,
    'checks'=>['actual_return_true'=>$status===true,'clip_keeps_RGB'=>$unchanged,
        'mask_inside_expected'=>$inside===[0,0,0],'mask_outside_expected'=>$outside===[255,255,255],
        'declared_type_observed'=>$declared==='bool']]);
echo "embedded-clippathimage-mask.png
metrics.json
";
printf("clipPathImage return=%s:%s reflected=%s RGB-changed=%d mask-inside=%d mask-outside=%d\n",
    get_debug_type($status),$status?'true':'false',$declared,(int)!$unchanged,$inside[0],$outside[0]);

clipPathImage("#1",false) выбрал противоположную область первого пути. Извлечённая маска чёрная внутри окна и белая снаружи. Размер изображения остаётся 400 × 320, никакой прямоугольный crop не выполнен. Архив содержит example.php и три текстовых fixture-файла: source.png.b64, profile.8bim.b64, clipping-fixture.php. Сохраните рядом и выполните php example.php с Imagick, поддержкой PNG/8BIM/SVG и правом записи. Helper восстановит точный собственный рисунок и настоящий бинарный профиль по SHA. Изучаемые clip-вызовы, извлечение маски и композиция остаются в программе; helper только готовит вход, сохраняет просмотры и диагностику.

Ожидаемый вывод
embedded-clippathimage-mask.png
metrics.json
clipPathImage return=bool:true reflected=bool RGB-changed=0 mask-inside=0 mask-outside=255

Показать фактическую границу clip→negate и собрать рабочее ограничение

В первой панели настоящий negate после clip изменил весь рисунок в данной сборке. Во второй отдельный слой с CopyAlpha ограничил изменение выбранной областью. Третья показывает сохранённый исходник после ошибок: нет профиля, нет имени пути и пустое изображение. Ошибки пойманы как ImagickException.

Показать фактическую границу clip→negate и собрать рабочее ограничениеПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/clipping-fixture.php';
clippingRestore();
$base = clippingParts();
$profile = (string)file_get_contents(__DIR__.'/profile.8bim');
$before=clippingHash($base);
// Each method executes on its own carrier. The original stays free of masks.
$selections=[]; $methodResults=[]; $maskHashes=[];
foreach (['clipImage','clipPathImage','clipImagePath'] as $method) {
    $carrier=clone $base; $carrier->setImageProfile('8bim',$profile);
    $value=$method==='clipImage' ? $carrier->clipImage()
        : ($method==='clipPathImage' ? $carrier->clipPathImage('product-window',true)
            : $carrier->clipImagePath('product-window',true));
    $actualMask=$carrier->getImageMask(Imagick::PIXELMASK_WRITE);
    if (!$actualMask instanceof Imagick) { throw new RuntimeException('Expected a real mask'); }
    $actualMask->setImageAlphaChannel(Imagick::ALPHACHANNEL_OFF);
    $selections[$method]=$carrier; $maskHashes[$method]=clippingHash($actualMask);
    $methodResults[$method]=['return_type'=>get_debug_type($value),'return_value'=>$value,
        'clip_RGB_unchanged'=>clippingHash($carrier)===$before];
}
$selection=$selections['clipImage']; $ok=$methodResults['clipImage']['return_value'];
$clipKeepsRGB=clippingHash($selection)===$before;
$mask=$selection->getImageMask(Imagick::PIXELMASK_WRITE);
if (!$mask instanceof Imagick) { throw new RuntimeException('Expected a real extracted mask'); }
$mask->setImageAlphaChannel(Imagick::ALPHACHANNEL_OFF);
// The direct sequence does NOT localise negate in the exact tested runtime.
$direct=clone $selection;
$direct->negateImage(false);
clippingView($direct,'direct-negate.png');
// Use a separate, never-clipped layer and the extracted mask's intensity.
$layer=clone $base; $layer->negateImage(false);
$layer->setImageAlphaChannel(Imagick::ALPHACHANNEL_SET);
$layer->compositeImage($mask,Imagick::COMPOSITE_COPYALPHA,0,0);
$result=clone $base;
$result->compositeImage($layer,Imagick::COMPOSITE_OVER,0,0);
clippingView($result,'masked-inside.png');
$errors=[];
$missing=clone $base;
try { $missing->clipImage(); $errors['missing_profile']='not caught'; }
catch (ImagickException $error) { $errors['missing_profile']=get_class($error); }
$unknown=clone $base; $unknown->setImageProfile('8bim',$profile);
try { $unknown->clipPathImage('no-such-path',true); $errors['unknown_name']='not caught'; }
catch (ImagickException $error) { $errors['unknown_name']=get_class($error); }
try { (new Imagick())->clipImagePath('product-window',true); $errors['empty_image']='not caught'; }
catch (ImagickException $error) { $errors['empty_image']=get_class($error); }
clippingView($base,'after-errors-source.png');
clippingView($mask,'workflow-mask.png');
$sourceInside=clippingPoint($base,300,160); $sourceOutside=clippingPoint($base,100,160);
$directInside=clippingPoint($direct,300,160); $directOutside=clippingPoint($direct,100,160);
$resultInside=clippingPoint($result,300,160); $resultOutside=clippingPoint($result,100,160);
$unchanged=clippingHash($base)===$before&&clippingHash($missing)===$before&&clippingHash($unknown)===$before;
$rects=clippingCompare([$direct,$result,$base],'embedded-workflow.png');
clippingDiagnostic('embedded-clipping-workflow',['executed_api'=>'clipImage',
    'executed_apis'=>array_keys($methodResults),'method_results'=>$methodResults,
    'mask_hashes'=>$maskHashes,
    'profile_sha256'=>hash('sha256',$profile),'return_value'=>$ok,'clip_RGB_unchanged'=>$clipKeepsRGB,
    'source_inside'=>$sourceInside,'source_outside'=>$sourceOutside,
    'direct_inside'=>$directInside,'direct_outside'=>$directOutside,
    'result_inside'=>$resultInside,'result_outside'=>$resultOutside,
    'errors'=>$errors,'source_unchanged'=>$unchanged,'panel_geometry'=>$rects,
    'checks'=>['clip_true'=>$ok===true,'clip_keeps_RGB'=>$clipKeepsRGB,
        'three_real_methods_true'=>array_column($methodResults,'return_value')===[true,true,true],
        'all_three_keep_RGB'=>array_column($methodResults,'clip_RGB_unchanged')===[true,true,true],
        'all_three_masks_equal'=>count(array_unique($maskHashes))===1,
        'direct_negate_changes_both_points'=>$directInside!==$sourceInside&&$directOutside!==$sourceOutside,
        'separate_layer_localises_effect'=>$resultInside!==$sourceInside&&$resultOutside===$sourceOutside,
        'three_errors_caught'=>array_values($errors)===array_fill(0,3,'ImagickException'),
        'source_kept_after_errors'=>$unchanged]]);
echo "embedded-workflow.png\ndirect-negate.png\nmasked-inside.png\nafter-errors-source.png\nworkflow-mask.png\nmetrics.json\ndirect-negate-changes-both=1 separate-layer-local=1 caught-errors=3 source-unchanged=1\n";

В первой панели настоящий negate после clip изменил весь рисунок в данной сборке. Во второй отдельный слой с CopyAlpha ограничил изменение выбранной областью. Третья показывает сохранённый исходник после ошибок: нет профиля, нет имени пути и пустое изображение. Ошибки пойманы как ImagickException. Архив содержит example.php и три текстовых fixture-файла: source.png.b64, profile.8bim.b64, clipping-fixture.php. Сохраните рядом и выполните php example.php с Imagick, поддержкой PNG/8BIM/SVG и правом записи. Helper восстановит точный собственный рисунок и настоящий бинарный профиль по SHA. Изучаемые clip-вызовы, извлечение маски и композиция остаются в программе; helper только готовит вход, сохраняет просмотры и диагностику.

Ожидаемый вывод
embedded-workflow.png
direct-negate.png
masked-inside.png
after-errors-source.png
workflow-mask.png
metrics.json
direct-negate-changes-both=1 separate-layer-local=1 caught-errors=3 source-unchanged=1
Imagick::clipImagePathСохранённый 8BIM-контур, настоящая маска и явное ограничение отдельного слоя

PECL imagick 2 >= 2.3.0, PECL imagick 3 · imagick

Imagick::clipImagePath(string $pathname, string $inside): void

Официальная запись без сокращения

Imagick::clipImagePath(string $pathname, string $inside): void

clipImagePath описан на отдельной официальной странице как выбор именованного пути 8BIM. Страница также допускает номер с префиксом #, например "#1" для первого пути, и говорит о действии последующих операций внутри контура; наблюдаемая граница этого обещания показана отдельно ниже. В исходном XML есть предупреждение о недокументированной функции; пояснения параметров и возвращаемого значения пусты. Сигнатура этой страницы указывает string для inside и void для результата. Эти исходные части сохранены ниже, чтобы дополнение не скрывало состояние официальной базы.

Уточнение по точной Imagick 3.8.1

public function clipImagePath(string $pathname, bool $inside): void

Точный stub использует bool для inside. Метод зарегистрирован через ZEND_MALIAS и выполняет реализацию clipPathImage; отдельная информация о возвращаемом типе объявляет void. Это не основание передавать строку вместо логического значения или автоматически объявлять наблюдаемое возвращаемое значение. Его фактическое поведение в проверенной сборке фиксируется отдельно.

Фактическая проверка в этой сборке: успешный clipImagePath вернул bool true; Reflection показывает void. Это наблюдение точной сборки, а не исправление исходного XML и не обещание всех версий. Минимальная программа и общий сценарий действительно вызывают сам псевдоним.

Официальная доступность: PECL imagick 2 >= 2.3.0, PECL imagick 3. Все три имени присутствуют в точных исходниках Imagick 3.8.1 без исключающего их IM7-условия. Пустой объект, неизвестное имя или отсутствие требуемого встроенного контура могут закончиться ImagickException. Проверка расширения и чтение исходного файла выполняются до выбора пути.

Наблюдение в PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46: все три вызова успешно создают настоящую маску и сохраняют прежние RGB, но negateImage(false) после clip меняет и внутреннюю, и внешнюю контрольную точку. Поэтому локальный эффект ниже построен явно через извлечение маски, альфа-канал отдельного слоя и OVER; поведение другой операции или версии нужно проверять на её реальном результате.

Полный официальный XML: описание, параметры, результат, предупреждения и ошибки
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->

<refentry xml:id="imagick.clipimagepath" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
 <refnamediv>
  <refname>Imagick::clipImagePath</refname>
  <refpurpose>Clips along the named paths from the 8BIM profile, if present</refpurpose>
 </refnamediv>

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <modifier>public</modifier> <type>void</type><methodname>Imagick::clipImagePath</methodname>
   <methodparam><type>string</type><parameter>pathname</parameter></methodparam>
   <methodparam><type>string</type><parameter>inside</parameter></methodparam>
  </methodsynopsis>
  <para>
   Clips along the named paths from the 8BIM profile, if present. Later operations take effect inside the path.  Id may be a number if preceded with #, to work on a numbered path, e.g., "#1" to use the first path.
  </para>

  &warn.undocumented.func;

 </refsect1>

 <refsect1 role="parameters">
  &reftitle.parameters;
  <variablelist>
   <varlistentry>
    <term><parameter>pathname</parameter></term>
    <listitem>
     <para>
      
     </para>
    </listitem>
   </varlistentry>
   <varlistentry>
    <term><parameter>inside</parameter></term>
    <listitem>
     <para>
      
     </para>
    </listitem>
   </varlistentry>
  </variablelist>
 </refsect1>

 <refsect1 role="returnvalues">
  &reftitle.returnvalues;
  <para>
   
  </para>
 </refsect1>


</refentry>

<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->

Проверить псевдоним и фактическое возвращаемое значение

clipImagePath исполнил тот же механизм именованного пути. В проверенной сборке наблюдается bool true, хотя Reflection объявляет void; маска совпадает с внутренней областью. Для нового кода предпочтительнее ясная запись clipPathImage с корректно описанным bool-параметром.

Проверить псевдоним и фактическое возвращаемое значениеПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/clipping-fixture.php';
clippingRestore();
$base = clippingParts();
$profile = (string)file_get_contents(__DIR__.'/profile.8bim');
$image = clone $base;
$image->setImageProfile('8bim',$profile);
$before = clippingHash($image);
$status = $image->clipImagePath('product-window',true);
$unchanged = $before===clippingHash($image);
$mask = $image->getImageMask(Imagick::PIXELMASK_WRITE);
if (!$mask instanceof Imagick) { throw new RuntimeException('Expected a real extracted mask'); }
$mask->setImageAlphaChannel(Imagick::ALPHACHANNEL_OFF);
clippingView($mask,'embedded-clipimagepath-mask.png');
$declared = (string)(new ReflectionMethod(Imagick::class,'clipImagePath'))->getReturnType();
$inside = clippingPoint($mask,300,160); $outside = clippingPoint($mask,100,160);
clippingDiagnostic('embedded-clipping-clipimagepath-minimal',['executed_api'=>'clipImagePath',
    'profile_sha256'=>hash('sha256',$profile), 'return_type'=>get_debug_type($status),
    'return_value'=>$status,'declared_return_type'=>$declared,'clip_RGB_unchanged'=>$unchanged,
    'mask_inside'=>$inside,'mask_outside'=>$outside,
    'checks'=>['actual_return_true'=>$status===true,'clip_keeps_RGB'=>$unchanged,
        'mask_inside_expected'=>$inside===[255,255,255],'mask_outside_expected'=>$outside===[0,0,0],
        'declared_type_observed'=>$declared==='void']]);
echo "embedded-clipimagepath-mask.png
metrics.json
";
printf("clipImagePath return=%s:%s reflected=%s RGB-changed=%d mask-inside=%d mask-outside=%d\n",
    get_debug_type($status),$status?'true':'false',$declared,(int)!$unchanged,$inside[0],$outside[0]);

clipImagePath исполнил тот же механизм именованного пути. В проверенной сборке наблюдается bool true, хотя Reflection объявляет void; маска совпадает с внутренней областью. Для нового кода предпочтительнее ясная запись clipPathImage с корректно описанным bool-параметром. Архив содержит example.php и три текстовых fixture-файла: source.png.b64, profile.8bim.b64, clipping-fixture.php. Сохраните рядом и выполните php example.php с Imagick, поддержкой PNG/8BIM/SVG и правом записи. Helper восстановит точный собственный рисунок и настоящий бинарный профиль по SHA. Изучаемые clip-вызовы, извлечение маски и композиция остаются в программе; helper только готовит вход, сохраняет просмотры и диагностику.

Ожидаемый вывод
embedded-clipimagepath-mask.png
metrics.json
clipImagePath return=bool:true reflected=void RGB-changed=0 mask-inside=255 mask-outside=0

Показать фактическую границу clip→negate и собрать рабочее ограничение

В первой панели настоящий negate после clip изменил весь рисунок в данной сборке. Во второй отдельный слой с CopyAlpha ограничил изменение выбранной областью. Третья показывает сохранённый исходник после ошибок: нет профиля, нет имени пути и пустое изображение. Ошибки пойманы как ImagickException.

Показать фактическую границу clip→negate и собрать рабочее ограничениеПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/clipping-fixture.php';
clippingRestore();
$base = clippingParts();
$profile = (string)file_get_contents(__DIR__.'/profile.8bim');
$before=clippingHash($base);
// Each method executes on its own carrier. The original stays free of masks.
$selections=[]; $methodResults=[]; $maskHashes=[];
foreach (['clipImage','clipPathImage','clipImagePath'] as $method) {
    $carrier=clone $base; $carrier->setImageProfile('8bim',$profile);
    $value=$method==='clipImage' ? $carrier->clipImage()
        : ($method==='clipPathImage' ? $carrier->clipPathImage('product-window',true)
            : $carrier->clipImagePath('product-window',true));
    $actualMask=$carrier->getImageMask(Imagick::PIXELMASK_WRITE);
    if (!$actualMask instanceof Imagick) { throw new RuntimeException('Expected a real mask'); }
    $actualMask->setImageAlphaChannel(Imagick::ALPHACHANNEL_OFF);
    $selections[$method]=$carrier; $maskHashes[$method]=clippingHash($actualMask);
    $methodResults[$method]=['return_type'=>get_debug_type($value),'return_value'=>$value,
        'clip_RGB_unchanged'=>clippingHash($carrier)===$before];
}
$selection=$selections['clipImage']; $ok=$methodResults['clipImage']['return_value'];
$clipKeepsRGB=clippingHash($selection)===$before;
$mask=$selection->getImageMask(Imagick::PIXELMASK_WRITE);
if (!$mask instanceof Imagick) { throw new RuntimeException('Expected a real extracted mask'); }
$mask->setImageAlphaChannel(Imagick::ALPHACHANNEL_OFF);
// The direct sequence does NOT localise negate in the exact tested runtime.
$direct=clone $selection;
$direct->negateImage(false);
clippingView($direct,'direct-negate.png');
// Use a separate, never-clipped layer and the extracted mask's intensity.
$layer=clone $base; $layer->negateImage(false);
$layer->setImageAlphaChannel(Imagick::ALPHACHANNEL_SET);
$layer->compositeImage($mask,Imagick::COMPOSITE_COPYALPHA,0,0);
$result=clone $base;
$result->compositeImage($layer,Imagick::COMPOSITE_OVER,0,0);
clippingView($result,'masked-inside.png');
$errors=[];
$missing=clone $base;
try { $missing->clipImage(); $errors['missing_profile']='not caught'; }
catch (ImagickException $error) { $errors['missing_profile']=get_class($error); }
$unknown=clone $base; $unknown->setImageProfile('8bim',$profile);
try { $unknown->clipPathImage('no-such-path',true); $errors['unknown_name']='not caught'; }
catch (ImagickException $error) { $errors['unknown_name']=get_class($error); }
try { (new Imagick())->clipImagePath('product-window',true); $errors['empty_image']='not caught'; }
catch (ImagickException $error) { $errors['empty_image']=get_class($error); }
clippingView($base,'after-errors-source.png');
clippingView($mask,'workflow-mask.png');
$sourceInside=clippingPoint($base,300,160); $sourceOutside=clippingPoint($base,100,160);
$directInside=clippingPoint($direct,300,160); $directOutside=clippingPoint($direct,100,160);
$resultInside=clippingPoint($result,300,160); $resultOutside=clippingPoint($result,100,160);
$unchanged=clippingHash($base)===$before&&clippingHash($missing)===$before&&clippingHash($unknown)===$before;
$rects=clippingCompare([$direct,$result,$base],'embedded-workflow.png');
clippingDiagnostic('embedded-clipping-workflow',['executed_api'=>'clipImage',
    'executed_apis'=>array_keys($methodResults),'method_results'=>$methodResults,
    'mask_hashes'=>$maskHashes,
    'profile_sha256'=>hash('sha256',$profile),'return_value'=>$ok,'clip_RGB_unchanged'=>$clipKeepsRGB,
    'source_inside'=>$sourceInside,'source_outside'=>$sourceOutside,
    'direct_inside'=>$directInside,'direct_outside'=>$directOutside,
    'result_inside'=>$resultInside,'result_outside'=>$resultOutside,
    'errors'=>$errors,'source_unchanged'=>$unchanged,'panel_geometry'=>$rects,
    'checks'=>['clip_true'=>$ok===true,'clip_keeps_RGB'=>$clipKeepsRGB,
        'three_real_methods_true'=>array_column($methodResults,'return_value')===[true,true,true],
        'all_three_keep_RGB'=>array_column($methodResults,'clip_RGB_unchanged')===[true,true,true],
        'all_three_masks_equal'=>count(array_unique($maskHashes))===1,
        'direct_negate_changes_both_points'=>$directInside!==$sourceInside&&$directOutside!==$sourceOutside,
        'separate_layer_localises_effect'=>$resultInside!==$sourceInside&&$resultOutside===$sourceOutside,
        'three_errors_caught'=>array_values($errors)===array_fill(0,3,'ImagickException'),
        'source_kept_after_errors'=>$unchanged]]);
echo "embedded-workflow.png\ndirect-negate.png\nmasked-inside.png\nafter-errors-source.png\nworkflow-mask.png\nmetrics.json\ndirect-negate-changes-both=1 separate-layer-local=1 caught-errors=3 source-unchanged=1\n";

В первой панели настоящий negate после clip изменил весь рисунок в данной сборке. Во второй отдельный слой с CopyAlpha ограничил изменение выбранной областью. Третья показывает сохранённый исходник после ошибок: нет профиля, нет имени пути и пустое изображение. Ошибки пойманы как ImagickException. Архив содержит example.php и три текстовых fixture-файла: source.png.b64, profile.8bim.b64, clipping-fixture.php. Сохраните рядом и выполните php example.php с Imagick, поддержкой PNG/8BIM/SVG и правом записи. Helper восстановит точный собственный рисунок и настоящий бинарный профиль по SHA. Изучаемые clip-вызовы, извлечение маски и композиция остаются в программе; helper только готовит вход, сохраняет просмотры и диагностику.

Ожидаемый вывод
embedded-workflow.png
direct-negate.png
masked-inside.png
after-errors-source.png
workflow-mask.png
metrics.json
direct-negate-changes-both=1 separate-layer-local=1 caught-errors=3 source-unchanged=1
Источники и обсуждения

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

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

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

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