Example #1
0
        Color GetColorAt(int x, int y)
        {
            double new_x = x;
            double new_y = y;

            _transformBackToHorizontal.Transform(ref new_x, ref new_y);

            if (new_x <= _beginX)
            {
                return(_beginColor);
            }
            else if (new_x >= _endX)
            {
                return(_endColor);
            }
            //-----------------
            //find proper range
            for (int i = 0; i < _pairList.Length; ++i)
            {
                LinearGradientPair p = _pairList[i];
                if (new_x >= p._dx1 && new_x < p._dx2)
                {
                    return(p.GetColor((float)new_x));
                }
            }
            return(_endColor);
        }
        static void TransformToVxs(ICoordTransformer tx, VertexStore src, VertexStore outputVxs)
        {
            int       count = src.Count;
            VertexCmd cmd;
            double    x, y;

            for (int i = 0; i < count; ++i)
            {
                cmd = src.GetVertex(i, out x, out y);
                tx.Transform(ref x, ref y);
                outputVxs.AddVertex(x, y, cmd);
            }
        }
        public void Render(IBitmapSrc 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 = new RectInt((int)destX, (int)destY, (int)destX + source.Width, (int)destY + source.Height);
            //sourceBounds.Offset((int)destX, (int)destY);

            RectInt destBounds = _destBitmapBlender.GetBounds();

            if (!RectInt.DoIntersect(sourceBounds, destBounds))
            {
                //if (inScaleX != 1 || inScaleY != 1 || angleRadians != 0)
                //{
                //    throw new NotImplementedException();
                //}
                return;
            }

            double scaleX = inScaleX;
            double scaleY = inScaleY;

            ICoordTransformer 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 = false;
            if (angleRadians != 0 && Math.Abs(angleRadians) >= (0.1 * MathHelper.Tau / 360))
            {
                isRotated = true;
            }
            else
            {
                angleRadians = 0;//reset very small angle to 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;


            //VectorToolBox.GetFreeVxs(out VertexStore imgBoundsPath);
            // 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


                using (VxsTemp.Borrow(out var imgBoundsPath, out var v1))
                {
                    BuildOrgImgRectVxs(source.Width, source.Height, imgBoundsPath);
                    Affine destRectTransform = CreateAffine(destX, destY, _ox, _oy, scaleX, scaleY, angleRadians);

                    //TODO: review reusable span generator an interpolator ***

                    // We invert it because it is the transform to make the image go to the same position as the polygon. LBB [2/24/2004]
                    //spanInterpolator.Transformer = destRectTransform.CreateInvert();

                    //var imgSpanGen = new ImgSpanGenRGBA_BilinearClip(
                    //    Drawing.Color.Black,
                    //    spanInterpolator);
                    _spanInterpolator.Transformer = destRectTransform.CreateInvert();
                    _currentImgSpanGen.SetInterpolator(_spanInterpolator);
                    _currentImgSpanGen.SetSrcBitmap(source);
                    destRectTransform.TransformToVxs(imgBoundsPath, v1);
                    Render(v1, _currentImgSpanGen);
                    _currentImgSpanGen.ReleaseSrcBitmap();
                }


                ////Affine destRectTransform = BuildImageBoundsPath(source.Width, source.Height,
                ////    destX, destY, ox, oy, scaleX, scaleY, angleRadians, imgBoundsPath);

                //Affine destRectTransform = CreateAffine(destX, destY, ox, oy, scaleX, scaleY, angleRadians);
                ////TODO: review reusable span generator an interpolator ***
                //var spanInterpolator = new SpanInterpolatorLinear();
                //// We invert it because it is the transform to make the image go to the same position as the polygon. LBB [2/24/2004]
                //spanInterpolator.Transformer = destRectTransform.CreateInvert();

                //var imgSpanGen = new ImgSpanGenRGBA_BilinearClip(
                //    source,
                //    Drawing.Color.Black,
                //    spanInterpolator);

                //VectorToolBox.GetFreeVxs(out VertexStore v1);
                //destRectTransform.TransformToVxs(imgBoundsPath, v1);
                //Render(v1, imgSpanGen);
                //VectorToolBox.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 intermediate buffer
            {
                //Affine destRectTransform = BuildImageBoundsPath(
                //    source.Width, source.Height,
                //    destX, destY, imgBoundsPath);
                using (VxsTemp.Borrow(out var imgBoundsPath, out var v1))
                {
                    BuildOrgImgRectVxs(source.Width, source.Height, imgBoundsPath);
                    //...
                    Affine destRectTransform = CreateAffine(destX, destY);

                    //TODO: review reusable span generator an interpolator ***


                    // We invert it because it is the transform to make the image go to the same position as the polygon. LBB [2/24/2004]

                    _spanInterpolator.Transformer = destRectTransform.CreateInvert();
                    //we generate image by this imagespan generator

                    ImgSpanGen imgSpanGen = null;
                    switch (source.BitDepth)
                    {
                    case 32:
                        if (_imgSpanGenCustom != null)
                        {
                            //use custom span gen
                            _imgSpanGenCustom.SetInterpolator(_spanInterpolator);
                            _imgSpanGenCustom.SetSrcBitmap(source);
                            imgSpanGen = _imgSpanGenCustom;
                        }
                        else
                        {
                            _imgSpanGenNNStepXBy1.SetInterpolator(_spanInterpolator);
                            _imgSpanGenNNStepXBy1.SetSrcBitmap(source);
                            imgSpanGen = _imgSpanGenNNStepXBy1;
                        }

                        break;

                    //case 8:
                    //    imgSpanGen = new ImgSpanGenGray_NNStepXby1(source, interpolator);
                    //    break;
                    default:
                        throw new NotImplementedException();
                    }

                    destRectTransform.TransformToVxs(imgBoundsPath, v1);
                    //...
                    Render(v1, imgSpanGen);
                }


                //
                unchecked { _destImageChanged++; };
            }
        }
Example #4
0
 public static void AddLineTo(this VertexStore vxs, double x, double y, ICoordTransformer tx)
 {
     tx.Transform(ref x, ref y);
     vxs.AddLineTo(x, y);
 }
Example #5
0
        public void ResolveBrush(LinearGradientBrush linearGrBrush)
        {
            PointF p1 = linearGrBrush.StartPoint;
            PointF p2 = linearGrBrush.EndPoint;

            //assume horizontal line


            _beginX = p1.X;
            _beginY = p1.Y;
            _endX   = p2.X;
            _endY   = p2.Y;
            //--------------
            //find transformation matrix
            double angle = Math.Atan2(p2.Y - p1.Y, p2.X - p1.X);


            ICoordTransformer rotateTx = Affine.NewRotation(angle);

            if (linearGrBrush.CoordTransformer != null)
            {
                //*** IMPORTANT : matrix transform order !**
                rotateTx = linearGrBrush.CoordTransformer.MultiplyWith(rotateTx);
            }

            _transformBackToHorizontal = rotateTx.CreateInvert();


            _totalLen = (float)Math.Sqrt((_endX - _beginX) * (_endX - _beginX) + (_endY - _beginY) * (_endY - _beginY));
            double tmpX = _beginX;
            double tmpY = _beginY;

            _transformBackToHorizontal.Transform(ref tmpX, ref tmpY);
            _beginX = (float)tmpX;
            _beginY = (float)tmpY;
            //--------------
            tmpX = _endX;
            tmpY = _endY;
            _transformBackToHorizontal.Transform(ref tmpX, ref tmpY);
            _endX = (float)tmpX;
            _endY = (float)tmpY;
            //--------------

            ColorStop[] colorStops = linearGrBrush.ColorStops;

            int pairCount = colorStops.Length - 1;

            _pairList = new LinearGradientPair[pairCount];

            ColorStop c0 = ColorStop.Empty;
            ColorStop c1 = ColorStop.Empty;

            for (int i = 0; i < pairCount; ++i)
            {
                c0 = colorStops[i];
                c1 = colorStops[i + 1];
                if (i == 0)
                {
                    _beginColor = c0.Color;
                }

                var pairN = new LinearGradientPair(
                    _beginX + c0.Offset * _totalLen, //to actual pixel
                    _beginX + c1.Offset * _totalLen, //to actual pixel
                    c0.Color,
                    c1.Color);
                _pairList[i] = pairN;
            }

            this.SpreadMethod = linearGrBrush.SpreadMethod;
            _endColor         = c1.Color;
        }
Example #6
0
 void ICoordTransformer.Transform(ref double x, ref double y)
 {
     _left.Transform(ref x, ref y);
     _right.Transform(ref x, ref y);
 }
        public void Render(IBitmapSrc source,
                           double destX, double destY,
                           double angleRadians,
                           double inScaleX, double inScaleY)
        {
            {   // exit early if the dest and source bounds don't touch.
                // TODO: <BUG> make this do rotation and scalling
                Q1Rect sourceBounds = source.GetBounds();
                Q1Rect destBounds   = _destBitmapBlender.GetBounds();
                sourceBounds.Offset((int)destX, (int)destY);
                if (!Q1Rect.DoIntersect(sourceBounds, destBounds))
                {
                    if (inScaleX != 1 || inScaleY != 1 || angleRadians != 0)
                    {
                        throw new NotImplementedException();
                    }
                    return;
                }
            }

            double            scaleX            = inScaleX;
            double            scaleY            = inScaleY;
            ICoordTransformer 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 renderRequriesSourceSampling = isScale || isRotated || destX != (int)destX || destY != (int)destY;


            using (VxsTemp.Borrow(out var v1, out var imgBoundsPath))
            {
                // this is the fast drawing path
                if (renderRequriesSourceSampling)
                {
                    // 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);
                    //}
                    //Affine destRectTransform = BuildImageBoundsPath(source.Width, source.Height,
                    //    destX, destY, ox, oy, scaleX, scaleY, angleRadians, imgBoundsPath);

                    //1.
                    BuildOrgImgRectVxs(source.Width, source.Height, imgBoundsPath);
                    //2.

                    AffineMat destRectTransform = AffineMat.Iden();
                    destRectTransform.Translate(-_ox, -_oy);
                    destRectTransform.Scale(scaleX, scaleY);
                    destRectTransform.Rotate(angleRadians);


                    //TODO: review reusable span generator an interpolator ***

                    // We invert it because it is the transform to make the image go to the same position as the polygon. LBB [2/24/2004]
                    _reuseableAffine.SetElements(destRectTransform.CreateInvert());

                    _spanInterpolator.Transformer      = _reuseableAffine;
                    _currentImgSpanGen.BackgroundColor = Drawing.Color.Black;
                    _currentImgSpanGen.SetInterpolator(_spanInterpolator);
                    _currentImgSpanGen.SetSrcBitmap(source);

                    destRectTransform.TransformToVxs(imgBoundsPath, v1); //not inverted version
                    Render(v1, _currentImgSpanGen);
                    _currentImgSpanGen.ReleaseSrcBitmap();

                    // 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);
                }
                else // TODO: this can be even faster if we do not use an intermediat buffer
                {
                    //Affine destRectTransform = BuildImageBoundsPath(source.Width, source.Height, destX, destY, imgBoundsPath);

                    BuildOrgImgRectVxs(source.Width, source.Height, imgBoundsPath);

                    //
                    var destRectTransform = new AffineMat();
                    destRectTransform.Translate(destX, destY);
                    //TODO: review reusable span generator an interpolator ***

                    // We invert it because it is the transform to make the image go to the same position as the polygon. LBB [2/24/2004]
                    _reuseableAffine.SetElements(destRectTransform.CreateInvert());
                    _spanInterpolator.Transformer = _reuseableAffine;

                    ImgSpanGen imgSpanGen = null;
                    switch (source.BitDepth)
                    {
                    case 32:
                        if (_imgSpanGenCustom != null)
                        {
                            //use custom span gen
                            _imgSpanGenCustom.SetInterpolator(_spanInterpolator);
                            _imgSpanGenCustom.SetSrcBitmap(source);
                            imgSpanGen = _imgSpanGenCustom;
                        }
                        else
                        {
                            _imgSpanGenNNStepXBy1.SetInterpolator(_spanInterpolator);
                            _imgSpanGenNNStepXBy1.SetSrcBitmap(source);
                            imgSpanGen = _imgSpanGenNNStepXBy1;
                        }


                        break;

                    //case 24:
                    //    imgSpanGen = new ImgSpanGenRGB_NNStepXby1(source, interpolator);
                    //    break;
                    //case 8:
                    //    imgSpanGen = new ImgSpanGenGray_NNStepXby1(source, interpolator);
                    //    break;
                    default:
                        throw new NotImplementedException();
                    }

                    TransformToVxs(destRectTransform, imgBoundsPath, v1);
                    Render(v1, imgSpanGen);
                    unchecked { _destImageChanged++; };
                }
            }
        }
Example #8
0
        public VertexStore MakeVxs(VertexStore vxs, ICoordTransformer tx, VertexStore output)
        {
            _curve3.Reset();
            _curve4.Reset();

            CurvePointMode latestCurveMode = CurvePointMode.NotCurve;
            double         x, y;
            VertexCmd      cmd;

            VectorMath.Vector2 c3p2      = new VectorMath.Vector2();
            VectorMath.Vector2 c4p2      = new VectorMath.Vector2();
            VectorMath.Vector2 c4p3      = new VectorMath.Vector2();
            double             lastX     = 0;
            double             lasty     = 0;
            double             lastMoveX = 0;
            double             lastMoveY = 0;


            int  index = 0;
            bool hasTx = tx != null;

            while ((cmd = vxs.GetVertex(index++, out x, out y)) != VertexCmd.NoMore)
            {
#if DEBUG
                if (VertexStore.dbugCheckNANs(x, y))
                {
                }
#endif

                //-----------------
                if (hasTx)
                {
                    tx.Transform(ref x, ref y);
                }

                //-----------------
                switch (cmd)
                {
                case VertexCmd.C3:
                {
                    switch (latestCurveMode)
                    {
                    case CurvePointMode.P2:
                    {
                    }
                    break;

                    case CurvePointMode.P3:
                    {
                    }
                    break;

                    case CurvePointMode.NotCurve:
                    {
                        c3p2.x = x;
                        c3p2.y = y;
                    }
                    break;

                    default:
                    {
                    }
                    break;
                    }
                    latestCurveMode = CurvePointMode.P2;
                }
                break;

                case VertexCmd.C4:
                {
                    //this is p3c
                    switch (latestCurveMode)
                    {
                    case CurvePointMode.P2:
                    {
                        c3p2.x = x;
                        c3p2.y = y;
                    }
                    break;

                    case CurvePointMode.P3:
                    {
                        c4p3.x = x;
                        c4p3.y = y;
                    }
                    break;

                    case CurvePointMode.NotCurve:
                    {
                        c4p2.x = x;
                        c4p2.y = y;
                    }
                    break;
                    }
                    latestCurveMode = CurvePointMode.P3;
                }
                break;

                case VertexCmd.LineTo:
                {
                    switch (latestCurveMode)
                    {
                    case CurvePointMode.P2:
                    {
                        _curve3.MakeLines(output,
                                          lastX,
                                          lasty,
                                          c3p2.X,
                                          c3p2.Y,
                                          x,
                                          y);
                    }
                    break;

                    case CurvePointMode.P3:
                    {
                        _curve4.MakeLines(output,
                                          lastX, lasty,
                                          c4p2.x, c4p2.y,
                                          c4p3.x, c4p3.y,
                                          x, y);
                    }
                    break;

                    default:
                    {
                        output.AddVertex(x, y, cmd);
                    }
                    break;
                    }
                    //-----------
                    latestCurveMode = CurvePointMode.NotCurve;
                    lastX           = x;
                    lasty           = y;
                    //-----------
                }
                break;

                case VertexCmd.MoveTo:
                {
                    //move to, and end command
                    output.AddVertex(x, y, cmd);
                    //-----------
                    latestCurveMode = CurvePointMode.NotCurve;
                    lastMoveX       = lastX = x;
                    lastMoveY       = lasty = y;
                    //-----------
                }
                break;

                case VertexCmd.Close:
                case VertexCmd.CloseAndEndFigure:
                {
                    latestCurveMode = CurvePointMode.NotCurve;
                    output.AddVertex(lastMoveX, lastMoveY, cmd);
                    //move to begin
                    lastX = lastMoveX;
                    lasty = lastMoveY;
                }
                break;

                default:
                {
                    //move to, and end command
                    output.AddVertex(x, y, cmd);
                    //-----------
                    latestCurveMode = CurvePointMode.NotCurve;
                    lastX           = x;
                    lasty           = y;
                    //-----------
                }
                break;
                }
            }

            return(output);
        }
Example #9
0
        protected override void OnStart(AppHost host)
        {
            _appHost = host;//**

            //string svgfile = "../Test8_HtmlRenderer.Demo/Samples/Svg/others/cat_simple.svg";
            //string svgfile = "../Test8_HtmlRenderer.Demo/Samples/Svg/others/cat_complex.svg";
            //string svgfile = "../Test8_HtmlRenderer.Demo/Samples/Svg/others/lion.svg";
            string svgfile = "../Test8_HtmlRenderer.Demo/Samples/Svg/others/tiger.svg";

            //return VgVisualElemHelper.ReadSvgFile(svgfile);
            _rotationUI.AngleUpdated += _rotationUI_AngleUpdated;
            //string svgfile = "../Test8_HtmlRenderer.Demo/Samples/Svg/freepik/dog1.svg";
            //string svgfile = "1f30b.svg";
            //string svgfile = "../Data/Svg/twemoji/1f30b.svg";
            //string svgfile = "../Data/1f30b.svg";
            //string svgfile = "../Data/Svg/twemoji/1f370.svg";

            //_svgRenderVx = CreateTestRenderVx_FromSvg();
            //_svgRenderVx = CreateTestRenderVx_BasicShape();
            //_vgVisualElem = CreateTestRenderVx_FromImg("d:\\WImageTest\\alpha1.png");

            //string fontfile = "../Test8_HtmlRenderer.Demo/Samples/Fonts/SOV_Thanamas.ttf";
            //_vgVisualElem = VgVisualElemHelper.CreateVgVisualElementFromGlyph(fontfile, 256, 'a'); //create from glyph

            _vgVisualElem = CreateTestRenderVx_FromImg("d:\\WImageTest\\fenec.png");
            //_vgVisualElem = VgVisualElemHelper.ReadSvgFile(svgfile);


            //PixelFarm.CpuBlit.RectD org_rectD = _svgRenderVx.GetBounds();
            //_svgRenderVx = CreateEllipseVxs(org_rectD);

            PixelFarm.CpuBlit.VertexProcessing.Q1RectD org_rectD = _vgVisualElem.GetRectBounds();
            org_rectD.Offset(-org_rectD.Left, -org_rectD.Bottom);
            //
            _quadController.SetSrcRect(org_rectD.Left, org_rectD.Bottom, org_rectD.Right, org_rectD.Top);
            _quadController.SetDestQuad(
                org_rectD.Left, org_rectD.Top,
                org_rectD.Right, org_rectD.Top,
                org_rectD.Right, org_rectD.Bottom,
                org_rectD.Left, org_rectD.Bottom);
            //create control point
            _quadController.SetPolygonController(_quadPolygonController);
            _quadController.BuildControlBoxes();
            _quadController.UpdateTransformTarget += (s1, e1) =>
            {
                //after quadController is updated then
                //we use the coordTransformer to transform target uiSprite
                _uiSprite.SetTransformation(_quadController.GetCoordTransformer());
                _uiSprite.InvalidateGraphics();
                if (_quadController.Left != 0 || _quadController.Top != 0)
                {
                    float xxdiff = _quadController.Left - _uiSprite.Left;
                    float yydiff = _quadController.Top - _uiSprite.Top;

                    _uiSprite.SetLocation(_quadController.Left, _quadController.Top);
                    _uiSprite.InvalidateGraphics();


                    //_rotationUI.InvalidateGraphics();
                    //_rotationUI.SetLocation(
                    //    _rotationUI.Left + xxdiff,
                    //    _rotationUI.Top + yydiff);
                    //_rotationUI.InvalidateGraphics();

                    //_rotationControllerPointUI.InvalidateGraphics();
                    //_rotationControllerPointUI.SetPosition(
                    //   (int)(_rotationControllerPointUI.Left + xxdiff),
                    //   (int)(_rotationControllerPointUI.Top + yydiff));
                    //_rotationControllerPointUI.InvalidateGraphics();
                }
            };



            //_rectBoundsWidgetBox = new Box2(50, 50); //visual rect box
            //Color c = KnownColors.FromKnownColor(KnownColor.DeepSkyBlue);
            //_rectBoundsWidgetBox.BackColor = Color.FromArgb(100, c);
            //_rectBoundsWidgetBox.SetLocation(10, 10);
            /////box1.dbugTag = 1;
            //SetupActiveBoxProperties(_rectBoundsWidgetBox);
            //host.AddChild(_rectBoundsWidgetBox);
            //_quadController.Visible = _quadPolygonController.Visible = false;
            //_rectBoxController.Init();

            PixelFarm.CpuBlit.VertexProcessing.Q1RectD svg_bounds = _vgVisualElem.GetRectBounds(); //bounds of graphic shape
            //ICoordTransformer tx = ((ICoordTransformer)_bilinearTx).MultiplyWith(scaleMat);
            ICoordTransformer tx = _quadController.GetCoordTransformer();

            //svgRenderVx._coordTx = tx;
            //svgRenderVx._coordTx = ((ICoordTransformer)_bilinearTx).MultiplyWith(scaleMat);
            //host.AddChild(_quadController);
            //host.AddChild(_quadPolygonController);
            //VgRenderVx svgRenderVx = CreateTestRenderVx();

            //test transform svgRenderVx

            _vgVisualElem.DisableBackingImage = true;


            //-----------------------------------------
            _uiSprite = new UISprite(10, 10); //init size = (10,10), location=(0,0)
            _uiSprite.DisableBmpCache = true;
            _uiSprite.LoadVg(_vgVisualElem);  //
            _uiSprite.SetTransformation(tx);  //set transformation
            host.AddChild(_uiSprite);
            //-----------------------------------------
            //host.AddChild(_quadController);
            host.AddChild(_quadPolygonController);
            {
                PointControllerBox center = new PointControllerBox(10, 10);
                PointControllerBox radius = new PointControllerBox(10, 10);
                host.AddChild(center);
                host.AddChild(radius);
                _rotationUI.AddControlPoints(center, radius);
            }

            _rotationUI.SetReferenceOwner(_quadController);

            double x_center = (svg_bounds.Left + svg_bounds.Right) / 2;
            double y_center = (svg_bounds.Top + svg_bounds.Bottom) / 2;

            _rotationUI.SetCenter(x_center, y_center);
            _rotationUI.SetRadius(x_center + 200, y_center);

            host.AddChild(_rotationUI);



            _quadController.Drag += (s1, ev) =>
            {
                _rotationUI.SetLocation(
                    _rotationUI.Left + ev.XDiff,
                    _rotationUI.Top + ev.YDiff);
            };


            var spriteEvListener = new GeneralEventListener();

            _uiSprite.AttachExternalEventListener(spriteEvListener);
            spriteEvListener.MouseMove += (s1, e1) =>
            {
                if (e1.IsDragging)
                {
                    //when drag on sprie


                    _uiSprite.InvalidateGraphics();
                    _uiSprite.SetLocation(
                        _uiSprite.Left + e1.XDiff,
                        _uiSprite.Top + e1.YDiff
                        );
                    //we also move quadController and _quadPolygonController
                    //
                    _quadController.InvalidateGraphics();
                    _quadController.SetLocation(
                        _quadController.Left + e1.XDiff,
                        _quadController.Top + e1.YDiff);
                    _quadController.InvalidateGraphics();
                    //
                    _quadPolygonController.InvalidateGraphics();
                    _quadPolygonController.SetLocation(
                        _quadPolygonController.Left + e1.XDiff,
                        _quadPolygonController.Top + e1.YDiff
                        );
                    _quadPolygonController.InvalidateGraphics();
                    //
                    _rotationUI.InvalidateGraphics();
                    _rotationUI.SetLocation(
                        _rotationUI.Left + e1.XDiff,
                        _rotationUI.Top + e1.YDiff);
                    _rotationUI.InvalidateGraphics();

                    //_rotationControllerPointUI.InvalidateGraphics();
                    //_rotationControllerPointUI.SetPosition(
                    //   _rotationControllerPointUI.Left + e1.XDiff,
                    //   _rotationControllerPointUI.Top + e1.YDiff);
                    //_rotationControllerPointUI.InvalidateGraphics();
                }
            };
            spriteEvListener.MouseDown += (s1, e1) =>
            {
                //mousedown on ui sprite
                //find exact part ...

                _quadController.BringToTopMost();
                _quadController.Visible        = true;
                _quadPolygonController.Visible = true;
                _quadController.Focus();

                // _polygonController.BringToTopMost();

                if (_hitTestOnSubPath)
                {
                    //find which part ...

                    double            e1_x = e1.X;
                    double            e1_y = e1.Y;
                    ICoordTransformer tx1  = _quadController.GetCoordTransformer();
                    if (tx1 != null)
                    {
                        //if the sprite is transformed before render
                        //we must invert x,y back to
                        ICoordTransformer tx1_inv = tx1.CreateInvert();
                        tx1_inv.Transform(ref e1_x, ref e1_y);
                    }

                    VgHitInfo hitInfo = _uiSprite.FindRenderElementAtPos((float)e1_x, (float)e1_y, true);

                    if (hitInfo.hitElem != null &&
                        hitInfo.hitElem.VxsPath != null)
                    {
                        PixelFarm.CpuBlit.VertexProcessing.Q1RectD bounds = hitInfo.copyOfVxs.GetBoundingRect();

                        _quadPolygonController.ClearControlPoints(); //clear old control points
                        _quadPolygonController.UpdateControlPoints(  //create new control points
                            hitInfo.copyOfVxs,
                            _uiSprite.ActualXOffset, _uiSprite.ActualYOffset, tx1);

                        ////move redbox and its controller
                        //_rectBoundsWidgetBox.SetLocationAndSize(
                        //    (int)(bounds.Left + _uiSprite.ActualXOffset), (int)(bounds.Top - bounds.Height + _uiSprite.ActualYOffset),
                        //    (int)bounds.Width, (int)bounds.Height);
                        ////_rectBoxController.UpdateControllerBoxes(_rectBoundsWidgetBox);

                        //_rectBoundsWidgetBox.Visible = true;
                        ////_rectBoxController.Visible = true;
                        //show bounds
                    }
                    else
                    {
                        //_rectBoundsWidgetBox.Visible = false;
                        // _rectBoxController.Visible = false;
                    }
                }
                else
                {
                    //hit on sprite
                    if (e1.Ctrl)
                    {
                        //test***
                        //
                        _uiSprite.GetElementBounds(out float left, out float top, out float right, out float bottom);
                        //
                        using (Tools.BorrowRect(out SimpleRect s))
                            using (Tools.BorrowVxs(out var v1))
                            {
                                s.SetRect(left - _uiSprite.ActualXOffset,
                                          bottom - _uiSprite.ActualYOffset,
                                          right - _uiSprite.ActualXOffset,
                                          top - _uiSprite.ActualYOffset);
                                s.MakeVxs(v1);
                                //_polygonController.UpdateControlPoints(v1.CreateTrim());
                            }
                    }
                    else
                    {
                        //_rectBoundsWidgetBox.SetTarget(_uiSprite);
                        //_rectBoundsWidgetBox.SetLocationAndSize(    //blue
                        //      (int)_uiSprite.Left, (int)_uiSprite.Top,
                        //      (int)_uiSprite.Width, (int)_uiSprite.Height);
                        ////_rectBoxController.SetTarget(_uiSprite);

                        ////_rectBoxController.UpdateControllerBoxes(_rectBoundsWidgetBox);  //control 4 corners
                        //_rectBoundsWidgetBox.Visible = true;
                        ////_rectBoxController.Visible = true;

                        //UpdateTransformedShape2();
                    }
                }
            };
        }