![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Чёрно-белой растр. [D7, XP]
Б © (2009-12-20 00:58:00)Здрасти!
Появилась необходимость вывести черно-белый bmp, да по-быстрее.
Нашёл свой старых исходник, немного подравил.
Procedure XGrayBmp(var B: TBitmap); // 24 bit.
Type
TRGBTripleArray = array [0..0] of TRGBTriple;
Var
x, y: LongWord;
Line: ^TRGBTripleArray;
begin
For y:= 0 to B.Height - 1 do
begin
Line:= B.ScanLine[y];
For x:= 0 to B.Width - 1 do
With Line[x] do
begin
rgbtRed := (rgbtRed + rgbtGreen + rgbtBlue) div 3;
rgbtGreen:= rgbtRed;
rgbtBlue := rgbtRed;
end;
end;
End;
Как бы её оптимизировать малехо?
0x00FF00 (2009-12-20 22:28:00) [21]мм.. стоп, лучше беззнаковое:
MUL EBX
вместо
IMUL EAX, EBX
звиняюсь.
PS.
Кстати, в самом деле, стоит просить у автора "эталонную" картинку.
0x00FF00 (2009-12-20 22:25:00) [22]
> Неочевидно. Команды умножения выполняются дольше команд
> сложения или вычисления адреса.
Согласен.
Поэтому, при "коэффициентном" варианте возможна дальнейшая оптимизация:
MOV EBX, 010101h;
...
@inner_loop:
MOV EDX, DWORD PTR [ESI];
MOVZX EAX, DL;
SHR EDX, 8;
MOVZX EDI, DL;
MOVZX EDX, DH;
LEA EDI, [EDI*4+EDX];
LEA EDI, [EDI+EDX*2];
ADD EAX, EDI;
IMUL EAX, EBX; // выполняется быстрее, чем 3 команды пересылки однобайтных регистров
MOV DWORD PTR [ESI], EAX;
add esi, k
dec ecx
jnz @inner_loop
Anatoly Podgoretsky (2009-12-20 21:42:00) [23]> Игорь Шевченко (20.12.2009 21:37:14) [14]
Команда LOOP очень медленная, пара команд dec ecx/jnz @inner_loop значительно быстрее.
Игорь Шевченко (2009-12-20 21:37:00) [24]
> dec ecx
> jnz @inner_loop
мой комментарий:
loop @inner_loop
> По-моему, так будет быстрее.
Неочевидно. Команды умножения выполняются дольше команд сложения или вычисления адреса.
Вообще-то практика - критерий истины, хотелось бы поиметь эталонную картинку, желательно большого размера, ну и
FlushInstructionCache;
rdtsc
код_1
rdtsc
FlushInstructionCache;
rdtsc
код_2
rdtsc
...
0x00FF00 (2009-12-20 21:16:00) [25]Ах да. Складывается всё без коэффициентов.
Если необходимы коэффициенты, то следует:
MOV BX, 5556h;
сменить на
MOV BX, 2001h;
а
ADD EAX, EDX;
на
LEA EDI, [EDI*4+EDX];
LEA EDI, [EDI+EDX*2];
вроде так.
0x00FF00 (2009-12-20 21:06:00) [26]Небольшой комментарий к коду antonn:
push ebx
push edi
push esi
MOV EBX, 5556h; // (2^16)+3 div 3
@outer_loop:
mov esi, p
mov ecx, i
@inner_loop:
MOV EDX, DWORD PTR [ESI]; // экономим два обращения к памяти
MOVZX EAX, DL;
SHR EDX, 8;
MOVZX EDI, DL;
MOVZX EDX, DH;
ADD EAX, EDX;
ADD EAX, EDI; // всё складываем (при этом сумма не вылезает за WORD)
MUL BX; // и делим на 3 (в итоге имеем результат не более 0FFh, влезающий в DL)
IMUL EDX, 010101; // затем размножаем на 3 байта то, что получилось
MOV DWORD PTR [ESI], EDX; // записываем всё разом
add esi, k
dec ecx
jnz @inner_loop
mov ecx, incr
add p, ecx
dec ii
jnz @outer_loop
pop esi
pop edi
pop ebx
Мои вставки выделены КАПСОМ.
По-моему, так будет быстрее.
Sapersky (2009-12-20 18:43:00) [27]antonn © (20.12.09 01:25) [1]
Ох уж эти маньяки-ассемблерщики... ровно то же самое на Паскале процентов на 10-15 быстрее, несмотря на "ляпы" компилятора (которых есть):
Procedure XGrayBmp(B: TBitmap);
Type
PFColor = ^TFColor;
TFColor = record
b,g,r: Byte;
end;
Var
x, y, h, Gap, dBpp : Integer;
pc : PFColor;
begin
h := B.Height-1;
If (h < 0) or (not (B.PixelFormat in [pf24bit, pf32bit])) then Exit;
If B.PixelFormat = pf24bit then dBpp := 3
else dBpp := 4;
pc := B.ScanLine[h];
If h > 0 then
Gap := (Integer(B.ScanLine[h-1]) - Integer(pc)) - B.Width * dBpp;
For y:= 0 to h do begin
For x:= 0 to B.Width - 1 do begin
With pc^ do begin
b := (b + g * 4 + r * 3) shr 3;
g := b; r := b;
end;
Inc(PByte(pc), dBpp);
end;
Inc(PByte(pc), Gap);
end;
End;
Если уж маньячить по полной - то movzx eax быстрее mov al. Иногда компилятор использует movzx, но в данном случае почему-то нет.
Омлет (2009-12-20 15:06:00) [28]Можно еще оптимизировать мой вариант - избавиться от умножения.
Для этого нужно заранее проициализировать 3 массива с уже умноженными значениями:
var
ArrXB, ArrXG, ArrXR: array[Byte] of Integer;
...
for I := 0 to 255 do
begin
ArrXB[I] := I * 28;
ArrXG[I] := I * 151;
ArrXR[I] := I * 77;
end;
А потом использовать
N := (ArrXB[P[XB]] + ArrXG[P[XG]] + ArrXR[P[XR]]) shr 8;
Омлет (2009-12-20 14:30:00) [29]> [6] antonn © (20.12.09 09:38)
> разве битмап не одним куском идет?
> один цикл, только вычислить где находится начало последней
> строки и с нее начинать:
> for i:=0 to DIB.dsBmih.biHeight*DIB.dsBmih.biWidth-1 do
> в каждом шаге увеличение XB/Xg/Xr
Ты забываешь про выравнивание. Это же 24bit, а не 32bit.
Б (2009-12-20 13:05:00) [30]
> Автор, а нафига var ?
Тогда out. ;)
> простое сложение и деление на три дает очень плохой вариант
Буду делать по стандарту YCbCr.
> Во первых черно-белый это когда 1 бит на пиксель
Старые фотографии называют чёрно-белыми, а не в шкале чёрного.
Anatoly Podgoretsky (2009-12-20 12:57:00) [31]> Б (20.12.2009 00:58:00) [0]
Во первых черно-белый это когда 1 бит на пиксель, а во вторых если речь идет о шкале черного, то простое сложение и деление на три дает очень плохой вариант, складывать надо по формуле и кроме того это должен быть 8 битный формат, а не 24 битный.
antonn (2009-12-20 09:38:00) [32]
> Омлет © (20.12.09 02:29) [4]
разве битмап не одним куском идет?
один цикл, только вычислить где находится начало последней строки и с нее начинать:
for i:=0 to DIB.dsBmih.biHeight*DIB.dsBmih.biWidth-1 do
в каждом шаге увеличение XB/Xg/Xr
Омлет (2009-12-20 02:30:00) [33]> BWThreshold24Bit
Название сменить только надо
Омлет (2009-12-20 02:29:00) [34]
procedure BWThreshold24Bit(Bmp: TBitmap);
var
P : PByteArray;
X, Y, N,
XR, XG, XB,
BytesInScanLine : Integer;
DIB : TDIBSection;
begin
if (Bmp.PixelFormat = pf24Bit) and
(GetObject(Bmp.Handle, SizeOf(DIB), @DIB) = SizeOf(DIB)) and
(DIB.dsBmih.biHeight > 0) then
begin
BytesInScanLine := (((DIB.dsBmih.biWidth * DIB.dsBmih.biBitCount) + 31) and not 31) div 8;
P := DIB.dsBm.bmBits;
for Y := DIB.dsBmih.biHeight - 1 downto 0 do
begin
XB := 0;
XG := 1;
XR := 2;
for X := 0 to DIB.dsBmih.biWidth - 1 do
begin
N := (28*P[XB] + 151*P[XG] + 77*P[XR]) shr 8; { Это по правильной формуле серого 0.299*Red + 0.587*Green + 0.114*Blue }
P[XB] := N;
P[XG] := N;
P[XR] := N;
Inc(XB, 3);
Inc(XG, 3);
Inc(XR, 3);
end;
Integer(P) := Integer(P) + BytesInScanLine;
end;
end;
end;
antonn (2009-12-20 02:10:00) [35]как хорошо что я пометил о "вырезке" =)
> Автор, а нафига var ?
чтобы враг не пролез
Игорь Шевченко (2009-12-20 01:48:00) [36]antonn © (20.12.09 01:25) [1]
> xor ebx, ebx
> xor edx, edx
> @outer_loop:
>
> @inner_loop:
> xor ebx, ebx
> xor edx, edx
это типа оптимизация такая, или в первый раз может не очиститься ? :)
> mov edi, eax
> shl ebx, 2
> add edi, edx
> add edi, ebx
> shl edx, 1
> add edi, edx
> shr edi, 3
> mov eax, edi
А если так:
mov al, byte ptr [esi]
mov bl, byte ptr [esi+1]
mov dl, byte ptr [esi+2]
lea eax,[eax+ebx*4]
lea eax,[eax+edx*2]
add eax, edx
shr eax, 3
...
Не быстрее получится ? (Я может глупость сказал ?)
> Procedure XGrayBmp(var B: TBitmap); // 24 bit.
Автор, а нафига var ?
antonn (2009-12-20 01:25:00) [37]такие вещи в мультимедии обычно спрашивают :)
procedure Greyscale_24_32(bt:tbitmap);
var i,ii,k:integer; p:pointer; incr:integer;
Section:TDIBSECTION;
begin
bt.HandleType:=bmDIB;
GetObject(bt.Handle,sizeof(TDIBSECTION),@Section);
i:=bt.Width;
p:=Section.dsBm.bmBits;
incr:=((Section.dsBmih.biBitCount * Section.dsBmih.biWidth + 31) shr 3) and $FFFFFFFC;
ii:=bt.height;
k:=Section.dsBmih.biBitCount shr 3;
asm
push ebx
push edi
push esi
xor ebx, ebx
xor edx, edx
@outer_loop:
mov esi, p
mov ecx, i
@inner_loop:
xor eax, eax
xor ebx, ebx
xor edi, edi
xor edx, edx
mov al, byte ptr [esi]
mov bl, byte ptr [esi+1]
mov dl, byte ptr [esi+2]
mov edi, eax
shl ebx, 2
add edi, edx
add edi, ebx
shl edx, 1
add edi, edx
shr edi, 3
mov eax, edi
mov byte ptr [esi], al
mov byte ptr [esi+1], al
mov byte ptr [esi+2], al
add esi, k
dec ecx
jnz @inner_loop
mov ecx, incr
add p, ecx
dec ii
jnz @outer_loop
pop esi
pop edi
pop ebx
end;
end;
код слегка вырезан из spriteutils, на моем проце в 3 раза быстрее твоей