Предположим, что у вас возникла нужда изменить разрешение изображения. Например, чтобы сделать его миниатюру (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 членам класса.
Комментариев нет:
Отправить комментарий