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


Автор: Proxin 26.12.2009, 20:25
В общем, как? Известна высота изображения и ширина. Рылся в сети - ничего не нашёл.

Автор: artsb 26.12.2009, 20:35
Код

bmp.LoadFromStream(stream);

?

Автор: Proxin 26.12.2009, 20:44
Гениально, да. Но не то.
У меня есть файлстрим, в который напихана ещё куча информации. С определённой позиции начианается рисунок. Конец рисунка - не конец файла. Я знаю размеры рисунка (высота и ширина). Куда мне его можно считать и как вывести потом на bitmap?

Автор: artsb 26.12.2009, 20:47
Цитата(Proxin @  26.12.2009,  20:44 Найти цитируемый пост)
Гениально, да. Но не то.

Как вы задали вопрос, так я и ответил smile

Цитата(Proxin @  26.12.2009,  20:44 Найти цитируемый пост)
С определённой позиции начианается рисунок.

Устанавливаете позицию курсора в потоке на нужную позицию и ...
Код

bmp.LoadFromStream(stream);

Всё будет ОК ;)

Добавлено @ 20:48
Цитата(Proxin @  26.12.2009,  20:44 Найти цитируемый пост)
Конец рисунка - не конец файла

Ну и что? LoadFromStream знает когда остановиться.

Автор: Alexeis 26.12.2009, 20:56
Цитата(Proxin @  26.12.2009,  19:44 Найти цитируемый пост)
Куда мне его можно считать и как вывести потом на bitmap? 

  Можно создать промежуточный TMemoryStream и в него сливать картинку.

Добавлено через 1 минуту и 51 секунду
Цитата(artsb @  26.12.2009,  19:47 Найти цитируемый пост)
Ну и что? LoadFromStream знает когда остановиться.

  К сожалению нет. TBitmap тупо читает до конца файла. В связи с этим иногда бывают проблемы, в частности с битмапами 64х64, 256х256 (текстуры)

Автор: artsb 26.12.2009, 21:00
Цитата(Alexeis @  26.12.2009,  20:56 Найти цитируемый пост)
К сожалению нет.

Странно. Я много раз так делал. До сих пор никаких проблем не было...

Добавлено через 2 минуты и 14 секунд
Тогда можно при сохранении, сначала сохранить изображение в промежуточный поток, потом в основной поток записать размер потока с картинкой и саму картинку. А при чтении читаем размер картинки, а потом во временный поток саму картинку и уже из него в битмап.

Автор: Proxin 26.12.2009, 22:01
Вот так должно быть?

Код

filestr.read(memorystr,bytescount);
mybitmap:=tbitmap.create;
mybitmap.width:=mywidth;
mybitmap.height:=myheight;
mybitmap.loadfromstream(memorystr);

Автор: artsb 26.12.2009, 22:12
Напишу на С++
Код

unsigned size;
filestr->Read(&size, sizeof(unsigned));
mybitmap = new Graphics::TBitmap();
// это не нужно
//mybitmap.width:=mywidth;
//mybitmap.height:=myheight;
TMemoryStream *str = new TMemoryStream();
str->CopyFrom(filestr, size);
str->Position = 0;
mybitmap->LoadFromStream(str);
delete str;


Добавлено @ 22:16
Цитата(Proxin @  26.12.2009,  22:01 Найти цитируемый пост)
Вот так должно быть?

А. Ну да. Только установка ширины и высоты излишни. И читайте кусок потока через метод CopyFrom. А перед этой строкой:
Код

mybitmap.loadfromstream(memorystr);

Обязательно позицию курсора в ноль нужно поставить. Иначе ничего не выйдет smile

Автор: Alexeis 26.12.2009, 22:29
Вот это вызывает сомнение
filestr.read(memorystr,bytescount);

Думаю тут должен быть CopyFrom

Автор: artsb 26.12.2009, 22:39
Цитата(Alexeis @  26.12.2009,  22:29 Найти цитируемый пост)
Думаю тут должен быть CopyFrom 

Упс. Вы правы. А я почему-то везде написал просто Copy. smile

Добавлено через 2 минуты и 18 секунд
Цитата(Alexeis @  26.12.2009,  22:29 Найти цитируемый пост)
Вот это вызывает сомнение

мне кажется, что это вообще работать не должно.

Автор: Proxin 26.12.2009, 22:52
сделал с помощью copyfrom. появляется ошибка "bitmap image is not valid" - это значит, что я его не оттуда копирую?

Автор: artsb 26.12.2009, 22:54
Цитата(Proxin @  26.12.2009,  22:52 Найти цитируемый пост)
это значит, что я его не оттуда копирую? 

Возможно...
А в поток вы как сохраняли? SaveToStream?

Автор: Proxin 26.12.2009, 23:12
Это вообще не мой файл - пытаюсь делать парсилку по открытому формату.
Описание дано так: screenshotdata=3*width*height. Width и height я получаю правильно, смотрел с помощью другой парсилки. А вот сам битмап получить по этой технологии уже как-то не получается.
Ещё заметил, что начало файла как-то странно читается - если даже сиком позицию после открытия установить в ноль, то символы читаются начиная со второго только.

Автор: artsb 26.12.2009, 23:20
Ещё бы знать, как они сохраняют картинку... А вы уверены, что там битмап? Может там JPEG?

Цитата(Proxin @  26.12.2009,  23:12 Найти цитируемый пост)
если даже сиком позицию после открытия установить в ноль

Не понял. Где разворачиваются события?

Добавлено через 7 минут и 1 секунду
Побольше бы информации...

Автор: Proxin 26.12.2009, 23:30
rgb - три бита на пиксель без компрессии есть битмап самый натуральный. или я не прав?
ещё, если это поможет - вот описание структуры файла.

конкретно интересует поле screenshot. может,  я что-то неправильно понял (ширину и высоту получаю нормально) ?

Автор: artsb 26.12.2009, 23:43
Цитата(Proxin @  26.12.2009,  23:30 Найти цитируемый пост)
rgb - три бита на пиксель без компрессии есть битмап самый натуральный. или я не прав?

ну да, по идее.
А вы пробовали так читать:
Код

mybitmap = new Graphics::TBitmap();
TMemoryStream *str = new TMemoryStream();
str->CopyFrom(filestr, 3*height*width);
str->Position = 0;
mybitmap->LoadFromStream(str);
delete str;

?

Добавлено @ 23:45
А до скриншота всё нормально считывается?

Добавлено через 9 минут и 6 секунд
Цитата(Proxin @  26.12.2009,  22:52 Найти цитируемый пост)
появляется ошибка "bitmap image is not valid"

мне кажется, это связано с тем, что в файле битмап хранится без шапки, т.е. только биты.
Попробуйте так:
Код

mybitmap->Width = width;
mybitmap->Height = height;
unsigned pos = 0;
for(int i=0; i<height; i++)
 for(int k=0; k<width; k++) {
  mybitmap->Pixels[k][i] = RGB(bits[pos], bits[pos+1], bits[pos+2]);
  pos +=3;
 }

где bits массив битов прочитанных из файла

Автор: Proxin 26.12.2009, 23:57
Да, до скриншота всё нормально.
А так и читаю:
Код

bs:=tmemorystream.create;
bs.copyfrom(f,3*w*h);
bs.position:=0;
bm:=tbitmap.create;
bm.loadfromstream(bs);

Автор: Proxin 27.12.2009, 00:15
А как эти биты считать именно в массив? Они считываются в int64 только.

Автор: Alexeis 27.12.2009, 00:22
3*w*h этого мало. Нужно еще sizeof(BITMAPINFOHEADER), sizeof(BITMAPFILEHEADER) и кроме того учесть 2 выравнивания. 1е выравнивание каждой строки на границу 4х байт + выравнивание всего растра на границу 4х байт. Подробнее можно тут почитать http://forum.vingrad.ru/topic-94227.html

Автор: artsb 27.12.2009, 00:53
Proxin, пришли мне на мыло файлик или выложи и пришли ссылку в личку. Завтра в обед приду, покумекаю.

Автор: artsb 27.12.2009, 13:44
Получилось:
user posted image
Код

// после того, как остальное прочитали
byte *bb = new byte[3*w*h];
f->Read(bb, 3*w*h);
bm = new Graphics::TBitmap();
bm->PixelFormat = pf32bit;
bm->Height = h;
bm->Width = w;
pos = 0;
for(int i=0; i<h; i++)
 for(int k=0; k<w; k++) {
  bm->Canvas->Pixels[k][i] = RGB(bb[pos], bb[pos+1], bb[pos+2]);
  pos+=3;
 }
Image1->Picture->Bitmap->Assign(bm);
delete bm;
delete [] bb;

Автор: Proxin 27.12.2009, 14:41
И ничего... не силён я в переводе с си на паскаль.
Вот как делаю:

Картинка получается абсолютно чёрной.

Автор: artsb 27.12.2009, 14:54
Мне кажется, вы неправильно длину массива bta устанавливаете...
Как в Делфи создать динамический массив?

Автор: Proxin 27.12.2009, 15:10
объявляется переменная array of что-то. затем с помощью setlength ей задаётся длина.

Автор: artsb 27.12.2009, 15:12
Proxin, вы действительно неверно создаёте массив. Написал так:
Код

bta: array [0..150000] of byte;

и всё заработало.
Только индексы в циклах начинаются с 1.

Добавлено @ 15:12
Цитата(Proxin @  27.12.2009,  15:10 Найти цитируемый пост)
затем с помощью setlength ей задаётся длина. 

setlength это для строк.

Добавлено @ 15:14
user posted image

Автор: Proxin 27.12.2009, 15:25
Спасибо Вам большое за помощь. Всё работает.

Автор: artsb 27.12.2009, 15:33
Но, конечно же, нужно всё реализовать так, чтобы массив создавался динамически.

Автор: Proxin 27.12.2009, 15:51
а как установить его длину тогда правильно? во всех мануалах пишут setlength. 

Автор: artsb 27.12.2009, 16:03
Цитата(Proxin @  27.12.2009,  15:51 Найти цитируемый пост)
а как установить его длину тогда правильно?

Я знаю как на С++. smile
Цитата(Proxin @  27.12.2009,  15:51 Найти цитируемый пост)
во всех мануалах пишут setlength.  

По крайней мере, в хелпе эта функция относится к строкам. А может вы просто bta не правильно объявили?

Автор: artsb 27.12.2009, 16:20
Написал так:
Код

setlength(bta, 3*w*h);
i := Length(bta);

i равно 0.

Автор: bems 27.12.2009, 17:44
Цитата(artsb @  27.12.2009,  16:03 Найти цитируемый пост)
По крайней мере, в хелпе эта функция относится к строкам
Эта функция работает и для динамических массивов. Это известно всем, кто прочитал хоть один учебник по языку.

Цитата(artsb @  27.12.2009,  16:20 Найти цитируемый пост)
i равно 0.

1) проверить что w и h больше нуля
2) проверить значение i не встроенным отладчиком а например так
Код

ShowMessage(IntToStr(i))


Автор: artsb 27.12.2009, 17:57
Цитата(bems @  27.12.2009,  17:44 Найти цитируемый пост)
Это известно всем, кто прочитал хоть один учебник по языку.

Я не читал smile

Цитата(bems @  27.12.2009,  17:44 Найти цитируемый пост)
проверить что w и h больше нуля

100% больше нуля

Добавлено @ 18:01
Цитата

2) проверить значение i не встроенным отладчиком а например так

Сейчас проверил. Всё ОК. Массив заполнен нулями. Поэтому картинка чёрная. Значит не происходит чтения из потока. Не верно передаём указатель?

Добавлено @ 18:11
Ха. Вот так работает:
Код

f.read(bta[0], 3*w*h);

Странно у вас всё в Делфи smile

Автор: bems 27.12.2009, 18:20
Да нормально всё у нас.
Для динамического массива bta и bta[0] это разные вещи, потому что bta это ссылка на первый элемент

Автор: artsb 27.12.2009, 18:36
Цитата(bems @  27.12.2009,  18:20 Найти цитируемый пост)
Да нормально всё у нас.

не сомневаюсь ;)
Цитата(bems @  27.12.2009,  18:20 Найти цитируемый пост)
bta это ссылка на первый элемент 

в С++ это указатель на массив.

Автор: bems 27.12.2009, 18:48
Ну я назвал ссылкой условно. Физически это указатель на первый элемент, но синтаксически к элементам обращаются без ^

Цитата(artsb @  27.12.2009,  18:36 Найти цитируемый пост)
в С++ это указатель на массив.

Ну по ходу получается то же самое. А первый параметр read это var-параметр, он и так передается по ссылке. Таким образом когда ты вызываешь f.read(bta, 3*w*h) в метод передается адрес адреса первого элемента. А для статического массива работало, потому что у него переменная-массив лежит по тому же адресу что и первый элемент. Форма f.read(bta[0], 3*w*h) будет работать в обоих случаях (для статического массива еще есть условие что он должен начинаться с нуля)

А вот так совсем универсально
Код

f.read(bta[Low(bta)], 3*w*h);

Автор: artsb 27.12.2009, 18:56
Цитата(bems @  27.12.2009,  18:48 Найти цитируемый пост)
Ну по ходу получается то же самое. А первый параметр read это var-параметр, он и так передается по ссылке. Таким образом когда ты вызываешь f.read(bta, 3*w*h) в метод передается адрес адреса первого элемента. А для статического массива работало, потому что у него переменная-массив лежит по тому же адресу что и первый элемент. Форма f.read(bta[0], 3*w*h) будет работать в обоих случаях (для статического массива еще есть условие что он должен начинаться с нуля)

Спасибо за разъяснение. В принципе, в С++ то же самое. Только в функцию Read передаётся именно указатель на буфер.

Автор: Proxin 27.12.2009, 19:09
Переделал всё с динамическим массивом. Работает так, как описал artsb.
И ещё вопрос. Почему a string perfixed with a byte length не читается полностью?
Пишу так:
Код

f.read(b,1);
f.read(mystr,b);

Строка читается, но без первой буквы.
UPD:
А вот так
Код

f.read(b,1);
f.seek(-1,sofromcurrent);
pl:='';
setlength(pl,b);
f.read(pl,b);
f.seek(1,sofromcurrent);
m.items.add('+'+pl);

Читаются все строки нормально, но без последенй буквы. В чём может быть дело?
Вообще, есть ли хороший материал по потокам - где детально всё на примерах расписано?

Автор: artsb 27.12.2009, 21:25
Proxin, проверил под BCB 6. Всё отлично работает без Seek (с ним наоборот мусор). Значит это уже нюансы языка (Object Pascal). Здесь я, к сожалению, помочь не могу.

ЗЫ обожаю С++ smile

Автор: bems 27.12.2009, 21:37
Попробуй так
Код

var 
  mystr: ShortString;
begin
f.read(mystr[0], 1);
f.read(mystr[1], Length(mystr));


Добавлено через 12 минут и 31 секунду
Это неверно, потому что есть еще ноль в конце

Автор: bems 27.12.2009, 21:53
Тебе нужно посмотреть на файл в хексредакторе и определить включается ли терминатор в байт длины.
Если нет, то так
Код

var 
  mystr: ShortString;
begin
f.read(mystr[0], 1);
f.read(mystr[1], Length(mystr));
f.seek(1, soFromCurrent);


А если да, то
Код

var 
  mystr: ShortString;
begin
f.read(mystr[0], 1);
f.read(mystr[1], Length(mystr));
SetLength(mystr, Length(mystr) - 1)

Автор: Proxin 28.12.2009, 08:02
Спасибо. Понял.

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