// ==============================================================================
  public final int[] renderGlyph(
      char glyph, Paint paint, android.graphics.Matrix matrix, Rect bounds) {
    Path p = new Path();
    paint.getTextPath(String.valueOf(glyph), 0, 1, 0.0f, 0.0f, p);

    RectF boundsF = new RectF();
    p.computeBounds(boundsF, true);
    matrix.mapRect(boundsF);

    boundsF.roundOut(bounds);
    bounds.left--;
    bounds.right++;

    final int w = bounds.width();
    final int h = bounds.height();

    Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);

    Canvas c = new Canvas(bm);
    matrix.postTranslate(-bounds.left, -bounds.top);
    c.setMatrix(matrix);
    c.drawPath(p, paint);

    final int sizeNeeded = w * h;
    if (cachedRenderArray.length < sizeNeeded) cachedRenderArray = new int[sizeNeeded];

    bm.getPixels(cachedRenderArray, 0, w, 0, 0, w, h);
    bm.recycle();
    return cachedRenderArray;
  }
    private Rect rectToPx(RectF rect) {
      rect.roundOut(mPixelRect);
      mPixelRect.left = (int) (mPixelRect.left * mDpToPx);
      mPixelRect.top = (int) (mPixelRect.top * mDpToPx);
      mPixelRect.right = (int) (mPixelRect.right * mDpToPx);
      mPixelRect.bottom = (int) (mPixelRect.bottom * mDpToPx);

      // Don't let any zero sized rects through, they'll cause parent
      // size errors in L.
      if (mPixelRect.width() == 0) {
        mPixelRect.right = mPixelRect.left + 1;
      }
      if (mPixelRect.height() == 0) {
        mPixelRect.bottom = mPixelRect.top + 1;
      }
      return mPixelRect;
    }
  /**
   * Draws an icon in the destination bitmap. If scale is set the source image is stretched to fit
   * within the destination dimensions; otherwise, the source image is cropped to the proper aspect
   * ratio.
   *
   * @param dest bitmap into which to draw the icon.
   * @param sourceImage image to create an icon from.
   * @param scale if true, stretch sourceImage to fit the destination.
   */
  public static void drawIcon(Bitmap dest, Bitmap sourceImage, boolean scale) {
    if (dest == null || sourceImage == null) {
      throw new IllegalArgumentException("Null argument to buildIcon");
    }

    int sourceWidth = sourceImage.getWidth();
    int sourceHeight = sourceImage.getHeight();
    int iconWidth = dest.getWidth();
    int iconHeight = dest.getHeight();

    if (sourceWidth == 0 || sourceHeight == 0 || iconWidth == 0 || iconHeight == 0) {
      throw new IllegalArgumentException("Bitmap with dimension 0 used as input");
    }

    Rect destRect = new Rect(0, 0, iconWidth, iconHeight);
    Canvas canvas = new Canvas(dest);

    Rect srcRect = null;
    if (scale) {
      // scale image to fit in icon (stretches if aspect isn't the same)
      srcRect = new Rect(0, 0, sourceWidth, sourceHeight);
    } else {
      // crop image to aspect ratio iconWidth:iconHeight
      float wScale = sourceWidth / (float) iconWidth;
      float hScale = sourceHeight / (float) iconHeight;
      float s = Math.min(hScale, wScale);

      float iw = iconWidth * s;
      float ih = iconHeight * s;

      float borderW = (sourceWidth - iw) / 2.0f;
      float borderH = (sourceHeight - ih) / 2.0f;
      RectF rec = new RectF(borderW, borderH, borderW + iw, borderH + ih);
      srcRect = new Rect();
      rec.roundOut(srcRect);
    }

    canvas.drawBitmap(sourceImage, srcRect, destRect, new Paint());
  }
    public boolean cropBitmap() {
      boolean failure = false;

      WallpaperManager wallpaperManager = null;
      if (mSetWallpaper) {
        wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext());
      }

      if (mSetWallpaper && mNoCrop) {
        try {
          InputStream is = regenerateInputStream();
          if (is != null) {
            wallpaperManager.setStream(is);
            Utils.closeSilently(is);
          }
        } catch (IOException e) {
          Log.w(LOGTAG, "cannot write stream to wallpaper", e);
          failure = true;
        }
        return !failure;
      } else {
        // Find crop bounds (scaled to original image size)
        Rect roundedTrueCrop = new Rect();
        Matrix rotateMatrix = new Matrix();
        Matrix inverseRotateMatrix = new Matrix();

        Point bounds = getImageBounds();
        if (mRotation > 0) {
          rotateMatrix.setRotate(mRotation);
          inverseRotateMatrix.setRotate(-mRotation);

          mCropBounds.roundOut(roundedTrueCrop);
          mCropBounds = new RectF(roundedTrueCrop);

          if (bounds == null) {
            Log.w(LOGTAG, "cannot get bounds for image");
            failure = true;
            return false;
          }

          float[] rotatedBounds = new float[] {bounds.x, bounds.y};
          rotateMatrix.mapPoints(rotatedBounds);
          rotatedBounds[0] = Math.abs(rotatedBounds[0]);
          rotatedBounds[1] = Math.abs(rotatedBounds[1]);

          mCropBounds.offset(-rotatedBounds[0] / 2, -rotatedBounds[1] / 2);
          inverseRotateMatrix.mapRect(mCropBounds);
          mCropBounds.offset(bounds.x / 2, bounds.y / 2);
        }

        mCropBounds.roundOut(roundedTrueCrop);

        if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) {
          Log.w(LOGTAG, "crop has bad values for full size image");
          failure = true;
          return false;
        }

        // See how much we're reducing the size of the image
        int scaleDownSampleSize =
            Math.max(
                1,
                Math.min(
                    roundedTrueCrop.width() / mOutWidth, roundedTrueCrop.height() / mOutHeight));
        // Attempt to open a region decoder
        BitmapRegionDecoder decoder = null;
        InputStream is = null;
        try {
          is = regenerateInputStream();
          if (is == null) {
            Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString());
            failure = true;
            return false;
          }
          decoder = BitmapRegionDecoder.newInstance(is, false);
          Utils.closeSilently(is);
        } catch (IOException e) {
          Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e);
        } finally {
          Utils.closeSilently(is);
          is = null;
        }

        Bitmap crop = null;
        if (decoder != null) {
          // Do region decoding to get crop bitmap
          BitmapFactory.Options options = new BitmapFactory.Options();
          if (scaleDownSampleSize > 1) {
            options.inSampleSize = scaleDownSampleSize;
          }
          crop = decoder.decodeRegion(roundedTrueCrop, options);
          decoder.recycle();
        }

        if (crop == null) {
          // BitmapRegionDecoder has failed, try to crop in-memory
          is = regenerateInputStream();
          Bitmap fullSize = null;
          if (is != null) {
            BitmapFactory.Options options = new BitmapFactory.Options();
            if (scaleDownSampleSize > 1) {
              options.inSampleSize = scaleDownSampleSize;
            }
            fullSize = BitmapFactory.decodeStream(is, null, options);
            Utils.closeSilently(is);
          }
          if (fullSize != null) {
            // Find out the true sample size that was used by the decoder
            scaleDownSampleSize = bounds.x / fullSize.getWidth();
            mCropBounds.left /= scaleDownSampleSize;
            mCropBounds.top /= scaleDownSampleSize;
            mCropBounds.bottom /= scaleDownSampleSize;
            mCropBounds.right /= scaleDownSampleSize;
            mCropBounds.roundOut(roundedTrueCrop);

            // Adjust values to account for issues related to rounding
            if (roundedTrueCrop.width() > fullSize.getWidth()) {
              // Adjust the width
              roundedTrueCrop.right = roundedTrueCrop.left + fullSize.getWidth();
            }
            if (roundedTrueCrop.right > fullSize.getWidth()) {
              // Adjust the left value
              int adjustment =
                  roundedTrueCrop.left
                      - Math.max(0, roundedTrueCrop.right - roundedTrueCrop.width());
              roundedTrueCrop.left -= adjustment;
              roundedTrueCrop.right -= adjustment;
            }
            if (roundedTrueCrop.height() > fullSize.getHeight()) {
              // Adjust the height
              roundedTrueCrop.bottom = roundedTrueCrop.top + fullSize.getHeight();
            }
            if (roundedTrueCrop.bottom > fullSize.getHeight()) {
              // Adjust the top value
              int adjustment =
                  roundedTrueCrop.top
                      - Math.max(0, roundedTrueCrop.bottom - roundedTrueCrop.height());
              roundedTrueCrop.top -= adjustment;
              roundedTrueCrop.bottom -= adjustment;
            }

            crop =
                Bitmap.createBitmap(
                    fullSize,
                    roundedTrueCrop.left,
                    roundedTrueCrop.top,
                    roundedTrueCrop.width(),
                    roundedTrueCrop.height());
          }
        }

        if (crop == null) {
          Log.w(LOGTAG, "cannot decode file: " + mInUri.toString());
          failure = true;
          return false;
        }
        if (mOutWidth > 0 && mOutHeight > 0 || mRotation > 0) {
          float[] dimsAfter = new float[] {crop.getWidth(), crop.getHeight()};
          rotateMatrix.mapPoints(dimsAfter);
          dimsAfter[0] = Math.abs(dimsAfter[0]);
          dimsAfter[1] = Math.abs(dimsAfter[1]);

          if (!(mOutWidth > 0 && mOutHeight > 0)) {
            mOutWidth = Math.round(dimsAfter[0]);
            mOutHeight = Math.round(dimsAfter[1]);
          }

          RectF cropRect = new RectF(0, 0, dimsAfter[0], dimsAfter[1]);
          RectF returnRect = new RectF(0, 0, mOutWidth, mOutHeight);

          Matrix m = new Matrix();
          if (mRotation == 0) {
            m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
          } else {
            Matrix m1 = new Matrix();
            m1.setTranslate(-crop.getWidth() / 2f, -crop.getHeight() / 2f);
            Matrix m2 = new Matrix();
            m2.setRotate(mRotation);
            Matrix m3 = new Matrix();
            m3.setTranslate(dimsAfter[0] / 2f, dimsAfter[1] / 2f);
            Matrix m4 = new Matrix();
            m4.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);

            Matrix c1 = new Matrix();
            c1.setConcat(m2, m1);
            Matrix c2 = new Matrix();
            c2.setConcat(m4, m3);
            m.setConcat(c2, c1);
          }

          Bitmap tmp =
              Bitmap.createBitmap(
                  (int) returnRect.width(), (int) returnRect.height(), Bitmap.Config.ARGB_8888);
          if (tmp != null) {
            Canvas c = new Canvas(tmp);
            Paint p = new Paint();
            p.setFilterBitmap(true);
            c.drawBitmap(crop, m, p);
            crop = tmp;
          }
        }

        if (mSaveCroppedBitmap) {
          mCroppedBitmap = crop;
        }

        // Get output compression format
        CompressFormat cf = convertExtensionToCompressFormat(getFileExtension(mOutputFormat));

        // Compress to byte array
        ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048);
        if (crop.compress(cf, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
          // If we need to set to the wallpaper, set it
          if (mSetWallpaper && wallpaperManager != null) {
            try {
              byte[] outByteArray = tmpOut.toByteArray();
              wallpaperManager.setStream(new ByteArrayInputStream(outByteArray));
              if (mOnBitmapCroppedHandler != null) {
                mOnBitmapCroppedHandler.onBitmapCropped(outByteArray);
              }
            } catch (IOException e) {
              Log.w(LOGTAG, "cannot write stream to wallpaper", e);
              failure = true;
            }
          }
        } else {
          Log.w(LOGTAG, "cannot compress bitmap");
          failure = true;
        }
      }
      return !failure; // True if any of the operations failed
    }