float remainingSpace; //keeps track of spacing protected unsafe override void OnMouseMove(QueuedToolEventArgs le) { SmudgeEventArgs e = le as SmudgeEventArgs; int jittermax = (int)(e.Jitter * e.BrushWidth); Rectangle invrect = new Rectangle(lastmouse.X - brushwidth / 2, lastmouse.Y - brushheight / 2, brushwidth, brushheight); invrect = Rectangle.Union(invrect, new Rectangle(e.X - brushwidth / 2, e.Y - brushheight / 2, brushwidth, brushheight)); invrect.Inflate(jittermax, jittermax); if (e.Button == MouseButtons.Left && mousedown) { float dist = Distance(lastmouse, e.Location); ColorBgra *ptr0 = (ColorBgra *)Surface.Scan0.VoidStar; float f; for (f = remainingSpace; f < dist && !IsAborted; f += spacing) { PointF currentpoint = Lerp(lastmouse, e.Location, f / dist); if (jittermax > 0) { currentpoint.X += r.Next(-jittermax, jittermax); currentpoint.Y += r.Next(-jittermax, jittermax); } Point dstpt = Point.Subtract(new Point((int)currentpoint.X, (int)currentpoint.Y), brushcenteroffset); Rectangle dstRect = new Rectangle(dstpt, brushsize); Rectangle clippedRect = Rectangle.Intersect(dstRect, Surface.Bounds); fixed(float *brshpin = brush, _strmask = strengthmask) { float *brshptr = brshpin, strmask = _strmask; int maxy = dstpt.Y + dstRect.Height; int maxx = dstpt.X + dstRect.Width; for (int y = dstpt.Y; y < maxy; ++y) { int dsty = Clamp(y, 0, srfcHeight - 1); ColorBgra *ptrrow = ptr0 + srfcWidth * dsty; for (int x = dstpt.X; x < maxx; ++x) { int dstx = Clamp(x, 0, srfcWidth - 1); ColorBgra *srfcptr = ptrrow + dstx; float invstr = 1.0f - *strmask; //eliminate fringing from 0-alpha //this officially makes us awesomer than Photoshop if (brshptr[A] == 0.0f) { brshptr[B] = srfcptr->B; brshptr[G] = srfcptr->G; brshptr[R] = srfcptr->R; } else if (srfcptr->A == 0) { srfcptr->B = (byte)(brshptr[B]); srfcptr->G = (byte)(brshptr[G]); srfcptr->R = (byte)(brshptr[R]); } //blend the surface into the brush buffer, then copy it back onto the surface brshptr[B] = (brshptr[B] * *strmask + srfcptr->B * invstr); srfcptr->B = (byte)(brshptr[B] + 0.5f); brshptr[G] = (brshptr[G] * *strmask + srfcptr->G * invstr); srfcptr->G = (byte)(brshptr[G] + 0.5f); brshptr[R] = (brshptr[R] * *strmask + srfcptr->R * invstr); srfcptr->R = (byte)(brshptr[R] + 0.5f); brshptr[A] = (brshptr[A] * *strmask + srfcptr->A * invstr); srfcptr->A = (byte)(brshptr[A] + 0.5f); brshptr += 4; ++strmask; } } } } remainingSpace = f - dist; } OnInvalidated(invrect); lastmouse = e.Location; }
protected unsafe override void OnMouseDown(QueuedToolEventArgs le) { SmudgeEventArgs e = le as SmudgeEventArgs; lastmouse = e.Location; if (e.Button == MouseButtons.Left) { mousedown = true; using (Surface brushsource = e.Brush.GetSurface(e.BrushWidth)) { brushheight = brushsource.Height; brushwidth = brushsource.Width; brushsize = new Size(brushwidth, brushheight); brushcenteroffset = new Size(brushwidth / 2, brushheight / 2); spacing = GetSpacing(brushsize, e.Quality); int brusharea = brushwidth * brushheight; brush = new float[brusharea * 4]; Point srcpt = Point.Subtract(e.Location, new Size(brushwidth / 2, brushheight / 2)); strengthmask = new float[brusharea]; float strength = (float)(Math.Pow(e.Strength, .25) / 255.0); fixed(float *_strmask = strengthmask) { float *strmask = _strmask; ColorBgra *voidstar = (ColorBgra *)brushsource.Scan0.VoidStar; for (int i = 0; i < brushsource.Scan0.Length; i += 4) { *strmask = voidstar->A * strength; ++voidstar; ++strmask; } } //fill the brush buffer by copying the surface where we clicked into the buffer fixed(float *brshptrpin = brush) { float *brshptr = brshptrpin; ColorBgra *ptr0 = (ColorBgra *)Surface.Scan0.VoidStar; for (int y = 0; y < brushheight; ++y) { int srcy = Clamp(srcpt.Y + y, 0, srfcHeight - 1); ColorBgra *ptrrow = ptr0 + srfcWidth * srcy; for (int x = 0; x < brushwidth; ++x) { int srcx = Clamp(srcpt.X + x, 0, srfcWidth - 1); ColorBgra *ptr = ptrrow + srcx; brshptr[B] = ptr->B; brshptr[G] = ptr->G; brshptr[R] = ptr->R; brshptr[A] = ptr->A; brshptr += 4; } } } } } }