Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Рисунок в файл


Автор: dimonskif 12.3.2006, 20:28
Каким образом можно несколько рисунков (и не только рисунков) добавить в один файл один за другим, чтобы в последующем можно было их оттуда вытащить и пользоваться?
я сделал так, но так только одна картинка smile
Код


procedure TForm1.Button1Click(Sender: TObject);
var
  f, f2:file of char;
  cop:char;
begin
AssignFile(f, 'c:\key.jpg');
AssignFile(f2, 'c:\picteres.txt');
{$I-}

Reset(f); 
ReWrite(f2); 
  while not Eof(f) do
  begin
    Read(f,cop);
    Write(f2,cop);
    end;
  CloseFile(f); 
  CloseFile(f2);
{$I+}

end;



Автор: Poseidon 12.3.2006, 21:07
Несколько рисунков в один файл? Так это у тебя уже Gif-анимация получится. Или тебе не обязательно иметь возможность их отображать?
В любом случае используй базу данных

Автор: dimonskif 12.3.2006, 21:49
Фишка в том, что отображать собранными мне их не надо. Это вроде для того, чтобы они были все в одном файле. Прога открывает этот файл и считывает из него все рисунки и файлы, а потом наоборот.
А базу данных использовать не хочу- с ней тягаться неудобно

Автор: Guedda 12.3.2006, 22:28
И так, начнем...
ЗАДАЧА:
Мне нужно сделать, чтобы картинки сохранялись в один файл, и по требованию пользователя загружались из этого файла (например в память). И считывая картинки из памяти программа показывает их в TImage по порядку с заданным интервалом. Короче
программа-слайдшоу.

ОБЩИЙ ПЛАН:
Придумываем свой формат файла (бинарный естественно).
- Изображения ложатся в файл друг за другом. Считываются так же.
- При чтении нужно знать, сколько занимает считываемая картинка. Варианты:
а) писать перед каждой картинкой её размер (одно целое число);
б) записать вначале список, содержащий размеры всех картинок, возможно, их смещения в файле.
Первый способ самый простой и понятный. Но второй более универсальный. Он даёт возможность хранить список имеющихся изображений, возможность произвольного доступа к любой из них.
Для начинающих лучше было бы избрать первый вариант. Но я тут предпочту второй. Не люблю тупиковых направлений.
- Обычно в бинарных форматах, в самом начале, помещают заголовок файла. Сделаем и мы его. Запишем туда какую-то сигнатуру (чтобы проверять, что файл «свой») и количество изображений. Можно зарезервировать немного места для каких-то будущих расширений формата, например, добавление картинок без перезаписи файла.

ФОРМАТ ФАЙЛА:
Файл состоит из следующих частей, расположенных друг за другом
1. Заголовок
Код

TFileHead = packed record 
  Signature:   Array [1..2] of Char;  // тут пишем 'IL' – Images List 
  Version:     Array [1..2] of Byte;  // на будущее, пока 0,0 
  RecordCount: DWord;                 // количество картинок в файле 
  Reserve:     Array [1..24] of Byte; 
end;    
//Всего вышло 32 байта на заголовок 


2. Список картинок
Последовательность лежащих друг за другом записей :
Код

TFileItemDescr = packed record 
  Offset: DWord;                 // Смещение картинки в файле 
  Size:   DWord;                 // Её размер 
  Kind:   Array [1..3] of Char;  // Тип (расширение) 
  Name:   String[20];            // Если захочется хранить имя 
end;

Каждая запись описывает одну из картинок, лежащих в теле файла. Количество записей задаётся параметром RecordCount заголовка. Таким образом, длинна этой части файла SizeOf(TFileItemDescr) * RecordCount.

3. Тело файла, сами картинки.
При записи сюда ложатся друг за другом картинки. Их местоположения записаны во 2-й части формата.
Замечания
а. Используются packed record, а не просто record для того, чтобы копилятор не пытался выравнивать поля, не вставлял «дырок» между ними.
б. Строки можно хранить только как массив байтов или как короткие строки (указывается длинна). Если короткая строка, нужно иметь ввиду, что один байт отводится на хранение длинны строки.

АЛГОРИТМ ЗАПИСИ
Предполагаем, что у нас есть список файлов, содержащих картинки, которые нужно собрать в один файл «нашего» формата. Будем работать с файлом через TFileStream. Его интерфейс самый простой.

1. Описываем базовые переменные
Код

var 
  Head: TFileHead; 
  List: Array of TFileItemDescr; 

Заполняем их
Код

ZeroMemory( @Head, SizeOf(Head) ); 
Head.Signature[1] := 'I'; 
Head.Signature[1] := 'I'; 
Head.RecordCount := … 
SetLength( List, … );    

2. Открываем файл, пишем первые два блока. Второй блок пока пустой, мы просто зарезервируем место в файле.
Код

var F: TFileStream; 
F := TFileStream.Create( '…', fmCreate   ); 
F.Write( Head, SizeOf(Head) ); 
F.Write( List[0], SizeOf(TFileItemDescr) * Head.RecordCount );    

3. Пишем картинки.
Заведём для доступа к ним отдельный поток.
Код

var 
  Fi: TFileStream; 
  i,n: Integer; 
... 
for i:=0 to … do 
begin 
  Fi := TFileStream.Create( '…', fmOpenRead ); 
  n := Fi.Size; 
  List[i].Offset := F.Position; 
  List[i].Size   := n; 
  List[i].Kind   := '…'; 
  F.CopyFrom( Fi, n ); 
  Fi.Free; 
end;    

4. Сохранение списка
Теперь 2-й блок нашего файла заполнен. Запишем его на место.
Код

F.Seek( SizeOf(Head), soFromBeginning ); 
F.Write( List[0], SizeOf(TFileItemDescr) * Head.RecordCount );    

Всё. Файл записан.

АЛГОРИТМ ЧТЕНИЯ

1. Открываем файл. Проверяем, что это «наш» файл, читаем список.
Код

var 
  Head: TFileHead; 
  List: Array of TFileItemDescr; 
  F: TFileStream; 

F := TFileStream.Create( '…', fmOpenRead ); 
F.Read( Head, SizeOf(Head) ); 
if (Head.Signature <> 'IL' ) then raise … 
if (Head.Vervion[1] <> 0) or (Head.Vervion[2] <> 0) then raise … 
SetLength( List, Head.RecordCount ); 
F.Read( List[0], SizeOf(TFileItemDescr) * Head.RecordCount );    

2. Чтение картинки.
Наш формат файла предусматривает возможность чтения произвольной картинки
Код

function Xxx.LoadImage( Index: Integer ): TGraphic; 
var 
  Fi: TMemoryStream; 
begin 
with List[Index] do 
begin 
  Result := nil; 
  If Kind = 'bmp' then Result := TBitmap.Create else 
  if Kind = 'jpg' then Result := TJPEGImage.Create else 
  Exit; 
  F.Seek( Offset, soFromBeginning ); 
  Fi := TMemoryStream.Create; 
  Fi.CopyFrom( F, Size ); 
  Fi.Offset( 0, soFromBeginning ); 
  Result.LoadFromStream( Fi ); 
  Fi.Free; 
end; 
end;    


Ну вот, вроде бы и всё. Надеюсь, что этого будет достаточно, чтобы Вы разобрались и написали работающий код. Все фрагменты кода писались «с ума», т.е. могут содержать описки, ошибки.

По поводу хранения в БД. Конечно, это тоже вариант. Скорее всего он даже даст более простую реализацию. Но по-моему, для такой простой задачи подключать БД – излишество. Тут никаких связей, поисков, только хранение данных в одной единственной табличке.

Автор: Alexeis 13.3.2006, 10:14
Guedda, Зачем так сложно можно ведь в ресурс поместить или
еще лучше в паинте склеить все справа налево сверху вниз тогда
вообще можно будет их грузить при помощи TImageList одной командой.

Автор: Snowy 13.3.2006, 10:20
http://forum.vingrad.ru/index.php?showtopic=40647&view=findpost&p=313213

Автор: Guedda 13.3.2006, 10:21
Я понимаю, что можно и в ресурс (чтобы все картинки были в программе), и по другому... Но человек попросил именно сохранить несколько картинок в один файл. И как, по-твоему, можно сохранить в файл ImageList с картинками быстрее, чем этот способ?

ИМХО, код нормальный, удовлетворяющий требованию участника, который задал этот вопрос.

Автор: Snowy 13.3.2006, 10:22
Если не торопит, то подожди недельку.
Я как раз работаю над такой тулзой, которая собирает файлы в кучу и генерит код для работы с ними.

Автор: Alexeis 13.3.2006, 10:43
Цитата

как, по-твоему, можно сохранить в файл ImageList с картинками быстрее, чем этот способ?


делается картинка на подобии этой
http://alexei-s1.narod.ru/Image2.jpg
А ImageList воспринимает ее как набор картинок одинакового (например 16х16) размера и позволяет обращатся к каждой.

Способ создания не быстрый(в графическом редакторе) зато наглядный.

Автор: Guedda 13.3.2006, 18:22
Я то это знаю... А если каждый раз прийдется? Заново постоянно перерисовывать картинки?

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)