@SuppressWarnings("null")
  public void draw(PolygonSpriteBatch batch, Skeleton skeleton) {
    boolean premultipliedAlpha = this.premultipliedAlpha;
    BlendMode blendMode = null;

    float[] vertices = null;
    short[] triangles = null;
    Array<Slot> drawOrder = skeleton.drawOrder;
    for (int i = 0, n = drawOrder.size; i < n; i++) {
      Slot slot = drawOrder.get(i);
      Attachment attachment = slot.attachment;
      Texture texture = null;
      if (attachment instanceof RegionAttachment) {
        RegionAttachment region = (RegionAttachment) attachment;
        vertices = region.updateWorldVertices(slot, premultipliedAlpha);
        triangles = quadTriangles;
        texture = region.getRegion().getTexture();

      } else if (attachment instanceof MeshAttachment) {
        MeshAttachment mesh = (MeshAttachment) attachment;
        vertices = mesh.updateWorldVertices(slot, premultipliedAlpha);
        triangles = mesh.getTriangles();
        texture = mesh.getRegion().getTexture();

      } else if (attachment instanceof SkeletonAttachment) {
        Skeleton attachmentSkeleton = ((SkeletonAttachment) attachment).getSkeleton();
        if (attachmentSkeleton == null) continue;
        Bone bone = slot.getBone();
        Bone rootBone = attachmentSkeleton.getRootBone();
        float oldScaleX = rootBone.getScaleX();
        float oldScaleY = rootBone.getScaleY();
        float oldRotation = rootBone.getRotation();
        attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY());
        // rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX);
        // rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY);
        // Set shear.
        rootBone.setRotation(oldRotation + bone.getWorldRotationX());
        attachmentSkeleton.updateWorldTransform();

        draw(batch, attachmentSkeleton);

        attachmentSkeleton.setPosition(0, 0);
        rootBone.setScaleX(oldScaleX);
        rootBone.setScaleY(oldScaleY);
        rootBone.setRotation(oldRotation);
      }

      if (texture != null) {
        BlendMode slotBlendMode = slot.data.getBlendMode();
        if (slotBlendMode != blendMode) {
          blendMode = slotBlendMode;
          batch.setBlendFunction(blendMode.getSource(premultipliedAlpha), blendMode.getDest());
        }
        batch.draw(texture, vertices, 0, vertices.length, triangles, 0, triangles.length);
      }
    }
  }
  /**
   * Returns the axis aligned bounding box (AABB) of the region, mesh, and skinned mesh attachments
   * for the current pose.
   *
   * @param offset The distance from the skeleton origin to the bottom left corner of the AABB.
   * @param size The width and height of the AABB.
   */
  public void getBounds(Vector2 offset, Vector2 size) {
    Array<Slot> drawOrder = this.drawOrder;
    float minX = Integer.MAX_VALUE,
        minY = Integer.MAX_VALUE,
        maxX = Integer.MIN_VALUE,
        maxY = Integer.MIN_VALUE;
    for (int i = 0, n = drawOrder.size; i < n; i++) {
      Slot slot = drawOrder.get(i);
      float[] vertices = null;
      Attachment attachment = slot.attachment;
      if (attachment instanceof RegionAttachment) {
        RegionAttachment region = (RegionAttachment) attachment;
        region.updateWorldVertices(slot, false);
        vertices = region.getWorldVertices();

      } else if (attachment instanceof MeshAttachment) {
        MeshAttachment mesh = (MeshAttachment) attachment;
        mesh.updateWorldVertices(slot, true);
        vertices = mesh.getWorldVertices();

      } else if (attachment instanceof SkinnedMeshAttachment) {
        SkinnedMeshAttachment mesh = (SkinnedMeshAttachment) attachment;
        mesh.updateWorldVertices(slot, true);
        vertices = mesh.getWorldVertices();
      }
      if (vertices != null) {
        for (int ii = 0, nn = vertices.length; ii < nn; ii += 5) {
          float x = vertices[ii], y = vertices[ii + 1];
          minX = Math.min(minX, x);
          minY = Math.min(minY, y);
          maxX = Math.max(maxX, x);
          maxY = Math.max(maxY, y);
        }
      }
    }
    offset.set(minX, minY);
    size.set(maxX - minX, maxY - minY);
  }
  private Attachment readAttachment(
      DataInput input, Skin skin, String attachmentName, boolean nonessential) throws IOException {
    float scale = this.scale;

    String name = input.readString();
    if (name == null) name = attachmentName;

    switch (AttachmentType.values()[input.readByte()]) {
      case region:
        {
          String path = input.readString();
          if (path == null) path = name;
          RegionAttachment region = attachmentLoader.newRegionAttachment(skin, name, path);
          if (region == null) return null;
          region.setPath(path);
          region.setX(input.readFloat() * scale);
          region.setY(input.readFloat() * scale);
          region.setScaleX(input.readFloat());
          region.setScaleY(input.readFloat());
          region.setRotation(input.readFloat());
          region.setWidth(input.readFloat() * scale);
          region.setHeight(input.readFloat() * scale);
          Color.rgba8888ToColor(region.getColor(), input.readInt());
          region.updateOffset();
          return region;
        }
      case boundingbox:
        {
          BoundingBoxAttachment box = attachmentLoader.newBoundingBoxAttachment(skin, name);
          if (box == null) return null;
          box.setVertices(readFloatArray(input, scale));
          return box;
        }
      case mesh:
        {
          String path = input.readString();
          if (path == null) path = name;
          MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, path);
          if (mesh == null) return null;
          mesh.setPath(path);
          float[] uvs = readFloatArray(input, 1);
          short[] triangles = readShortArray(input);
          float[] vertices = readFloatArray(input, scale);
          mesh.setVertices(vertices);
          mesh.setTriangles(triangles);
          mesh.setRegionUVs(uvs);
          mesh.updateUVs();
          Color.rgba8888ToColor(mesh.getColor(), input.readInt());
          mesh.setHullLength(input.readInt(true) * 2);
          if (nonessential) {
            mesh.setEdges(readIntArray(input));
            mesh.setWidth(input.readFloat() * scale);
            mesh.setHeight(input.readFloat() * scale);
          }
          return mesh;
        }
      case skinnedmesh:
        {
          String path = input.readString();
          if (path == null) path = name;
          SkinnedMeshAttachment mesh = attachmentLoader.newSkinnedMeshAttachment(skin, name, path);
          if (mesh == null) return null;
          mesh.setPath(path);
          float[] uvs = readFloatArray(input, 1);
          short[] triangles = readShortArray(input);

          int vertexCount = input.readInt(true);
          FloatArray weights = new FloatArray(uvs.length * 3 * 3);
          IntArray bones = new IntArray(uvs.length * 3);
          for (int i = 0; i < vertexCount; i++) {
            int boneCount = (int) input.readFloat();
            bones.add(boneCount);
            for (int nn = i + boneCount * 4; i < nn; i += 4) {
              bones.add((int) input.readFloat());
              weights.add(input.readFloat() * scale);
              weights.add(input.readFloat() * scale);
              weights.add(input.readFloat());
            }
          }
          mesh.setBones(bones.toArray());
          mesh.setWeights(weights.toArray());
          mesh.setTriangles(triangles);
          mesh.setRegionUVs(uvs);
          mesh.updateUVs();
          Color.rgba8888ToColor(mesh.getColor(), input.readInt());
          mesh.setHullLength(input.readInt(true) * 2);
          if (nonessential) {
            mesh.setEdges(readIntArray(input));
            mesh.setWidth(input.readFloat() * scale);
            mesh.setHeight(input.readFloat() * scale);
          }
          return mesh;
        }
    }
    return null;
  }