/// <summary> /// Draws a geometry. /// </summary> /// <param name="brush">The fill brush.</param> /// <param name="pen">The stroke pen.</param> /// <param name="geometry">The geometry.</param> public void DrawGeometry(Brush brush, Pen pen, Geometry geometry) { var impl = geometry.PlatformImpl as StreamGeometryImpl; var oldMatrix = Transform; Transform = impl.Transform * Transform; if (brush != null) { _context.AppendPath(impl.Path); using (var b = SetBrush(brush, geometry.Bounds.Size)) { if (pen != null) { _context.FillPreserve(); } else { _context.Fill(); } } } Transform = oldMatrix; if (pen != null) { _context.AppendPath(impl.Path); using (var p = SetPen(pen, geometry.Bounds.Size)) { _context.Stroke(); } } }
// Called from asynchronously from Renderer.OnCompletion () void HandleApply () { Debug.WriteLine ("LivePreviewManager.HandleApply()"); var item = new SimpleHistoryItem (effect.Icon, effect.Name); item.TakeSnapshotOfLayer (PintaCore.Layers.CurrentLayerIndex); using (var ctx = new Cairo.Context (layer.Surface)) { ctx.Save (); ctx.AppendPath (PintaCore.Layers.SelectionPath); ctx.FillRule = Cairo.FillRule.EvenOdd; ctx.Clip (); ctx.Operator = Cairo.Operator.Source; ctx.SetSourceSurface (live_preview_surface, (int)layer.Offset.X, (int)layer.Offset.Y); ctx.Paint (); ctx.Restore (); } PintaCore.History.PushNewItem (item); FireLivePreviewEndedEvent(RenderStatus.Completed, null); live_preview_enabled = false; PintaCore.Workspace.Invalidate (); //TODO keep track of dirty bounds. CleanUp (); }
/// <summary> /// Draws a geometry. /// </summary> /// <param name="brush">The fill brush.</param> /// <param name="pen">The stroke pen.</param> /// <param name="geometry">The geometry.</param> public void DrawGeometry(Brush brush, Pen pen, Geometry geometry) { var impl = geometry.PlatformImpl as StreamGeometryImpl; using (var pop = PushTransform(impl.Transform)) { _context.AppendPath(impl.Path); if (brush != null) { SetBrush(brush, geometry.Bounds.Size); if (pen != null) { _context.FillPreserve(); } else { _context.Fill(); } } if (pen != null) { SetPen(pen, geometry.Bounds.Size); _context.Stroke(); } } }
public override void AppendPath(object backend, object otherBackend) { Cairo.Context dest = ((CairoContextBackend)backend).Context; Cairo.Context src = ((CairoContextBackend)otherBackend).Context; using (var path = src.CopyPath()) dest.AppendPath(path); }
internal bool FillContains(Point point) { using (var context = new Cairo.Context(new Cairo.ImageSurface(Cairo.Format.Argb32, 0, 0))) { context.AppendPath(Path); return(context.InFill(point.X, point.Y)); } }
internal bool FillContains(Point point) { using (var context = new Cairo.Context(new Cairo.ImageSurface(Cairo.Format.Argb32, 0, 0))) { context.AppendPath(Path); return context.InFill(point.X, point.Y); } }
public StreamGeometryContextImpl(Cairo.Path path = null) { _surf = new Cairo.ImageSurface(Cairo.Format.Argb32, 0, 0); _context = new Cairo.Context(_surf); this.Path = path; if (this.Path != null) { _context.AppendPath(this.Path); } }
public StreamGeometryContextImpl(Cairo.Path path = null) { _surf = new Cairo.ImageSurface (Cairo.Format.Argb32, 0, 0); _context = new Cairo.Context (_surf); this.Path = path; if (this.Path != null) { _context.AppendPath(this.Path); } }
/// <summary> /// Draws a geometry. /// </summary> /// <param name="brush">The fill brush.</param> /// <param name="pen">The stroke pen.</param> /// <param name="geometry">The geometry.</param> public void DrawGeometry(IBrush brush, Pen pen, IGeometryImpl geometry) { var impl = geometry as StreamGeometryImpl; var oldMatrix = Transform; Transform = impl.Transform * Transform; if (brush != null) { _context.AppendPath(impl.Path); using (var b = SetBrush(brush, geometry.Bounds.Size)) { _context.FillRule = impl.FillRule == FillRule.EvenOdd ? Cairo.FillRule.EvenOdd : Cairo.FillRule.Winding; if (pen != null) { _context.FillPreserve(); } else { _context.Fill(); } } } Transform = oldMatrix; if (pen != null) { _context.AppendPath(impl.Path); using (var p = SetPen(pen, geometry.Bounds.Size)) { _context.Stroke(); } } }
// Used by the workspace drawing area expose render loop. // Takes care of the clipping. public void RenderLivePreviewLayer(Cairo.Context ctx, double opacity) { if (!IsEnabled) { throw new InvalidOperationException("Tried to render a live preview after live preview has ended."); } // TODO remove seam around selection during live preview. ctx.Save(); if (selection_path != null) { // Paint area outsize of the selection path, with the pre-effect image. var imageSize = PintaCore.Workspace.ImageSize; ctx.Rectangle(0, 0, imageSize.Width, imageSize.Height); ctx.AppendPath(selection_path); ctx.Clip(); ctx.SetSourceSurface(layer.Surface, (int)layer.Offset.X, (int)layer.Offset.Y); ctx.PaintWithAlpha(opacity); ctx.ResetClip(); // Paint area inside the selection path, with the post-effect image. ctx.AppendPath(selection_path); ctx.Clip(); ctx.SetSourceSurface(live_preview_surface, (int)layer.Offset.X, (int)layer.Offset.Y); ctx.PaintWithAlpha(opacity); ctx.AppendPath(selection_path); ctx.FillRule = Cairo.FillRule.EvenOdd; ctx.Clip(); } else { ctx.SetSourceSurface(live_preview_surface, (int)layer.Offset.X, (int)layer.Offset.Y); ctx.PaintWithAlpha(opacity); } ctx.Restore(); }
/// <summary> /// Draws the text. /// </summary> /// <param name="showCursor">Whether or not to show the mouse cursor in the drawing.</param> /// <param name="useTextLayer">Whether or not to use the TextLayer (as opposed to the Userlayer).</param> private void RedrawText(bool showCursor, bool useTextLayer) { Rectangle r = CurrentTextEngine.GetLayoutBounds(); r.Inflate(10 + OutlineWidth, 10 + OutlineWidth); CurrentTextBounds = r; Rectangle cursorBounds = Rectangle.Zero; Cairo.ImageSurface surf; if (!useTextLayer) { //Draw text on the current UserLayer's surface as finalized text. surf = PintaCore.Workspace.ActiveDocument.CurrentUserLayer.Surface; } else { //Draw text on the current UserLayer's TextLayer's surface as re-editable text. surf = PintaCore.Workspace.ActiveDocument.CurrentUserLayer.TextLayer.Surface; ClearTextLayer(); } using (var g = new Cairo.Context (surf)) { g.Save (); // Show selection if on text layer if (useTextLayer) { // Selected Text Cairo.Color c = new Cairo.Color (0.7, 0.8, 0.9, 0.5); foreach (Rectangle rect in CurrentTextEngine.SelectionRectangles) g.FillRectangle (rect.ToCairoRectangle (), c); } g.AppendPath (PintaCore.Workspace.ActiveDocument.Selection.SelectionPath); g.FillRule = Cairo.FillRule.EvenOdd; g.Clip (); g.MoveTo (new Cairo.PointD (CurrentTextEngine.Origin.X, CurrentTextEngine.Origin.Y)); g.Color = PintaCore.Palette.PrimaryColor; //Fill in background if (BackgroundFill) { using (var g2 = new Cairo.Context (surf)) { g2.FillRectangle(CurrentTextEngine.GetLayoutBounds().ToCairoRectangle(), PintaCore.Palette.SecondaryColor); } } // Draw the text if (FillText) Pango.CairoHelper.ShowLayout (g, CurrentTextEngine.Layout); if (FillText && StrokeText) { g.Color = PintaCore.Palette.SecondaryColor; g.LineWidth = OutlineWidth; Pango.CairoHelper.LayoutPath (g, CurrentTextEngine.Layout); g.Stroke (); } else if (StrokeText) { g.Color = PintaCore.Palette.PrimaryColor; g.LineWidth = OutlineWidth; Pango.CairoHelper.LayoutPath (g, CurrentTextEngine.Layout); g.Stroke (); } if (showCursor) { var loc = CurrentTextEngine.GetCursorLocation (); g.Antialias = Cairo.Antialias.None; g.DrawLine (new Cairo.PointD (loc.X, loc.Y), new Cairo.PointD (loc.X, loc.Y + loc.Height), new Cairo.Color (0, 0, 0, 1), 1); cursorBounds = Rectangle.Inflate (loc, 2, 10); } g.Restore (); if (useTextLayer && (is_editing || ctrlKey) && !CurrentTextEngine.IsEmpty()) { //Draw the text edit rectangle. g.Save(); g.Translate(.5, .5); using (Cairo.Path p = g.CreateRectanglePath(new Cairo.Rectangle(CurrentTextBounds.Left, CurrentTextBounds.Top, CurrentTextBounds.Width, CurrentTextBounds.Height - FontSize))) { g.AppendPath(p); } g.LineWidth = 1; g.Color = new Cairo.Color(1, 1, 1); g.StrokePreserve(); g.SetDash(new double[] { 2, 4 }, 0); g.Color = new Cairo.Color(1, .1, .2); g.Stroke(); g.Restore(); } } InflateAndInvalidate(PintaCore.Workspace.ActiveDocument.CurrentUserLayer.previousTextBounds); PintaCore.Workspace.Invalidate(old_cursor_bounds); PintaCore.Workspace.Invalidate(r); PintaCore.Workspace.Invalidate(cursorBounds); old_cursor_bounds = cursorBounds; }
private void RedrawText(bool showCursor, bool useToolLayer) { Cairo.ImageSurface surf; var invalidate_cursor = old_cursor_bounds; if (!useToolLayer) { surf = PintaCore.Workspace.ActiveDocument.CurrentLayer.Surface; } else { surf = PintaCore.Workspace.ActiveDocument.ToolLayer.Surface; surf.Clear(); } using (var g = new Cairo.Context(surf)) { g.Save(); g.AppendPath(PintaCore.Workspace.ActiveDocument.SelectionPath); g.FillRule = Cairo.FillRule.EvenOdd; g.Clip(); g.MoveTo(new Cairo.PointD(engine.Origin.X, engine.Origin.Y)); g.Color = PintaCore.Palette.PrimaryColor; //Fill in background if (BackgroundFill) { using (var g2 = new Cairo.Context(surf)) { g2.FillRectangle(engine.GetLayoutBounds().ToCairoRectangle(), PintaCore.Palette.SecondaryColor); } } // Draw the text if (FillText) { Pango.CairoHelper.ShowLayout(g, engine.Layout); } if (FillText && StrokeText) { g.Color = PintaCore.Palette.SecondaryColor; g.LineWidth = OutlineWidth; Pango.CairoHelper.LayoutPath(g, engine.Layout); g.Stroke(); } else if (StrokeText) { g.Color = PintaCore.Palette.PrimaryColor; g.LineWidth = OutlineWidth; Pango.CairoHelper.LayoutPath(g, engine.Layout); g.Stroke(); } if (showCursor) { var loc = engine.GetCursorLocation(); g.Antialias = Cairo.Antialias.None; g.DrawLine(new Cairo.PointD(loc.X, loc.Y), new Cairo.PointD(loc.X, loc.Y + loc.Height), new Cairo.Color(0, 0, 0, 1), 1); loc.Inflate(2, 10); old_cursor_bounds = loc; } g.Restore(); } Rectangle r = engine.GetLayoutBounds(); r.Inflate(10 + OutlineWidth, 10 + OutlineWidth); PintaCore.Workspace.Invalidate(old_bounds); PintaCore.Workspace.Invalidate(invalidate_cursor); PintaCore.Workspace.Invalidate(r); old_bounds = r; }
private void RedrawText(bool showCursor, bool useToolLayer) { Cairo.ImageSurface surf; var invalidate_cursor = old_cursor_bounds; if (!useToolLayer) surf = PintaCore.Workspace.ActiveDocument.CurrentLayer.Surface; else { surf = PintaCore.Workspace.ActiveDocument.ToolLayer.Surface; surf.Clear (); } using (var g = new Cairo.Context (surf)) { g.Save (); // Show selection if on tool layer if (useToolLayer) { // Selected Text Cairo.Color c = new Cairo.Color (0.7, 0.8, 0.9, 0.5); foreach (Rectangle rect in engine.SelectionRectangles) g.FillRectangle (rect.ToCairoRectangle (), c); } g.AppendPath (PintaCore.Workspace.ActiveDocument.SelectionPath); g.FillRule = Cairo.FillRule.EvenOdd; g.Clip (); g.MoveTo (new Cairo.PointD (engine.Origin.X, engine.Origin.Y)); g.Color = PintaCore.Palette.PrimaryColor; if (BackgroundFill) { using (var g2 = new Cairo.Context (surf)) { g2.FillRectangle (engine.GetLayoutBounds ().ToCairoRectangle (),PintaCore.Palette.SecondaryColor); } } if (FillText) { Pango.CairoHelper.ShowLayout (g, engine.Layout); } if (FillText && StrokeText) { g.Color = PintaCore.Palette.SecondaryColor; g.LineWidth = OutlineWidth; Pango.CairoHelper.LayoutPath (g, engine.Layout); g.Stroke (); } else if (StrokeText) { g.Color = PintaCore.Palette.PrimaryColor; g.LineWidth = OutlineWidth; Pango.CairoHelper.LayoutPath (g, engine.Layout); g.Stroke (); } if (showCursor) { var loc = engine.GetCursorLocation (); g.Antialias = Cairo.Antialias.None; g.DrawLine (new Cairo.PointD (loc.X, loc.Y), new Cairo.PointD (loc.X, loc.Y + loc.Height), new Cairo.Color (0, 0, 0, 1), 1); loc.Inflate (2, 10); old_cursor_bounds = loc; } g.Restore (); } Rectangle r = engine.GetLayoutBounds (); r.Inflate (10 + OutlineWidth, 10 + OutlineWidth); PintaCore.Workspace.Invalidate (old_bounds); PintaCore.Workspace.Invalidate (invalidate_cursor); PintaCore.Workspace.Invalidate (r); old_bounds = r; }
protected override bool OnExposeEvent(EventExpose e) { base.OnExposeEvent(e); if (!PintaCore.Workspace.HasOpenDocuments) { return(true); } double scale = PintaCore.Workspace.Scale; int x = (int)PintaCore.Workspace.Offset.X; int y = (int)PintaCore.Workspace.Offset.Y; // Translate our expose area for the whole drawingarea to just our canvas Rectangle canvas_bounds = new Rectangle(x, y, PintaCore.Workspace.CanvasSize.Width, PintaCore.Workspace.CanvasSize.Height); canvas_bounds.Intersect(e.Area); if (canvas_bounds.IsEmpty) { return(true); } canvas_bounds.X -= x; canvas_bounds.Y -= y; // Resize our offscreen surface to a surface the size of our drawing area if (canvas == null || canvas.Width != canvas_bounds.Width || canvas.Height != canvas_bounds.Height) { if (canvas != null) { (canvas as IDisposable).Dispose(); } canvas = new Cairo.ImageSurface(Cairo.Format.Argb32, canvas_bounds.Width, canvas_bounds.Height); } cr.Initialize(PintaCore.Workspace.ImageSize, PintaCore.Workspace.CanvasSize); using (Cairo.Context g = CairoHelper.Create(GdkWindow)) { // Draw our 1 px black border g.DrawRectangle(new Cairo.Rectangle(x, y, PintaCore.Workspace.CanvasSize.Width + 1, PintaCore.Workspace.CanvasSize.Height + 1), new Cairo.Color(0, 0, 0), 1); // Set up our clip rectangle g.Rectangle(new Cairo.Rectangle(x, y, PintaCore.Workspace.CanvasSize.Width, PintaCore.Workspace.CanvasSize.Height)); g.Clip(); g.Translate(x, y); bool checker = true; // Resize each layer and paint it to the screen foreach (Layer layer in PintaCore.Layers.GetLayersToPaint()) { cr.Render(layer.Surface, canvas, canvas_bounds.Location, checker); g.SetSourceSurface(canvas, canvas_bounds.X + (int)(layer.Offset.X * scale), canvas_bounds.Y + (int)(layer.Offset.Y * scale)); g.PaintWithAlpha(layer.Opacity); if (layer == PintaCore.Layers.CurrentLayer && PintaCore.LivePreview.IsEnabled) { cr.Render(PintaCore.LivePreview.LivePreviewSurface, canvas, canvas_bounds.Location, checker); g.Save(); g.Scale(scale, scale); g.AppendPath(PintaCore.Layers.SelectionPath); g.Clip(); g.Scale(1 / scale, 1 / scale); g.SetSourceSurface(canvas, canvas_bounds.X, canvas_bounds.Y); g.PaintWithAlpha(layer.Opacity); g.Restore(); } checker = false; } // If we are at least 200% and grid is requested, draw it if (PintaCore.Actions.View.PixelGrid.Active && cr.ScaleFactor.Ratio <= 0.5d) { gr.Render(canvas, canvas_bounds.Location); g.SetSourceSurface(canvas, canvas_bounds.X, canvas_bounds.Y); g.Paint(); } // Selection outline if (PintaCore.Layers.ShowSelection) { g.Save(); g.Translate(0.5, 0.5); g.Scale(scale, scale); g.AppendPath(PintaCore.Layers.SelectionPath); if (PintaCore.Tools.CurrentTool.Name.Contains("Select") && !PintaCore.Tools.CurrentTool.Name.Contains("Selected")) { g.Color = new Cairo.Color(0.7, 0.8, 0.9, 0.2); g.FillRule = Cairo.FillRule.EvenOdd; g.FillPreserve(); } g.LineWidth = 1 / scale; // Draw a white line first so it shows up on dark backgrounds g.Color = new Cairo.Color(1, 1, 1); g.StrokePreserve(); // Draw a black dashed line over the white line g.SetDash(new double[] { 2 / scale, 4 / scale }, 0); g.Color = new Cairo.Color(0, 0, 0); g.Stroke(); g.Restore(); } } return(true); }
/// <summary> /// Draws the text. /// </summary> /// <param name="showCursor">Whether or not to show the mouse cursor in the drawing.</param> /// <param name="useTextLayer">Whether or not to use the TextLayer (as opposed to the Userlayer).</param> private void RedrawText(bool showCursor, bool useTextLayer) { Rectangle r = CurrentTextLayout.GetLayoutBounds(); r.Inflate(10 + OutlineWidth, 10 + OutlineWidth); InflateAndInvalidate(r); CurrentTextBounds = r; Rectangle cursorBounds = Rectangle.Zero; Cairo.ImageSurface surf; if (!useTextLayer) { //Draw text on the current UserLayer's surface as finalized text. surf = PintaCore.Workspace.ActiveDocument.Layers.CurrentUserLayer.Surface; } else { //Draw text on the current UserLayer's TextLayer's surface as re-editable text. surf = PintaCore.Workspace.ActiveDocument.Layers.CurrentUserLayer.TextLayer.Layer.Surface; ClearTextLayer(); } using (var g = new Cairo.Context(surf)) { g.Save(); // Show selection if on text layer if (useTextLayer) { // Selected Text Cairo.Color c = new Cairo.Color(0.7, 0.8, 0.9, 0.5); foreach (Rectangle rect in CurrentTextLayout.SelectionRectangles) { g.FillRectangle(rect.ToCairoRectangle(), c); } } if (selection != null) { selection.Clip(g); } g.MoveTo(new Cairo.PointD(CurrentTextEngine.Origin.X, CurrentTextEngine.Origin.Y)); g.SetSourceColor(PintaCore.Palette.PrimaryColor); //Fill in background if (BackgroundFill) { using (var g2 = new Cairo.Context(surf)) { if (selection != null) { selection.Clip(g2); } g2.FillRectangle(CurrentTextLayout.GetLayoutBounds().ToCairoRectangle(), PintaCore.Palette.SecondaryColor); } } // Draw the text if (FillText) { Pango.CairoHelper.ShowLayout(g, CurrentTextLayout.Layout); } if (FillText && StrokeText) { g.SetSourceColor(PintaCore.Palette.SecondaryColor); g.LineWidth = OutlineWidth; Pango.CairoHelper.LayoutPath(g, CurrentTextLayout.Layout); g.Stroke(); } else if (StrokeText) { g.SetSourceColor(PintaCore.Palette.PrimaryColor); g.LineWidth = OutlineWidth; Pango.CairoHelper.LayoutPath(g, CurrentTextLayout.Layout); g.Stroke(); } if (showCursor) { var loc = CurrentTextLayout.GetCursorLocation(); var color = PintaCore.Palette.PrimaryColor; g.Antialias = Cairo.Antialias.None; g.DrawLine(new Cairo.PointD(loc.X, loc.Y), new Cairo.PointD(loc.X, loc.Y + loc.Height), color, 1); cursorBounds = Rectangle.Inflate(loc, 2, 10); } g.Restore(); if (useTextLayer && (is_editing || ctrlKey) && !CurrentTextEngine.IsEmpty()) { //Draw the text edit rectangle. g.Save(); g.Translate(.5, .5); using (Cairo.Path p = g.CreateRectanglePath(CurrentTextBounds.ToCairoRectangle())) { g.AppendPath(p); } g.LineWidth = 1; g.SetSourceColor(new Cairo.Color(1, 1, 1)); g.StrokePreserve(); g.SetDash(new double[] { 2, 4 }, 0); g.SetSourceColor(new Cairo.Color(1, .1, .2)); g.Stroke(); g.Restore(); } } InflateAndInvalidate(PintaCore.Workspace.ActiveDocument.Layers.CurrentUserLayer.previousTextBounds); PintaCore.Workspace.Invalidate(old_cursor_bounds); InflateAndInvalidate(r); PintaCore.Workspace.Invalidate(cursorBounds); old_cursor_bounds = cursorBounds; }
protected override void OnMouseMove(object o, Gtk.MotionNotifyEventArgs args, Cairo.PointD point) { if (!painting || offset.IsNotSet ()) return; int x = (int)point.X; int y = (int)point.Y; if (last_point.IsNotSet ()) { last_point = new Point (x, y); return; } using (Cairo.Context g = new Cairo.Context (PintaCore.Layers.ToolLayer.Surface)) { g.AppendPath (PintaCore.Layers.SelectionPath); g.FillRule = Cairo.FillRule.EvenOdd; g.Clip (); g.Antialias = Cairo.Antialias.Subpixel; g.MoveTo (last_point.X, last_point.Y); g.LineTo (x, y); g.SetSource (PintaCore.Workspace.ActiveDocument.CurrentLayer.Surface, offset.X, offset.Y); g.LineWidth = BrushWidth; g.LineCap = Cairo.LineCap.Round; g.Stroke (); } var dirty_rect = GetRectangleFromPoints (last_point, new Point (x, y)); last_point = new Point (x, y); surface_modified = true; PintaCore.Workspace.Invalidate (dirty_rect); }
// Called from asynchronously from Renderer.OnCompletion () void HandleApply () { Debug.WriteLine ("LivePreviewManager.HandleApply()"); var item = new SimpleHistoryItem (effect.Icon, effect.Name); item.TakeSnapshotOfLayer (PintaCore.Layers.CurrentLayerIndex); using (var ctx = new Cairo.Context (layer.Surface)) { ctx.Save (); ctx.AppendPath (PintaCore.Workspace.ActiveDocument.Selection.SelectionPath); ctx.FillRule = Cairo.FillRule.EvenOdd; ctx.Clip (); ctx.Operator = Cairo.Operator.Source; ctx.SetSourceSurface (live_preview_surface, (int)layer.Offset.X, (int)layer.Offset.Y); ctx.Paint (); ctx.Restore (); } PintaCore.History.PushNewItem (item); FireLivePreviewEndedEvent(RenderStatus.Completed, null); live_preview_enabled = false; PintaCore.Workspace.Invalidate (); //TODO keep track of dirty bounds. CleanUp (); }
protected override bool OnExposeEvent(EventExpose e) { base.OnExposeEvent(e); if (!PintaCore.Workspace.HasOpenDocuments) { return(true); } double scale = PintaCore.Workspace.Scale; int x = (int)PintaCore.Workspace.Offset.X; int y = (int)PintaCore.Workspace.Offset.Y; // Translate our expose area for the whole drawingarea to just our canvas Rectangle canvas_bounds = new Rectangle(x, y, PintaCore.Workspace.CanvasSize.Width, PintaCore.Workspace.CanvasSize.Height); canvas_bounds.Intersect(e.Area); if (canvas_bounds.IsEmpty) { return(true); } canvas_bounds.X -= x; canvas_bounds.Y -= y; // Resize our offscreen surface to a surface the size of our drawing area if (canvas == null || canvas.Width != canvas_bounds.Width || canvas.Height != canvas_bounds.Height) { if (canvas != null) { (canvas as IDisposable).Dispose(); } canvas = new Cairo.ImageSurface(Cairo.Format.Argb32, canvas_bounds.Width, canvas_bounds.Height); } cr.Initialize(PintaCore.Workspace.ImageSize, PintaCore.Workspace.CanvasSize); using (Cairo.Context g = CairoHelper.Create(GdkWindow)) { // Draw our canvas drop shadow g.DrawRectangle(new Cairo.Rectangle(x, y, PintaCore.Workspace.CanvasSize.Width + 1, PintaCore.Workspace.CanvasSize.Height + 1), new Cairo.Color(.5, .5, .5), 1); g.DrawRectangle(new Cairo.Rectangle(x - 1, y - 1, PintaCore.Workspace.CanvasSize.Width + 3, PintaCore.Workspace.CanvasSize.Height + 3), new Cairo.Color(.8, .8, .8), 1); g.DrawRectangle(new Cairo.Rectangle(x - 2, y - 2, PintaCore.Workspace.CanvasSize.Width + 5, PintaCore.Workspace.CanvasSize.Height + 5), new Cairo.Color(.9, .9, .9), 1); // Set up our clip rectangle g.Rectangle(new Cairo.Rectangle(x, y, PintaCore.Workspace.CanvasSize.Width, PintaCore.Workspace.CanvasSize.Height)); g.Clip(); g.Translate(x, y); // Render all the layers to a surface var layers = PintaCore.Layers.GetLayersToPaint(); if (layers.Count == 0) { canvas.Clear(); } cr.Render(layers, canvas, canvas_bounds.Location); // Paint the surface to our canvas g.SetSourceSurface(canvas, canvas_bounds.X + (int)(0 * scale), canvas_bounds.Y + (int)(0 * scale)); g.Paint(); // Selection outline if (PintaCore.Layers.ShowSelection) { g.Save(); g.Translate(0.5, 0.5); g.Scale(scale, scale); g.AppendPath(PintaCore.Workspace.ActiveDocument.Selection.SelectionPath); if (PintaCore.Tools.CurrentTool.Name.Contains("Select") && !PintaCore.Tools.CurrentTool.Name.Contains("Selected")) { g.Color = new Cairo.Color(0.7, 0.8, 0.9, 0.2); g.FillRule = Cairo.FillRule.EvenOdd; g.FillPreserve(); } g.LineWidth = 1 / scale; // Draw a white line first so it shows up on dark backgrounds g.Color = new Cairo.Color(1, 1, 1); g.StrokePreserve(); // Draw a black dashed line over the white line g.SetDash(new double[] { 2 / scale, 4 / scale }, 0); g.Color = new Cairo.Color(0, 0, 0); g.Stroke(); g.Restore(); } } return(true); }