public void Update(double elapsedSeconds) { elapsedRemainder += elapsedSeconds; while (elapsedRemainder > updateInterval) { elapsedRemainder -= updateInterval; CreateParticle(); particleColor.H += .1; particleColor.H = particleColor.H % 255; for (int i = Particles.Count - 1; i >= 0; i--) { Particle p = Particles[i]; p.Update(updateInterval); if (p.Color.A == 0) { Particles.Remove(p); } } } using (TargetBitmap.GetBitmapContext()) { using (ParticleBitmap.GetBitmapContext(ReadWriteMode.ReadOnly)) { for (int i = 0; i < Particles.Count; i++) { Particle p = Particles[i]; TargetBitmap.Blit(p.Position, ParticleBitmap, sourceRect, p.Color, BitmapBufferExtensions.BlendMode.Additive); } } } }
private void Draw(BitmapBuffer writeableBmp) { if (this.points != null) { ReloadRandomPoints(); // Wrap updates in a GetContext call, to prevent invalidation and nested locking/unlocking during this block using (writeableBmp.GetBitmapContext()) { writeableBmp.Clear(); DrawPoints(writeableBmp); //DrawBeziers(writeableBmp); // DrawCardinal(writeableBmp); //if (ChkShowPoints.IsChecked.Value) //{ // DrawPoints(); //} //if (RBBezier.IsChecked.Value) //{ // DrawBeziers(); //} //else if (RBCardinal.IsChecked.Value) //{ // DrawCardinal(); //} } } }
/// <summary> /// Draws random shapes. /// </summary> private void DrawShapes(BitmapBuffer writeableBmp) { // Wrap updates in a GetContext call, to prevent invalidation and nested locking/unlocking during this block using (writeableBmp.GetBitmapContext()) { // Init some size vars int w = writeableBmp.PixelWidth - 2; int h = writeableBmp.PixelHeight - 2; int w2 = w >> 1; int h2 = h >> 1; // Clear writeableBmp.Clear(); // Fill Shapes for (int i = 0; i < shapeCount; i++) { // Random polygon int[] p = new int[rand.Next(5, 10) * 2]; for (int j = 0; j < p.Length; j += 2) { p[j] = rand.Next(w); p[j + 1] = rand.Next(h); } writeableBmp.FillPolygon(p, GetRandomColor()); } // Invalidates on exit of Using block } }
/// <summary> /// Draws circles that decrease in size to build a flower that is animated /// </summary> private void DrawEllipsesFlower(BitmapBuffer writeableBmp) { // Init some size vars int w = writeableBmp.PixelWidth - 2; int h = writeableBmp.PixelHeight - 2; // Wrap updates in a GetContext call, to prevent invalidation and nested locking/unlocking during this block using (writeableBmp.GetBitmapContext()) { // Increment frame counter if (++frameCounter >= int.MaxValue || frameCounter < 1) { frameCounter = 1; } double s = Math.Sin(frameCounter * 0.01); if (s < 0) { s *= -1; } // Clear writeableBmp.Clear(); // Draw center circle int xc = w >> 1; int yc = h >> 1; // Animate base size with sine int r0 = (int)((w + h) * 0.07 * s) + 10; BitmapBufferEx.ColorInt color_brown = BitmapBufferEx.ColorInt.FromArgb( 255, System.Drawing.Color.Brown.R, System.Drawing.Color.Brown.G, System.Drawing.Color.Brown.B); writeableBmp.DrawEllipseCentered(xc, yc, r0, r0, color_brown); // Draw outer circles int dec = (int)((w + h) * 0.0045f); int r = (int)((w + h) * 0.025f); int offset = r0 + r; for (int i = 1; i < 6 && r > 1; i++) { for (double f = 1; f < 7; f += 0.7) { // Calc postion based on unit circle int xc2 = (int)(Math.Sin(frameCounter * 0.002 * i + f) * offset + xc); int yc2 = (int)(Math.Cos(frameCounter * 0.002 * i + f) * offset + yc); int col = (int)(0xFFFF0000 | (uint)(0x1A * i) << 8 | (uint)(0x20 * f)); writeableBmp.DrawEllipseCentered(xc2, yc2, r, r, col); } // Next ring offset += r; r -= dec; offset += r; } // Invalidates on exit of using block } }
public void Draw(BitmapBuffer writeableBmp) { // Wrap updates in a GetContext call, to prevent invalidation and nested locking/unlocking during this block using (writeableBmp.GetBitmapContext()) { writeableBmp.Clear(); Draw(writeableBmp, this.Root); #if SILVERLIGHT writeableBmp.Invalidate(); #endif } }
/// <summary> /// Draws a triangle. /// </summary> /// <param name="bmp">The WriteableBitmap.</param> /// <param name="x1">The x-coordinate of the 1st point.</param> /// <param name="y1">The y-coordinate of the 1st point.</param> /// <param name="x2">The x-coordinate of the 2nd point.</param> /// <param name="y2">The y-coordinate of the 2nd point.</param> /// <param name="x3">The x-coordinate of the 3rd point.</param> /// <param name="y3">The y-coordinate of the 3rd point.</param> /// <param name="color">The color.</param> public static void DrawTriangle(this BitmapBuffer bmp, int x1, int y1, int x2, int y2, int x3, int y3, int color) { using (BitmapContext context = bmp.GetBitmapContext()) { // Use refs for faster access (really important!) speeds up a lot! int w = context.Width; int h = context.Height; DrawLine(context, w, h, x1, y1, x2, y2, color); DrawLine(context, w, h, x2, y2, x3, y3, color); DrawLine(context, w, h, x3, y3, x1, y1, color); } }
/// <summary> /// Draws a polyline anti-aliased. Add the first point also at the end of the array if the line should be closed. /// </summary> /// <param name="bmp">The WriteableBitmap.</param> /// <param name="points">The points of the polyline in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, ..., xn, yn).</param> /// <param name="color">The color for the line.</param> public static void DrawPolyline(this BitmapBuffer bmp, int[] points, int color) { using (BitmapContext context = bmp.GetBitmapContext()) { // Use refs for faster access (really important!) speeds up a lot! int w = context.Width; int h = context.Height; int x1 = points[0]; int y1 = points[1]; int len = points.Length; for (int i = 2; i < len; i += 2) { DrawLine(context, w, h, x1, y1, x1 += points[i], y1 += points[i + 1], //also update x1,y1 color); } } }
/// <summary> /// Draws a polyline anti-aliased. Add the first point also at the end of the array if the line should be closed. /// </summary> /// <param name="bmp">The WriteableBitmap.</param> /// <param name="points">The points of the polyline in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, ..., xn, yn).</param> /// <param name="color">The color for the line.</param> public static void DrawPolylineAa(this BitmapBuffer bmp, int[] points, int color) { using (var context = bmp.GetBitmapContext()) { // Use refs for faster access (really important!) speeds up a lot! int w = context.Width; int h = context.Height; int x1 = points[0]; int y1 = points[1]; for (int i = 2; i < points.Length; i += 2) { int x2 = points[i]; int y2 = points[i + 1]; DrawLineAa(context, w, h, x1, y1, x2, y2, color); x1 = x2; y1 = y2; } } }
/// <summary> /// Draws random ellipses /// </summary> private void DrawEllipses(BitmapBuffer writeableBmp) { // Init some size vars int w = writeableBmp.PixelWidth - 2; int h = writeableBmp.PixelHeight - 2; int wh = w >> 1; int hh = h >> 1; // Wrap updates in a GetContext call, to prevent invalidation and nested locking/unlocking during this block using (writeableBmp.GetBitmapContext()) { // Clear writeableBmp.Clear(); // Draw Ellipses for (int i = 0; i < shapeCount; i++) { writeableBmp.DrawEllipse(rand.Next(wh), rand.Next(hh), rand.Next(wh, w), rand.Next(hh, h), GetRandomColor()); } // Invalidates on exit of using block } }
/// <summary> /// Draws the different types of shapes. /// </summary> private void DrawStaticShapes(BitmapBuffer writeableBmp) { // Wrap updates in a GetContext call, to prevent invalidation and nested locking/unlocking during this block using (writeableBmp.GetBitmapContext()) { // Init some size vars int w = writeableBmp.PixelWidth - 2; int h = writeableBmp.PixelHeight - 2; int w3rd = w / 3; int h3rd = h / 3; int w6th = w3rd >> 1; int h6th = h3rd >> 1; // Clear writeableBmp.Clear(); // Draw some points for (int i = 0; i < 200; i++) { writeableBmp.SetPixel(rand.Next(w3rd), rand.Next(h3rd), GetRandomColor()); } // Draw Standard shapes writeableBmp.DrawLine(rand.Next(w3rd, w3rd * 2), rand.Next(h3rd), rand.Next(w3rd, w3rd * 2), rand.Next(h3rd), GetRandomColor()); writeableBmp.DrawTriangle(rand.Next(w3rd * 2, w - w6th), rand.Next(h6th), rand.Next(w3rd * 2, w), rand.Next(h6th, h3rd), rand.Next(w - w6th, w), rand.Next(h3rd), GetRandomColor()); writeableBmp.DrawQuad(rand.Next(0, w6th), rand.Next(h3rd, h3rd + h6th), rand.Next(w6th, w3rd), rand.Next(h3rd, h3rd + h6th), rand.Next(w6th, w3rd), rand.Next(h3rd + h6th, 2 * h3rd), rand.Next(0, w6th), rand.Next(h3rd + h6th, 2 * h3rd), GetRandomColor()); writeableBmp.DrawRectangle(rand.Next(w3rd, w3rd + w6th), rand.Next(h3rd, h3rd + h6th), rand.Next(w3rd + w6th, w3rd * 2), rand.Next(h3rd + h6th, 2 * h3rd), GetRandomColor()); // Random polyline int[] p = new int[rand.Next(7, 10) * 2]; for (int j = 0; j < p.Length; j += 2) { p[j] = rand.Next(w3rd * 2, w); p[j + 1] = rand.Next(h3rd, 2 * h3rd); } writeableBmp.DrawPolyline(p, GetRandomColor()); // Random closed polyline p = new int[rand.Next(6, 9) * 2]; for (int j = 0; j < p.Length - 2; j += 2) { p[j] = rand.Next(w3rd); p[j + 1] = rand.Next(2 * h3rd, h); } p[p.Length - 2] = p[0]; p[p.Length - 1] = p[1]; writeableBmp.DrawPolyline(p, GetRandomColor()); // Ellipses writeableBmp.DrawEllipse(rand.Next(w3rd, w3rd + w6th), rand.Next(h3rd * 2, h - h6th), rand.Next(w3rd + w6th, w3rd * 2), rand.Next(h - h6th, h), GetRandomColor()); writeableBmp.DrawEllipseCentered(w - w6th, h - h6th, w6th >> 1, h6th >> 1, GetRandomColor()); // Draw Grid writeableBmp.DrawLine(0, h3rd, w, h3rd, Colors.Black); writeableBmp.DrawLine(0, 2 * h3rd, w, 2 * h3rd, Colors.Black); writeableBmp.DrawLine(w3rd, 0, w3rd, h, Colors.Black); writeableBmp.DrawLine(2 * w3rd, 0, 2 * w3rd, h, Colors.Black); // Invalidates on exit of using block } }
/// <summary> /// Draws the different types of shapes. /// </summary> private void DrawStaticShapes(BitmapBuffer writeableBmp) { // HideShapeCountText(); // Wrap updates in a GetContext call, to prevent invalidation and nested locking/unlocking during this block using (writeableBmp.GetBitmapContext()) { // Init some size vars int w = writeableBmp.PixelWidth; int h = writeableBmp.PixelHeight; int w3 = w / 3; int h3 = h / 3; int w6 = w3 >> 1; int h6 = h3 >> 1; int w12 = w6 >> 1; int h12 = h6 >> 1; // Clear writeableBmp.Clear(); // Fill closed concave polygon var p = new int[] { w12 >> 1, h12, w6, h3 - (h12 >> 1), w3 - (w12 >> 1), h12, w6 + w12, h12, w6, h6 + h12, w12, h12, w12 >> 1, h12, }; writeableBmp.FillPolygonsEvenOdd(new[] { p }, GetRandomColor()); // Fill closed convex polygon p = new int[] { w3 + w6, h12 >> 1, w3 + w6 + w12, h12, w3 + w6 + w12, h6 + h12, w3 + w6, h6 + h12 + (h12 >> 1), w3 + w12, h6 + h12, w3 + w12, h12, w3 + w6, h12 >> 1, }; writeableBmp.FillPolygon(p, GetRandomColor()); // Fill Triangle + Quad writeableBmp.FillTriangle(2 * w3 + w6, h12 >> 1, 2 * w3 + w6 + w12, h6 + h12, 2 * w3 + w12, h6 + h12, GetRandomColor()); writeableBmp.FillQuad(w6, h3 + (h12 >> 1), w6 + w12, h3 + h6, w6, h3 + h6 + h12 + (h12 >> 1), w12, h3 + h6, GetRandomColor()); // Fill Ellipses writeableBmp.FillEllipse(rand.Next(w3, w3 + w6), rand.Next(h3, h3 + h6), rand.Next(w3 + w6, 2 * w3), rand.Next(h3 + h6, 2 * h3), GetRandomColor()); writeableBmp.FillEllipseCentered(2 * w3 + w6, h3 + h6, w12, h12, GetRandomColor()); // Fill closed Cardinal Spline curve p = new int[] { w12 >> 1, 2 * h3 + h12, w6, h - (h12 >> 1), w3 - (w12 >> 1), 2 * h3 + h12, w6 + w12, 2 * h3 + h12, w6, 2 * h3 + (h12 >> 1), w12, 2 * h3 + h12, }; writeableBmp.FillCurveClosed(p, 0.5f, GetRandomColor()); // Fill closed Beziér curve p = new int[] { w3 + w12, 2 * h3 + h6 + h12, w3 + w6 + (w12 >> 1), 2 * h3, w3 + w6 + w12 + (w12 >> 1), 2 * h3, w3 + w6 + w12, 2 * h3 + h6 + h12, }; writeableBmp.FillBeziers(p, GetRandomColor()); // Fill Rectangle writeableBmp.FillRectangle(rand.Next(2 * w3, 2 * w3 + w6), rand.Next(2 * h3, 2 * h3 + h6), rand.Next(2 * w3 + w6, w), rand.Next(2 * h3 + h6, h), GetRandomColor()); // Fill another rectangle with alpha blending writeableBmp.FillRectangle(rand.Next(2 * w3, 2 * w3 + w6), rand.Next(2 * h3, 2 * h3 + h6), rand.Next(2 * w3 + w6, w), rand.Next(2 * h3 + h6, h), GetRandomColor(), true); BitmapBufferEx.ColorInt black = BitmapBufferEx.ColorInt.FromArgb(255, 0, 0, 0); // Draw Grid writeableBmp.DrawLine(0, h3, w, h3, Colors.Black); writeableBmp.DrawLine(0, 2 * h3, w, 2 * h3, Colors.Black); writeableBmp.DrawLine(w3, 0, w3, h, Colors.Black); writeableBmp.DrawLine(2 * w3, 0, 2 * w3, h, Colors.Black); // Invalidates on exit of Using block } }
private void DrawFillDemo(BitmapBuffer writeableBmp) { // Wrap updates in a GetContext call, to prevent invalidation and nested locking/unlocking during this block using (writeableBmp.GetBitmapContext()) { // Init some size vars int w = writeableBmp.PixelWidth - 2; int h = writeableBmp.PixelHeight - 2; int w2 = w >> 1; int h2 = h >> 1; int w4 = w2 >> 1; int h4 = h2 >> 1; int w8 = w4 >> 1; int h8 = h4 >> 1; // Clear writeableBmp.Clear(); // Add circles const float startTimeFixed = 1; const float endTimeFixed = startTimeFixed + timeStep; const float startTimeRandom = 3; const float endTimeCurve = 9.7f; const int intervalRandom = 2; const int maxCircles = 30; // Spread fixed position and color circles if (time > startTimeFixed && time < endTimeFixed) { unchecked { circles.Add(new Circle { X = w8, Y = h8, Radius = 10f, Velocity = 1, Color = (int)0xFFC88717 }); circles.Add(new Circle { X = w8, Y = h - h8, Radius = 10f, Velocity = 1, Color = (int)0xFFFB522B }); circles.Add(new Circle { X = w - w8, Y = h8, Radius = 10f, Velocity = 1, Color = (int)0xFFDB6126 }); circles.Add(new Circle { X = w - w8, Y = h - h8, Radius = 10f, Velocity = 1, Color = (int)0xFFFFCE25 }); } } // Spread random position and color circles if (time > startTimeRandom && (int)time % intervalRandom == 0) { unchecked { circles.Add(new Circle { X = rand.Next(w), Y = rand.Next(h), Radius = 1f, Velocity = rand.Next(1, 5), Color = rand.Next((int)0xFFFF0000, (int)0xFFFFFFFF), }); } } // Render and update circles foreach (var circle in circles) { var r = (int)circle.Radius; writeableBmp.FillEllipseCentered(circle.X, circle.Y, r, r, circle.Color); circle.Update(); } if (circles.Count > maxCircles) { circles.RemoveAt(0); } // Fill closed Cardinal Spline curve if (time < endTimeCurve) { var p = new int[] { w4, h2, w2, h2 + h4, w2 + w4, h2, w2, h4, }; writeableBmp.FillCurveClosed(p, (float)Math.Sin(time) * 7, Colors.Black); } // Update time time += timeStep; // Invalidates on exit of Using block } }
/// <summary> /// A Fast Bresenham Type Algorithm For Drawing Ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf /// Uses a different parameter representation than DrawEllipse(). /// </summary> /// <param name="bmp">The WriteableBitmap.</param> /// <param name="xc">The x-coordinate of the ellipses center.</param> /// <param name="yc">The y-coordinate of the ellipses center.</param> /// <param name="xr">The radius of the ellipse in x-direction.</param> /// <param name="yr">The radius of the ellipse in y-direction.</param> /// <param name="color">The color for the line.</param> public static unsafe void DrawEllipseCentered(this BitmapBuffer bmp, int xc, int yc, int xr, int yr, int color) { // Use refs for faster access (really important!) speeds up a lot! using (BitmapContext context = bmp.GetBitmapContext()) { int *pixels = context.Pixels._inf32Buffer; int w = context.Width; int h = context.Height; // Avoid endless loop if (xr < 1 || yr < 1) { return; } // Init vars int uh, lh, uy, ly, lx, rx; int x = xr; int y = 0; int xrSqTwo = (xr * xr) << 1; int yrSqTwo = (yr * yr) << 1; int xChg = yr * yr * (1 - (xr << 1)); int yChg = xr * xr; int err = 0; int xStopping = yrSqTwo * xr; int yStopping = 0; // Draw first set of points counter clockwise where tangent line slope > -1. while (xStopping >= yStopping) { // Draw 4 quadrant points at once uy = yc + y; // Upper half ly = yc - y; // Lower half if (uy < 0) { uy = 0; // Clip } if (uy >= h) { uy = h - 1; // ... } if (ly < 0) { ly = 0; } if (ly >= h) { ly = h - 1; } uh = uy * w; // Upper half lh = ly * w; // Lower half rx = xc + x; lx = xc - x; if (rx < 0) { rx = 0; // Clip } if (rx >= w) { rx = w - 1; // ... } if (lx < 0) { lx = 0; } if (lx >= w) { lx = w - 1; } pixels[rx + uh] = color; // Quadrant I (Actually an octant) pixels[lx + uh] = color; // Quadrant II pixels[lx + lh] = color; // Quadrant III pixels[rx + lh] = color; // Quadrant IV y++; yStopping += xrSqTwo; err += yChg; yChg += xrSqTwo; if ((xChg + (err << 1)) > 0) { x--; xStopping -= yrSqTwo; err += xChg; xChg += yrSqTwo; } } // ReInit vars x = 0; y = yr; uy = yc + y; // Upper half ly = yc - y; // Lower half if (uy < 0) { uy = 0; // Clip } if (uy >= h) { uy = h - 1; // ... } if (ly < 0) { ly = 0; } if (ly >= h) { ly = h - 1; } uh = uy * w; // Upper half lh = ly * w; // Lower half xChg = yr * yr; yChg = xr * xr * (1 - (yr << 1)); err = 0; xStopping = 0; yStopping = xrSqTwo * yr; // Draw second set of points clockwise where tangent line slope < -1. while (xStopping <= yStopping) { // Draw 4 quadrant points at once rx = xc + x; lx = xc - x; if (rx < 0) { rx = 0; // Clip } if (rx >= w) { rx = w - 1; // ... } if (lx < 0) { lx = 0; } if (lx >= w) { lx = w - 1; } pixels[rx + uh] = color; // Quadrant I (Actually an octant) pixels[lx + uh] = color; // Quadrant II pixels[lx + lh] = color; // Quadrant III pixels[rx + lh] = color; // Quadrant IV x++; xStopping += yrSqTwo; err += xChg; xChg += yrSqTwo; if ((yChg + (err << 1)) > 0) { y--; uy = yc + y; // Upper half ly = yc - y; // Lower half if (uy < 0) { uy = 0; // Clip } if (uy >= h) { uy = h - 1; // ... } if (ly < 0) { ly = 0; } if (ly >= h) { ly = h - 1; } uh = uy * w; // Upper half lh = ly * w; // Lower half yStopping -= xrSqTwo; err += yChg; yChg += xrSqTwo; } } } }
/// <summary> /// Draws a rectangle. /// x2 has to be greater than x1 and y2 has to be greater than y1. /// </summary> /// <param name="bmp">The WriteableBitmap.</param> /// <param name="x1">The x-coordinate of the bounding rectangle's left side.</param> /// <param name="y1">The y-coordinate of the bounding rectangle's top side.</param> /// <param name="x2">The x-coordinate of the bounding rectangle's right side.</param> /// <param name="y2">The y-coordinate of the bounding rectangle's bottom side.</param> /// <param name="color">The color.</param> public static unsafe void DrawRectangle(this BitmapBuffer bmp, int x1, int y1, int x2, int y2, int color) { using (BitmapContext context = bmp.GetBitmapContext()) { // Use refs for faster access (really important!) speeds up a lot! int w = context.Width; int h = context.Height; int *pixels = context.Pixels._inf32Buffer; // Check boundaries if ((x1 < 0 && x2 < 0) || (y1 < 0 && y2 < 0) || (x1 >= w && x2 >= w) || (y1 >= h && y2 >= h)) { return; } // Clamp boundaries if (x1 < 0) { x1 = 0; } if (y1 < 0) { y1 = 0; } if (x2 < 0) { x2 = 0; } if (y2 < 0) { y2 = 0; } if (x1 >= w) { x1 = w - 1; } if (y1 >= h) { y1 = h - 1; } if (x2 >= w) { x2 = w - 1; } if (y2 >= h) { y2 = h - 1; } int startY = y1 * w; int endY = y2 * w; int offset2 = endY + x1; int endOffset = startY + x2; int startYPlusX1 = startY + x1; // top and bottom horizontal scanlines for (int x = startYPlusX1; x <= endOffset; x++) { pixels[x] = color; // top horizontal line pixels[offset2] = color; // bottom horizontal line offset2++; } // offset2 == endY + x2 // vertical scanlines endOffset = startYPlusX1 + w; offset2 -= w; for (int y = startY + x2 + w; y <= offset2; y += w) { pixels[y] = color; // right vertical line pixels[endOffset] = color; // left vertical line endOffset += w; } } }
/// <summary> /// Renders a bitmap using any affine transformation and transparency into this bitmap /// Unlike Silverlight's Render() method, this one uses 2-3 times less memory, and is the same or better quality /// The algorithm is simple dx/dy (bresenham-like) step by step painting, optimized with fixed point and fast bilinear filtering /// It's used in Fantasia Painter for drawing stickers and 3D objects on screen /// </summary> /// <param name="bmp">Destination bitmap.</param> /// <param name="source">The source WriteableBitmap.</param> /// <param name="shouldClear">If true, the the destination bitmap will be set to all clear (0) before rendering.</param> /// <param name="opacity">opacity of the source bitmap to render, between 0 and 1 inclusive</param> /// <param name="transform">Transformation to apply</param> public static void BlitRender(this BitmapBuffer bmp, BitmapBuffer source, bool shouldClear = true, float opacity = 1f, GeneralTransform transform = null) { const int PRECISION_SHIFT = 10; const int PRECISION_VALUE = (1 << PRECISION_SHIFT); const int PRECISION_MASK = PRECISION_VALUE - 1; using (BitmapContext destContext = bmp.GetBitmapContext()) { if (transform == null) { transform = new MatrixTransform(Affine.IdentityMatrix); } int[] destPixels = destContext.Pixels; int destWidth = destContext.Width; int destHeight = destContext.Height; MatrixTransform inverse = transform.Inverse; if (shouldClear) { destContext.Clear(); } using (BitmapContext sourceContext = source.GetBitmapContext(ReadWriteMode.ReadOnly)) { var sourcePixels = sourceContext.Pixels; int sourceWidth = sourceContext.Width; int sourceHeight = sourceContext.Height; RectD sourceRect = new RectD(0, 0, sourceWidth, sourceHeight); RectD destRect = new RectD(0, 0, destWidth, destHeight); RectD bounds = transform.TransformBounds(sourceRect); bounds.Intersect(destRect); int startX = (int)bounds.Left; int startY = (int)bounds.Top; int endX = (int)bounds.Right; int endY = (int)bounds.Bottom; #if NETFX_CORE Point zeroZero = inverse.TransformPoint(new Point(startX, startY)); Point oneZero = inverse.TransformPoint(new Point(startX + 1, startY)); Point zeroOne = inverse.TransformPoint(new Point(startX, startY + 1)); #else PointD zeroZero = inverse.Transform(new PointD(startX, startY)); PointD oneZero = inverse.Transform(new PointD(startX + 1, startY)); PointD zeroOne = inverse.Transform(new PointD(startX, startY + 1)); #endif float sourceXf = ((float)zeroZero.X); float sourceYf = ((float)zeroZero.Y); int dxDx = (int)((((float)oneZero.X) - sourceXf) * PRECISION_VALUE); // for 1 unit in X coord, how much does X change in source texture? int dxDy = (int)((((float)oneZero.Y) - sourceYf) * PRECISION_VALUE); // for 1 unit in X coord, how much does Y change in source texture? int dyDx = (int)((((float)zeroOne.X) - sourceXf) * PRECISION_VALUE); // for 1 unit in Y coord, how much does X change in source texture? int dyDy = (int)((((float)zeroOne.Y) - sourceYf) * PRECISION_VALUE); // for 1 unit in Y coord, how much does Y change in source texture? int sourceX = (int)(((float)zeroZero.X) * PRECISION_VALUE); int sourceY = (int)(((float)zeroZero.Y) * PRECISION_VALUE); int sourceWidthFixed = sourceWidth << PRECISION_SHIFT; int sourceHeightFixed = sourceHeight << PRECISION_SHIFT; int opacityInt = (int)(opacity * 255); int index = 0; for (int destY = startY; destY < endY; destY++) { index = destY * destWidth + startX; int savedSourceX = sourceX; int savedSourceY = sourceY; for (int destX = startX; destX < endX; destX++) { if ((sourceX >= 0) && (sourceX < sourceWidthFixed) && (sourceY >= 0) && (sourceY < sourceHeightFixed)) { // bilinear filtering int xFloor = sourceX >> PRECISION_SHIFT; int yFloor = sourceY >> PRECISION_SHIFT; if (xFloor < 0) { xFloor = 0; } if (yFloor < 0) { yFloor = 0; } int xCeil = xFloor + 1; int yCeil = yFloor + 1; if (xCeil >= sourceWidth) { xFloor = sourceWidth - 1; xCeil = 0; } else { xCeil = 1; } if (yCeil >= sourceHeight) { yFloor = sourceHeight - 1; yCeil = 0; } else { yCeil = sourceWidth; } int i1 = yFloor * sourceWidth + xFloor; int p1 = sourcePixels[i1]; int p2 = sourcePixels[i1 + xCeil]; int p3 = sourcePixels[i1 + yCeil]; int p4 = sourcePixels[i1 + yCeil + xCeil]; int xFrac = sourceX & PRECISION_MASK; int yFrac = sourceY & PRECISION_MASK; // alpha byte a1 = (byte)(p1 >> 24); byte a2 = (byte)(p2 >> 24); byte a3 = (byte)(p3 >> 24); byte a4 = (byte)(p4 >> 24); int comp1, comp2; byte a; if ((a1 == a2) && (a1 == a3) && (a1 == a4)) { if (a1 == 0) { destPixels[index] = 0; sourceX += dxDx; sourceY += dxDy; index++; continue; } a = a1; } else { comp1 = a1 + ((xFrac * (a2 - a1)) >> PRECISION_SHIFT); comp2 = a3 + ((xFrac * (a4 - a3)) >> PRECISION_SHIFT); a = (byte)(comp1 + ((yFrac * (comp2 - comp1)) >> PRECISION_SHIFT)); } // red comp1 = ((byte)(p1 >> 16)) + ((xFrac * (((byte)(p2 >> 16)) - ((byte)(p1 >> 16)))) >> PRECISION_SHIFT); comp2 = ((byte)(p3 >> 16)) + ((xFrac * (((byte)(p4 >> 16)) - ((byte)(p3 >> 16)))) >> PRECISION_SHIFT); byte r = (byte)(comp1 + ((yFrac * (comp2 - comp1)) >> PRECISION_SHIFT)); // green comp1 = ((byte)(p1 >> 8)) + ((xFrac * (((byte)(p2 >> 8)) - ((byte)(p1 >> 8)))) >> PRECISION_SHIFT); comp2 = ((byte)(p3 >> 8)) + ((xFrac * (((byte)(p4 >> 8)) - ((byte)(p3 >> 8)))) >> PRECISION_SHIFT); byte g = (byte)(comp1 + ((yFrac * (comp2 - comp1)) >> PRECISION_SHIFT)); // blue comp1 = ((byte)p1) + ((xFrac * (((byte)p2) - ((byte)p1))) >> PRECISION_SHIFT); comp2 = ((byte)p3) + ((xFrac * (((byte)p4) - ((byte)p3))) >> PRECISION_SHIFT); byte b = (byte)(comp1 + ((yFrac * (comp2 - comp1)) >> PRECISION_SHIFT)); // save updated pixel if (opacityInt != 255) { a = (byte)((a * opacityInt) >> 8); r = (byte)((r * opacityInt) >> 8); g = (byte)((g * opacityInt) >> 8); b = (byte)((b * opacityInt) >> 8); } destPixels[index] = (a << 24) | (r << 16) | (g << 8) | b; } sourceX += dxDx; sourceY += dxDy; index++; } sourceX = savedSourceX + dyDx; sourceY = savedSourceY + dyDy; } } } }
/// <summary> /// Copies (blits) the pixels from the WriteableBitmap source to the destination WriteableBitmap (this). /// </summary> /// <param name="bmp">The destination WriteableBitmap.</param> /// <param name="destRect">The rectangle that defines the destination region.</param> /// <param name="source">The source WriteableBitmap.</param> /// <param name="sourceRect">The rectangle that will be copied from the source to the destination.</param> /// <param name="color">If not Colors.White, will tint the source image. A partially transparent color and the image will be drawn partially transparent. If the BlendMode is ColorKeying, this color will be used as color key to mask all pixels with this value out.</param> /// <param name="blendMode">The blending mode <see cref="BlendMode"/>.</param> internal static void Blit(this BitmapBuffer bmp, RectD destRect, BitmapBuffer source, RectD sourceRect, ColorInt color, BlendMode blendMode) { if (color.A == 0) { return; } #if WPF bool isPrgba = source.Format == PixelFormats.Pbgra32 || source.Format == PixelFormats.Prgba64 || source.Format == PixelFormats.Prgba128Float; #endif int dw = (int)destRect.Width; int dh = (int)destRect.Height; using (var srcContext = source.GetBitmapContext(ReadWriteMode.ReadOnly)) using (var destContext = bmp.GetBitmapContext()) { int sourceWidth = srcContext.Width; int dpw = destContext.Width; int dph = destContext.Height; RectD intersect = new RectD(0, 0, dpw, dph); intersect.Intersect(destRect); if (intersect.IsEmpty) { return; } int[] sourcePixels = srcContext.Pixels; int[] destPixels = destContext.Pixels; int sourceLength = srcContext.Length; int sourceIdx = -1; int px = (int)destRect.X; int py = (int)destRect.Y; int x; int y; int idx; double ii; double jj; // int sr = 0; int sg = 0; int sb = 0; // int dr, dg, db; int sourcePixel; int sa = 0; int da; int ca = color.A; int cr = color.R; int cg = color.G; int cb = color.B; bool tinted = color != Colors.White; int sw = (int)sourceRect.Width; double sdx = sourceRect.Width / destRect.Width; double sdy = sourceRect.Height / destRect.Height; int sourceStartX = (int)sourceRect.X; int sourceStartY = (int)sourceRect.Y; int lastii, lastjj; lastii = -1; lastjj = -1; jj = sourceStartY; y = py; for (int j = 0; j < dh; j++) { if (y >= 0 && y < dph) { ii = sourceStartX; idx = px + y * dpw; x = px; sourcePixel = sourcePixels[0]; // Scanline BlockCopy is much faster (3.5x) if no tinting and blending is needed, // even for smaller sprites like the 32x32 particles. if (blendMode == BlendMode.None && !tinted) { sourceIdx = (int)ii + (int)jj * sourceWidth; int offset = x < 0 ? -x : 0; int xx = x + offset; int wx = sourceWidth - offset; int len = xx + wx < dpw ? wx : dpw - xx; if (len > sw) { len = sw; } if (len > dw) { len = dw; } BitmapContext.BlockCopy(srcContext, (sourceIdx + offset) * 4, destContext, (idx + offset) * 4, len * 4); } // Pixel by pixel copying else { for (int i = 0; i < dw; i++) { if (x >= 0 && x < dpw) { if ((int)ii != lastii || (int)jj != lastjj) { sourceIdx = (int)ii + (int)jj * sourceWidth; if (sourceIdx >= 0 && sourceIdx < sourceLength) { sourcePixel = sourcePixels[sourceIdx]; sa = ((sourcePixel >> 24) & 0xff); sr = ((sourcePixel >> 16) & 0xff); sg = ((sourcePixel >> 8) & 0xff); sb = ((sourcePixel) & 0xff); if (tinted && sa != 0) { sa = (((sa * ca) * 0x8081) >> 23); sr = ((((((sr * cr) * 0x8081) >> 23) * ca) * 0x8081) >> 23); sg = ((((((sg * cg) * 0x8081) >> 23) * ca) * 0x8081) >> 23); sb = ((((((sb * cb) * 0x8081) >> 23) * ca) * 0x8081) >> 23); sourcePixel = (sa << 24) | (sr << 16) | (sg << 8) | sb; } } else { sa = 0; } } if (blendMode == BlendMode.None) { destPixels[idx] = sourcePixel; } else if (blendMode == BlendMode.ColorKeying) { sr = ((sourcePixel >> 16) & 0xff); sg = ((sourcePixel >> 8) & 0xff); sb = ((sourcePixel) & 0xff); if (!color.EqualsOnRGB(sr, sg, sb)) { destPixels[idx] = sourcePixel; } //if (sr != color.R || sg != color.G || sb != color.B) //{ // destPixels[idx] = sourcePixel; //} } else if (blendMode == BlendMode.Mask) { int destPixel = destPixels[idx]; da = ((destPixel >> 24) & 0xff); dr = ((destPixel >> 16) & 0xff); dg = ((destPixel >> 8) & 0xff); db = ((destPixel) & 0xff); destPixel = ((((da * sa) * 0x8081) >> 23) << 24) | ((((dr * sa) * 0x8081) >> 23) << 16) | ((((dg * sa) * 0x8081) >> 23) << 8) | ((((db * sa) * 0x8081) >> 23)); destPixels[idx] = destPixel; } else if (sa > 0) { int destPixel = destPixels[idx]; da = ((destPixel >> 24) & 0xff); if ((sa == 255 || da == 0) && blendMode != BlendMode.Additive && blendMode != BlendMode.Subtractive && blendMode != BlendMode.Multiply ) { destPixels[idx] = sourcePixel; } else { dr = ((destPixel >> 16) & 0xff); dg = ((destPixel >> 8) & 0xff); db = ((destPixel) & 0xff); if (blendMode == BlendMode.Alpha) { int isa = 255 - sa; #if NETFX_CORE // Special case for WinRT since it does not use pARGB (pre-multiplied alpha) destPixel = ((da & 0xff) << 24) | ((((sr * sa + isa * dr) >> 8) & 0xff) << 16) | ((((sg * sa + isa * dg) >> 8) & 0xff) << 8) | (((sb * sa + isa * db) >> 8) & 0xff); #elif WPF if (isPrgba) { destPixel = ((da & 0xff) << 24) | (((((sr << 8) + isa * dr) >> 8) & 0xff) << 16) | (((((sg << 8) + isa * dg) >> 8) & 0xff) << 8) | ((((sb << 8) + isa * db) >> 8) & 0xff); } else { destPixel = ((da & 0xff) << 24) | (((((sr * sa) + isa * dr) >> 8) & 0xff) << 16) | (((((sg * sa) + isa * dg) >> 8) & 0xff) << 8) | ((((sb * sa) + isa * db) >> 8) & 0xff); } #else destPixel = ((da & 0xff) << 24) | (((((sr << 8) + isa * dr) >> 8) & 0xff) << 16) | (((((sg << 8) + isa * dg) >> 8) & 0xff) << 8) | ((((sb << 8) + isa * db) >> 8) & 0xff); #endif } else if (blendMode == BlendMode.Additive) { int a = (255 <= sa + da) ? 255 : (sa + da); destPixel = (a << 24) | (((a <= sr + dr) ? a : (sr + dr)) << 16) | (((a <= sg + dg) ? a : (sg + dg)) << 8) | (((a <= sb + db) ? a : (sb + db))); } else if (blendMode == BlendMode.Subtractive) { int a = da; destPixel = (a << 24) | (((sr >= dr) ? 0 : (sr - dr)) << 16) | (((sg >= dg) ? 0 : (sg - dg)) << 8) | (((sb >= db) ? 0 : (sb - db))); } else if (blendMode == BlendMode.Multiply) { // Faster than a division like (s * d) / 255 are 2 shifts and 2 adds int ta = (sa * da) + 128; int tr = (sr * dr) + 128; int tg = (sg * dg) + 128; int tb = (sb * db) + 128; int ba = ((ta >> 8) + ta) >> 8; int br = ((tr >> 8) + tr) >> 8; int bg = ((tg >> 8) + tg) >> 8; int bb = ((tb >> 8) + tb) >> 8; destPixel = (ba << 24) | ((ba <= br ? ba : br) << 16) | ((ba <= bg ? ba : bg) << 8) | ((ba <= bb ? ba : bb)); } destPixels[idx] = destPixel; } } } x++; idx++; ii += sdx; } } } jj += sdy; y++; } } }