Шаг 50 - Пример использования TFileStream.

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

Некоторая оговорка. Я не говорю, что TFileStream вообще и в данном случае - наилучший вариант поточного io класса. Например, можно было использовать fstream [basic_fstream]. Но все-таки это раздел, посвященный VCL и всему, что с ним связано. Поскольку программист на C++ Builder часто использует компонентные классы VCL, то ему приходится обращаться к сооствествующим поточным классам (потомкам TStream).

Алгоритм просто перписывает информацию в файл с указанием ширины и высоты изображения. Вообще-то это неэффективное использование массива Pixels и свойства ScanLine. В реальной программе для этого бы использовались функции WinAPI для прямой работы с матрицей, типа GetDIBits и SetDIBits. Но при использовании этих функций пример бы потерял выразительность.

Я использовал некоторые возможности программы ScrollView для просмотра изображений. Вот форма приложения:

gif/50_1.gif (3329 b)

Для более удобного отображения здесь использован компонент TPageControl gif/50_2.gif (113 b) с палитры Win32. Для создания новых страниц (это компонент, работающий по принципу ноутбука или блокнота) используются команды контескстного меню <New page>, <Next Page>. Рассмотрю его подробнее позже. В каждую из страниц помещен компонент TScrollBox и TImage. У первого Align = alClient, у второго обычный. Кнопки на нижней панели отвечают за загрузку, конвертирование и преобразование изображения. Для оного используется временный файл "test.xgr" в текущем каталоге (надеюсь, такое расширение еще никто не задействовал :)). Еще на форму кинут один компонент TOpenPictureDialog.

Для оперделения скорости процесса конвертирования еще использован компонент TProgressBar gif/50_3.gif (98 b) со страницы Win32. Еще раз предупреждаю, что процесс обращения к отдельным пикселям очень неэффективен. Поэтому не рекомендую открывать в программе большие изображения, типа 1024x768 скриншотов. У меня (P850E) один такой скриншот открывался секунд 30. В смысле сохранялся. Открывается быстрее, но все равно не надо.

Так вот. TProgressBar - это индикатор. Обычно его используют в процессах, продолжающихся длительное время. Вкратце. Свойства Min и Max определяют минимальную и максимальную позиции индикатора. Свойство Position текущую позицию индикатора. Свойство Step сдвиг в позиции пл вызову метода StepIt.

Вот код:

//Эту функцию мне пришлось использовать из-за
// несовместимости формата DIB пикселей
// с выдаваемыми свойством Pixels
//Она переставляет R и B части цвета и удаляет высший байт.. 
long ConvertCol(long col)
{
col&=0x00FFFFFF;
long bcol=col;
bcol&=0x0000FF;
bcol*=0x10000;
long rcol=col;
rcol&=0xFF0000;
rcol/=0x10000;
long gcol=col;
gcol&=0x00FF00;
TColor result=(TColor)(rcol|gcol|bcol);
return result;
};
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::SaveClick(TObject *Sender)
{
TFileStream* fs=new TFileStream("test.xgr",fmCreate);
TVclBmp* bmp=new TVclBmp;
TColor *row;
TColor ptr;
int iptr;
int ns=sizeof(TColor);
bmp->Width=Image1->Picture->Bitmap->Width;
bmp->Height=Image1->Picture->Bitmap->Height;
TRect rect(0,0,Image1->Width,Image1->Height);
bmp->Canvas->CopyRect(rect,Image1->Picture->Bitmap->Canvas,rect);
iptr=bmp->Width;
fs->Write(&iptr,ns);
iptr=bmp->Height;
fs->Write(&iptr,ns);
Bar->Position=0;
Bar->Max=bmp->Height;
for(int i=0;iHeight;i++)
{
Bar->StepIt();
row=(TColor*)bmp->ScanLine[i];
fs->WriteBuffer(row,ns*bmp->Width);
};
delete fs;
delete bmp;
ScrollBox1Resize(0);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::LoadClick(TObject *Sender)
{
TFileStream* fs=new TFileStream("test.xgr",fmOpenRead);
TVclBmp* bmp=new TVclBmp;
TColor ptr;
int ns=sizeof(TColor);
bmp->PixelFormat=pf32bit;
fs->Read(&ptr,ns);
bmp->Width=ptr;
fs->Read(&ptr,ns);
bmp->Height=ptr;
Bar->Position=0;
Bar->Max=bmp->Height;
for(int i=0;iHeight;i++)
{
Bar->StepIt();
for(int j=0;jWidth;j++)
{
fs->Read(&ptr,ns);
bmp->Canvas->Pixels[j][i]=ConvertCol(ptr);
};
};
Image2->Picture->Bitmap->Width=bmp->Width;
Image2->Picture->Bitmap->Height=bmp->Height;
Image2->Canvas->Draw(0,0,bmp);
delete fs;
delete bmp;
ScrollBox2Resize(0);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::LoadFileClick(TObject *Sender)
{
if(!Open1->Execute())return;
Image1->Picture->LoadFromFile(Open1->FileName);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::ScrollBox1Resize(TObject *Sender)
{
if(Image1->WidthClientRect.Width())
  Image1->Left=(ScrollBox1->ClientRect.Width()-Image1->Width)/2;
else Image1->Left=0;
if(Image1->HeightClientRect.Height())
  Image1->Top=(ScrollBox1->ClientRect.Height()-Image1->Height)/2;
else Image1->Top=0;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::ScrollBox2Resize(TObject *Sender)
{
if(Image2->WidthClientRect.Width())
  Image2->Left=(ScrollBox1->ClientRect.Width()-Image2->Width)/2;
else Image2->Left=0;
if(Image2->HeightClientRect.Height())
  Image2->Top=(ScrollBox1->ClientRect.Height()-Image2->Height)/2;
else Image2->Top=0;
}
//---------------------------------------------------------------------------

Вот так вот.


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