public static IPixelCanvas Blit(IPixelCanvas first, IPixelCanvas second, BlendMode blendMode) { var pc = new PixelArrayCanvas(first.Width, first.Height); pc.Blit(second, blendMode); return pc; }
/// <summary> /// http://en.wikipedia.org/wiki/Alpha_compositing /// </summary> /// <param name="destination_rect"></param> /// <param name="source"></param> /// <param name="source_rect"></param> /// <param name="alpha"></param> /// <param name="red"></param> /// <param name="green"></param> /// <param name="blue"></param> /// <param name="blendMode"></param> public static void DEBUG__Blit( this IPixelCanvas pc, Rectangle destination_rect, IPixelCanvas source, Rect source_rect, byte alpha, byte red, byte green, byte blue, BlendMode blendMode) { int debug_counter = 0; var debug_destination_pc = new PixelArrayCanvas(pc.Width, pc.Height); debug_destination_pc.Blit(pc, BlendMode.Copy); debug_destination_pc.Save(@"c:\temp\blend\{0}_destination.bmp".FormatWith(debug_counter)); var debug_source_pc = new PixelArrayCanvas(source.Width, source.Height); debug_source_pc.Blit(pc, BlendMode.Copy); debug_source_pc.Save(@"c:\temp\blend\{0}_source.bmp".FormatWith(debug_counter)); // http://keithp.com/~keithp/porterduff/p253-porter.pdf // http://en.wikipedia.org/wiki/Alpha_compositing // tint with transparent color, nothing to do here, since this would make every source pixel transparent if (alpha == 0) return; if (!pc.IntersectsWith(destination_rect)) return; // tinted if color is not opaque white var is_tinted = alpha != 255 || red != 255 || green != 255 || blue != 255; var source_width = source.Width; var source_pixels = source; var source_length = source.Length; int source_rectangle_x = (int)source_rect.X; int source_rectangle_y = (int)source_rect.Y; var destination_width = pc.Width; var destination_height = pc.Height; var destination_pixels = pc; var destination_length = pc.Length; int destination_rectangle_x = (int)destination_rect.X; int destination_rectangle_y = (int)destination_rect.Y; int destination_rect_width = (int)destination_rect.Width; int destination_rect_height = (int)destination_rect.Height; int sourceIdx = -1; int idx; double ii; double jj; int source_pixel = 0; int source_alpha = 0; int source_red = 0; int source_green = 0; int source_blue = 0; int destination_alpha; int destination_red; int destination_green; int destination_blue; var source_rectangle_width = source_rect.Width; var sdx = source_rect.Width / destination_rect.Width; var sdy = source_rect.Height / destination_rect.Height; int lastii = -1; int lastjj = -1; jj = source_rectangle_y; int current_x; int current_y = destination_rectangle_y; // each row in destination rectangle for (int destination_rect_row = 0; destination_rect_row < destination_rect_height; destination_rect_row++) { // check if current y is within destination rectangle bounds (todo: >= 0? should it be destination_rect_y ?) if (current_y >= 0 && current_y < destination_height) { ii = source_rectangle_x; idx = destination_rectangle_x + current_y * destination_width; // start from left most column in destination rectangle current_x = destination_rectangle_x; // each column in destination rectangle for (int destination_rect_col = 0; destination_rect_col < destination_rect_width; destination_rect_col++) { // todo: >= 0? if (current_x >= 0 && current_x < destination_width) { if ((int)ii != lastii || (int)jj != lastjj) { sourceIdx = (int)ii + (int)jj * source_width; if (sourceIdx >= 0 && sourceIdx < source_length) { // retrieve current source pixel and it's channels values source_pixel = source_pixels[sourceIdx]; if (blendMode == BlendMode.Copy) { destination_pixels[idx] = source_pixel; current_x++; idx++; ii += sdx; continue; } if (source_pixel != 0) { source_alpha = ((source_pixel >> 24) & 0xff); source_red = ((source_pixel >> 16) & 0xff); source_green = ((source_pixel >> 8) & 0xff); source_blue = ((source_pixel) & 0xff); // tint the pixel if needed if (is_tinted && source_alpha != 0) { source_alpha = (((source_alpha * alpha) * 0x8081) >> 23); source_red = ((((((source_red * red) * 0x8081) >> 23) * alpha) * 0x8081) >> 23); source_green = ((((((source_green * green) * 0x8081) >> 23) * alpha) * 0x8081) >> 23); source_blue = ((((((source_blue * blue) * 0x8081) >> 23) * alpha) * 0x8081) >> 23); source_pixel = (source_alpha << 24) | (source_red << 16) | (source_green << 8) | source_blue; } } else { source_alpha = 0; } } else { source_alpha = 0; } } if (source_alpha == 0) { current_x++; idx++; ii += sdx; continue; } else { // get destination pixel int destPixel = destination_pixels[idx]; destination_alpha = ((destPixel >> 24) & 0xff); // destination is transparent or source is opaque, // just replace destination pixel with source pixel if (blendMode == BlendMode.Alpha && (source_alpha == 255 || destination_alpha == 0)) { // debug code debug_counter++; debug_destination_pc[idx] = pc.GetColor(System.Windows.Media.Colors.Red); debug_source_pc[sourceIdx] = pc.GetColor(System.Windows.Media.Colors.Red); debug_destination_pc.Save(@"c:\temp\blend\{0}_destination.bmp".FormatWith(debug_counter)); debug_source_pc.Save(@"c:\temp\blend\{0}_source.bmp".FormatWith(debug_counter)); // end debug code destination_pixels[idx] = source_pixel; current_x++; idx++; ii += sdx; continue; } // get destination pixel rgb values destination_red = ((destPixel >> 16) & 0xff); destination_green = ((destPixel >> 8) & 0xff); destination_blue = ((destPixel) & 0xff); if (blendMode == BlendMode.Alpha) { var isa = 255 - source_alpha; // debug code debug_destination_pc[idx] = pc.GetColor(System.Windows.Media.Colors.Red); debug_source_pc[sourceIdx] = pc.GetColor(System.Windows.Media.Colors.Red); var deb_part = new PixelArrayCanvas(3, 1); deb_part[0] = source_pixel; deb_part[1] = destPixel; // end debug code destPixel = (((((source_alpha << 8) + isa * destination_alpha) >> 8) & 0xff) << 24) | (((((source_red << 8) + isa * destination_red) >> 8) & 0xff) << 16) | (((((source_green << 8) + isa * destination_green) >> 8) & 0xff) << 8) | ((((source_blue << 8) + isa * destination_blue) >> 8) & 0xff); // debug code deb_part[2] = destPixel; debug_counter++; debug_destination_pc.Save(@"c:\temp\blend\{0}_destination.bmp".FormatWith(debug_counter)); debug_source_pc.Save(@"c:\temp\blend\{0}_source.bmp".FormatWith(debug_counter)); deb_part.Save(@"c:\temp\blend\{0}_blend.bmp".FormatWith(debug_counter)); //end debug code } else throw new NotSupportedException(); destination_pixels[idx] = destPixel; } } current_x++; idx++; ii += sdx; } } jj += sdy; current_y++; } // debug code debug_destination_pc.Save(@"c:\temp\blend\final_destination.bmp".FormatWith(debug_counter)); debug_source_pc.Save(@"c:\temp\blend\final_source.bmp".FormatWith(debug_counter)); pc.Save(@"c:\temp\blend\final_blend.bmp".FormatWith(debug_counter)); // end debug code }
public static void DrawGeometry( this IPixelCanvas pc, int x, int y, Geometry geometry, Brush fillBrush, Pen pen, BlendMode blendMode) { var size = geometry.GetRenderBounds(pen).Size; var width = (int)size.Width; var height = (int)size.Height; var bmp = geometry.RenderToBitmap( size, new Point(), size, fillBrush, pen, PixelFormats.Pbgra32); int[] pixels; if (blendMode == BlendMode.Copy && width == pc.Width && height == pc.Height) { pixels = pc.GetPixels(); bmp.CopyPixels(pixels, pc.Stride, 0); } else { var pc2 = new PixelArrayCanvas(width, height); pixels = pc2.GetPixels(); bmp.CopyPixels(pixels, pc2.Stride, 0); pc.Blit( new Rectangle(x, y, width, height), pc2, new Rectangle(0, 0, width, height), 255, 255, 255, 255, blendMode); } }
public static void DrawVisual(this IPixelCanvas pc, int x, int y, ContainerVisual visual, BlendMode blendMode) { if (visual.ContentBounds.IsEmpty) return; var width = (int)visual.ContentBounds.Width; var height = (int)visual.ContentBounds.Height; var bmp = visual.RenderToBitmap(visual.ContentBounds.Size, new Point(0, 0)); if (blendMode == BlendMode.Copy && width == pc.Width && height == pc.Height) { var pac = pc as PixelArrayCanvas; if(pac == null) { throw new NotSupportedException("BlendMode.Copy is only supported with PixelArrayCanvas"); } bmp.CopyPixels(pac.Pixels, pc.Stride, 0); } else { var sw = Stopwatch.StartNew(); var pc2 = new PixelArrayCanvas(width, height); bmp.CopyPixels(pc2.Pixels, pc2.Stride, 0); Debug.WriteLine("Copy Pixels " + sw.GetElapsedAndRestart().TotalMilliseconds); pc.Blit( new Rectangle(x, y, width, height), pc2, new Rectangle(0, 0, width, height), 255, 255, 255, 255, blendMode); Debug.WriteLine("Blit " + sw.GetElapsedAndRestart().TotalMilliseconds); } }
public static void DrawLine(this IPixelCanvas pc, int x1, int y1, int x2, int y2, System.Windows.Media.Color color, double width) { var l = new Shapes.Line(); l.X1 = x1; l.Y1 = y1; l.X2 = x2; l.Y2 = y2; l.Stroke = new SolidColorBrush(color); l.StrokeThickness = width; l.Arrange(pc.Bounds.ToRect()); var sw = Stopwatch.StartNew(); var bitmap_source = l.RenderToBitmap(pc.Bounds.ToSize(), new Point(x1, y1)); var pixels = new int[pc.Length]; bitmap_source.CopyPixels(pixels, pc.Stride, 0); var pc2 = new PixelArrayCanvas(pc.Width, pc.Height); pc2.ReplaceFromPixels(pixels, pc.Width, pc.Height); pc.Blit(pc2, BlendMode.Alpha); }