content: /*
   * Copyright (C) 2007 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.graphics.drawable.shapes;
  
  import android.annotation.Nullable;
  import android.graphics.Canvas;
  import android.graphics.Outline;
  import android.graphics.Paint;
  import android.graphics.Path;
  import android.graphics.RectF;
  
  import java.util.Arrays;
  import java.util.Objects;
  
  /**
   * Creates a rounded-corner rectangle. Optionally, an inset (rounded) rectangle
   * can be included (to make a sort of "O" shape).
   * <p>
   * The rounded rectangle can be drawn to a Canvas with its own draw() method,
   * but more graphical control is available if you instead pass
   * the RoundRectShape to a {@link android.graphics.drawable.ShapeDrawable}.
   */
  public class RoundRectShape extends RectShape {
      private float[] mOuterRadii;
      private RectF mInset;
      private float[] mInnerRadii;
      
      private RectF mInnerRect;
      private Path mPath; // this is what we actually draw
      
      /**
       * RoundRectShape constructor.
       * <p>
       * Specifies an outer (round)rect and an optional inner (round)rect.
       *
       * @param outerRadii An array of 8 radius values, for the outer roundrect. 
       *                   The first two floats are for the top-left corner
       *                   (remaining pairs correspond clockwise). For no rounded
       *                   corners on the outer rectangle, pass {@code null}.
       * @param inset A RectF that specifies the distance from the inner
       *              rect to each side of the outer rect. For no inner, pass
       *              {@code null}.
       * @param innerRadii An array of 8 radius values, for the inner roundrect.
       *                   The first two floats are for the top-left corner
       *                   (remaining pairs correspond clockwise). For no rounded
       *                   corners on the inner rectangle, pass {@code null}. If
       *                   inset parameter is {@code null}, this parameter is
       *                   ignored.
       */
      public RoundRectShape(@Nullable float[] outerRadii, @Nullable RectF inset,
              @Nullable float[] innerRadii) {
          if (outerRadii != null && outerRadii.length < 8) {
              throw new ArrayIndexOutOfBoundsException("outer radii must have >= 8 values");
          }
          if (innerRadii != null && innerRadii.length < 8) {
              throw new ArrayIndexOutOfBoundsException("inner radii must have >= 8 values");
          }
          mOuterRadii = outerRadii;
          mInset = inset;
          mInnerRadii = innerRadii;
          
          if (inset != null) {
              mInnerRect = new RectF();
          }
          mPath = new Path();
      }
      
      @Override
      public void draw(Canvas canvas, Paint paint) {
          canvas.drawPath(mPath, paint);
      }
  
      @Override
      public void getOutline(Outline outline) {
          if (mInnerRect != null) return; // have a hole, can't produce valid outline
  
          float radius = 0;
          if (mOuterRadii != null) {
              radius = mOuterRadii[0];
              for (int i = 1; i < 8; i++) {
                  if (mOuterRadii[i] != radius) {
                      // can't call simple constructors, use path
                      outline.setConvexPath(mPath);
                      return;
                  }
              }
          }
  
          final RectF rect = rect();
          outline.setRoundRect((int) Math.ceil(rect.left), (int) Math.ceil(rect.top),
                  (int) Math.floor(rect.right), (int) Math.floor(rect.bottom), radius);
      }
  
      @Override
      protected void onResize(float w, float h) {
          super.onResize(w, h);
  
          RectF r = rect();
          mPath.reset();
  
          if (mOuterRadii != null) {
              mPath.addRoundRect(r, mOuterRadii, Path.Direction.CW);
          } else {
              mPath.addRect(r, Path.Direction.CW);
          }
          if (mInnerRect != null) {
              mInnerRect.set(r.left + mInset.left, r.top + mInset.top,
                             r.right - mInset.right, r.bottom - mInset.bottom);
              if (mInnerRect.width() < w && mInnerRect.height() < h) {
                  if (mInnerRadii != null) {
                      mPath.addRoundRect(mInnerRect, mInnerRadii, Path.Direction.CCW);
                  } else {
                      mPath.addRect(mInnerRect, Path.Direction.CCW);
                  }
              }
          }
      }
  
      @Override
      public RoundRectShape clone() throws CloneNotSupportedException {
          final RoundRectShape shape = (RoundRectShape) super.clone();
          shape.mOuterRadii = mOuterRadii != null ? mOuterRadii.clone() : null;
          shape.mInnerRadii = mInnerRadii != null ? mInnerRadii.clone() : null;
          shape.mInset = new RectF(mInset);
          shape.mInnerRect = new RectF(mInnerRect);
          shape.mPath = new Path(mPath);
          return shape;
      }
  
      @Override
      public boolean equals(Object o) {
          if (this == o) {
              return true;
          }
          if (o == null || getClass() != o.getClass()) {
              return false;
          }
          if (!super.equals(o)) {
              return false;
          }
          RoundRectShape that = (RoundRectShape) o;
          return Arrays.equals(mOuterRadii, that.mOuterRadii)
              && Objects.equals(mInset, that.mInset)
              && Arrays.equals(mInnerRadii, that.mInnerRadii)
              && Objects.equals(mInnerRect, that.mInnerRect)
              && Objects.equals(mPath, that.mPath);
      }
  
      @Override
      public int hashCode() {
          int result = Objects.hash(super.hashCode(), mInset, mInnerRect, mPath);
          result = 31 * result + Arrays.hashCode(mOuterRadii);
          result = 31 * result + Arrays.hashCode(mInnerRadii);
          return result;
      }
  }
  