public virtual void HandleMouseDown(Gtk.DrawingArea canvas, Gtk.ButtonPressEventArgs args, Cairo.PointD point) { //If we are already drawing, ignore any additional mouse down events. if (is_drawing) { return; } //Redraw the previously (and possibly currently) active shape without any control points in case another shape is made active. DrawActiveShape(false, false, false, false, false); Document doc = PintaCore.Workspace.ActiveDocument; shape_origin = new PointD(Utility.Clamp(point.X, 0, doc.ImageSize.Width - 1), Utility.Clamp(point.Y, 0, doc.ImageSize.Height - 1)); current_point = shape_origin; bool shiftKey = (args.Event.State & Gdk.ModifierType.ShiftMask) == Gdk.ModifierType.ShiftMask; if (shiftKey) { CalculateModifiedCurrentPoint(); } is_drawing = true; //Right clicking changes tension. if (args.Event.Button == 1) { changing_tension = false; } else { changing_tension = true; } bool ctrlKey = (args.Event.State & Gdk.ModifierType.ControlMask) == Gdk.ModifierType.ControlMask; int closestCPIndex, closestCPShapeIndex; ControlPoint closestControlPoint; double closestCPDistance; SEngines.FindClosestControlPoint(current_point, out closestCPShapeIndex, out closestCPIndex, out closestControlPoint, out closestCPDistance); int closestShapeIndex, closestPointIndex; PointD closestPoint; double closestDistance; OrganizedPointCollection.FindClosestPoint(SEngines, current_point, out closestShapeIndex, out closestPointIndex, out closestPoint, out closestDistance); bool clickedOnControlPoint = false; double currentClickRange = ShapeClickStartingRange + BrushWidth * ShapeClickThicknessFactor; //Determine if the closest ControlPoint is within the expected click range. if (closestControlPoint != null && closestCPDistance < currentClickRange) { //User clicked directly on a ControlPoint on a shape. clicked_without_modifying = true; SelectedPointIndex = closestCPIndex; SelectedShapeIndex = closestCPShapeIndex; clickedOnControlPoint = true; } else if (closestDistance < currentClickRange) //Determine if the user clicked close enough to a shape. { //User clicked on a generated point on a shape. List<ControlPoint> controlPoints = SEngines[closestShapeIndex].ControlPoints; //Note: compare the currentPoint's distance here because it's the actual mouse position. if (controlPoints.Count > closestPointIndex && current_point.Distance(controlPoints[closestPointIndex].Position) < currentClickRange) { //User clicked on a control point (on the "previous order" side of the point). clicked_without_modifying = true; SelectedPointIndex = closestPointIndex; SelectedShapeIndex = closestShapeIndex; clickedOnControlPoint = true; } else if (closestPointIndex > 0) { if (current_point.Distance(controlPoints[closestPointIndex - 1].Position) < currentClickRange) { //User clicked on a control point (on the "following order" side of the point). clicked_without_modifying = true; SelectedPointIndex = closestPointIndex - 1; SelectedShapeIndex = closestShapeIndex; clickedOnControlPoint = true; } else if (controlPoints.Count > 0 && current_point.Distance(controlPoints[controlPoints.Count - 1].Position) < currentClickRange) { //User clicked on a control point (on the "following order" side of the point). clicked_without_modifying = true; SelectedPointIndex = closestPointIndex - 1; SelectedShapeIndex = closestShapeIndex; clickedOnControlPoint = true; } } //Check for clicking on a non-control point. Don't do anything here if right clicked. if (!changing_tension && !clickedOnControlPoint && closestShapeIndex > -1 && closestPointIndex > -1 && SEngines.Count > closestShapeIndex) { //User clicked on a non-control point on a shape. //Determine if the currently active tool matches the clicked on shape's corresponding tool, and if not, switch to it. if (ActivateCorrespondingTool(closestShapeIndex, true) != null) { //Pass on the event and its data to the newly activated tool. PintaCore.Tools.CurrentTool.DoMouseDown(canvas, args, point); //Don't do anything else here once the tool is switched and the event is passed on. return; } //The currently active tool matches the clicked on shape's corresponding tool. //Only create a new shape if the user isn't holding the control key down. if (!ctrlKey) { //Create a new ShapesModifyHistoryItem so that the adding of a control point can be undone. doc.History.PushNewItem(new ShapesModifyHistoryItem(this, owner.Icon, ShapeName + " " + Catalog.GetString("Point Added"))); controlPoints.Insert(closestPointIndex, new ControlPoint(new PointD(current_point.X, current_point.Y), DefaultMidPointTension)); } //These should be set after creating the history item. SelectedPointIndex = closestPointIndex; SelectedShapeIndex = closestShapeIndex; ShapeEngine activeEngine = ActiveShapeEngine; if (activeEngine != null) { UpdateToolbarSettings(activeEngine); } } } //Create a new shape if the user control + clicks on a shape or if the user simply clicks outside of any shapes. if (!changing_tension && (ctrlKey || (closestCPDistance >= currentClickRange && closestDistance >= currentClickRange))) { //Verify that the user clicked inside the image bounds or that the user is //holding the Ctrl key (to ignore the Image bounds and draw on the edge). if ((point.X == shape_origin.X && point.Y == shape_origin.Y) || ctrlKey) { PointD prevSelPoint; //First, store the position of the currently selected point. if (SelectedPoint != null && ctrlKey) { prevSelPoint = new PointD(SelectedPoint.Position.X, SelectedPoint.Position.Y); } else { //This doesn't matter, other than the fact that it gets set to a value in order for the code to build. prevSelPoint = new PointD(0d, 0d); } //Create a new ShapesHistoryItem so that the creation of a new shape can be undone. doc.History.PushNewItem(new ShapesHistoryItem(this, owner.Icon, ShapeName + " " + Catalog.GetString("Added"), doc.CurrentUserLayer.Surface.Clone(), doc.CurrentUserLayer, SelectedPointIndex, SelectedShapeIndex, false)); //Create the shape, add its starting points, and add it to SEngines. SEngines.Add(CreateShape(ctrlKey, clickedOnControlPoint, prevSelPoint)); //Select the new shape. SelectedShapeIndex = SEngines.Count - 1; ShapeEngine activeEngine = ActiveShapeEngine; if (activeEngine != null) { //Set the AntiAliasing. activeEngine.AntiAliasing = owner.UseAntialiasing; } StorePreviousSettings(); } } else if (clickedOnControlPoint) { //Since the user is not creating a new shape or control point but rather modifying an existing control point, it should be determined //whether the currently active tool matches the clicked on shape's corresponding tool, and if not, switch to it. if (ActivateCorrespondingTool(SelectedShapeIndex, true) != null) { //Pass on the event and its data to the newly activated tool. PintaCore.Tools.CurrentTool.DoMouseDown(canvas, args, point); //Don't do anything else here once the tool is switched and the event is passed on. return; } //The currently active tool matches the clicked on shape's corresponding tool. ShapeEngine activeEngine = ActiveShapeEngine; if (activeEngine != null) { UpdateToolbarSettings(activeEngine); } } //Determine if the user right clicks outside of any shapes (neither on their control points nor on their generated points). if ((closestCPDistance >= currentClickRange && closestDistance >= currentClickRange) && changing_tension) { clicked_without_modifying = true; } DrawActiveShape(false, false, true, shiftKey, false); }
protected unsafe void eraseSmooth(ImageSurface surf, Context g, PointD start, PointD end) { int rad = (int)(BrushWidth / 2.0) + 1; //Premultiply with alpha value byte bk_col_a = (byte)(PintaCore.Palette.SecondaryColor.A * 255.0); byte bk_col_r = (byte)(PintaCore.Palette.SecondaryColor.R * bk_col_a); byte bk_col_g = (byte)(PintaCore.Palette.SecondaryColor.G * bk_col_a); byte bk_col_b = (byte)(PintaCore.Palette.SecondaryColor.B * bk_col_a); int num_steps = (int)start.Distance(end) / rad + 1; //Initialize lookup table when first used (to prevent slower startup of the application) initLookupTable (); for (int step = 0; step < num_steps; step++) { PointD pt = Utility.Lerp(start, end, (float)step / num_steps); int x = (int)pt.X, y = (int)pt.Y; Gdk.Rectangle surface_rect = new Gdk.Rectangle (0, 0, surf.Width, surf.Height); Gdk.Rectangle brush_rect = new Gdk.Rectangle (x - rad, y - rad, 2 * rad, 2 * rad); Gdk.Rectangle dest_rect = Gdk.Rectangle.Intersect (surface_rect, brush_rect); if ((dest_rect.Width > 0) && (dest_rect.Height > 0)) { //Allow Clipping through a temporary surface using (ImageSurface tmp_surface = copySurfacePart (surf, dest_rect)) { for (int iy = dest_rect.Top; iy < dest_rect.Bottom; iy++) { ColorBgra* srcRowPtr = tmp_surface.GetRowAddressUnchecked (iy - dest_rect.Top); int dy = ((iy - y) * LUT_Resolution) / rad; if (dy < 0) dy = -dy; byte[] lut_factor_row = lut_factor [dy]; for (int ix = dest_rect.Left; ix < dest_rect.Right; ix++) { ColorBgra col = *srcRowPtr; int dx = ((ix - x) * LUT_Resolution) / rad; if (dx < 0) dx = -dx; int force = lut_factor_row [dx]; //Note: premultiplied alpha is used! if (mouse_button == 3) { col.A = (byte)((col.A * force + bk_col_a * (255 - force)) / 255); col.R = (byte)((col.R * force + bk_col_r * (255 - force)) / 255); col.G = (byte)((col.G * force + bk_col_g * (255 - force)) / 255); col.B = (byte)((col.B * force + bk_col_b * (255 - force)) / 255); } else { col.A = (byte)(col.A * force / 255); col.R = (byte)(col.R * force / 255); col.G = (byte)(col.G * force / 255); col.B = (byte)(col.B * force / 255); } *srcRowPtr = col; srcRowPtr++; } } //Draw the final result on the surface pasteSurfacePart (g, tmp_surface, dest_rect); } } } }