Статьи к пособию-самоучителю on-line "Visual Basic с нуля"
Как узнать размеры графического изображения файла BMP, не загружая его. Формат BMP-файла.
Скачать исходник примера InfoBMP
Дата создания 03.06.2005 {Автор 4us}

При загрузке картинок в проект VB постоянно возникает проблема их вывода размером, удобным для просмотра. В большинстве примеров и статей, размещенных в глобальной сети, авторы ограничиваются установкой размеров Image или PictureBox'а равным размеру формы, видимо предполагая, что картинка не может быть размером более 1024 х 768 пикселей. Уверяю тебя, это предположение верно только для картинок, скаченных с Интернета. Обычные фотографии (сделанные не мобильником) значительно больше.

Для того, чтобы вывести фотографию любым, меньшим чем оригинал размером, достаточно установить свойство Image.Stretch=True. Тогда картинка впишется в рамки установленного размера Image. Проблема остается в том, что нарушается пропорция между шириной и длиной картинки и изображение искажается. Для того, чтобы избежать этого, надо заранее, перед загрузкой картинки в Image, знать реальный размер загружаемой картинки и установить Image.Width и Image.Hight пропорционально этим размерам. В главе 6 мы, для того, чтобы определить размер изображения использовали дополнительно элемент Picture. Загрузив картинку в Picture Мы по свойствам Picture.ScaleWidth и Picture.ScaleHight определяли реальный размер изображения и исходя из него, устанавливали размер Image. После этого копировали картинку из Picture в Image (или в другой Picture) с помощью метода PaintPicture. Желаемый результат, мы как говориться, в большинстве случаев получали. Однако, по-скольку работа с графикой не является сильным местом VB, этот процесс при значительных размерах картинки занимал достаточно много времени и отнимал много ресурсов. Например, на компьютере Pentium III 700Мгц c оперативной памятью 128 Мб под Win98 при размере файла картинки JPG более 1.2 мб этот процесс вообще не имел счастливого завершения и уменьшеной копии картинки из таких файлов я так и не увидел (причем что на Pentium IV 1500 Мгц и 256 Мб оперативки под Windows XP это работало).

Такое положение вещей приводит к логическому выводу: хорошо бы сразу знать размер графического изображения, не загружая файла, тогда можно будет выставить размер Image перед загрузкой картинки и будет нам счастье.
Даже не изучая вопроса графического хранения и сжатия информации ясно, что в файле картинки где-то черным по-белому прописан размер изображения. Нам надо только его найти и считать. Сразу скажу, что нашей целью не является подробный анализ внутренней структуры графических файлов и написание кодировщика-раскодировщика этих файлов. Наша цель - получить нужную нам информацию (конкретно - размер изображения). Мы попробуем по рабоче-крестьянски проанализировать три самых распространенных формата BMP, GIF и JPG (JFIF), при этом особо не напрягая наши драгоценные мозги. Для того, чтобы поработать с внутренним устройстом графических файлов нам понадобится шестнадцатеричный редактор. Если его нет под рукой, скачать простой бесплатный редактор можно здесь (507 kb). Если тебе все это лениво, то можешь просто воспользоваться результатами в виде готового кода (исходник вверху страницы).

Итак начнем с самого простого и приближенного к Windows:

Растровый файл BMP (Bitmap).

Этот формат является основным для Windows, так называемый Bitmap. Кроме того из него довольно-таки легко получить искомую нам информацию. Эти файлы обычно имеют расширение .bmp и используются Windows везде, где только возможно.
Структура файла такова, что нужная нам информация хранится в первых 54 байтах файла. Ниже представлена таблица с подробным представлением этих самых байт для файла BMP с размером изображения 719 х 781 пикселов.

Таблица заголовка BMP-файла.

Область
Заголовок файла
Заголовок растра
Информация
Идентиф. BMP
Размер файла
Не используется
Место растра
Длина заголовка растра
Номер байта (десятичное)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
Пример значения (шестнадцат.)
42
4D
E6
BD
19
00
00
00
00
00
36
00
00
00
28
00
00
00
продолжение таблицы
Область
Заголовок растра
Информация
Ширина изображения
Высота изображения
Число цветовых плоскостей
Бит.пиксель
Метод сжатия
Номер байта (десятичное)
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Пример значения (шестнадцат.)
CF
02
00
00
0D
03
00
00
01
00
18
00
00
00
00
00
окончание таблицы
Область
Заголовок растра
Информация
Длина растра
Горизонтальное разрешение
Вертикальное разрешение
Число цветов изображения
Число основных цветов
Номер байта (десятичное)
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
Пример значения (шестнадцат.)
00
00
00
00
40
0B
00
00
40
0B
00
00
00
00
00
00
00
00
00
00


Все это вы сможете увидеть, открыв какой-нибудь BMP-файл с помощью шестнадцатеричного редактора.
Естественно, для работы с основным виндусовым файлом предусмотрены широкие воможности, которые реализованы большим набором структур, которые обычно используются совместно с API-функциями: BITMAP, BITMAPCOREHEADER, BITMAPCOREINFO, BITMAPINFO, но нашу информацию о типе графического изображения можно получить с помощью структуры BITMAPFILEHEADER, а о размере - с помощью структуры BITMAPINFOHEADER. Ниже представлено их объявление и краткое описание.

Type BITMAPFILEHEADER '14 первых байт файла BMP
bfType As Integer 'символы "BM" (&H4D42 или 19778), обозначающие bmp-файл
bfSize As Long 'размер файла
bfReserved1 As Integer 'не используется, должно равнятся нулю
bfReserved2 As Integer 'не используется, должно равнятся нулю
bfOffBits As Long 'смещение графических данных от конца структуры
End Type


Type BITMAPINFOHEADER '40 байт - общие характеристики растра
biSize As Long 'размер структуры в байтах, всегда 40 байт
biWidth As Long 'ширина растра в пикселах
biHeight As Long 'высота растра в пикселах
biPlanes As Integer 'количество цветовых плоскостей - всегда 1
biBitCount As Integer 'задает количество бит на пиксел:
'1 - для монохромного растра
' 4 - для 16 цветов
' 8 - для 256 цветов
'16 - для 16-разрядных цветов RGB
'24 - для 24-разрядных цветов RGB
'32 - для 32-разрядных цветов RGB

biCompression As Long 'тип сжатия (если BI_RGB, то сжатие не используется)
biSizeImage As Long 'размер изображения в байтах
biXPelsPerMeter As Long 'количество пикселов на метр по-горизонтали, для DIB
biYPelsPerMeter As Long 'количество пикселов на метр по-вертикали, для DIB
biClrUsed As Long 'количество фактически используемых элементов в цветовой таблице DIP
biClrImportant As Long 'количество значащих элементов в таблице DIP
End Type

Реализуем это на практике. Создадим exe-проект, положим на форму Text1 и Command1. Добавим к проекту стандартный модуль и запишем в него объявления наших двух структур BITMAPFILEHEADER и BITMAPINFOHEADER. Можно скопировать прямо с этой страницы. Закроем стандартный модуль. И далее в форме в процедуре командной кнопки напишем код, позволяющий вывести в текстовое поле тип файла в виде десятичного числа 19778 (что соответствует &H4D42 или буквам BM), его размер в байтах, и ширину высоту графического изображения в пикселах.

Private Sub Command1_Click()
'объявим переменные с типами наших структур
Dim bmpInfo As BITMAPINFOHEADER
Dim bmpType As BITMAPFILEHEADER
'откроем файл для двоичного доступа
Open App.Path & "\untitled.bmp" For Binary As #1
'читаем первую структуру (14 байт от начала файла)
Get #1, , bmpType
'читаем вторую структуру (40 байт) с 15-го байта, т.е. с байта, на котором остановилась первая структура
Get #1, , bmpInfo
' закрываем файл и выводим нашу информацию в Text1
Close #1
Text1 = "Тип изображения " & bmpType.bfType & vbCrLf
Text1 = Text1 & "Размер файла=" & bmpType.bfSize & vbCrLf
Text1 = Text1 & "Ширина картинки=" & bmpInfo.biWidth & vbCrLf
Text1 = Text1 & "Высота картинки=" & bmpInfo.biHeight & vbCrLf
End Sub


На всякий случай скачать исходник этого кода можно здесь.

Однако с другими форматами не так все просто и структур для них в VB нету. И с ними придется работать собственными силами. Поэтому потренируемся на BMP, так как это самый простой формат по которому мы уже имеем кучу информации. Давайте реализуем ту же задачу не используя структур BITMAPFILEHEADER и BITMAPINFOHEADER. Не зря же я рисовал в начале таблицу структуры заголовка BMP-файла.
На самом деле все очень просто. Мы просто будем читать необходимые нам байты и обязательно в шестнадцатеричном представлении. Для полной ясности проговорим все пошагово. К исходнику примера приложен файл BMP, который называется "untitled.bmp" размером 80 768 байтов с графическим изображением 157 x 171 пиксель. Каким образом мы получаем эти данные?
Создадим новый exe-проект и положим на форму Text1 (Multiline=True) и Command1. Процедуру определения информации из BMP-файла пишем в командной кнопке.

Private Sub Command1_Click()
'Сначала объявим переменные
Dim FileName As String 'имя открываемого файла
Dim TypeOfFile As String 'тип файл (BM)
Dim b As Byte
Dim RazmW As String 'ширина изображения
Dim RazmH As String 'высота изображения
Dim RazmF As String 'размер файла в байтах
Dim x As Long
'установим имя файла
FileName = App.Path & "\untitled.bmp"
'и откроем его
Open FileName For Binary As #1
'Первым делом нам надо установить, что это файл BMP,
' иначе считываемая информация будет неверна. Первые два байта должны
' содержать символы "BM". Считываем эти 2 байта в переменную TypeOfFile.

TypeOfFile = String(2, " ")
Get #1, , TypeOfFile
'Проверяем, эти ли символы
If TypeOfFile = "BM" Then
'если да, то выводим в Text1 соответствующий текст
Text1 = "BMP-файл" & vbCrLf


Далее читаем размер файла. Он находится в байтах 3, 4, 5 и 6. Однако порядок записи данных в формате BMP таков, что старший байт находится в младшем, т.е нам наши четыре байта надо читать наоборот, с 6-го по 3-й. Сделаем это в цикле.

For
x = 6 To 3 Step -1
Get #1, x, b


С каждым тактом цикла в переменную b будет считываться один байт.Он будет в десятичной системе счисления (0, 1,59,128). Но из этого мы не можем получить число, соответствующее размеру файла. Нам нужны данные в шестнадцатеричной форме. Перевести можно с помощью функции Hex. Но этого недостаточно. Если у нас в байте было число, например 0D, то при переводе оно превратится в D, а это недопустимо, так как полностью исказит результат. Поэтому я здесь применил такую лихую формулу. Смысл ее в том, что в случае, если полученное шестнадцатеричное число состоит из одного знака, мы вперед добавляем ноль.

RazmF = RazmF & String((Len(Hex(b)) - 2) * -1, 0) & Hex(b)

Таким образом наши десятичные числа b преобразуются в строковые шестнадцатеричные и сцепляясь в переменной RazmF образуют строковое шестнадцатиричное число: RazmF = "00" & "01" & "3B" & "80" = "13B80" и это и есть искомый размер файла. Нам надо только перевести его в десятичную систему счисления. Для этого я сделал функцию ConvertDec() и поместил ее в стандартный модуль Модуль1. Подробное описание алгоритма и кода функции можно прочитать в статье "Шестнадцатеричное представление числа. Перевод из шестнадцатеричной системы счисления в десятичную".

Next x
'Переводим число в десятичное и полчаем как раз 80768 байт - размер файла. Выводим это в Text1
Text1 = Text1 & "Размер файла = " & ConvertDec(RazmF) & " bytes" & vbCrLf
'ширину получаем точно также
For x = 22 To 19 Step -1
Get #1, x, b
RazmW = RazmW & String((Len(Hex(b)) - 2) * -1, 0) & Hex(b)
Next x
Text1 = Text1 & "Ширина изображения = " & ConvertDec(RazmW) & " pixels" & vbCrLf
'аналогично получаем и высоту
For x = 26 To 23 Step -1
Get #1, x, b
RazmH = RazmH & String((Len(Hex(b)) - 2) * -1, 0) & Hex(b)
Next x
Text1 = Text1 & "Высота изображения = " & ConvertDec(RazmH) & " pixels" & vbCrLf
Else
' если файл начинается не с BM выводим соответствующий текст
Text1 = "Это не BMP-файл"
End If
Close
End Sub


Вот в общем и вся реализация. Скачать исходник примера можно вверху страницы.
Продолжение практического анализа графических форматов GIF и JPG (JFIF) смотри в следующих статьх.

Copyright © 2005 4us




Сайт создан в системе uCoz