protected override Gdk.Rectangle OnMouseMove(Context g, Color strokeColor, ImageSurface surface, int x, int y, int lastX, int lastY) { // Cairo does not support a single-pixel-long single-pixel-wide line if (x == lastX && y == lastY && g.LineWidth == 1 && PintaCore.Workspace.ActiveWorkspace.PointInCanvas(new PointD(x, y))) { surface.Flush(); ColorBgra source = surface.GetColorBgraUnchecked(x, y); source = UserBlendOps.NormalBlendOp.ApplyStatic(source, strokeColor.ToColorBgra()); surface.SetColorBgra(source, x, y); surface.MarkDirty(); return(new Gdk.Rectangle(x - 1, y - 1, 3, 3)); } g.MoveTo(lastX + 0.5, lastY + 0.5); g.LineTo(x + 0.5, y + 0.5); g.StrokePreserve(); Gdk.Rectangle dirty = g.FixedStrokeExtents().ToGdkRectangle(); // For some reason (?!) we need to inflate the dirty // rectangle for small brush widths in zoomed images dirty.Inflate(1, 1); return(dirty); }
private ColorBgra ComputeCellColor(int x, int y, ImageSurface src, int cellSize, Gdk.Rectangle srcBounds) { Gdk.Rectangle cell = GetCellBox(x, y, cellSize); cell.Intersect(srcBounds); int left = cell.Left; int right = cell.GetRight(); int bottom = cell.GetBottom(); int top = cell.Top; ColorBgra colorTopLeft = src.GetColorBgraUnchecked(left, top).ToStraightAlpha(); ColorBgra colorTopRight = src.GetColorBgraUnchecked(right, top).ToStraightAlpha(); ColorBgra colorBottomLeft = src.GetColorBgraUnchecked(left, bottom).ToStraightAlpha(); ColorBgra colorBottomRight = src.GetColorBgraUnchecked(right, bottom).ToStraightAlpha(); ColorBgra c = ColorBgra.BlendColors4W16IP(colorTopLeft, 16384, colorTopRight, 16384, colorBottomLeft, 16384, colorBottomRight, 16384); return(c.ToPremultipliedAlpha()); }
protected override void OnMouseDown(Gtk.DrawingArea canvas, Gtk.ButtonPressEventArgs args, PointD point) { Document doc = PintaCore.Workspace.ActiveDocument; Point pos = new Point((int)point.X, (int)point.Y); // Don't do anything if we're outside the canvas if (pos.X < 0 || pos.X >= doc.ImageSize.Width) { return; } if (pos.Y < 0 || pos.Y >= doc.ImageSize.Height) { return; } base.OnMouseDown(canvas, args, point); Gdk.Region currentRegion = Gdk.Region.Rectangle(doc.GetSelectedBounds(true)); // See if the mouse click is valid if (!currentRegion.PointIn(pos.X, pos.Y) && limitToSelection) { currentRegion.Dispose(); currentRegion = null; return; } ImageSurface surface = doc.CurrentUserLayer.Surface; using (var stencil_surface = new ImageSurface(Format.Argb32, (int)surface.Width, (int)surface.Height)) { IBitVector2D stencilBuffer = new BitVector2DSurfaceAdapter(stencil_surface); int tol = (int)(Tolerance * Tolerance * 256); Rectangle boundingBox; if (IsContinguousMode) { FillStencilFromPoint(surface, stencilBuffer, pos, tol, out boundingBox, currentRegion, limitToSelection); } else { FillStencilByColor(surface, stencilBuffer, surface.GetColorBgraUnchecked(pos.X, pos.Y), tol, out boundingBox, currentRegion, LimitToSelection); } OnFillRegionComputed(stencilBuffer); // If a derived tool is only going to use the stencil, // don't waste time building the polygon set if (CalculatePolygonSet) { Point[][] polygonSet = stencilBuffer.CreatePolygonSet(boundingBox, 0, 0); OnFillRegionComputed(polygonSet); } } }
/// <summary> /// Gets the final pixel color for the given point, taking layers, opacity, and blend modes into account. /// </summary> public ColorBgra GetComputedPixel(int x, int y) { using (var dst = new ImageSurface(Format.Argb32, 1, 1)) { using (var g = new Context(dst)) { foreach (var layer in GetLayersToPaint()) { var color = layer.Surface.GetColorBgraUnchecked(x, y).ToStraightAlpha().ToCairoColor(); g.SetBlendMode(layer.BlendMode); g.SetSourceColor(color); g.Rectangle(dst.GetBounds().ToCairoRectangle()); g.PaintWithAlpha(layer.Opacity); } } return(dst.GetColorBgraUnchecked(0, 0)); } }
protected unsafe override void OnMouseMove(object o, Gtk.MotionNotifyEventArgs args, Cairo.PointD point) { Document doc = PintaCore.Workspace.ActiveDocument; ColorBgra old_color; ColorBgra new_color; if (mouse_button == 1) { old_color = PintaCore.Palette.PrimaryColor.ToColorBgra(); new_color = PintaCore.Palette.SecondaryColor.ToColorBgra(); } else if (mouse_button == 3) { old_color = PintaCore.Palette.SecondaryColor.ToColorBgra(); new_color = PintaCore.Palette.PrimaryColor.ToColorBgra(); } else { last_point = point_empty; return; } int x = (int)point.X; int y = (int)point.Y; if (last_point.Equals(point_empty)) { last_point = new Point(x, y); } if (doc.Workspace.PointInCanvas(point)) { surface_modified = true; } ImageSurface surf = doc.CurrentUserLayer.Surface; ImageSurface tmp_layer = doc.ToolLayer.Surface; Gdk.Rectangle roi = GetRectangleFromPoints(last_point, new Point(x, y)); roi = PintaCore.Workspace.ClampToImageSize(roi); myTolerance = (int)(Tolerance * 256); tmp_layer.Flush(); ColorBgra *tmp_data_ptr = (ColorBgra *)tmp_layer.DataPtr; int tmp_width = tmp_layer.Width; ColorBgra *surf_data_ptr = (ColorBgra *)surf.DataPtr; int surf_width = surf.Width; // The stencil lets us know if we've already checked this // pixel, providing a nice perf boost // Maybe this should be changed to a BitVector2DSurfaceAdapter? for (int i = roi.X; i <= roi.GetRight(); i++) { for (int j = roi.Y; j <= roi.GetBottom(); j++) { if (stencil[i, j]) { continue; } if (IsColorInTolerance(new_color, surf.GetColorBgraUnchecked(surf_data_ptr, surf_width, i, j))) { *tmp_layer.GetPointAddressUnchecked(tmp_data_ptr, tmp_width, i, j) = AdjustColorDifference(new_color, old_color, surf.GetColorBgraUnchecked(surf_data_ptr, surf_width, i, j)); } stencil[i, j] = true; } } tmp_layer.MarkDirty(); using (Context g = new Context(surf)) { g.AppendPath(doc.Selection.SelectionPath); g.FillRule = FillRule.EvenOdd; g.Clip(); g.Antialias = UseAntialiasing ? Antialias.Subpixel : Antialias.None; g.MoveTo(last_point.X, last_point.Y); g.LineTo(x, y); g.LineWidth = BrushWidth; g.LineJoin = LineJoin.Round; g.LineCap = LineCap.Round; g.SetSource(tmp_layer); g.Stroke(); } doc.Workspace.Invalidate(roi); last_point = new Point(x, y); }
private void Draw(DrawingArea drawingarea1, Color tool_color, Cairo.PointD point, bool first_pixel) { int x = (int)point.X; int y = (int)point.Y; if (last_point.Equals(point_empty)) { last_point = new Point(x, y); if (!first_pixel) { return; } } Document doc = PintaCore.Workspace.ActiveDocument; if (doc.Workspace.PointInCanvas(point)) { surface_modified = true; } ImageSurface surf = doc.CurrentUserLayer.Surface; if (first_pixel && doc.Workspace.PointInCanvas(point)) { // Does Cairo really not support a single-pixel-long single-pixel-wide line? surf.Flush(); int shiftedX = (int)point.X; int shiftedY = (int)point.Y; ColorBgra source = surf.GetColorBgraUnchecked(shiftedX, shiftedY); if (UseAlphaBlending) { source = UserBlendOps.NormalBlendOp.ApplyStatic(source, tool_color.ToColorBgra()); } else { source = tool_color.ToColorBgra(); } surf.SetColorBgra(source.ToPremultipliedAlpha(), shiftedX, shiftedY); surf.MarkDirty(); } else { using (Context g = new Context(surf)) { g.AppendPath(doc.Selection.SelectionPath); g.FillRule = FillRule.EvenOdd; g.Clip(); g.Antialias = Antialias.None; // Adding 0.5 forces cairo into the correct square: // See https://bugs.launchpad.net/bugs/672232 g.MoveTo(last_point.X + 0.5, last_point.Y + 0.5); g.LineTo(x + 0.5, y + 0.5); g.SetSourceColor(tool_color); if (UseAlphaBlending) { g.SetBlendMode(BlendMode.Normal); } else { g.Operator = Operator.Source; } g.LineWidth = 1; g.LineCap = LineCap.Square; g.Stroke(); } } Gdk.Rectangle r = GetRectangleFromPoints(last_point, new Point(x, y)); doc.Workspace.Invalidate(doc.ClampToImageSize(r)); last_point = new Point(x, y); }
public unsafe static void FillStencilFromPoint(ImageSurface surface, IBitVector2D stencil, Point start, int tolerance, out Rectangle boundingBox, Gdk.Region limitRegion, bool limitToSelection) { ColorBgra cmp = surface.GetColorBgraUnchecked(start.X, start.Y); int top = int.MaxValue; int bottom = int.MinValue; int left = int.MaxValue; int right = int.MinValue; Gdk.Rectangle[] scans; stencil.Clear(false); if (limitToSelection) { using (Gdk.Region excluded = Gdk.Region.Rectangle(new Gdk.Rectangle(0, 0, stencil.Width, stencil.Height))) { excluded.Xor(limitRegion); scans = excluded.GetRectangles(); } } else { scans = new Gdk.Rectangle[0]; } foreach (Gdk.Rectangle rect in scans) { stencil.Set(rect, true); } Queue <Point> queue = new Queue <Point> (16); queue.Enqueue(start); while (queue.Count > 0) { Point pt = queue.Dequeue(); ColorBgra *rowPtr = surface.GetRowAddressUnchecked(pt.Y); int localLeft = pt.X - 1; int localRight = pt.X; while (localLeft >= 0 && !stencil.GetUnchecked(localLeft, pt.Y) && CheckColor(cmp, rowPtr[localLeft], tolerance)) { stencil.SetUnchecked(localLeft, pt.Y, true); --localLeft; } int surfaceWidth = surface.Width; while (localRight < surfaceWidth && !stencil.GetUnchecked(localRight, pt.Y) && CheckColor(cmp, rowPtr[localRight], tolerance)) { stencil.SetUnchecked(localRight, pt.Y, true); ++localRight; } ++localLeft; --localRight; Action <int> checkRow = (row) => { int sleft = localLeft; int sright = localLeft; ColorBgra *otherRowPtr = surface.GetRowAddressUnchecked(row); for (int sx = localLeft; sx <= localRight; ++sx) { if (!stencil.GetUnchecked(sx, row) && CheckColor(cmp, otherRowPtr[sx], tolerance)) { ++sright; } else { if (sright - sleft > 0) { queue.Enqueue(new Point(sleft, row)); } ++sright; sleft = sright; } } if (sright - sleft > 0) { queue.Enqueue(new Point(sleft, row)); } }; if (pt.Y > 0) { checkRow(pt.Y - 1); } if (pt.Y < surface.Height - 1) { checkRow(pt.Y + 1); } if (localLeft < left) { left = localLeft; } if (localRight > right) { right = localRight; } if (pt.Y < top) { top = pt.Y; } if (pt.Y > bottom) { bottom = pt.Y; } } foreach (Gdk.Rectangle rect in scans) { stencil.Set(rect, false); } boundingBox = new Rectangle(left, top, right - left + 1, bottom - top + 1); }
public unsafe override void Render(ImageSurface src, ImageSurface dst, Gdk.Rectangle[] rois) { var selection = PintaCore.LivePreview.RenderBounds; this.defaultRadius = Math.Min(selection.Width, selection.Height) * 0.5; this.defaultRadius2 = this.defaultRadius * this.defaultRadius; var x_center_offset = selection.Left + (selection.Width * (1.0 + Data.CenterOffset.X) * 0.5); var y_center_offset = selection.Top + (selection.Height * (1.0 + Data.CenterOffset.Y) * 0.5); ColorBgra colPrimary = PintaCore.Palette.PrimaryColor.ToColorBgra(); ColorBgra colSecondary = PintaCore.Palette.SecondaryColor.ToColorBgra(); ColorBgra colTransparent = ColorBgra.Transparent; int aaSampleCount = Data.Quality * Data.Quality; Cairo.PointD *aaPoints = stackalloc Cairo.PointD[aaSampleCount]; Utility.GetRgssOffsets(aaPoints, aaSampleCount, Data.Quality); ColorBgra *samples = stackalloc ColorBgra[aaSampleCount]; TransformData td; foreach (Gdk.Rectangle rect in rois) { for (int y = rect.Top; y <= rect.GetBottom(); y++) { ColorBgra *dstPtr = dst.GetPointAddressUnchecked(rect.Left, y); double relativeY = y - y_center_offset; for (int x = rect.Left; x <= rect.GetRight(); x++) { double relativeX = x - x_center_offset; int sampleCount = 0; for (int p = 0; p < aaSampleCount; ++p) { td.X = relativeX + aaPoints[p].X; td.Y = relativeY - aaPoints[p].Y; InverseTransform(ref td); float sampleX = (float)(td.X + x_center_offset); float sampleY = (float)(td.Y + y_center_offset); ColorBgra sample = colPrimary; if (IsOnSurface(src, sampleX, sampleY)) { sample = src.GetBilinearSample(sampleX, sampleY); } else { switch (Data.EdgeBehavior) { case WarpEdgeBehavior.Clamp: sample = src.GetBilinearSampleClamped(sampleX, sampleY); break; case WarpEdgeBehavior.Wrap: sample = src.GetBilinearSampleWrapped(sampleX, sampleY); break; case WarpEdgeBehavior.Reflect: sample = src.GetBilinearSampleClamped(ReflectCoord(sampleX, src.Width), ReflectCoord(sampleY, src.Height)); break; case WarpEdgeBehavior.Primary: sample = colPrimary; break; case WarpEdgeBehavior.Secondary: sample = colSecondary; break; case WarpEdgeBehavior.Transparent: sample = colTransparent; break; case WarpEdgeBehavior.Original: sample = src.GetColorBgraUnchecked(x, y); break; default: break; } } samples[sampleCount] = sample; ++sampleCount; } *dstPtr = ColorBgra.Blend(samples, sampleCount); ++dstPtr; } } } }