Модераторы: Snowy, Alexeis, MetalFan

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Графический формат BMP, Статья 
:(
    Опции темы
Alexeis
Дата 2.5.2006, 10:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 55
Всего: 459



Давно хотел сам разборраться в этом. Что же это такое на самом деле. Накопил порядка 5-6 источников и попробовал их обобщить.
Хотелось конечно уйти от сложного языка на котором пишут документацию, но повсему вижу, что не совсем получилось.
html версии пока нет, как нибуь потом выложу. Во второй части попробую опиать как это все читается, там же будут примеры.
В общем вот первая ее теоретическая часть.
------------------------------------------------------------------------------------------

    Эта статья про то, что собой представляет графический формат BMP. Хоть это и один из простых форматов, но из-за того, что существует много вариаций этого формата, то не все моменты очевидны.

Введение

    Формат BMP (от слов BitMaP - битовая карта, или, говоря по-русски, битовый массив) является одной из форм представления растровой графики. Проще говоря, изображение представляется в виде матрицы прямоугольных точек, где каждая точка характеризуется тремя параметрами  -  x  координатой, y координатой и цветом. Формат BMP разрабатывался изначально двумя корпорациями Intel и Microsoft, и в то время был одинаков для обеих операционных систем Intel OS/2 Warp и Microsoft Windows 2.x. Однако далее фирма Microsoft расширила формат, расширив структуры (при этом сохранив как обратную, так и прямую совместимость для несжатых разновидностей) и добавив поддержку компрессии. Добавилась поддержка сжатия без потерь PNG и RLE, а также сжатие с потерями JPEG. Казалось бы, JPEG и BMP, совместили несовместимое, однако это только на первый взгляд. На самом деле формат BMP - является  родным не только для операционных систем Windows и OS/2, но и для различных аппаратных устройств (имеется ввиду его аппаратная версия DDB - будет описана далее). Родным в том смысле, что все операции графического ввода - вывода на экран (принтер и на некоторые другие устройства) в конечном итоге осуществляются посредством него (в том или ином его виде). Так вот, поскольку современные принтеры поддерживают прямой вывод изображений в форматах PNG и JPEG на устройство, и была введена их поддержка. Тем самым, обеспечив аппаратный вывод в рамках единого формата. Под вывод BitMaP-в, оптимизируется архитектура большинства видеоадаптеров.     Для чтения и вывода в ОС Windows, предусмотрено много специальных функций и структур API (библиотека gdi32.dll и gdiplus.dll), которые помогают производить все необходимые операции на достаточно высоком логическом уровне. Delphi - еще более упрощает работу предоставляя нам класс надстройку над API - TBitMap, который здесь рассматриваться не будет поскольку хорошо описан во многих источникахВ заключение к разделу хочется развеять одну неопределенность. Windows поддерживает работу с тремя битмапоподобными форматами *.bmp, *.rle, *.dib. *.rle - это сжатый битмап (как это следует из названия в формате RLE), полностью совместимый с bmp. *.dib - битмап версий Windows более чем 3.0. *.bmp - изначально предполагался быть совместимым с  Windows 2.x, в последствии вероятно от этого отказались и сделали его мультиформатным. Данные форматы внутренне ни чем не друг от друга не отличаются (т.е. являются, по сути, псевдонимами *.bmp) и были введены для явного указания формата сжатия.
Осознали значимость этого формата J, теперь приступим к делу.

Аппаратно-зависимые и аппаратно-независимые битовые карты (Device-Dependenent  and Device-Independent bitmap - DDB и DIB)

    Аппаратно-зависимые или DDB битмапы используются windows для хранения изображений в памяти различных графических устройств (в частности в видеопамяти). Фактически такой битмап представляет собой урезанную версию аппаратно-независимого. Его данные формируются таким образом, чтобы соответствовать конкретному графическому режиму, кроме того, такой битмап содержит упрощенный заголовок. Например, для старого 16 цветного EGA/VGA видеоадаптера, такой битмап будет представлять собой 3 цветовые матрицы (для каждого из цветов), аппаратно-независимый битмап будет содержать всего одну матрицу разрядностью 4бита на пиксель.
    Поскольку структура DDB битмапа меняется в зависимости от устройства к устройству, то он, как правило, создается прямо в памяти и не сохраняется в файл. Для сохранения в файл DDB конвертируется в DIB (т.е. аппаратно-независимый). В настоящее время графические ускорители оптимизируются под работу с DIB - универсальность в ущерб производительности (на самом деле эти потери незначительны). DDB битмап далее не будет описываться. Часто в литературе говорят битмап а подразумевают DIB (и наоборот) это не является грубой ошибкой.

Структура формата

    Перед описанием структуры уточню, что структура битмапа в оперативной памяти повторяет файловую структуру, и все что верно для файла верно и для его образа в памяти, но неверно для DDB. Все структуры (записи) взяты из windows.pas с измененными именами, константы взяты из заголовочных файлов Borland C++ windows.h, wingdi.h.
Файл всегда состоит из трех частей.
1)    Файловый заголовок - всегда структура  TBitMapFileHeader - это единственная общая структура для всех типов и версии.
2)    Затем для Windows версии 2.x и OS/2 идет структура (запись) TBitmapCoreInfo. Для всех остальных версий это TBitmapInfo
3)    Массив данных - структура которого весьма разнообразна.

Теперь подробнее о каждой.

В начале стоит заголовок файла (TBitMapFileHeader). Он описан следующим образом:
  TBitMapFileHeader = packed record
    bfType: Word;
    bfSize: DWORD;
    bfReserved1: Word;
    bfReserved2: Word;
    bfOffBits: DWORD;
  
end;
   bfType - два символа определяющие тип файла. Могут быть следующие варианты:
'BM'  - Windows 3.1x, 95, NT, …
'BA'   - OS/2 Bitmap Array, Windows 2.x
'CI'    - OS/2 Color Icon
'CP'   - OS/2 Color Pointer
'IC'    - OS/2 Icon
'PT'    - OS/2 Pointer  
Программисты Windows - вероятно встречали только 'BM' (от слова BitMap, как вы уже, наверное, догадались). Не смотря на это, я полагаю, будет не лишним проверять, что за битмап нам передали (чтоб потом “не радовать” пользователя неожиданными ошибками при чтении правильных, с точки зрения, формата битмапов).
   bfSize - это размер самого файла в байтах. В идеале все программы для того, чтобы убедиться, что перед ними действительно правильный bmp, должны, проверить, что    bfType содержит "BM" (без кавычек), а, во-вторых, что bfSize равен действительному размеру файла. Хотя далеко не все программы используют это значение, оно должно быть верным, так как - это позволит нам убедиться в том, что файл был скопирован (или скачен) целиком.
   bfReserved1 и bfReserved2 зарезервированы и должны быть нулями. Эти значение тоже желательно проверить, ведь в будущем они могут быть использованы для расширения формата. Естественно, что ваша программа не сможет их (такие файлы) прочитать, поэтому, узнав о ненулевых значениях можно правильно проинформировать пользователя, тем самым сэкономить его время.
   bfOffBits - это один из самых важных полей в этой структуре. Он показывает, где начинается сам битовый массив относительно начала файла, который и описывает картинку. Несмотря на то, что это значение можно определить по концу таблицы цветов, рекомендуется использовать именно это значение, для совместимости с  возможными новыми вариациями формата.
Для Windows версии 2.x и OS/2 идет структура (запись) TBitmapCoreInfo
  TBitmapCoreInfo = record
    bmciHeader: TBitmapCoreHeader;
    bmciColors: 
array[0..0 of TRGBTriple;
    Reserved  : 
array[0..0 of Char;
  
End;

Где TBitmapCoreHeader = packed record
    bcSize    : DWORD;
    bcWidth   : Word;
    bcHeight  : Word;
    bcPlanes  : Word;
    bcBitCount: Word;
  
end

а bmciColors -  цветовая палитра в формате

  TRGBTRiple = packed record
    rgbtBlue : Byte;
    rgbtGreen: Byte;
    rgbtRed  : Byte;
  
end

array0..0 - не следует понимать буквально, просто количество цветов может быть различным 
Размер заголовка TBitmapCoreHeader хранится вbcSize и для OS/2 1.x совпадает с приведенным выше и равен 12 байтам  - $0Ch.  Для OS/2 2.x он занимает 15 байт? (вероятно в источнике ошибка - там указано 240 байт т.е. $F0h, вероятно имелось ввиду $0Fh - поскольку данная структура не содержит ни таблицы цветов, ни самих данных).
В Windows 3.x, 95, NT возможны две версии измененной TBitmapCoreHeader. Первая TBitmapInfoHeader структура занимает 40 байт - $28h, вторая TBitmapV4Header - занимает 108 байт - $6Ch (будут описаны ниже). И, наконец, для Windows 98/Me, Windows 2000/XP - это TBitmapV5Header размером 124 байта - $7Сh (также будет описана ниже). Все размеры я указал не ради порядка, а для правильной идентификации версии, поскольку форматом не предусмотрено ни одного поля идентифицирующего версию. Чтоб не напугать новичков обилием версий замечу, что для чтения подавляющего большинства битмапов достаточно знать и использовать один лишь TBitmapInfoHeader, при этом все современные приложения правильно и корректно будут производить с ним все допустимые операции.
    Все структуры являются расширением первой TBitmapCoreInfo - поэтому в последующих структурах будут описаны только новые поля.
bcWidth и bcHeight - ширина и высота изображения в пикселях. В последующих структурах это будут уже двойные слова (из-за этого и нет полной совместимостей новых структур с TBitmapCoreHeader). Значение bcHeight (для версий Windows 3.x, 95, NT) может быть отрицательным. В этом случае модуль bcHeight определяет действительную высоту, а строки изображения читаются в обратном порядке, т.е. сверху вниз (обычно снизу вверх). Кроме того, такие изображения всегда несжатые (т.е. BI_RGB или BI_BITFIELDS).
bcPlanes - задает количество плоскостей или цветовых слоев (помните я упоминал что DDB - могут иметь 3 цветовые плоскости например R G B или C M Y- связанные с особенностями графического устройства). Сохраняемая версия битмапа т.е. DIB поддерживает пока одну общую для всех цветов плоскость, разрядность цвета этой плоскости задается bcBitCount. Попытки установить значения отличные от единицы вызывают ошибку при передаче структуры api функциям.
bcBitCount - определяет разрядность цвета. Допустимы следующие варианты
0 - изображение только PNG и BMP допустимо только версии Windows 98/Me, Windows 2000/XP
1 - монохромное изображение (поддерживается всеми версиями). Каждый пиксель представлен одним битом данных, т.е. один байт содержит информацию о цвете 8 последовательно идущих пикселей. Цвет первого пикселя определяется состоянием старшего бита первого байта (и так далее), если его значение равно единице, то цвет пикселя будет определяться первой записью таблицы цветов (считается от нуля). Вообще значение цвета определяется по RGB (для BI_RGB версии) составляющим, таблицы цветов, по индексу. 
4 - 16-ти цветное изображение. Каждый пиксель представлен 4 битами (поддерживается всеми версиями).
8 - изображения с количеством цветов до 256. 1 байт - 1 пиксель (поддерживается всеми версиями)
16 - поддерживается не менее чем Windows 95, и является достаточно редким. Количество цветов зависит от версии Windows и выбранного формата сжатия и может быть как 2 в 16 либо 2 в 15 степени. Это самый запутанный вариант. Начнем с того, что он беспалитровый, то есть каждые два байта (одно слово WORD) в растре однозначно определяют один пиксель. Но вот что получается: битов-то 16, а компонентов цветов - 3 (Красный, Зеленый, Синий). А 16 никак на 3 делиться не хочет. Поэтому здесь есть две варианта. Первый - использовать не 16, а 15 битов, тогда на каждую компоненту цвета выходит по 5 бит. Таким образом, мы можем использовать максимум 2 в 15 = 32768 цветов, и получается тройка R-G-B = 5-5-5. Но тогда за зря теряется целый бит из 16. Но так уж случилось, что наши глаза среди всех цветов лучше воспринимают зеленый цвет, поэтому и решили этот один бит отдавать на зеленую компоненту, то есть тогда получается тройка R-G-B = 5-6-5, и теперь мы может использовать 2 в 16 = 65536 цветов. Но что самое неприятное, что используют оба варианта. В MSDN предлагают для того, чтобы различать, сколько же цветов используется, заполнять этим значением поле biClrUsed (будет описано ниже) из структуры BITMAPINFOHEADER. Чтобы выделить каждую компоненту надо использовать следующие маски. Для формата 5-5-5: $001Fh для синей компоненты, $03E0h для зеленой и $7C00h для красной. Для формата 5-6-5: $001Fh - синяя, $07E0h - зеленая и $F800h красная компоненты соответственно.
24 - а это самый простой формат. Количество цветов 2 в 24 степени. Здесь 3 байта определяют 3 компоненты цвета. То есть по компоненте на байт. Просто читаем по структуре RGBTRIPLE(для Windows версии 2.x и OS/2) и используем его поляrgbtBluergbtGreenrgbtRed. Они идут именно в таком порядке (поддерживается всеми версиями).
32 - Здесь 4 байта определяют 3 компоненты, т.е. по-прежнему количество цветов 2 в 24 степени. Но, правда, один байт не используется. Его можно отдать, например, для альфа-канала (прозрачности). Поддерживается не менее чем Windows 95
На этом описание структур совместимых с Windows версии 2.x и OS/2 заканчиваем и переходим к современным.

Это сообщение отредактировал(а) Alexeis - 10.5.2007, 18:18


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Alexeis
Дата 2.5.2006, 11:01 (ссылка) |  (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 55
Всего: 459



    В Windows 3.x и старше на замену TBitmapCoreInfo приходит структура TBitMapInfo описанная следующим образом

  TBitMapInfo = packed record
    bmiHeader : TBitmapInfoHeader;
    bmiColors : 
array[0..0  of TRGBQuad;
  
end;
Как вы, вероятно, заметили, на смену TBitmapCoreHeader пришел TBitmapInfoHeader, или его совместимые расширенные версии TBitmapV4Header и TBitmapV5Header

 TBitmapInfoHeader = packed record
    biSize          : DWORD;
    biWidth         : Longint;
    biHeight        : Longint;
    biplanes        : Word;
    biBitCount      : Word;
    biCompression   : DWORD;
    biSizeImage     : DWORD;
    biXPelsPerMeter : Longint;
    biYPelsPerMeter : Longint;
    biClrUsed       : DWORD;
    biClrImportant  : DWORD;
  
end;

  
//--вспомогательная структура
  
  TCIEXYZTriple = 
packed record
    ciexyzRed   : TCIEXYZ;
    ciexyzGreen : TCIEXYZ;
    ciexyzBlue  : TCIEXYZ;
  
end;

  
TBitmapV4Header = packed record
    bV4Size          : DWORD;
    bV4Width         : Longint;
    bV4Height        : Longint;
    bV4Planes        : Word;
    bV4BitCount      : Word;
    bV4V4Compression : DWORD;
    bV4SizeImage     : DWORD;
    bV4XPelsPerMeter : Longint;
    bV4YPelsPerMeter : Longint;
    bV4ClrUsed       : DWORD;
    bV4ClrImportant  : DWORD;
    bV4RedMask       : DWORD;
    bV4GreenMask     : DWORD;
    bV4BlueMask      : DWORD;
    bV4AlphaMask     : DWORD;
    bV4CSType        : DWORD;
    bV4Endpoints     : TCIEXYZTriple;
    bV4GammaRed      : DWORD;
    bV4GammaGreen    : DWORD;
    bV4GammaBlue     : DWORD;
  
end;

    
  
TBitmapV5Header = packed record
    bV5Size          : DWORD;
    bV5Width         : Longint;
    bV5Height        : Longint;
    bV5Planes        : Word;
    bV5BitCount      : Word;
    bV5Compression   : DWORD;
    bV5SizeImage     : DWORD;
    bV5XPelsPerMeter : Longint;
    bV5YPelsPerMeter : Longint;
    bV5ClrUsed       : DWORD;
    bV5ClrImportant  : DWORD;
    bV5RedMask       : DWORD;
    bV5GreenMask     : DWORD;
    bV5BlueMask      : DWORD;
    bV5AlphaMask     : DWORD;
    bV5CSType        : DWORD;
    bV5Endpoints     : TCIEXYZTriple;
    bV5GammaRed      : DWORD;
    bV5GammaGreen    : DWORD;
    bV5GammaBlue     : DWORD;
    bV5Intent        : DWORD;
    bV5ProfileData   : DWORD;
    bV5ProfileSize   : DWORD;
    bV5Reserved      : DWORD;
  
end;

    Цветовая палитра (если имеется) состоит из четырех полей, причем ее структура не зависит от разрядности цвета.

  TRGBQuad = packed record
    rgbBlue     : Byte;
    rgbGreen    : Byte;
    rgbRed      : Byte;
    rgbReserved : Byte;
  
end;
Теперь рассмотрим все новые поля (тех, что не было в TBitmapCoreHeader) по очереди.
   biCompression - тип сжатия. О сжатии уже упоминал ранее, теперь рассмотрим его более подробно. Как вы знаете сжатие позволяет уменьшить размер изображения. Однако формат bmp создавался как несжатый и до сих пор сжатие в bmp встречается крайне редко. Здесь возможны 6 вариантов:
Const
  BI_RGB       = $00000000;
  BI_RLE8      = 
$00000001;
  BI_RLE4      = 
$00000002;
  BI_BITFIELDS = 
$00000003;
  BI_JPEG      = 
$00000004;
  BI_PNG       = 
$00000005;

Из них BI_RGB и BI_BITFIELDS являются несжатыми. Вопрос сжатых битмапов  слишком сложный чтоб рассмотреть его в рамках этой статьи, поэтому подробно рассмотрен не будет. Ручная распаковка таких форматов сложна, и мало полезна, поскольку Windows берет на себя эту задачу. Форматы сжатия BI_RLE8, BI_RLE4, BI_BITFIELDS были введены в Windows начиная с 3 версии и были дополнены  BI_JPEG  и BI_PNG в Windows 98/Me, Windows 2000/XP. Форматы BI_RLE8, BI_RLE4 используются для сжатия только 8-ми и 4-х бит на пиксель изображений соответственно. Формат BI_BITFIELDS - представляет собой беспалитровый формат цвета. В нем цвета задаются при помощи 3-х  четырех битовых масок, идущих сразу после структуры (по маске на каждый цвет). Примеры таких масок приведены уже были в описании цвета 16 бит на пиксель. Для 32 бита на пиксель маски для синего, зеленого и красного цветов имеют вид $000000FFh$0000FF00h$00FF0000h соответственно. Как же можно выделить цвет при помощи такой маски, да легко! Например, пусть нужно выделить зеленый цвет из двойного слова 32 бит на пиксель. 
G := (Data and $0000FF00hshr 8; или в общем 

B := (Data and BlueMask)   shr (0 * biBitCount);
G := (Data and GreenMask) shr (1 * biBitCount);
G := (Data and RedMask)    shr (2 * biBitCount);

Такой формат можно использовать только для разрядности цвета 16 и 32 бита на пиксель.
Данные представлены не индексами в палитре, а значениями цвета.
Формат BI_RGB также является несжатым. Отличие от  BI_BITFIELDS, состоит в том, что он поддерживает изображения с разрядностью цвета biBitCount от 1 до 32 бит на пиксель, при этом битмапы с biBitCount от 1 до 8 обязаны содержать палитру, а значения цветовых данных определяют индекс цвета в палитре, битмапы с biBitCount от 16 до 32 как правило не содержат палитры (но формат допускает присутствие палитры и в этом случае), значения цветовых данных определяют цвет пикселя (даже при наличии палитры). Подробнее см. biClrUsed. 
 biSizeImage обозначает размер картинки в байтах. Если изображение несжатое (то есть предыдущее поле установлено в BI_RGB), то это значение не используется - рекомендовано устанавливать в ноль (но другие значения не запрещены). Для сжатых изображений (biCompression  имеет значение BI_JPEG или  BI_PNG)  - biSizeImage определяет размер буфера данных (блока данных).
biXPelsPerMeter и biYPelsPerMeter обозначают соответственно горизонтальное и вертикальное разрешение (в пикселях на метр) рекомендуемое для установки в конечном устройстве, на которое будет выводиться битовый массив. Приложение может использовать это значение для того, чтобы выбирать из группы ресурсов наиболее подходящий битовый массив для нужного устройства. Напомню, что формат bmp - это по сути аппаратно-независимый растр, то есть универсальный для всех устройств, отличительной особенностью которого является то, что картинка будет выглядеть одинаково вне зависимости от того, рисуется она на экране монитора или печатается на принтере. Но вот разрешение у устройств разное, и именно для того, чтобы выбрать наиболее подходящую картинку из имеющихся и используют эти параметры.
    biClrUsed определяет количество используемых цветов из таблицы. Если это значение равно нулю, то либо в растре используется максимально возможное количество цветов, которые разрешены значением biBitCount, либо изображение сжато. Если biClrUsed не нуль, а biBitCount меньше 16, то biClrUsed определяет текущее число цветов палитры. Если biBitCount больше или равно 16 (количество цветов более 256), то biClrUsed определяет размер таблицы цветов, используемой для оптимизации текущей системной палитры. Такая таблица цветов, как уже упоминалось, присутствует достаточно редко, но может привести к появлению ошибок при чтении цветового массива, если программа не использует значение bfOffBits для определения начала битового массива, а вместо этого интерпретирует байты, следующие непосредственно за TBitmapInfoHeader  (или ее расширенного варианта), как начало растра.
    biClrImportant - это количество важных для изображения цветов. Если это значение равно 0 (как это обычно и бывает), то все цвета считаются важными. Это поле было введено поддержки устройств с малым количеством цветов. Например, если на устройстве, поддерживающем только 256 цветов, отображаются более одного изображения с числом цветов 256 с разными палитрами, то естественно, что для правильного отображения суммарной картинки потребуется общая палитра с числом цветов превышающим 256. В этом случае в общую палитру вводят вначале важные цвета (в палитре они должны идти первыми) по возможности всех изображений, затем вносят другие. Если нужного цвета в палитре не окажется, то он будет заменен, на наиболее близкий.
На этом описание основных полей TBitmapInfoHeader закончено, дальше пойдет описание расширенных полей (TBitmapV4HeaderTBitmapV5Header), т.е. не обязательных для чтения. Эта информация нигде, кроме как в MSDN, не дается.  

Это сообщение отредактировал(а) Alexeis - 10.5.2007, 18:32


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Alexeis
Дата 2.5.2006, 11:02 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 55
Всего: 459



bV4RedMask, bV4GreenMask, bV4BlueMask, bV4AlphaMask - Маски заменяющие палитру при сжатии bV4Compression (аналог biCompression) установленном в  BI_BITFIELDS - были описаны выше. bV4AlphaMask - дополнительная маска используемая при bV4BitCount равном 32 и служит для описания альфа состовляющей цвета. 
bV4CSType - Определяет цветовое пространство DIB. Цветовое пространство это некоторое логическое описание цвета в виде вектора в 3-х или 4-х мерном пространстве, где каждое измерение - интенсивность одного из цветов или альфа-канала. Допустимые значения -
  LCS_CALIBRATED_RGB      = #0#0#0#0;
  LCS_sRGB                = 
'sRGB';
  LCS_WINDOWS_COLOR_SPACE = 
'Win ';
  PROFILE_LINKED          = 
'LINK';
  PROFILE_EMBEDDED        = 
'MBED';
 
последние 2 варианта доступны только в пятой версии заголовка
LCS_CALIBRATED_RGB  - указывает, что координаты конца вектора цвета и гамма составляющая идут в смежных полях. О гамма составляющей см. ниже.
LCS_sRGB - указывает на то, что будет использоваться цветовое пространство sRGB.
LCS_WINDOWS_COLOR_SPACE - 
указывается, что используется стандартное для Windows цветовое пространство, обычно соответствует LCS_sRGB.
PROFILE_LINKED - говорит о том, что данные специальной профильной области не содержатся в изображении, хранятся в дополнительном файле имя которого хранится в bV5ProfileData. Профиль должен соответствовать ICC для интерфейса ICM 2.0. Профили были введены начиная с Windows 98, для обеспечения более точной цветопередачи. В этом режиме вектор цвета и гамма игнорируются. 
PROFILE_EMBEDDED  -  говорит о том, что данные специальной профильной области расположены в памяти указатель на которую находится в поле bV5ProfileData. Значение bV5ProfileData - обычно не определено до загрузки изображения в память.  В этом режиме вектор цвета и гамма игнорируются.  

bV4Endpoints - 
структура типа TCIEXYZTriple определяющая x, y, z - координаты соответствующие трем цветам  - красному, зеленому и синему для логического цветового пространства битмапа, определенного константой LCS_CALIBRATED_RGB.
bV4GammaRed
bV4GammaGreenbV4GammaBlue - параметр, характеризующий кривую отклика оттенка цвета (красного, зеленого и синего соответственно). Используется только при bV4CSType установленном в LCS_CALIBRATED_RGB.
bV5Intent - 
тип тонирования (intent) изображения. 
Возможные варианты:
  LCS_GM_ABS_COLORIMETRIC = $00000008;
  LCS_GM_BUSINESS         = 
$00000001;
  LCS_GM_GRAPHICS         = 
$00000002;
  LCS_GM_IMAGES           = 
$00000004;
  
Данные параметры определяют каким образом цветовое пространство будет конвертироваться в палитру цветов. Более подробно см. Image Color Management Version 2.0 в MSDN
    bV5ProfileData - смещение в битмапе. Для bV5CSType установленном в PROFILE_LINKED указывает на нультерминатную строку хранящую имя файла цветового профиля. Строка может содержать полный путь к файлу профиля включая также сетевой путь. Для bV5CSType установленном в PROFILE_EMBEDDED это смещение собственно на цветовой профиль. Смещение отсчитывается от конца структуры TBitmapV5Header (данные растра не следуют сразу за таблицей цветов). Значение bV5ProfileData имеет смыл только для загруженного в память битмапа и должно быть установлено только после загрузки. Использование таких цветовых профилей допустимо только для файлов, у которых размер структуры (поле bV5Size) TBitmapInfoHeader, равно значению SizeOf(TBitmapV5Header) т.е. 124 байтам.
  
    После структуры TBitmapInfoHeader следует массив из структур TRGBQuad хранящий таблицу цветов. Такой массив может и отсутствовать в зависимости от битности данных. Каждый элемент характеризует один из используемых цветов. Четвертое поле должно быть установлено всегда  в нуль. Рекомендуется записывать цвета в порядке их важности см. biClrImportant. Как уже упоминалось для 5-й версии заголовка, в памяти цветовая палитра может идти не сразу за структурой TBitmapInfoHeader а с некоторым смещением.

    Растр - представляет собой блок данных каждый байт, которого интерпретируется согласно формату сжатия и битности данных. Для biCompression  BI_JPEG, BI_PNG  структура блока не тривиальна (определяется форматом). Для  BI_RLE8, BI_RLE4 этот блок разбивается на множество чередующихся подблоков различного размера, в каждом из которых хранится либо последовательность цветов с неповторяющимися значениями, либо число повторений некоторого цвета, либо маркеры конца строки или блока данных. Для BI_RGB иBI_BITFIELD это строки изображения, записанные в порядке снизу вверх (если значение высоты битмапа отрицательное, то наоборот). Каждая строка выравнивается на границу двойного слова (4 байта). Лишние младшие биты могут принимать произвольные значения, но обычно устанавливаются в нуль. Размер растра в байтах можно вычислить следующим образом: 
(((((((biWidth  * biBitCount + 7) shr 3) + 3) shr 2) shl 2) * abs(biHeight) + 3) shr 2) shl 2.

Конец первой части  
 



Это сообщение отредактировал(а) Alexeis - 14.5.2008, 16:18


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Alexeis
Дата 17.5.2006, 14:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 55
Всего: 459



Наконец дописал вторую часть
 Формат BMP (часть вторая)

В этой части попробуем написать программу, работающую с BMP файлами.
Как вы знаете, несжатые битмапы занимают на диске достаточно много места, особенно много занимают изображения в формате 24 бита на пиксель. Фотографии сжимают в jpeg.
Однако такие изображения как чертежи и графики сжимать с потерями, конечно, нельзя.
Очень хорошее сжатие без потерь дает формат png (в настоящее время он очень распространен). Это довольно сложный формат и в рамках этой статьи не рассматривается. Оказывается, некоторые изображения можно в несколько раз уменьшить в размерах не прибегая к другим форматам, а просто уменьшив число байт занимаемых одним пикселем. Многие, наверное, пробовали сохранять изображение True Color в формате 256 цветов – результат жуткое искажение цвета, даже если действительное  число цветов не превышало 256. Дело в том, что программы типа Microsft Paint создают свою собственную палитру на 256 цветов и интерполируют исходное изображение при помощи имеющихся цветов. Метод, конечно, универсальный, но  совершенным его не назовешь.
Эта программа будет считать количество цветов реально использованных в изображении и если их число не более 256, то создавать новый палитровый битмап с числом цветов 2, 16, 256 – в зависимости от числа использованных цветов. Чтобы сделать программу еще более универсальной, я добавил поддержку беспалитровых изображений не только на 24 бита на пиксель, но и 16 и 32. Как показал мой печальный опыт изображения в формате 16 бит на пиксель не могут быть однозначно прочтены, поскольку программы обычно не оставляют ни каких заметок (хотя MSDN этого требует) какую схему цветов они использовали (5-5-5 или 5-6-5). По умолчанию принимается старый формат 5-5-5. Кроме того введена нестрогая проверка цветов – если хоть один цвет в палитре имеет значение, превышающее 2 в 15 степени, то такое изображение рассматривается по схеме 5-6-5. Буду очень благодарен тому, кто расскажет, как более строго можно отличить такие форматы.
Код снабжен подробными комментариями, а потому дальнейшее описание думаю будет лишним.

Добавлено @ 14:24 
Код в один пост не поместился так что разбил на два
Код

unit Unit1;
 //-----------------------------------------------------+//
 //               Дата создания 13.05.06                 //
 //                                                      //
 //    Программа для уменьшения размера битмапа          //
 //    за счет приведения битмапа из беспалитрового      //
 //    в палитровый вариант, когда число использованных  //
 //    цветов не превышает 256.                          //
 //    Является приложением к статье о формате BMP       //
 //                                                      //
 //------------------------------------------------------//

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, ExtDlgs, BitmapHeaders, ComCtrls;

type
  //Более удобный эквивалент оригинальной структуры
  TBitMapFileHeader = packed record
    bfType: array[0..1] of Char;
    bfSize: DWORD;
    bfReserved1: Word;
    bfReserved2: Word;
    bfOffBits: DWORD;
  end;

  PBitMapFileHeader = ^TBitMapFileHeader;
  PBitmapInfoHeader = ^TBitmapInfoHeader;
  
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    Image1: TImage;
    Button2: TButton;
    Label3: TLabel;
    Bevel1: TBevel;
    OpenPictureDialog1: TOpenPictureDialog;
    ProgressBar1: TProgressBar;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    FH        : PBitMapFileHeader;
    BIH       : PBitmapInfoHeader;
    Palette   : array[0..255] of Cardinal; //Цветовая палитра
    NColors   : integer;                   //Число цветов битмапа
    OldDelta  : Integer;                   //Число байт необходимых для выравнивания
    bmp       : TFileStream;               //на границу 4х байт исходного битмапа
    mem       : TMemoryStream;
    memResult : TMemoryStream;
    Function TestBmp(IgnoreNonCritical : Boolean) : Integer;
                                            //Проверка на пригодность битмапа
    procedure Convert(oldFH  : TBitMapFileHeader;
                      oldBIH : TBitmapInfoHeader);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
           
uses math;

Const
  NoError            = 0;    //Коды ошибок возможных при загрузке битмапа
  FileReadError      = 1;
  OS2BMP             = 2;
  UnknownBitmap      = 3;
  PossibleCorrupt    = 4;
  PossibleExtended   = 5;
  CompressedBitMap   = 6;
  NotHighOrTrueColor = 7;
  MoreThan256Colors  = 8;

  //Проверка Заголовка TBitmapInfoHeader на правильность
  //и битмапа на пригодность к сжатию без потерь
Function TestInfoHeader(Header : TBitmapInfoHeader) : Integer;
Begin
  Result := NoError;

  if Header.biPlanes <> 1  //число плоскостей должно быть равно единице
  then
    Begin
      Result := UnknownBitmap;
      exit;
    end;
                           //Проверка действительно ли High или True Color
  if not (Header.biBitCount in [16, 24, 32])
  then
    Begin
      Result := NotHighOrTrueColor;
      exit;
    end;
                           //Проверка действительно ли несжатое изображение
  if not (Header.biCompression in [BI_RGB, BI_BITFIELDS])
  then
    Begin
      Result := CompressedBitMap;
      exit;
    end;
end;

    //Тестируем битмап(целиком) на пригодность
Function TForm1.TestBmp(IgnoreNonCritical : Boolean) : Integer;
var
  HSize : DWORD;

Begin
  Result := NoError;
  bmp    := TFileStream.Create(OpenPictureDialog1.FileName, fmOpenRead);
  bmp.Seek(0, soFromBeginning);
  Try
    GetMem(FH, SizeOf(TBitMapFileHeader)); //Выделяем память под структуру 
                                           //файлового заголовка
    bmp.ReadBuffer(FH^, SizeOf(TBitMapFileHeader)); //Читаем заголовок

    if (FH.bfType = 'BA') or     //Проверяем не является ли битмап
       (FH.bfType = 'CI') or     //битмапом стандарта OS/2
       (FH.bfType = 'CP') or
       (FH.bfType = 'IC') or
       (FH.bfType = 'PT')
    then
      Begin
        Result := OS2BMP;
        exit;
      end;

    if FH.bfType <> 'BM'         //Windows битмап
    then
      Begin
        Result := UnknownBitmap;
        exit;
      end;

    if bmp.Size <> FH.bfSize     //Правельно ли указан размер битмапа
    then
      Begin
        Result := PossibleCorrupt;
        if not IgnoreNonCritical
        then
          exit;
      end;

    if (FH.bfReserved1 <> 0) or   //Если резрвные поля не нулевые возможно
       (FH.bfReserved2 <> 0)      //мы имеем дело с новым стандартом
    then                          //на настоящий момент
      Begin
        Result := PossibleCorrupt;
        if not IgnoreNonCritical
        then
          exit;
      end;

    //Проверяем не является ли битмап битмапом стандарта OS/2

    bmp.ReadBuffer(HSize, SizeOf(HSize)); //читаем размер заголовка битмапа
    Case HSize                            //12 - OS/2 version 2.x
    of                                    //64 - OS/2 version 3.x
      12, 64 :  Begin
                  Result := OS2BMP;
                  exit;
                end;

      40, 108,                                 //40, 108, 124 - Windows bitmap
      124..255 :  Begin                        //Windows 3.х-XP
                    if (HSize in [125..255])
                    then
                      Begin                        //125..255 - возможно в новые
                        Result := PossibleExtended;//версии будут иметь больший размер
                        
                        if not IgnoreNonCritical   //Возможно в будущем сохранится
                        then                       //совместимость
                          exit;
                      end;
                                                   //возвращаемся на SizeOf(HSize)
                    bmp.Seek(-SizeOf(HSize), soFromCurrent); //байт для чтения 
                                                            // всей структуры
                    GetMem(BIH, HSize);            //Выделяем память - столько
                                                   //сколько структура занимает
                                                   //в файле
                    bmp.ReadBuffer(BIH^, HSize);   //Читаем структуру и рассматриваем
                    Result := TestInfoHeader(BIH^); //ее как TBitmapInfoHeader.
                  end;                            //Дополнительные поля (если есть)
      else                                        //игнорируем за ненадобнотью
        Begin
          Result := UnknownBitmap;                //Любые другие значения размера
          exit;                                   //заголовка считаем неверными
        end;
    end;

  except
    Result := FileReadError;                     //Ошибка чтения из файла
    exit;
  end;
end;
 


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Alexeis
Дата 17.5.2006, 14:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 55
Всего: 459



Код

       //Процедура конвертирования изображения
procedure TForm1.Convert(oldFH  : TBitMapFileHeader;
                         oldBIH : TBitmapInfoHeader);
var
  FH            : TBitMapFileHeader;
  BIH           : TBitmapInfoHeader;
  b1, b2        : Byte;             //Служат для набивки байта битами смежных пикселов
  i, j, k, l, m : integer;          //Переменные циклов
  DataSize      : Integer;          //Размер растра
  LineSize      : Integer;          //Размер строки изображения
  Delta         : Integer;          //Число дополнительных байт выравнивания
  pixInByte     : Byte;             //Число пикселей помещающихся в байте
  ReadPix       : Pointer;          //указтель на переменную в которую читается
                                    //цвет пикселя (размер 2, 3 или 4 байта)
  OldPixSize    : Integer;          //Размер (в байтах) исходного пикселя
  PaletteColor  : TRGBQuad;         //Элемент палитры (один цвет)
  DW            : DWORD;            //для временного хранения данных двойного слова
  tocopy        : Integer;          //Число байт растра еще не скопированных

  function is16bitPattern : Boolean; //ненадежный вариант позволяющий отличить
   var                               //битмап 5-5-5 от 5-6-5
    i : byte;                        //в случае если интенсивность красной 
  begin                              //состовляющей ни одного из пикселей формата 
    Result := false;                 //5-6-5 не превысит 15 - дает ложный результат
    For i := 0 to 255
    do
      if palette[i] > High(SmallInt)
      then
        Begin
          Result := True;
          break;
        end;
  end;

  function Get15RGBQuad(C : Cardinal) : TRGBQuad;
  const
    BlueMask  = $001F;                 //Создание цвета палтры из цвета
    GreenMask = $03E0;                 //пикселя формата 5-5-5
    RedMask   = $7C00;
    GreenShift= 5;
    RedShift  = 10;

  Begin
     Result.rgbBlue     := (C and BlueMask)    shl 3;
     Result.rgbGreen    := ((C and GreenMask)  shr GreenShift) shl 3;
     Result.rgbRed      := ((C and RedMask)    shr RedShift)   shl 3;
     Result.rgbReserved := 0;
  end;

  function Get16RGBQuad(C : Cardinal) : TRGBQuad;
  const
    BlueMask  = $001F;
    GreenMask = $07E0;                  //Создание цвета палтры из цвета
    RedMask   = $F800;                  //пикселя формата 5-6-5
    GreenShift= 5;
    RedShift  = 11;
    
  Begin
     Result.rgbBlue     := (C and BlueMask) shl 3;
     Result.rgbGreen    := ((C and GreenMask)  shr GreenShift) shl 2;
     Result.rgbRed      := ((C and RedMask)    shr RedShift) shl 3;
     Result.rgbReserved := 0;
  end;

  function Get24RGBQuad(C : Cardinal) : TRGBQuad;
  const
    BlueMask  = $000000FF;              //Создание цвета палтры из цвета
    GreenMask = $0000FF00;              //пикселя формата 8-8-8 или 8-8-8-8
    RedMask   = $00FF0000;
    GreenShift= 8;
    RedShift  = 16;
    
  Begin
     Result.rgbBlue     := (C and BlueMask);
     Result.rgbGreen    := (C and GreenMask)  shr GreenShift;
     Result.rgbRed      := (C and RedMask)    shr RedShift;
     Result.rgbReserved := 0;
  end;

  
Begin
  if mem = nil
  then
    exit;

  FH.bfType      := 'BM';                //Заполняем поля файловой структуры
  FH.bfReserved1 := 0;                   //нового битмапа
  FH.bfReserved2 := 0;
                                         //Копирум все поля структуры
  BIH := oldBIH;                         //TBitMapInfoHeader
  Case NColors
  of
     0  ..2 : BIH.biBitCount := 1;       //Определем необходимое число бит на пиксель
     3 ..16 : BIH.biBitCount := 4;       //для найденного числа цветов
    17..256 : BIH.biBitCount := 8;
  end;
  BIH.biCompression := BI_RGB;           //Несжатый палитровый формат
  BIH.biClrUsed     := Round(Intpower(2, BIH.biBitCount)); //Число цветов палитры

  LineSize := (BIH.biWidth * BIH.biBitCount) div 8; //Длина строки битмапа(в байтах)
  if ((BIH.biWidth * BIH.biBitCount) mod 8) <> 0
  then                                              //Выравнивание строки на границу
    inc(LineSize);                                  //одного байта

  Delta := LineSize mod 4;
  if  Delta <> 0                                    //Выравнивание строки на границу
  then                                              //четырех байт
    Delta := 4 - Delta;

  inc(LineSize, Delta);
  DataSize := LineSize * abs(BIH.biheight);        //Определяем размер нового растра
                                                   //(в байтах)
  pixInByte := 8 div BIH.biBitCount;
  memResult := TMemoryStream.Create;               //Поток будет содержать растр
  memResult.Seek(0, soFromBeginning);
  memResult.SetSize(DataSize);

  mem.Seek(0, soFromBeginning);
  OldPixSize := OldBIH.biBitCount div 8;
  GetMem(ReadPix, OldPixSize);                     //переменная для чтения из исходного
                                                   //растра
  ProgressBar1.Visible := true;
  ProgressBar1.Max     := BIH.biHeight;
  ProgressBar1.Position:= 0;

  Try                                               //Защищеный блок записи в файла
    For j := 1 to abs(BIH.biheight)                  //цикл по строкам
    do
      Begin
        ProgressBar1.Position := j;
        application.ProcessMessages;
        i := 1;
        Repeat                                       //цикл по пикселям строки
          K  := (pixInByte - 1) * BIH.biBitCount;    //Сдвиг текущего пикселя в байте
          m  := 0;                                   //номер пикселя в байте
          b2 := 0;                                   //"тот самый" байт данных
          Repeat                                     //нового растра
             mem.ReadBuffer(ReadPix^, OldPixSize);
             For l := 0 to NColors - 1               //определяем номерцвета в палитре
             do                                      //Сравнивая текущий цвет с цветом
               if CompareMem(ReadPix, @Palette[l], OldPixSize) //палитры
               then
                 Begin
                   b1 := l;                          //Сохраняем номер цвета
                   break;                            //(при выходе из цикла значение
                 end;                                //l может быть произвольным)

             b2 := b2 or (b1 shl K);                 //Сдвигаем цвет на k разрядов
                                                     //и записываем его в байт данных
             Dec(K, BIH.biBitCount);                 //следующий пиксель находится
                                                     //в более младшем разряде
             inc(m);
          until (m = pixInByte) or ((i + m) > BIH.biWidth); //последний байт данных строки
                                                         //может быть не заполнен до конца
          memResult.WriteBuffer(b2, SizeOf(b2));         //запись байта
          inc(i, m);
        until i > BIH.biWidth;

        mem.Seek(OldDelta, soFromCurrent);          //Выравнивание на границу 4 байт
        memResult.Seek(Delta, soFromCurrent);       //как по входу так и по выходу
      end;
    ProgressBar1.Visible := False;

    bmp := TFileStream.Create(Edit2.Text, fmCreate);
    bmp.Seek(0, soFromBeginning);


    bmp.WriteBuffer(FH, SizeOf(FH));               //Запись заголовков битмапа
    bmp.WriteBuffer(BIH, SizeOf(BIH));

    if oldBIH.biBitCount = 16                      //Определяем тип исходного
    then                                           //High Color битмапа
      if not is16bitPattern                        //по умалчанию - 5-5-5
      then
        oldBIH.biBitCount := 15;

    Case oldBIH.biBitCount                          //Конвертируем цвета в
    of                                              //палитру и пишем палитру в файл
      15 :
          For i := 0 to BIH.biClrUsed - 1
          do
            Begin
              PaletteColor := Get15RGBQuad(palette[i]);
              bmp.WriteBuffer(PaletteColor, SizeOf(PaletteColor));
            end;

      16 :
          For i := 0 to BIH.biClrUsed - 1
          do
            Begin
              PaletteColor := Get16RGBQuad(palette[i]);
              bmp.WriteBuffer(PaletteColor, SizeOf(PaletteColor));
            end;

      24, 32 :
          For i := 0 to BIH.biClrUsed - 1
          do
            Begin
              PaletteColor := Get24RGBQuad(palette[i]);
              bmp.WriteBuffer(PaletteColor, SizeOf(PaletteColor));
            end;
    end;

    DW := bmp.Position;                               //Определяем начало растра
    bmp.Seek(10, soFromBeginning);                    //Записываем это значение
    bmp.WriteBuffer(DW, SizeOf(DW));                  //в файловый заголовок

    bmp.Seek(DW, soFromBeginning);
    memResult.Seek(0, soFromBeginning);

    ProgressBar1.Visible := true;
    ProgressBar1.Max     := memResult.Size div 100000;
    ProgressBar1.Position:= 0;
    tocopy := memResult.Size;
    While (tocopy - 100000) > 0                       //Записываем растр блоками по
    do                                                // 100000 байт (нужно более
      Begin                                           // для progressbar а и важно)
        bmp.CopyFrom(memResult, 100000);              // для сверхбольших битмапов
        tocopy := tocopy - 100000;                    // размером от 100 МБ
        ProgressBar1.Position := ProgressBar1.Position + 1;
      end;
    bmp.CopyFrom(memResult, tocopy);
    ProgressBar1.Visible := false;

    DW := bmp.Size;                                  //Читаем размер файла и пишем
    bmp.Seek(2, soFromBeginning);                    //в файловый заголовок
    bmp.WriteBuffer(DW, SizeOf(DW));
    
  Except
    ShowMessage('Fail to Write File');
  end;
    bmp.Free;
    memResult.Free;
end;

 //Загружает битмап в память и определяет число цветов

procedure TForm1.Button1Click(Sender: TObject);
var
  DataSize: Integer;
  LineSize: Integer;
  i, j, k : Integer;
  ReadPix : Pointer;
  PixSize : Integer;
  Found   : Boolean;
  err     : Integer;
  tocopy  : Integer;

begin
  if OpenPictureDialog1.Execute
  then
    Begin
      Edit1.Text := OpenPictureDialog1.FileName;
      Edit2.Text := ExtractFilePath(OpenPictureDialog1.FileName) +
         'Compressed_' + ExtractFileName(OpenPictureDialog1.FileName);
      Image1.Picture.LoadFromFile(OpenPictureDialog1.FileName);

      err := TestBmp(False);   //Проверяем годится ли битмап и читаем заголовки
      if err = NoError
      then
        Begin
          NColors := 0;
          bmp.Seek(FH.bfOffBits, soFromBeginning);

          OldDelta := (BIH.biWidth * BIH.biBitCount div 8) mod 4;
          if OldDelta <> 0                      //Считаем число байт выравнивания
          then
            OldDelta := 4 - OldDelta;

          LineSize := BIH.biWidth * BIH.biBitCount div 8 + OldDelta;
                                                 //Вычисляем полную длину строки
          DataSize := LineSize * abs(BIH.biHeight); //Размер растра
          mem := TMemoryStream.Create;
          mem.SetSize(DataSize);                 //Выделяем память под растр

          ProgressBar1.Visible := true;
          ProgressBar1.Max     := DataSize div 100000;
          ProgressBar1.Position:= 0;
          try
            tocopy := DataSize;                    //Читаем растр блоками по 100000
            While (tocopy - 100000) > 0            //байт
            do                                     //Позволяет отоброзить прогресс
              Begin                                //Важно для битмапов более 10Мб
                mem.CopyFrom(bmp, 100000);
                tocopy := tocopy - 100000;
                ProgressBar1.Position := ProgressBar1.Position + 1;
              end;
            mem.CopyFrom(bmp, tocopy);
            ProgressBar1.Visible := false;
          except
            ShowMessage('File Read Error');
            exit;
          end;
          bmp.Free;
          mem.Seek(0, soFromBeginning);

          PixSize := BIH.biBitCount div 8;     //Размер пикселя в байтах
          GetMem(ReadPix, PixSize);
          FillChar(Palette, 256, 0);           //Зануляем палитру
          
          ProgressBar1.Visible := true;
          ProgressBar1.Max     := BIH.biHeight;
          ProgressBar1.Position:= 0;
          For i := 1 to BIH.biHeight
          do
            Begin
              ProgressBar1.Position := i;
              application.ProcessMessages;
              For j := 1 to BIH.biWidth
              do
                Begin
                  mem.ReadBuffer(ReadPix^, PixSize); //Читаем очередной пиксель

                  found := false;
                  For k := 0 to NColors - 1
                  do
                    if CompareMem(ReadPix, @Palette[k], PixSize)
                    then                             //Если такой цвет есть в палитре
                      Begin                          //переходим к следующему
                        found := True;
                        Break;
                      end;

                  if not Found                     //Если такого цвета еще нет
                  then                             //то добавляем
                    Begin
                      inc(NColors);
                      if NColors > 256             //Если число цветов перевалило через
                      then                         //256 завершаем работу
                        Begin
                          ProgressBar1.Visible := False;
                          err := MoreThan256Colors;
                          ShowMessage('More than 256 colors Detected');
                          mem.Free;
                          mem := nil;
                          exit;
                        end;                       //Иначе копируем цвет в палтру
                      Move(ReadPix^, Palette[NColors - 1], PixSize);
                    end
                end;
                mem.Seek(OldDelta, soFromCurrent);
              end;

          ProgressBar1.Visible := False;
        end
      else
        Begin
          Case err
          of
            FileReadError      : ShowMessage('Error file data accessing');
            OS2BMP             : ShowMessage('OS/2 BMP Detected');
            UnknownBitmap      : ShowMessage('Unknown BMP type');
            PossibleCorrupt    : ShowMessage('Bitmap is Corrupt or incorect');
            PossibleExtended   : ShowMessage('Possible Extended format or corrupt');
            CompressedBitMap   : ShowMessage('BitMap is already compressed');
            NotHighOrTrueColor : ShowMessage('BitMap is not High or True Color');
          end;
          FH  := Nil;
          BIH := Nil;
          mem := Nil;
        end;
    end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FH  := Nil;
  BIH := Nil;
  mem := Nil;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  mem.Free;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  if (FH <> nil) and (BIH <> nil)
  then
    Convert(FH^, BIH^);
end;

end.

 

Прикрепляю и весь проект целиком  

Это сообщение отредактировал(а) alexeis1 - 17.5.2006, 14:32

Присоединённый файл ( Кол-во скачиваний: 216 )
Присоединённый файл  sample.zip 41,75 Kb


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
rz3rr
Дата 17.5.2006, 22:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 51
Регистрация: 27.11.2005

Репутация: нет
Всего: нет



Подскажи, пожалуйста, как мне из массива (двухмерного, 1-е измерение-адрес точки, 2-е - палитра(RGB)) загрузить данные в TImage (естественно, через bitmap). Что-то никак не врублюсь. 
PM MAIL   Вверх
rz3rr
Дата 17.5.2006, 22:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 51
Регистрация: 27.11.2005

Репутация: нет
Всего: нет



Да, забыл уточнить. Высота и ширина "картинки" (integer) идет отдельными параметрами. 
PM MAIL   Вверх
Alexeis
Дата 19.5.2006, 12:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 55
Всего: 459



Уточните задачу 
Цитата(rz3rr @  17.5.2006,  21:20 Найти цитируемый пост)
 из массива (двухмерного, 1-е измерение-адрес точки, 2-е - палитра(RGB))

Как двумерный массив может содержать и растр и палитру? 
Опишите более строго ваши струкруры данных.(желательно фрагменты кода) 


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
rz3rr
Дата 19.5.2006, 20:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 51
Регистрация: 27.11.2005

Репутация: нет
Всего: нет



Не проблема преобразовать в трехмерный, типа Myarr[v,h,rgb].
На определенном этапе мне нужен был духмерный. А количество точек по вертикали и горизонтали шли отдельным параметром. 
PM MAIL   Вверх
Poseidon
Дата 21.5.2006, 04:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Delphi developer
****


Профиль
Группа: Комодератор
Сообщений: 5273
Регистрация: 4.2.2005
Где: Гомель, Беларусь

Репутация: нет
Всего: 133



Эксперементы со шрифтами на пользу не пошли. Трудно читать. Исправь пожалуйста smile 


--------------------
Если хочешь, что бы что-то работало - используй написанное, 
если хочешь что-то понять - пиши сам...
PM MAIL ICQ   Вверх
Albinos_x
Дата 21.5.2006, 08:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Evil Skynet
****


Профиль
Группа: Комодератор
Сообщений: 3288
Регистрация: 28.5.2004
Где: X-6120400 Y-1 4624650

Репутация: 1
Всего: 108



Цитата(Poseidon)
Эксперементы со шрифтами на пользу не пошли. Трудно читать. Исправь пожалуйста 

угу... наверно стандартный лучше будет...
и статью наверно стоит или закрепить, или в FAQ отправить... smile  


--------------------
"Кто владеет информацией, тот владеет миром"    
Уинстон Черчилль
PM MAIL ICQ   Вверх
Alexeis
Дата 12.6.2006, 09:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 55
Всего: 459



Poseidon, Обязательно поправлю, вот только сейчас не могу. Дело в том, что в текст писал в Microsoft Word, а в ручную переделывать очень долго, поэтому написал специальную програмку - конвертер RTF->BBCode. Т.о. основной текст написан шрифтом "Times new roman", как видно интервал получается не полуторным а одинарным. Кроме того полученый bbCode пока великоват. Как только переделаю программу, так сразу исправлю пост... 


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Yanis
Дата 2.7.2006, 00:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник Клуба
Сообщений: 2937
Регистрация: 9.2.2004
Где: Москва

Репутация: 2
Всего: 111



alexeis1
Может закачаешь статью в виде файла? А то никак не собирусь статью прочитать. Глаза ломаются после 10 строчек smile 


--------------------
user posted image *щёлк*
PM MAIL WWW ICQ   Вверх
Alexeis
Дата 3.7.2006, 09:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 55
Всего: 459



Появилось немного времени
Вот HTML версия ее по легче будет читать 
ссылка 

Это сообщение отредактировал(а) alexeis1 - 3.7.2006, 09:45


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Yuriy1
Дата 18.5.2008, 12:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 2
Регистрация: 18.5.2008

Репутация: нет
Всего: нет



Вступление
==========   
Есть картинка, красного цвета, в формате "bmp", шириной 1 пиксель и высотой 1 пикселей. Чтобы Вы получили представление об этой картинке изображу эту картинку в виде буквы: 

К

или её можно записать, условно так:

FF0000

В base64 она будет выглядеть так:

Qk06AAAAAAAAADYAAAAoAAAAAQAAAAEAAAABABgAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAD/
AA==



Цветная картинка,  красного цвета, шириной 2 пикселя и высотой 1 пиксель.  В виде букв: 

КК

или её можно записать, условно так:

FF0000 FF0000

В base64 она будет выглядеть так:

Qk0+AAAAAAAAADYAAAAoAAAAAgAAAAEAAAABABgAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAD/
AAD/AAA=



Цветная картинка,  красного цвета, шириной 3 пикселя и высотой 1 пиксель.  В виде букв: 

ККК

или её можно записать, условно так:

FF0000 FF0000 FF0000

В base64 она будет выглядеть так:

Qk1CAAAAAAAAADYAAAAoAAAAAwAAAAEAAAABABgAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAD/
AAD/AAD/AAAA



Вопрос
======
Есть цветная картинка, в формате "bmp", шириной (например)3 пикселя и высотой (например) 2 пикселя. Чтобы Вы получили представление об этой картинке изображу эту картинку в виде букв:

КЖЗ
КЖЗ

Здесь одна буква, как бы показывает один пиксель, а её название цвет этого пикселя (К-красный, Ж-Жёлтый , З-Зелёный). 

или её можно записать, условно так:

FF0000 FFFF00 00FF00
FF0000 FFFF00 00FF00

Здесь FF0000 означает один пиксель красного цвета, 
FFFF00 означает один пиксель жёлтого цвета, и т.д. две строки означают 2 пикселя по высоте.

Подскажите, пожалуйста, по какому алгоритму можно из записи:

FF0000 FFFF00 00FF00
FF0000 FFFF00 00FF00

получить (вручную) запись этой картинки в base64:

Qk1OAAAAAAAAADYAAAAoAAAAAwAAAAIAAAABABgAAAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAD/
AP//AP8AAAAAAAD/AP//AP8AAAAA


(формат "bmp", не принципиален, если для какого то другого формата это сделать легче, то можно и в другом)
PM MAIL   Вверх
Alexeis
Дата 18.5.2008, 12:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 55
Всего: 459



Для начала запись 
Цитата

FF0000 FFFF00 00FF00
FF0000 FFFF00 00FF00


Можно загрузить в StringList, после чего нужно выделить отдельные числа типа "FFFF00", дальше это строковое представление превращаем в число функцией HexToBin и создаем битмапку, куда запишем эти числа. Ну а как превратить в base64 так это в любом ФАКе есть.


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Yuriy1
Дата 18.5.2008, 13:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 2
Регистрация: 18.5.2008

Репутация: нет
Всего: нет



Если я правильно понял, то в ответе идёт речь о програмировании. Я хотел бы разабраться в алгоритме однозначного соответствия положения каждого пикселя и его цвета его предстовлению в base64. Как я писал "в ручную"
PM MAIL   Вверх
Cinot
Дата 19.10.2010, 16:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 33
Регистрация: 20.11.2008

Репутация: нет
Всего: нет



Alexeis, подскажите, пожалуйста, как, имея файл BMP в шестнадцатиричном формате, получить наглядное представление о рисунке? Файлы монохромные. Есть ли среди байтов информация о количестве строк и столбцов? Если да, то какие по счёту байты за это отвечают.
Мне просто нужен некий простой алгоритм, а не понимая соотношения расположения  пикселей и байтов, я не смогу его составить. То есть, как узнать, что в имеется 4 строки по 32 пиксела, а не 2 строки по 64 пиксела? Или как узнать, что очередной ноль - это не пиксель, а знак конца строки (ведь конец забивается нулями до кратности 4 байтам).
Не могли бы Вы подробнее остановиться на этом? Как, имея перед глазами цифры, понять структуру рисунка? (можно на примерах вроде 1*7, 1*33, 2*15, 2*17 с "шашечками" в виде рисунка. Если есть время, конечно.
PM MAIL   Вверх
Alexeis
Дата 19.10.2010, 18:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 55
Всего: 459



Цитата(Cinot @  19.10.2010,  17:06 Найти цитируемый пост)
Alexeis, подскажите, пожалуйста, как, имея файл BMP в шестнадцатиричном формате, получить наглядное представление о рисунке? Файлы монохромные. Есть ли среди байтов информация о количестве строк и столбцов? Если да, то какие по счёту байты за это отвечают

  По описанию формата легко увидеть, чаще всего битмап cодержит старый инфохедер размером в 40 байт = 28 00 00 00 в хексе . сразу за ним 2 по 4 байта размеры битмапа в хексе.


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
skyboy
Дата 26.12.2010, 21:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


неОпытный
****


Профиль
Группа: Модератор
Сообщений: 9820
Регистрация: 18.5.2006
Где: Днепропетровск

Репутация: нет
Всего: 260



под свои нужды написал модуль, который разбирает структуру BMP и перебирает все пиксели, вызывая для каждого функцию, переданную в качестве аргумента.
поддерживают несжатые 32, 24, 16, 8, 4, 2 и 1-битные изображения, а также - 8-битные с RLE. не поддерживаются PNG и JPG компрессия, а также почему-то проблема с реализацией 4-битного RLE кодирования - при переборе вполне валидных изображений срабатывает встречается 00 02 - и "выпадают" строки. Может, GIMP косо сохраняет в 4-битный с RLE(и на самом деле 00 02 надо трактовать не как инфорацию о смещении, а как "абсолютный режим" - вывести два пиксела?)
Код

unit BMP_OP;
interface
const readQuant = 2;
{ методы сжатия }
  BI_RGB       = $00000000;
  BI_RLE8      = $00000001;
  BI_RLE4      = $00000002;
  BI_BITFIELDS = $00000003;
  BI_JPEG      = $00000004;
  BI_PNG       = $00000005;
type
 { в нижеописанных структурах
   везде, где используется longint,
   должен быть DWORD. Потому возможно
   переполнение со сменой знака, т.к.
   Pascal не поддерживает тип DWORD
 }
 TColor = record
  R,G,B: byte;
 end;
 TColorTable = ^TColor;
 TBitMapFileHeader = record
  bfType: array[0..1] of char;
  bfSize: longint;
  bfReserved1, bfReserved2: word;
  bfOffBits: longint;
 end;
 TBitMapInfoHeader = record
  biSize            : longint;
  biWidth            : longint;
  biHeight            : longint;
  biPlanes            : word;
  biBitCount        : word;
  biCompression        : longint;
  biSizeImage        : longint;
  biXPelsPerMeter,
  biYPelsPerMeter    : longint;
  biClrUsed            : longint;
  biClrImportant    : longint;
  biRedMask            : longint;
  biGreenMask        : longint;
  biBlueMask        : longint;
  {
    Описанная в документации структура не "включает"
    в себя палитру; но чтоб считать палитру один
    раз, а затем пользоваться считанными данными,
    надо хрнить указать на область памяти;
    поле структуры info подходит под это
    как можно лучше, так как структура передается
    параметров почти во все функции
  }
  xPalette              : TColorTable;
 end;
 TPixelsIterateCallback = function(const x,y : longint;
                        const color: TColor;
                        const header: TBitmapFileHeader;
                        const info: TBitmapInfoHeader): boolean;
 TIteratePixelsInBMPProcedure = procedure(
                                 var bmpFile: file;
                                 const head: TBitmapFileHeader;
                                 const info: TBitmapInfoHeader;
                                 callback: TPixelsIterateCallback);
{
 Считать из файла основной заголовок
}
procedure fetchBMPHeader(var bitmapFile: file; var header: TBitmapFileHeader);
{
 Считать из файла дополнительный заголовок
}
procedure fetchBMPInfoHeader(var bitmapFile: file; var infoHeader: TBitmapInfoHeader);
{
 Загрузить из файла палитру
}
procedure loadPalette(var bmpFile: file;
                     const head: TBitmapFileHeader;
                     var info: TBitmapInfoHeader
                     );
procedure enumeratePixels(var bmpFile: file;
                          const header: TBitmapFileHeader;
                          const info: TBitmapInfoHeader;
                          callback: TPixelsIterateCallback;
                          unknownFormatCallback: TIteratePixelsInBMPProcedure);
implementation
uses graph;
type
 TGetPixelFromBMPProcedure = procedure(
                              var bitmapFile: file;
                              x,y: longint;
                              const head: TBitmapFileHeader;
                              const info: TBitmapInfoHeader;
                              var color: TColor);
function testBMPHeader(header: TBitmapFileHeader; bmpFileSize: longint): boolean;
var result: boolean;
begin
 result:= true;
 if (bmpFileSize< 0) OR (header.bfSize< 0)
 {
  переполнение типа longint
  для файлов, больших 2Гб;
  правда, дождаться
  отрисовки таких файлов
  все равно маловероятно
 }
 then
  runError(204);
 if (header.bfReserved1 <> 0)
    OR (header.bfReserved2 <> 0)
 {
  если в этих полях не 0,
  возможно, структура повреждена
 }
 then
  result := false;
 if header.bfOffBits >= bmpFileSize
 {
  смещение до начала данных
  изображения не может быть
  больше размера файла
 }
 then
  result := false;
 testBMPheader := result;
end;
function testBmpInfoHeader(Info: TBitmapInfoHeader): boolean;
var result: boolean;
begin
 result := true;
 if info.biPlanes <> 1
 {
  biPlanes должно быть 1
  в текущей версии структуры
 }
 then
  result := false;
 testBmpInfoHeader := result;
end;
procedure fetchBMPHeader(var bitmapFile: file; var header: TBitmapFileHeader);
var hasBeenRead: word;
begin
 { читаем заголовок }
 blockRead(bitmapFile, header, sizeOf(header) div readQuant, hasBeenRead);
 if (hasBeenRead <> sizeOf(header) div readQuant)
     OR (testBMPHeader(header, fileSize(bitmapFile) * readQuant)=false)
 {
  если прочитали с ошибкой или, судя по заголовку,
  структура не может быть обработана,
  вызываем ошибку
 }
 then
  runError(19);
end;
procedure fetchBMPInfoHeader(var bitmapFile: file; var infoHeader: TBitmapInfoHeader);
var hasBeenRead: word;
begin
 { читаем заголовок }
 blockRead(bitmapFile, infoHeader, sizeOf(infoHeader) div ReadQuant, hasBeenRead);
 if (hasBeenRead <> sizeOf(infoHeader) div readQuant)
    OR (testBMPInfoHeader(infoHeader) = false)
 {
  если прочитали с ошибкой или, судя по заголовку,
  структура не может быть обработана,
  вызываем ошибку
 }
 then
  runError(19);
end;
procedure scrollColorTable(var colorTable: TColorTable; shift: integer);
begin
 colorTable := Ptr(Seg(colorTable^), Ofs(colorTable^) + shift * sizeof(TColor));
end;
procedure loadPalette(var bmpFile: file;
                     const head: TBitmapFileHeader;
                     var info: TBitmapInfoHeader
                     );
var i, j: byte;
    temp: integer;
    colorTable: TColorTable;
begin
 if info.biClrUsed = 0
 {
  Если у нас безпалитровое изображение,
  нет смысла обрабатывать файл
 }
 then
  begin
   info.xPalette := nil;
   exit;
  end;
 reset(bmpFile, 1);
 getMem(info.xPalette, sizeof(TColor) * info.biClrUsed);
 colorTable := info.xPalette;
 {
  "Пропускаем" в файле структуры TBitMapFileHeader
  и TBitMapInfoHeader
 }
 seek(bmpFile, sizeof(head) + info.biSize);
 for i:= 0  to info.biClrUsed - 1 do
  begin
   blockRead(bmpFile, colorTable^.B, 1, temp);
   blockRead(bmpFile, colorTable^.G, 1, temp);
   blockRead(bmpFile, colorTable^.R, 1, temp);
   {
    Четвертый байт каждого элемента палитры
    используется только для выравнивания;
    прочитали, чтоб сместиться дальше,
    и проигнорировали значение
   }
   blockRead(bmpFile, j, 1, temp);
   {
    getMem мог выделить память в конце сегмента;
    тогда последнее, biClrUsed * sizeof(TColor)
    смещение приведет к ошибке переполнения адреса
   }
   if i <> info.biClrUsed - 1
   then
    scrollColorTable(colorTable, 1);
  end;
end;
function getJunkValue(const info: TBitmapInfoHeader): byte;
var temp: longint;
begin
 temp := info.biWidth * info.biBitCount;
 if (temp AND 7) <> 0
 then
  temp := (temp OR 7) + 1;
 temp := (temp shr 3) AND 3;
 if temp = 0
 then
  getJunkValue := 0
 else
  getJunkValue := 4 - temp;
end;
procedure getColorFromPalette(const colorIndex: byte;
                               var color: TColor;
                               const info: TBitmapInfoHeader);
var colorTable: TColorTable;
begin
 colorTable := info.xPalette;
 scrollColorTable(colorTable, colorIndex);
 color.R := colorTable^.R;
 color.G := colorTable^.G;
 color.B := colorTable^.B;
end;
procedure enum_32b_uncompressed(var bmpFile: file;
                                 const head: TBitmapFileHeader;
                                 const info: TBitmapInfoHeader;
                                 callback: TPixelsIterateCallback);far;
var x, y: longint;
    callbackResult: boolean;
    color: TColor;
    temp: byte;
    hasBeenRead: integer;
begin
 callbackResult := true;
 for y := info.biHeight downto 1 do
  begin
   for x := 1 to info.biWidth do
    begin
     blockRead(bmpFile, temp, 1, hasBeenRead);
     blockRead(bmpFile, color.B, 1, hasBeenRead);
     blockRead(bmpFile, color.G, 1, hasBeenRead);
     blockRead(bmpFile, color.R, 1, hasBeenRead);
     callbackResult := callback(x, y, color, head, info);
     if NOT(callbackResult)
     then
      break;
    end;
   if NOT(callbackResult)
   then
    break;
  end;
end;
procedure enum_24b_uncompressed(var bmpFile: file;
                                 const head: TBitmapFileHeader;
                                 const info: TBitmapInfoHeader;
                                 callback: TPixelsIterateCallback);far;
var x, y: longint;
    callbackResult: boolean;
    color: TColor;
    temp: byte;
    hasBeenRead: integer;
begin
 callbackResult := true;
 for y := info.biHeight downto 1 do
  begin
   for x := 1 to info.biWidth do
    begin
     blockRead(bmpFile, color.B, 1, hasBeenRead);
     blockRead(bmpFile, color.G, 1, hasBeenRead);
     blockRead(bmpFile, color.R, 1, hasBeenRead);
     callbackResult := callback(x, y, color, head, info);
     if NOT(callbackResult)
     then
      break;
    end;
   if NOT(callbackResult)
   then
    break;
   for x:= 1 to getJunkValue(info) do
    blockRead(bmpFile, temp, 1, hasBeenRead);
  end;
end;
procedure enum_16b_uncompressed(var bmpFile: file;
                                 const head: TBitmapFileHeader;
                                 const info: TBitmapInfoHeader;
                                 callback: TPixelsIterateCallback);far;
var x, y: longint;
    callbackResult: boolean;
    color: TColor;
    temp: word;
    hasBeenRead: integer;
begin
 callbackResult := true;
 for y := info.biHeight downto 1 do
  begin
   for x := 1 to info.biWidth do
    begin
     blockRead(bmpFile, temp, 2, hasBeenRead);
     if (info.biRedMask = info.biGreenMask) AND (info.biGreenMask = info.biBlueMask)
     then
 { Цвета хранятся в двойном байте по схеме 5-5-5:
  0RRRRRGGGGGBBBBB
 }
      begin
       color.R := (temp AND 31744) shr 7;
       color.G := (temp AND 992) shr 2;
       color.B := (temp AND 31) shl 3;
      end
     else
{
 Цвета хранятся по схеме 5-6-5:
 RRRRRGGGGGGBBBBB
}
      begin
       color.R := (temp AND 63488) shr 8;
       color.G := (temp AND 2016) shr 2;
       color.B := (temp AND 31) shl 3;
      end;
     callbackResult := callback(x, y, color, head, info);
     if NOT(callbackResult)
     then
      break;
    end;
   if NOT(callbackResult)
   then
    break;
   for x:= 1 to getJunkValue(info) div 2 do
    blockRead(bmpFile, temp, 2, hasBeenRead);
  end;
end;
procedure enum_8b_uncompressed(var bmpFile: file;
                                 const head: TBitmapFileHeader;
                                 const info: TBitmapInfoHeader;
                                 callback: TPixelsIterateCallback);far;
var x, y: longint;
    callbackResult: boolean;
    colorIndex: byte;
    color: TColor;
    colorTable: TColorTable;
    temp: byte;
    hasBeenRead: integer;
begin
 callbackResult := true;
 for y := info.biHeight downto 1 do
  begin
   for x := 1 to info.biWidth do
    begin
     blockRead(bmpFile, colorIndex, 1, hasBeenRead);
     getColorFromPalette(colorIndex, color, info);
     callbackResult := callback(x, y, color, head, info);
     if NOT(callbackResult)
     then
      break;
    end;
   if NOT(callbackResult)
   then
    break;
   for x:= 1 to getJunkValue(info) do
    blockRead(bmpFile, temp, 1, hasBeenRead);
  end;
end;
procedure enum_4b_uncompressed(var bmpFile: file;
                                 const head: TBitmapFileHeader;
                                 const info: TBitmapInfoHeader;
                                 callback: TPixelsIterateCallback);far;
var x, y: longint;
    callbackResult: boolean;
    colorIndex: byte;
    color: TColor;
    colorTable: TColorTable;
    temp: byte;
    hasBeenRead: integer;
begin
 callbackResult := true;
 for y := info.biHeight downto 1 do
  begin
   for x := 1 to info.biWidth do
    begin
     if (x AND 1) = 1
     then
      begin
       blockRead(bmpFile, temp, 1, hasBeenRead);
       colorIndex := temp shr 4;
      end
     else
      colorIndex := temp AND 15;
     getColorFromPalette(colorIndex, color, info);
     callbackResult := callback(x, y, color, head, info);
     if NOT(callbackResult)
     then
      break;
    end;
   if NOT(callbackResult)
   then
    break;
   for x:= 1 to getJunkValue(info) do
    blockRead(bmpFile, temp, 1, hasBeenRead);
  end;
end;
procedure enum_2b_uncompressed(var bmpFile: file;
                                 const head: TBitmapFileHeader;
                                 const info: TBitmapInfoHeader;
                                 callback: TPixelsIterateCallback);far;
var x, y: longint;
    callbackResult: boolean;
    colorIndex: byte;
    color: TColor;
    colorTable: TColorTable;
    temp: byte;
    hasBeenRead: integer;
begin
 callbackResult := true;
 for y := info.biHeight downto 1 do
  begin
   for x := 1 to info.biWidth do
    begin
     if (x AND 3) = 1
     then
       blockRead(bmpFile, temp, 1, hasBeenRead);
     case (x AND 3) of
     1: colorIndex := temp shr 6;
     2: colorIndex := (temp shr 4) AND 3;
     3: colorIndex := (temp shr 2) AND 3;
     0: colorIndex := temp AND 3;
     end;
     getColorFromPalette(colorIndex, color, info);
     callbackResult := callback(x, y, color, head, info);
     if NOT(callbackResult)
     then
      break;
    end;
   if NOT(callbackResult)
   then
    break;
   for x:= 1 to getJunkValue(info) do
    blockRead(bmpFile, temp, 1, hasBeenRead);
  end;
end;
procedure enum_1b_uncompressed(var bmpFile: file;
                                 const head: TBitmapFileHeader;
                                 const info: TBitmapInfoHeader;
                                 callback: TPixelsIterateCallback);far;
var x, y: longint;
    callbackResult: boolean;
    colorIndex: byte;
    color: TColor;
    colorTable: TColorTable;
    temp: byte;
    hasBeenRead: integer;
begin
 callbackResult := true;
 for y := info.biHeight downto 1 do
  begin
   for x := 1 to info.biWidth do
    begin
     if (x AND 7) = 1
     then
       blockRead(bmpFile, temp, 1, hasBeenRead);
     colorIndex := temp AND (1 shl (7 - ((x - 1) mod 8)));
     colorIndex := colorIndex shr (7 - ((x - 1) mod 8));
     getColorFromPalette(colorIndex, color, info);
     callbackResult := callback(x, y, color, head, info);
     if NOT(callbackResult)
     then
      break;
    end;
   if NOT(callbackResult)
   then
    break;
   for x:= 1 to getJunkValue(info) do
    blockRead(bmpFile, temp, 1, hasBeenRead);
  end;
end;
procedure enum_8b_RLE8(var bmpFile: file;
                                 const head: TBitmapFileHeader;
                                 const info: TBitmapInfoHeader;
                                 callback: TPixelsIterateCallback);far;
var x, y: longint;
    callbackResult: boolean;
    colorIndex, i: byte;
    color: TColor;
    colorTable: TColorTable;
    temp: word;
    hasBeenRead: integer;
begin
 callbackResult := true;
 x := 1;
 y := info.biHeight;
 while NOT(eof(bmpFile)) do
    begin
     blockRead(bmpFile, temp, 2, hasBeenRead);
     case temp of
     0: begin
         x := 1;
         y := y - 1;
         continue;
        end;
     1: exit;
     2: begin
         blockRead(bmpFile, temp, 2, hasBeenRead);
         x := x + Hi(temp);
         y := y - Lo(temp);
         continue;
        end;
     else
      begin
       if Lo(temp) = 0
       then
        begin
         for i := 1 to Hi(temp) do
          begin
           blockRead(bmpFile, colorIndex, 1, hasBeenRead);
           getColorFromPalette(colorIndex, color, info);
           if (x<= info.biWidth) AND (y> 0)
           then
            callbackResult := callback(x, y, color, head, info);
           x := x + 1;
           if NOT(callbackResult)
           then
            exit;
          end;
         if Hi(temp) AND 1 = 1
         then
          blockRead(bmpFile, colorIndex, 1, hasBeenRead);
        end
       else
        begin
         colorIndex := Hi(temp);
         getColorFromPalette(colorIndex, color, info);
         for i:= 1 to Lo(temp) do
          begin
           if (x<= info.biWidth) AND (y> 0)
           then
            callbackResult := callback(x, y, color, head, info);
           x := x + 1;
           if NOT(callbackResult)
           then
            exit;
          end;
        end;
      end;
     end;
    end;
end;
procedure enumeratePixels(var bmpFile: file;
                          const header: TBitmapFileHeader;
                          const info: TBitmapInfoHeader;
                          callback: TPixelsIterateCallback;
                          unknownFormatCallback: TIteratePixelsInBMPProcedure);
var  iterateProc : TIteratePixelsInBMPProcedure;
begin
 iterateProc := unknownFormatCallback;
 reset(bmpFile, 1);
 seek(bmpFile, header.bfOffBits);
 if info.biCompression in [BI_RGB, BI_BITFIELDS]
 then
  case info.biBitCount of
   32: begin
        iterateProc := enum_32b_uncompressed;
       end;
   24: begin
        iterateProc := enum_24b_uncompressed;
       end;
   16: begin
        iterateProc := enum_16b_uncompressed;
       end;
   8: begin
        iterateProc := enum_8b_uncompressed;
       end;
   4: begin
        iterateProc := enum_4b_uncompressed;
       end;
   2: begin
        iterateProc := enum_2b_uncompressed;
       end;
   1: begin
        iterateProc := enum_1b_uncompressed;
       end;
  end;
 if (info.biBitCount = 8) AND (info.biCompression = BI_RLE8)
 then
  iterateProc := enum_8b_RLE8;
 iterateProc(bmpFile, header, info, callback);
end;
end.

пример использования:
Код

procedure cannt_enum_BMP(var bmpFile: file;
                          const head: TBitmapFileHeader;
                          const info: TBitmapInfoHeader;
                          callback: TPixelsIterateCallback);far;
begin
 graph.closeGraph;
 writeln('Cannot handle this BMP file');
 writeln('Info about file:');
 writeln('Size is ', info.biWidth, ' X ', info.biHeight);
 writeln('Bit per pixel: ', info.biBitCount);
 write('Compression type is: ');
 case info.biCompression of
 BI_RGB: write('Uncompressed');
 BI_BITFIELDS: write('Uncompressed - bitfields');
 BI_RLE8: write('RLE - 8');
 BI_RLE4: write('RLE - 4');
 BI_PNG:  write('PNG');
 BI_JPEG: write('JPEG');
 else
  write('Unknown');
 end;
 writeln('(',info.biCompression,')');
 halt(2);
end;

function pixelsIterator(const x,y : longint;
                        const color: TColor;
                        const header: TBitmapFileHeader;
                        const info: TBitmapInfoHeader): boolean; far;
begin
 pixelsIterator := true;
 if (keypressed) AND (readkey = #27)
 then
  pixelsIterator := false
 else
   GRAPH_OP.drawPoint(x, y, info.biWidth, info.biHeight, color.R, color.G, color.B);
{ просто вывод точки с преобразованием координат и цветовой палитры под VGA режим 2 }
end;

cont bmpFilePath = 'c:\8uncompressed.bmp';
var bmpFile: file;
    header: TBitMapFileHeader;
    info: TBitmapInfoHeader;
begin
 assign(bmpfile, bmpFilePath);
 reset(bmpFile, 2);
 BMP_OP.fetchBMPheader(bmpfile, header);
{ получаем общий заголовок для структуры 3 версии }
BMP_OP.fetchBMPInfoHeader(bmpfile, info);
{загружаем палитру}
BMP_OP.loadPalette(bmpFile, header, info);
{ инициируем перечисление пикселей }
BMP_OP.enumeratePixels(bmpFile, header, info, pixelsIterator, cannt_enum_BMP);
end;

PM MAIL   Вверх
Страницы: (2) [Все] 1 2 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Звук, графика и видео"
Girder
Snowy
Alexeis

Запрещено:

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делится вскрытыми компонентами

  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи
  • По вопросам разработки игр стоит заглянуть сюда

FAQ раздела лежит здесь!


Если Вам помогли и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, Girder, Snowy.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Звук, графика и видео | Следующая тема »


 




[ Время генерации скрипта: 0.2950 ]   [ Использовано запросов: 23 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.