После Шага 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. Один байт про запас.
Форма программы несколько другая:
А вот новый код:
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 не открыть! Так что... Может пригодиться.