/* * SoftFocus.cs * * Author * Larry Ewing * * See COPYING for details. */ using Cairo; using System; namespace FSpot.Widgets { public class SoftFocus : IEffect { ImageInfo info; double radius; double amount; Gdk.Point center; ImageInfo blur; Pattern mask; public SoftFocus (ImageInfo info) { this.info = info; center.X = info.Bounds.Width / 2; center.Y = info.Bounds.Height / 2; Amount = 3; Radius = .5; } public Gdk.Point Center { get { return center; } set { center = value; } } public double Amount { get { return amount; } set { amount = value; if (blur != null) blur.Dispose (); blur = CreateBlur (info); } } public double Radius { get { return radius; } set { radius = value; if (blur == null) return; if (mask != null) mask.Destroy (); mask = CreateMask (); } } private ImageInfo CreateBlur (ImageInfo source) { double scale = Math.Max (256 / (double) source.Bounds.Width, 256 / (double) source.Bounds.Height); Gdk.Rectangle small = new Gdk.Rectangle (0, 0, (int) Math.Ceiling (source.Bounds.Width * scale), (int) Math.Ceiling (source.Bounds.Height * scale)); MemorySurface image = new MemorySurface (Format.Argb32, small.Width, small.Height); Context ctx = new Context (image); //Pattern solid = new SolidPattern (0, 0, 0, 0); //ctx.Source = solid; //ctx.Paint (); //solid.Destroy (); ctx.Matrix = source.Fit (small); ctx.Operator = Operator.Source; Pattern p = new SurfacePattern (source.Surface); ctx.Source = p; Console.WriteLine (small); ctx.Paint (); p.Destroy (); ((IDisposable)ctx).Dispose (); Gdk.Pixbuf normal = CairoUtils.CreatePixbuf (image); Gdk.Pixbuf blur = PixbufUtils.Blur (normal, 3); ImageInfo overlay = new ImageInfo (blur); blur.Dispose (); normal.Dispose (); image.Destroy (); return overlay; } private Pattern CreateMask () { double max = Math.Max (blur.Bounds.Width, blur.Bounds.Height) * .25; double scale = blur.Bounds.Width / (double) info.Bounds.Width; RadialGradient circle; // FIXME ugh cairo 1.0.2 is so broken why are we still shipping it. if (CairoAPI.cairo_version () > (1 * 10000 + 2 * 100 + 0)) { circle = new RadialGradient (center.X * scale, center.Y * scale, radius * max * .7, center.X * scale, center.Y * scale, radius * max + max * .2); circle.AddColorStop (0, new Cairo.Color (0.0, 0.0, 0.0, 0.0)); circle.AddColorStop (1.0, new Cairo.Color (1.0, 1.0, 1.0, 1.0)); } else { circle = new RadialGradient (center.X * scale, center.Y * scale, radius * max + max * .2, center.X * scale, center.Y * scale, radius * max * .7); circle.AddColorStop (1.0, new Cairo.Color (0.0, 0.0, 0.0, 0.0)); circle.AddColorStop (0, new Cairo.Color (1.0, 1.0, 1.0, 1.0)); } return circle; } public bool OnExpose (Context ctx, Gdk.Rectangle allocation) { SurfacePattern p = new SurfacePattern (info.Surface); ctx.Matrix = new Matrix (); Matrix m = info.Fit (allocation); ctx.Operator = Operator.Over; ctx.Matrix = m; ctx.Source = p; ctx.Paint (); SurfacePattern overlay = new SurfacePattern (blur.Surface); ctx.Matrix = new Matrix (); ctx.Matrix = blur.Fit (allocation); ctx.Operator = Operator.Over; ctx.Source = overlay; // FIXME ouch this is ugly. if (mask == null) Radius = Radius; //ctx.Paint (); ctx.Mask (mask); overlay.Destroy (); p.Destroy (); return true; } public void Dispose () { if (mask != null) mask.Destroy (); if (blur != null) blur.Dispose (); } } }