Region vom Bitmap erstellen...
Hi,
isch möschde eine Region (HRGN) von einem Bitmap auf Basis einer bestimmten Farbe erstellen. Soweit sogut. Das eigentliche Problem an der Sache ist das Auffinden und Zusammensetzen der Regionen. Ich kann ja jeden passenden Pixel zu einer Region "adden", das ist mir aber zu unoptimal™. Nun suche ich einen Algo. der was taugt. Hat jemand einen Vorschlag ? ( ich hatte mir schon mal vor längerer zeit was gebastelt, leider finde ich es nicht mehr... ) |
Re: Region vom Bitmap erstellen...
ich weiß grad nicht wie, aber man konnte auch ein (Schwarz/Weiß) BitMap irgendwie direkt zur Region hinzufügen.
|
Re: Region vom Bitmap erstellen...
Ja, irgend wie mit ExtCreateRegion. Bin gerade am Nachforschen...
|
Re: Region vom Bitmap erstellen...
im Notfall wirklich Pixel für Pixel :freak:
http://www.bobpowell.net/region_from_bitmap.htm http://www.codeguru.com/cpp/g-m/bitm...icle.php/c1751 |
Re: Region vom Bitmap erstellen...
Liste der Anhänge anzeigen (Anzahl: 1)
Also mal von C nach D.
Delphi-Quellcode:
Das ist allerding (in etwa) genauso wie mein alter Code nur das ich nur mit CombineRgn() und CreateRoundRgn() arbeitete.
function BitmapToRegion(bmp: TBitmap; TransparentColor: TColor): HRGN;
const AllocUnit = 100; type PRectArray = ^TRectArray; TRectArray = array[0..(MaxInt div SizeOf(TRect)) - 1] of TRect; var pr: PRectArray; // used to access the rects array of RgnData by index h: HRGN; // Handles to regions RgnData: PRgnData; // Pointer to structure RGNDATA used to create regions x, y, x0: Integer; // coordinates of current rect of visible pixels maxRects: Cardinal; // Number of rects to realloc memory by chunks of AllocUnit begin Result := 0; maxRects := AllocUnit; GetMem(RgnData, SizeOf(RGNDATAHEADER) + (SizeOf(TRect) * maxRects)); try with RgnData^.rdh do begin dwSize := SizeOf(RGNDATAHEADER); iType := RDH_RECTANGLES; nCount := 0; nRgnSize := 0; SetRect(rcBound, MAXLONG, MAXLONG, 0, 0); end; for y := 0 to bmp.Height - 1 do begin x := 0; while x < bmp.Width - 1 do begin // Pixel suchen die der transp. Farbe entsprechen & x solange erhöhen x0 := x; while x < bmp.Width - 1 do begin // ohne scanline zu Testzwecken - mit (Windows.)GetPixel if GetPixel(bmp.Canvas.Handle, x, y) = DWORD(TransparentColor) then break; Inc(x); end; // test to see if we have a non-transparent area in the image if x > x0 then begin // increase RgnData by AllocUnit rects if we exceeds maxRects if RgnData^.rdh.nCount >= maxRects then begin Inc(maxRects, AllocUnit); ReallocMem(RgnData, SizeOf(RGNDATAHEADER) + (SizeOf(TRect) * MaxRects)); end; // Add the rect (x0, y)-(x, y+1) as a new visible area in the region pr := @RgnData^.Buffer; // Buffer is an array of rects with RgnData^.rdh do begin SetRect(pr[nCount], x0, y, x, y + 1); // adjust the bound rectangle of the region if we are "out-of-bounds" if x0 < rcBound.Left then rcBound.Left := x0; if y < rcBound.Top then rcBound.Top := y; if x > rcBound.Right then rcBound.Right := x; if y + 1 > rcBound.Bottom then rcBound.Bottom := y + 1; Inc(nCount); end; end; // if x > x0 // Need to create the region by muliple calls to ExtCreateRegion, 'cause // it will fail on Windows 98 if the number of rectangles is too large if RgnData^.rdh.nCount = 2000 then begin h := ExtCreateRegion(nil, SizeOf(RGNDATAHEADER) + (SizeOf(TRect) * maxRects), RgnData^); if Result > 0 then begin // Expand the current region CombineRgn(Result, Result, h, RGN_OR); DeleteObject(h); end else // First region, assign it to Result Result := h; RgnData^.rdh.nCount := 0; SetRect(RgnData^.rdh.rcBound, MAXLONG, MAXLONG, 0, 0); end; Inc(x); end; // scan every sample byte of the image end; // need to call ExCreateRegion one more time because we could have left // a RgnData with less than 2000 rects, so it wasn't yet created/combined h := ExtCreateRegion(nil, SizeOf(RGNDATAHEADER) + (SizeOf(TRect) * MaxRects), RgnData^); if Result > 0 then begin CombineRgn(Result, Result, h, RGN_OR); DeleteObject(h); end else Result := h; finally FreeMem(RgnData, SizeOf(RGNDATAHEADER) + (SizeOf(TRect) * MaxRects)); end; end; procedure TForm1.FormCreate(Sender: TObject); var ARgn: HRGN; ABitmap: TBitmap; begin ABitmap := TBitmap.Create; try ARgn := BitmapToRegion(Image1.Picture.Bitmap, clFuchsia); SetWindowRgn(Form1.Handle, ARgn, True); finally ABitmap.Free; end; end; Irgend wie heht das sicherlich auch anders / einfacher... |
Re: Region vom Bitmap erstellen...
Vorallen da ich mir etwas sichter bin, mal eine Version gesehn zu haben, wo ein Bitmap direkt einer Funktion gegeben wurde und fertig war's :nerd:
Aber ich hatte vor 'ner ganzen Weile selber nochmal danach gesucht und nix gefunden, wobei ich mir halt sicher war das noch 'ne größere Weile vorher (ist schon bestimmt mindestens 3 Jahre her) mal gesehn zu haben :? |
Re: Region vom Bitmap erstellen...
Zitat:
Zitat:
In der TRgnData-Strucktur müsste man den "Daten"-Buffer irgend wie nutzen. Aber erst mal muss man verstehen wie und was TRgnData-Strucktur macht usw. |
Re: Region vom Bitmap erstellen...
Meinst du sowas?
Delphi-Quellcode:
Das erzeugt einer Region anhand eines Bitmaps. Das Fenster hat dann die Form des Bitmaps. Als transparent wird die Farbe der Pixels 0,0 festgelegt (glaub ich) ;)
type
TRGBArray = array[0..32767] of TRGBTriple; PRGBArray = ^TRGBArray; type TfrmMain = class(TForm) ... private { Private-Deklarationen } HG: TBitmap; FRegion: THandle; function CreateRegion(Bmp: TBitmap): THandle; public ... end; implementation function TfrmMain.CreateRegion(Bmp: TBitmap): THandle; var X, Y, StartX: Integer; Excl: THandle; Row: PRGBArray; TransparentColor: TRGBTriple; begin Bmp.PixelFormat := pf24Bit; Result := CreateRectRGN(0, 0, Bmp.Width, Bmp.Height); for Y := 0 to Bmp.Height - 1 do begin Row := Bmp.Scanline[Y]; StartX := -1; if Y = 0 then TransparentColor := Row[0]; for X := 0 to Bmp.Width - 1 do begin if (Row[X].rgbtRed = TransparentColor.rgbtRed) and (Row[X].rgbtGreen = TransparentColor.rgbtGreen) and (Row[X].rgbtBlue = TransparentColor.rgbtBlue) then begin if StartX = -1 then StartX := X; end else begin if StartX > -1 then begin Excl := CreateRectRGN(StartX, Y, X + 1, Y + 1); try CombineRGN(Result, Result, Excl, RGN_DIFF); StartX := -1; finally DeleteObject(Excl); end; end; end; end; if StartX > -1 then begin Excl := CreateRectRGN(StartX, Y, Bmp.Width, Y + 1); try CombineRGN(Result, Result, Excl, RGN_DIFF); finally DeleteObject(Excl); end; end; end; end; //Borderstyle muss bsNone sein procedure TfrmMain.FormCreate(Sender: TObject); var Bmp: TBitmap; begin HG:=TBitmap.Create; HG.LoadFromFile(ResPath+'Monitor.bmp'); Bmp := TBitmap.Create; try bmp.Assign(HG); Width:=HG.Width; ClientHeight:=HG.Height; FRegion := CreateRegion(Bmp); SetWindowRGN(Handle, FRegion, True); finally Bmp.Free; end; ... end; procedure TfrmMain.FormDestroy(Sender: TObject); begin DeleteObject(FRegion); HG.Free; end; procedure TfrmMain.FormPaint(Sender: TObject); begin BitBlt(canvas.handle, 0, 0, Clientwidth, Clientheight,HG.Canvas.handle, 0, 0, SRCCOPY); end; |
AW: Region vom Bitmap erstellen...
Ich möchte meinen Button eine region verpassen
Mit Standard Button (TButton) funktioniert das aber leider nicht mit nonvcl Button. Habe es mal mit beiden Functionen versucht. Diese hier.. http://www.delphipraxis.net/948896-post5.html und auf dieser Seite. http://www.delphipraxis.net/404171-post7.html beides will nicht funktionieren. Warum ist mir schleierhaft. jemand eine Idee?
Delphi-Quellcode:
if Region > 0 then
begin GdipCreateHBITMAPFromBitmap(pointer(img), hbmReturn, $000000); if hbmReturn <> 0 then begin ABitmap := TBitmap.Create; ABitmap.Handle := hbmReturn; ABitmap.Width := btW; ABitmap.Height:= btH; FHImageButton := CreateWindowEx(WS_EX_TRANSPARENT, SKIMAGEBUTTON, '', Style, x, y, btW, btH, hOwner, ButID, SkinEngine.skInstance, nil); if FHImageButton <> 0 then begin SkinEngine.SetImageProperty(FHImageButton, PROP_IMAGE_BACK, Img); SkinEngine.SetImageProperty(FHImageButton, PROP_STYLE, BS_BITMAP); SkinEngine.SetImageProperty(FHImageButton, PROP_STATEMAX, StateMax); ARgn := BitmapToRegion(ABitmap.Handle, imgw, imgh, RGB(255,0,255), 0); SetWindowRgn(FHImageButton, ARgn, True); ABitmap.Free; end else begin // Lösche das Image SkinEngine.DeleteResource(hOwner, Img); end; end; gruss |
AW: Region vom Bitmap erstellen...
Push .. :duck:
Hab mal ein Button Beispiel von Luckie umgestrickt. (nonvcl) Also der Rosa bereich des Button soll abgeschnitten werden. Vielleicht schaut ja mal jemand in den Sample rein. gruss |
AW: Region vom Bitmap erstellen...
Niemand eine Idee? :cry:
gruss |
AW: Region vom Bitmap erstellen...
Ich dachte mal, daß ich da auch irgendwann einmal sowas gesehn hatte,
abr als ich letztens danach suchte, fand ich nur eine (etwas langsame) Funktion, welche das Bild Pixelweise auslas und dieses in eine Region eintrug. :shock: Wäre och gespannt, ob es dennoch was Besseres gibt. |
AW: Region vom Bitmap erstellen...
Wenn du mein projekt im vorherigen Thread mal laden würdest ;)
Button.rar. Grundsätzlich funktioniert das ja aber nur in der VCL mit einem TButton nicht in nonvcl. Das ist was mich stutzig macht. gruss |
AW: Region vom Bitmap erstellen...
Oh komma, hier gibt es ja nocheine Seite. :shock:
Na mal sehn ... vielleicht schau ich am WE nochmal rein. |
AW: Region vom Bitmap erstellen...
Liste der Anhänge anzeigen (Anzahl: 2)
Zitat:
Zitat:
Man muss die richtigen Window-Attribute setzen. ;) |
AW: Region vom Bitmap erstellen...
Zitat:
EDIT: Nur WS_CLIPSIBLINGS vergessen? EDIT2: Doch nicht so einfach.. hmmm Warum wird der Button trotzdem gedrückt obwohl ich im leeren bereich klicke? Es sieht also so aus das ich das Bitmap selbst auch noch ausschneiden muss. Was aber eigentlich wenn der bereich Transparent ist nicht nötig sein sollte. gruss Emil |
AW: Region vom Bitmap erstellen...
Will immer noch nicht so wie ich es will :)
Siehe Bild!
Delphi-Quellcode:
Ich erstelle ein HBitmap vom Bitmap
if Region > 0 then
begin Style := WS_CHILD or WS_VISIBLE or WS_CLIPSIBLINGS or BS_PUSHBUTTON; if ButID = ID_LED then Style := Style or WS_DISABLED; GdipLoadImageFromFile(PWideChar(FullPathImageName), img); GdipCreateHBITMAPFromBitmap(pointer(img), hbmReturn, $000000); if hbmReturn <> 0 then begin ABitmap := TBitmap.Create; ABitmap.Handle := hbmReturn; ABitmap.Width := btW; ABitmap.Height:= btH; FHImageButton := CreateWindowEx(WS_EX_TRANSPARENT, SKIMAGEBUTTON, '', Style, x, y, btW, btH, hOwner, ButID, SkinEngine.skInstance, nil); if FHImageButton <> 0 then begin SkinEngine.SetImageProperty(FHImageButton, PROP_IMAGE_BACK, Img); SkinEngine.SetImageProperty(FHImageButton, PROP_STYLE, BS_BITMAP); SkinEngine.SetImageProperty(FHImageButton, PROP_STATEMAX, StateMax); ARgn := RegionFromBitmap(ABitmap.Handle, $FF00FF); SetWindowRgn(FHImageButton, ARgn, true); ABitmap2 := TBitmap.Create; ABitmap2.Width := ImgW; ABitmap2.Height:= ImgH; ARgn2 := RegionFromBitmap(hbmReturn, $FF00FF); SetWindowRgn(ABitmap2.Handle, ARgn2, true); ABitmap2.Free; ABitmap.Free; end else begin // Lösche das Image SkinEngine.DeleteResource(hOwner, Img); end; end; end Danach jage ich ABitmap durch die Function RegionFromBitmap Der Button wird dann Ordnungsgemäß ausgeschnitten. Aber wie man sieht kann man immer noch den Bereich Magenta des Images sehen. Ich dachte eigentlich das der ausgeschnittene Bereich des Button wenn ein Image überlagert wird nicht mehr sichtbar sein sollte. Ist er aber... Nun gehe ich hin und erstelle eine zusätzliche Maske vom Image selbst das als Strip abgelegt ist und 2 Bilder in einem enthält. Down/Up Status.
Delphi-Quellcode:
Jetzt jage ich ABitmap2 ebenfalls durch diese Function.
function TSkinImageButton.RegionFromBitmap(hbmp: HBitmap;
TransColor: ColorRef): HRGN; type TRectArray = array[0..0] of TRect; var bm: Bitmap; pData: PRgnData; lpRect: PRect; hRgn2: HRGN; hRgn1: HRGN; sRegionData: THandle; MaxRegions: integer; I, J, K, M: integer; pr: ^TRectArray; p32: PByte; lPixel: PRGBQuad; begin hRgn1 := 0; lPixel := nil; GetObject(hbmp, SIZEOF(bm), @bm); if TransColor = 0 then TransColor := cardinal(lPixel) and $FFFFFF; MaxRegions := 4000; sRegionData := GlobalAlloc(GMEM_MOVEABLE, SizeOf(RGNDATAHEADER) + SizeOf(TRECT) * MaxRegions); pData := GlobalLock(sRegionData); pData^.rdh.nCount := 0; pData^.rdh.dwSize := sizeof(RGNDATAHEADER); pData^.rdh.iType := RDH_RECTANGLES; pData^.rdh.nRgnSize := 0; pData^.rdh.rcBound.Left := 0; pData^.rdh.rcBound.Top := 0; pData^.rdh.rcBound.Right := bm.bmWidth; pData^.rdh.rcBound.Bottom := bm.bmHeight; lpRect := PRECT(@pData^.Buffer); p32 := PByte(integer(bm.bmBits) + (bm.bmHeight - 1) * bm.bmWidthBytes); for J := 0 to bm.bmHeight - 1 do begin I := 0; while I < bm.bmWidth do begin M := I; lPixel := Pointer(integer(p32) + I * SizeOf(cardinal)); while I < bm.bmWidth do begin K := MakeColor(0, lPixel^.rgbRed, lPixel^.rgbGreen, lPixel^.rgbBlue) and $FFFFFF; if K <> integer(TransColor) then begin Inc(I); Inc(lPixel); end else Break; end; if I > M then begin if integer(pData^.rdh.nCount) >= MaxRegions then begin hRgn2 := ExtCreateRegion(nil, sizeof(RGNDATAHEADER) + (sizeof(TRECT) * pData^.rdh.nCount), PRGNDATA(pData)^); if hRgn1 = 0 then begin hRgn1 := hRgn2; end else begin CombineRgn(hRgn1, hRgn1, hRgn2, RGN_OR); SkinEngine.skDeleteObject(hRgn2); end; pData^.rdh.nCount := 0; end; pr := @pData^.Buffer; SetRect(pr^[pData^.rdh.nCount], M, J, I, J + 1); if M < lpRect.Left then lpRect.Left := M; if I > lpRect.Right then lpRect.Right := I; if J < lpRect.Top then lpRect.Top := J; if J > lpRect.Bottom then lpRect.Bottom := J + 1; Inc(pData^.rdh.nCount); Inc(lpRect); end; Inc(I); end; Dec(p32, bm.bmWidthBytes); end; hRgn2 := ExtCreateRegion(nil, sizeof(RGNDATAHEADER) + (sizeof(TRECT) * pData^.rdh.nCount), pData^); if hRgn1 = 0 then begin hRgn1 := hRgn2; end else begin CombineRgn(hRgn1, hRgn1, hRgn2, RGN_OR); DeleteObject(hRgn2); end; GlobalFree(sRegionData); DeleteObject(hbmp); Result := hRgn1; end; Aber der Magenta bereich wird nicht ausgeschnitten bzw. das Bitmap nicht aktualisiert.
Delphi-Quellcode:
Ist das Handle vom ABitmap2 nicht gültig ?
ARgn2 := RegionFromBitmap(hbmReturn, $FF00FF);
SetWindowRgn(ABitmap2.Handle, ARgn2, true); Oder woran hapert es denn jetzt schon wieder. Kann zwar den Magenta Bereich mit den Halbton Button überlagern dann tritt aber das problem hier auf. Klicke ich die C Taste geht alles wunderbar wenn ich aber nach dem loslassen dann C# klicke und in dem Magentabereich der C Taste komme dann werden zwei Töne gleichzeitig gespielt. C+C# Puhh hoffe dass das noch jemand ließt. PS: Das Image transparent im bereich Magenta zu machen (müßte dann ein zusätzliches MaskImg anlegen um den Button auszuschneiden) bringt auch nichts. Auch hier werden 2 Tasten betätigt wenn ich auf den Transparenten bereich klicke (Das ist der aktuelle status was weg soll). gruss |
AW: Region vom Bitmap erstellen...
Es will einfach nicht.
Ich ersetze jetzt die Farbe mit
Delphi-Quellcode:
aber das Resultat ist das gleiche als wenn ich den bereich direkt Transparent mache im Bitmap selbst.
if SkinEngine.GetProperty(Winhandle, PROP_REGION) = 1 then
begin if GdipCreateImageAttributes(ImgAtt) = 0 then begin TransColor := SkinEngine.ColorARGB(255, RGB(255, 0, 255)); if GdipSetImageAttributesColorKeys(ImgAtt, ColorAdjustTypeDefault, True, 0, TransColor) = 0 then begin GdipDrawImageRectRectI(Graphics, Img, x, y, ImgW, ImgH, ImgW * cardinal(UseState) - ImgW, 0, ImgW, ImgH, 2, ImgAtt, False, nil) end; end; end else GdipDrawImageRectRectI(Graphics, Img, x, y, ImgW, ImgH, ImgW * cardinal(UseState) - ImgW, 0, ImgW, ImgH, 2, nil, False, nil); Warum läßt sich das Bitmap nicht ausschneiden? geht doch beim Button auch.. hmmm Oder ist es möglich das nach dem down status des Button die Region von diesen wieder auf default gesetzt wird? Sehen kann ich das nicht. Seltsamerweise Wenn ich noch keinen Button geklickt habe lassen sich keine zwei Button gleichzeitig drücken. gruss |
AW: Region vom Bitmap erstellen...
Warum heißt SetWindowRgn wohl SetWindowRgn?
Richtig, weil es für Fenster, sprich TWinControls funktioniert. Ein TBitmap.Handle hat nichts damit zu tun, ist nur "leider" zuweisungskompatibel da es halt auch eine einfache "Hausnummer" ist(LongWord). Nimm ein TWinControl (z.B. TPanel), packt das Image drauf und wende SetWindowRgn auf Panel.Handle an. Grüsse, Dirk |
AW: Region vom Bitmap erstellen...
Zitat:
Zeig mal wie du in einer Nonvcl Anwendung ein TPanel oder was auch immer deiner meinung nach dafür passt einbinden willst. Mein Problem mit dem Bitmap hab ich schon gelößt wie im vorherigen Beitrag mitgeteilt. GdipSetImageAttributesColorKeys Zitat:
Auch wenn es jetzt wieder transparent ist wird ein Klickevent auf dem Transparenten bereich ausgeführt. Hat mit dem Ausgeschnittenen Button nix zu tun das funktioniert nach nochmaligen testen. PS: Ach so das Panel auf einen Button knallen würde ja auch nicht gehn. So einfach ist es also nicht. gruss |
AW: Region vom Bitmap erstellen...
Zitat:
Technisch und inhaltlich ist jecoh nichts einzuwenden. |
AW: Region vom Bitmap erstellen...
Zitat:
Er konnte ja nicht wissen das ich in Nonvcl arbeite. Obwohl es nicht böse gemeint war. gruss |
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:39 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz