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

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

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

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

Глава 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. Исторический контракт сохраняем полностью, но не выдаём выполнение альтернативы за выполнение отсутствующего метода.

Реальные PNG современного orderedDitherImage: o4x4,2, h8x8a,2 и o4x4,4
Реальные PNG современного orderedDitherImage: o4x4,2, h8x8a,2 и o4x4,4

Дизеринг создаёт ощущение промежуточного оттенка из нескольких допустимых значений пикселей. В упорядоченном варианте по изображению повторяется небольшая таблица порогов: положение пикселя в сетке влияет на выбор ближайшего уровня. На расстоянии чёрные и белые точки выглядят как серый участок. Это не случайный шум и не добавление настоящих оттенков в двухцветный файл. $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

Разобрать повторяемую карту и число уровней

Условная увеличенная сетка объясняет повторение порогов; настоящий результат показан отдельной PNG-областью
Условная увеличенная сетка объясняет повторение порогов; настоящий результат показан отдельной PNG-областью

Модель на схеме специально использует маленькие условные числа и увеличенные клетки. Она объясняет выбор, но не выдаётся за точную матрицу o4x4 или физические размеры пикселей. Реальная таблица o4x4 в thresholds.xml установленной версии имеет divisor 17 и следующие значения:

x=0x=1x=2x=3
19311
135157
412210
168146

Для каждого пикселя 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. Исторический контракт сохраняем полностью, но не выдаём выполнение альтернативы за выполнение отсутствующего метода.

Двухцветный результат современного вызова o4x4,2
Двухцветный результат современного вызова o4x4,2

Начальный полезный результат — один PNG с чёрными и белыми пикселями. Перед вызовом проверяем method_exists, выбираем RGB-каналы и сохраняем прежнюю маску. try содержит обработку, finally возвращает настройку даже при исключении. Второй setImageChannelMask с прежним значением нужен только для наблюдения возвращённой старой маски в этом учебном примере; он не запускает дизеринг ещё раз.

Получить чёрно-белый PNG одним вызовом современной операции

На подготовленном сером рисунке o4x4,2 создаёт два цвета: чёрный и белый. Возвращено true, а прежняя маска каналов восстановлена. Проверена доступность современного имени; старое имя не вызывалось.

Получить чёрно-белый PNG одним вызовом современной операцииПример для указанного окружения
<?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, а прежняя маска каналов восстановлена. Проверена доступность современного имени; старое имя не вызывалось.

Получить чёрно-белый PNG одним вызовом современной операцииПример для указанного окружения
<?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. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.