コード例 #1
0
 public AlphaMaskAdaptor(IImageReaderWriter image, IAlphaMask mask)
     : base(image)
 {
     linkedImage = image;
     m_mask = mask;
     m_spans = new byte[256];
 }
 void Attach(IImageReaderWriter imgRW)
 {
     this.imgRW = imgRW;
     m_buffer = imgRW.GetBuffer();
     m_src_width = imgRW.Width;
     m_src_height = imgRW.Height;
     m_distanceBetweenPixelsInclusive = imgRW.BytesBetweenPixelsInclusive;
 }
コード例 #3
0
 //--------------------------------------------------------------------
 public ImgSpanGenRGB_BilinearClip(IImageReaderWriter src,
                                   Drawing.Color back_color,
                                   ISpanInterpolator inter)
     : base(inter)
 {
     m_bgcolor = back_color;
     srcRW = (ImageReaderWriterBase)src;
 }
コード例 #4
0
 public ImgSpanGenGray_NNStepXby1(IImageReaderWriter src, ISpanInterpolator spanInterpolator)
     : base(spanInterpolator)
 {
     srcRW = (ImageReaderWriterBase)src;
     if (srcRW.BitDepth != 8)
     {
         throw new NotSupportedException("The source is expected to be 32 bit.");
     }
 }
コード例 #5
0
ファイル: ChildImage.cs プロジェクト: prepare/HTML-Renderer
 public ChildImage(IImageReaderWriter image,
     IPixelBlender blender,
     int distanceBetweenPixelsInclusive,
     int bufferOffset,
     int bitsPerPixel)
 {
     SetRecieveBlender(blender);
     Attach(image, blender, distanceBetweenPixelsInclusive, bufferOffset, bitsPerPixel);
 }
コード例 #6
0
 public ImgSpanGenRGBA_BilinearClip(IImageReaderWriter src,
     Drawing.Color back_color,
     ISpanInterpolator inter)
     : base(inter)
 {
     m_bgcolor = back_color;
     srcRW = (ImageReaderWriterBase)src;
     bytesBetweenPixelInclusive = srcRW.BytesBetweenPixelsInclusive;
 }
コード例 #7
0
 //--------------------------------------------------------------------
 public FilterRGBAImageSpanGen(IImageReaderWriter src,
                     ISpanInterpolator inter,
                     ImageFilterLookUpTable filter) :
     base(src, inter, filter)
 {
     if (src.GetRecieveBlender().NumPixelBits != 32)
     {
         throw new System.FormatException("You have to use a rgba blender with span_image_resample_rgba");
     }
 }
コード例 #8
0
 public FilterImageSpanGenerator(IImageReaderWriter src,
                     ISpanInterpolator inter,
                     ImageFilterLookUpTable filterLookup)
     : base(inter)
 {
     this.imageBufferAccessor = new ImageBufferAccessor(src);
     m_scale_limit = 20;
     //m_blur_x = ((int)img_subpix_scale.SCALE);
     //m_blur_y = ((int)img_subpix_scale.SCALE);
     this.filterLookup = filterLookup;
 }
コード例 #9
0
ファイル: ChildImage.cs プロジェクト: prepare/HTML-Renderer
 int bufferOffset; // the beggining of the image in this buffer 
 public ChildImage(IImageReaderWriter image,
     int bufferOffsetToFirstPixel,
     int width,
     int height)
 {
     SetRecieveBlender(image.GetRecieveBlender());
     AttachBuffer(image.GetBuffer(),
        bufferOffsetToFirstPixel,
         width,
         height,
         image.Stride,
         image.BitDepth,
         image.BytesBetweenPixelsInclusive);
 }
コード例 #10
0
ファイル: ImageHelper.cs プロジェクト: prepare/HTML-Renderer
        /// <summary>
        /// This will create a new ImageBuffer that references the same memory as the image that you took the sub image from.
        /// It will modify the original main image when you draw to it.
        /// </summary>
        /// <param name="parentImage"></param>
        /// <param name="childImageBounds"></param>
        /// <returns></returns>
        public static ChildImage CreateChildImage(IImageReaderWriter parentImage, RectInt childImageBounds)
        {
            if (childImageBounds.Left < 0 || childImageBounds.Bottom < 0 || childImageBounds.Right > parentImage.Width || childImageBounds.Top > parentImage.Height
                || childImageBounds.Left >= childImageBounds.Right || childImageBounds.Bottom >= childImageBounds.Top)
            {
                throw new ArgumentException("The subImageBounds must be on the image and valid.");
            }

            int left = Math.Max(0, childImageBounds.Left);
            int bottom = Math.Max(0, childImageBounds.Bottom);
            int width = Math.Min(parentImage.Width - left, childImageBounds.Width);
            int height = Math.Min(parentImage.Height - bottom, childImageBounds.Height);
            int bufferOffsetToFirstPixel = parentImage.GetBufferOffsetXY(left, bottom);
            return new ChildImage(parentImage, bufferOffsetToFirstPixel, width, height);
        }
コード例 #11
0
 public static void RenderSolidAllPaths(this ScanlineRasToDestBitmapRenderer sclineRasToBmp,
      IImageReaderWriter destImage,
       ScanlineRasterizer sclineRas,
       Scanline scline,
       VertexStore vxs,
       Drawing.Color[] colors,
       int[] path_id,
       int num_paths)
 {
     for (int i = 0; i < num_paths; ++i)
     {
         sclineRas.Reset();
         sclineRas.AddPath(new VertexStoreSnap(vxs, path_id[i]));
         sclineRasToBmp.RenderWithColor(destImage, sclineRas, scline, colors[i]);
     }
 }
コード例 #12
0
        public void RenderWithColor(IImageReaderWriter dest,
                ScanlineRasterizer sclineRas,
                Scanline scline,
                Color color)
        {
            if (!sclineRas.RewindScanlines()) { return; } //early exit
            //----------------------------------------------- 
            scline.ResetSpans(sclineRas.MinX, sclineRas.MaxX);
            switch (this.ScanlineRenderMode)
            {
                default:
                    {
                        //prev mode  
                        //this mode 
                        while (sclineRas.SweepScanline(scline))
                        {
                            //render solid single scanline
                            int y = scline.Y;
                            int num_spans = scline.SpanCount;
                            byte[] covers = scline.GetCovers();
                            //render each span in the scanline
                            for (int i = 1; i <= num_spans; ++i)
                            {
                                ScanlineSpan span = scline.GetSpan(i);
                                if (span.len > 0)
                                {
                                    //positive len 
                                    dest.BlendSolidHSpan(span.x, y, span.len, color, covers, span.cover_index);
                                }
                                else
                                {
                                    //fill the line, same coverage area
                                    int x = span.x;
                                    int x2 = (x - span.len - 1);
                                    dest.BlendHL(x, y, x2, color, covers[span.cover_index]);
                                }
                            }
                        }
                    }
                    break;
                case Agg.ScanlineRenderMode.SubPixelRendering:
                    {
#if DEBUG
                        int dbugMinScanlineCount = 0;
#endif

                        while (sclineRas.SweepScanline(scline))
                        {
                            SubPixRender(dest, scline, color);
#if DEBUG
                            dbugMinScanlineCount++;
#endif
                        }
                    }
                    break;
                case Agg.ScanlineRenderMode.Custom:
                    {
                        while (sclineRas.SweepScanline(scline))
                        {
                            CustomRenderSingleScanLine(dest, scline, color);
                        }
                    }
                    break;
            }
        }
コード例 #13
0
        public void RenderWithSpan(IImageReaderWriter dest,
                ScanlineRasterizer sclineRas,
                Scanline scline,
                ISpanGenerator spanGenerator)
        {
            if (!sclineRas.RewindScanlines()) { return; } //early exit
            //-----------------------------------------------

            scline.ResetSpans(sclineRas.MinX, sclineRas.MaxX);
            spanGenerator.Prepare();
            if (dest.Stride / 4 > (tempSpanColors.AllocatedSize))
            {
                //if not enough -> alloc more
                tempSpanColors.Clear(dest.Stride / 4);
            }

            Color[] colorArray = tempSpanColors.Array;
            while (sclineRas.SweepScanline(scline))
            {
                //render single scanline 
                int y = scline.Y;
                int num_spans = scline.SpanCount;
                byte[] covers = scline.GetCovers();
                for (int i = 1; i <= num_spans; ++i)
                {
                    ScanlineSpan span = scline.GetSpan(i);
                    int x = span.x;
                    int span_len = span.len;
                    bool firstCoverForAll = false;
                    if (span_len < 0)
                    {
                        span_len = -span_len;
                        firstCoverForAll = true;
                    } //make absolute value

                    //1. generate colors -> store in colorArray
                    spanGenerator.GenerateColors(colorArray, 0, x, y, span_len);
                    //2. blend color in colorArray to destination image
                    dest.BlendColorHSpan(x, y, span_len,
                        colorArray, 0,
                        covers, span.cover_index,
                        firstCoverForAll);
                }
            }
        }
コード例 #14
0
ファイル: AlphaMask.cs プロジェクト: prepare/HTML-Renderer
 public void attach(IImageReaderWriter rbuf) { m_rbuf = rbuf; }
コード例 #15
0
ファイル: ChildImage.cs プロジェクト: prepare/HTML-Renderer
 public ChildImage(IImageReaderWriter image, IPixelBlender blender)
 {
     Attach(image, blender, image.BytesBetweenPixelsInclusive, 0, image.BitDepth);
 }
コード例 #16
0
 public void BlurY(IImageReaderWriter img, double radius)
 {
     FormatTransposer img2 = new FormatTransposer(img);
     BlurX(img2, radius);
 }
コード例 #17
0
ファイル: AlphaMask.cs プロジェクト: prepare/HTML-Renderer
        //public static readonly int cover_shift = 8;
        //public static readonly int cover_none = 0;
        //public static readonly int cover_full = 255;

        public AlphaMaskByteUnclipped(IImageReaderWriter rbuf, uint Step, uint Offset)
        {
            m_Step = Step;
            m_Offset = Offset;
            m_rbuf = rbuf;
        }
コード例 #18
0
ファイル: ChildImage.cs プロジェクト: prepare/HTML-Renderer
 void Attach(IImageReaderWriter sourceImage,
   IPixelBlender recieveBlender,
   int distanceBetweenPixelsInclusive,
   int bufferOffset,
   int bitsPerPixel)
 {
     SetDimmensionAndFormat(sourceImage.Width,
         sourceImage.Height,
         sourceImage.Stride,
         bitsPerPixel,
         distanceBetweenPixelsInclusive);
     int offset = sourceImage.GetBufferOffsetXY(0, 0);
     byte[] buffer = sourceImage.GetBuffer();
     SetBuffer(buffer, offset + bufferOffset);
     SetRecieveBlender(recieveBlender);
 }
コード例 #19
0
ファイル: Transposer.cs プロジェクト: prepare/HTML-Renderer
 public FormatTransposer(IImageReaderWriter pixelFormat)
     : base(pixelFormat)
 {
 }
コード例 #20
0
        public void Render(IImageReaderWriter source, AffinePlan[] affinePlans)
        {
            VertexStore v1 = GetFreeVxs();
            Affine destRectTransform = BuildImageBoundsPath(source.Width, source.Height, v1, affinePlans);
            // We invert it because it is the transform to make the image go to the same position as the polygon. LBB [2/24/2004]
            Affine sourceRectTransform = destRectTransform.CreateInvert();
            var imgSpanGen = new ImgSpanGenRGBA_BilinearClip(
                source,
                Drawing.Color.Black,
                new SpanInterpolatorLinear(sourceRectTransform));

            var v2 = destRectTransform.TransformToVxs(v1, GetFreeVxs());
            Render(v2, imgSpanGen);
            //
            ReleaseVxs(ref v1);
            ReleaseVxs(ref v2);
        }
コード例 #21
0
        public void Render(IImageReaderWriter source, double destX, double destY)
        {
            int inScaleX = 1;
            int inScaleY = 1;
            int angleRadians = 0;
            // exit early if the dest and source bounds don't touch.
            // TODO: <BUG> make this do rotation and scalling
            RectInt sourceBounds = source.GetBounds();
            RectInt destBounds = this.destImageReaderWriter.GetBounds();
            sourceBounds.Offset((int)destX, (int)destY);
            if (!RectInt.DoIntersect(sourceBounds, destBounds))
            {
                //if (inScaleX != 1 || inScaleY != 1 || angleRadians != 0)
                //{
                //    throw new NotImplementedException();
                //}
                return;
            }

            double scaleX = inScaleX;
            double scaleY = inScaleY;
            Affine graphicsTransform = this.CurrentTransformMatrix;
            if (!graphicsTransform.IsIdentity())
            {
                if (scaleX != 1 || scaleY != 1 || angleRadians != 0)
                {
                    throw new NotImplementedException();
                }
                graphicsTransform.Transform(ref destX, ref destY);
            }


#if false // this is an optomization that eliminates the drawing of images that have their alpha set to all 0 (happens with generated images like explosions).
	        MaxAlphaFrameProperty maxAlphaFrameProperty = MaxAlphaFrameProperty::GetMaxAlphaFrameProperty(source);

	        if((maxAlphaFrameProperty.GetMaxAlpha() * color.A_Byte) / 256 <= ALPHA_CHANNEL_BITS_DIVISOR)
	        {
		        m_OutFinalBlitBounds.SetRect(0,0,0,0);
	        }
#endif
            bool isScale = (scaleX != 1 || scaleY != 1);
            bool isRotated = true;
            if (Math.Abs(angleRadians) < (0.1 * MathHelper.Tau / 360))
            {
                isRotated = false;
                angleRadians = 0;
            }

            //bool IsMipped = false;
            //double ox, oy;
            //source.GetOriginOffset(out ox, out oy);

            bool canUseMipMaps = isScale;
            if (scaleX > 0.5 || scaleY > 0.5)
            {
                canUseMipMaps = false;
            }

            bool needSourceResampling = isScale || isRotated || destX != (int)destX || destY != (int)destY;
            VertexStore imgBoundsPath = GetFreeVxs();
            // this is the fast drawing path
            if (needSourceResampling)
            {
#if false // if the scalling is small enough the results can be improved by using mip maps
	        if(CanUseMipMaps)
	        {
		        CMipMapFrameProperty* pMipMapFrameProperty = CMipMapFrameProperty::GetMipMapFrameProperty(source);
		        double OldScaleX = scaleX;
		        double OldScaleY = scaleY;
		        const CFrameInterface* pMippedFrame = pMipMapFrameProperty.GetMipMapFrame(ref scaleX, ref scaleY);
		        if(pMippedFrame != source)
		        {
			        IsMipped = true;
			        source = pMippedFrame;
			        sourceOriginOffsetX *= (OldScaleX / scaleX);
			        sourceOriginOffsetY *= (OldScaleY / scaleY);
		        }

			    HotspotOffsetX *= (inScaleX / scaleX);
			    HotspotOffsetY *= (inScaleY / scaleY);
	        }
#endif


                Affine destRectTransform = BuildImageBoundsPath(source.Width, source.Height, imgBoundsPath,
                    destX, destY, ox, oy, scaleX, scaleY, angleRadians);
                // We invert it because it is the transform to make the image go to the same position as the polygon. LBB [2/24/2004]
                Affine sourceRectTransform = destRectTransform.CreateInvert();
                var imgSpanGen = new ImgSpanGenRGBA_BilinearClip(
                    source,
                    Drawing.Color.Black,
                    new SpanInterpolatorLinear(sourceRectTransform));

                var v1 = destRectTransform.TransformToVxs(imgBoundsPath, GetFreeVxs());
                Render(v1, imgSpanGen);
                ReleaseVxs(ref v1);
#if false // this is some debug you can enable to visualize the dest bounding box
		        LineFloat(BoundingRect.left, BoundingRect.top, BoundingRect.right, BoundingRect.top, WHITE);
		        LineFloat(BoundingRect.right, BoundingRect.top, BoundingRect.right, BoundingRect.bottom, WHITE);
		        LineFloat(BoundingRect.right, BoundingRect.bottom, BoundingRect.left, BoundingRect.bottom, WHITE);
		        LineFloat(BoundingRect.left, BoundingRect.bottom, BoundingRect.left, BoundingRect.top, WHITE);
#endif
            }
            else // TODO: this can be even faster if we do not use an intermediat buffer
            {
                Affine destRectTransform = BuildImageBoundsPath(source.Width, source.Height,
                    imgBoundsPath,
                    destX, destY);
                // We invert it because it is the transform to make the image go to the same position as the polygon. LBB [2/24/2004]
                Affine sourceRectTransform = destRectTransform.CreateInvert();
                var interpolator = new SpanInterpolatorLinear(sourceRectTransform);
                ImgSpanGen imgSpanGen = null;
                switch (source.BitDepth)
                {
                    case 32:
                        imgSpanGen = new ImgSpanGenRGBA_NN_StepXBy1(source, interpolator);
                        break;
                    case 24:
                        imgSpanGen = new ImgSpanGenRGB_NNStepXby1(source, interpolator);
                        break;
                    case 8:
                        imgSpanGen = new ImgSpanGenGray_NNStepXby1(source, interpolator);
                        break;
                    default:
                        throw new NotImplementedException();
                }

                var v1 = destRectTransform.TransformToVxs(imgBoundsPath, GetFreeVxs());
                Render(v1, imgSpanGen);
                ReleaseVxs(ref v1);
                unchecked { destImageChanged++; };
            }
            ReleaseVxs(ref imgBoundsPath);
        }
コード例 #22
0
 public void AttachImage(IImageReaderWriter image)
 {
     linkedImage = image;
 }
コード例 #23
0
ファイル: ProxyImage.cs プロジェクト: prepare/HTML-Renderer
 public ProxyImage(IImageReaderWriter linkedImage)
 {
     this.linkedImage = linkedImage;
 }
コード例 #24
0
 public void Blur(IImageReaderWriter img, double radius)
 {
     BlurX(img, radius);
     BlurY(img, radius);
 }
コード例 #25
0
 protected virtual void CustomRenderSingleScanLine(
     IImageReaderWriter dest,
     Scanline scline,
     Color color)
 {
     //implement
 }
コード例 #26
0
ファイル: ChildImage.cs プロジェクト: prepare/HTML-Renderer
 public ChildImage(IImageReaderWriter image, IPixelBlender blender, int x1, int y1, int x2, int y2)
 {
     SetRecieveBlender(blender);
     Attach(image, x1, y1, x2, y2);
 }
コード例 #27
0
        void SubPixRender(IImageReaderWriter dest, Scanline scanline, Color color)
        {
            byte[] covers = scanline.GetCovers();
            int num_spans = scanline.SpanCount;
            int y = scanline.Y;
            byte[] buffer = dest.GetBuffer();
            IPixelBlender blender = dest.GetRecieveBlender();
            int last_x = int.MinValue;
            int bufferOffset = 0;
            //------------------------------------------
            Color bgColor = Color.White;
            float cb_R = bgColor.R / 255f;
            float cb_G = bgColor.G / 255f;
            float cb_B = bgColor.B / 255f;
            float cf_R = color.R / 255f;
            float cf_G = color.G / 255f;
            float cf_B = color.B / 255f;
            //------------------------------------------
            int prevCover = -1;
            for (int i = 1; i <= num_spans; ++i)
            {
                //render span by span  
                ScanlineSpan span = scanline.GetSpan(i);
                if (span.x != last_x + 1)
                {
                    bufferOffset = dest.GetBufferOffsetXY(span.x, y);
                }

                last_x = span.x;
                int num_pix = span.len;
                if (num_pix < 0)
                {
                    //special encode***
                    num_pix = -num_pix; //make it positive value
                    last_x += (num_pix - 1);
                    //long span with coverage
                    int coverageValue = covers[span.cover_index];
                    //------------------------------------------- 
                    if (coverageValue >= 255)
                    {
                        //100% cover
                        int a = ((coverageValue + 1) * color.Alpha0To255) >> 8;
                        Color todrawColor = Color.FromArgb(a, Color.FromArgb(color.R, color.G, color.B));
                        while (num_pix > 0)
                        {
                            blender.BlendPixel(buffer, bufferOffset, todrawColor);
                            bufferOffset += 4; //1 pixel 4 bytes
                            --num_pix;
                        }
                    }
                    else
                    {
                        int a = ((coverageValue + 1) * color.Alpha0To255) >> 8;
                        Color newc = Color.FromArgb(color.R, color.G, color.B);
                        Color todrawColor = Color.FromArgb(a, newc);
                        while (num_pix > 0)
                        {
                            blender.BlendPixel(buffer, bufferOffset, todrawColor);
                            bufferOffset += 4; //1 pixel 4 bytes
                            --num_pix;
                        }
                    }
                    prevCover = coverageValue;
                }
                else
                {
                    int coverIndex = span.cover_index;
                    last_x += (num_pix - 1);
                    while (num_pix > 0)
                    {
                        int coverageValue = covers[coverIndex++];
                        if (coverageValue >= 255)
                        {
                            //100% cover
                            Color newc = Color.FromArgb(color.R, color.G, color.B);
                            int a = ((coverageValue + 1) * color.Alpha0To255) >> 8;
                            blender.BlendPixel(buffer, bufferOffset, Color.FromArgb(a, newc));
                            prevCover = coverageValue;
                        }
                        else
                        {
                            //check direction : 

                            bool isUpHill = coverageValue >= prevCover;
                            //if (isUpHill != ((coverageValue % 2) > 0))
                            //{
                            //}
                            //---------------------------- 
                            byte c_r = 0, c_g = 0, c_b = 0;
                            //----------------------------
                            //assume lcd screen is RGB
                            float subpix_percent = ((float)(coverageValue) / 256f);
                            if (coverageValue < cover_1_3)
                            {
                                //assume LCD color arrangement is BGR                            
                                if (isUpHill)
                                {
                                    c_r = bgColor.R;
                                    c_g = bgColor.G;
                                    c_b = (byte)(mix(cb_B, cf_B, subpix_percent) * 255);
                                }
                                else
                                {
                                    c_r = (byte)(mix(cb_R, cf_R, subpix_percent) * 255);
                                    c_g = bgColor.G;
                                    c_b = bgColor.B;
                                }

                                int a = ((coverageValue + 1) * color.Alpha0To255) >> 8;
                                blender.BlendPixel(buffer, bufferOffset, Color.FromArgb(a, Color.FromArgb(c_r, c_g, c_b)));
                            }
                            else if (coverageValue < cover_2_3)
                            {
                                if (isUpHill)
                                {
                                    c_r = bgColor.R;
                                    c_g = (byte)(mix(cb_G, cf_G, subpix_percent) * 255);
                                    c_b = (byte)(mix(cb_B, cf_B, 1) * 255);
                                }
                                else
                                {
                                    c_r = (byte)(mix(cb_R, cf_R, 1) * 255);
                                    c_g = (byte)(mix(cb_G, cf_G, subpix_percent) * 255);
                                    c_b = bgColor.B;
                                }

                                int a = ((coverageValue + 1) * color.Alpha0To255) >> 8;
                                blender.BlendPixel(buffer, bufferOffset, Color.FromArgb(a, Color.FromArgb(c_r, c_g, c_b)));
                            }
                            else
                            {
                                //cover > 2/3 but not full 
                                if (isUpHill)
                                {
                                    c_r = (byte)(mix(cb_R, cf_R, subpix_percent) * 255);
                                    c_g = (byte)(mix(cb_G, cf_G, 1) * 255);
                                    c_b = (byte)(mix(cb_B, cf_B, 1) * 255);
                                }
                                else
                                {
                                    c_r = (byte)(mix(cb_R, cf_R, 1) * 255);
                                    c_g = (byte)(mix(cb_G, cf_G, 1) * 255);
                                    c_b = (byte)(mix(cb_B, cf_B, subpix_percent) * 255);
                                }

                                int a = ((coverageValue + 1) * color.Alpha0To255) >> 8;
                                blender.BlendPixel(buffer, bufferOffset, Color.FromArgb(a, Color.FromArgb(c_r, c_g, c_b)));
                            }
                        }
                        bufferOffset += 4; //1 pixel 4 bits 
                        --num_pix;
                        prevCover = coverageValue;
                    }
                }
            }
        }
コード例 #28
0
ファイル: AlphaMask.cs プロジェクト: prepare/HTML-Renderer
        //public static readonly int cover_shift = 8;
        //public static readonly int cover_none = 0;
        //public static readonly int cover_full = 255;

        public AlphaMaskByteClipped(IImageReaderWriter rbuf, uint step, uint offset)
        {
            m_Step = step;
            m_Offset = offset;
            m_rbuf = rbuf;
        }
コード例 #29
0
ファイル: ProxyImage.cs プロジェクト: prepare/HTML-Renderer
 public virtual void CopyFrom(IImageReaderWriter sourceImage, RectInt sourceImageRect, int destXOffset, int destYOffset)
 {
     linkedImage.CopyFrom(sourceImage, sourceImageRect, destXOffset, destYOffset);
 }
コード例 #30
0
        public void BlurX(IImageReaderWriter img, double radius)
        {
            if (radius < 0.62) return;
            if (img.Width < 3) return;
            double s = (double)(radius * 0.5);
            double q = (double)((s < 2.5) ?
                                    3.97156 - 4.14554 * Math.Sqrt(1 - 0.26891 * s) :
                                    0.98711 * s - 0.96330);
            double q2 = (double)(q * q);
            double q3 = (double)(q2 * q);
            double b0 = (double)(1.0 / (1.578250 +
                                            2.444130 * q +
                                            1.428100 * q2 +
                                            0.422205 * q3));
            double b1 = (double)(2.44413 * q +
                                      2.85619 * q2 +
                                      1.26661 * q3);
            double b2 = (double)(-1.42810 * q2 +
                                     -1.26661 * q3);
            double b3 = (double)(0.422205 * q3);
            double b = (double)(1 - (b1 + b2 + b3) * b0);
            b1 *= b0;
            b2 *= b0;
            b3 *= b0;
            int w = img.Width;
            int h = img.Height;
            int wm = (int)w - 1;
            int x, y;
            int startCreatingAt = (int)m_sum1.Count;
            m_sum1.AdjustSize(w);
            m_sum2.AdjustSize(w);
            m_buf.Allocate(w);
            RecursizeBlurCalculator[] Sum1Array = m_sum1.Array;
            RecursizeBlurCalculator[] Sum2Array = m_sum2.Array;
            Color[] BufferArray = m_buf.Array;
            for (int i = startCreatingAt; i < w; i++)
            {
                Sum1Array[i] = m_RecursizeBlurCalculatorFactory.CreateNew();
                Sum2Array[i] = m_RecursizeBlurCalculatorFactory.CreateNew();
            }

            for (y = 0; y < h; y++)
            {
                RecursizeBlurCalculator c = m_RecursizeBlurCalculatorFactory;
                c.FromPix(img.GetPixel(0, y));
                Sum1Array[0].Calc(b, b1, b2, b3, c, c, c, c);
                c.FromPix(img.GetPixel(1, y));
                Sum1Array[1].Calc(b, b1, b2, b3, c, Sum1Array[0], Sum1Array[0], Sum1Array[0]);
                c.FromPix(img.GetPixel(2, y));
                Sum1Array[2].Calc(b, b1, b2, b3, c, Sum1Array[1], Sum1Array[0], Sum1Array[0]);
                for (x = 3; x < w; ++x)
                {
                    c.FromPix(img.GetPixel(x, y));
                    Sum1Array[x].Calc(b, b1, b2, b3, c, Sum1Array[x - 1], Sum1Array[x - 2], Sum1Array[x - 3]);
                }

                Sum2Array[wm].Calc(b, b1, b2, b3, Sum1Array[wm], Sum1Array[wm], Sum1Array[wm], Sum1Array[wm]);
                Sum2Array[wm - 1].Calc(b, b1, b2, b3, Sum1Array[wm - 1], Sum2Array[wm], Sum2Array[wm], Sum2Array[wm]);
                Sum2Array[wm - 2].Calc(b, b1, b2, b3, Sum1Array[wm - 2], Sum2Array[wm - 1], Sum2Array[wm], Sum2Array[wm]);
                Sum2Array[wm].ToPix(ref BufferArray[wm]);
                Sum2Array[wm - 1].ToPix(ref BufferArray[wm - 1]);
                Sum2Array[wm - 2].ToPix(ref BufferArray[wm - 2]);
                for (x = wm - 3; x >= 0; --x)
                {
                    Sum2Array[x].Calc(b, b1, b2, b3, Sum1Array[x], Sum2Array[x + 1], Sum2Array[x + 2], Sum2Array[x + 3]);
                    Sum2Array[x].ToPix(ref BufferArray[x]);
                }

                img.CopyColorHSpan(0, y, w, BufferArray, 0);
            }
        }