Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Формат bitmap [D7, WinXP]


Страницы:   1 2

Woolen ©   (2005-12-21 17:43:00) 

Помогите разобраться в формате, пожалуйста. Не могу понять, что лежит между строк и какого оно размера.
GetPixel - это слишком медленно (знают все), но и обрабатывать в через ScanLine тоже, мягко говоря, не быстро.
Написал по другому:
procedure BitmapDarkness6(Bmp: TBitmap);
var
 PixelCicle: Cardinal;
 FirstClr,SecClr,ThirdClr: PByte;
 LinePtr: PByteArray;
 WinBmp: Windows.TBitmap;
begin
if GetObject(Bmp.Handle,SizeOf(WinBmp),@WinBmp) > 0 then
 begin
 LinePtr := WinBmp.bmBits;
 for PixelCicle := 0 to WinBmp.bmWidth * WinBmp.bmHeight - 1
   do
   begin
   FirstClr := Pointer(Cardinal(LinePtr) + PixelCicle * 3);
   SecClr := Pointer(Cardinal(FirstClr) + 1);
   ThirdClr := Pointer(Cardinal(FirstClr) + 2);
   FirstClr^ := FirstClr^ div 2;
   SecClr^ := SecClr^ div 2;
   ThirdClr^ := ThirdClr^ div 2;
   end;
 end;
end;

Время работы такой процедуры составила менее 39% от варианта со ScanLine, но некоторое количество пикселей не закрашивается. Цикл завершается раньше. То есть, строки, как я понял, лежат не вплотную. Видимо я корежу какую-то служебную информацию между ними, в то время как через ее надо "перешагивать". Поправьте, если я не прав, и помогите разобраться с форматом, пожалуйста. Что там лежит и для чего оно нужно? Спасибо
P.S. Битмап - 24 бита, RGB.


MBo   (2005-12-22 14:50:00) [21]

> MMX
>А у него есть побайтовый сдвиг? Я знаю только о пословном!

Это ничему не мешает. однако использование MMX в данном случае ускоряет незначительно, процентов на 10 (проба на скорую руку)

  PMask^:= $FEFEFEFEFEFEFEFE;
 asm
   mov eax, PMask
   movq mm1, [eax]
   mov eax,p
   mov edx, Size
@@Start:
   movq mm0,[eax]
   pand mm0, mm1
   psrlq mm0,1
   movq [eax],mm0
   add eax,8
   dec edx
   jnz @@Start
   emms
 end;
end;


Sapersky   (2005-12-22 14:11:00) [22]

от ScanLine надо избавляться, только процесорное время жрет

Сейчас посмотрел в реализацию TBitmap - да, накручено изрядно. Я-то обычно использую FastLIB:

http://prdownloads.sourceforge.net/skinner/FastLib.zip?download

Там Scanline - просто свойство, даже поле объекта, и поэтому никаких тормозов не добавляет.

А у него есть побайтовый сдвиг? Я знаю только о пословном!

Точно, нету :(
Тогда лучше MBo ©   (22.12.05 13:54) [7]


MBo   (2005-12-22 13:54:00) [23]

Ограничивает не арифметика, а скорость работы с памятью.
В следующем коде (не вполне корректном) нет почти ничего лишнего. Проверь на своей машине. У меня в полтора раза быстрее, чем BitmapDarkness6


procedure DarkBmp(B:TBitmap);
var
 Delta:Integer;
 P:PInteger;
 i, Size:Integer;
begin
 if B.Height>1 then
   Delta:=Integer(B.Scanline[1])-Integer(B.Scanline[0]);
 Size:=B.Height*Abs(Delta);
 P:=B.Scanline[(B.Height-1)*Ord(Delta<0)];
 for i:= 0 to (Size shr 2)- 1 do begin
   P^:=(P^ and $FEFEFEFE) shr 1;
   Inc(P);
 end;
end;


Woolen   (2005-12-22 12:36:00) [24]


> Указатель лучше не высчитывать для каждого пикселя, а инкрементировать.

А это мысль!

> хотя по идее компилятор сам должен заменять на это

Так и есть. Он сам понимает, что деление на 2, это сдвиг на 1. Он так и делает

> for RowCicle := 0 to Bmp.Height - 1 do begin
>  FirstClr := PByte( Bmp.ScanLine[RowCicle] );
>  for PixelCicle := 0 to Bmp.Width*3 - 1 do begin
>    FirstClr^ := FirstClr^ shr 1;
>   Inc(FirstClr);
>  end;
> end;

Нет, это будет очень медленно. Я уже убедился, что от ScanLine надо избавляться, только процесорное время жрет. От вложенного цикла тоже. При каждом завершении внутреннего цикла - останов, сброс и перезагрузка конвейера гарантирована. А на большинстве нынешних процов - это очень жрет производительность. В особенности это касается Prescott. Там 31 ступень. Кроме того, обрабатывать по одной цветовой компоненте за итерацию медленно. Как правило, процессор может гораздо больше комманд вертеть внутри ядра и кэша, причем NetBurst вообще имеет очередь на 12000 микроопераций уже во внутреннем представлении ядра. Так что я думаю, что надо еще побольше как-то тело цикла сделать. Должно быть быстрее так. А одна компонента за итерацию - медленно, я вчера уже проверял. Так и есть, как я думал.

> MMX

А у него есть побайтовый сдвиг? Я знаю только о пословном!

> Аппаратный вывод графики

Нужно затенить bmp очень быстро и все. Вывод потом не мое дело. Я просто делаю Canvas.Draw.

> 8-битные картинки, у которых нужно обрабатывать только палитру

Нет, 24-бита. И только.


wicked   (2005-12-21 21:54:00) [25]


> 2. Начала строк выравниваются на 4-байтовую границу

афаир размерыстрок выравниваются на 4 байтовую границу.... соответственно, между ними будут промежутки, если картинка не 32 битная или кол-во пикселей в строке не делится нацело на 4....


Sapersky   (2005-12-21 20:01:00) [26]

Указатель лучше не высчитывать для каждого пикселя, а инкрементировать.

for RowCicle := 0 to Bmp.Height - 1 do begin
 FirstClr := PByte( Bmp.ScanLine[RowCicle] );
 for PixelCicle := 0 to Bmp.Width*3 - 1 do begin
   FirstClr^ := FirstClr^ shr 1; // хотя по идее компилятор сам должен заменять на это
  Inc(FirstClr);
 end;
end;

Поскольку все цвета обрабатываются одинаково, можно цикл сделать по байтам. Не знаю, будет ли это быстрее; с одной стороны, меньше переменных - они лучше влезают в регистры, с другой - расписывание каждого цвета по отдельности можно считать за развёртку цикла (приём оптимизации).

Ещё варианты:
1) MMX
2) Аппаратный вывод графики
3) 8-битные картинки, у которых нужно обрабатывать только палитру.


Woolen   (2005-12-21 19:13:00) [27]


> Доступ через Scanline не медленнее использованного способа

Быстрее, но на много смотря как делать. Возможно, я не прав, поправьте, если что.
Даю полные выкладки, потому как объяснять будет долго.
Проц - Celeron (P4) 2 ГГц. На процах с архитектурой, отличной от NetBirst, и (возможно) другим размером кэша, при обработке изображения другого размера, результаты могут немного отличаться. Расчет ведется при помощи вызова процедур, высталяющих и снимающих максимальные приоритеты процесса и потока, снимающих копию регистра RDTSC (счетчика тактов) до и после вызова с вычислением разницы. Есть пограшности на вход, выход таких процедур и т.п.
Вариант 1. Самый простой и распространенный. Принимаю за 100% времени.
procedure BitmapDarkness1(Bmp: TBitmap);
var
 RowCicle,PixelCicle,FirstClr,SecClr,ThirdClr: Cardinal;
 LinePtr: PByteArray;
begin
for RowCicle := 0 to Bmp.Height - 1 do
 begin
 LinePtr := Bmp.ScanLine[RowCicle];
 for PixelCicle := 0 to Bmp.Width - 1 do
   begin
   FirstClr := PixelCicle * 3;
   SecClr := FirstClr + 1;
   ThirdClr := FirstClr + 2;
   LinePtr[FirstClr] := LinePtr[FirstClr] div 2;
   LinePtr[SecClr] := LinePtr[SecClr] div 2;
   LinePtr[ThirdClr] := LinePtr[ThirdClr] div 2;
   end;
 end;
end;

Вариант 2. Оптимизированный со ScanLine'ом. Сотавляет 63% от первого.
procedure BitmapDarkness2(Bmp: TBitmap);
var
 RowCicle,PixelCicle: Cardinal;
 FirstClr,SecClr,ThirdClr: PByte;
 LinePtr: PByteArray;
begin
for RowCicle := 0 to Bmp.Height - 1 do
 begin
 LinePtr := Bmp.ScanLine[RowCicle];
 for PixelCicle := 0 to Bmp.Width - 1 do
   begin
   FirstClr := Pointer(Cardinal(LinePtr) + PixelCicle * 3);
   SecClr := Pointer(Cardinal(FirstClr) + 1);
   ThirdClr := Pointer(Cardinal(FirstClr) + 2);
   FirstClr^ := FirstClr^ div 2;
   SecClr^ := SecClr^ div 2;
   ThirdClr^ := ThirdClr^ div 2;
   end;
 end;
end;

Вариант 3. Составляет 39% от первого.
procedure BitmapDarkness6(Bmp: TBitmap);
var
 PixelCicle: Cardinal;
 FirstClr,SecClr,ThirdClr: PByte;
 LinePtr: PByteArray;
 WinBmp: Windows.TBitmap;
begin
if GetObject(Bmp.Handle,SizeOf(WinBmp),@WinBmp) > 0 then
 begin
 LinePtr := WinBmp.bmBits;
 for PixelCicle := 0 to WinBmp.bmWidth * WinBmp.bmHeight - 1
   do
   begin
   FirstClr := Pointer(Cardinal(LinePtr) + PixelCicle * 3);
   SecClr := Pointer(Cardinal(FirstClr) + 1);
   ThirdClr := Pointer(Cardinal(FirstClr) + 2);
   FirstClr^ := FirstClr^ div 2;
   SecClr^ := SecClr^ div 2;
   ThirdClr^ := ThirdClr^ div 2;
   end;
 end;
end;

Подозреваю, что можно сделать вариант, работающий еще быстрее.

> Начала строк выравниваются на 4-байтовую границу

Спасибо


MBo   (2005-12-21 18:46:00) [28]

1. Доступ через Scanline не медленнее использованного способа
2. Начала строк выравниваются на 4-байтовую границу


PAVIA   (2005-12-21 18:40:00) [29]

Скорее всего там передается 32битная картика. Для 24битной картике код рабоате верно. Там нету никакой дополнительной информации строки идут одна за другой. Только надо помнить что Бэмпешка храниться в обратном порядке.
С таким же успехом ты мог использовать и ScanLine[bmp.Height - 1]


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо