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

Дерево руководства
Глава 378 · После объектов, файлов, каналов и исключений
Imagick: упорядоченный дизеринг, точки и переход с orderedPosterizeImage
Получить видимый двухцветный или многоуровневый рисунок, разобрать карту порогов и заменить исторический метод в IM7.
Увидеть, как точки передают полутона
Устройство или оформление допускает мало оттенков, но хочется сохранить узнаваемую форму товара и переходы света. Обычная отсечка «темнее порога — чёрный, светлее — белый» теряет детали. Здесь можно разложить полутона на регулярные точки и сразу сравнить два двухцветных рисунка с четырёхуровневым вариантом. Для цветного декоративного постера используем тот же принцип отдельно по RGB-каналам.
Важная граница версии: orderedPosterizeImage — историческое имя, deprecated с Imagick 3.4.4; в исходниках Imagick 3.8.1 оно включается только при сборке с ImageMagick 6 (ниже 7). В проверяемой сборке с ImageMagick 7 этого метода нет. Современные рабочие программы ниже вызывают orderedDitherImage, добавленный в Imagick 3.6.0; доступность зависит от сборки и проверяется method_exists. Исторический контракт сохраняем полностью, но не выдаём выполнение альтернативы за выполнение отсутствующего метода.

Дизеринг создаёт ощущение промежуточного оттенка из нескольких допустимых значений пикселей. В упорядоченном варианте по изображению повторяется небольшая таблица порогов: положение пикселя в сетке влияет на выбор ближайшего уровня. На расстоянии чёрные и белые точки выглядят как серый участок. Это не случайный шум и не добавление настоящих оттенков в двухцветный файл. $image хранит объект; -> вызывает метод, строка в скобках задаёт карту и уровни, ; завершает инструкцию. Переменные, объекты и строки — связанные объяснения.
$image->orderedDitherImage('o4x4,2');o4x4 называет повторяемую карту 4 × 4; запятая отделяет число уровней 2. Два уровня на сером изображении дают чёрный и белый, а не два новых серых оттенка между ними. Один вызов меняет текущее изображение; true — статус успешного действия. До обработки собственный PNG 400 × 320 положен на белый фон, затем сделана серая копия. В нижние 64 строки явно добавлен градиент black-white, созданный PHP, чтобы на нём были видны промежуточные узоры; это подготовка учебного входа, а не часть исходной иллюстрации.
Сравнить сетку, точки и число уровней серого
Первые два PNG содержат только чёрные и белые пиксели, но разные регулярные узоры. Третий допускает четыре уровня серого. Все три созданы современным orderedDitherImage; устаревший orderedPosterizeImage в проверенной сборке ImageMagick 7 отсутствует.
<?php
declare(strict_types=1);
require __DIR__.'/ordered-fixture.php';
orderedRestore();
$legacyAvailable = method_exists(Imagick::class, 'orderedPosterizeImage');
$modernAvailable = method_exists(Imagick::class, 'orderedDitherImage');
if (!$modernAvailable) { throw new RuntimeException('This example needs orderedDitherImage'); }
[$base, $gray] = orderedParts();
orderedView($base, 'source-preview8.png');
orderedView($gray, 'gray-input.png');
$specs = ['bayer2'=>'o4x4,2', 'halftone2'=>'h8x8a,2', 'bayer4'=>'o4x4,4'];
$views = []; $observed = [];
foreach ($specs as $name=>$spec) {
$image = clone $gray;
$previousMask = $image->setImageChannelMask((Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE));
try {
$ok = $image->orderedDitherImage($spec);
} finally {
$usedMask = $image->setImageChannelMask($previousMask);
}
$restored = $image->setImageChannelMask($previousMask) === $previousMask;
orderedView($image, $name.'.png');
$views[] = new Imagick(__DIR__.'/'.$name.'.png');
$observed[] = ['name'=>$name, 'spec'=>$spec, 'return_value'=>$ok,
'used_mask'=>$usedMask, 'mask_restored'=>$restored, 'stats'=>orderedStats($image)];
}
$rects = orderedCompare($views, 'ordered-first.png');
orderedDiagnostic('ordered-dither-first', ['legacy_available'=>$legacyAvailable,
'modern_available'=>$modernAvailable, 'executed_api'=>'orderedDitherImage',
'cases'=>$observed, 'panel_geometry'=>$rects,
'checks'=>['modern_api_available'=>$modernAvailable,
'all_calls_true'=>array_column($observed,'return_value')===[true,true,true],
'masks_restored'=>array_column($observed,'mask_restored')===[true,true,true],
'three_distinct_results'=>count(array_unique(array_map('orderedHash',$views)))===3]]);
echo "ordered-first.png\nsource-preview8.png\ngray-input.png\nbayer2.png\nhalftone2.png\nbayer4.png\nmetrics.json\napi=orderedDitherImage variants=3 masks-restored=1\n";
Первые два PNG содержат только чёрные и белые пиксели, но разные регулярные узоры. Третий допускает четыре уровня серого. Все три созданы современным orderedDitherImage; устаревший orderedPosterizeImage в проверенной сборке ImageMagick 7 отсутствует. Сохраните example.php и два дополнительных текстовых файла архива рядом. Запустите php example.php с расширением Imagick и методом orderedDitherImage, поддержкой PNG и правом записи. Это современная альтернатива для ImageMagick 7, а не выполненный вызов исторического метода. Helper проверяет собственный PNG по SHA, готовит основу и серую копию с явно добавленной полосой градиента; target-вызовы находятся в программе.
Ожидаемый вывод
ordered-first.png
source-preview8.png
gray-input.png
bayer2.png
halftone2.png
bayer4.png
metrics.json
api=orderedDitherImage variants=3 masks-restored=1
Разобрать повторяемую карту и число уровней

Модель на схеме специально использует маленькие условные числа и увеличенные клетки. Она объясняет выбор, но не выдаётся за точную матрицу o4x4 или физические размеры пикселей. Реальная таблица o4x4 в thresholds.xml установленной версии имеет divisor 17 и следующие значения:
| x=0 | x=1 | x=2 | x=3 |
|---|---|---|---|
| 1 | 9 | 3 | 11 |
| 13 | 5 | 15 | 7 |
| 4 | 12 | 2 | 10 |
| 16 | 8 | 14 | 6 |
Для каждого пикселя Core берёт элемент [x % 4, y % 4], поэтому сетка повторяется. Порог карты влияет на выбор уровня между соседними допустимыми значениями. Разные карты создают рассеянные точки или сгруппированные полутоновые узоры. Число 17 относится к матрице и её промежуточным узорам; параметр ,4 задаёт четыре уровня канала. Это разные числа с разными обязанностями.
| Запись | Значение |
|---|---|
| o4x4 | Карта 4 × 4, без числа уровней — бинарный вариант по умолчанию |
| o4x4,2 | Та же карта, явно два уровня на обрабатываемый канал |
| o4x4,4 | Четыре уровня каждого обрабатываемого канала |
| o8x8,6 | Другая сетка и шесть уровней; доступность карты зависит от конфигурации |
| o4x4,4,4,2 | Разные количества уровней последовательно по каналам; сначала разберитесь с цветовой моделью и выбранной маской |
В этих примерах используются только проверенные значения 2 и 4. Не обещаем, что произвольная строка, отрицательное или дробное число будет одинаково отвергаться всеми версиями: разбор параметров имеет особенности. Для пользовательского выбора приложение предлагает готовые варианты. Количество реальных цветов измеряется в готовом растре, а не угадывается по имени карты.
Запустить простой современный вызов с восстановлением каналов
Важная граница версии: orderedPosterizeImage — историческое имя, deprecated с Imagick 3.4.4; в исходниках Imagick 3.8.1 оно включается только при сборке с ImageMagick 6 (ниже 7). В проверяемой сборке с ImageMagick 7 этого метода нет. Современные рабочие программы ниже вызывают orderedDitherImage, добавленный в Imagick 3.6.0; доступность зависит от сборки и проверяется method_exists. Исторический контракт сохраняем полностью, но не выдаём выполнение альтернативы за выполнение отсутствующего метода.

Начальный полезный результат — один PNG с чёрными и белыми пикселями. Перед вызовом проверяем method_exists, выбираем RGB-каналы и сохраняем прежнюю маску. try содержит обработку, finally возвращает настройку даже при исключении. Второй setImageChannelMask с прежним значением нужен только для наблюдения возвращённой старой маски в этом учебном примере; он не запускает дизеринг ещё раз.
Получить чёрно-белый PNG одним вызовом современной операции
На подготовленном сером рисунке o4x4,2 создаёт два цвета: чёрный и белый. Возвращено true, а прежняя маска каналов восстановлена. Проверена доступность современного имени; старое имя не вызывалось.
<?php
declare(strict_types=1);
require __DIR__.'/ordered-fixture.php';
orderedRestore();
$legacyAvailable = method_exists(Imagick::class, 'orderedPosterizeImage');
$modernAvailable = method_exists(Imagick::class, 'orderedDitherImage');
if (!$modernAvailable) { throw new RuntimeException('This example needs orderedDitherImage'); }
[, $image] = orderedParts();
$previousMask = $image->setImageChannelMask((Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE));
try {
$ok = $image->orderedDitherImage('o4x4,2');
} finally {
$usedMask = $image->setImageChannelMask($previousMask);
}
$restored = $image->setImageChannelMask($previousMask) === $previousMask;
orderedView($image, 'ordered-minimal.png');
$stats = orderedStats($image);
orderedDiagnostic('ordered-dither-minimal', ['legacy_available'=>$legacyAvailable,
'modern_available'=>$modernAvailable, 'executed_api'=>'orderedDitherImage',
'spec'=>'o4x4,2', 'return_value'=>$ok, 'used_mask'=>$usedMask,
'mask_restored'=>$restored, 'stats'=>$stats,
'checks'=>['modern_api_available'=>$modernAvailable,'success'=>$ok===true,
'mask_restored'=>$restored,'black_and_white'=>$stats['RGB8_values']===[[0,255],[0,255],[0,255]]&&$stats['color_count']===2]]);
printf("legacy=%d modern=%d set=%s colors=%d\n", (int)$legacyAvailable,
(int)$modernAvailable, $ok?'true':'false', $stats['color_count']);
echo "ordered-minimal.png\nmetrics.json\n";
На подготовленном сером рисунке o4x4,2 создаёт два цвета: чёрный и белый. Возвращено true, а прежняя маска каналов восстановлена. Проверена доступность современного имени; старое имя не вызывалось. Сохраните example.php и два дополнительных текстовых файла архива рядом. Запустите php example.php с расширением Imagick и методом orderedDitherImage, поддержкой PNG и правом записи. Это современная альтернатива для ImageMagick 7, а не выполненный вызов исторического метода. Helper проверяет собственный PNG по SHA, готовит основу и серую копию с явно добавленной полосой градиента; target-вызовы находятся в программе.
Ожидаемый вывод
legacy=0 modern=1 set=true colors=2
ordered-minimal.png
metrics.json
setImageChannelMask возвращает предыдущую маску каналов, а не bool. В этих серых входах red/green/blue при чтении RGB дают одинаковые компоненты. Каналы alpha не требуются, потому что вход заранее положен на белый фон и сделан непрозрачным. При сохранении здесь используется PNG RGB8: два цвета рисунка не означают, что файл автоматически стал индексированным PNG1 или готовым форматом конкретного e-ink устройства.
Собрать обработчик стилей и честно обработать отказ

Сервис подготовки изображений получает понятное название стиля. print применяется к серому изображению для двухцветного декоративного макета, poster — к цветному исходнику и ограничивает значения каждого RGB-канала. Пользователь не отправляет произвольные названия карт и уровни. Функция делает clone, чтобы результат не заменял оригинал, и возвращает новый объект: исходник можно сохранить для другого оформления.
Подготовить два варианта оформления и перехватить ошибки карты
Сервис получает стиль print или poster из разрешённой таблицы. Первый даёт чёрно-белый рисунок; второй ограничивает каждый RGB-канал четырьмя значениями. Неизвестный стиль отклонён до обработки. Намеренно неверная карта вызвала ImagickException: последняя панель показывает сохранённый исходник после ошибки, а не удачный печатный результат.
<?php
declare(strict_types=1);
require __DIR__.'/ordered-fixture.php';
orderedRestore();
$legacyAvailable = method_exists(Imagick::class, 'orderedPosterizeImage');
$modernAvailable = method_exists(Imagick::class, 'orderedDitherImage');
if (!$modernAvailable) { throw new RuntimeException('This example needs orderedDitherImage'); }
function makeArtwork(Imagick $input, string $style): Imagick {
$allowed = ['print'=>'o4x4,2', 'poster'=>'o4x4,4'];
if (!array_key_exists($style, $allowed)) {
throw new InvalidArgumentException('Unknown artwork style');
}
$result = clone $input;
$previousMask = $result->setImageChannelMask((Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE));
try {
$result->orderedDitherImage($allowed[$style]);
} finally {
$result->setImageChannelMask($previousMask);
}
return $result;
}
[$base, $gray] = orderedParts();
$bw = makeArtwork($gray, 'print');
$poster = makeArtwork($base, 'poster');
orderedView($bw, 'product-bw.png');
orderedView($poster, 'product-four-level.png');
$invalidPolicy = 'not caught';
try { makeArtwork($base, 'unknown'); }
catch (InvalidArgumentException $error) { $invalidPolicy = get_class($error); }
// Deliberately bypass the UI allowlist once to demonstrate a real library error.
$rejected = clone $base; $before = orderedHash($rejected); $invalidMap = 'not caught';
$previousMask = $rejected->setImageChannelMask((Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE));
try { $rejected->orderedDitherImage('aen-no-such-threshold-map'); }
catch (ImagickException $error) { $invalidMap = get_class($error); }
finally { $rejected->setImageChannelMask($previousMask); }
$unchanged = $before === orderedHash($rejected);
orderedView($rejected, 'rejected-map-source.png');
$views = [new Imagick(__DIR__.'/product-bw.png'),
new Imagick(__DIR__.'/product-four-level.png'), new Imagick(__DIR__.'/rejected-map-source.png')];
$rects = orderedCompare($views, 'ordered-workflow.png');
$bwStats = orderedStats($bw); $posterStats = orderedStats($poster);
$fourLevels = true;
foreach ($posterStats['RGB8_values'] as $values) {
if (count(array_diff($values,[0,85,170,255]))>0) { $fourLevels=false; }
}
orderedDiagnostic('ordered-dither-workflow', ['legacy_available'=>$legacyAvailable,
'modern_available'=>$modernAvailable, 'executed_api'=>'orderedDitherImage',
'styles'=>['print'=>'o4x4,2','poster'=>'o4x4,4'], 'bw_stats'=>$bwStats,
'poster_stats'=>$posterStats, 'invalid_policy'=>$invalidPolicy,
'invalid_map'=>$invalidMap, 'rejected_source_unchanged'=>$unchanged,
'panel_geometry'=>$rects, 'checks'=>['modern_api_available'=>$modernAvailable,
'print_has_two_colors'=>$bwStats['color_count']===2,
'poster_has_only_four_levels_per_channel'=>$fourLevels&&$posterStats['color_count']<=64,
'invalid_policy_caught'=>$invalidPolicy==='InvalidArgumentException',
'invalid_map_caught'=>$invalidMap==='ImagickException', 'source_retained_after_failure'=>$unchanged]]);
echo "ordered-workflow.png\nproduct-bw.png\nproduct-four-level.png\nrejected-map-source.png\nmetrics.json\ninvalid-policy=InvalidArgumentException invalid-map=ImagickException source-unchanged=1\n";
Сервис получает стиль print или poster из разрешённой таблицы. Первый даёт чёрно-белый рисунок; второй ограничивает каждый RGB-канал четырьмя значениями. Неизвестный стиль отклонён до обработки. Намеренно неверная карта вызвала ImagickException: последняя панель показывает сохранённый исходник после ошибки, а не удачный печатный результат. Сохраните example.php и два дополнительных текстовых файла архива рядом. Запустите php example.php с расширением Imagick и методом orderedDitherImage, поддержкой PNG и правом записи. Это современная альтернатива для ImageMagick 7, а не выполненный вызов исторического метода. Helper проверяет собственный PNG по SHA, готовит основу и серую копию с явно добавленной полосой градиента; target-вызовы находятся в программе.
Ожидаемый вывод
ordered-workflow.png
product-bw.png
product-four-level.png
rejected-map-source.png
metrics.json
invalid-policy=InvalidArgumentException invalid-map=ImagickException source-unchanged=1
Четыре значения в каждом RGB-канале допускают до 4 × 4 × 4 = 64 сочетаний, а не четыре цвета всего. Часть сочетаний может не встретиться. Последняя панель намеренно показывает исходник после ImagickException от неизвестной карты. Приложение должно сообщить, что обработка не выполнена; сохранённый оригинал — возможность повторить работу, а не скрывать ошибку под видом готового результата. Проверка стиля и ошибка самой библиотеки показаны отдельно.
Сохранить исторический контракт и выбрать подходящий метод
Важная граница версии: orderedPosterizeImage — историческое имя, deprecated с Imagick 3.4.4; в исходниках Imagick 3.8.1 оно включается только при сборке с ImageMagick 6 (ниже 7). В проверяемой сборке с ImageMagick 7 этого метода нет. Современные рабочие программы ниже вызывают orderedDitherImage, добавленный в Imagick 3.6.0; доступность зависит от сборки и проверяется method_exists. Исторический контракт сохраняем полностью, но не выдаём выполнение альтернативы за выполнение отсутствующего метода.
Полный исторический контракт
Imagick::orderedPosterizeImage(string $threshold_map, int $channel = Imagick::CHANNEL_DEFAULT): bool| Часть вызова | Что означает |
|---|---|
| $threshold_map: string, обязательный | Имя предопределённой карты; расширенная запись может включать число уровней, например o4x4,4. |
| $channel: int, необязательный | В старом методе default CHANNEL_DEFAULT. Допустимые каналы зависят от цветовой модели; несколько каналов объединяются побитовым |. |
| bool true | Успешная обработка текущего изображения на поддерживаемой старой сборке. |
| Ошибка | Официально объявлен ImagickException; отсутствие самого метода на IM7 — другая ситуация, поэтому сначала проверяйте доступность. |
Официальная доступность: PECL imagick 2 >= 2.2.2, PECL imagick 3; старому вызову требовалась ImageMagick 6.3.1 или новее, при этом точный текущий binding исключает его из IM7. Описание обещает упорядоченный дизеринг по предопределённым картам и нескольким уровням интенсивности, которые могут различаться по каналам. Ни число уровней, ни карта не означают размер общей индексированной палитры.
Историческая запись: только для чтения старого кода
// Historical IM6 fragment: this method is absent from the tested IM7 build.
$image->orderedPosterizeImage('o4x4,4', Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE);Это контекстный фрагмент, а не результат нашего запуска. У современного orderedDitherImage один строковый аргумент: второго $channel у него нет. На IM7 нужные каналы выбираются через setImageChannelMask; сохранённую прежнюю маску возвращают в finally, чтобы настройка не повлияла на последующую обработку. Для пустого Imagick современный wrapper выбрасывает ImagickException; неверная карта тоже может привести к исключению. Ошибка пути к исходнику возникает ещё до обработки.
Официальный пример: что нужно дополнить для запуска
В оригинале функция принимает $imagePath и $orderedPosterizeType, читает realpath, вызывает исторический метод, выставляет PNG, отправляет Content-Type и бинарные байты. Варианты o4x4,3,3 и o8x8,6,6 закомментированы, активен h8x8a. Переменная $imagePath в самом фрагменте не определена: читатель должен задать существующий путь. Этот HTTP-пример требует совместимой IM6-сборки и ответа без текстового вывода перед PNG; на нашей IM7 его имя не существует. Полный исходный пример сохранён в XML ниже, а рабочий современный файл доступен отдельно.
Полный официальный XML и пример без сокращений
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<refentry xmlns="http://docbook.org/ns/docbook" xml:id="imagick.orderedposterizeimage">
<refnamediv>
<refname>Imagick::orderedPosterizeImage</refname>
<refpurpose>Performs an ordered dither</refpurpose>
</refnamediv>
<refsynopsisdiv>
&imagick.deprecated.function-3-4-4;
</refsynopsisdiv>
<refsect1 role="description">
&reftitle.description;
<methodsynopsis>
<modifier>public</modifier> <type>bool</type><methodname>Imagick::orderedPosterizeImage</methodname>
<methodparam><type>string</type><parameter>threshold_map</parameter></methodparam>
<methodparam choice="opt"><type>int</type><parameter>channel</parameter><initializer>Imagick::CHANNEL_DEFAULT</initializer></methodparam>
</methodsynopsis>
<para>
Performs an ordered dither based on a number of pre-defined dithering threshold maps,
but over multiple intensity levels, which can be different for different channels,
according to the input arguments. &imagick.method.available.0x631;
</para>
</refsect1>
<refsect1 role="parameters">
&reftitle.parameters;
<para>
<variablelist>
<varlistentry>
<term><parameter>threshold_map</parameter></term>
<listitem>
<para>
A string containing the name of the threshold dither map to use
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><parameter>channel</parameter></term>
<listitem>
<para>
Provide any channel constant that is valid for your channel mode. To
apply to more than one channel, combine channeltype constants using
bitwise operators. Refer to this
list of <link linkend="imagick.constants.channel">channel constants</link>.
</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>
<refsect1 role="examples">
&reftitle.examples;
<para>
<example>
<title> <function>Imagick::orderedPosterizeImage</function></title>
<programlisting role="php">
<![CDATA[
<?php
function orderedPosterizeImage($imagePath, $orderedPosterizeType) {
$imagick = new \Imagick(realpath($imagePath));
$imagick->orderedPosterizeImage($orderedPosterizeType);
$imagick->setImageFormat('png');
header("Content-Type: image/png");
echo $imagick->getImageBlob();
}
//orderedPosterizeImage($imagePath, 'o4x4,3,3');
//orderedPosterizeImage($imagePath, 'o8x8,6,6');
orderedPosterizeImage($imagePath, 'h8x8a');
?>
]]>
</programlisting>
</example>
</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
-->
| Задача | Инструмент и граница |
|---|---|
| Регулярный узор с выбранными уровнями | orderedDitherImage в текущей IM7-сборке; историческое orderedPosterizeImage — для чтения IM6-кода |
| Просто отсечь светлое и тёмное | thresholdImage: один порог, без регулярной имитации полутонов |
| Уложиться в заданную общую палитру | quantizeImage или remapImage с подходящим способом дизеринга; это другая постановка |
| Распространять ошибку округления на соседей | Floyd–Steinberg и другие методы диффузии ошибки; не та же повторяемая карта |
| Повторить печатный растр с заданными LPI/углом | Нужны отдельно заданные масштаб, разрешение и подходящая карта; h8x8a не обещает эквивалент конкретных настроек Photoshop |
| Уменьшить файл | Измерить результат выбранного кодека; меньше уровней не гарантируют меньший PNG или JPEG |
Сначала приведите изображение к конечному размеру, затем создавайте узор. Масштабирование с интерполяцией после обработки может добавить серые пиксели и уничтожить строгую двухцветность. Периодическая сетка может давать заметные узоры или муар; смотрите результат при целевом размере. Для документов отдельно оценивайте читаемость мелкого текста: художественный растр товара не является сертифицированной подготовкой факса или бумаги.
Упражнение: добавьте в таблицу приложения стиль с h8x8a,2 и сравните его с print на том же сером входе. Предлагаемое решение — ещё одно разрешённое имя, clone исходника, маска каналов, вызов и finally. Оба результата останутся двухцветными, но рисунок точек изменится. Оригинал храните отдельно. Операция проходит по пикселям; стоимость зависит от размеров и каналов, а не только длины строки карты.
Полный helper воспроизводимого входа
<?php
declare(strict_types=1);
function orderedRestore():void {$raw=base64_decode((string)file_get_contents(__DIR__.'/source.png.b64'),true);if($raw===false||hash('sha256',$raw)!=='f51e5780445e0f5130a7f23112b2a00618035afd5545b8e3042c7ccff09b617c'||file_put_contents(__DIR__.'/source.png',$raw)===false){throw new RuntimeException('Own source missing or changed');}}
function orderedHash(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 orderedPixels(Imagick $image):array {return ['width'=>$image->getImageWidth(),'height'=>$image->getImageHeight(),'RGBA8_hash'=>orderedHash($image)];}
function orderedOpaque(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 orderedView(Imagick $image,string $filename):void {$view=orderedOpaque($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 orderedParts():array {
$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);
$gray=clone $base;$gray->transformImageColorspace(Imagick::COLORSPACE_GRAY);
$gradient=new Imagick();$gradient->newPseudoImage(64,400,'gradient:black-white');$gradient->rotateImage('white',90);$gradient->setImagePage(0,0,0,0);$gray->compositeImage($gradient,Imagick::COMPOSITE_OVER,0,256);
return [$base,$gray];
}
function orderedStats(Imagick $image):array {
$values=$image->exportImagePixels(0,0,$image->getImageWidth(),$image->getImageHeight(),'RGB',Imagick::PIXEL_CHAR);$sets=[[],[],[]];$colors=[];
for($i=0,$n=count($values);$i<$n;$i+=3){$sets[0][$values[$i]]=true;$sets[1][$values[$i+1]]=true;$sets[2][$values[$i+2]]=true;$colors[$values[$i].','.$values[$i+1].','.$values[$i+2]]=true;}
$channels=[];foreach($sets as $set){$keys=array_keys($set);sort($keys,SORT_NUMERIC);$channels[]=$keys;}
return ['RGB8_values'=>$channels,'color_count'=>count($colors),'pixels'=>orderedPixels($image)];
}
function orderedCompare(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 complete 400x320 panel');}$board->compositeImage($image,Imagick::COMPOSITE_OVER,400*$i,0);$rects[]=['x'=>400*$i,'y'=>0,'width'=>400,'height'=>320];}orderedView($board,$filename);return $rects;}
function orderedDiagnostic(string $id,array $data):void {$doc=['schema'=>'aen-ordered-dither-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');}}
Сохранение RGB8-просмотров и подсчёт значений не заменяют изучаемую операцию и не скрывают её вызов. В веб-приложении PNG отдаётся отдельным ответом с Content-Type: image/png без echo диагностики перед бинарными байтами. Далее: палитры и типы изображений, пороги, цветовое пространство и исключения.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
Imagick::orderedPosterizeImageИсторический упорядоченный дизеринг и современная замена
PECL imagick 2 >= 2.2.2, PECL imagick 3 · imagick
Imagick::orderedPosterizeImage(string $threshold_map, int $channel = Imagick::CHANNEL_DEFAULT): boolВажная граница версии: orderedPosterizeImage — историческое имя, deprecated с Imagick 3.4.4; в исходниках Imagick 3.8.1 оно включается только при сборке с ImageMagick 6 (ниже 7). В проверяемой сборке с ImageMagick 7 этого метода нет. Современные рабочие программы ниже вызывают orderedDitherImage, добавленный в Imagick 3.6.0; доступность зависит от сборки и проверяется method_exists. Исторический контракт сохраняем полностью, но не выдаём выполнение альтернативы за выполнение отсутствующего метода.
Полный исторический контракт
Imagick::orderedPosterizeImage(string $threshold_map, int $channel = Imagick::CHANNEL_DEFAULT): bool| Часть вызова | Что означает |
|---|---|
| $threshold_map: string, обязательный | Имя предопределённой карты; расширенная запись может включать число уровней, например o4x4,4. |
| $channel: int, необязательный | В старом методе default CHANNEL_DEFAULT. Допустимые каналы зависят от цветовой модели; несколько каналов объединяются побитовым |. |
| bool true | Успешная обработка текущего изображения на поддерживаемой старой сборке. |
| Ошибка | Официально объявлен ImagickException; отсутствие самого метода на IM7 — другая ситуация, поэтому сначала проверяйте доступность. |
Официальная доступность: PECL imagick 2 >= 2.2.2, PECL imagick 3; старому вызову требовалась ImageMagick 6.3.1 или новее, при этом точный текущий binding исключает его из IM7. Описание обещает упорядоченный дизеринг по предопределённым картам и нескольким уровням интенсивности, которые могут различаться по каналам. Ни число уровней, ни карта не означают размер общей индексированной палитры.
Историческая запись: только для чтения старого кода
// Historical IM6 fragment: this method is absent from the tested IM7 build.
$image->orderedPosterizeImage('o4x4,4', Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE);Это контекстный фрагмент, а не результат нашего запуска. У современного orderedDitherImage один строковый аргумент: второго $channel у него нет. На IM7 нужные каналы выбираются через setImageChannelMask; сохранённую прежнюю маску возвращают в finally, чтобы настройка не повлияла на последующую обработку. Для пустого Imagick современный wrapper выбрасывает ImagickException; неверная карта тоже может привести к исключению. Ошибка пути к исходнику возникает ещё до обработки.
Официальный пример: что нужно дополнить для запуска
В оригинале функция принимает $imagePath и $orderedPosterizeType, читает realpath, вызывает исторический метод, выставляет PNG, отправляет Content-Type и бинарные байты. Варианты o4x4,3,3 и o8x8,6,6 закомментированы, активен h8x8a. Переменная $imagePath в самом фрагменте не определена: читатель должен задать существующий путь. Этот HTTP-пример требует совместимой IM6-сборки и ответа без текстового вывода перед PNG; на нашей IM7 его имя не существует. Полный исходный пример сохранён в XML ниже, а рабочий современный файл доступен отдельно.
Полный официальный XML и пример без сокращений
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<refentry xmlns="http://docbook.org/ns/docbook" xml:id="imagick.orderedposterizeimage">
<refnamediv>
<refname>Imagick::orderedPosterizeImage</refname>
<refpurpose>Performs an ordered dither</refpurpose>
</refnamediv>
<refsynopsisdiv>
&imagick.deprecated.function-3-4-4;
</refsynopsisdiv>
<refsect1 role="description">
&reftitle.description;
<methodsynopsis>
<modifier>public</modifier> <type>bool</type><methodname>Imagick::orderedPosterizeImage</methodname>
<methodparam><type>string</type><parameter>threshold_map</parameter></methodparam>
<methodparam choice="opt"><type>int</type><parameter>channel</parameter><initializer>Imagick::CHANNEL_DEFAULT</initializer></methodparam>
</methodsynopsis>
<para>
Performs an ordered dither based on a number of pre-defined dithering threshold maps,
but over multiple intensity levels, which can be different for different channels,
according to the input arguments. &imagick.method.available.0x631;
</para>
</refsect1>
<refsect1 role="parameters">
&reftitle.parameters;
<para>
<variablelist>
<varlistentry>
<term><parameter>threshold_map</parameter></term>
<listitem>
<para>
A string containing the name of the threshold dither map to use
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><parameter>channel</parameter></term>
<listitem>
<para>
Provide any channel constant that is valid for your channel mode. To
apply to more than one channel, combine channeltype constants using
bitwise operators. Refer to this
list of <link linkend="imagick.constants.channel">channel constants</link>.
</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>
<refsect1 role="examples">
&reftitle.examples;
<para>
<example>
<title> <function>Imagick::orderedPosterizeImage</function></title>
<programlisting role="php">
<![CDATA[
<?php
function orderedPosterizeImage($imagePath, $orderedPosterizeType) {
$imagick = new \Imagick(realpath($imagePath));
$imagick->orderedPosterizeImage($orderedPosterizeType);
$imagick->setImageFormat('png');
header("Content-Type: image/png");
echo $imagick->getImageBlob();
}
//orderedPosterizeImage($imagePath, 'o4x4,3,3');
//orderedPosterizeImage($imagePath, 'o8x8,6,6');
orderedPosterizeImage($imagePath, 'h8x8a');
?>
]]>
</programlisting>
</example>
</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
-->
Получить чёрно-белый PNG одним вызовом современной операции
На подготовленном сером рисунке o4x4,2 создаёт два цвета: чёрный и белый. Возвращено true, а прежняя маска каналов восстановлена. Проверена доступность современного имени; старое имя не вызывалось.
<?php
declare(strict_types=1);
require __DIR__.'/ordered-fixture.php';
orderedRestore();
$legacyAvailable = method_exists(Imagick::class, 'orderedPosterizeImage');
$modernAvailable = method_exists(Imagick::class, 'orderedDitherImage');
if (!$modernAvailable) { throw new RuntimeException('This example needs orderedDitherImage'); }
[, $image] = orderedParts();
$previousMask = $image->setImageChannelMask((Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE));
try {
$ok = $image->orderedDitherImage('o4x4,2');
} finally {
$usedMask = $image->setImageChannelMask($previousMask);
}
$restored = $image->setImageChannelMask($previousMask) === $previousMask;
orderedView($image, 'ordered-minimal.png');
$stats = orderedStats($image);
orderedDiagnostic('ordered-dither-minimal', ['legacy_available'=>$legacyAvailable,
'modern_available'=>$modernAvailable, 'executed_api'=>'orderedDitherImage',
'spec'=>'o4x4,2', 'return_value'=>$ok, 'used_mask'=>$usedMask,
'mask_restored'=>$restored, 'stats'=>$stats,
'checks'=>['modern_api_available'=>$modernAvailable,'success'=>$ok===true,
'mask_restored'=>$restored,'black_and_white'=>$stats['RGB8_values']===[[0,255],[0,255],[0,255]]&&$stats['color_count']===2]]);
printf("legacy=%d modern=%d set=%s colors=%d\n", (int)$legacyAvailable,
(int)$modernAvailable, $ok?'true':'false', $stats['color_count']);
echo "ordered-minimal.png\nmetrics.json\n";
На подготовленном сером рисунке o4x4,2 создаёт два цвета: чёрный и белый. Возвращено true, а прежняя маска каналов восстановлена. Проверена доступность современного имени; старое имя не вызывалось. Сохраните example.php и два дополнительных текстовых файла архива рядом. Запустите php example.php с расширением Imagick и методом orderedDitherImage, поддержкой PNG и правом записи. Это современная альтернатива для ImageMagick 7, а не выполненный вызов исторического метода. Helper проверяет собственный PNG по SHA, готовит основу и серую копию с явно добавленной полосой градиента; target-вызовы находятся в программе.
Ожидаемый вывод
legacy=0 modern=1 set=true colors=2
ordered-minimal.png
metrics.json
Подготовить два варианта оформления и перехватить ошибки карты
Сервис получает стиль print или poster из разрешённой таблицы. Первый даёт чёрно-белый рисунок; второй ограничивает каждый RGB-канал четырьмя значениями. Неизвестный стиль отклонён до обработки. Намеренно неверная карта вызвала ImagickException: последняя панель показывает сохранённый исходник после ошибки, а не удачный печатный результат.
<?php
declare(strict_types=1);
require __DIR__.'/ordered-fixture.php';
orderedRestore();
$legacyAvailable = method_exists(Imagick::class, 'orderedPosterizeImage');
$modernAvailable = method_exists(Imagick::class, 'orderedDitherImage');
if (!$modernAvailable) { throw new RuntimeException('This example needs orderedDitherImage'); }
function makeArtwork(Imagick $input, string $style): Imagick {
$allowed = ['print'=>'o4x4,2', 'poster'=>'o4x4,4'];
if (!array_key_exists($style, $allowed)) {
throw new InvalidArgumentException('Unknown artwork style');
}
$result = clone $input;
$previousMask = $result->setImageChannelMask((Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE));
try {
$result->orderedDitherImage($allowed[$style]);
} finally {
$result->setImageChannelMask($previousMask);
}
return $result;
}
[$base, $gray] = orderedParts();
$bw = makeArtwork($gray, 'print');
$poster = makeArtwork($base, 'poster');
orderedView($bw, 'product-bw.png');
orderedView($poster, 'product-four-level.png');
$invalidPolicy = 'not caught';
try { makeArtwork($base, 'unknown'); }
catch (InvalidArgumentException $error) { $invalidPolicy = get_class($error); }
// Deliberately bypass the UI allowlist once to demonstrate a real library error.
$rejected = clone $base; $before = orderedHash($rejected); $invalidMap = 'not caught';
$previousMask = $rejected->setImageChannelMask((Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE));
try { $rejected->orderedDitherImage('aen-no-such-threshold-map'); }
catch (ImagickException $error) { $invalidMap = get_class($error); }
finally { $rejected->setImageChannelMask($previousMask); }
$unchanged = $before === orderedHash($rejected);
orderedView($rejected, 'rejected-map-source.png');
$views = [new Imagick(__DIR__.'/product-bw.png'),
new Imagick(__DIR__.'/product-four-level.png'), new Imagick(__DIR__.'/rejected-map-source.png')];
$rects = orderedCompare($views, 'ordered-workflow.png');
$bwStats = orderedStats($bw); $posterStats = orderedStats($poster);
$fourLevels = true;
foreach ($posterStats['RGB8_values'] as $values) {
if (count(array_diff($values,[0,85,170,255]))>0) { $fourLevels=false; }
}
orderedDiagnostic('ordered-dither-workflow', ['legacy_available'=>$legacyAvailable,
'modern_available'=>$modernAvailable, 'executed_api'=>'orderedDitherImage',
'styles'=>['print'=>'o4x4,2','poster'=>'o4x4,4'], 'bw_stats'=>$bwStats,
'poster_stats'=>$posterStats, 'invalid_policy'=>$invalidPolicy,
'invalid_map'=>$invalidMap, 'rejected_source_unchanged'=>$unchanged,
'panel_geometry'=>$rects, 'checks'=>['modern_api_available'=>$modernAvailable,
'print_has_two_colors'=>$bwStats['color_count']===2,
'poster_has_only_four_levels_per_channel'=>$fourLevels&&$posterStats['color_count']<=64,
'invalid_policy_caught'=>$invalidPolicy==='InvalidArgumentException',
'invalid_map_caught'=>$invalidMap==='ImagickException', 'source_retained_after_failure'=>$unchanged]]);
echo "ordered-workflow.png\nproduct-bw.png\nproduct-four-level.png\nrejected-map-source.png\nmetrics.json\ninvalid-policy=InvalidArgumentException invalid-map=ImagickException source-unchanged=1\n";
Сервис получает стиль print или poster из разрешённой таблицы. Первый даёт чёрно-белый рисунок; второй ограничивает каждый RGB-канал четырьмя значениями. Неизвестный стиль отклонён до обработки. Намеренно неверная карта вызвала ImagickException: последняя панель показывает сохранённый исходник после ошибки, а не удачный печатный результат. Сохраните example.php и два дополнительных текстовых файла архива рядом. Запустите php example.php с расширением Imagick и методом orderedDitherImage, поддержкой PNG и правом записи. Это современная альтернатива для ImageMagick 7, а не выполненный вызов исторического метода. Helper проверяет собственный PNG по SHA, готовит основу и серую копию с явно добавленной полосой градиента; target-вызовы находятся в программе.
Ожидаемый вывод
ordered-workflow.png
product-bw.png
product-four-level.png
rejected-map-source.png
metrics.json
invalid-policy=InvalidArgumentException invalid-map=ImagickException source-unchanged=1
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


