コード例 #1
0
        public IrregularSurface(Cairo.ImageSurface source, Rectangle[] roi)
        {
            this.placedSurfaces = new List <PlacedSurface> (roi.Length);

            foreach (Rectangle rect in roi)
            {
                Rectangle ri = Rectangle.Intersect(source.GetBounds(), rect);

                if (!ri.IsEmpty)
                {
                    this.placedSurfaces.Add(new PlacedSurface(source, ri));
                }
            }

            this.region = Utility.RectanglesToRegion(roi);
            this.region.Intersect(Region.Rectangle(source.GetBounds()));
        }
コード例 #2
0
ファイル: CanvasRenderer.cs プロジェクト: mfcallahan/Pinta
        public void Render(List <Layer> layers, Cairo.ImageSurface dst, Point offset)
        {
            dst.Flush();

            // Our rectangle of interest
            var r   = new Rectangle(offset, dst.GetBounds().Size).ToCairoRectangle();
            var doc = PintaCore.Workspace.ActiveDocument;

            using (var g = new Cairo.Context(dst)) {
                // Create the transparent checkerboard background
                g.Translate(-offset.X, -offset.Y);
                g.FillRectangle(r, tranparent_pattern, new Cairo.PointD(offset.X, offset.Y));

                for (var i = 0; i < layers.Count; i++)
                {
                    var layer = layers[i];

                    // If we're in LivePreview, substitute current layer with the preview layer
                    if (layer == doc.Layers.CurrentUserLayer && PintaCore.LivePreview.IsEnabled)
                    {
                        layer = CreateLivePreviewLayer(layer);
                    }

                    // If the layer is offset, handle it here
                    if (!layer.Transform.IsIdentity())
                    {
                        layer = CreateOffsetLayer(layer);
                    }

                    // No need to resize the surface if we're at 100% zoom
                    if (scale_factor.Ratio == 1)
                    {
                        layer.Draw(g, layer.Surface, layer.Opacity, false);
                    }
                    else
                    {
                        using (var scaled = CairoExtensions.CreateImageSurface(Cairo.Format.Argb32, dst.Width, dst.Height)) {
                            g.Save();
                            // Have to undo the translate set above
                            g.Translate(offset.X, offset.Y);
                            CopyScaled(layer.Surface, scaled, r.ToGdkRectangle());
                            layer.Draw(g, scaled, layer.Opacity, false);
                            g.Restore();
                        }
                    }
                }
            }

            // If we are at least 200% and grid is requested, draw it
            if (enable_pixel_grid && PintaCore.Actions.View.PixelGrid.Value && scale_factor.Ratio <= 0.5d)
            {
                RenderPixelGrid(dst, offset);
            }

            dst.MarkDirty();
        }
コード例 #3
0
ファイル: CanvasRenderer.cs プロジェクト: p07r0457/Pinta
        private unsafe void RenderOneToOne(Cairo.ImageSurface src, Cairo.ImageSurface dst, Gdk.Point offset, bool checker)
        {
            Gdk.Rectangle srcRect = new Gdk.Rectangle(offset, dst.GetBounds().Size);
            srcRect.Intersect(src.GetBounds());

            ColorBgra *src_ptr = (ColorBgra *)src.DataPtr;
            ColorBgra *dst_ptr = (ColorBgra *)dst.DataPtr;

            int src_width  = src.Width;
            int dst_width  = dst.Width;
            int dst_height = dst.Height;

            for (int dstRow = 0; dstRow < srcRect.Height; ++dstRow)
            {
                ColorBgra *dstRowPtr = dst.GetRowAddressUnchecked(dst_ptr, dst_width, dstRow);
                ColorBgra *srcRowPtr = src.GetPointAddressUnchecked(src_ptr, src_width, offset.X, dstRow + offset.Y);

                int dstCol    = offset.X;
                int dstColEnd = offset.X + srcRect.Width;
                int checkerY  = dstRow + offset.Y;

                while (dstCol < dstColEnd)
                {
                    // Blend it over the checkerboard background
                    if (checker)
                    {
                        int b = srcRowPtr->B;
                        int g = srcRowPtr->G;
                        int r = srcRowPtr->R;
                        int a = srcRowPtr->A;

                        int v = (((dstCol ^ checkerY) & 8) << 3) + 191;
                        a = a + (a >> 7);
                        int vmia = v * (256 - a);

                        r = ((r * a) + vmia) >> 8;
                        g = ((g * a) + vmia) >> 8;
                        b = ((b * a) + vmia) >> 8;

                        dstRowPtr->Bgra = (uint)b + ((uint)g << 8) + ((uint)r << 16) + ((uint)255 << 24);
                    }
                    else
                    {
                        *dstRowPtr = *srcRowPtr;
                    }

                    ++dstRowPtr;
                    ++srcRowPtr;
                    ++dstCol;
                }
            }
        }
コード例 #4
0
ファイル: BinaryPixelOp.cs プロジェクト: p07r0457/Pinta
        /// <summary>
        /// Provides a default implementation for performing dst = F(lhs, rhs) over some rectangle of interest.
        /// </summary>
        /// <param name="dst">The Surface to write pixels to.</param>
        /// <param name="dstOffset">The pixel offset that defines the upper-left of the rectangle-of-interest for the dst Surface.</param>
        /// <param name="lhs">The Surface to read pixels from for the lhs parameter given to the method <b>ColorBgra Apply(ColorBgra, ColorBgra)</b>b>.</param></param>
        /// <param name="lhsOffset">The pixel offset that defines the upper-left of the rectangle-of-interest for the lhs Surface.</param>
        /// <param name="rhs">The Surface to read pixels from for the rhs parameter given to the method <b>ColorBgra Apply(ColorBgra, ColorBgra)</b></param>
        /// <param name="rhsOffset">The pixel offset that defines the upper-left of the rectangle-of-interest for the rhs Surface.</param>
        /// <param name="roiSize">The size of the rectangles-of-interest for all Surfaces.</param>
        public void Apply(Cairo.ImageSurface dst, Point dstOffset,
                          Cairo.ImageSurface lhs, Point lhsOffset,
                          Cairo.ImageSurface rhs, Point rhsOffset,
                          Size roiSize)
        {
            // Bounds checking only enabled in Debug builds.
#if DEBUG
            // Create bounding rectangles for each Surface
            Rectangle dstRect = new Rectangle(dstOffset, roiSize);
            Rectangle lhsRect = new Rectangle(lhsOffset, roiSize);
            Rectangle rhsRect = new Rectangle(rhsOffset, roiSize);

            // Clip those rectangles to those Surface's bounding rectangles
            Rectangle dstClip = Rectangle.Intersect(dstRect, dst.GetBounds());
            Rectangle lhsClip = Rectangle.Intersect(lhsRect, lhs.GetBounds());
            Rectangle rhsClip = Rectangle.Intersect(rhsRect, rhs.GetBounds());

            // If any of those Rectangles actually got clipped, then throw an exception
            if (dstRect != dstClip)
            {
                throw new ArgumentOutOfRangeException("roiSize", "Destination roi out of bounds");
            }

            if (lhsRect != lhsClip)
            {
                throw new ArgumentOutOfRangeException("roiSize", "lhs roi out of bounds");
            }

            if (rhsRect != rhsClip)
            {
                throw new ArgumentOutOfRangeException("roiSize", "rhs roi out of bounds");
            }
#endif

            // Cache the width and height properties
            int width  = roiSize.Width;
            int height = roiSize.Height;

            // Do the work.
            unsafe {
                for (int row = 0; row < height; ++row)
                {
                    ColorBgra *dstPtr = dst.GetPointAddress(dstOffset.X, dstOffset.Y + row);
                    ColorBgra *lhsPtr = lhs.GetPointAddress(lhsOffset.X, lhsOffset.Y + row);
                    ColorBgra *rhsPtr = rhs.GetPointAddress(rhsOffset.X, rhsOffset.Y + row);

                    Apply(dstPtr, lhsPtr, rhsPtr, width);
                }
            }
        }
コード例 #5
0
        /// <summary>
        /// Constructs an IrregularSurface by copying the given region-of-interest from an Image.
        /// </summary>
        /// <param name="source">The Surface to copy pixels from.</param>
        /// <param name="roi">Defines the Region from which to copy pixels from the Image.</param>
        public IrregularSurface(Cairo.ImageSurface source, Region roi)
        {
            Region roiClipped = (Region)roi.Copy();

            roiClipped.Intersect(Region.Rectangle(source.GetBounds()));

            Rectangle[] rects = roiClipped.GetRectangles();
            this.placedSurfaces = new List <PlacedSurface> (rects.Length);

            foreach (Rectangle rect in rects)
            {
                this.placedSurfaces.Add(new PlacedSurface(source, rect));
            }

            this.region = roiClipped;
        }
コード例 #6
0
ファイル: LivePreviewManager.cs プロジェクト: ericksson/Pinta
		public void Start (BaseEffect effect)
		{			
			if (live_preview_enabled)
				throw new InvalidOperationException ("LivePreviewManager.Start() called while live preview is already enabled.");
			
			// Create live preview surface.
			// Start rendering.
			// Listen for changes to effectConfiguration object, and restart render if needed.

			live_preview_enabled = true;
			apply_live_preview_flag = false;
			cancel_live_preview_flag = false;
			
			layer = PintaCore.Layers.CurrentLayer;
			this.effect = effect;

			//TODO Use the current tool layer instead.
			live_preview_surface = new Cairo.ImageSurface (Cairo.Format.Argb32,
			                                  PintaCore.Workspace.ImageSize.Width,
			                                  PintaCore.Workspace.ImageSize.Height);

			// Handle selection path.
			PintaCore.Tools.Commit ();
			selection_path = (PintaCore.Layers.ShowSelection) ? PintaCore.Workspace.ActiveDocument.Selection.SelectionPath : null;
			render_bounds = (selection_path != null) ? selection_path.GetBounds () : live_preview_surface.GetBounds ();
			render_bounds = PintaCore.Workspace.ClampToImageSize (render_bounds);	

			history_item = new SimpleHistoryItem (effect.Icon, effect.Name);
			history_item.TakeSnapshotOfLayer (PintaCore.Layers.CurrentLayerIndex);	
			
			// Paint the pre-effect layer surface into into the working surface.
			using (var ctx = new Cairo.Context (live_preview_surface)) {
				layer.Draw(ctx, layer.Surface, 1);
			}
			
			if (effect.EffectData != null)
				effect.EffectData.PropertyChanged += EffectData_PropertyChanged;
			
			if (Started != null) {
				Started (this, new LivePreviewStartedEventArgs());
			}
			
			var settings = new AsyncEffectRenderer.Settings () {
				ThreadCount = PintaCore.System.RenderThreads,
				TileWidth = render_bounds.Width,
				TileHeight = 1,
				ThreadPriority = ThreadPriority.BelowNormal
			};
			
			Debug.WriteLine (DateTime.Now.ToString("HH:mm:ss:ffff") + "Start Live preview.");
			
			renderer = new Renderer (this, settings);
			renderer.Start (effect, layer.Surface, live_preview_surface, render_bounds);
			
			if (effect.IsConfigurable) {		
				if (!effect.LaunchConfiguration ()) {
					PintaCore.Chrome.MainWindowBusy = true;
					Cancel ();
				} else {
					PintaCore.Chrome.MainWindowBusy = true;
					Apply ();
				}
			} else {
				PintaCore.Chrome.MainWindowBusy = true;
				Apply ();
			}
		}
コード例 #7
0
ファイル: CanvasRenderer.cs プロジェクト: mfcallahan/Pinta
        private unsafe void CopyScaledZoomOut(Cairo.ImageSurface src, Cairo.ImageSurface dst, Rectangle roi)
        {
            // Tell Cairo we need the latest raw data
            dst.Flush();

            const int fpShift  = 12;
            const int fpFactor = (1 << fpShift);

            var source_size = src.GetBounds().Size;

            // Find destination bounds
            var dst_left   = (int)(((long)roi.X * fpFactor * (long)source_size.Width) / (long)destination_size.Width);
            var dst_top    = (int)(((long)roi.Y * fpFactor * (long)source_size.Height) / (long)destination_size.Height);
            var dst_right  = (int)(((long)(roi.X + dst.Width) * fpFactor * (long)source_size.Width) / (long)destination_size.Width);
            var dst_bottom = (int)(((long)(roi.Y + dst.Height) * fpFactor * (long)source_size.Height) / (long)destination_size.Height);
            var dx         = (dst_right - dst_left) / dst.Width;
            var dy         = (dst_bottom - dst_top) / dst.Height;

            // Cache pointers to surface raw data and sizes
            var src_ptr    = (ColorBgra *)src.DataPtr;
            var dst_ptr    = (ColorBgra *)dst.DataPtr;
            var src_width  = src.Width;
            var dst_width  = dst.Width;
            var dst_height = dst.Height;

            for (int dstRow = 0, fDstY = dst_top; dstRow < dst_height && fDstY < dst_bottom; ++dstRow, fDstY += dy)
            {
                var srcY1 = fDstY >> fpShift;                                            // y
                var srcY2 = (fDstY + (dy >> 2)) >> fpShift;                              // y + 0.25
                var srcY3 = (fDstY + (dy >> 1)) >> fpShift;                              // y + 0.50
                var srcY4 = (fDstY + (dy >> 1) + (dy >> 2)) >> fpShift;                  // y + 0.75

                var src1   = src.GetRowAddressUnchecked(src_ptr, src_width, srcY1);
                var src2   = src.GetRowAddressUnchecked(src_ptr, src_width, srcY2);
                var src3   = src.GetRowAddressUnchecked(src_ptr, src_width, srcY3);
                var src4   = src.GetRowAddressUnchecked(src_ptr, src_width, srcY4);
                var dstPtr = dst.GetRowAddressUnchecked(dst_ptr, dst_width, dstRow);

                var checkerY    = dstRow + roi.Y;
                var checkerX    = roi.X;
                var maxCheckerX = checkerX + dst.Width;

                for (var fDstX = dst_left; checkerX < maxCheckerX && fDstX < dst_right; ++checkerX, fDstX += dx)
                {
                    var srcX1 = (fDstX + (dx >> 2)) >> fpShift;                                 // x + 0.25
                    var srcX2 = (fDstX + (dx >> 1) + (dx >> 2)) >> fpShift;                     // x + 0.75
                    var srcX3 = fDstX >> fpShift;                                               // x
                    var srcX4 = (fDstX + (dx >> 1)) >> fpShift;                                 // x + 0.50

                    var p1 = src1 + srcX1;
                    var p2 = src2 + srcX2;
                    var p3 = src3 + srcX3;
                    var p4 = src4 + srcX4;

                    var r = (2 + p1->R + p2->R + p3->R + p4->R) >> 2;
                    var g = (2 + p1->G + p2->G + p3->G + p4->G) >> 2;
                    var b = (2 + p1->B + p2->B + p3->B + p4->B) >> 2;
                    var a = (2 + p1->A + p2->A + p3->A + p4->A) >> 2;

                    // Copy color to destination
                    *dstPtr++ = ColorBgra.FromUInt32((uint)b + ((uint)g << 8) + ((uint)r << 16) + ((uint)a << 24));
                }
            }

            // Tell Cairo we changed the raw data
            dst.MarkDirty();
        }
コード例 #8
0
        private void HandlePintaCoreActionsImageAutoCropActivated(object sender, EventArgs e)
        {
            Document doc = PintaCore.Workspace.ActiveDocument;

            PintaCore.Tools.Commit();

            Cairo.ImageSurface image = doc.CurrentUserLayer.Surface;
            Gdk.Rectangle      rect  = image.GetBounds();

            Cairo.Color borderColor = image.GetPixel(0, 0);
            bool        cropSide    = true;
            int         depth       = -1;

            //From the top down
            while (cropSide)
            {
                depth++;
                for (int i = 0; i < image.Width; i++)
                {
                    if (!borderColor.Equals(image.GetPixel(i, depth)))
                    {
                        cropSide = false;
                        break;
                    }
                }
                //Check if the image is blank/mono-coloured, only need to do it on this scan
                if (depth == image.Height)
                {
                    return;
                }
            }

            rect = new Gdk.Rectangle(rect.X, rect.Y + depth, rect.Width, rect.Height - depth);

            depth    = image.Height;
            cropSide = true;
            //From the bottom up
            while (cropSide)
            {
                depth--;
                for (int i = 0; i < image.Width; i++)
                {
                    if (!borderColor.Equals(image.GetPixel(i, depth)))
                    {
                        cropSide = false;
                        break;
                    }
                }
            }

            rect = new Gdk.Rectangle(rect.X, rect.Y, rect.Width, depth - rect.Y);

            depth    = 0;
            cropSide = true;
            //From left to right
            while (cropSide)
            {
                depth++;
                for (int i = 0; i < image.Height; i++)
                {
                    if (!borderColor.Equals(image.GetPixel(depth, i)))
                    {
                        cropSide = false;
                        break;
                    }
                }
            }

            rect = new Gdk.Rectangle(rect.X + depth, rect.Y, rect.Width - depth, rect.Height);

            depth    = image.Width;
            cropSide = true;
            //From right to left
            while (cropSide)
            {
                depth--;
                for (int i = 0; i < image.Height; i++)
                {
                    if (!borderColor.Equals(image.GetPixel(depth, i)))
                    {
                        cropSide = false;
                        break;
                    }
                }
            }

            rect = new Gdk.Rectangle(rect.X, rect.Y, depth - rect.X, rect.Height);

            CropImageToRectangle(doc, rect);
        }
コード例 #9
0
ファイル: CanvasRenderer.cs プロジェクト: casey0102/Pinta
        private unsafe void RenderOneToOne(List <Layer> layers, Cairo.ImageSurface dst, Gdk.Point offset)
        {
            // The first layer should be blended with the transparent checkerboard
            var checker = true;
            CheckerBoardOperation checker_op = null;

            for (int i = 0; i < layers.Count; i++)
            {
                var layer = layers[i];

                // If we're in LivePreview, substitute current layer with the preview layer
                if (layer == PintaCore.Layers.CurrentLayer && PintaCore.LivePreview.IsEnabled)
                {
                    layer = CreateLivePreviewLayer(layer);
                }

                // If the layer is offset, handle it here
                if (!layer.Offset.IsEmpty())
                {
                    layer = CreateOffsetLayer(layer);
                }

                var src = layer.Surface;

                // Get the blend mode for this layer and opacity
                var blend_op = UserBlendOps.GetBlendOp(layer.BlendMode, layer.Opacity);

                if (checker)
                {
                    checker_op = new CheckerBoardOperation(layer.Opacity);
                }

                // Figure out where our source and destination intersect
                var srcRect = new Gdk.Rectangle(offset, dst.GetBounds().Size);
                srcRect.Intersect(src.GetBounds());

                // Get pointers to our surfaces
                var src_ptr = (ColorBgra *)src.DataPtr;
                var dst_ptr = (ColorBgra *)dst.DataPtr;

                // Cache widths
                int src_width = src.Width;
                int dst_width = dst.Width;

                for (int dstRow = 0; dstRow < srcRect.Height; ++dstRow)
                {
                    ColorBgra *dstRowPtr = dst.GetRowAddressUnchecked(dst_ptr, dst_width, dstRow);
                    ColorBgra *srcRowPtr = src.GetPointAddressUnchecked(src_ptr, src_width, offset.X, dstRow + offset.Y);

                    int dstCol    = offset.X;
                    int dstColEnd = offset.X + srcRect.Width;
                    int checkerY  = dstRow + offset.Y;

                    while (dstCol < dstColEnd)
                    {
                        // Blend it over the checkerboard background
                        if (checker)
                        {
                            *dstRowPtr = checker_op.Apply(*srcRowPtr, dstCol, checkerY);
                        }
                        else
                        {
                            *dstRowPtr = blend_op.Apply(*dstRowPtr, *srcRowPtr);
                        }

                        ++dstRowPtr;
                        ++srcRowPtr;
                        ++dstCol;
                    }
                }

                // Only checker the first layer
                checker = false;
            }
        }
コード例 #10
0
ファイル: LivePreviewManager.cs プロジェクト: xiexin36/Pinta
        public void Start(BaseEffect effect)
        {
            if (live_preview_enabled)
            {
                throw new InvalidOperationException("LivePreviewManager.Start() called while live preview is already enabled.");
            }

            // Create live preview surface.
            // Start rendering.
            // Listen for changes to effectConfiguration object, and restart render if needed.

            live_preview_enabled     = true;
            apply_live_preview_flag  = false;
            cancel_live_preview_flag = false;

            layer       = PintaCore.Layers.CurrentLayer;
            this.effect = effect;

            //TODO Use the current tool layer instead.
            live_preview_surface = new Cairo.ImageSurface(Cairo.Format.Argb32,
                                                          PintaCore.Workspace.ImageSize.Width,
                                                          PintaCore.Workspace.ImageSize.Height);

            // Handle selection path.
            PintaCore.Tools.Commit();
            selection_path = (PintaCore.Layers.ShowSelection) ? PintaCore.Workspace.ActiveDocument.Selection.SelectionPath : null;
            render_bounds  = (selection_path != null) ? selection_path.GetBounds() : live_preview_surface.GetBounds();
            render_bounds  = PintaCore.Workspace.ClampToImageSize(render_bounds);

            history_item = new SimpleHistoryItem(effect.Icon, effect.Name);
            history_item.TakeSnapshotOfLayer(PintaCore.Layers.CurrentLayerIndex);

            // Paint the pre-effect layer surface into into the working surface.
            using (var ctx = new Cairo.Context(live_preview_surface)) {
                layer.Draw(ctx, layer.Surface, 1);
            }

            if (effect.EffectData != null)
            {
                effect.EffectData.PropertyChanged += EffectData_PropertyChanged;
            }

            if (Started != null)
            {
                Started(this, new LivePreviewStartedEventArgs());
            }

            var settings = new AsyncEffectRenderer.Settings()
            {
                ThreadCount    = PintaCore.System.RenderThreads,
                TileWidth      = render_bounds.Width,
                TileHeight     = 1,
                ThreadPriority = ThreadPriority.BelowNormal
            };

            Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss:ffff") + "Start Live preview.");

            renderer = new Renderer(this, settings);
            renderer.Start(effect, layer.Surface, live_preview_surface, render_bounds);

            if (effect.IsConfigurable)
            {
                if (!effect.LaunchConfiguration())
                {
                    PintaCore.Chrome.MainWindowBusy = true;
                    Cancel();
                }
                else
                {
                    PintaCore.Chrome.MainWindowBusy = true;
                    Apply();
                }
            }
            else
            {
                PintaCore.Chrome.MainWindowBusy = true;
                Apply();
            }
        }
コード例 #11
0
ファイル: CanvasRenderer.cs プロジェクト: p07r0457/Pinta
        private unsafe void RenderZoomOut(Cairo.ImageSurface src, Cairo.ImageSurface dst, Gdk.Point offset, Gdk.Size destinationSize, bool checker)
        {
            const int fpShift  = 12;
            const int fpFactor = (1 << fpShift);

            Gdk.Size sourceSize     = src.GetBounds().Size;
            long     fDstLeftLong   = ((long)offset.X * fpFactor * (long)sourceSize.Width) / (long)destinationSize.Width;
            long     fDstTopLong    = ((long)offset.Y * fpFactor * (long)sourceSize.Height) / (long)destinationSize.Height;
            long     fDstRightLong  = ((long)(offset.X + dst.Width) * fpFactor * (long)sourceSize.Width) / (long)destinationSize.Width;
            long     fDstBottomLong = ((long)(offset.Y + dst.Height) * fpFactor * (long)sourceSize.Height) / (long)destinationSize.Height;
            int      fDstLeft       = (int)fDstLeftLong;
            int      fDstTop        = (int)fDstTopLong;
            int      fDstRight      = (int)fDstRightLong;
            int      fDstBottom     = (int)fDstBottomLong;
            int      dx             = (fDstRight - fDstLeft) / dst.Width;
            int      dy             = (fDstBottom - fDstTop) / dst.Height;

            ColorBgra *src_ptr   = (ColorBgra *)src.DataPtr;
            ColorBgra *dst_ptr   = (ColorBgra *)dst.DataPtr;
            int        src_width = src.Width;
            int        dst_width = dst.Width;

            for (int dstRow = 0, fDstY = fDstTop; dstRow < dst.Height && fDstY < fDstBottom; ++dstRow, fDstY += dy)
            {
                int srcY1 = fDstY >> fpShift;                                            // y
                int srcY2 = (fDstY + (dy >> 2)) >> fpShift;                              // y + 0.25
                int srcY3 = (fDstY + (dy >> 1)) >> fpShift;                              // y + 0.50
                int srcY4 = (fDstY + (dy >> 1) + (dy >> 2)) >> fpShift;                  // y + 0.75

                ColorBgra *src1        = src.GetRowAddressUnchecked(src_ptr, src_width, srcY1);
                ColorBgra *src2        = src.GetRowAddressUnchecked(src_ptr, src_width, srcY2);
                ColorBgra *src3        = src.GetRowAddressUnchecked(src_ptr, src_width, srcY3);
                ColorBgra *src4        = src.GetRowAddressUnchecked(src_ptr, src_width, srcY4);
                ColorBgra *dstPtr      = dst.GetRowAddressUnchecked(dst_ptr, dst_width, dstRow);
                int        checkerY    = dstRow + offset.Y;
                int        checkerX    = offset.X;
                int        maxCheckerX = checkerX + dst.Width;

                for (int fDstX = fDstLeft; checkerX < maxCheckerX && fDstX < fDstRight; ++checkerX, fDstX += dx)
                {
                    int        srcX1 = (fDstX + (dx >> 2)) >> fpShift;                          // x + 0.25
                    int        srcX2 = (fDstX + (dx >> 1) + (dx >> 2)) >> fpShift;              // x + 0.75
                    int        srcX3 = fDstX >> fpShift;                                        // x
                    int        srcX4 = (fDstX + (dx >> 1)) >> fpShift;                          // x + 0.50
                    ColorBgra *p1    = src1 + srcX1;
                    ColorBgra *p2    = src2 + srcX2;
                    ColorBgra *p3    = src3 + srcX3;
                    ColorBgra *p4    = src4 + srcX4;

                    int r = (2 + p1->R + p2->R + p3->R + p4->R) >> 2;
                    int g = (2 + p1->G + p2->G + p3->G + p4->G) >> 2;
                    int b = (2 + p1->B + p2->B + p3->B + p4->B) >> 2;
                    int a = (2 + p1->A + p2->A + p3->A + p4->A) >> 2;

                    if (checker)
                    {
                        // Blend it over the checkerboard background
                        int v = ((checkerX ^ checkerY) & 8) * 8 + 191;
                        a = a + (a >> 7);
                        int vmia = v * (256 - a);

                        r = ((r * a) + vmia) >> 8;
                        g = ((g * a) + vmia) >> 8;
                        b = ((b * a) + vmia) >> 8;

                        dstPtr->Bgra = (uint)b + ((uint)g << 8) + ((uint)r << 16) + 0xff000000;
                    }
                    else
                    {
                        dstPtr->Bgra = (uint)b + ((uint)g << 8) + ((uint)r << 16) + ((uint)a << 24);
                    }

                    ++dstPtr;
                }
            }
        }