Filter nach Maske auf dem Bildschirm

Fragen zu Grafik- & Soundproblemen und zur Spieleprogrammierung haben hier ihren Platz.
Benutzeravatar
Bobo-Jack
Beiträge: 26
Registriert: 08.09.2010 11:59

Filter nach Maske auf dem Bildschirm

Beitrag von Bobo-Jack »

Hallo :)

Ich suche eine Möglichkeit, einen Filter (einfach die Farben aufm Bildschirm etwas abdunkeln etwa) auf den Bildschirm anzuwenden, so ähnlich wie bei DisplayRGBFilter().

Allerdings gibt's ne Maske die über den Bildschirm passt, und der Maske entsprechend sollen nur bestimmte Bereiche schattiert werden.

Die Formen auf der Maske sind mehr oder weniger beliebig. Der Hintergrund ist, dass auf einer Tilemap einzelne Felder mit unterschiedlichen kleinen Masken schattiert werden sollen. Bisher hab ich kleine Masken für jedes Tile einzeln angezeigt, das geht aber jetzt aus bestimmten Gründen nichtmehr so.

Hab schon einen Versuch gemacht, bei dem direkt im Videospeicher gearbeitet wird, ist aber leider sau langsam... hm.
Ihr braucht zum Testen ein großes Sprite mit Irgendwas, so groß wie der Bildschrim etwa, und eine Maskensprite, bei dem alles was nicht Schwarz ist schattiert wird.

Mich hätte mal interessiert wie DisplayRGBFilter arbeitet, denn der Befehl ist ja Pfeilschnell und vielleicht könnte man die Arbeitsweise irgendwie kopieren mit Maskenunterstützung.

Habt ihr noch ne andere Idee wie man das so umsetzen könnte, dass es möglichst schnell ist?

Vielen Dank für eure Ideen!! <)

Code: Alles auswählen

; Maskensprite:
;
; $000000      -> Filter wirkt nicht
; Alles andere -> Filter wirkt
; 

BG_Path.s = OpenFileRequester("Background Image", "", "", 0)
M_Path.s = OpenFileRequester("Mask File", "", "", 0)

; ---------------

; > procedure coded by STARGÅTE!
; Korrigiert einen zu kleinen oder zu großen Wert für einen Farbwert
Procedure Real255(Wert)
 If Wert < 0
  ProcedureReturn 0
 ElseIf Wert > 255
  ProcedureReturn 255
 Else
  ProcedureReturn Wert
 EndIf
EndProcedure

; > procedure coded by STARGÅTE!
; Gibt die Graufarbe einer Farbe zurück
;  Intensity = 0.0  ->  Farbig
;  Intensity = 1.0  ->  Grau
;  GrayColor :   Graufarbe die erreich werden soll (RGB-Farbe)     
Procedure ColorGray(Color, Intensity.f, GrayColor=$FFFFFF)
 Protected R, G, B, Gray.f
 #GrayColorFactorRed   = 0.299
 #GrayColorFactorGreen = 0.587
 #GrayColorFactorBlue  = 0.114
 Gray = #GrayColorFactorRed*Red(Color) + #GrayColorFactorGreen*Green(Color) + #GrayColorFactorBlue*Blue(Color)
 R =   Red(GrayColor) * Gray/255*Intensity +   Red(Color) * (1-Intensity)
 G = Green(GrayColor) * Gray/255*Intensity + Green(Color) * (1-Intensity)
 B =  Blue(GrayColor) * Gray/255*Intensity +  Blue(Color) * (1-Intensity)
 ProcedureReturn RGB(R,G,B)
EndProcedure

; --------------

InitSprite()
InitKeyboard()

UsePNGImageDecoder()
UseJPEG2000ImageDecoder()
UseJPEGImageDecoder()

ExamineDesktops()
OpenScreen(DesktopWidth(0), DesktopHeight(0), DesktopDepth(0), "")

Global BG_Spr = LoadSprite(#PB_Any, BG_Path)
Global M_Spr = LoadSprite(#PB_Any, M_Path)

Procedure FPS()
   Static NextSecond
   Static Counter
   Static FPS
   Protected Now
   
   Now = ElapsedMilliseconds()
   If Now >= NextSecond
      FPS = Counter
      If NextSecond = 0 : NextSecond = Now : EndIf
      NextSecond + 1000
      Counter = 1
   Else
      Counter + 1
   EndIf
   
   ProcedureReturn FPS
EndProcedure

Procedure DisplayShadedSprite(Sprite, x, y, MaskSprite=-1)
  
  Protected UseMask.b
  
  If IsSprite(MaskSprite)
    UseMask = #True
  EndIf
  
  DisplayTransparentSprite(Sprite, x, y)
  
  ; /// Get Information
  
  StartDrawing(ScreenOutput())
  Protected s_mem = DrawingBuffer()
  Protected s_pitch = DrawingBufferPitch()
  Protected s_format = DrawingBufferPixelFormat()
  Protected s_width = OutputWidth()
  Protected s_height = OutputHeight()
  StopDrawing()
  Protected *s_line.long
  
  If UseMask
    StartDrawing(SpriteOutput(M_Spr))
    Protected m_mem = DrawingBuffer()
    Protected m_pitch = DrawingBufferPitch()
    Protected m_format = DrawingBufferPixelFormat()
    Protected m_width = OutputWidth()
    Protected m_height = OutputHeight()
    StopDrawing()
    Protected *m_line.long
  EndIf
  
  ; /// Do it
  
  Protected.u x_max, y_max
  If UseMask
    y_max = m_height-1
    x_max = m_width-1 
  Else
    y_max = s_height-1
    x_max = s_width-1
  EndIf
    
  For y = 0 To y_max
    
    If UseMask
      *m_line = m_mem+m_pitch*y
    EndIf
    
    *s_line = s_mem+s_pitch*y
    
    For x = 0 To x_max 
      
      If UseMask = #False Or *m_line\l > 0
        *s_line\l = ColorGray(*s_line\l, 0.5)
      EndIf
        
      
      *s_line + 4
      
      If UseMask
        *m_line + 4
      EndIf
        
    Next
    
  Next
      
  
EndProcedure

Repeat
  FlipBuffers()
  ClearScreen($000000)
  ExamineKeyboard()
  
  DisplayShadedSprite(BG_Spr, 10, 10, M_Spr)
  
  StartDrawing(ScreenOutput())
  DrawText(OutputWidth()-100, 10, Str(FPS()))
  StopDrawing()
  
Until KeyboardPushed(#PB_Key_Escape)
End

Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7031
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Filter nach Maske auf dem Bildschirm

Beitrag von STARGÅTE »

Du kannst doch mit Sprite3D() arbeiten.
Dann kannst du dein Masken-Sprite mit Alpha-Kanal und dem passenden BlendingMode() direkt mit dem Hintergrund "verschmelzen".

Ansonsten kannst du noch den CustomFilterCallback() nutzen.

"ist aber leider sau langsam... "
Vermutlich weil du mit Debugger laufen lässt, der bremst natürlich diese doppelschleife der Pixel extrem aus.

EDIT: hier mal ein gekürztes Beispiel zum CustomFilterCallback() mit Masken

Code: Alles auswählen


Enumeration
	#DrawingFilter_Desaturate
EndEnumeration


Structure DrawingFilterInclude
	Filter.i
	StructureUnion
		Parameter.i
		Mode.i
	EndStructureUnion
EndStructure

Global DrawingFilterInclude.DrawingFilterInclude


Procedure DrawingFilter_Desaturate(X.i, Y.i, SourceColor.i, DestinationColor.i)
	
	Protected Gray.i = (DestinationColor>>16&$FF + DestinationColor>>8&$FF + DestinationColor&$FF) / 3
	Protected Factor.f = Red(SourceColor)/255
	Protected Color.i = RGBA(Red(DestinationColor)*(1-Factor)+Gray*Factor, Green(DestinationColor)*(1-Factor)+Gray*Factor, Blue(DestinationColor)*(1-Factor)+Gray*Factor, Alpha(DestinationColor))
	ProcedureReturn Color
	
EndProcedure

Procedure DrawingFilter(Filter.i, Parameter.i=#Null)
	
	DrawingFilterInclude\Filter = Filter
	
	Select Filter
		Case #DrawingFilter_Desaturate
			DrawingFilterInclude\Mode = Parameter
			CustomFilterCallback(@DrawingFilter_Desaturate())
	EndSelect
	
EndProcedure



Enumeration
	#Window
	#Gadget
	#Image
	#SourceImage
	#MaskImage
EndEnumeration



InitNetwork()
UsePNGImageDecoder()

If FileSize(GetTemporaryDirectory()+"earth-icon.png") = -1
	Define URL.s = "http://icons.iconarchive.com/icons/treetog/junior/256/earth-icon.png"
	ReceiveHTTPFile(URL, GetTemporaryDirectory()+"earth-icon.png")
EndIf
If FileSize(GetTemporaryDirectory()+"Mask.png") = -1
	Define URL.s = "http://data.unionbytes.de/Mask.png"
	ReceiveHTTPFile(URL, GetTemporaryDirectory()+"Mask.png")
EndIf

LoadImage(#SourceImage, GetTemporaryDirectory()+"earth-icon.png")
LoadImage(#MaskImage,  GetTemporaryDirectory()+"Mask.png")

CopyImage(#SourceImage, #Image)
If StartDrawing(ImageOutput(#Image))
	DrawingMode(#PB_2DDrawing_CustomFilter)
	DrawingFilter(#DrawingFilter_Desaturate)
	DrawImage(ImageID(#MaskImage), 0, 0)
	StopDrawing()
EndIf

OpenWindow(#Window, 0, 0, 768, 256, "Image", #PB_Window_MinimizeGadget|#PB_Window_ScreenCentered)
ImageGadget(#PB_Any, 0, 0, 256, 256, ImageID(#SourceImage))
ImageGadget(#PB_Any, 256, 0, 256, 256, ImageID(#MaskImage))
ImageGadget(#PB_Any, 512, 0, 256, 256, ImageID(#Image))

Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
Bobo-Jack
Beiträge: 26
Registriert: 08.09.2010 11:59

Re: Filter nach Maske auf dem Bildschirm

Beitrag von Bobo-Jack »

Danke für die antwort :D
STARGÅTE hat geschrieben:Vermutlich weil du mit Debugger laufen lässt, der bremst natürlich diese doppelschleife der Pixel extrem aus.
Stimmt (dumm von mir das zu vergessen).

Würde Sprite3D in der Hauptschleife nicht ziemlich auf Kosten der Geschwindigkeit gehen?
Dieser Filter muss ja für jedes Frame neu angewendet werden.
Habs bisher gemieden, Sprite3D zu verwenden und versucht alles mit normalen Sprites zu realisieren..

CustomFilterCallback() ist ne gute Idee und das Beispiel sieht echt klasse aus, ist doch aber laut der Hilfe nur auf Images anwendbar?
(2DDrawing in meiner Hauptschleife hab ich ebenfalls gemieden, weils ja oft heißt wie langsam das ist... oder täusch ich mich da)

Weißt du oder irgendjemand noch etwas zum DisplayRGBFilter() und warum das so schnell funktioniert?
Man musste den Filter eben auch auf andere Formen anwenden können... hmm.
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7031
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Filter nach Maske auf dem Bildschirm

Beitrag von STARGÅTE »

Würde Sprite3D in der Hauptschleife nicht ziemlich auf Kosten der Geschwindigkeit gehen?
[...]
Habs bisher gemieden, Sprite3D zu verwenden und versucht alles mit normalen Sprites zu realisieren..
Ganz im gegenteil. Sprite3D läuft mit Hardwereunterstützung (ist ja eine Art Mini-3D-Engine für 2D-Sprites), ist also um Längen schneller als das normale Sprite.
(2DDrawing in meiner Hauptschleife hab ich ebenfalls gemieden, weils ja oft heißt wie langsam das ist... oder täusch ich mich da)
Du verwendest doch 2DDrawing in deiner DisplayShadedSprite()-Prozedur.
Das "langsamme" bei 2DDrawing ist das StartDrawing()/StopDrawing(), und das ist ja eh schon bei dir drin!
Und gegenüber der Doppelschleife ist CustomFilterCallback() schneller.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
Bobo-Jack
Beiträge: 26
Registriert: 08.09.2010 11:59

Re: Filter nach Maske auf dem Bildschirm

Beitrag von Bobo-Jack »

STARGÅTE hat geschrieben:Sprite3D läuft mit Hardwereunterstützung (ist ja eine Art Mini-3D-Engine für 2D-Sprites), ist also um Längen schneller als das normale Sprite.
Tatsächlich, grade getestet. :o Wenn ich das zu Beginn meines Projekts gewusst hätte.. werd ich wohl nochmal komplett umschreiben.
Immerhin schon stolze ~17000 Zeilen, na super aber danke fürs mich drauf aufmerksam machen! :)

Werd das jetzt mit Sprite3D und BlendingMode() umsetzen.
Ist das dann eigentlich wirklich so tragisch, dass das Sprite3D kein Quadrat ist?

Wegen dem Start/StopDrawing(), da hab ich die Zeichenbufferadresse, Pitch, Format usw. einmal gespeichert um zu vermeiden, jedesmal Zeichenfunktionen verwenden zu müssen.

Naja, also besten Dank :allright:
Drago
Beiträge: 148
Registriert: 02.02.2010 18:22
Computerausstattung: Win XP SP3, AMD Sempron (MMX) 1.2 GHz, 512 MB, Nvidia GeForce FX 5200, 128 MB, DirectX 9.0c
Wohnort: Westerwald

Re: Filter nach Maske auf dem Bildschirm

Beitrag von Drago »

Bobo-Jack hat geschrieben:Ist das dann eigentlich wirklich so tragisch, dass das Sprite3D kein Quadrat ist?
Hi, nicht wirklich. Es sei denn das Programm soll auch noch auf uralte Mühlen rennen, aber fast alle Grafikkarten können mittlerweile
auch andere Formate....

Lg Klaus
Antworten