![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Формат bitmap [D7, WinXP]
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]