Конференция "Media" » Спрашивали тут ;)
 
  • homm © (21.10.07 15:05) [0]
    Функция
    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;

  • homm © (21.10.07 15:05) [1]
    > на первый из них рисуется на второй

    на первый из них рисуется второй
  • homm © (21.10.07 15:07) [2]
    Ах, да, самое главное забыл :)
    MMX версия работатет в 2,7 раз быстрее паскалевской.
  • homm © (21.10.07 15:54) [3]
    Вариант №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

          //pxor mm0, mm0
          db $0F, $EF, $C0
          mov eax, $01010101
          //movd mm5, eax
          db $0F, $6E, $E8
          //punpcklbw mm5, mm0
          db $0F, $60, $E8
          //movq mm6, mm5
          db $0F, $6F, $F5
          //psllw mm5, 8
          db $0F, $71, $F5, $08

          mov ecx, H
          push ecx
    @@loop1:
            mov esi, [SL2]
            mov edi, [SL1]
            mov ecx, W
    @@loop2:

              //movd mm1, [esi]
              db $0F, $6E, $0E
              //punpcklbw mm1, mm0
              db $0F, $60, $C8
              //movd mm2, [edi]
              db $0F, $6E, $17
              //punpcklbw mm2, mm0
              db $0F, $60, $D0

              mov ah, [esi + 3]
              mov al, ah
              shl eax, 8
              mov al, ah

              //movd mm4, eax
              db $0F, $6E, $E0
              //punpcklbw mm4, mm0
              db $0F, $60, $E0
              //paddw mm4, mm6
              db $0F, $FD, $E6
              //movq mm3, mm5
              db $0F, $6F, $DD
              //psubw mm3, mm4
              db $0F, $F9, $DC

              //pmullw mm1, mm3
              db $0F, $D5, $CB
              //pmullw mm2, mm4
              db $0F, $D5, $D4

              //paddw mm1, mm2
              db $0F, $FD, $CA

              //psrlw mm1, 8
              db $0F, $71, $D1, $08

              //packuswb mm1, mm0
              db $0F, $67, $C8
              //movd [edi], mm1
              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
          //emms
          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;

  • homm © (21.10.07 17:30) [4]
    Черт, зачем писал %)

    Глянул Graphics32.

    Их асемблерный вариант без ММХ работает даже быстрее моего ММХ-а. А уж их ММХ и подавно %)

    Моя в печале.
  • antonn © (21.10.07 20:22) [5]
    я уже нашел, для моего велосипедного класса:
    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;

  • homm © (21.10.07 20:30) [6]
    > [5] antonn ©   (21.10.07 20:22)
    > if BTSourc.CPUisMMX then begin

    А если нет, что будешь делать ;) ?
  • antonn © (21.10.07 20:38) [7]
    вызову другую функицию, порезано у меня тут :)
    а еще в ней плохо то, что она обнуляет альфу конечного изображения в местах блитинга первого :(
  • homm © (21.10.07 20:53) [8]
    > [7] antonn ©   (21.10.07 20:38)
    > а еще в ней плохо то, что она обнуляет альфу конечного изображения
    > в местах блитинга первого :(

    Моя тоже обновляет. Хочешь версию, которая не обновляет? Счас попробуя состряпать.
  • antonn © (21.10.07 20:58) [9]
    не, я бы предпочел ту, которая берет наиболее яркую - если фон боле яркий - берем фон, если у накладываемого более яркая - берем ее (яркая - где более непрозрачная получается). В версии без ММХ все пучком, но хочется в ММХ :)
    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;

  • antonn © (21.10.07 21:11) [10]
    кстати, а сейчас вообще есть процессоры без поддержки ММХ, но наботающие под управлением вин2к минимум? :)
  • homm © (21.10.07 21:16) [11]
    antonn, а ты зачем сам решил велоспортом занятся? :)

    Чем Graphics32 не устроил?
  • homm © (21.10.07 21:18) [12]
    > [9] antonn ©   (21.10.07 20:58)
    > не, я бы предпочел ту, которая берет наиболее яркую - если
    > фон боле яркий - берем фон, если у накладываемого более
    > яркая - берем ее (яркая - где более непрозрачная получается)
    > . В версии без ММХ все пучком, но хочется в ММХ :)

    С ММХ такое — врятли. Там всего 57 команд, вся суть в конвеерной обработке данных, без ветвлений.

    Выкладывать версию, которая не портит фоновую прозрачность?
  • DVM © (21.10.07 21:19) [13]
    без MMX уже найти сложно. PentiumPRO может еще где.
  • antonn © (21.10.07 21:22) [14]

    > antonn, а ты зачем сам решил велоспортом занятся? :)

    мне нужна функция, которая обработает массив (в котором битмап, вообще это небольшая унификация некоторых вещей, но да ладно), а в График32 куча всего и не нужные мне махинации с битмапами (в смысле разные битности и все такое), короче я за велосипедный спорт! :)


    > Выкладывать версию, которая не портит фоновую прозрачность?

    давай, для коррекции:)
    (обожаю ассемблерные процедурки, прям гоню на них:))
  • homm © (21.10.07 21:27) [15]
    > [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;
        //PWORD(P)^ := $0;
        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

          //pxor mm0, mm0
          db $0F, $EF, $C0
          mov eax, $01010101
          //movd mm5, eax
          db $0F, $6E, $E8
          //punpcklbw mm5, mm0
          db $0F, $60, $E8
          //movq mm6, mm5
          db $0F, $6F, $F5
          //psllw mm5, 8
          db $0F, $71, $F5, $08

          xor edx, edx
          mov ecx, H
          push ecx
    @@loop1:
            mov esi, [SL2]
            mov edi, [SL1]
            mov ecx, W
    @@loop2:

              //movd mm1, [esi]
              db $0F, $6E, $0E
              //punpcklbw mm1, mm0
              db $0F, $60, $C8
              //movd mm2, [edi]
              db $0F, $6E, $17
              //punpcklbw mm2, mm0
              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]

              //pmullw mm1, mm3
              db $0F, $D5, $CB
              //pmullw mm2, mm4
              db $0F, $D5, $D4

              //paddw mm1, mm2
              db $0F, $FD, $CA

              //psrlw mm1, 8
              db $0F, $71, $D1, $08

              //packuswb mm1, mm0
              db $0F, $67, $C8
              //movd [edi], mm1
              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
          //emms
          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, реализация осмыслена и переработана.
  • antonn © (21.10.07 22:25) [16]

    > ЗЫ Идея с GenAlphaTable полностью слизана с Graphics32,
    > реализация осмыслена и переработана.

    вот из-за этой альфаТайбл я и не стал разбираться с Г32 :)
  • Sapersky (22.10.07 14:33) [17]
    вся суть в конвеерной обработке данных, без ветвлений.

    Некую имитацию ветвлений можно делать с помощью pcmpeq/pcmpgt и масок, которые они дают; в качестве примера см. вывод с прозрачным цветом (MMXTransPut) из SpriteUtils. Хотя расписывание сколь-нибудь сложного алгоритма с ветвлениями на MMX - это, конечно, кошмар.

    Хорошее описание MMX (со схемами - я только по ним и понял, как работает, например, упаковка/распаковка):
    http://www.tommesani.com/

    А от ассемблера без MMX толку немного. Если хотя бы примерно представлять, как работает оптимизатор и периодически заглядывать в CPU window, можно и на Паскале писать не (ну или не намного) хуже. Система команд ведь та же самая.
    По особенностям компилятора есть такой документ:
    http://dennishomepage.gugs-cats.dk/CodingForSpeedInDelphi.doc
    Не идеальный, но кое-что почерпнуть можно.
  • antonn © (24.10.07 17:31) [18]
    чего то я не пойму, как сделать так, чтобы в первом байте была наиболее яркая альфа, из той же SpriteUtils.
       push      edi
       push      esi

       pxor      mm7, mm7    

       mov       edx, $10101

       push      $001100FF   // маска первого двойного (11 это альфа, по идее там FF:))
       push      $00FF00FF   // маска второго двойного
       movq      mm5, [esp]  // mm5 - $0011.00FF|00FF.00FF
    @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    // получить произведение источника в mm0
       pxor      mm6, mm5    // получить обратную прозрачность в mm6
       pmullw    mm1, mm6    // получить произведение источника в mm0
     
       paddw     mm0, mm1    // сложить

       psrlw     mm0, 8      // переместить в младшую сторону для упаковки

       add       edi, 4

       packuswb  mm0, mm7    // упаковать перед записью на место

    //в mm0 конечные 4 байта

  • homm © (24.10.07 17:41) [19]
    > [18] antonn ©   (24.10.07 17:31)
    > чего то я не пойму, как сделать так, чтобы в первом байте
    > была наиболее яркая альфа

    С помошью ММХ — никак. В крайнем сучае считать альфу до, запоминать, считать пиксель, писать альфу.
 
Конференция "Media" » Спрашивали тут ;)
Есть новые Нет новых   [134431   +10][b:0][p:0.013]