Entwickler-Ecke

WPF / Silverlight - Per PixelShader manipuliertes Bild neuzeichnen


FrEEzE2046 - Mi 28.04.10 12:38
Titel: Per PixelShader manipuliertes Bild neuzeichnen
Hallo,

ich habe einen simplen HLSL Pixel Shader, der Kontrast und Helligkeit verändert.
Das ganze ist dafür gedacht, ein Bild aufleuchten zu lassen.

Das aufleuchten soll "flüssig" ablaufen, also kontinuierlich bis zum eingestellten Maximum.
In meiner ShaderEffect Klasse, habe ich Brightness und Contrast als DependencyPropertys deklariert. Ich möchte jetzt vom CodeBehind diese Propertys manuell setzen:

Wenn ich die Propertys beispielsweiße an einen Slider binde, dann funktioniert das wunderbar:

XML-Daten
1:
2:
3:
4:
5:
6:
7:
<Image Source="MyImage.jpg" x:Name="MyImage">
  <Image.Effect>
    <local:BrightnessEffect x:Name="MyBrightnessEffect"
      Brightness="{Binding ElementName=BrightnessSlider, Path=Value}"
      Contrast="{Binding ElementName=ConstrastSlider, Path=Value}"/>
    </Image.Effect>
</Image>


Ich denke das Problem ist, dass das Bild nicht neu gezeichnet wird. Wie kann ich ein Redraw() (o.ä.) auslösen?


Kha - Mi 28.04.10 13:29

Kann es sein, dass du einen Code-Block vergessen hast ;) ?


FrEEzE2046 - Mo 17.05.10 09:15

user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
Kann es sein, dass du einen Code-Block vergessen hast ;) ?



C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
private void cbBlur_Checked(object sender, RoutedEventArgs e)
{
     float b = 1.0f;
     this.BrightnessSlider.Value = 0.0;

     for (; this.BrightnessSlider.Value < b; this.BrightnessSlider.Value += 0.0001) ;
     for (; this.BrightnessSlider.Value > 0.0this.BrightnessSlider.Value -= 0.0001) ;
}


danielf - Mo 17.05.10 09:24

Hallo,

ich kenne mich zwar nicht wirklich gut mit WPF aus, aber ich sehe keine Verbindung zwischen dem Wert den du veränderst (this.bVal.Value) und jenem den du verändern willst (BrightnessEffect.Britghness/Contrast). Dieser ist ja noch immer an die Sliders gebunden. Das musst du ändern und bei deinen zwei schleifen wird ja einfach der wert zuerst von 0 auf 1 und dann wieder von 1 auf 0 gesetzt. Zumindest bei WinForms (und deshalb wahrscheinlich in WPF auch) muss du dann die Anzeige aktualisieren, damit es auch einen optischen Effekt hat.

Gruß


FrEEzE2046 - Mo 17.05.10 09:27

user profile icondanielf hat folgendes geschrieben Zum zitierten Posting springen:
Zumindest bei WinForms (und deshalb wahrscheinlich in WPF auch) muss du dann die Anzeige aktualisieren, damit es auch einen optischen Effekt hat.


Daher ja meine Frage. Bei WinForms gibt es die Methode Refreseh(). Die gibt es in WPF aber nicht.

Wegen den unterschiedlichen Werten: Ich hab den Code mal aktualisiert, ich hatte den falschen gepostet.


danielf - Mo 17.05.10 09:29

UpdateLayout() ?


FrEEzE2046 - Mo 17.05.10 09:32

user profile icondanielf hat folgendes geschrieben Zum zitierten Posting springen:
UpdateLayout() ?


Das habe ich natürlich schon probiert. Er "hängt sich auf" während der Code läuft und aktualisiert sich erst danach. Vorher 0.0 und Nacher 0.0 lässt sich halt schlecht optisch erkennen.

Gibt es keine Möglichkeit, das System dazu zu zwingen erst die Nachrichten zu verarbeiten (wie Application.ProcessMessages) bevor es weiter geht? Immerhin funktioniert es doch auch, wenn ich den Slider manuell per Hand bewege.


FrEEzE2046 - Mo 17.05.10 09:40

Ich habe diesen Code im Internet gefunden und auf diese Art funktioniert es auch:

C#-Quelltext
1:
2:
3:
4:
5:
6:
private static Action EmptyDelegate = delegate() { };

public void Refresh()
{
     this.Dispatcher.Invoke(EmptyDelegate, System.Windows.Threading.DispatcherPriority.Render);
}


Ein kleiner Trick in dem man einfach irgendeinen Delegate synchron per Dispatcher.Invoke ausführt. Die Priorität muss prinzipiell nicht Render sein, denke ich.


danielf - Mo 17.05.10 09:50

Aber mal was anderes... wäre das ganze nicht eher eine Animation?

Ich hab mal einen Zoom In/Out-Effekt gemacht. Bei dem ich eine ScaleTransform mit einer DoubleAnimation die Werte verändert habe. Das sieht mir genau nach dem aus, was du hier brauchst.


FrEEzE2046 - Mo 17.05.10 10:55

user profile icondanielf hat folgendes geschrieben Zum zitierten Posting springen:
Aber mal was anderes... wäre das ganze nicht eher eine Animation?


Also ich wollte es jetzt als Behavior aufbauen. Dann könnte ich es auch jedem beliebigen Image zuordnen. Da ich noch nie mit Animations gearbeitet habe, weiß ich auch nicht, wie man da am besten - für den universellen Einsatz - ansetzt.

Intern würde man dem Image.Effect den ShaderEffect zuweisen.


FrEEzE2046 - Mo 17.05.10 12:01


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Interactivity;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;


namespace IlluminatedPictureTestApp
{
    public class IlluminateImageTrigger : TriggerAction<FrameworkElement>
    {
        private Storyboard _Storyboard = new Storyboard();
        private DoubleAnimation _DoubleAnimation = new DoubleAnimation();


        public IlluminateImageTrigger()
            : base()
        {
            this._InitializeTrigger();
            this._Storyboard.Children.Add(this._DoubleAnimation);
        }


        private void _InitializeTrigger()
        {
            this._DoubleAnimation.From = 0;
            this._DoubleAnimation.To = this.Duration;
            this._DoubleAnimation.AutoReverse = false;
        }


        protected override void OnAttached()
        {
            base.OnAttached();

            try
            {
                this.AssociatedObject.Effect = new IlluminateImageEffect(this.Duration);
            }
            catch (Exception e)
            {
                do
                {
                    MessageBox.Show(e.Message);
                }
                while ((e = e.InnerException) != null);
            }

            Storyboard.SetTarget(this._DoubleAnimation, this.AssociatedObject.Effect);
            Storyboard.SetTargetProperty(this._DoubleAnimation, new PropertyPath(IlluminateImageEffect.BrightnessProperty));
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
        }

        protected override void Invoke(object parameter)
        {
            this._Storyboard.Begin();
        }


        public double Duration { get; set; }
    }


    public class IlluminateImageEffect : ShaderEffect
    {
        private static PixelShader _Shader = new PixelShader() { UriSource = new Uri("/illuminate.ps", UriKind.Relative) };
        private double _Duration;


        public IlluminateImageEffect(double Duration)
        {
            this._Duration = Duration;
            this.PixelShader = _Shader;
            this.UpdateShaderValue(InputProperty);
            this.UpdateShaderValue(BrightnessProperty);
            this.UpdateShaderValue(ContrastProperty);
        }


        public Brush Input
        {
            get { return (Brush)GetValue(InputProperty); }
            set { SetValue(InputProperty, value); }
        }

        public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input"typeof(IlluminateImageEffect), 0);

        public float Brightness
        {
            get { return (float)GetValue(BrightnessProperty); }
            set { SetValue(BrightnessProperty, value / this._Duration); }
        }

        public static readonly DependencyProperty BrightnessProperty = DependencyProperty.Register("Brightness"typeof(double), typeof(IlluminateImageEffect), new PropertyMetadata(0.0, PixelShaderConstantCallback(0)));

        public float Contrast
        {
            get { return (float)GetValue(ContrastProperty); }
            set { SetValue(ContrastProperty, value / this._Duration); }
        }

        public static readonly DependencyProperty ContrastProperty = DependencyProperty.Register("Contrast"typeof(double), typeof(IlluminateImageEffect), new PropertyMetadata(0.0, PixelShaderConstantCallback(1)));
    }
}



Funktioniert allerdings noch nicht. Der Browser hängt sich jedesmal auf. Der Konstruktur und OnAttached() werden ganz normaler ohne Fehler durchlaufen.

Quelltext
1:
Fehler beim Initialisieren des visuellen Objektes im Stamm der Anwendung                    


Kha - Mo 17.05.10 18:20

Der Fehler stammt aus der this.AssociatedObject.Effect =-Zeile? Sollte eigentlich eine InnerException haben :gruebel: .


FrEEzE2046 - Di 18.05.10 07:42

user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
Der Fehler stammt aus der this.AssociatedObject.Effect =-Zeile? Sollte eigentlich eine InnerException haben :gruebel: .


Guten Morgen,

der Fehler WAR in dieser Zeile, weil er die Uri nicht gefunden hatte. Jetzt tut er das angeblich, es wird kein Fehler geworfen. Über Greasemonkey hab ich die Fehlermeldung bekommen, die ich dann hier gepostet habe. Direkt im Source habe ich keinen Fehler gefunden.

Mein Pixelshader ist (aus Testzwecken) derart minimalistisch, dass ich mir kaum vorstellen kann, dass hier der Fehler liegt:


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
sampler2D input : register(s0);
float brightness : register(c0);
float contrast : register(c1);

float4 main(float2 uv : TEXCOORD) : COLOR
{
  return tex2D(input, uv) * (1.0 + contrast) + brightness;
}


Das *.ps File liegt im Root meines Projektes und ich habe es als Resource in das Projekt eingebunden.
Erzeugen tue ich es über ein Pre-Build-Script:

Quelltext
1:
x86\fxc /T ps_2_0 /E main /Fo "$(ProjectDir)illuminate.ps" "$(ProjectDir)illuminate.fx"                    


Die Uri wird im PixelShader folgendermaßen gesetzt:

C#-Quelltext
1:
2:
3:
4:
5:
// Auszug aus großem Codeauschnitt im vorherigen Post:
public class IlluminateImageEffect : ShaderEffect
{
  private static PixelShader _Shader = new PixelShader() { UriSource = new Uri("illuminate.ps", UriKind.Absolute) };
}


Auf MSDN [http://msdn.microsoft.com/de-de/library/system.windows.media.effects.pixelshader.urisource%28v=VS.95%29.aspx] ist die Syntax für die UriSource jedoch eine andere:

C#-Quelltext
1:
new UriSource("pack://application:,,,/myEffects;component/sepiaShader.fx.ps");                    


Diese kann ich jedoch syntaktisch nicht richtig deuten, daher auch nicht selbst verwenden. Leider ist dies dort auch nicht weiter erklärt ...


PS: Nicht das sich jemand wundert; ich weiß, dass bisher nur die Helligkeit hochgesetzt wird. Solange ich das überhaupt nicht zum laufen bekomme, erspare ich mir weitere Komplexität.


Kha - Di 18.05.10 09:25

user profile iconFrEEzE2046 hat folgendes geschrieben Zum zitierten Posting springen:
Über Greasemonkey hab ich die Fehlermeldung bekommen, die ich dann hier gepostet habe. Direkt im Source habe ich keinen Fehler gefunden.
An dem Punkt waren wir irgendwie schon einmal, aber ich bin weiterhin davon überzeugt, dass mit den richtigen Debug-Einstellung jede Exception auch in VS abgefangen und inspiziert werden kann ;) .

user profile iconFrEEzE2046 hat folgendes geschrieben Zum zitierten Posting springen:
Diese kann ich jedoch syntaktisch nicht richtig deuten, daher auch nicht selbst verwenden. Leider ist dies dort auch nicht weiter erklärt ...
Das ist eine Pack URI [http://msdn.microsoft.com/en-us/library/aa970069.aspx] - man kann sich durchaus fragen, ob es keine einfachere Lösung gegeben hätte :) .


FrEEzE2046 - Di 18.05.10 09:41

Okay,

ich habe es jetzt hinbekommen. Mir wird die Page samt meinem Image auch angezeigt und der Trigger wird auch ausgelöst, aber ... es passiert nichts. Verwende ich das Storyboard hier irgendwie falsch?


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Interactivity;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;


namespace IlluminatedPictureTestApp
{
    public class IlluminateImageTrigger : TriggerAction<FrameworkElement>
    {
        private Storyboard _Storyboard = new Storyboard();
        private DoubleAnimation _DoubleAnimation = new DoubleAnimation();


        public IlluminateImageTrigger()
            : base()
        {
            this._InitializeTrigger();
            this._Storyboard.Children.Add(this._DoubleAnimation);
        }


        private void _InitializeTrigger()
        {
            this._DoubleAnimation.From = 0;
            this._DoubleAnimation.To = this.Duration;
        }


        protected override void OnAttached()
        {
            base.OnAttached();

            this.AssociatedObject.Effect = new IlluminateImageEffect() { Duration = this.Duration };

            Storyboard.SetTarget(this._DoubleAnimation, this.AssociatedObject.Effect);
            Storyboard.SetTargetProperty(this._DoubleAnimation, new PropertyPath(IlluminateImageEffect.BrightnessProperty));
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
        }

        protected override void Invoke(object parameter)
        {
            this._Storyboard.Begin();
        }


        public double Duration { get; set; }
    }


    public class IlluminateImageEffect : ShaderEffect
    {
        private static PixelShader _Shader = new PixelShader() { UriSource = new Uri(@"/IlluminatedPictureTestApp;component/illuminate.ps", UriKind.Relative) };
        private double _Duration = 1.0;


        public IlluminateImageEffect()
        {
            this.PixelShader = _Shader;
            this.UpdateShaderValue(InputProperty);
            this.UpdateShaderValue(BrightnessProperty);
            this.UpdateShaderValue(ContrastProperty);
        }


        public double Duration
        {
            get { return this._Duration; }
            set { this._Duration = value; }
        }

        public Brush Input
        {
            get { return (Brush)GetValue(InputProperty); }
            set { SetValue(InputProperty, value); }
        }

        public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input"typeof(IlluminateImageEffect), 0);

        public float Brightness
        {
            get { return (float)GetValue(BrightnessProperty); }
            set { SetValue(BrightnessProperty, value / this.Duration); }
        }

        public static readonly DependencyProperty BrightnessProperty = DependencyProperty.Register("Brightness"typeof(double), typeof(IlluminateImageEffect), new PropertyMetadata(0.0, PixelShaderConstantCallback(0)));

        public float Contrast
        {
            get { return (float)GetValue(ContrastProperty); }
            set { SetValue(ContrastProperty, value / this.Duration); }
        }

        public static readonly DependencyProperty ContrastProperty = DependencyProperty.Register("Contrast"typeof(double), typeof(IlluminateImageEffect), new PropertyMetadata(0.0, PixelShaderConstantCallback(1)));
    }
}


FrEEzE2046 - Di 18.05.10 10:39

Könnte es das Problem sein, dass ich hier die DoubleAnimation auf ein float Property anwende?


Kha - Di 18.05.10 12:36

ich bin mir nicht sicher, muss ich mal ausprobieren. Schau aber auf jeden Fall mal während des Debuggings ins Output-Fenster, dort werden solche Binding-Fehler protokolliert.


FrEEzE2046 - Mi 19.05.10 12:08

user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
ich bin mir nicht sicher, muss ich mal ausprobieren. Schau aber auf jeden Fall mal während des Debuggings ins Output-Fenster, dort werden solche Binding-Fehler protokolliert.


Also ich hab es jetzt zum laufen bekommen. Das Problem ist aber:

1. Es funktioniert nur, wenn ich die Helligkeit des Bildes schon vorher hoch gesetzt habe
2. Wird das Bild dann von "total hell" auf "normal" heruntergeregelt (also genau andersrum als ich es will)
3. Spielt die Zeit dabei irgendwie keine Rolle, es geht immer gleich schnell.


Ich habe mir das so gedacht:
Ich binde die DoubleAnimation an mein DependencyProperty für die Helligkeit. Durch Invoke wird das Storyboard gestartet und damit meine DoubleAnimation. Das DependencyProperty wird durch die laufende Zeit (von From bis To) mit diesen Werten aktualisert.

Scheinbar irre ich mich. Hier noch mal mein Code den ich bisher habe. Ich habe "Duration" zum Spaß mal auf 50.0 gesetzt:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Interactivity;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;


namespace IlluminatedPictureTestApp
{
    public class IlluminateImageTrigger : TriggerAction<FrameworkElement>
    {
        private Storyboard _Storyboard = new Storyboard();
        private DoubleAnimation _DoubleAnimation = new DoubleAnimation();


        public IlluminateImageTrigger()
            : base()
        {
            this._InitializeTrigger();
            this._Storyboard.Children.Add(this._DoubleAnimation);
        }


        private void _InitializeTrigger()
        {
            this._DoubleAnimation.From = 0;
            this._DoubleAnimation.To = this.Duration;
            //this._DoubleAnimation.Duration = new Duration(new TimeSpan(0, 1, 5));a
        }


        protected override void OnAttached()
        {
            base.OnAttached();

            (this.AssociatedObject as Image).Effect = new IlluminateImageEffect() { Duration = this.Duration, Brightness = 30.8f };

            Storyboard.SetTarget(this._DoubleAnimation, this.AssociatedObject.Effect);
            Storyboard.SetTargetProperty(this._DoubleAnimation, new PropertyPath(IlluminateImageEffect.BrightnessProperty));
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
        }

        protected override void Invoke(object parameter)
        {
            this._Storyboard.Begin();
        }


        public double Duration { get; set; }
    }


    public class IlluminateImageEffect : ShaderEffect
    {
        private static PixelShader _Shader = new PixelShader() { UriSource = new Uri(@"/IlluminatedPictureTestApp;component/illuminate.ps", UriKind.Relative) };
        private double _Duration = 1.0;


        public IlluminateImageEffect()
        {
            this.PixelShader = _Shader;
            this.UpdateShaderValue(InputProperty);
            this.UpdateShaderValue(BrightnessProperty);
            this.UpdateShaderValue(ContrastProperty);
        }


        public double Duration
        {
            get { return this._Duration; }
            set { this._Duration = value; }
        }

        public Brush Input
        {
            get { return (Brush)GetValue(InputProperty); }
            set { SetValue(InputProperty, value); }
        }

        public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input"typeof(IlluminateImageEffect), 0);

        public float Brightness
        {
            get { return (float)GetValue(BrightnessProperty); }
            set { SetValue(BrightnessProperty, value / this.Duration); }
        }

        public static readonly DependencyProperty BrightnessProperty = DependencyProperty.Register("Brightness"typeof(double), typeof(IlluminateImageEffect), new PropertyMetadata(0.0, PixelShaderConstantCallback(0)));

        public float Contrast
        {
            get { return (float)GetValue(ContrastProperty); }
            set { SetValue(ContrastProperty, value / this.Duration); }
        }

        public static readonly DependencyProperty ContrastProperty = DependencyProperty.Register("Contrast"typeof(double), typeof(IlluminateImageEffect), new PropertyMetadata(0.0, PixelShaderConstantCallback(1)));
    }
}


Kha - Mi 19.05.10 12:49

Ehrlich gesagt blicke ich langsam nicht mehr durch, auch weil du DoubleAnimation.Durationnirgendwo setzt? Kannst du die wichtigen Teile mal in ein separates Projekt ausgliedern und anhängen?


FrEEzE2046 - Mi 19.05.10 13:11

user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
Ehrlich gesagt blicke ich langsam nicht mehr durch, auch weil du DoubleAnimation.Durationnirgendwo setzt? Kannst du die wichtigen Teile mal in ein separates Projekt ausgliedern und anhängen?


Was soll ich da ausgliedern? Das ist doch das Wesentliche.

EDIT:

So, jetzt funktioniert es. Ich hatte in der Tat Duration nicht gesetzt. Ich dachte From und To wären die angaben in Sekunden.

Das einzige Problem, was ich noch habe, ist, dass ich eine Animation habe die die Helligkeit hoch setzt und eine, die das Ganze wieder dimmt.

Wenn ich beide dem Storyboard hinzufüge, funktioniert das Ganze aber nicht mehr. Werden evtl. beide Animationen dann gleichzeitig ausgeführt?
Sie sollen logischerweiße nacheinander ablaufen.



C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Interactivity;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;


namespace IlluminatedPictureTestApp
{
    public class IlluminateImageTrigger : TriggerAction<FrameworkElement>
    {
        private Storyboard _Storyboard = new Storyboard();
        private DoubleAnimation _LightUpAnimation = new DoubleAnimation(),
                                _DimAnimation = new DoubleAnimation();


        public IlluminateImageTrigger()
            : base()
        {
            this._InitializeTrigger();
            this._Storyboard.Children.Add(this._LightUpAnimation);
            this._Storyboard.Children.Add(this._DimAnimation);
        }


        private void _InitializeTrigger()
        {
            this._LightUpAnimation.From = 0;
            this._LightUpAnimation.To = 1;
            this._LightUpAnimation.Duration = this.LightUpDuration;

            this._DimAnimation.From = 1;
            this._DimAnimation.To = 0;
            this._DimAnimation.Duration = this.DimDuration;
        }


        protected override void OnAttached()
        {
            base.OnAttached();

            (this.AssociatedObject as Image).Effect = new IlluminateImageEffect();

            Storyboard.SetTarget(this._LightUpAnimation, this.AssociatedObject.Effect);
            Storyboard.SetTarget(this._DimAnimation, this.AssociatedObject.Effect);
            Storyboard.SetTargetProperty(this._LightUpAnimation, new PropertyPath(IlluminateImageEffect.BrightnessProperty));
            Storyboard.SetTargetProperty(this._DimAnimation, new PropertyPath(IlluminateImageEffect.BrightnessProperty));
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
        }

        protected override void Invoke(object parameter)
        {
            this._Storyboard.Begin();
        }


        public Duration LightUpDuration { get; set; }
        public Duration DimDuration { get; set; }
    }


    public class IlluminateImageEffect : ShaderEffect
    {
        private static PixelShader _Shader = new PixelShader() { UriSource = new Uri(@"/IlluminatedPictureTestApp;component/illuminate.ps", UriKind.Relative) };


        public IlluminateImageEffect()
        {
            this.PixelShader = _Shader;
            this.UpdateShaderValue(InputProperty);
            this.UpdateShaderValue(BrightnessProperty);
            this.UpdateShaderValue(ContrastProperty);
        }


        public Brush Input
        {
            get { return (Brush)GetValue(InputProperty); }
            set { SetValue(InputProperty, value); }
        }

        public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input"typeof(IlluminateImageEffect), 0);

        public float Brightness
        {
            get { return (float)GetValue(BrightnessProperty); }
            set { SetValue(BrightnessProperty, value); }
        }

        public static readonly DependencyProperty BrightnessProperty = DependencyProperty.Register("Brightness"typeof(double), typeof(IlluminateImageEffect), new PropertyMetadata(0.0, PixelShaderConstantCallback(0)));

        public float Contrast
        {
            get { return (float)GetValue(ContrastProperty); }
            set { SetValue(ContrastProperty, value); }
        }

        public static readonly DependencyProperty ContrastProperty = DependencyProperty.Register("Contrast"typeof(double), typeof(IlluminateImageEffect), new PropertyMetadata(0.0, PixelShaderConstantCallback(1)));
    }
}


Kha - Mi 19.05.10 16:04

Dann musst du BeginTime entsprechend setzen.


FrEEzE2046 - Do 20.05.10 11:19

user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
Dann musst du BeginTime entsprechend setzen.


Ja, aber auf was? Ich hatte es auf LightUpDuration.TimeSpan gesetzt. Das klang für mich logisch, weil es ja nach dem Ende der LightUpAnimation ausgeführt werden soll.

Allerdings ließ sich die Seite nach dem Setzen dieses Propertys auf den Wert gar nicht mehr starten.


Kha - Do 20.05.10 12:36

user profile iconFrEEzE2046 hat folgendes geschrieben Zum zitierten Posting springen:
Das klang für mich logisch, weil es ja nach dem Ende der LightUpAnimation ausgeführt werden soll.
Das hätte ich eigentlich auch gesagt. Keine Exception? Dann weiß ich auch nicht mehr weiter, ohne das Projekt vor mir zu haben.