Предположим, что у вас возникла нужда изменить разрешение изображения. Например, чтобы сделать его миниатюру (thumbnail) для предварительного просмотра. Для этого можно воспользоваться какой-нибудь библиотекой для работы с графикой. Можно реализовать алгоритм самому. Но можно обойтись и подручными средствами, то есть функциями Delphi и WinAPI.
Классической реализацией масштабирования изображения, которую вы можете найти в различных FAQ и на форумах является примерно вот такая реализация:
Кого-то это устроит, а кого-то - нет. Причина этих зубчиков нашлась в методе Vcl.Graphics.TBitmap.Draw. В том, с какими параметрами он вызывает из WinAPI функцию SetStretchBltMode.
Функция SetStretchBltMode устанавливает режим масштабирования изображения в заданном контексте устройства. В нашем случае устройство - это canvas, на котором будем рисовать изображение нового масштаба.
В большинстве случаев метод Graphics.TBitmap.Draw использует режим COLORONCOLOR. Поэтому, если мы хотим улучшить качество изображения, то нам с ним не по пути. Давайте нарисуем изображение с новым масштабом сами.
При вызове процедуры ScaleJpeg2 с параметром StretchMode=COLORONCOLOR мы получим изображение идентичное результату работы ScaleJpeg1. Но если вызвать ее с параметром StretchMode=HALFTONE, то зубчики сгладятся и станут еле заметными:
Таким образом мы имеем функцию изменения разрешения изображения, которая позволяет нам выбирать, в зависимости от поставленной задачи, между скоростью обработки изображения и качеством ее результатов.
P.S. Я отвлекся на релиз Delphi 10.4 Sydney, поэтому между датой написания этой статьи и датой ее публикации прошло больше трех недель. За это время, в результате моих тестов, всплыл один интересный факт, под влиянием которого в функцию ScaleJpeg2 мне пришлось внести небольшие изменения, которые привели к значительному повышению скорости ее работы, и послужили поводом к написанию продолжения этой статьи. Которое так же будет примером практического применения информации из статьи Доступ к private членам класса.
Классической реализацией масштабирования изображения, которую вы можете найти в различных FAQ и на форумах является примерно вот такая реализация:
procedure ScaleJpeg1(jpg: TJPEGImage; const NewWidth, NewHeight: Integer);
var
bmp: TBitmap;
fScale: Double;
begin
fScale := Min(NewWidth / jpg.Width, NewHeight / jpg.Height);
bmp := TBitmap.Create;
try
bmp.SetSize(Round(jpg.Width * fScale), Round(jpg.Height * fScale));
bmp.Canvas.StretchDraw(bmp.Canvas.ClipRect, jpg);
jpg.Assign(bmp);
finally
bmp.Free;
end;
end;
Это работает. Но если хорошо присмотреться или увеличить полученное изображение, то по краям предмета можно увидеть "зубчики", хотя на исходном изображении их нет:Кого-то это устроит, а кого-то - нет. Причина этих зубчиков нашлась в методе Vcl.Graphics.TBitmap.Draw. В том, с какими параметрами он вызывает из WinAPI функцию SetStretchBltMode.
Функция SetStretchBltMode устанавливает режим масштабирования изображения в заданном контексте устройства. В нашем случае устройство - это canvas, на котором будем рисовать изображение нового масштаба.
int SetStretchBltMode
(
HDC hdc,
int mode
);
где - hdc – хэндл устройства (Canvas.Handle) на котором будем рисовать рисунок;
- mode - режим масштабирования.
- BLACKONWHITE - выполняется булева операция AND используя коды цвета для ликвидируемых и существующих пикселей (если растровое изображение является монохромны, то этот режиме сохраняет черные пиксели за счет белых пикселей);
- WHITEONBLACK - выполняется булева операция OR используя коды цвета для ликвидируемых и существующих пикселей (если растровое изображение является монохромны, то этот режим сохраняет белые пиксели за счет черных пикселей);
- COLORONCOLOR - удаляет все ликвидируемые строки пикселей, не пытаясь сберечь их информацию
- HALFTONE - преобразует пиксели исходного прямоугольника в блоки пикселей в целевом прямоугольнике и среднее значение цвета всего целевого блока пикселей подбирается близким по значению к цвету исходных пикселей;
- STRETCH_ANDSCANS - равнозначен BLACKONWHITE (Windows 95/98);
- STRETCH_ORSCANS - равнозначен WHITEONBLACK (Windows 95/98);
- STRETCH_DELETESCANS - равнозначен COLORONCOLOR (Windows 95/98);
- STRETCH_HALFTONE - равнозначен HALFTONE (Windows 95/98).
В большинстве случаев метод Graphics.TBitmap.Draw использует режим COLORONCOLOR. Поэтому, если мы хотим улучшить качество изображения, то нам с ним не по пути. Давайте нарисуем изображение с новым масштабом сами.
procedure ScaleJpeg2(jpg: TJPEGImage; const NewWidth, NewHeight: Integer;
const StretchMode: Integer = HALFTONE);
var
bmp: Vcl.Graphics.TBitmap;
fScale: Double;
PrevPt: TPoint;
begin
fScale := Min(NewWidth / jpg.Width, NewHeight / jpg.Height);
bmp := Vcl.Graphics.TBitmap.Create;
try
bmp.SetSize(Round(jpg.Width * fScale), Round(jpg.Height * fScale));
GetBrushOrgEx(bmp.Canvas.Handle, PrevPt);
SetStretchBltMode(bmp.Canvas.Handle, StretchMode);
SetBrushOrgEx(bmp.Canvas.Handle, PrevPt.x, PrevPt.y, @PrevPt);
StretchBlt(bmp.Canvas.Handle, 0, 0, bmp.Width, bmp.Height,
jpg.Canvas.Handle, 0, 0, jpg.Width, jpg.Height,
SRCCOPY);
jpg.Assign(bmp);
finally
bmp.Free;
end;
end;
Согласно документации, после установки режима HALFTONE, чтобы избежать смещения кисти при ее автоматической корректировке операционной системой, необходимо вызвать функцию SetBrushOrgEx. Поэтому в мою процедуру добавлен вызов GetBrushOrgEx и SetBrushOrgEx (строки 12 и 14).При вызове процедуры ScaleJpeg2 с параметром StretchMode=COLORONCOLOR мы получим изображение идентичное результату работы ScaleJpeg1. Но если вызвать ее с параметром StretchMode=HALFTONE, то зубчики сгладятся и станут еле заметными:
Таким образом мы имеем функцию изменения разрешения изображения, которая позволяет нам выбирать, в зависимости от поставленной задачи, между скоростью обработки изображения и качеством ее результатов.
P.S. Я отвлекся на релиз Delphi 10.4 Sydney, поэтому между датой написания этой статьи и датой ее публикации прошло больше трех недель. За это время, в результате моих тестов, всплыл один интересный факт, под влиянием которого в функцию ScaleJpeg2 мне пришлось внести небольшие изменения, которые привели к значительному повышению скорости ее работы, и послужили поводом к написанию продолжения этой статьи. Которое так же будет примером практического применения информации из статьи Доступ к private членам класса.


Комментариев нет:
Отправить комментарий