-
Функция AlphaDraw . Принимаемый параметры — 2 32-х битных битмапа, на первый из них рисуется на второй, по альфа маске, которая содержится в резервных битах второго изображения. Изображение копируется на смещение X и Y, которые не должны быть отрицательны. procedure AlphaDraw(DestBitmap, SourceBitmap: TBitmap; X, Y: Integer);
type
ARGBQuad = array [0..0] of TRGBQuad;
PARGBQuad = ^ARGBQuad;
AByte = array [0..0] of Byte;
PAByte = ^AByte;
var
SL1, SL2: PARGBQuad;
V1, V2: TRGBQuad;
R: ^TRGBQuad;
i, j: Integer;
A_1, A: Integer;
Delta1, Delta2: DWORD;
W, H: Integer;
begin
if (X >= DestBitmap.Width) or (Y >= DestBitmap.Height) then
exit;
W := min(SourceBitmap.Width, DestBitmap.Width-X);
H := min(SourceBitmap.Height, DestBitmap.Height-Y);
SL1 := DestBitmap.ScanLine[Y];
SL2 := SourceBitmap.ScanLine[0];
Delta1 := DWORD(DestBitmap.ScanLine[1]) - DWORD(DestBitmap.ScanLine[0]);
Delta2 := DWORD(SourceBitmap.ScanLine[1]) - DWORD(SourceBitmap.ScanLine[0]);
if CPUisMMX then begin
asm
pushad
pxor mm0, mm0
mov eax, $01010101
movd mm5, eax
punpcklbw mm5, mm0
movq mm6, mm5
psllw mm5, 8
mov ecx, H
push ecx
@@loop1:
mov esi, [SL2]
mov edi, [SL1]
mov eax, [X]
lea edi, [edi+ eax*4]
mov ecx, W
@@loop2:
movd mm1, [esi]
punpcklbw mm1, mm0
movd mm2, [edi]
punpcklbw mm2, mm0
mov al, [esi + 3]
mov ah, al
mov bx, ax
shl eax, 16
mov ax, bx
movd mm4, eax
punpcklbw mm4, mm0
paddw mm4, mm6
movq mm3, mm5
psubw mm3, mm4
pmullw mm1, mm3
pmullw mm2, mm4
paddw mm1, mm2
psrlw mm1, 8
packuswb mm1, mm0
movd [edi], mm1
add esi, 4
add edi, 4
dec ecx
jnz @@loop2
mov eax, [Delta1]
add [SL1], eax
mov eax, [Delta2]
add [SL2], eax
dec [esp]
jnz @@loop1
pop ecx
emms
popad
end;
end else begin
for i := 0 to H-1 do begin
for j := 0 to W-1 do begin
V1 := SL1[j+X];
V2 := SL2[j];
A := V2.rgbReserved+1;
A_1 := 256-V2.rgbReserved;
R := @SL1[j+X];
R.rgbBlue := (V2.rgbBlue*A_1+V1.rgbBlue*A) shr 8;
R.rgbGreen := (V2.rgbGreen*A_1+V1.rgbGreen*A) shr 8;
R.rgbRed := (V2.rgbRed*A_1+V1.rgbRed*A) shr 8;
end;
SL1 := Pointer(DWORD(SL1) + Delta1);
SL2 := Pointer(DWORD(SL2) + Delta2);
end;
end;
end;
-
> на первый из них рисуется на второй
на первый из них рисуется второй
-
Ах, да, самое главное забыл :) MMX версия работатет в 2,7 раз быстрее паскалевской.
-
Вариант №2 производительность MMX версии +2% производительность Pascal версии +11% Добавлена совместимость с компилятором пятой дельфи. procedure AlphaDraw(DestBitmap, SourceBitmap: TBitmap; X, Y: Integer);
type
ARGBQuad = array [0..0] of TRGBQuad;
PARGBQuad = ^ARGBQuad;
AByte = array [0..0] of Byte;
PAByte = ^AByte;
var
SL1, SL2: PARGBQuad;
V1, V2: TRGBQuad;
R: ^TRGBQuad;
i, j: Integer;
A_1, A: Integer;
Delta1, Delta2: DWORD;
W, H: Integer;
begin
if (X >= DestBitmap.Width) or (Y >= DestBitmap.Height) then
exit;
W := min(SourceBitmap.Width, DestBitmap.Width-X);
H := min(SourceBitmap.Height, DestBitmap.Height-Y);
SL1 := Pointer(DWORD(DestBitmap.ScanLine[Y])+X*4);
SL2 := SourceBitmap.ScanLine[0];
Delta1 := DWORD(DestBitmap.ScanLine[1]) - DWORD(DestBitmap.ScanLine[0]);
Delta2 := DWORD(SourceBitmap.ScanLine[1]) - DWORD(SourceBitmap.ScanLine[0]);
if CPUisMMX then begin
asm
pushad
db $0F, $EF, $C0
mov eax, $01010101
db $0F, $6E, $E8
db $0F, $60, $E8
db $0F, $6F, $F5
db $0F, $71, $F5, $08
mov ecx, H
push ecx
@@loop1:
mov esi, [SL2]
mov edi, [SL1]
mov ecx, W
@@loop2:
db $0F, $6E, $0E
db $0F, $60, $C8
db $0F, $6E, $17
db $0F, $60, $D0
mov ah, [esi + 3]
mov al, ah
shl eax, 8
mov al, ah
db $0F, $6E, $E0
db $0F, $60, $E0
db $0F, $FD, $E6
db $0F, $6F, $DD
db $0F, $F9, $DC
db $0F, $D5, $CB
db $0F, $D5, $D4
db $0F, $FD, $CA
db $0F, $71, $D1, $08
db $0F, $67, $C8
db $0F, $7E, $0F
add esi, 4
add edi, 4
dec ecx
jnz @@loop2
mov eax, [Delta1]
add [SL1], eax
mov eax, [Delta2]
add [SL2], eax
dec dword ptr [esp]
jnz @@loop1
pop ecx
db $0F, $77
popad
end;
end else begin
for i := 0 to H-1 do begin
for j := 0 to W-1 do begin
V1 := SL1[j];
V2 := SL2[j];
A := V2.rgbReserved+1;
A_1 := 256-V2.rgbReserved;
R := @SL1[j];
R.rgbBlue := (V2.rgbBlue*A_1+V1.rgbBlue*A) shr 8;
R.rgbGreen := (V2.rgbGreen*A_1+V1.rgbGreen*A) shr 8;
R.rgbRed := (V2.rgbRed*A_1+V1.rgbRed*A) shr 8;
end;
SL1 := Pointer(DWORD(SL1) + Delta1);
SL2 := Pointer(DWORD(SL2) + Delta2);
end;
end;
end;
-
Черт, зачем писал %)
Глянул Graphics32.
Их асемблерный вариант без ММХ работает даже быстрее моего ММХ-а. А уж их ММХ и подавно %)
Моя в печале.
-
я уже нашел, для моего велосипедного класса: procedure Bliting_alpha_MMX(BTSourc,BTDest:TBT; x,y:integer);
var SrcBits: DWORD;
DstBits: DWORD;
xTo, sx, YTo, ddx, ddy, sy, w, h, dstw, dsth: integer;
inc1, inc2: integer;
begin
if BTSourc.CPUisMMX then begin
SrcBits := DWORD(BTSourc.P);
DstBits := DWORD(BTDest.p);
w:= BTSourc.DIBWidth;
h:= BTSourc.DIBHeight;
dstw:= BTDest.DIBWidth;
dsth:= BTDest.DIBHeight;
XTo:= x+W-1;
YTo:= y+H-1;
if (y>=dstH) or (x>=dstW) or (YTo<0) or (XTo<0) then exit;
asm
xor eax, eax
mov ddx, eax
mov ddy, eax
end;
sx:= W;
sy:= H;
if X<0 then
begin
ddx:= -X;
inc( sx, X);
x:= 0;
end;
if Y<0 then
begin
ddy:= -Y;
inc( sy, Y);
y:= 0;
end;
if XTo>=dstw then
dec( sx, XTo-dstw+1);
if YTo>=dsth then
dec( sy, YTo-dsth+1);
if (sx<=0) or (sy<=0) then exit;
SrcBits := DWORD(@BTSourc.P^[ (ddy*BTSourc.DIBWidth)+ddx ]);
DstBits := DWORD(@BTDest.P^[(y*BTDest.DIBWidth)+x ]);
inc1:=BTDest.DIBWidth*SizeOf(BTElement);
inc2:=BTSourc.DIBWidth*SizeOf(BTElement);
asm
push edi
push esi
pxor mm7, mm7
mov edx, $10101
push $000000FF
push $00FF00FF
movq mm5, [esp]
@outer_loop:
mov esi, SrcBits
mov edi, DstBits
mov ecx, sx
@inner_loop:
mov eax, [esi]
movd mm0, [esi]
test eax, $FF000000
jz @noblend
shr eax, 24
add esi, 4
imul eax, edx
punpcklbw mm0, mm7
movd mm6, eax
movd mm1, [edi]
punpcklbw mm6, mm7
punpcklbw mm1, mm7
pmullw mm0, mm6
pxor mm6, mm5
pmullw mm1, mm6
paddw mm0, mm1
psrlw mm0, 8
add edi, 4
packuswb mm0, mm7
movd [edi-4], mm0
dec ecx
jnz @inner_loop
jmp @endline
@noblend:
add edi, 4
add esi, 4
dec ecx
jnz @inner_loop
@endline:
mov ecx, inc1
mov eax, inc2
add DstBits, ecx
add SrcBits, eax
dec sy
jnz @outer_loop
add esp, 8
emms
pop esi
pop edi
end;
end;
end;
-
> [5] antonn © (21.10.07 20:22) > if BTSourc.CPUisMMX then begin
А если нет, что будешь делать ;) ?
-
вызову другую функицию, порезано у меня тут :) а еще в ней плохо то, что она обнуляет альфу конечного изображения в местах блитинга первого :(
-
> [7] antonn © (21.10.07 20:38) > а еще в ней плохо то, что она обнуляет альфу конечного изображения > в местах блитинга первого :(
Моя тоже обновляет. Хочешь версию, которая не обновляет? Счас попробуя состряпать.
-
не, я бы предпочел ту, которая берет наиболее яркую - если фон боле яркий - берем фон, если у накладываемого более яркая - берем ее (яркая - где более непрозрачная получается). В версии без ММХ все пучком, но хочется в ММХ :) procedure Bliting_alpha(BTSourc,BTDest:TBT; x,y:integer);
var SrcBits: DWORD; DstBits: DWORD;
xTo, sx, YTo, ddx, ddy, sy, w, h, dstw, dsth: integer;
inc1, inc2: integer;
begin
w:= BTSourc.DIBWidth;
h:= BTSourc.DIBHeight;
dstw:= BTDest.DIBWidth;
dsth:= BTDest.DIBHeight;
XTo:= x+W-1;
YTo:= y+H-1;
if (y>=dstH) or (x>=dstW) or (YTo<0) or (XTo<0) then exit;
asm
xor eax, eax
mov ddx, eax
mov ddy, eax
end;
sx:= W;
sy:= H;
if X<0 then
begin
ddx:= -X;
inc( sx, X);
x:= 0;
end;
if Y<0 then
begin
ddy:= -Y;
inc( sy, Y);
y:= 0;
end;
if XTo>=dstw then
dec( sx, XTo-dstw+1);
if YTo>=dsth then
dec( sy, YTo-dsth+1);
if (sx<=0) or (sy<=0) then exit;
SrcBits := DWORD(@BTSourc.P^[ (ddy*BTSourc.DIBWidth)+ddx ]);
DstBits := DWORD(@BTDest.P^[(y*BTDest.DIBWidth)+x ]);
inc1:=BTDest.DIBWidth*SizeOf(BTElement);
inc2:=BTSourc.DIBWidth*SizeOf(BTElement);
asm
push ebx
push edi
push esi
@outer_loop:
mov ecx, sx
mov edi, DstBits
mov esi, SrcBits
@loop:
mov bl, byte ptr [esi+3]
mov bh, byte ptr [esi+3]
and bl, bl
je @skiptransparent
mov al, [esi]
not bh
mul al, bl
mov dl, ah
mov al, [edi]
mul al, bh
add dl, ah
mov [edi], dl
mov al, [esi+1]
mul al, bl
mov dl, ah
mov al, [edi+1]
mul al, bh
add dl, ah
mov [edi+1], dl
mov al, [esi+2]
mul al, bl
mov dl, ah
mov al, [edi+2]
mul al, bh
add dl, ah
mov [edi+2], dl
@skiptransparent:
add esi, 4
add edi, 4
dec ecx
jnz @loop
@l1:
mov ecx, inc1
mov eax, inc2
add DstBits, ecx
add SrcBits, eax
dec sy
jnz @outer_loop
pop esi
pop edi
pop ebx
end;
end;
-
кстати, а сейчас вообще есть процессоры без поддержки ММХ, но наботающие под управлением вин2к минимум? :)
-
antonn, а ты зачем сам решил велоспортом занятся? :)
Чем Graphics32 не устроил?
-
> [9] antonn © (21.10.07 20:58) > не, я бы предпочел ту, которая берет наиболее яркую - если > фон боле яркий - берем фон, если у накладываемого более > яркая - берем ее (яркая - где более непрозрачная получается) > . В версии без ММХ все пучком, но хочется в ММХ :)
С ММХ такое — врятли. Там всего 57 команд, вся суть в конвеерной обработке данных, без ветвлений.
Выкладывать версию, которая не портит фоновую прозрачность?
-
без MMX уже найти сложно. PentiumPRO может еще где.
-
> antonn, а ты зачем сам решил велоспортом занятся? :)
мне нужна функция, которая обработает массив (в котором битмап, вообще это небольшая унификация некоторых вещей, но да ладно), а в График32 куча всего и не нужные мне махинации с битмапами (в смысле разные битности и все такое), короче я за велосипедный спорт! :)
> Выкладывать версию, которая не портит фоновую прозрачность?
давай, для коррекции:) (обожаю ассемблерные процедурки, прям гоню на них:))
-
> [14] antonn © (21.10.07 21:22) > а в График32 куча всего и не нужные мне махинации с битмапами > (в смысле разные битности и все такое)
Да нет, как я посмотрел, там вокруг TBitmap32 все крутится. > давай, для коррекции:)
procedure GenAlphaTable;
var
I: Integer;
P: ^Longword;
begin
GetMem(AlphaTable, 256*16);
P := AlphaTable;
for I := 1 to 256 do begin
P^ := I + I shl 16;
Inc(P);
P^ := I;
Inc(P);
end;
for I := 256 downto 1 do begin
P^ := I + I shl 16;
Inc(P);
P^ := I + $1000000;
Inc(P);
end;
end;
procedure AlphaDraw(DestBitmap, SourceBitmap: TBitmap; X, Y: Integer);
type
ARGBQuad = array [0..0] of TRGBQuad;
PARGBQuad = ^ARGBQuad;
AByte = array [0..0] of Byte;
PAByte = ^AByte;
var
SL1, SL2: PARGBQuad;
V1, V2: TRGBQuad;
R: ^TRGBQuad;
i, j: Integer;
A_1, A: Integer;
Delta1, Delta2: DWORD;
W, H: Integer;
begin
if AlphaTable = nil then
GenAlphaTable;
if (X >= DestBitmap.Width) or (Y >= DestBitmap.Height) then
exit;
W := min(SourceBitmap.Width, DestBitmap.Width-X);
H := min(SourceBitmap.Height, DestBitmap.Height-Y);
SL1 := Pointer(Integer(DestBitmap.ScanLine[Y])+X*4);
SL2 := SourceBitmap.ScanLine[0];
Delta1 := DWORD(DestBitmap.ScanLine[1]) - DWORD(DestBitmap.ScanLine[0]);
Delta2 := DWORD(SourceBitmap.ScanLine[1]) - DWORD(SourceBitmap.ScanLine[0]);
if CPUisMMX then begin
asm
pushad
db $0F, $EF, $C0
mov eax, $01010101
db $0F, $6E, $E8
db $0F, $60, $E8
db $0F, $6F, $F5
db $0F, $71, $F5, $08
xor edx, edx
mov ecx, H
push ecx
@@loop1:
mov esi, [SL2]
mov edi, [SL1]
mov ecx, W
@@loop2:
db $0F, $6E, $0E
db $0F, $60, $C8
db $0F, $6E, $17
db $0F, $60, $D0
mov dl, [esi+3]
mov ebx, [AlphaTable]
lea ebx, [ebx+edx*8]
movq mm3, [ebx]
add ebx, 256*8
movq mm4, [ebx]
db $0F, $D5, $CB
db $0F, $D5, $D4
db $0F, $FD, $CA
db $0F, $71, $D1, $08
db $0F, $67, $C8
db $0F, $7E, $0F
add esi, 4
add edi, 4
dec ecx
jnz @@loop2
mov eax, [Delta1]
add [SL1], eax
mov eax, [Delta2]
add [SL2], eax
dec dword ptr [esp]
jnz @@loop1
pop ecx
db $0F, $77
popad
end;
end else begin
for i := 0 to H-1 do begin
for j := 0 to W-1 do begin
V1 := SL1[j];
V2 := SL2[j];
A_1 := V2.rgbReserved+1;
if A_1 = 1 then
else if A_1 = $100 then begin
SL1[j] := SL2[j];
end else begin
A := 256-V2.rgbReserved;
R := @SL1[j];
R.rgbBlue := (V2.rgbBlue*A_1+V1.rgbBlue*A) shr 8;
R.rgbGreen := (V2.rgbGreen*A_1+V1.rgbGreen*A) shr 8;
R.rgbRed := (V2.rgbRed*A_1+V1.rgbRed*A) shr 8;
end;
end;
SL1 := Pointer(DWORD(SL1) + Delta1);
SL2 := Pointer(DWORD(SL2) + Delta2);
end;
end;
end; По хорошему еше в конце обработки нужно где-то пристроить FreeMem(AlphaTable); ЗЫ Идея с GenAlphaTable полностью слизана с Graphics32, реализация осмыслена и переработана.
-
> ЗЫ Идея с GenAlphaTable полностью слизана с Graphics32, > реализация осмыслена и переработана.
вот из-за этой альфаТайбл я и не стал разбираться с Г32 :)
-
вся суть в конвеерной обработке данных, без ветвлений.Некую имитацию ветвлений можно делать с помощью pcmpeq/pcmpgt и масок, которые они дают; в качестве примера см. вывод с прозрачным цветом (MMXTransPut) из SpriteUtils. Хотя расписывание сколь-нибудь сложного алгоритма с ветвлениями на MMX - это, конечно, кошмар. Хорошее описание MMX (со схемами - я только по ним и понял, как работает, например, упаковка/распаковка): http://www.tommesani.com/А от ассемблера без MMX толку немного. Если хотя бы примерно представлять, как работает оптимизатор и периодически заглядывать в CPU window, можно и на Паскале писать не (ну или не намного) хуже. Система команд ведь та же самая. По особенностям компилятора есть такой документ: http://dennishomepage.gugs-cats.dk/CodingForSpeedInDelphi.docНе идеальный, но кое-что почерпнуть можно.
-
чего то я не пойму, как сделать так, чтобы в первом байте была наиболее яркая альфа, из той же SpriteUtils. push edi
push esi
pxor mm7, mm7
mov edx, $10101
push $001100FF push $00FF00FF movq mm5, [esp] @outer_loop:
mov esi, SrcBits
mov edi, DstBits
mov ecx, sx
@inner_loop:
mov eax, [esi]
movd mm0, [esi]
test eax, $FF000000
jz @noblend shr eax, 24
add esi, 4
imul eax, edx punpcklbw mm0, mm7
movd mm6, eax
movd mm1, [edi]
punpcklbw mm6, mm7
punpcklbw mm1, mm7
pmullw mm0, mm6 pxor mm6, mm5 pmullw mm1, mm6
paddw mm0, mm1
psrlw mm0, 8
add edi, 4
packuswb mm0, mm7
-
> [18] antonn © (24.10.07 17:31) > чего то я не пойму, как сделать так, чтобы в первом байте > была наиболее яркая альфа
С помошью ММХ — никак. В крайнем сучае считать альфу до, запоминать, считать пиксель, писать альфу.
|