5
* Ruben Vermeersch <ruben@savanne.be>
7
* This is free software. See COPYING for details.
7
21
namespace FSpot.Editors {
22
[ExtensionNode ("Editor")]
23
public class EditorNode : ExtensionNode {
24
[NodeAttribute (Required=true)]
25
protected string editor_type;
27
public Editor GetEditor () {
28
return (Editor) Addin.CreateInstance (editor_type);
32
public class EditorSelection {
34
public int width, height;
37
public class EditorState {
38
// The area selected by the user.
39
public EditorSelection Selection;
41
// The images selected by the user.
42
public IBrowsableItem [] Items;
44
// The view, into which images are shown (null if we are in the browse view).
45
public PhotoImageView PhotoImageView;
47
// Has a portion of the image been selected?
48
public bool HasSelection {
49
get { return Selection != null; }
52
// Is the user in browse mode?
53
public bool InBrowseMode {
54
get { return PhotoImageView == null; }
58
// This is the base class from which all editors inherit.
8
59
public abstract class Editor {
9
protected PhotoImageView view;
10
protected Gtk.Window controls;
12
public event EventHandler Done;
14
public Editor (PhotoImageView view)
19
protected virtual string GetTitle ()
24
protected virtual void SetView (PhotoImageView view)
35
Widget w = CreateControls ();
39
ControlOverlay c = new ControlOverlay (view);
43
c.Visibility = ControlOverlay.VisibilityType.Full;
46
Window win = new Window (String.Format ("{0}", GetTitle ()));
47
win.TransientFor = (Gtk.Window) view.Toplevel;
50
win.DeleteEvent += delegate { Destroy (); };
57
protected virtual Widget CreateControls ()
60
public delegate void ProcessingStartedHandler (string name, int count);
61
public delegate void ProcessingStepHandler (int done);
62
public delegate void ProcessingFinishedHandler ();
64
public event ProcessingStartedHandler ProcessingStarted;
65
public event ProcessingStepHandler ProcessingStep;
66
public event ProcessingFinishedHandler ProcessingFinished;
68
// Contains the current selection, the items being edited, ...
69
private EditorState state;
70
public EditorState State {
72
if (!StateInitialized)
73
throw new ApplicationException ("Editor has not been initialized yet!");
77
private set { state = value; }
80
public bool StateInitialized {
81
get { return state != null; }
85
// Whether the user needs to select a part of the image before it can be applied.
86
public bool NeedsSelection = false;
88
// A tool can be applied if it doesn't need a selection, or if it has one.
89
public bool CanBeApplied {
91
Log.DebugFormat ("{0} can be applied? {1}", this, !NeedsSelection || (NeedsSelection && State.HasSelection));
92
return !NeedsSelection || (NeedsSelection && State.HasSelection);
96
private bool can_handle_multiple = false;
97
public bool CanHandleMultiple {
98
get { return can_handle_multiple; }
99
protected set { can_handle_multiple = value; }
103
protected void LoadPhoto (Photo photo, out Pixbuf photo_pixbuf, out Cms.Profile photo_profile) {
104
// FIXME: We might get this value from the PhotoImageView.
105
using (ImageFile img = ImageFile.Create (photo.DefaultVersionUri)) {
106
photo_pixbuf = img.Load ();
107
photo_profile = img.GetProfile ();
111
// The human readable name for this action.
112
public readonly string Label;
114
// The label on the apply button (usually shorter than the label).
115
private string apply_label = "";
116
public string ApplyLabel {
117
get { return apply_label == "" ? Label : apply_label; }
118
protected set { apply_label = value; }
122
// The icon name for this action (will be loaded from the theme).
123
public readonly string IconName;
125
public Editor (string label, string icon_name) {
127
IconName = icon_name;
130
// Apply the editor's action to a photo.
131
public void Apply () {
133
if (ProcessingStarted != null) {
134
ProcessingStarted (Label, State.Items.Length);
138
if (ProcessingFinished != null) {
139
ProcessingFinished ();
144
private void TryApply () {
145
if (NeedsSelection && !State.HasSelection) {
146
throw new Exception ("Cannot apply without selection!");
150
foreach (Photo photo in State.Items) {
152
Cms.Profile input_profile;
153
LoadPhoto (photo, out input, out input_profile);
155
Pixbuf edited = Process (input, input_profile);
158
bool create_version = photo.DefaultVersion.IsProtected;
159
photo.SaveVersion (edited, create_version);
160
photo.Changes.DataChanged = true;
161
Core.Database.Photos.Commit (photo);
164
if (ProcessingStep != null) {
165
ProcessingStep (done);
172
protected abstract Pixbuf Process (Pixbuf input, Cms.Profile input_profile);
174
protected virtual Pixbuf ProcessFast (Pixbuf input, Cms.Profile input_profile) {
175
return Process (input, input_profile);
178
private bool has_settings;
179
public bool HasSettings {
180
get { return has_settings; }
181
protected set { has_settings = value; }
184
private Pixbuf original;
185
private Pixbuf preview;
186
protected void UpdatePreview () {
187
if (State.InBrowseMode) {
188
throw new Exception ("Previews cannot be made in browse mode!");
191
if (State.Items.Length > 1) {
192
throw new Exception ("We should have one item selected when this happened, otherwise something is terribly wrong.");
195
if (original == null) {
196
original = State.PhotoImageView.Pixbuf;
199
Pixbuf old_preview = null;
200
if (preview == null) {
202
CalcPreviewSize (original, out width, out height);
203
preview = original.ScaleSimple (width, height, InterpType.Nearest);
205
// We're updating a previous preview
206
old_preview = State.PhotoImageView.Pixbuf;
209
Pixbuf previewed = ProcessFast (preview, null);
210
State.PhotoImageView.Pixbuf = previewed;
211
State.PhotoImageView.ZoomFit (false);
212
MainWindow.Toplevel.InfoBox.UpdateHistogram (previewed);
214
if (old_preview != null) {
215
old_preview.Dispose ();
219
private void CalcPreviewSize (Pixbuf input, out int width, out int height) {
220
int awidth = State.PhotoImageView.Allocation.Width;
221
int aheight = State.PhotoImageView.Allocation.Height;
222
int iwidth = input.Width;
223
int iheight = input.Height;
225
if (iwidth <= awidth && iheight <= aheight) {
230
double wratio = (double) iwidth / awidth;
231
double hratio = (double) iheight / aheight;
233
double ratio = Math.Max (wratio, hratio);
234
width = (int) (iwidth / ratio);
235
height = (int) (iheight / ratio);
237
//Log.DebugFormat ("Preview size: Allocation: {0}x{1}, Input: {2}x{3}, Result: {4}x{5}", awidth, aheight, iwidth, iheight, width, height);
240
public void Restore () {
241
if (original != null && State.PhotoImageView != null) {
242
State.PhotoImageView.Pixbuf = original;
243
State.PhotoImageView.ZoomFit (false);
245
MainWindow.Toplevel.InfoBox.UpdateHistogram (null);
251
private void Reset () {
252
if (preview != null) {
261
// Can be overriden to provide a specific configuration widget.
262
// Returning null means no configuration widget.
263
public virtual Widget ConfigurationWidget () {
62
public void Destroy ()
67
protected virtual void Close ()
74
Done (this, EventArgs.Empty);
80
public class GlEditor : Editor {
81
protected GlTransition transition;
82
protected Scale scale;
83
protected Texture texture;
85
public GlEditor (PhotoImageView view) : base (view)
87
transition = new GlTransition.Flip ();
90
protected override Widget CreateControls ()
92
scale = new HScale (0, 1, 0.01);
93
scale.ValueChanged += HandleValueChanged;
94
scale.WidthRequest = 250;
99
protected override void SetView (PhotoImageView value)
102
view.ExposeEvent -= ExposeEvent;
106
base.SetView (value);
111
view.ExposeEvent += ExposeEvent;
116
public virtual void ExposeEvent (object sender, ExposeEventArgs args)
118
view.Glx.MakeCurrent (view.GdkWindow);
119
Gl.glEnable (Gl.GL_CONVOLUTION_2D);
120
Gdk.Color c = view.Style.Background (view.State);
121
Gl.glClearColor (c.Red / (float) ushort.MaxValue,
122
c.Blue / (float) ushort.MaxValue,
123
c.Green / (float) ushort.MaxValue,
126
if (texture == null) {
127
float [] kernel = new float [] { .25f, .5f, .25f,
132
bool supported = GlExtensionLoader.LoadExtension ("GL_ARB_imaging");
134
System.Console.WriteLine ("GL_ARB_imaging not supported");
138
GlExtensionLoader.LoadAllExtensions ();
141
Gl.glConvolutionParameteri (Gl.GL_CONVOLUTION_2D,
142
Gl.GL_CONVOLUTION_BORDER_MODE,
143
Gl.GL_REPLICATE_BORDER);
145
Gl.glConvolutionFilter2D (Gl.GL_CONVOLUTION_2D,
153
texture = new Texture (view.CompletePixbuf ());
156
Gl.glShadeModel(Gl.GL_FLAT);
158
Gl.glColor3f(1.0f, 1.0f, 1.0f);
160
Gl.glEnable (Gl.GL_DEPTH_TEST);
161
Gl.glEnable (Gl.GL_NORMALIZE);
162
Gl.glShadeModel (Gl.GL_FLAT);
163
Gl.glEnable (Gl.GL_TEXTURE_RECTANGLE_ARB);
164
Gl.glClear (Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);
168
transition.Draw (view.Allocation, texture, texture);
170
view.Glx.SwapBuffers (view.GdkWindow);
172
Gl.glDisable (Gl.GL_CONVOLUTION_2D);
175
private void HandleValueChanged (object sender, System.EventArgs args)
177
transition.Percent = (float) scale.Value;
181
protected override void Close ()
183
if (texture != null && view != null && view.Glx != null && view.GdkWindow != null) {
184
view.Glx.MakeCurrent (view.GdkWindow);
192
public class EffectEditor : Editor {
193
protected IEffect effect;
194
protected Widgets.ImageInfo info;
197
public EffectEditor (PhotoImageView view) : base (view)
201
protected override void SetView (PhotoImageView value)
204
view.ExposeEvent -= ExposeEvent;
206
view.DoubleBuffered = double_buffer;
209
base.SetView (value);
214
info = new Widgets.ImageInfo (view.CompletePixbuf ());
216
double_buffer = (view.WidgetFlags & WidgetFlags.DoubleBuffered) == WidgetFlags.DoubleBuffered;
217
view.DoubleBuffered = true;
218
view.ExposeEvent += ExposeEvent;
223
public virtual void ExposeEvent (object sender, ExposeEventArgs args)
225
Context ctx = Widgets.CairoUtils.CreateContext (view.GdkWindow);
226
Gdk.Color c = view.Style.Background (view.State);
227
ctx.Source = new SolidPattern (c.Red / (float) ushort.MaxValue,
228
c.Blue / (float) ushort.MaxValue,
229
c.Green / (float) ushort.MaxValue);
233
effect.OnExpose (ctx, view.Allocation);
238
protected override void Close ()
268
public virtual EditorState CreateState () {
269
return new EditorState ();
272
public event InitializedHandler Initialized;
273
public delegate void InitializedHandler ();
275
public void Initialize (EditorState state) {
278
if (Initialized != null)