Пример #1
0
        /// <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++;
                    }
                }
        }