content: /*
   * Copyright (C) 2010 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;
  
  import com.android.ide.common.rendering.api.AssetRepository;
  import com.android.ide.common.rendering.api.LayoutLog;
  import com.android.ide.common.rendering.api.RenderResources;
  import com.android.ide.common.rendering.api.ResourceValue;
  import com.android.layoutlib.bridge.Bridge;
  import com.android.layoutlib.bridge.android.BridgeContext;
  import com.android.layoutlib.bridge.impl.DelegateManager;
  import com.android.layoutlib.bridge.impl.RenderAction;
  import com.android.resources.Density;
  import com.android.resources.ResourceType;
  import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
  
  import android.annotation.Nullable;
  import android.graphics.Bitmap.Config;
  import android.hardware.HardwareBuffer;
  import android.os.Parcel;
  
  import java.awt.Graphics2D;
  import java.awt.image.BufferedImage;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.nio.Buffer;
  import java.util.Arrays;
  import java.util.EnumSet;
  import java.util.Set;
  
  import javax.imageio.ImageIO;
  import libcore.util.NativeAllocationRegistry_Delegate;
  
  import static android.content.res.AssetManager.ACCESS_STREAMING;
  
  /**
   * Delegate implementing the native methods of android.graphics.Bitmap
   *
   * Through the layoutlib_create tool, the original native methods of Bitmap have been replaced
   * by calls to methods of the same name in this delegate class.
   *
   * This class behaves like the original native implementation, but in Java, keeping previously
   * native data into its own objects and mapping them to int that are sent back and forth between
   * it and the original Bitmap class.
   *
   * @see DelegateManager
   *
   */
  public final class Bitmap_Delegate {
  
      public enum BitmapCreateFlags {
          NONE, PREMULTIPLIED, MUTABLE
      }
  
      // ---- delegate manager ----
      private static final DelegateManager<Bitmap_Delegate> sManager =
              new DelegateManager<>(Bitmap_Delegate.class);
      private static long sFinalizer = -1;
  
      // ---- delegate helper data ----
  
      // ---- delegate data ----
      private final Config mConfig;
      private final BufferedImage mImage;
      private boolean mHasAlpha = true;
      private boolean mHasMipMap = false;      // TODO: check the default.
      private boolean mIsPremultiplied = true;
      private int mGenerationId = 0;
      private boolean mIsMutable;
  
  
      // ---- Public Helper methods ----
  
      /**
       * Returns the native delegate associated to a given an int referencing a {@link Bitmap} object.
       */
      public static Bitmap_Delegate getDelegate(long native_bitmap) {
          return sManager.getDelegate(native_bitmap);
      }
  
      /**
       * Creates and returns a {@link Bitmap} initialized with the given stream content.
       *
       * @param input the stream from which to read the bitmap content
       * @param isMutable whether the bitmap is mutable
       * @param density the density associated with the bitmap
       *
       * @see Bitmap#isMutable()
       * @see Bitmap#getDensity()
       */
      public static Bitmap createBitmap(@Nullable InputStream input, boolean isMutable,
              Density density) throws IOException {
          return createBitmap(input, getPremultipliedBitmapCreateFlags(isMutable), density);
      }
  
      /**
       * Creates and returns a {@link Bitmap} initialized with the given file content.
       *
       * @param input the file from which to read the bitmap content
       * @param density the density associated with the bitmap
       *
       * @see Bitmap#isPremultiplied()
       * @see Bitmap#isMutable()
       * @see Bitmap#getDensity()
       */
      static Bitmap createBitmap(@Nullable InputStream input, Set<BitmapCreateFlags> createFlags,
              Density density) throws IOException {
          // create a delegate with the content of the file.
          BufferedImage image = input == null ? null : ImageIO.read(input);
          if (image == null) {
              // There was a problem decoding the image, or the decoder isn't registered. Webp maybe.
              // Replace with a broken image icon.
              BridgeContext currentContext = RenderAction.getCurrentContext();
              if (currentContext != null) {
                  RenderResources resources = currentContext.getRenderResources();
                  ResourceValue broken = resources.getResolvedResource(
                          BridgeContext.createFrameworkResourceReference(
                                  ResourceType.DRAWABLE, "ic_menu_report_image"));
                  AssetRepository assetRepository = currentContext.getAssets().getAssetRepository();
                  try (InputStream stream =
                          assetRepository.openNonAsset(0, broken.getValue(), ACCESS_STREAMING)) {
                      if (stream != null) {
                          image = ImageIO.read(stream);
                      }
                  }
              }
          }
          Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888);
          delegate.mIsMutable = createFlags.contains(BitmapCreateFlags.MUTABLE);
  
          return createBitmap(delegate, createFlags, density.getDpiValue());
      }
  
      /**
       * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
       *
       * @param image the bitmap content
       * @param isMutable whether the bitmap is mutable
       * @param density the density associated with the bitmap
       *
       * @see Bitmap#isMutable()
       * @see Bitmap#getDensity()
       */
      public static Bitmap createBitmap(BufferedImage image, boolean isMutable, Density density) {
          return createBitmap(image, getPremultipliedBitmapCreateFlags(isMutable), density);
      }
  
      /**
       * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
       *
       * @param image the bitmap content
       * @param density the density associated with the bitmap
       *
       * @see Bitmap#isPremultiplied()
       * @see Bitmap#isMutable()
       * @see Bitmap#getDensity()
       */
      public static Bitmap createBitmap(BufferedImage image, Set<BitmapCreateFlags> createFlags,
              Density density) {
          // create a delegate with the given image.
          Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888);
          delegate.mIsMutable = createFlags.contains(BitmapCreateFlags.MUTABLE);
  
          return createBitmap(delegate, createFlags, density.getDpiValue());
      }
  
      private static int getBufferedImageType() {
          return BufferedImage.TYPE_INT_ARGB;
      }
  
      /**
       * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}.
       */
      public BufferedImage getImage() {
          return mImage;
      }
  
      /**
       * Returns the Android bitmap config. Note that this not the config of the underlying
       * Java2D bitmap.
       */
      public Config getConfig() {
          return mConfig;
      }
  
      /**
       * Returns the hasAlpha rendering hint
       * @return true if the bitmap alpha should be used at render time
       */
      public boolean hasAlpha() {
          return mHasAlpha && mConfig != Config.RGB_565;
      }
  
      /**
       * Update the generationId.
       *
       * @see Bitmap#getGenerationId()
       */
      public void change() {
          mGenerationId++;
      }
  
      // ---- native methods ----
  
      @LayoutlibDelegate
      /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width,
              int height, int nativeConfig, boolean isMutable, long nativeColorSpace) {
          int imageType = getBufferedImageType();
  
          // create the image
          BufferedImage image = new BufferedImage(width, height, imageType);
  
          if (colors != null) {
              image.setRGB(0, 0, width, height, colors, offset, stride);
          }
  
          // create a delegate with the content of the stream.
          Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig));
          delegate.mIsMutable = isMutable;
  
          return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable),
                              Bitmap.getDefaultDensity());
      }
  
      @LayoutlibDelegate
      /*package*/ static Bitmap nativeCopy(long srcBitmap, int nativeConfig, boolean isMutable) {
          Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(srcBitmap);
          if (srcBmpDelegate == null) {
              return null;
          }
  
          BufferedImage srcImage = srcBmpDelegate.getImage();
  
          int width = srcImage.getWidth();
          int height = srcImage.getHeight();
  
          int imageType = getBufferedImageType();
  
          // create the image
          BufferedImage image = new BufferedImage(width, height, imageType);
  
          // copy the source image into the image.
          int[] argb = new int[width * height];
          srcImage.getRGB(0, 0, width, height, argb, 0, width);
          image.setRGB(0, 0, width, height, argb, 0, width);
  
          // create a delegate with the content of the stream.
          Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig));
          delegate.mIsMutable = isMutable;
  
          return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable),
                  Bitmap.getDefaultDensity());
      }
  
      @LayoutlibDelegate
      /*package*/ static Bitmap nativeCopyAshmem(long nativeSrcBitmap) {
          // Unused method; no implementation provided.
          assert false;
          return null;
      }
  
      @LayoutlibDelegate
      /*package*/ static Bitmap nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig) {
          // Unused method; no implementation provided.
          assert false;
          return null;
      }
  
      @LayoutlibDelegate
      /*package*/ static long nativeGetNativeFinalizer() {
          synchronized (Bitmap_Delegate.class) {
              if (sFinalizer == -1) {
                  sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor);
              }
              return sFinalizer;
          }
      }
  
      @LayoutlibDelegate
      /*package*/ static void nativeRecycle(long nativeBitmap) {
          // In our case recycle() is a no-op. We will let the finalizer to dispose the bitmap.
      }
  
      @LayoutlibDelegate
      /*package*/ static void nativeReconfigure(long nativeBitmap, int width, int height,
              int config, boolean isPremultiplied) {
          Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
                  "Bitmap.reconfigure() is not supported", null /*data*/);
      }
  
      @LayoutlibDelegate
      /*package*/ static boolean nativeCompress(long nativeBitmap, int format, int quality,
              OutputStream stream, byte[] tempStorage) {
          Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
                  "Bitmap.compress() is not supported", null /*data*/);
          return true;
      }
  
      @LayoutlibDelegate
      /*package*/ static void nativeErase(long nativeBitmap, int color) {
          // get the delegate from the native int.
          Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
          if (delegate == null) {
              return;
          }
  
          BufferedImage image = delegate.mImage;
  
          Graphics2D g = image.createGraphics();
          try {
              g.setColor(new java.awt.Color(color, true));
  
              g.fillRect(0, 0, image.getWidth(), image.getHeight());
          } finally {
              g.dispose();
          }
      }
  
      @LayoutlibDelegate
      /*package*/ static void nativeErase(long nativeBitmap, long colorSpacePtr, long color) {
          nativeErase(nativeBitmap, Color.toArgb(color));
      }
  
      @LayoutlibDelegate
      /*package*/ static int nativeRowBytes(long nativeBitmap) {
          // get the delegate from the native int.
          Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
          if (delegate == null) {
              return 0;
          }
  
          return delegate.mImage.getWidth();
      }
  
      @LayoutlibDelegate
      /*package*/ static int nativeConfig(long nativeBitmap) {
          // get the delegate from the native int.
          Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
          if (delegate == null) {
              return 0;
          }
  
          return delegate.mConfig.nativeInt;
      }
  
      @LayoutlibDelegate
      /*package*/ static boolean nativeHasAlpha(long nativeBitmap) {
          // get the delegate from the native int.
          Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
          return delegate == null || delegate.mHasAlpha;
  
      }
  
      @LayoutlibDelegate
      /*package*/ static boolean nativeHasMipMap(long nativeBitmap) {
          // get the delegate from the native int.
          Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
          return delegate == null || delegate.mHasMipMap;
  
      }
  
      @LayoutlibDelegate
      /*package*/ static int nativeGetPixel(long nativeBitmap, int x, int y) {
          // get the delegate from the native int.
          Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
          if (delegate == null) {
              return 0;
          }
  
          return delegate.mImage.getRGB(x, y);
      }
  
      @LayoutlibDelegate
      /*package*/ static long nativeGetColor(long nativeBitmap, int x, int y) {
          return nativeGetPixel(nativeBitmap, x, y);
      }
  
      @LayoutlibDelegate
      /*package*/ static void nativeGetPixels(long nativeBitmap, int[] pixels, int offset,
              int stride, int x, int y, int width, int height) {
          Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
          if (delegate == null) {
              return;
          }
  
          delegate.getImage().getRGB(x, y, width, height, pixels, offset, stride);
      }
  
  
      @LayoutlibDelegate
      /*package*/ static void nativeSetPixel(long nativeBitmap, int x, int y, int color) {
          Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
          if (delegate == null) {
              return;
          }
  
          delegate.getImage().setRGB(x, y, color);
      }
  
      @LayoutlibDelegate
      /*package*/ static void nativeSetPixels(long nativeBitmap, int[] colors, int offset,
              int stride, int x, int y, int width, int height) {
          Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
          if (delegate == null) {
              return;
          }
  
          delegate.getImage().setRGB(x, y, width, height, colors, offset, stride);
      }
  
      @LayoutlibDelegate
      /*package*/ static void nativeCopyPixelsToBuffer(long nativeBitmap, Buffer dst) {
          // FIXME implement native delegate
          Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                  "Bitmap.copyPixelsToBuffer is not supported.", null, null /*data*/);
      }
  
      @LayoutlibDelegate
      /*package*/ static void nativeCopyPixelsFromBuffer(long nb, Buffer src) {
          // FIXME implement native delegate
          Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                  "Bitmap.copyPixelsFromBuffer is not supported.", null, null /*data*/);
      }
  
      @LayoutlibDelegate
      /*package*/ static int nativeGenerationId(long nativeBitmap) {
          Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
          if (delegate == null) {
              return 0;
          }
  
          return delegate.mGenerationId;
      }
  
      @LayoutlibDelegate
      /*package*/ static Bitmap nativeCreateFromParcel(Parcel p) {
          // This is only called by Bitmap.CREATOR (Parcelable.Creator<Bitmap>), which is only
          // used during aidl call so really this should not be called.
          Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
                  "AIDL is not suppored, and therefore Bitmaps cannot be created from parcels.",
                  null /*data*/);
          return null;
      }
  
      @LayoutlibDelegate
      /*package*/ static boolean nativeWriteToParcel(long nativeBitmap, boolean isMutable,
              int density, Parcel p) {
          // This is only called when sending a bitmap through aidl, so really this should not
          // be called.
          Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
                  "AIDL is not suppored, and therefore Bitmaps cannot be written to parcels.",
                  null /*data*/);
          return false;
      }
  
      @LayoutlibDelegate
      /*package*/ static Bitmap nativeExtractAlpha(long nativeBitmap, long nativePaint,
              int[] offsetXY) {
          Bitmap_Delegate bitmap = sManager.getDelegate(nativeBitmap);
          if (bitmap == null) {
              return null;
          }
  
          // get the paint which can be null if nativePaint is 0.
          Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint);
  
          if (paint != null && paint.getMaskFilter() != null) {
              Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
                      "MaskFilter not supported in Bitmap.extractAlpha",
                      null, null /*data*/);
          }
  
          int alpha = paint != null ? paint.getAlpha() : 0xFF;
          BufferedImage image = createCopy(bitmap.getImage(), BufferedImage.TYPE_INT_ARGB, alpha);
  
          // create the delegate. The actual Bitmap config is only an alpha channel
          Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ALPHA_8);
          delegate.mIsMutable = true;
  
          // the density doesn't matter, it's set by the Java method.
          return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.MUTABLE),
                  Density.DEFAULT_DENSITY /*density*/);
      }
  
      @LayoutlibDelegate
      /*package*/ static boolean nativeIsPremultiplied(long nativeBitmap) {
          // get the delegate from the native int.
          Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
          return delegate != null && delegate.mIsPremultiplied;
  
      }
  
      @LayoutlibDelegate
      /*package*/ static void nativeSetPremultiplied(long nativeBitmap, boolean isPremul) {
          // get the delegate from the native int.
          Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
          if (delegate == null) {
              return;
          }
  
          delegate.mIsPremultiplied = isPremul;
      }
  
      @LayoutlibDelegate
      /*package*/ static void nativeSetHasAlpha(long nativeBitmap, boolean hasAlpha,
              boolean isPremul) {
          // get the delegate from the native int.
          Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
          if (delegate == null) {
              return;
          }
  
          delegate.mHasAlpha = hasAlpha;
      }
  
      @LayoutlibDelegate
      /*package*/ static void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap) {
          // get the delegate from the native int.
          Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
          if (delegate == null) {
              return;
          }
  
          delegate.mHasMipMap = hasMipMap;
      }
  
      @LayoutlibDelegate
      /*package*/ static boolean nativeSameAs(long nb0, long nb1) {
          Bitmap_Delegate delegate1 = sManager.getDelegate(nb0);
          if (delegate1 == null) {
              return false;
          }
  
          Bitmap_Delegate delegate2 = sManager.getDelegate(nb1);
          if (delegate2 == null) {
              return false;
          }
  
          BufferedImage image1 = delegate1.getImage();
          BufferedImage image2 = delegate2.getImage();
          if (delegate1.mConfig != delegate2.mConfig ||
                  image1.getWidth() != image2.getWidth() ||
                  image1.getHeight() != image2.getHeight()) {
              return false;
          }
  
          // get the internal data
          int w = image1.getWidth();
          int h = image2.getHeight();
          int[] argb1 = new int[w*h];
          int[] argb2 = new int[w*h];
  
          image1.getRGB(0, 0, w, h, argb1, 0, w);
          image2.getRGB(0, 0, w, h, argb2, 0, w);
  
          // compares
          if (delegate1.mConfig == Config.ALPHA_8) {
              // in this case we have to manually compare the alpha channel as the rest is garbage.
              final int length = w*h;
              for (int i = 0 ; i < length ; i++) {
                  if ((argb1[i] & 0xFF000000) != (argb2[i] & 0xFF000000)) {
                      return false;
                  }
              }
              return true;
          }
  
          return Arrays.equals(argb1, argb2);
      }
  
      @LayoutlibDelegate
      /*package*/ static int nativeGetAllocationByteCount(long nativeBitmap) {
          // get the delegate from the native int.
          Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
          if (delegate == null) {
              return 0;
          }
          int size = nativeRowBytes(nativeBitmap) * delegate.mImage.getHeight();
          return size < 0 ? Integer.MAX_VALUE : size;
  
      }
  
      @LayoutlibDelegate
      /*package*/ static void nativePrepareToDraw(long nativeBitmap) {
          // do nothing as Bitmap_Delegate does not have caches
      }
  
      @LayoutlibDelegate
      /*package*/ static Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap) {
          Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(nativeBitmap);
          if (srcBmpDelegate == null) {
              return null;
          }
  
          BufferedImage srcImage = srcBmpDelegate.getImage();
  
          // create the image
          BufferedImage image = new BufferedImage(srcImage.getColorModel(), srcImage.copyData(null),
                  srcImage.isAlphaPremultiplied(), null);
  
          // create a delegate with the content of the stream.
          Bitmap_Delegate delegate = new Bitmap_Delegate(image, srcBmpDelegate.getConfig());
          delegate.mIsMutable = srcBmpDelegate.mIsMutable;
  
          return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.NONE),
                  Bitmap.getDefaultDensity());
      }
  
      @LayoutlibDelegate
      /*package*/ static Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer,
              long nativeColorSpace) {
          Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
                  "Bitmap.nativeWrapHardwareBufferBitmap() is not supported", null, null, null);
          return null;
      }
  
      @LayoutlibDelegate
      /*package*/ static GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap) {
          Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
                  "Bitmap.nativeCreateGraphicBufferHandle() is not supported", null /*data*/);
          return null;
      }
  
      @LayoutlibDelegate
      /*package*/ static boolean nativeIsSRGB(long nativeBitmap) {
          Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
                  "Color spaces are not supported", null /*data*/);
          return false;
      }
  
      @LayoutlibDelegate
      /*package*/ static ColorSpace nativeComputeColorSpace(long nativePtr) {
          Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
                  "Color spaces are not supported", null /*data*/);
          return null;
      }
  
      @LayoutlibDelegate
      /*package*/ static void nativeSetColorSpace(long nativePtr, long nativeColorSpace) {
          Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
                  "Color spaces are not supported", null /*data*/);
      }
  
      @LayoutlibDelegate
      /*package*/ static boolean nativeIsSRGBLinear(long nativePtr) {
          Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
                  "Color spaces are not supported", null /*data*/);
          return false;
      }
  
      @LayoutlibDelegate
      /*package*/ static void nativeSetImmutable(long nativePtr) {
          Bitmap_Delegate bmpDelegate = sManager.getDelegate(nativePtr);
          if (bmpDelegate == null) {
              return;
          }
          bmpDelegate.mIsMutable = false;
      }
  
      @LayoutlibDelegate
      /*package*/ static boolean nativeIsImmutable(long nativePtr) {
          Bitmap_Delegate bmpDelegate = sManager.getDelegate(nativePtr);
          if (bmpDelegate == null) {
              return false;
          }
          return !bmpDelegate.mIsMutable;
      }
  
      // ---- Private delegate/helper methods ----
  
      private Bitmap_Delegate(BufferedImage image, Config config) {
          mImage = image;
          mConfig = config;
      }
  
      private static Bitmap createBitmap(Bitmap_Delegate delegate,
              Set<BitmapCreateFlags> createFlags, int density) {
          // get its native_int
          long nativeInt = sManager.addNewDelegate(delegate);
  
          int width = delegate.mImage.getWidth();
          int height = delegate.mImage.getHeight();
          boolean isPremultiplied = createFlags.contains(BitmapCreateFlags.PREMULTIPLIED);
  
          // and create/return a new Bitmap with it
          return new Bitmap(nativeInt, width, height, density, isPremultiplied,
                  null /*ninePatchChunk*/, null /* layoutBounds */, true /* fromMalloc */);
      }
  
      private static Set<BitmapCreateFlags> getPremultipliedBitmapCreateFlags(boolean isMutable) {
          Set<BitmapCreateFlags> createFlags = EnumSet.of(BitmapCreateFlags.PREMULTIPLIED);
          if (isMutable) {
              createFlags.add(BitmapCreateFlags.MUTABLE);
          }
          return createFlags;
      }
  
      /**
       * Creates and returns a copy of a given BufferedImage.
       * <p/>
       * if alpha is different than 255, then it is applied to the alpha channel of each pixel.
       *
       * @param image the image to copy
       * @param imageType the type of the new image
       * @param alpha an optional alpha modifier
       * @return a new BufferedImage
       */
      /*package*/ static BufferedImage createCopy(BufferedImage image, int imageType, int alpha) {
          int w = image.getWidth();
          int h = image.getHeight();
  
          BufferedImage result = new BufferedImage(w, h, imageType);
  
          int[] argb = new int[w * h];
          image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
  
          if (alpha != 255) {
              final int length = argb.length;
              for (int i = 0 ; i < length; i++) {
                  int a = (argb[i] >>> 24 * alpha) / 255;
                  argb[i] = (a << 24) | (argb[i] & 0x00FFFFFF);
              }
          }
  
          result.setRGB(0, 0, w, h, argb, 0, w);
  
          return result;
      }
  
  }
  