AWARE [SYSTEMS] Imaging expertise voor de Delphi ontwikkelaar
AWare Systems, Imaging expertise voor de Delphi ontwikkelaar, Home TechTalks / Ongebruikte kleur
AWare Systems on-line

Home
Custom development
Imaging
TechTalks
    Bestandformaat uit gegevens
    Tiling versus banding
    VCL TGraphic grondbeginselen
    Ongebruikte kleur
Company
Links

Talen

  English version
  Nederlandse versie

Contact

Informatie: info@awaresystems.be
Helpdesk: support@awaresystems.be
Verkoop: sales@awaresystems.be



Valid HTML 4.01!



Een ongebruikte kleur afleiden uit een Delphi VCL TBitmap

> Ik zoek een manier om een kleur te vinden die niet gebruikt is in een TBitmap.
> Ik wil die "vrije kleur" gebruiken om transparantie te coderen.

Ik neem aan dat uw TBitmap pf24bit of pf32bit is? Een kleinere bitdiepte kan geen probleem van betekenis opleveren, u kunt immers gewoon als volgt tewerk gaan:

  • maak een bit array aan met één bit voor elke mogelijk kleurwaarde
  • zorg dat elke bit in de array op 0 staat
  • scan door de bitmap
    • voor elke pixelkleurwaarde, zet de passende bit in de bit array op 1
  • scan door de bit array tot u een niet ingestelde bit tegenkomt
  • retourneer de index van die niet ingestelde bit als de niet-gebruikte kleurwaarde.

Voor een pf16bit TBitmap zou u bijvoorbeeld de volgende code kunnen gebruiken:

function Bitmap16UnusedIndex(const a: TBitmap): Integer;

var

BitArray: Pointer;

ma: PByte;

mb: Integer;

mc: Integer;

md: PWord;

na: Integer;

nb: Integer;

nba: PByte;

nbb: Integer;

oaa: PByte;

oab: Integer;

ob, oc: Integer;

begin

{$IFDEF DEBUG}

if a.PixelFormat <> pf16bit then

raise Exception.Create('Uitsluitend bedoeld voor 16bit bitmaps');

{$ENDIF}

GetMem(BitArray, 8192);

ZeroMemory(BitArray, 8192);

{m wordt gebruikt om door de bitmap scanlines te scannen}

ma := a.Scanline[0];

mb := Integer(a.Scanline[1])-Integer(a.Scanline[0]);

for mc := 0 to a.Height-1 do

begin

md := PWord(ma);

for na := 0 to a.Width-1 do

begin

nb := md^;

Inc(md);

{opsplitsen van de 16bit-waarde in een bit array index bestaande uit byte pointer en bit mask}

nba := PByte(Cardinal(BitArray)+Cardinal(nb shr 3));

nbb := (128 shr (nb and 7));

{en ervoor zorgen dat de juiste bit wordt gezet}

nba^ := (nba^ or nbb);

end;

Inc(ma, mb);

end;

{oa wordt gebruikt om door de bit array te scannen}

oaa := BitArray;

for oab := 0 to 8191 do

begin

if oaa^ <> 255 then

begin

ob := 0;

oc := 128;

while True do

begin

if (oaa^ and oc) = 0 then break;

Inc(ob);

oc := (oc shr 1);

end;

{complete 16bit-waarde aanmaken}

Result := ((oab shl 3) or ob);

FreeMem(BitArray, 8192);

exit;

end;

Inc(oaa);

end;

FreeMem(BitArray, 8192);

Result := -1;

end;

Deze functie retourneert een ongebruikte 16bit kleurwaarde, of -1 als alle kleuren zijn gebruikt.

Merk op dat u de 16bit kleurwaarde nog moet interpreteren, op welke manier dan ook nodig is; dat de functie louter die kleurwaarde oplevert zoals ze is opgeslagen in een pf16bit bitmap. Merk verder ook op dat we geen misbruik maken van de stack door een 8Kb bit array te declareren in de var sectie, maar dit afzonderlijk alloceren aangezien het geen goed idee is om de stack te gebruiken voor dergelijke grote blokken. Ten slotte gebruiken we geen kwakkels zoals TBitArray en dergelijke, omdat onze gebruikers het resultaat van de functie nog bij leven willen zien...

Het probleem wordt ingewikkelder als we te maken hebben met pf24bit of pf32bit TBitmaps. Als we hetzelfde algorithme zouden gebruiken om een niet-gebruikte kleur te achterhalen, zouden we gebruik moeten maken van een bit array van 2^24 bits, zijnde 2 MB. 8 kilobyte past tegenwoordig makkelijk in elke processor cache, 2 meg duidelijk niet. Op willekeurige wijze 2 meg aanraken voor elke pixel in een TBitmap is, op z'n zachtst gezegd, een uiterst slechte werkwijze.

De makkelijkste oplossing bestaat erin op zoek te gaan naar een niet-gebruikte combinatie van R- en G-waarden, gegeven een bepaalde vaste waarde voor B. Als het algorithme een dergelijke combinatie vindt, dan vormen die R- en G-waarden, samen met de vaste B-waarde, sowieso een niet-gebruikte kleur. Als geen RG combinatie wordt gevonden, kan hetzelfde algorithme worden herhaald op basis van de volgende vaste B-waarde. Voor deze oplossing is dus een bit array van slechts 2^16 bits nodig. Het nadeel is dat we de TBitmap mogelijks meerdere keren moeten doorlopen of scannen. Die multiple scanning is echter niet half zo slecht als het beheren van een 2^24 bits array. Bovendien zal de TBitmap doorgaans niet meerdere malen doorlopen hoeven worden, zal de eerste pass meestal reeds een niet-gebruikte kleur opleveren.

De onderstaande code implementeert de voorgestelde oplossing voor pf24bit bitmaps.

function Bitmap24UnusedColor(const a: TBitmap): TColor;

var

BitArray: Pointer;

p: Integer;

ma: PByte;

mb: Integer;

mc: Integer;

md: PByte;

na: Integer;

nb: Integer;

nba: PByte;

nbb: Integer;

oaa: PByte;

oab: Integer;

ob, oc: Integer;

o: Integer;

begin

{$IFDEF DEBUG}

if a.PixelFormat <> pf24bit then

raise Exception.Create('Uitsluitend bedoeld voor 24bit bitmaps');

{$ENDIF}

GetMem(BitArray, 8192);

{p is the vooropgestelde B waarde}

for p := 0 to 255 do

begin

ZeroMemory(BitArray, 8192);

{m wordt gebruikt om door de bitmap scanlines te scannen}

ma := a.Scanline[0];

mb := Integer(a.Scanline[1])-Integer(a.Scanline[0]);

for mc := 0 to a.Height-1 do

begin

md := ma;

{n wordt gebruikt om door de pixels van de scanline te scannen}

for na := 0 to a.Width-1 do

begin

if md^ = p then

begin

Inc(md);

nb := (md^ shl 8);

Inc(md);

nb := (nb or md^);

Inc(md);

{opsplitsen van de 16bit-waarde in een bit array index bestaande uit byte pointer en bit mask}

nba := PByte(Cardinal(BitArray)+Cardinal(nb shr 3));

nbb := (128 shr (nb and 7));

{en ervoor zorgen dat de juiste bit wordt gezet}

nba^ := (nba^ or nbb);

end

else

Inc(md, 3);

end;

Inc(ma, mb);

end;

oaa := BitArray;

for oab := 0 to 8191 do

begin

if oaa^ <> 255 then

begin

ob := 0;

oc := 128;

while True do

begin

if (oaa^ and oc) = 0 then break;

Inc(ob);

oc := (oc shr 1);

end;

{de complete 16bit waarde berekenen}

o := ((oab shl 3) or ob);

{we zijn er}

FreeMem(BitArray, 8192);

Result := RGB((o and 255), (o shr 8), p);

exit;

end;

Inc(oaa);

end;

end;

FreeMem(BitArray, 8192);

Result := TColor(-1);

end;

Deze functie retourneert ofwel een ongebruikte TColor of TColor(-1) als alle kleuren zijn gebruikt.

Geef ons een seintje als u op de hoogte wilt blijven van de veranderingen of toevoegingen aan deze pagina.