using System; using System.Collections.Generic; namespace Assistance { public class DynamicSurface: IDisposable { private readonly double incrementScale; private int offsetX; private int offsetY; private Cairo.ImageSurface surface; private Cairo.Context privateContext; public DynamicSurface(double incrementScale = 1.2) { this.incrementScale = incrementScale; } public bool isEmpty { get { return surface == null; } } public int left { get { return isEmpty ? 0 : offsetX; } } public int top { get { return isEmpty ? 0 : offsetY; } } public int width { get { return isEmpty ? 0 : surface.Width; } } public int height { get { return isEmpty ? 0 : surface.Height; } } public Cairo.Context context { get { return privateContext; } } public Rectangle getBounds() { return isEmpty ? new Rectangle() : new Rectangle((double)offsetX, (double)offsetY, (double)(offsetX + surface.Width), (double)(offsetY + surface.Height)); } public void flush() { if (surface != null) surface.Flush(); } public void draw(Cairo.Context context, double alpha = 1.0) { if (isEmpty) return; flush(); context.Save(); context.Translate(offsetX, offsetY); context.SetSource(surface); if (alpha >= 1.0 - Geometry.precision) context.Paint(); else context.PaintWithAlpha(alpha); context.Restore(); } public void clear() { if (isEmpty) return; privateContext.Dispose(); privateContext = null; surface.Dispose(); surface = null; } public bool expand(Rectangle rect, bool noScale = false) { rect = new Rectangle(rect.p0).expand(rect.p1); int rl = (int)Math.Floor(rect.x0 + Geometry.precision); int rt = (int)Math.Floor(rect.y0 + Geometry.precision); int rr = Math.Max(rl, (int)Math.Ceiling(rect.x1 - Geometry.precision)) + 1; int rb = Math.Max(rt, (int)Math.Ceiling(rect.y1 - Geometry.precision)) + 1; int l, t, r, b; if (surface == null) { l = rl; t = rt; r = rr; b = rb; } else { l = offsetX; t = offsetY; r = l + surface.Width; b = t + surface.Height; } int incX = noScale ? 0 : Math.Max(0, (int)Math.Ceiling( (incrementScale - 1.0)*(Math.Max(r, rr) - Math.Min(l, rl)) )); int incY = noScale ? 0 : Math.Max(0, (int)Math.Ceiling( (incrementScale - 1.0)*(Math.Max(b, rb) - Math.Min(t, rt)) )); if (rl < l) l = rl - incX; if (rt < t) t = rt - incY; if (rr > r) r = rr + incX; if (rb > b) b = rb + incY; int w = r - l; int h = b - t; if (surface != null && l == offsetX && t == offsetY && w == surface.Width && h == surface.Height) return false; if (w <= 0 || h <= 0) return false; Cairo.ImageSurface newSurface = new Cairo.ImageSurface(Cairo.Format.ARGB32, w, h); Cairo.Context newContext = new Cairo.Context(newSurface); if (!isEmpty) { flush(); newContext.Save(); newContext.Translate(offsetX - l, offsetY - t); newContext.SetSource(surface); newContext.Paint(); newContext.Restore(); clear(); } offsetX = l; offsetY = t; surface = newSurface; privateContext = newContext; privateContext.Antialias = Cairo.Antialias.Gray; privateContext.Translate(-offsetX, -offsetY); return true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { clear(); } ~DynamicSurface() { Dispose(false); } } }