При загрузке изображения из файла, ресурса или потока класс
TBitmap
обычно создает изображение в формате DIB-секции, соответствующее источнику по цветовой глубине. Исключение составляют сжатые файлы (формат BMP поддерживает сжатие только для 16- и 256-цветных изображений) — в этом случае создается DDB. В файле
Graphics
определена глобальная переменная
DDBsOnly
, которая по умолчанию равна
False
. Если изменить ее значение на
True
, загружаемое изображение всегда будет иметь формат DDB.
Примечание
В справке сказано, что когда
DDBsOnly = False
, вновь
создаваемые изображения по умолчанию хранятся в виде DIB-секций. На самом деле из-за ошибки в модуле
Graphics
(как минимум до 2007-й версии Delphi включительно) вновь созданное изображение всегда хранится как DDB независимо от значения
DDBsOnly
.
Класс
TBitmap
имеет свойство
ScanLine
, через которое можно получить прямой доступ к массиву пикселов, составляющих изображение. В справке написано, что это свойство можно использовать только с DIB-изображениями. Но на самом деле DDB-изображения тоже позволяют использовать это свойство, хотя и с существенными ограничениями. Если изображение хранится в DDB- формате, при обращении к
ScanLine
создастся его DIB-копия, и
ScanLine
возвращает указатель на массив этой копии. Поэтому, во-первых,
ScanLine
работает с DDB-изображениями очень медленно, а во-вторых, работает не с изображением, а с его копией, откуда вытекают следующие ограничения:
1. Копия создаётся на момент обращения к
ScanLine
, поэтому изменения, сделанные на изображении с помощью GDI-функций после этого, будут недоступными.
2. Каждое обращение к
ScanLine
создает новую копию изображения, а старая при этом уничтожается. Гарантии, что новая копия будет располагаться в той же области памяти, нет, поэтому указатель, полученный при предыдущем обращении к
ScanLine
, больше нельзя использовать.
3. Изменения, сделанные в массиве пикселов, затрагивают только копию изображения, но само изображение при этом не меняется. Поэтому в случае DDB свойство
ScanLine
дает возможность прочитать, но не изменить изображение.
Следует отметить, что
TBitmap
иногда создает DIB-секции, даже если свойства
HandleType
и
PixelFormat
явно указывают на использование DDB. Особенно часто это наблюдается для изображений большого размера. По всей видимости, это происходит, когда в системном пуле нет места для хранения DDB-изображения такого размера, и разработчики TBitmap решили, что в таком случае лучше создать DIB-изображение, чем не создавать никакого. Пример
BitmapSpeed
на прилагаемом компакт-диске позволяет сравнить скорость выполнения различных операций с DDB- и DIB-изображениями.
1.1.12. ANSI и Unicode
Windows поддерживает две кодировки: ANSI и Unicode. В кодировке ANSI (American National Standard Institute) каждый символ кодируется однобайтным кодом. Коды от 0 до 127 совпадают с кодами ASCII, коды от 128 до 255 могут означать разные символы различных языков в зависимости от выбранной кодовой страницы. Кодовые страницы позволяют уместить многочисленные символы различных языков в однобайтный код, но при этом можно работать только с одной кодовой страницей, т.е. с одним языком. Неверно выбранная кодовая страница приводит к появлению непонятных символов (в Интернете их обычно называют "кракозябрами") вместо осмысленного текста.
В кодировке Unicode используется 2 байта на символ, что даёт возможность закодировать 65 536 символов. Этого хватает для символов латиницы и кириллицы, греческого алфавита, китайских иероглифов, арабских и еврейских букв, а также многочисленных дополнительных (финансовых, математических и т. п.) символов. Кодовых страниц в Unicode нет.
Примечание
Кодовая страница русского языка в ANSI имеет номер 1251.
Кодировка символов в ней отличается от принятой в DOS так называемой альтернативной кодировки. В целях совместимости для DOS-программ, а также для консольных приложений Windows использует альтернативную кодировку. Именно поэтому при выводе русского текста в консольных приложениях получаются те самые "кракозябры". Чтобы избежать этого, следует перекодировать символы из кодировки ANSI в DOS при выводе, и наоборот — при вводе. Это можно сделать с помощью функций
CharToOem
и
OemToChar
.
Windows NT/2000/XP поддерживает ANSI и Unicode в полном объеме. Это значит, что любая функция, работающая со строками, представлена в двух вариантах: для ANSI и для Unicode. Windows 9x/МЕ в полном объеме поддерживает только ANSI. Unicode-варианты в этих системах есть у относительно небольшого числа функций. Каждая страница MSDN, посвященная функции, работающей со строками (или со структурами, содержащими строки), в нижней части содержит надпись, показывающую, реализован ли Unicode-вариант этой функции только для NT/2000/XP или для всех платформ.
Примечание
В качестве примера рассмотрим функции вывода текста на экран. Unicode-версию на всех платформах имеют только две из них
TextOut
и
ExtTextOut
. Функции
DrawText
и
DrawTextEx
имеют Unicode-варианты только в Windows NT/2000/XP. Если же смотреть функции для работы с окнами, то среди них нет ни одной, которая имела бы Unicode-вариант в Windows 9х/МЕ. Следует отметить, что с относительно недавнего времени Microsoft предоставляет расширение для Windows 9x/МЕ которое позволяет добавить полную поддержку Unicode в эти системы. Это расширение называется MSLU (Microsoft Layer for Unicode), его можно скачать с официального сайта Microsoft.
Рассмотрим, как сосуществуют два варианта на примере функции
RegisterWindowMessage
. Согласно справке, она экспортируется библиотекой user32.dll. Однако если посмотреть список функций, экспортируемых этой библиотекой (это можно сделать, например, при помощи утилиты TDump.exe, входящей в состав Delphi), то там этой функции не будет, зато будут функции
RegisterWindowMessageA
и
RegisterWindowMessageW
. Первая из них — это ANSI-вариант функции, вторая — Unicode-вариант (буква W означает Wide — широкий; символы кодировки Unicode часто называются широкими из-за того, что на один символ приходится не один, а два байта).
Сначала разберемся с тем, как используются два варианта одной функции в Microsoft Visual C++. В стандартных заголовочных файлах учитывается наличие макроопределения
UNICODE
. Есть два символьных типа —
CHAR
для ANSI и
WCHAR
для Unicode. Если макрос
UNICODE
определен, тип
TCHAR
соответствует типу
WCHAR
, если не определен — типу
CHAR
(после этого производные от
TCHAR
типы, такие как
LPCTSTR
автоматически начинают соответствовать кодировке, определяемой наличием или отсутствием определения
UNICODE
). В заголовочных файлах импортируются оба варианта функции, а также определяется макрос
RegisterWindowMessage
. Его смысл также зависит от макроса
UNICODE
: если он определен,
RegisterWindowMessage
эквивалентен
RegisterWindowMessageW
, если не определен —
RegisterWindowMessageA
. Все функции, поддерживающие два варианта кодировки, импортируются точно так же. Таким образом, вставляя или убирая макроопределение
UNICODE
, можно, не меняя ни единого символа в программе, компилировать ее ANSI- или Unicode-версию.