/// <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++; } } }