Łączenie dwóch bitmap (optymalizacja scanline)...

0

czesc!
chcialem zrobic ladnie wygladajace okienko: polprzezroczyste, czarne okienko a na nim bialy napis (ale juz nieprzezroczysty)... cos jak przy regulacji glosnosci w macu, tylko z napisem zamiast glosnika:
user image
odpada zwykle ustawienie AlphaBlend formy, bo wtedy wszystko co na niej jest -czyli napis tez - bedzie polprzezroczyste.

kiedys sie bawilem robieniem graficznych form z png lub z bitmapy 32bit (z kanalem alfa).
niestety w tym przypadku dlugosc napisu i rozmiar formy musza sie zmieniac wiec to tez odpada.

pomyslalem ze zamiast ladowac ta bitmape narysuje ja na canvasie i uzyje reszty kodu odpowiedzialnego za zmienianie formy w "graficzna":
http://4programmers.net/Forum/277695

problem mam ze zmiana dwoch bitmap (jedna "normalna" a druga to maska dla przezroczystosci)
w jedna. niby zrobilem, dziala, ale jakos nie jestem z tego zadowolony:


{...}
combined:=TBitmap.Create;
 with combined do
    begin
      Width := rgbBitmap.Width;
      Height := rgbBitmap.Height;
      PixelFormat := pf32bit;
        for y := 0 to combined.Height-1 do
                begin
                LiniaA:=alphaBitmap.ScanLine[y];
                LiniaRGB:=rgbBitmap.ScanLine[y];
                Linia32:=ScanLine[y];

                for x := 0 to combined.Width-1 do
                        begin
                        Linia32[x].rgbRed := MulDiv(LiniaRGB[x].rgbtRed, LiniaA[x], 255);
                        Linia32[x].rgbGreen := MulDiv(LiniaRGB[x].rgbtGreen, LiniaA[x], 255);
                        Linia32[x].rgbBlue := MulDiv(LiniaRGB[x].rgbtBlue, LiniaA[x], 255);
                        Linia32[x].rgbReserved := LiniaA[x];
                        end;
                end;
      end;

macie jakis pomysl jak to zoptymalizowac?

0

Jak chcesz na prawdę szybko To MMX lub SSE (MMX wydaje mi się nico ławiejszy, ogarniecie podstaw do wykonania tego powinno zająć ci ok 2-3h ale będzie to ok 3x szybsze. Pisanie w asm bez użycia tych funkcji nie ma żadnego sensu (+~10% wydajności))

Jak Ci się nie chce to zrób tak:

tc:=GetTickCount;
 with combined do
    begin
      Width := rgbBitmap.Width;
      Height := rgbBitmap.Height;
      PixelFormat := pf32bit;
        for y := 0 to combined.Height-1 do
                begin
                LiniaA:=alphaBitmap.ScanLine[y];
                LiniaRGB:=rgbBitmap.ScanLine[y];
                Linia32:=ScanLine[y];
                LiniaEnd:=Pointer(Cardinal(ScanLine[y])+Combined.Width*4);
                while Cardinal(Linia32)<Cardinal(LiniaEnd) do
                begin
                        TRGBQuad(Linia32^).rgbRed := (TRGBQuad(LiniaRGB^).rgbRed*byte(LiniaA^)) shr 8;
                        TRGBQuad(Linia32^).rgbGreen := (TRGBQuad(LiniaRGB^).rgbGreen*byte(LiniaA^)) shr 8;
                        TRGBQuad(Linia32^).rgbBlue := (TRGBQuad(LiniaRGB^).rgbBlue*byte(LiniaA^)) shr 8;
                        TRGBQuad(Linia32^).rgbReserved := byte(LiniaA^);
                        inc(Cardinal(Linia32),4);
                        inc(Cardinal(LiniaRGB),4);
                        inc(Cardinal(LiniaA),1);
                end;
        end;

      end;

P.S. Skąd wytrzaskałeś taką funkcję jak MulDiv? Z testów wynika że jej wywoływanie MulDiv(a,b,c) jest wolniesze niż zwykłe a*b div c.
P.S 2 w ogóle wykonujesz 4 razy Lina32[x],LiniaA[x] itd co za każdym razem oblicza wskaźnik etc

0
RFabianski napisał(a)

Jak chcesz na prawdę szybko To MMX lub SSE (MMX wydaje mi się nico ławiejszy, ogarniecie podstaw do wykonania tego powinno zająć ci ok 2-3h ale będzie to ok 3x szybsze. Pisanie w asm bez użycia tych funkcji nie ma żadnego sensu (+~10% wydajności))
[...]
P.S. Skąd wytrzaskałeś taką funkcję jak MulDiv? Z testów wynika że jej wywoływanie MulDiv(a,b,c) jest wolniesze niż zwykłe a*b div c.
P.S 2 w ogóle wykonujesz 4 razy Lina32[x],LiniaA[x] itd co za każdym razem oblicza wskaźnik etc

oj za asm wole sie nie zabierac, tym bardziej ze AZ tak mi na szybkosci nie zalezy (ostatecznie bitmapy beda zazwyczaj wielkosci ok 500x110px.
jesli chodzi o to MulDiv to to z jakis kodow wlasnie od robienia graficznej formy z png/32bit bitmapy.
a co do "Lina32[x],LiniaA[x]", dziki musze sie temu przyjrzec.. nigdy nie bylem dobry ze wskaznikow ;]

P.S. à propos asm i bitmap... czy nie da sie potraktowac bitmapy w Delphi jako jakiegos obszaru w pamieci i pracoawc na niej jak na zwyklej tablicy? bylo by to szybsze od ScanLine a jednoczesnie uzywalo by sie tego tak wygodnie (w pewnych przypadkach) jak Canvas.Pixels[] ;]

pozdrawiam

//edit

wywala blad Invalid typecast przy (za pierwsza kropka):

TRGBQuad(Linia32^).rgbtRed := (TRGBQuad(LiniaRGB^).rgbRed*byte(LiniaA^)) shr 8;

:(

0

czy nie da sie potraktowac bitmapy w Delphi jako jakiegos obszaru w pamieci i pracoawc na niej jak na zwyklej tablicy?

Ja dokładnie to robię (no prawie), z tym że wykożystuję wskaźniki (A propos - wszystkie zmiene na których operuję są typu Pointer - zapomniałem dodać - to jest przyczyną twojego errora)

Jeśli w np w zmiennej Line będziesz miał ScanLine[10] to do 15 pixla w tym wierszu odwołujesz się następująco TRGBQuad(Pointer(Cardinal(Line)+(15-1)*4)^):=costam; (przy zalożeniu że masz 32 bity na pixel)

P.S Kod Który podałem powinien działać poprawnie pod warunkiem że rgbbitmap.pixelformat=32bit Jesli masz 24bit to inc w petli dajesz : inc(Cardinal(LiniaRGB),3); Natomiast Bitmapa Alpha powinna być 8bits

0
RFabianski napisał(a)

Ja dokładnie to robię (no prawie), z tym że wykożystuję wskaźniki (A propos - wszystkie zmiene na których operuję są typu Pointer - zapomniałem dodać - to jest przyczyną twojego errora)

Jeśli w np w zmiennej Line będziesz miał ScanLine[10] to do 15 pixla w tym wierszu odwołujesz się następująco TRGBQuad(Pointer(Cardinal(Line)+(15-1)*4)^):=costam; (przy zalożeniu że masz 32 bity na pixel)

no wiem, ale jesli bedziesz chcial sie odwolac raz do 10 raz do 180 raz do jeszcze inego wiersza to musisz wiele razy uzyc ScanLine...
Kiedys zrobilem taka funckje ktora zwracala kolor w xy bitmapy,
byla oparta wlasnie na scanline i uzywanie tej funkcji zajmowalo doklanie tyle samo czasu co canvas.pixels[];
ScanLine jest i tak wolniejsze, niz gdyby operowac na "zwyklej" tablicy...

1

Robiłem ostanio coś takiego, oto część kodu:

type TK32=record
   b,g,r,a:byte;
end;

Type TBmp=class(TBitmap)
  protected
    AofP:array of pointer;
    procedure SetPixel(x,y:integer;Value:Tk32);
    function  GetPixel(x,y:integer):Tk32;
    procedure SetWidth(Value: Integer); override;
    procedure SetHeight(Value: Integer); override;
  public
    constructor Create; override;
    destructor Destroy; override;
    property Pixels[x,y:integer]:tk32 read GetPixel write SetPixel; default;
  end;

....

function K32(a,r,g,b:byte):tk32;
begin
  k32.b:=b;
  K32.g:=g;
  K32.r:=r;
  K32.a:=a;
end;


constructor TBmp.Create;
begin
  inherited Create;
  PixelFormat:=pf32bit;
  SetLength(AofP,0);
end;

destructor TBmp.Destroy;
begin
  Setlength(AofP,0);
  inherited Destroy;
end;

procedure TBmp.SetWidth(Value: Integer);
var i:integer;
begin
  inherited SetWidth(value);
  for i:=0 to Height-1 do
    AofP[i]:=Scanline[i];
end;

procedure TBmp.SetHeight(Value: Integer);
var i:integer;
begin
  inherited SetHeight(Value);
  SetLength(AofP,Value);
  for i:=0 to Value-1 do
    AofP[i]:=Scanline[i];
end;

function TBmp.GetPixel(x,y:integer):TK32;
begin
  result:=TK32(Pointer(Cardinal(AofP[y])+Cardinal(x) shl 2)^);
end;

procedure TBmp.SetPixel(x,y:integer;Value:Tk32);
begin
  TK32(Pointer(Cardinal(AofP[y])+Cardinal(x) shl 2)^):=Value;
end;

Przy czym założenie było takie że używam tylko i wyłącznie 32-bitowego formatu
i teraz masz szybki dostęp do pixeli realizując to np tak :

Bitmapa[x,y]:=k32(255,255,255,0);

1 użytkowników online, w tym zalogowanych: 0, gości: 1