- •Глава 8. Использование графики
- •Глава 8
- •Графические инструменты Delphi
- •Imagel.Picture.LoadFromFile('rnyicon.Ico'); //Создан и загружен объект класса tIcon
- •Компонент tImage
- •Использование диалогов для загрузки и сохранения графических файлов
- •Класс tcIipboard
- •Класс tScreen
- •Вывод графики с использованием отображаемых файлов
- •Класс tAnimate
Imagel.Picture.LoadFromFile('rnyicon.Ico'); //Создан и загружен объект класса tIcon
MyBitmap := Imagel.Picture.Bitmap; // прежний TIcon уничтожается
Если же вы описали свой класс (допустим, TGiFimage), то к его методам и свойствам следует обращаться так:
(Graphic as TGIFImage).MyProperty := MyValue;
Перечислим остальные методы и свойства:
procedure LoadFromFile(const Filename: string);
Анализирует расширение имени файла FileName и, если оно известно (зарегистрировано), то создается объект нужного класса и вызывается его метод LoddFromFiie. В противном случае возникает исключительная ситуация EirivalidGraphic. Стандартными расширениями являются ico, wmf (emf) и bmp. Если подключить к приложению модуль jpeg.pas, то можно будет загрузить и файлы с расширениями jpg (формат JPEG).
procedure SaveToFile(const Filename: string);
Сохраняет графику в файле, вызывая соответствующий метод объекта
Graphic.
procedure LoadFroiriCl ipboardFormat (AFormat: Word; AData: THandle;
APaiette: HPALETTE);
Во многом аналогичен методу LoadFromFile. Если формат AFormat найден среди зарегистрированных, то AData и APaiette передаются для загрузки методу соответствующего объекта. Изначально зарегистрированных форматов три: битовое изображение CF BITMAP и метафайлы CF_METAFILEPICT и CF_ENHMETAFILE.
procedure SaveToCIiphoardFormat(var AFormat: Word; var AData: THandle; var APaiette: HPALETTE);
Сохраняет графику в буфере обмена, вызывая метод объекта Graphic.
procedure Assign(Source.: TPersistent);
Метод Assign переписан таким образом, чтобы присваиваемый объект мог принадлежать как классу Tpicture, так и TGraphic или любого его потомка. Кроме того, он может быть равен Nil - в этомслучае поле Graphic очищается с удалением прежнего объекта.
class function SupportsClipboardFormat(AFormat: Word): Boolean;
Метод класса возвращает True, если формат AFormat поддерживается классом Tpicture (зарегистрирован в системе). Напомним, что методы класса можно вызывать через ссылку на класс, без создания экземпляра объекта.
class procedure RegisterFileFormat(const AExtension, ADescription:
string; AGraphicClass: TGraphicClass);
class procedure RegisterClipboardFormat(AFormat: Word; AGraphicClass:
TGraphicClass);
Предназначены для создателей новых графических классов. Они позволяют зарегистрировать формат файла и буфера обмена и связать их с созданным классом — потомком TGraphic, который умеет читать и записывать информацию в этом формате.
property Width: Integers; property Height: Integer;
Ширина и высота картинки. Значения этого свойства всегда те же, что и у объекта из свойства Graphic.
Все три разновидности графических объектов имеют свои системы кэширования. Это означает, что на один реально существующий в системе (и занимающий долю ресурсов!) дескриптор могут одновременно ссылаться несколько объектов. Реализуется такое связывание через метод Assign. Выражение:
Iconl.Assign(Icon2);
означает, что два этих объекта разделяют теперь один фактически находящийся в памяти значок.
Более простым является кэширование для Ticon и metafile, которые умеют только отображать себя и не предназначены для редактирования (создатели Delphi считают, что дескриптор графического объекта дается программисту не для того, чтобы "ковыряться" в нем на уровне двоичных кодов). Гораздо сложнее устроен механизм кэширования для канала TBitmap, который имеет свою канву для рисования.
Внутреннее представление информации в графических объектах двоякое — Она может храниться как поток типа TMemoryStream (в него загружается содержимое соответствующего файла), как область памяти с дескриптором (структура которой зависит от типа графического объекта) и одновременно в двух этих видах, содержимое которых автоматически синхронизируется. Поэтому будьте готовы, что загрузка изображения потребует вдвое большего объема памяти — особенно это актуально для больших картинок.
Кого-то может удивить отсутствие объявленных методов рисования, вроде метода Draw для классов Ticon, TMetafile и TBitmap. Объяснение простое — в процессе рисования они играют пассивную роль; рисуют не они — рисуют их. Все рисование должно осуществляться через вызовы методов Draw и StretchDraw канвы, содержащей графику, ибо канва соответствует тому контексту, в котором должна осуществляться операция.
Рассмотрим предопределенные графические классы.
Класс TMetafile
Класс TMetafile инкапсулирует свойства метафайла Windows. С появлением Windows 95 к стандартному метафайлу (формат wmf) добавился расширенный (формат emf), обладающий расширенными возможностями. Соответственно в объекте TMetafile имеется свойство
property Enhanced: Boolean;
Внутреннее представление метафайла всегда новое (emf), и устанавливать свойство Enhanced в False следует только для обеспечения совместимости со старыми программами.
В классе TMetafile перекрываются методы Assign, LoadFromStream, SaveToStream, LoadFromClipboardFomat, SaveToClipboardFormat. В буфер обмена объект помещает свое содержимое в формате CF_ENHMETAFILE. Помимо общих, класс имеет следующие свойства:
дескриптор метафайла property Handle: hmetafile;
свойство property inch: word. Число точек на дюйм в координатной системе метафайла. Связано с установленным режимом отображения.
свойства property MMHeight: Integer; property MMWidth: integer;, настоящие высота и ширина метафайла в единицах, равных 0,01 миллиметра. Свойства Height и width задаются в пикселах.
в метафайл можно добавить свою палитру: property Palette: HPalette;
вы можете увековечить себя, установив два свойства метафайла:
property Description: string;
property CreatedBy: string;
Содержащаяся в них информация записывается в файл и может быть прочитана благодарными потомками.
Класс TIcon
Этот класс инкапсулирует значок Windows.
Не пытайтесь изменить размеры значка — они по определению постоянны (и равны GetSystemMetrics(SM_CXICON) И GetSystemMetrics(SM_CYICON)), и при попытке присвоить новые значения возникает исключительная ситуация EinvaiidGraphicOperation. Значок нельзя также читать и писать в буфер обмена, так как в Windows нет соответствующего формата.
Свойство Transparent для значка всегда равно True. Изменить его нельзя — значки прозрачны также по определению.
В этом классе перекрываются методы класса TGraphic: Assign, LoadFromStream и SaveToStream. Дополнительно также определены:
property Handle: hicon; — дескриптор значка
function ReleaseHandle: HICON; - метод "отдает" дескриптор — возвращает его значение, обнуляя ссылку на него в объекте.
Класс TBitmap
Класс TBitmap является основой растровой графики в Delphi. В первых версиях среды этот класс соответствовал битовой карте, зависимой от устройства (Device Dependent Bitmap, DDB). Этот формат хорош для деловой графики — отображения небольших картинок с малой глубиной цвета, например, на кнопках. Формат DDB родился во времена первых версий Windows, когда еще не было графических ускорителей и кое-где еще помнили о EGA. Поэтому и форматы хранения были привязаны к определенным видеорежимам.
Со временем аппаратура совершенствовалась, росло и количество поддерживаемых видеорежимов. Появились режимы High Color (15—16 бит на точку) и True Color (24 бита на точку). Все это привело к тому, что картинка стала храниться в аппаратно-независимом формате (Device Independent Bitmap, DIB), а проблемы ее быстрого отображения легли на аппаратуру и драйверы.
За формат битовой карты — DIB или DDB — отвечает свойство:
type TBitmapHandleType = (bmDIB, bmDDB);
property HandleType: TBitmapHandleType;
По умолчанию устанавливается режим bmDIB. Впрочем, можно заставить приложение, написанное на Delphi, вернуться к старому типу. Для этого нужно установить значение глобальной переменной DDBsOnly (модуль graphics. pas) равной True. Впрочем, необходимость этого сомнительна. Все новые видеокарты и драйверы к ним, а также графические интерфейсы (такие, как DirectX), оптимизированы для использования DIB.
Желаемую глубину цвета битовой карты можно узнать и переустановить, меняя значение свойства:
TPixelFormat = (pfDevice, pflbit, pf4bit, pfSbit, pfl5bit, pfl6bit, pf24bit, pf32bit, pfCustom);
property PixelFormat: TPixeiFormat;
Формат pf Device соответствует битовой карте DDB. Глубина цвета в 1, 4 и 8 бит на пиксел — традиционная и предусматривает наличие у изображения палитры. Другие режимы предусматривают хранение непосредственных яркостей точек в каждом из трех основных цветов — красном (R), зеленом (G) и синем (В). Разрядность 15 бит соответствует распределению бит 5-5-5 (RGB 555), 16 бит - RGB 565, 24 бит -RGB 888. Режим 32 бит похож на 24-битный, но в нем дополнительно добавлен четвертый канал (альфа-канал), содержащий дополнительную информацию о прозрачности каждой точки. Формат pf custom предназначен для реализации программистом собственных графических конструкций. В стандартном классе TBitmap установка свойства PixelFormat в pfCustom приведет к ошибке — поэтому использовать его нужно только в написанных вами потомках TBitmap.
Битовая карта является одним из видов ресурсов. Естественно, что класс TBitmap поддерживает загрузку из ресурсов приложения:
procedure LoadFromResourceID(Instance: THandle; ResID: Integers-procedure LoadFromResourceName(Instance: THandle; const ResName: string);
Здесь instance — это глобальная переменная модуля System, хранящая уникальный идентификатор запущенной копии приложения (или динамической библиотеки).
Канва битовой карты доступна через свойство:
property Canvas: TCanvas;
С ее помощью можно рисовать на поверхности растрового изображения. Обратите внимание, что никакие другие потомки TGraphic канвы не имеют.
Дескрипторы битовой карты и ее палитры доступны как свойства:
property Handle: HBITMAP;
property Palette: HPALETTE;
Имея дело с TBitmap, учитывайте, что принцип "один объект-один дескриптор" из-за наличия механизма кэширования неверен. Два метода:
function ReleaseHandle: HBITMAP;
function ReleasePalette: HPALETTE;
возвращают дескрипторы битовой карты и палитры соответственно и после этого обнуляют дескрипторы, то есть как бы "отдают" их пользователю.
При любом внешнем обращении к дескриптору битовой карты и любой попытке рисовать на ее канве, разделение одной картинки несколькими объектами прерывается, и объект получает собственную копию содержимого дескриптора. Для этого есть методы:
procedure Dormant, который выгружает изображение в поток и уничтожает дескрипторы битовой карты и палитры;
procedure Freelmage, "освобождающий" Дескриптор 6итовой карты для дальнейшего использования и внесения изменений. Это означает, что если на данный дескриптор есть ссылки, то он дублируется; а поток очищается.
Битовая карта может быть монохромной и цветной, что определено свойством:
property Monochrome: Boolean;
Значение True соответствует монохромной битовой карте. При его изменении происходит преобразование содержимого к требуемому виду.
За прозрачность битовой карты отвечают свойства:
property TransparentColor: TColor;
type TTransparentMode - (tmAuto, tmFixed);
property TransparentMode: TTransparentMode;
Если значение свойства TransparentMode установлено равным tmAuto, за прозрачный (фоновый) принимается цвет верхнего левого пиксела. В противном случае этот цвет берется из свойства TransparentColor.
Битовая карта может использоваться в качестве маски для других битовых карт. В этом случае она превращается в двухцветную, где в белый цвет превращаются точки фона (см. TransparentColor), а в черный — все остальные. Для поддержки этого режима служат следующие методы и свойства:
procedure Mask(TransparentColor: TColor);
property MaskHandle: HBitmap;
function ReleaseMaskHandle: HBitmap;
Наконец, последним по счету будет рассмотрено очень важное свойство битовой карты TBitmap. Если формат ее хранения — DIB, то есть возможность получить доступ к данным самой битовой карты:
property ScanLine[Row: Integer]: Pointer;
Это свойство представляет собой массив указателей на строки с данными битовой карты. Параметр Row содержит номер строки. Следует помнить, что в большинстве случаев строки в битовой карте упорядочены в памяти снизу вверх и фактически первой после заголовка хранится нижняя строка. Код, возвращающий значение свойства scanLine, это учитывает; поэтому не удивляйтесь, если с ростом параметра Row значение свойства уменьшается.
Внутри строки данные упорядочены в соответствии с форматом (PixeiFormat). Для формата pfSbit все просто — каждый байт в строке соответствует одному пикселу. Для pflSbit и pfl6bit пикселу соответствуют два байта (в этих 16 битах упакованы данные о трех каналах), pf24bit - три байта (по байту на канал).
Примерно так может выглядеть обработчик события onMouseMove, выводящий на панель состояния информацию о яркости в данной точке (подразумевается, что формат битовой карты — 8 или 24 бита):
procedure TMainForm.ImagelMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integers);
begin
if not Assigned(Imagel.Picture.Bitmap) then Exit;
with Imagel.Picture.Bitmap, do case PixelFormat of
pfSbit: Statusbarl.SimpleText := Fonnat('x: %d y: %d b: %d',[x,y, pByteArray(ScanLine[y])^[x] ]) ;
pf24bit: Statusbarl. SimpleText :=Format('x: id y: %d R: %d,G: %d, B: %d',
[x,y, pByteArray(ScanLine[y])^[3*x], pByteArray(ScanLine[y])"[ 3*x+l], pByteArray(ScanLine[у])л[ 3*x+2]]) ;
end;
Само значение свойства ScanLine изменить нельзя (оно доступно только для чтения). Но можно изменить данные, на которые оно указывает. Вот так можно получить негатив 24-битной картинки:
Var line : pByteArray;
For i:=0 to Imagel.Picture.Bitmap.Height — 1 do
Begin
Line := Imagel.Picture.Bitmap.ScanLine[i];
For j:=0 to Imagel.Picture.Bitmap.Width * 3 - 1 do Line^j] := 255 - Line'" [ j ];
End;
Если вы хотите решать более серьезные задачи — на уровне профессиональных средств — на помощь может прийти библиотека обработки изображений фирмы Intel — Intel Image Processing Library. Этот набор инструментов позволяет разработчику включать в программы алгоритмы обработки изображений, написанные и оптимизированные специально для процессоров фирмы Intel. Библиотека является свободно распространяемой, и последняя ее версия находится на WEB-сайте фирмы. Интерфейсы к функциям библиотеки для Delphi разработаны авторами этой книги и вместе с примерами находятся на прилагаемом к книге диске.
В Delphi можно столкнуться с "тезкой" рассматриваемого объекта — структурой TBitmap, описанной в файле windows . pas. Поскольку обе они относятся к одной и той же предметной области, часто возникают коллизии, приводящие к ошибкам. Напомним, чтобы отличить структуры-синонимы, следует использовать имя модуля, в котором они описаны. Поэтому, если в вашей программе есть модули Windows и Graphics, то описывайте и употребляйте типы Windows.TBitmap И Graphics.TBitmap.
В состав Windows входят карточные игры (точнее, пасьянсы), которые черпают ресурсы из динамической библиотеки cards, dll. Если вы откроете эту библиотеку в редакторе ресурсов, то увидите там изображения всех пятидесяти двух карт и десятка вариантов их рубашек (оборотных сторон). Используем эту возможность для рисования карт. Так загружается битовая карта для рубашки:
var CardsDIl : THandle;
BackBitmap : Graphics.TBitmap;
initialization
CardsDIl := LoadLibraryEx('cards.dll',0, LOAD_LIBRARY_AS_DATAFILE);
BackBitmap := Graphics.TBitmap.Create;
BackBitmap. LoadFromResourceID(CardsDIl, 64);
finalization
BackBitmap.Free;
FreeLibrary(CardsDIl);
end.
В Windows 95/98 эта динамическая библиотека— 16-разрядная, и работать так, как описано, не будет. Используйте cards .dll из состава Windows NT.
Аналогичным образом можно загрузить битовые карты для всей колоды. При показе карты, в зависимости от того, открыта она или закрыта, отрисовывается один из объектов TBitmap:
if Known then //карта открыта
Canvas.StretchDraw(ClientRect, FaceBitmap) else
Canvas.StretchDraw(ClientRect,BackBitmap) end;
Графический формат JPEG. Класс TJPEGImage
В 1988 году был принят первый международный стандарт сжатия неподвижных изображений. Он был назван по имени группы, которая над ним работала — JPEG (Joint Photographic Expert Group, формат Объединенной группы экспертов по фотографии). Дело в том, что стандартные архиваторы (ZIP, ARJ) и традиционные алгоритмы сжатия в форматах GIF, TIFF и PCX не могут достаточно сильно сжать полутоновую или цветную картинку — максимум в 2—3 раза. Примененный в JPEG алгоритм позволяет достичь сжатия в десятки раз — правда, при этом изображение подвергается необратимому искажению, и из него пропадает часть деталей. Бессмысленно (и вредно!) подвергать хранению в формате JPEG чертежи, рисунки, а также любые изображения с малым числом градаций — он предназначен именно для изображений фотографического качества.
Поддержка формата JPEG реализована в Delphi посредством класса TJPEGImage, который является потоком класса TGraphic.
Н азвание TJPEGImage — не совсем удачное, к Timage этот класс не имеет ни малейшего отношения. Скорее, это "двоюродный брат" класса TBitmap.
К такому объекту предъявляются двоякие требования. С одной стороны, он должен поддерживать сжатие данных для записи на диск. С другой — распакованные данные в формате DIB, чтобы по требованию системы отрисо-вать их. Поэтому объект класса TJPEGImage может хранить оба вида данных, а также производить их взаимные преобразования, то есть сжатие и распаковку. Для этого в нем предусмотрены методы:
procedure Compress;
procedure DIBNeeded;
procedure JPEGNeeded;
Рекомендуется вызывать метод DIBNeeded заранее, перед отрисовкой картинки — это ускорит процесс ее вывода на экран.
Кроме того, полезно использовать метод Assign, который позволяет поместить в класс TJPEGImage объект TBitmap и наоборот:
MyJPEGImage.Assign(MyBitmap);
MyBitmap.Assign(MyJPEGImage);
При этом происходит преобразование форматов.
Свойства TJPEGImage можно условно разделить на две группы: используемые при сжатии и при распаковке.
Важнейшим из свойств,нужных при сжатии, является CompressionQuality:
type TJPEGQualityRange = 1..100;
property CompressionQuality: TJPEGQualityRange;
Оно определяет качество сжимаемого изображения и его размер. При малых значениях этого свойства файлы получаются очень маленькими, но с большими искажениями (напомним, что стандарт JPEG предусматривает сжатие с потерями качества). При значениях, близких к 100, потери незаметны, но и размер файла при этом максимален.
По умолчанию значение этого свойства равно 75, что обеспечивает разумный компромисс между размером и качеством.
З аранее предсказать размер сжатого файла нельзя — разные картинки сжимаются по-разному, даже при одном значении CompressionQuality.
Кроме CompressionQuality, на качество отображения может повлиять и свойство
type TJPEGPerformance = (jpBestQuality, jpBestSpeed);
property Performance: TJPEGPerformance;
Оно нужно только при распаковке и отвечает за способ восстановления цветовой палитры из сжатой информации. На качество записываемого изображения оно никак не влияет.
Как и у TBitmap, у TJPEGImage есть свойство
type TJPEGPixelFormat = (jf24Bit, jf8Bit);
property PixelFormat: TJPEGPixelForm;
Для рассматриваемого объекта возможных значений всего два— jfSbit и jf24bit. По умолчанию используется 24-битный формат. Если информация о цвете не нужна, то можно установить значение свойства Grayscale равным True — в этом случае изображение будет записано (или распаковано) в полутоновом виде (256 оттенков серого).
Свойства ProgressiveEncoding И ProgressiveDisplay определяют способ показа изображения при распаковке. Первое из них отвечает за порядок записи в файл сжатых компонентов. Если значение свойства ProgressiveEncoding установлено равным True, начинает играть роль свойство ProgressiveDisplay. От его значения зависит, будет ли показываться изображение по мере распаковки (при значении True), либо будет сначала полностью распаковано, а потом показано (при значении False).
Чтобы организовать предварительный просмотр большого числа больших изображений, уместно воспользоваться свойством:
type TJPEGScale = (jsFullSize, jsHalf, jsQuarter, jsEighth);
property Scale: TJPEGScale;
Искушенные в графике специалисты зададут вопрос: зачем оно? Ведь можно прочитать изображение, а затем уменьшить его масштаб стандартными способами. Представление информации в файлах JPEG таково, что можно достаточно просто извлечь изображение сразу в нужном масштабе. Таким образом достигается двойной выигрыш — на времени распаковки и на времени отображения.
П ечать растровых изображений может вызвать проблемы при согласовании его размеров с размерами листа принтера и его разрешением. Большую часть из них можно снять, изучив пример, поставляемый с Delphi — jpegproj. Он находится не в папке \Denios, как обычно, а в папке Help\Examples\Jpeg.
В заключение отметим, что класс TJpEGimage не имеет своей канвы для рисования — для этого его нужно преобразовать в TBitmap.
