Farben eines Bitmaps manipulieren

Grafische Primitive, XbaseParts und Darstellungsfragen allgemein.

Moderator: Moderatoren

Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 12906
Registriert: Do, 16. Mär 2006 7:55
Wohnort: Hamburg
Hat sich bedankt: 19 Mal
Danksagung erhalten: 45 Mal

Beitrag von AUGE_OHR »

hi,
Jan hat geschrieben:Hallo Jimmy,
ohne dll.ch würde bei das kompilieren nicht funktionieren (hohes Warnlevel). Und die DLL-Funktionen sind auch drin. Es gibt keine bestimmte Position, wo die hin müssen, oder?
hm ... versuche es hiermit mal :

Code: Alles auswählen

PROCEDURE Main
LOCAL oBMP, oPS
LOCAL nEvent, mp1, mp2, oXbp
LOCAL oDlg,aSize,aPos

   aSize := {640,480}
   aPos  := CenterPos( aSize, AppDesktop():currentSize() )

   oDlg         := XbpDialog():new( ,, aPos, aSize )
   oDlg:title   := "Invert BMP"
   oDlg:create()

   oBMP := XbpBitmap():new():create()
   oBMP:loadfile( "SOFI.BMP" )

   oLogo          := XbpStatic():new(oDlg:drawingArea,,{0,0},aSize)
   oLogo:type     := XBPSTATIC_TYPE_BITMAP
   oLogo:autoSize := .F.
   oLogo:caption  := oBMP
   oLogo:create()

   oDa      := oDlg:drawingArea
   aBmpPos  := {0,0}
   aBmpSize := aSize

   NegImage(@oLogo,oDa, @aBmpPos, @aBmpSize)

   oDlg:Show()
   oLogo:Show()

   nEvent := 0
   DO WHILE nEvent <> xbeP_Close
      nEvent := AppEvent( @mp1, @mp2, @oXbp )
      oXbp:handleEvent( nEvent, mp1, mp2 )
   ENDDO

RETURN
... "schnell" ist es nicht, aber es funktioniert.

gruss by OHR
Jimmy
Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 12906
Registriert: Do, 16. Mär 2006 7:55
Wohnort: Hamburg
Hat sich bedankt: 19 Mal
Danksagung erhalten: 45 Mal

Beitrag von AUGE_OHR »

weil es ja nicht "schnell" ist ...

frag mal "Simon Burford <support@ulurudatabase.com>".
Er schrieb in der "archived.generic" vom 19. 11.2005
You will all be very pleased to hear that this works! Very strange, as I
would have imagined this code just converts the value in one direction and back again, but there you are:

cBlendFunction:=Chr(SRC_OVER)+Chr(0)+Chr(128)+Chr(0)
nBlend:=Bin2L(cBlendFunction)
... and then pass nBlend to AlphaBlend instead of cBlendFunction...

Now I can get rid of all the horrible slow code which was doing all my
drawing functions pixel by pixel and use alphablend instead. Tomorrow will be spent converting my greyscale, brightness and contrast, filter and
transformation functions to use alphablend...

Thanks to all that have helped. Hopefully this will make my library appear
sooner rather than later :)
gruss by OHR
Jimmy
Sören
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 205
Registriert: Mo, 07. Aug 2006 10:18
Wohnort: Leipzig
Danksagung erhalten: 11 Mal

Beitrag von Sören »

Hallo,

Weiter vorne hat Jimmy ein ähnliches Beispiel aus "public.xbase++.gui"
gepostet.

Hier ein späteres Posting von Till Warweg zum gleichen Thema,
in dem er evtl. Fehler bei seinem ersten Beitrag einräumt.

Das folgende Programm funktioniert bei mir einwandfrei!
I must have made some mistake, then. Below is a complete
program that just requires "test.bmp" to be present in
the current directory. It ran fine on my system...

Code: Alles auswählen

PROCEDURE Main()

   LOCAL xReturn

   LOCAL oPS, oBMP, oTargetPS, oTgtBitmap
   LOCAL aRGB := Array(3)
   LOCAL aColorTable := {}


   SetColor("W/B+")
   CLS
   oPS := SetAppWindow():presSpace()
   oBMP:= XbpBitmap():new():create( oPS )
   oBMP:loadFile( "test.bmp" )

   /*
   ** Create the destination bitmap object;
   ** note that we'll explicitly request a
   ** format with a Color table; currently
   ** 256 shades of gray.
   */
   oTgtBitmap := XbpBitmap():new():create( oPS )
   oTargetPS :=XbpPresSpace():new():create()
   oTgtBitmap:presSpace( oTargetPS )
   oTgtBitmap:make( oBMP:xSize, oBMP:ySize, oBMP:Planes, 8 )

   FOR i := 0 TO 255
      aRGB := Array( 3 )
      aRGB[1] := i
      aRGB[2] := i
      aRGB[3] := i

      AAdd( aColorTable, aRGB )
   NEXT

   oTargetPS:setColorIndex( 0, aColorTable )

   // Render the source into the destination bitmap;
   // this implicitly maps the source bitmap's color
   // to the colors just defined
   oBMP:draw( oTargetPS, {0,0})

   // Draw the bitmap
   oTgtBitmap:draw(oPS, {0,0})

   WAIT

RETURN
--
Regards,
Till Warweg
Sören
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14653
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Beitrag von Jan »

Sören,

hab ich Tomaten auf den Augen? Wo ist denn der Unterschied zu dem Code von Till, wie er weiter vorne gepostet wurde?

Jan
Sören
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 205
Registriert: Mo, 07. Aug 2006 10:18
Wohnort: Leipzig
Danksagung erhalten: 11 Mal

Beitrag von Sören »

Hallo Jan,

ich hatte den Code vorher nicht verglichen. Da Till Warweg von mgl. Fehlern in seinem Beispiel sprach, habe ich einfach das neuere Beispiel getestet und festgestellt, dass es bei mir problemlos läuft.

Jetzt hab ich Alt und Neu verglichen und muss zugeben, dass auch ich keinen Unterschied erkennen kann.
In dem ersten Code von Till war scheinbar nur die Ausgabe der Bitmap nicht enthalten:

Code: Alles auswählen

   // Draw the bitmap 
   oTgtBitmap:draw(oPS, {0,0}) 
Aber daran wird's wohl nicht gelegen haben?! Oder doch?

Naja, wie gesagt: Ich habe das Programm mit versch. Bmp's und Jpg's getestet - und diese werden, wie versprochen, monochrom (s/w) dargestellt.
Bei Dir nicht? Du sprachst von einem "Klecks", der Dir anstelle einer Grafik ausgegeben wird. Das kann ich so nicht nachvollziehen...

Sören
Günter Beyes
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 315
Registriert: Mo, 16. Okt 2006 13:04
Wohnort: Region Stuttgart

Beitrag von Günter Beyes »

Hallo Jan,

die Ursache für das streifige Graubild (bei 24-Bit-Grafiken) ist gefunden.

Hier ist der geänderte Code, falls Du ihn nochmal ausprobieren möchtest.
Leider funktioniert 24-Bit-PNG mit Transparenzfarbe überhaupt nicht;
ich konnte nicht herausfinden wieso.

Günter

Code: Alles auswählen


#INCLUDE "common.CH"
#include "xbp.ch"

FUNCTION BmpGreyscale( oBmpIn, lNeu )

LOCAL oBmpOut
LOCAL oPS
LOCAL cColorTable
LOCAL nBits             := oBmpIn:bits
LOCAL cBuffer           := oBmpIn:setBuffer()
LOCAL nBufferOffset     := oBmpIn:bufferOffset
LOCAL nXSize            := oBmpIn:xSize
LOCAL nYSize            := oBmpIn:ySize
LOCAL aTransparentColor := GraGetRGBIntensity( oBmpIn:transparentClr )

IF len( cBuffer ) = 0
   MsgBox( "Die Bitmap ist offenbar mit einem Presentation Space verknüpft;"+chr(13) + ;
           "Farbmanipulationen sind daher nicht möglich.",;
		   procname() ;
		  )

   RETURN oBmpIn
ENDIF

DEFAULT lNeu TO TRUE

IF nBits = 24

   /*
   True-color-Bitmap.
   Die Intensität der Grundfarben rot, grün und blau 
   ist für jedes Pixel separat angegeben.
   (Je Grundfarbe 1 Byte: Wert 0 = nicht verwendet; 255 = Maximum).
   
   Die Tabelle der Pixelfarben beginnt bei oBitmap:bufferOffset+1.
   Die Zahl der Einträge (Zahl der Pixel) ist gleich 
   oBitmap:xSize * oBitmap:ySize.
   
   Um an der Farbe zu drehen, muß man die Pixeltabelle direkt bearbeiten.
   */
   
   PixelTableGreyscale( @cBuffer, nBufferOffset, nXSize, nYSize, aTransparentColor )

ELSEIF nBits >= 1 .AND. nBits < 24

   /*
   Ansonsten existiert eine Farbtabelle; die Pixeltabelle stellt lediglich 
   die Zuordnung Pixel -> Farbtabellenindex her.
   
   Um an der Farbe zu drehen, muß man die Farbtabelle bearbeiten.
   
   Leider gibt's keine XbpBitmap-Methode :setColorTable(),
   deshalb wird die geänderte Farbtabelle "zu Fuß" 
   zusammengebaut und dann in den Bitmap-Puffer eingefügt.
   Siehe dazu auch die Stichworte BITMAPINFO und BITMAPINFOHEADER 
   in Microsofts Plattform-SDK oder MSDN.
   */
   
   ColorTableGreyscale( @cColorTable, oBmpIn:getColorTable(), aTransparentColor )

   IF cColorTable <> NIL
      /*
      Den geänderten Bitmap-Puffer zusammenbauen.
      Die ersten vier Bytes enthalten die Länge der BITMAPINFOHEADER-Struktur,
	  gefolgt vom Rest der Struktur.
      Es folgt die Farbtabelle. Sie erstreckt sich bis nBufferOffset (inklusive).
      Ab nBufferOffset+1 kommt schließlich die Zuordnungstabelle 
	  Pixel -> Farbtabellenindex.
      */
      
	  cBuffer := left( cBuffer, Bin2U( substr(cBuffer,1,4) ) ) + ;
                 cColorTable + ;
                 substr( cBuffer, nBufferOffset+1 )

   ENDIF
ENDIF

IF lNeu
   // Ein neues Bitmap-Objekt erzeugen
   
   // Es geht auch ohne Presentation Space
   // oPS  := AppDesktop():LockPS()

   oBmpOut := XbpBitmap():New():Create( /*oPS*/ )

   oBmpOut:Make( nXSize, nYSize, 1, nBits )
   oBmpOut:setBuffer( cBuffer, XBPBMP_FORMAT_WIN3X )

   // ggf die geänderte Transparenzfarbe eintragen
   IF aTransparentColor <> NIL
      oBmpOut:transparentClr := GraMakeRGBColor( aTransparentColor )
   ENDIF 	  

   // AppDesktop():UnlockPS( oPS )
ELSE
   // das vorgegebene Bitmap-Objekt ändern	
   oBmpOut := oBmpIn
   oBmpOut:setBuffer( cBuffer, XBPBMP_FORMAT_WIN3X )

   // ggf die geänderte Transparenzfarbe eintragen
   IF aTransparentColor <> NIL
      oBmpOut:transparentClr := GraMakeRGBColor( aTransparentColor )
   ENDIF 	  
ENDIF

RETURN oBmpOut

******************************************************************************
// cBuffer muß "by reference" übergeben werden

STATIC PROCEDURE PixelTableGreyscale( cBuffer,;
				nBufferOffset, nXSize, nYSize, aTransparentColor )

LOCAL i, nBufferIndex, cGrau

LOCAL nPixelCount       := nXSize * nYSize

// Wieviele "echte" Bytes hat eine Pixelzeile
LOCAL nBytesPerScanline := (nXSize*3)

// Anzahl Füllbytes, damit Pixelzeilenlänge durch 4 teilbar wird
LOCAL nPadding          := nXSize % 4          

LOCAL nByteCounter

nBufferIndex    := nBufferOffset+1
nByteCounter    := 0

FOR i := 1 TO nPixelCount
   /*
   Die Pixeldaten beginnen bei nBufferOffset+1.

   Bei 24-Bit-Truecolor-Bitmaps belegt jedes Pixel drei Byte im Puffer.
   Farbfolge: B, G, R.

   R, G und B jedes Pixels durch den Grauwert ersetzen.

   Grauwert := 0.299 * rot + 0.587 * grün + 0.114 * blau

   Diese Transformationsgleichung kommt aus der Fernsehtechnik 
   und wird dort verwendet, um Farbbilder in Schwarzweiß
   darzustellen.
	
   Es sind 256 verschiedene Graustufen möglich, von
   0 (schwarz) bis 255 (weiß).
 
   Wenn man die Zahl der Graustufen reduziert, z.B. auf 64,
   sieht das Ergebnis unter Umständen besser aus, aber
   das kostet etwas mehr Rechenzeit.
   
   */
   
   cGrau := chr( min( 0.114 * asc( cBuffer[ nBufferIndex ] ) + ;	// B
                      0.587 * asc( cBuffer[ nBufferIndex+1 ] ) + ;  // G
			          0.299 * asc( cBuffer[ nBufferIndex+2 ] ) , ;  // R
				      255 ) )
   
   cBuffer[ nBufferIndex ]   := cGrau
   cBuffer[ nBufferIndex+1 ] := cGrau
   cBuffer[ nBufferIndex+2 ] := cGrau
   
   // zum nächsten Pixel
   nBufferIndex += 3
   
   /*

   [b]Wesentliche Änderung:[/b]   
   Die Länge der Pixelzeilen beträgt stets ein Vielfaches von 4;
   gegebenfalls wird mit chr(0) aufgefüllt.
   Diese Füllbytes müssen bei der Farbmanipulation übersprungen werden;
   die Variable nBufferIndex wird entsprechend hochgezählt.
   */
   
   IF nPadding > 0
	   nByteCounter += 3
	   IF nByteCounter = nBytesPerScanline
	      nByteCounter := 0
		  nBufferIndex += nPadding
	   ENDIF
   ENDIF
NEXT

RETURN

******************************************************************************
// cColorTable muß "by reference" übergeben werden

STATIC PROCEDURE ColorTableGreyscale( cColorTable, aColorTable, aTransparentColor )
LOCAL nColors := len( aColorTable )
LOCAL i, nBufferIndex, nGrau, cGrau
LOCAL lTransparenzSetzen := ( aTransparentColor <> NIL )

   /*
   Jeder Eintrag in der Bitmap-internen Farbtabelle belegt vier Bytes. 
   Drei davon sind die RGB-Werte; der vierte 
   ist für Alpha-Transparenz reserviert.
   */
   
   cColorTable  := replicate( chr(0), 4*nColors )
   nBufferIndex := 1
   FOR i := 1 TO nColors
	   
	   nGrau := int( min( 0.114 * aColorTable[i,3] + ; // B
                          0.587 * aColorTable[i,2] + ; // G
		                  0.299 * aColorTable[i,1] , ; // R
					      255 ) )

	   cGrau := chr( nGrau )
	   
       cColorTable[nBufferIndex]   := cGrau // B
       cColorTable[nBufferIndex+1] := cGrau // G
       cColorTable[nBufferIndex+2] := cGrau // R

       IF lTransparenzSetzen
	      // die transparente Farbe ändern
	      IF aTransparentColor[1] = aColorTable[i,1] .and. ;
		     aTransparentColor[2] = aColorTable[i,2] .and. ;
			 aTransparentColor[3] = aColorTable[i,3]
		  
	         aTransparentColor[1] := nGrau
             aTransparentColor[2] := nGrau
			 aTransparentColor[3] := nGrau
			 
			 // ist jetzt erledigt
			 lTransparenzSetzen := FALSE
		  ENDIF
	   ENDIF
       
	   // zum nächsten Farbeintrag
	   nBufferIndex += 4
   NEXT

RETURN
Benutzeravatar
Martin Altmann
Foren-Administrator
Foren-Administrator
Beiträge: 16516
Registriert: Fr, 23. Sep 2005 4:58
Wohnort: Berlin
Hat sich bedankt: 111 Mal
Danksagung erhalten: 48 Mal
Kontaktdaten:

Beitrag von Martin Altmann »

Hallo Günter,
Dein Code funktioniert (auch) bei mir wunderbar!!
Habe es mal mit Jans Screenshot aus einem anderen Thread versucht - anbei mal die beiden Screens:
Bild

Bild

Viele Grüße,
Martin
:grommit:
Webseite mit XB2.NET und ausschließlich statischem Content in Form von HTML-Dateien: https://www.altem.de/
Webseite mit XB2.NET und ausschließlich dynamischem Content in Form von in-memory-HTML: https://meldungen.altem.de/

Mitglied der XUG Osnabrück
Vorsitzender des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14653
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Beitrag von Jan »

Hey Günter,

1a Lösung!

Jetzt funktioniert es. Das es mit 24 Bit PNG Transparent nicht funktioniert stört mich nicht sonderlich, weil ich die nicht nutze.

Danke!!!

Jan
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15696
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 66 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

Hi,

wenn ich im Code richtig lese, darf die BMP Datei gerade nicht mit einem Presentationspace verknüpft sein oder ?

Könntet Ihr den Code mit einem kleinen Beispiel-Fenster zusammenstellen und mit einem tragenden Titel in die Wissensbasis stellen, das wäre super.
Gruß
Hubert
Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 12906
Registriert: Do, 16. Mär 2006 7:55
Wohnort: Hamburg
Hat sich bedankt: 19 Mal
Danksagung erhalten: 45 Mal

Beitrag von AUGE_OHR »

hi,
brandelh hat geschrieben:
wenn ich im Code richtig lese, darf die BMP Datei gerade nicht mit einem Presentationspace verknüpft sein oder ?
welchen meint du den jetzt ? den von Günter ?
Könntet Ihr den Code mit einem kleinen Beispiel-Fenster zusammenstellen und mit einem tragenden Titel in die Wissensbasis stellen, das wäre super.
... und da wäre auch schon das "Problem" :

gehst du vom "invers" Code aus, welcher der "langsamste" ist, dann
wird dort wie beschrieben das BMP auf einem Xbpstatic erwartet sonst
crasht es bei "LOCAL nHDC := GetWindowDC(oXbp:getHWND())"

nimmst du nun den Code von Till so ist das Hybridcode welcher mit
"meinen" BMP funktioniert und "sehr schnell" ist.

Der Code von Günter scheint mir der "genaueste" zu sein und ist wohl
"univerell" zu verwenden ist da er "nur" ein BMP-Object als Parameter
erwartet und das "Resultat" zurück gibt.

Mein "Test" Code für Günter´s BmpGreyscale()

Code: Alles auswählen

#INCLUDE "common.CH"
#include "xbp.ch"
#include "APPEVENT.CH"

PROCEDURE APPSYS
RETURN

FUNCTION    CENTERPOS( aSize, aRefSize)
RETURN ( { Int( (aRefSize[1] - aSize[1]) / 2 ) ;
                , Int( (aRefSize[2] - aSize[2]) / 2 ) } )

PROCEDURE Main
LOCAL oBMP,oLogo
LOCAL nEvent, mp1, mp2, oXbp
LOCAL oDlg,aSize,aPos

   aSize := {640,480}
   aPos  := CenterPos( aSize, AppDesktop():currentSize() )

   oDlg         := XbpDialog():new( ,, aPos, aSize )
   oDlg:title   := "Invert BMP"
   oDlg:create()

   oBMP := XbpBitmap():new():create()
   oBMP:loadfile( "SOFI.BMP" )    // hier wird mein BMP geladen

   oLogo          := XbpStatic():new(oDlg:drawingArea,,{0,0},aSize)
   oLogo:type     := XBPSTATIC_TYPE_BITMAP
   oLogo:autoSize := .F.
   oLogo:caption  := BmpGreyscale( oBMP )  // Günter´s Funktion
   oLogo:create()

   oDlg:Show()
   oLogo:Show()

   nEvent := 0
   DO WHILE nEvent <> xbeP_Close
      nEvent := AppEvent( @mp1, @mp2, @oXbp )
      oXbp:handleEvent( nEvent, mp1, mp2 )
   ENDDO

RETURN
gruss by OHR
Jimmy
Günter Beyes
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 315
Registriert: Mo, 16. Okt 2006 13:04
Wohnort: Region Stuttgart

Beitrag von Günter Beyes »

Hallo Hubert,
wenn ich im Code richtig lese, darf die BMP Datei gerade nicht mit einem Presentationspace verknüpft sein oder ?
Ja, offenbar ist XbpBitmap:setBuffer() außer Funktion, solange ein Presentationspace verknüpft ist, und liefert dann immer einen Nullstring.

Günter
Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 12906
Registriert: Do, 16. Mär 2006 7:55
Wohnort: Hamburg
Hat sich bedankt: 19 Mal
Danksagung erhalten: 45 Mal

Beitrag von AUGE_OHR »

hi,

ich setzte Günters Lösung erfolgreich ein. Jetzt hab ich die v190.340 geholt
wo ein 32bit transparent nun richtig bei ownerdraw angezeigt wird.

Nun habe ich eine "Problem" mit 32bit "Farbe" nach "greyscale". Es wird
ein "greyscale" angezeigt aber der Background ist nicht transparent.

Da nun das ganze mit 24bit / "grayscale" funktioniert und im Source

Code: Alles auswählen

IF nBits >= 24
   ::PixelTableInvertColors()
steht gehe ich davon aus das es bei 24bit und 32bit gehen müsste, oder ?
gruss by OHR
Jimmy
Antworten