示例#1
0
 private void ParseImageLayerXml(XElement xml)
 {
     if (xml.Element("image") == null)
     {
         Logger.WriteWarning("Image Layer '{0}' is being ignored since it has no image.", base.Name);
         base.Ignore = IgnoreSettings.True;
     }
     else
     {
         Width  = 1;
         Height = 1;
         string  imagePath = TmxHelper.GetAttributeAsFullPath(xml.Element("image"), "source");
         TmxTile value     = base.ParentMap.Tiles.First((KeyValuePair <uint, TmxTile> t) => t.Value.TmxImage.AbsolutePath.ToLower() == imagePath.ToLower()).Value;
         Data = new TmxData(this);
         TmxChunk tmxChunk = new TmxChunk(Data);
         tmxChunk.X      = 0;
         tmxChunk.Y      = 0;
         tmxChunk.Width  = 1;
         tmxChunk.Height = 1;
         tmxChunk.TileIds.Add(value.GlobalId);
         Data.Chunks.Add(tmxChunk);
         PointF offset = base.Offset;
         offset.Y -= (float)base.ParentMap.TileHeight;
         offset.Y += (float)value.TmxImage.Size.Height;
         PointF pointF = TmxMath.TileCornerInScreenCoordinates(base.ParentMap, 0, 0);
         offset.X   -= pointF.X;
         offset.Y   -= pointF.Y;
         offset.X   += TmxHelper.GetAttributeAsFloat(xml, "x", 0f);
         offset.Y   += TmxHelper.GetAttributeAsFloat(xml, "y", 0f);
         base.Offset = offset;
     }
 }
        private RectangleF CalculateBoundary()
        {
            RectangleF rcMap = new RectangleF(Point.Empty, this.tmxMap.MapSizeInPixels());

            // Take boundaries from object groups
            var objBounds = from g in this.tmxMap.ObjectGroups
                            from o in g.Objects
                            where o.Visible == true
                            where IsLayerEnabled(g.Name)
                            select o.GetWorldBounds();

            // Take boundaries from objects embedded in tiles
            var tileBounds = from layer in tmxMap.Layers
                             where layer.Visible == true
                             where IsLayerEnabled(layer.DefaultName)
                             from y in Enumerable.Range(0, layer.Height)
                             from x in Enumerable.Range(0, layer.Width)
                             let tileId = layer.GetTileIdAt(x, y)
                                          where tileId != 0
                                          let tile = this.tmxMap.Tiles[tileId]
                                                     from o in tile.ObjectGroup.Objects
                                                     let bound                         = o.GetWorldBounds()
                                                                             let point = TmxMath.TileCornerInScreenCoordinates(this.tmxMap, x, y)
                                                                                         select new RectangleF(bound.X + point.X, bound.Y + point.Y, bound.Width, bound.Height);

            var allBounds = objBounds.Concat(tileBounds);
            var union     = allBounds.Aggregate(rcMap, RectangleF.Union);

            // Inflate a tile size to make room for the grid
            union.Inflate(this.tmxMap.TileWidth, this.tmxMap.TileHeight);
            union.Inflate(Tiled2UnityViewer.GridSize, Tiled2UnityViewer.GridSize);

            return(union);
        }
        private void DrawTiles(Graphics g)
        {
            // Load all our tiled images
            var images = from layer in this.tmxMap.Layers
                         where layer.Properties.GetPropertyValueAsBoolean("unity:collisionOnly", false) == false
                         where layer.Visible == true
                         where IsLayerEnabled(layer.DefaultName)
                         from y in Enumerable.Range(0, layer.Height)
                         from x in Enumerable.Range(0, layer.Width)
                         let tileId = layer.GetTileIdAt(x, y)
                                      where tileId != 0
                                      let tile = this.tmxMap.Tiles[tileId]
                                                 select new
            {
                Path  = tile.TmxImage.Path,
                Trans = tile.TmxImage.TransparentColor,
            };

            images = images.Distinct();

            Dictionary <string, Bitmap> tileSetBitmaps = new Dictionary <string, Bitmap>();

            foreach (var img in images)
            {
                Bitmap bmp = (Bitmap)Bitmap.FromFile(img.Path);

                if (!String.IsNullOrEmpty(img.Trans))
                {
                    System.Drawing.Color transColor = System.Drawing.ColorTranslator.FromHtml(img.Trans);
                    bmp.MakeTransparent(transColor);
                }

                tileSetBitmaps.Add(img.Path, bmp);
            }

            foreach (TmxLayer layer in this.tmxMap.Layers)
            {
                if (layer.Visible == false)
                {
                    continue;
                }

                if (IsLayerEnabled(layer.DefaultName) == false)
                {
                    continue;
                }

                if (layer.Properties.GetPropertyValueAsBoolean("unity:collisionOnly", false) == true)
                {
                    continue;
                }

                // The range of x and y depends on the render order of the tiles
                // By default we draw right and down but may reverse the tiles we visit
                var range_x = Enumerable.Range(0, layer.Width);
                var range_y = Enumerable.Range(0, layer.Height);

                if (this.tmxMap.DrawOrderHorizontal == -1)
                {
                    range_x = range_x.Reverse();
                }

                if (this.tmxMap.DrawOrderVertical == -1)
                {
                    range_y = range_y.Reverse();
                }

                // Visit the tiles we are going to draw
                var tiles = from y in range_y
                            from x in range_x
                            let rawTileId = layer.GetRawTileIdAt(x, y)
                                            let tileId = layer.GetTileIdAt(x, y)
                                                         where tileId != 0

                                                         let tile = this.tmxMap.Tiles[tileId]

                                                                    // Support for animated tiles. Just show the first frame of the animation.
                                                                    let frame = (tile.Animation == null) ? tile : this.tmxMap.Tiles[tile.Animation.Frames[0].GlobalTileId]

                                                                                select new
                {
                    Tile                  = frame,
                    Position              = TmxMath.TileCornerInScreenCoordinates(this.tmxMap, x, y),
                    Bitmap                = tileSetBitmaps[frame.TmxImage.Path],
                    IsFlippedDiagnoally   = TmxMath.IsTileFlippedDiagonally(rawTileId),
                    IsFlippedHorizontally = TmxMath.IsTileFlippedHorizontally(rawTileId),
                    IsFlippedVertically   = TmxMath.IsTileFlippedVertically(rawTileId),
                };

                PointF[] destPoints  = new PointF[4];
                PointF[] destPoints3 = new PointF[3];
                foreach (var t in tiles)
                {
                    PointF location = t.Position;

                    // Individual tiles may be larger than the given tile size of the overall map
                    location.Y = (t.Position.Y - t.Tile.TileSize.Height) + this.tmxMap.TileHeight;

                    // Make up the 'quad' of texture points and transform them
                    PointF center = new PointF(t.Tile.TileSize.Width * 0.5f, t.Tile.TileSize.Height * 0.5f);
                    destPoints[0] = new Point(0, 0);
                    destPoints[1] = new Point(t.Tile.TileSize.Width, 0);
                    destPoints[2] = new Point(t.Tile.TileSize.Width, t.Tile.TileSize.Height);
                    destPoints[3] = new Point(0, t.Tile.TileSize.Height);

                    // Transform the points based on our flipped flags
                    TmxMath.TransformPoints(destPoints, center, t.IsFlippedDiagnoally, t.IsFlippedHorizontally, t.IsFlippedVertically);

                    // Put the destination points back into world space
                    TmxMath.TranslatePoints(destPoints, location);

                    // Stupid DrawImage function only takes 3 destination points otherwise it throws an exception
                    destPoints3[0] = destPoints[0];
                    destPoints3[1] = destPoints[1];
                    destPoints3[2] = destPoints[3];

                    // Draw the tile
                    Rectangle source = new Rectangle(t.Tile.LocationOnSource, t.Tile.TileSize);
                    g.DrawImage(t.Bitmap, destPoints3, source, GraphicsUnit.Pixel);
                }
            }

            tileSetBitmaps.Clear();
        }
        private void DrawGridHex(Graphics g)
        {
            // Our collection of points to render
            HashSet <Point> points = new HashSet <Point>();

            // Note: borrowed heavily from Tiled source (HexagonalRenderer::drawGrid)
            int tileWidth  = this.tmxMap.TileWidth & ~1;
            int tileHeight = this.tmxMap.TileHeight & ~1;

            int sideLengthX = tmxMap.StaggerAxis == TmxMap.MapStaggerAxis.X ? tmxMap.HexSideLength : 0;
            int sideLengthY = tmxMap.StaggerAxis == TmxMap.MapStaggerAxis.Y ? tmxMap.HexSideLength : 0;

            int sideOffsetX = (tmxMap.TileWidth - sideLengthX) / 2;
            int sideOffsetY = (tmxMap.TileHeight - sideLengthY) / 2;

            int columnWidth = sideOffsetX + sideLengthX;
            int rowHeight   = sideOffsetY + sideLengthY;

            bool staggerX = this.tmxMap.StaggerAxis == TmxMap.MapStaggerAxis.X;

            // Determine the tile and pixel coordinates to start at
            Point startTile = new Point(0, 0);
            Point startPos  = TmxMath.TileCornerInScreenCoordinates(this.tmxMap, startTile.X, startTile.Y);

            Point[] oct = new Point[8]
            {
                new Point(0, tileHeight - sideOffsetY),
                new Point(0, sideOffsetY),
                new Point(sideOffsetX, 0),
                new Point(tileWidth - sideOffsetX, 0),
                new Point(tileWidth, sideOffsetY),
                new Point(tileWidth, tileHeight - sideOffsetY),
                new Point(tileWidth - sideOffsetX, tileHeight),
                new Point(sideOffsetX, tileHeight)
            };

            if (staggerX)
            {
                // Odd row shifting is applied in the rendering loop, so un-apply it here
                if (TmxMath.DoStaggerX(this.tmxMap, startTile.X))
                {
                    startPos.Y -= rowHeight;
                }

                for (; startTile.X < this.tmxMap.Width; startTile.X++)
                {
                    Point rowTile = startTile;
                    Point rowPos  = startPos;

                    if (TmxMath.DoStaggerX(this.tmxMap, startTile.X))
                    {
                        rowPos.Y += rowHeight;
                    }

                    for (; rowTile.Y < this.tmxMap.Height; rowTile.Y++)
                    {
                        points.Add(TmxMath.AddPoints(rowPos, oct[1]));
                        points.Add(TmxMath.AddPoints(rowPos, oct[2]));
                        points.Add(TmxMath.AddPoints(rowPos, oct[3]));
                        points.Add(TmxMath.AddPoints(rowPos, oct[4]));

                        bool isStaggered = TmxMath.DoStaggerX(tmxMap, startTile.X);
                        bool lastRow     = rowTile.Y == tmxMap.Height - 1;
                        bool lastColumn  = rowTile.X == tmxMap.Width - 1;
                        bool bottomLeft  = rowTile.X == 0 || (lastRow && isStaggered);
                        bool bottomRight = lastColumn || (lastRow && isStaggered);

                        if (bottomRight)
                        {
                            points.Add(TmxMath.AddPoints(rowPos, oct[5]));
                            points.Add(TmxMath.AddPoints(rowPos, oct[6]));
                        }
                        if (lastRow)
                        {
                            points.Add(TmxMath.AddPoints(rowPos, oct[6]));
                            points.Add(TmxMath.AddPoints(rowPos, oct[7]));
                        }
                        if (bottomLeft)
                        {
                            points.Add(TmxMath.AddPoints(rowPos, oct[7]));
                            points.Add(TmxMath.AddPoints(rowPos, oct[0]));
                        }

                        rowPos.Y += tileHeight + sideLengthY;
                    }

                    startPos.X += columnWidth;
                }
            }
            else
            {
                // Odd row shifting is applied in the rendering loop, so un-apply it here
                if (TmxMath.DoStaggerY(this.tmxMap, startTile.Y))
                {
                    startPos.X -= columnWidth;
                }

                for (; startTile.Y < tmxMap.Height; startTile.Y++)
                {
                    Point rowTile = startTile;
                    Point rowPos  = startPos;

                    if (TmxMath.DoStaggerY(this.tmxMap, startTile.Y))
                    {
                        rowPos.X += columnWidth;
                    }

                    for (; rowTile.X < this.tmxMap.Width; rowTile.X++)
                    {
                        points.Add(TmxMath.AddPoints(rowPos, oct[0]));
                        points.Add(TmxMath.AddPoints(rowPos, oct[1]));
                        points.Add(TmxMath.AddPoints(rowPos, oct[2]));
                        points.Add(TmxMath.AddPoints(rowPos, oct[3]));
                        points.Add(TmxMath.AddPoints(rowPos, oct[4]));


                        bool isStaggered = TmxMath.DoStaggerY(this.tmxMap, startTile.Y);
                        bool lastRow     = rowTile.Y == this.tmxMap.Height - 1;
                        bool lastColumn  = rowTile.Y == this.tmxMap.Width - 1;
                        bool bottomLeft  = lastRow || (rowTile.X == 0 && !isStaggered);
                        bool bottomRight = lastRow || (lastColumn && isStaggered);

                        if (lastColumn)
                        {
                            points.Add(TmxMath.AddPoints(rowPos, oct[4]));
                            points.Add(TmxMath.AddPoints(rowPos, oct[5]));
                        }
                        if (bottomRight)
                        {
                            points.Add(TmxMath.AddPoints(rowPos, oct[5]));
                            points.Add(TmxMath.AddPoints(rowPos, oct[6]));
                        }
                        if (bottomLeft)
                        {
                            points.Add(TmxMath.AddPoints(rowPos, oct[7]));
                            points.Add(TmxMath.AddPoints(rowPos, oct[0]));
                        }

                        rowPos.X += tileWidth + sideLengthX;
                    }

                    startPos.Y += rowHeight;
                }
            }

            foreach (var p in points)
            {
                RectangleF rc = new RectangleF(p.X, p.Y, Tiled2UnityViewer.GridSize, Tiled2UnityViewer.GridSize);
                rc.Offset(-Tiled2UnityViewer.GridSize * 0.5f, -Tiled2UnityViewer.GridSize * 0.5f);

                g.FillRectangle(Brushes.White, rc);
                g.DrawRectangle(Pens.Black, rc.X, rc.Y, rc.Width, rc.Height);
            }
        }
示例#5
0
        public static TmxLayer FromXml(XElement elem, TmxLayerNode parent, TmxMap tmxMap)
        {
            TmxLayer tmxLayer = new TmxLayer(parent, tmxMap);

            tmxLayer.FromXmlInternal(elem);

            // We can build a layer from a "tile layer" (default) or an "image layer"
            if (elem.Name == "layer")
            {
                tmxLayer.Width  = TmxHelper.GetAttributeAsInt(elem, "width");
                tmxLayer.Height = TmxHelper.GetAttributeAsInt(elem, "height");
                tmxLayer.ParseData(elem.Element("data"));
            }
            else if (elem.Name == "imagelayer")
            {
                XElement xmlImage = elem.Element("image");
                if (xmlImage == null)
                {
                    Logger.WriteWarning("Image Layer '{0}' is being ignored since it has no image.", tmxLayer.Name);
                    tmxLayer.Ignore = IgnoreSettings.True;
                    return(tmxLayer);
                }

                // An image layer is sort of like an tile layer but with just one tile
                tmxLayer.Width  = 1;
                tmxLayer.Height = 1;

                // Find the "tile" that matches our image
                string  imagePath = TmxHelper.GetAttributeAsFullPath(elem.Element("image"), "source");
                TmxTile tile      = tmxMap.Tiles.First(t => t.Value.TmxImage.AbsolutePath == imagePath).Value;
                tmxLayer.TileIds = new uint[1] {
                    tile.GlobalId
                };

                // The image layer needs to be tranlated in an interesting way when expressed as a tile layer
                PointF translated = tmxLayer.Offset;

                // Make up for height of a regular tile in the map
                translated.Y -= (float)tmxMap.TileHeight;

                // Make up for the height of this image
                translated.Y += (float)tile.TmxImage.Size.Height;

                // Correct for any orientation effects on the map (like isometric)
                // (We essentially undo the translation via orientation here)
                PointF orientation = TmxMath.TileCornerInScreenCoordinates(tmxMap, 0, 0);
                translated.X -= orientation.X;
                translated.Y -= orientation.Y;

                // Translate by the x and y coordiantes
                translated.X   += TmxHelper.GetAttributeAsFloat(elem, "x", 0);
                translated.Y   += TmxHelper.GetAttributeAsFloat(elem, "y", 0);
                tmxLayer.Offset = translated;
            }

            // Sometimes TMX files have "dead" tiles in them (tiles that were removed but are still referenced)
            // Remove these tiles from the layer by replacing them with zero
            for (int t = 0; t < tmxLayer.TileIds.Length; ++t)
            {
                uint tileId = tmxLayer.TileIds[t];
                tileId = TmxMath.GetTileIdWithoutFlags(tileId);
                if (!tmxMap.Tiles.ContainsKey(tileId))
                {
                    tmxLayer.TileIds[t] = 0;
                }
            }

            // Each layer will be broken down into "meshes" which are collections of tiles matching the same texture or animation
            tmxLayer.Meshes = TmxMesh.ListFromTmxLayer(tmxLayer);

            // Each layer may contain different collision types which are themselves put into "Collison Layers" to be processed later
            tmxLayer.BuildCollisionLayers();

            return(tmxLayer);
        }
示例#6
0
 public Point GetMapPositionAt(int x, int y)
 {
     return(TmxMath.TileCornerInScreenCoordinates(this, x, y));
 }
示例#7
0
        public static TmxLayer FromXml(XElement elem, TmxMap tmxMap)
        {
            Program.WriteVerbose(elem.ToString());
            TmxLayer tmxLayer = new TmxLayer(tmxMap);

            // Order within Xml file is import for layer types
            tmxLayer.XmlElementIndex = elem.NodesBeforeSelf().Count();

            // Have to decorate layer names in order to force them into being unique
            // Also, can't have whitespace in the name because Unity will add underscores
            tmxLayer.Name = TmxHelper.GetAttributeAsString(elem, "name");

            tmxLayer.Visible = TmxHelper.GetAttributeAsInt(elem, "visible", 1) == 1;
            tmxLayer.Opacity = TmxHelper.GetAttributeAsFloat(elem, "opacity", 1);

            PointF offset = new PointF(0, 0);

            offset.X        = TmxHelper.GetAttributeAsFloat(elem, "offsetx", 0);
            offset.Y        = TmxHelper.GetAttributeAsFloat(elem, "offsety", 0);
            tmxLayer.Offset = offset;

            // Set our properties
            tmxLayer.Properties = TmxProperties.FromXml(elem);

            // Set the "ignore" setting on this layer
            tmxLayer.Ignore = tmxLayer.Properties.GetPropertyValueAsEnum <IgnoreSettings>("unity:ignore", IgnoreSettings.False);

            // We can build a layer from a "tile layer" (default) or an "image layer"
            if (elem.Name == "layer")
            {
                tmxLayer.Width  = TmxHelper.GetAttributeAsInt(elem, "width");
                tmxLayer.Height = TmxHelper.GetAttributeAsInt(elem, "height");
                tmxLayer.ParseData(elem.Element("data"));
            }
            else if (elem.Name == "imagelayer")
            {
                XElement xmlImage = elem.Element("image");
                if (xmlImage == null)
                {
                    Program.WriteWarning("Image Layer '{0}' is being ignored since it has no image.", tmxLayer.Name);
                    tmxLayer.Ignore = IgnoreSettings.True;
                    return(tmxLayer);
                }

                // An image layer is sort of like an tile layer but with just one tile
                tmxLayer.Width  = 1;
                tmxLayer.Height = 1;

                // Find the "tile" that matches our image
                string  imagePath = TmxHelper.GetAttributeAsFullPath(elem.Element("image"), "source");
                TmxTile tile      = tmxMap.Tiles.First(t => t.Value.TmxImage.AbsolutePath == imagePath).Value;
                tmxLayer.TileIds = new uint[1] {
                    tile.GlobalId
                };

                // The image layer needs to be tranlated in an interesting way when expressed as a tile layer
                PointF translated = tmxLayer.Offset;

                // Make up for height of a regular tile in the map
                translated.Y -= (float)tmxMap.TileHeight;

                // Make up for the height of this image
                translated.Y += (float)tile.TmxImage.Size.Height;

                // Correct for any orientation effects on the map (like isometric)
                // (We essentially undo the translation via orientation here)
                PointF orientation = TmxMath.TileCornerInScreenCoordinates(tmxMap, 0, 0);
                translated.X -= orientation.X;
                translated.Y -= orientation.Y;

                // Translate by the x and y coordiantes
                translated.X   += TmxHelper.GetAttributeAsFloat(elem, "x", 0);
                translated.Y   += TmxHelper.GetAttributeAsFloat(elem, "y", 0);
                tmxLayer.Offset = translated;
            }

            // Each layer will be broken down into "meshes" which are collections of tiles matching the same texture or animation
            tmxLayer.Meshes = TmxMesh.ListFromTmxLayer(tmxLayer);

            return(tmxLayer);
        }
示例#8
0
        private void DrawTiles(Graphics g)
        {
            foreach (TmxLayer layer in this.tmxMap.Layers)
            {
                if (layer.Visible == false)
                {
                    continue;
                }

                if (layer.Ignore == TmxLayer.IgnoreSettings.Visual)
                {
                    continue;
                }

                // Set the opacity for the layer
                ColorMatrix colorMatrix = new ColorMatrix();
                colorMatrix.Matrix33 = layer.Opacity;

                ImageAttributes imageAttributes = new ImageAttributes();
                imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

                // Translate by the offset
                GraphicsState state = g.Save();
                g.TranslateTransform(layer.Offset.X, layer.Offset.Y);

                // The range of x and y depends on the render order of the tiles
                // By default we draw right and down but may reverse the tiles we visit
                var range_x = Enumerable.Range(0, GetMaxTilesWide(layer));
                var range_y = Enumerable.Range(0, GetMaxTilesHigh(layer));

                if (this.tmxMap.DrawOrderHorizontal == -1)
                {
                    range_x = range_x.Reverse();
                }

                if (this.tmxMap.DrawOrderVertical == -1)
                {
                    range_y = range_y.Reverse();
                }

                // Visit the tiles we are going to draw
                var tiles = from y in range_y
                            from x in range_x
                            let rawTileId = layer.GetRawTileIdAt(x, y)
                                            let tileId = layer.GetTileIdAt(x, y)
                                                         where tileId != 0

                                                         let tile = this.tmxMap.Tiles[tileId]

                                                                    // Support for animated tiles. Just show the first frame of the animation.
                                                                    let frame = this.tmxMap.Tiles[tile.Animation.Frames[0].GlobalTileId]

                                                                                select new
                {
                    Tile                  = frame,
                    Position              = TmxMath.TileCornerInScreenCoordinates(this.tmxMap, x, y),
                    Bitmap                = frame.TmxImage.ImageBitmap,
                    IsFlippedDiagnoally   = TmxMath.IsTileFlippedDiagonally(rawTileId),
                    IsFlippedHorizontally = TmxMath.IsTileFlippedHorizontally(rawTileId),
                    IsFlippedVertically   = TmxMath.IsTileFlippedVertically(rawTileId),
                };

                PointF[] destPoints  = new PointF[4];
                PointF[] destPoints3 = new PointF[3];
                foreach (var t in tiles)
                {
                    PointF location = t.Position;

                    // Individual tiles may be larger than the given tile size of the overall map
                    location.Y = (t.Position.Y - t.Tile.TileSize.Height) + this.tmxMap.TileHeight;

                    // Take tile offset into account
                    location.X += t.Tile.Offset.X;
                    location.Y += t.Tile.Offset.Y;

                    // Make up the 'quad' of texture points and transform them
                    PointF center = new PointF(t.Tile.TileSize.Width * 0.5f, t.Tile.TileSize.Height * 0.5f);
                    destPoints[0] = new Point(0, 0);
                    destPoints[1] = new Point(t.Tile.TileSize.Width, 0);
                    destPoints[2] = new Point(t.Tile.TileSize.Width, t.Tile.TileSize.Height);
                    destPoints[3] = new Point(0, t.Tile.TileSize.Height);

                    // Transform the points based on our flipped flags
                    TmxMath.TransformPoints(destPoints, center, t.IsFlippedDiagnoally, t.IsFlippedHorizontally, t.IsFlippedVertically);

                    // Put the destination points back into world space
                    TmxMath.TranslatePoints(destPoints, location);

                    // Stupid DrawImage function only takes 3 destination points otherwise it throws an exception
                    destPoints3[0] = destPoints[0];
                    destPoints3[1] = destPoints[1];
                    destPoints3[2] = destPoints[3];

                    // Draw the tile
                    Rectangle source = new Rectangle(t.Tile.LocationOnSource, t.Tile.TileSize);
                    g.DrawImage(t.Bitmap, destPoints3, source, GraphicsUnit.Pixel, imageAttributes);
                }

                g.Restore(state);
            }
        }