Шаг 52 - Быстрый ImgConvert

После Шага 50 ко мне пришло несколько писем с упреком, мол, зачем же нужен такой медленный конвертор? Действительно, не нужен. Поэтому плюнул я на некоторую запутанность функций API и решил представить вариант с их использованием.

Будем обращаться к фунциям, взаимодействующим впрямую с матрицей DIB. Формат файла при этом будет очень (ну совсем) похож на BMP. Впрочем, это неважно - при желании его можно дополнить чем угодно.

Немного теории. Формат BMP или DIB является, так скажем, системным форматом Windows. То есть операции с ним поддерживаются на системном уровня. К подобным форматам относятся и ICO, WMF, EMF. Однако BMP наиболее употребляем. Этот формат несжатый, поэтому занимает много места на нашем многострадальном винчестере ;(.

В позапрошлом шаге я вроде бы мог обратится к массиву пикселей. Однако свойство Pixels[0][0] указывает не на первый пиксель, как того хотелось бы, а просто на отдельный. То есть реального порядка пикселей мы узнать не можем. Но есть такие функции, называются GetDIBits и SetDIBits, которые позволяют подобные операции проводить массово. В справке API можно найти по ним информацию. Там довольно мутно говорится, что если вот так и так подставить и сделать вот это, то получится то-то и то-то.

Логика такая - нужен указатель void на область памяти. К этому указателю можно обращаться как к массиву, если присвоить его тип, ну скажем TColor*. Для того, чтобы его использовать, нужно занять область памяти (memory location) в куче (heap). По-английски значит allocate. Для этого есть такая функция malloc (memory allocate). Но ей нужен размер должен быть? Высота помножить на ширина и на количество байтов, нужных одному пикселю. А сколько их нужно? Это дает форму npix=biBitsCountl/4. Один байт про запас.

Форма программы несколько другая:

gif/52_1.gif (3806 b)

А вот новый код:

void __fastcall TImgForm::ConvertToXGR(TObject *Sender)
{
if(FileExists(fname))DeleteFile(fname);
TFileStream* fs=new TFileStream(fname.c_str(),fmCreate);
BITMAPINFO info;
setmem(&info,sizeof(info),0);
info.bmiHeader.biSize=sizeof(info.bmiHeader);
Graphics::TBitmap* bmp=Image1->Picture->Bitmap;
int res;
GetDIBits(bmp->Canvas->Handle,bmp->Handle,0,bmp->Height,0,&info,DIB_RGB_COLORS);
int pixelsize=info.bmiHeader.biBitCount*bmp->Height*bmp->Width/4;
void* bitptr=malloc(pixelsize);
res=GetDIBits(bmp->Canvas->Handle,bmp->Handle,0,bmp->Height,bitptr,&info,DIB_RGB_COLORS);
int wid=bmp->Width;
int het=bmp->Height;
fs->Write(&wid,sizeof(wid));
fs->Write(&het,sizeof(het));
fs->Write(&info,sizeof(info));
fs->Write(bitptr,pixelsize);
delete fs;
}
//---------------------------------------------------------------------------

void __fastcall TImgForm::LoadBmp(TObject *Sender)
{
if(!Open1->Execute())return;
Image1->Picture->LoadFromFile(Open1->FileName);
}
//---------------------------------------------------------------------------

void __fastcall TImgForm::ConvertToDIB(TObject *Sender)
{
if(!FileExists(fname))
{
Application->MessageBox(String().sprintf("Cannot open file %s!",fname).c_str(),
          "Open error",MB_OK);
return;
};
TFileStream* fs=new TFileStream(fname.c_str(),fmOpenRead);
BITMAPINFO info;
setmem(&info,sizeof(info),0);
Graphics::TBitmap* bmp=Image2->Picture->Bitmap;
int wid,het;
fs->Read(&wid,sizeof(wid));
fs->Read(&het,sizeof(het));
bmp->Width=wid;
bmp->Height=het;
fs->Read(&info,sizeof(info));
int pixelsize=info.bmiHeader.biBitCount*bmp->Height*bmp->Width/4;
void* bitptr=malloc(pixelsize);
fs->Read(bitptr,pixelsize);
SetDIBits(bmp->Canvas->Handle,bmp->Handle,0,bmp->Height,bitptr,&info,DIB_RGB_COLORS);
ScrollBox2Resize(0);
delete fs;
}
//---------------------------------------------------------------------------

void __fastcall TImgForm::ExitClick(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------

void __fastcall TImgForm::ProcessClick(TObject *Sender)
{
ConvertToXGR(0);
ConvertToDIB(0);

}
//---------------------------------------------------------------------------

void __fastcall TImgForm::OpenAsXGRClick(TObject *Sender)
{
if(!Open1->Execute())return;
CopyFile(Open1->FileName.c_str(),fname.c_str(),FALSE);
ConvertToDIB(0);
}
//---------------------------------------------------------------------------

void __fastcall TImgForm::SaveAsXGRClick(TObject *Sender)
{
if(!Save1->Execute())return;
ConvertToXGR(0);
RenameFile(fname,Save1->FileName);
}

Я думаю, каждую функцию отдельно пояснять очень много времени займет. Прирлагается файл проекта, можно его скачать и посмотреть.

Итог. Этот формат особой информационной нагрузки не несет. Но подобный алгоритм можно использовать, например, для символического шифрования изображения. Фактически мы из спецификации BMP выкинули BM: и несколько biReserved. Но его уже все равно как BMP не открыть! Так что... Может пригодиться.


Загрузить проект | Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Аванесов Самвел - 21.01.2002