blob: 7fc952d10cf4cd05857dcea5da8e8d9c3dbf99d6 [file] [log] [blame]
Adam Lesinskiab775ec2014-01-23 18:17:42 -08001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.graphics;
18
Sergey Prigogin46f95672018-05-26 21:38:44 -070019import com.android.ide.common.rendering.api.AssetRepository;
Adam Lesinskiab775ec2014-01-23 18:17:42 -080020import com.android.ide.common.rendering.api.LayoutLog;
Deepanshu Guptafbe158f2015-10-06 17:56:37 -070021import com.android.ide.common.rendering.api.RenderResources;
22import com.android.ide.common.rendering.api.ResourceValue;
Adam Lesinskiab775ec2014-01-23 18:17:42 -080023import com.android.layoutlib.bridge.Bridge;
Deepanshu Guptafbe158f2015-10-06 17:56:37 -070024import com.android.layoutlib.bridge.android.BridgeContext;
Adam Lesinskiab775ec2014-01-23 18:17:42 -080025import com.android.layoutlib.bridge.impl.DelegateManager;
Deepanshu Guptafbe158f2015-10-06 17:56:37 -070026import com.android.layoutlib.bridge.impl.RenderAction;
Adam Lesinskiab775ec2014-01-23 18:17:42 -080027import com.android.resources.Density;
Deepanshu Guptafbe158f2015-10-06 17:56:37 -070028import com.android.resources.ResourceType;
Adam Lesinskiab775ec2014-01-23 18:17:42 -080029import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
30
Deepanshu Gupta442aee62015-05-22 14:11:22 -070031import android.annotation.Nullable;
Adam Lesinskiab775ec2014-01-23 18:17:42 -080032import android.graphics.Bitmap.Config;
Jerome Gaillarddd43eba2018-10-15 21:00:13 +010033import android.hardware.HardwareBuffer;
Adam Lesinskiab775ec2014-01-23 18:17:42 -080034import android.os.Parcel;
35
36import java.awt.Graphics2D;
37import java.awt.image.BufferedImage;
Adam Lesinskiab775ec2014-01-23 18:17:42 -080038import java.io.IOException;
39import java.io.InputStream;
40import java.io.OutputStream;
41import java.nio.Buffer;
42import java.util.Arrays;
Deepanshu Gupta835e2792013-09-09 12:32:19 -070043import java.util.EnumSet;
44import java.util.Set;
Adam Lesinskiab775ec2014-01-23 18:17:42 -080045
46import javax.imageio.ImageIO;
Deepanshu Guptafbe158f2015-10-06 17:56:37 -070047import libcore.util.NativeAllocationRegistry_Delegate;
Adam Lesinskiab775ec2014-01-23 18:17:42 -080048
Sergey Prigogin46f95672018-05-26 21:38:44 -070049import static android.content.res.AssetManager.ACCESS_STREAMING;
50
Adam Lesinskiab775ec2014-01-23 18:17:42 -080051/**
52 * Delegate implementing the native methods of android.graphics.Bitmap
53 *
54 * Through the layoutlib_create tool, the original native methods of Bitmap have been replaced
55 * by calls to methods of the same name in this delegate class.
56 *
57 * This class behaves like the original native implementation, but in Java, keeping previously
58 * native data into its own objects and mapping them to int that are sent back and forth between
59 * it and the original Bitmap class.
60 *
61 * @see DelegateManager
62 *
63 */
64public final class Bitmap_Delegate {
65
Deepanshu Gupta835e2792013-09-09 12:32:19 -070066 public enum BitmapCreateFlags {
Jerome Gaillard6d60df12017-01-04 19:12:02 +000067 NONE, PREMULTIPLIED, MUTABLE
Deepanshu Gupta835e2792013-09-09 12:32:19 -070068 }
69
Adam Lesinskiab775ec2014-01-23 18:17:42 -080070 // ---- delegate manager ----
71 private static final DelegateManager<Bitmap_Delegate> sManager =
Diego Perez42b832b2016-03-14 09:31:07 +000072 new DelegateManager<>(Bitmap_Delegate.class);
Deepanshu Guptafbe158f2015-10-06 17:56:37 -070073 private static long sFinalizer = -1;
Adam Lesinskiab775ec2014-01-23 18:17:42 -080074
75 // ---- delegate helper data ----
76
77 // ---- delegate data ----
78 private final Config mConfig;
Deepanshu Guptafbe158f2015-10-06 17:56:37 -070079 private final BufferedImage mImage;
Adam Lesinskiab775ec2014-01-23 18:17:42 -080080 private boolean mHasAlpha = true;
81 private boolean mHasMipMap = false; // TODO: check the default.
Deepanshu Guptaeeb44a22014-07-10 13:57:52 -070082 private boolean mIsPremultiplied = true;
Adam Lesinskiab775ec2014-01-23 18:17:42 -080083 private int mGenerationId = 0;
Jerome Gaillard333c1402018-04-25 15:55:27 +010084 private boolean mIsMutable;
Adam Lesinskiab775ec2014-01-23 18:17:42 -080085
86
87 // ---- Public Helper methods ----
88
89 /**
Adam Lesinskiab775ec2014-01-23 18:17:42 -080090 * Returns the native delegate associated to a given an int referencing a {@link Bitmap} object.
91 */
Narayan Kamathec411892014-01-27 14:24:16 +000092 public static Bitmap_Delegate getDelegate(long native_bitmap) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -080093 return sManager.getDelegate(native_bitmap);
94 }
95
96 /**
Sergey Prigogin5ea5b0a2018-04-26 18:46:03 -070097 * Creates and returns a {@link Bitmap} initialized with the given stream content.
Adam Lesinskiab775ec2014-01-23 18:17:42 -080098 *
Sergey Prigogin5ea5b0a2018-04-26 18:46:03 -070099 * @param input the stream from which to read the bitmap content
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800100 * @param isMutable whether the bitmap is mutable
101 * @param density the density associated with the bitmap
102 *
103 * @see Bitmap#isMutable()
104 * @see Bitmap#getDensity()
105 */
Sergey Prigogin5ea5b0a2018-04-26 18:46:03 -0700106 public static Bitmap createBitmap(@Nullable InputStream input, boolean isMutable,
107 Density density) throws IOException {
Deepanshu Gupta835e2792013-09-09 12:32:19 -0700108 return createBitmap(input, getPremultipliedBitmapCreateFlags(isMutable), density);
109 }
Deepanshu Gupta69fea482014-01-27 11:45:47 -0800110
Deepanshu Gupta835e2792013-09-09 12:32:19 -0700111 /**
112 * Creates and returns a {@link Bitmap} initialized with the given file content.
113 *
114 * @param input the file from which to read the bitmap content
115 * @param density the density associated with the bitmap
116 *
Deepanshu Gupta69fea482014-01-27 11:45:47 -0800117 * @see Bitmap#isPremultiplied()
Deepanshu Gupta835e2792013-09-09 12:32:19 -0700118 * @see Bitmap#isMutable()
119 * @see Bitmap#getDensity()
120 */
Sergey Prigogin5ea5b0a2018-04-26 18:46:03 -0700121 static Bitmap createBitmap(@Nullable InputStream input, Set<BitmapCreateFlags> createFlags,
Deepanshu Gupta835e2792013-09-09 12:32:19 -0700122 Density density) throws IOException {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800123 // create a delegate with the content of the file.
Sergey Prigogin5ea5b0a2018-04-26 18:46:03 -0700124 BufferedImage image = input == null ? null : ImageIO.read(input);
125 if (image == null) {
Deepanshu Guptafbe158f2015-10-06 17:56:37 -0700126 // There was a problem decoding the image, or the decoder isn't registered. Webp maybe.
127 // Replace with a broken image icon.
128 BridgeContext currentContext = RenderAction.getCurrentContext();
129 if (currentContext != null) {
130 RenderResources resources = currentContext.getRenderResources();
Sergey Prigogin46f95672018-05-26 21:38:44 -0700131 ResourceValue broken = resources.getResolvedResource(
132 BridgeContext.createFrameworkResourceReference(
133 ResourceType.DRAWABLE, "ic_menu_report_image"));
134 AssetRepository assetRepository = currentContext.getAssets().getAssetRepository();
135 try (InputStream stream =
136 assetRepository.openNonAsset(0, broken.getValue(), ACCESS_STREAMING)) {
137 if (stream != null) {
138 image = ImageIO.read(stream);
139 }
Deepanshu Guptafbe158f2015-10-06 17:56:37 -0700140 }
141 }
142 }
143 Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888);
Jerome Gaillard333c1402018-04-25 15:55:27 +0100144 delegate.mIsMutable = createFlags.contains(BitmapCreateFlags.MUTABLE);
Deepanshu Gupta69fea482014-01-27 11:45:47 -0800145
Deepanshu Gupta835e2792013-09-09 12:32:19 -0700146 return createBitmap(delegate, createFlags, density.getDpiValue());
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800147 }
148
149 /**
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800150 * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
151 *
152 * @param image the bitmap content
153 * @param isMutable whether the bitmap is mutable
154 * @param density the density associated with the bitmap
155 *
156 * @see Bitmap#isMutable()
157 * @see Bitmap#getDensity()
158 */
Deepanshu Guptac8460df2014-11-03 15:52:51 -0800159 public static Bitmap createBitmap(BufferedImage image, boolean isMutable, Density density) {
Deepanshu Gupta835e2792013-09-09 12:32:19 -0700160 return createBitmap(image, getPremultipliedBitmapCreateFlags(isMutable), density);
161 }
162
163 /**
164 * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
165 *
166 * @param image the bitmap content
Deepanshu Gupta835e2792013-09-09 12:32:19 -0700167 * @param density the density associated with the bitmap
168 *
Deepanshu Gupta69fea482014-01-27 11:45:47 -0800169 * @see Bitmap#isPremultiplied()
Deepanshu Gupta835e2792013-09-09 12:32:19 -0700170 * @see Bitmap#isMutable()
171 * @see Bitmap#getDensity()
172 */
173 public static Bitmap createBitmap(BufferedImage image, Set<BitmapCreateFlags> createFlags,
Deepanshu Guptac8460df2014-11-03 15:52:51 -0800174 Density density) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800175 // create a delegate with the given image.
176 Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888);
Jerome Gaillard333c1402018-04-25 15:55:27 +0100177 delegate.mIsMutable = createFlags.contains(BitmapCreateFlags.MUTABLE);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800178
Deepanshu Gupta835e2792013-09-09 12:32:19 -0700179 return createBitmap(delegate, createFlags, density.getDpiValue());
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800180 }
181
Deepanshu Gupta9fe7fca2015-05-12 12:01:16 -0700182 private static int getBufferedImageType() {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800183 return BufferedImage.TYPE_INT_ARGB;
184 }
185
186 /**
187 * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}.
188 */
189 public BufferedImage getImage() {
190 return mImage;
191 }
192
193 /**
194 * Returns the Android bitmap config. Note that this not the config of the underlying
195 * Java2D bitmap.
196 */
197 public Config getConfig() {
198 return mConfig;
199 }
200
201 /**
202 * Returns the hasAlpha rendering hint
203 * @return true if the bitmap alpha should be used at render time
204 */
205 public boolean hasAlpha() {
206 return mHasAlpha && mConfig != Config.RGB_565;
207 }
208
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800209 /**
210 * Update the generationId.
211 *
212 * @see Bitmap#getGenerationId()
213 */
214 public void change() {
215 mGenerationId++;
216 }
217
218 // ---- native methods ----
219
220 @LayoutlibDelegate
221 /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width,
Jerome Gaillardad7af142019-01-23 17:34:54 +0000222 int height, int nativeConfig, boolean isMutable, long nativeColorSpace) {
Deepanshu Gupta9fe7fca2015-05-12 12:01:16 -0700223 int imageType = getBufferedImageType();
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800224
225 // create the image
226 BufferedImage image = new BufferedImage(width, height, imageType);
227
228 if (colors != null) {
229 image.setRGB(0, 0, width, height, colors, offset, stride);
230 }
231
232 // create a delegate with the content of the stream.
233 Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig));
Jerome Gaillard333c1402018-04-25 15:55:27 +0100234 delegate.mIsMutable = isMutable;
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800235
Deepanshu Gupta835e2792013-09-09 12:32:19 -0700236 return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable),
Deepanshu Gupta69fea482014-01-27 11:45:47 -0800237 Bitmap.getDefaultDensity());
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800238 }
239
240 @LayoutlibDelegate
Narayan Kamathec411892014-01-27 14:24:16 +0000241 /*package*/ static Bitmap nativeCopy(long srcBitmap, int nativeConfig, boolean isMutable) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800242 Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(srcBitmap);
243 if (srcBmpDelegate == null) {
244 return null;
245 }
246
247 BufferedImage srcImage = srcBmpDelegate.getImage();
248
249 int width = srcImage.getWidth();
250 int height = srcImage.getHeight();
251
Deepanshu Gupta9fe7fca2015-05-12 12:01:16 -0700252 int imageType = getBufferedImageType();
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800253
254 // create the image
255 BufferedImage image = new BufferedImage(width, height, imageType);
256
257 // copy the source image into the image.
258 int[] argb = new int[width * height];
259 srcImage.getRGB(0, 0, width, height, argb, 0, width);
260 image.setRGB(0, 0, width, height, argb, 0, width);
261
262 // create a delegate with the content of the stream.
263 Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig));
Jerome Gaillard333c1402018-04-25 15:55:27 +0100264 delegate.mIsMutable = isMutable;
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800265
Deepanshu Gupta835e2792013-09-09 12:32:19 -0700266 return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable),
267 Bitmap.getDefaultDensity());
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800268 }
269
270 @LayoutlibDelegate
Deepanshu Guptab8275ed2015-05-21 16:03:24 -0700271 /*package*/ static Bitmap nativeCopyAshmem(long nativeSrcBitmap) {
272 // Unused method; no implementation provided.
273 assert false;
274 return null;
275 }
276
277 @LayoutlibDelegate
Diego Perez74065ac2016-04-22 19:00:39 +0100278 /*package*/ static Bitmap nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig) {
279 // Unused method; no implementation provided.
280 assert false;
281 return null;
282 }
283
284 @LayoutlibDelegate
Deepanshu Guptafbe158f2015-10-06 17:56:37 -0700285 /*package*/ static long nativeGetNativeFinalizer() {
286 synchronized (Bitmap_Delegate.class) {
287 if (sFinalizer == -1) {
288 sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor);
289 }
290 return sFinalizer;
291 }
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800292 }
293
294 @LayoutlibDelegate
Jerome Gaillarda27b5da2018-05-29 17:23:00 +0100295 /*package*/ static void nativeRecycle(long nativeBitmap) {
296 // In our case recycle() is a no-op. We will let the finalizer to dispose the bitmap.
Deepanshu Gupta69fea482014-01-27 11:45:47 -0800297 }
298
299 @LayoutlibDelegate
300 /*package*/ static void nativeReconfigure(long nativeBitmap, int width, int height,
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000301 int config, boolean isPremultiplied) {
Deepanshu Gupta69fea482014-01-27 11:45:47 -0800302 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
Jerome Gaillardb484b7e2020-04-14 16:10:43 +0100303 "Bitmap.reconfigure() is not supported", null, null /*data*/);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800304 }
305
306 @LayoutlibDelegate
Narayan Kamathec411892014-01-27 14:24:16 +0000307 /*package*/ static boolean nativeCompress(long nativeBitmap, int format, int quality,
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800308 OutputStream stream, byte[] tempStorage) {
309 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
Jerome Gaillardb484b7e2020-04-14 16:10:43 +0100310 "Bitmap.compress() is not supported", null, null /*data*/);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800311 return true;
312 }
313
314 @LayoutlibDelegate
Narayan Kamathec411892014-01-27 14:24:16 +0000315 /*package*/ static void nativeErase(long nativeBitmap, int color) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800316 // get the delegate from the native int.
317 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
318 if (delegate == null) {
319 return;
320 }
321
322 BufferedImage image = delegate.mImage;
323
324 Graphics2D g = image.createGraphics();
325 try {
326 g.setColor(new java.awt.Color(color, true));
327
328 g.fillRect(0, 0, image.getWidth(), image.getHeight());
329 } finally {
330 g.dispose();
331 }
332 }
333
334 @LayoutlibDelegate
Jerome Gaillardad7af142019-01-23 17:34:54 +0000335 /*package*/ static void nativeErase(long nativeBitmap, long colorSpacePtr, long color) {
336 nativeErase(nativeBitmap, Color.toArgb(color));
Jerome Gaillardc5119a82019-01-16 13:31:14 +0000337 }
338
339 @LayoutlibDelegate
Narayan Kamathec411892014-01-27 14:24:16 +0000340 /*package*/ static int nativeRowBytes(long nativeBitmap) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800341 // get the delegate from the native int.
342 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
343 if (delegate == null) {
344 return 0;
345 }
346
347 return delegate.mImage.getWidth();
348 }
349
350 @LayoutlibDelegate
Narayan Kamathec411892014-01-27 14:24:16 +0000351 /*package*/ static int nativeConfig(long nativeBitmap) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800352 // get the delegate from the native int.
353 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
354 if (delegate == null) {
355 return 0;
356 }
357
358 return delegate.mConfig.nativeInt;
359 }
360
361 @LayoutlibDelegate
Narayan Kamathec411892014-01-27 14:24:16 +0000362 /*package*/ static boolean nativeHasAlpha(long nativeBitmap) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800363 // get the delegate from the native int.
364 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
Deepanshu Gupta9fe7fca2015-05-12 12:01:16 -0700365 return delegate == null || delegate.mHasAlpha;
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800366
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800367 }
368
369 @LayoutlibDelegate
Narayan Kamathec411892014-01-27 14:24:16 +0000370 /*package*/ static boolean nativeHasMipMap(long nativeBitmap) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800371 // get the delegate from the native int.
372 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
Deepanshu Gupta9fe7fca2015-05-12 12:01:16 -0700373 return delegate == null || delegate.mHasMipMap;
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800374
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800375 }
376
377 @LayoutlibDelegate
Deepanshu Guptaeeb44a22014-07-10 13:57:52 -0700378 /*package*/ static int nativeGetPixel(long nativeBitmap, int x, int y) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800379 // get the delegate from the native int.
380 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
381 if (delegate == null) {
382 return 0;
383 }
384
385 return delegate.mImage.getRGB(x, y);
386 }
387
388 @LayoutlibDelegate
Jerome Gaillardddc4eb72019-01-30 14:49:37 +0000389 /*package*/ static long nativeGetColor(long nativeBitmap, int x, int y) {
390 return nativeGetPixel(nativeBitmap, x, y);
391 }
392
393 @LayoutlibDelegate
Narayan Kamathec411892014-01-27 14:24:16 +0000394 /*package*/ static void nativeGetPixels(long nativeBitmap, int[] pixels, int offset,
Deepanshu Guptaeeb44a22014-07-10 13:57:52 -0700395 int stride, int x, int y, int width, int height) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800396 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
397 if (delegate == null) {
398 return;
399 }
400
401 delegate.getImage().getRGB(x, y, width, height, pixels, offset, stride);
402 }
403
404
405 @LayoutlibDelegate
Deepanshu Guptaeeb44a22014-07-10 13:57:52 -0700406 /*package*/ static void nativeSetPixel(long nativeBitmap, int x, int y, int color) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800407 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
408 if (delegate == null) {
409 return;
410 }
411
412 delegate.getImage().setRGB(x, y, color);
413 }
414
415 @LayoutlibDelegate
Narayan Kamathec411892014-01-27 14:24:16 +0000416 /*package*/ static void nativeSetPixels(long nativeBitmap, int[] colors, int offset,
Deepanshu Guptaeeb44a22014-07-10 13:57:52 -0700417 int stride, int x, int y, int width, int height) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800418 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
419 if (delegate == null) {
420 return;
421 }
422
423 delegate.getImage().setRGB(x, y, width, height, colors, offset, stride);
424 }
425
426 @LayoutlibDelegate
Narayan Kamathec411892014-01-27 14:24:16 +0000427 /*package*/ static void nativeCopyPixelsToBuffer(long nativeBitmap, Buffer dst) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800428 // FIXME implement native delegate
429 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
Jerome Gaillardb484b7e2020-04-14 16:10:43 +0100430 "Bitmap.copyPixelsToBuffer is not supported.", null, null, null /*data*/);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800431 }
432
433 @LayoutlibDelegate
Narayan Kamathec411892014-01-27 14:24:16 +0000434 /*package*/ static void nativeCopyPixelsFromBuffer(long nb, Buffer src) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800435 // FIXME implement native delegate
436 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
Jerome Gaillardb484b7e2020-04-14 16:10:43 +0100437 "Bitmap.copyPixelsFromBuffer is not supported.", null, null, null /*data*/);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800438 }
439
440 @LayoutlibDelegate
Narayan Kamathec411892014-01-27 14:24:16 +0000441 /*package*/ static int nativeGenerationId(long nativeBitmap) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800442 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
443 if (delegate == null) {
444 return 0;
445 }
446
447 return delegate.mGenerationId;
448 }
449
450 @LayoutlibDelegate
451 /*package*/ static Bitmap nativeCreateFromParcel(Parcel p) {
452 // This is only called by Bitmap.CREATOR (Parcelable.Creator<Bitmap>), which is only
453 // used during aidl call so really this should not be called.
454 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
455 "AIDL is not suppored, and therefore Bitmaps cannot be created from parcels.",
Jerome Gaillardb484b7e2020-04-14 16:10:43 +0100456 null, null /*data*/);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800457 return null;
458 }
459
460 @LayoutlibDelegate
Jerome Gaillardc8633b22020-01-29 20:49:48 +0000461 /*package*/ static boolean nativeWriteToParcel(long nativeBitmap, int density, Parcel p) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800462 // This is only called when sending a bitmap through aidl, so really this should not
463 // be called.
464 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
465 "AIDL is not suppored, and therefore Bitmaps cannot be written to parcels.",
Jerome Gaillardb484b7e2020-04-14 16:10:43 +0100466 null, null /*data*/);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800467 return false;
468 }
469
470 @LayoutlibDelegate
Narayan Kamathec411892014-01-27 14:24:16 +0000471 /*package*/ static Bitmap nativeExtractAlpha(long nativeBitmap, long nativePaint,
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800472 int[] offsetXY) {
473 Bitmap_Delegate bitmap = sManager.getDelegate(nativeBitmap);
474 if (bitmap == null) {
475 return null;
476 }
477
478 // get the paint which can be null if nativePaint is 0.
479 Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint);
480
481 if (paint != null && paint.getMaskFilter() != null) {
482 Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
483 "MaskFilter not supported in Bitmap.extractAlpha",
Jerome Gaillardb484b7e2020-04-14 16:10:43 +0100484 null, null, null /*data*/);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800485 }
486
487 int alpha = paint != null ? paint.getAlpha() : 0xFF;
488 BufferedImage image = createCopy(bitmap.getImage(), BufferedImage.TYPE_INT_ARGB, alpha);
489
490 // create the delegate. The actual Bitmap config is only an alpha channel
491 Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ALPHA_8);
Jerome Gaillard333c1402018-04-25 15:55:27 +0100492 delegate.mIsMutable = true;
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800493
494 // the density doesn't matter, it's set by the Java method.
Deepanshu Gupta835e2792013-09-09 12:32:19 -0700495 return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.MUTABLE),
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800496 Density.DEFAULT_DENSITY /*density*/);
497 }
498
499 @LayoutlibDelegate
Deepanshu Guptaeeb44a22014-07-10 13:57:52 -0700500 /*package*/ static boolean nativeIsPremultiplied(long nativeBitmap) {
501 // get the delegate from the native int.
502 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
503 return delegate != null && delegate.mIsPremultiplied;
504
505 }
506
507 @LayoutlibDelegate
508 /*package*/ static void nativeSetPremultiplied(long nativeBitmap, boolean isPremul) {
509 // get the delegate from the native int.
510 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
511 if (delegate == null) {
512 return;
513 }
514
515 delegate.mIsPremultiplied = isPremul;
516 }
517
518 @LayoutlibDelegate
519 /*package*/ static void nativeSetHasAlpha(long nativeBitmap, boolean hasAlpha,
Deepanshu Guptacd0ed562014-01-28 18:55:33 -0800520 boolean isPremul) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800521 // get the delegate from the native int.
522 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
523 if (delegate == null) {
524 return;
525 }
526
527 delegate.mHasAlpha = hasAlpha;
528 }
529
530 @LayoutlibDelegate
Narayan Kamathec411892014-01-27 14:24:16 +0000531 /*package*/ static void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800532 // get the delegate from the native int.
533 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
534 if (delegate == null) {
535 return;
536 }
537
538 delegate.mHasMipMap = hasMipMap;
539 }
540
541 @LayoutlibDelegate
Narayan Kamathec411892014-01-27 14:24:16 +0000542 /*package*/ static boolean nativeSameAs(long nb0, long nb1) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800543 Bitmap_Delegate delegate1 = sManager.getDelegate(nb0);
544 if (delegate1 == null) {
545 return false;
546 }
547
548 Bitmap_Delegate delegate2 = sManager.getDelegate(nb1);
549 if (delegate2 == null) {
550 return false;
551 }
552
553 BufferedImage image1 = delegate1.getImage();
554 BufferedImage image2 = delegate2.getImage();
555 if (delegate1.mConfig != delegate2.mConfig ||
556 image1.getWidth() != image2.getWidth() ||
557 image1.getHeight() != image2.getHeight()) {
558 return false;
559 }
560
561 // get the internal data
562 int w = image1.getWidth();
563 int h = image2.getHeight();
564 int[] argb1 = new int[w*h];
565 int[] argb2 = new int[w*h];
566
567 image1.getRGB(0, 0, w, h, argb1, 0, w);
568 image2.getRGB(0, 0, w, h, argb2, 0, w);
569
570 // compares
571 if (delegate1.mConfig == Config.ALPHA_8) {
572 // in this case we have to manually compare the alpha channel as the rest is garbage.
573 final int length = w*h;
574 for (int i = 0 ; i < length ; i++) {
575 if ((argb1[i] & 0xFF000000) != (argb2[i] & 0xFF000000)) {
576 return false;
577 }
578 }
579 return true;
580 }
581
582 return Arrays.equals(argb1, argb2);
583 }
584
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000585 @LayoutlibDelegate
586 /*package*/ static int nativeGetAllocationByteCount(long nativeBitmap) {
587 // get the delegate from the native int.
588 Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
589 if (delegate == null) {
590 return 0;
591 }
Jerome Gaillard28508732017-11-30 14:52:32 +0000592 int size = nativeRowBytes(nativeBitmap) * delegate.mImage.getHeight();
593 return size < 0 ? Integer.MAX_VALUE : size;
Jerome Gaillard7fa71f52016-11-11 12:39:34 +0000594
595 }
596
597 @LayoutlibDelegate
598 /*package*/ static void nativePrepareToDraw(long nativeBitmap) {
599 // do nothing as Bitmap_Delegate does not have caches
600 }
601
Jerome Gaillard6d60df12017-01-04 19:12:02 +0000602 @LayoutlibDelegate
603 /*package*/ static Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap) {
604 Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(nativeBitmap);
605 if (srcBmpDelegate == null) {
606 return null;
607 }
608
609 BufferedImage srcImage = srcBmpDelegate.getImage();
610
611 // create the image
612 BufferedImage image = new BufferedImage(srcImage.getColorModel(), srcImage.copyData(null),
613 srcImage.isAlphaPremultiplied(), null);
614
615 // create a delegate with the content of the stream.
616 Bitmap_Delegate delegate = new Bitmap_Delegate(image, srcBmpDelegate.getConfig());
Jerome Gaillard333c1402018-04-25 15:55:27 +0100617 delegate.mIsMutable = srcBmpDelegate.mIsMutable;
Jerome Gaillard6d60df12017-01-04 19:12:02 +0000618
619 return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.NONE),
620 Bitmap.getDefaultDensity());
621 }
622
Diego Perez0cd6c5b2017-02-02 16:08:41 +0000623 @LayoutlibDelegate
Jerome Gaillarddd43eba2018-10-15 21:00:13 +0100624 /*package*/ static Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer,
Jerome Gaillardad7af142019-01-23 17:34:54 +0000625 long nativeColorSpace) {
Jerome Gaillarddd43eba2018-10-15 21:00:13 +0100626 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
627 "Bitmap.nativeWrapHardwareBufferBitmap() is not supported", null, null, null);
628 return null;
629 }
630
631 @LayoutlibDelegate
Diego Perez0cd6c5b2017-02-02 16:08:41 +0000632 /*package*/ static GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap) {
633 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
634 "Bitmap.nativeCreateGraphicBufferHandle() is not supported", null /*data*/);
635 return null;
636 }
637
Jerome Gaillardce2fcba2017-03-08 14:02:22 +0000638 @LayoutlibDelegate
639 /*package*/ static boolean nativeIsSRGB(long nativeBitmap) {
640 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
Jerome Gaillardb484b7e2020-04-14 16:10:43 +0100641 "Color spaces are not supported", null, null /*data*/);
Jerome Gaillardce2fcba2017-03-08 14:02:22 +0000642 return false;
643 }
644
645 @LayoutlibDelegate
Jerome Gaillard604cce32019-02-15 14:29:17 +0000646 /*package*/ static ColorSpace nativeComputeColorSpace(long nativePtr) {
Jerome Gaillardce2fcba2017-03-08 14:02:22 +0000647 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
Jerome Gaillardb484b7e2020-04-14 16:10:43 +0100648 "Color spaces are not supported", null, null /*data*/);
Jerome Gaillard604cce32019-02-15 14:29:17 +0000649 return null;
Jerome Gaillardce2fcba2017-03-08 14:02:22 +0000650 }
651
Jerome Gaillard6c050fc2017-04-10 17:07:49 +0100652 @LayoutlibDelegate
Jerome Gaillard551ce7a2019-01-29 13:51:46 +0000653 /*package*/ static void nativeSetColorSpace(long nativePtr, long nativeColorSpace) {
654 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
Jerome Gaillardb484b7e2020-04-14 16:10:43 +0100655 "Color spaces are not supported", null, null /*data*/);
Jerome Gaillard551ce7a2019-01-29 13:51:46 +0000656 }
657
658 @LayoutlibDelegate
Jerome Gaillardb6ad0c62018-03-21 13:45:46 +0000659 /*package*/ static boolean nativeIsSRGBLinear(long nativePtr) {
660 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
Jerome Gaillardb484b7e2020-04-14 16:10:43 +0100661 "Color spaces are not supported", null, null /*data*/);
Jerome Gaillardb6ad0c62018-03-21 13:45:46 +0000662 return false;
663 }
664
Jerome Gaillard333c1402018-04-25 15:55:27 +0100665 @LayoutlibDelegate
666 /*package*/ static void nativeSetImmutable(long nativePtr) {
667 Bitmap_Delegate bmpDelegate = sManager.getDelegate(nativePtr);
668 if (bmpDelegate == null) {
669 return;
670 }
671 bmpDelegate.mIsMutable = false;
672 }
673
674 @LayoutlibDelegate
675 /*package*/ static boolean nativeIsImmutable(long nativePtr) {
676 Bitmap_Delegate bmpDelegate = sManager.getDelegate(nativePtr);
677 if (bmpDelegate == null) {
678 return false;
679 }
680 return !bmpDelegate.mIsMutable;
681 }
682
Jerome Gaillard4eedc8d2020-02-19 18:23:24 +0000683 @LayoutlibDelegate
684 /*package*/ static HardwareBuffer nativeGetHardwareBuffer(long nativeBitmap) {
685 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
Jerome Gaillardb484b7e2020-04-14 16:10:43 +0100686 "HardwareBuffer is not supported", null, null /*data*/);
Jerome Gaillard4eedc8d2020-02-19 18:23:24 +0000687 return null;
688 }
689
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800690 // ---- Private delegate/helper methods ----
691
692 private Bitmap_Delegate(BufferedImage image, Config config) {
693 mImage = image;
694 mConfig = config;
695 }
696
Deepanshu Gupta835e2792013-09-09 12:32:19 -0700697 private static Bitmap createBitmap(Bitmap_Delegate delegate,
698 Set<BitmapCreateFlags> createFlags, int density) {
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800699 // get its native_int
Narayan Kamathec411892014-01-27 14:24:16 +0000700 long nativeInt = sManager.addNewDelegate(delegate);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800701
Deepanshu Gupta835e2792013-09-09 12:32:19 -0700702 int width = delegate.mImage.getWidth();
703 int height = delegate.mImage.getHeight();
Deepanshu Gupta835e2792013-09-09 12:32:19 -0700704 boolean isPremultiplied = createFlags.contains(BitmapCreateFlags.PREMULTIPLIED);
705
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800706 // and create/return a new Bitmap with it
Nader Jawad88caa912018-04-20 12:13:00 -0700707 return new Bitmap(nativeInt, width, height, density, isPremultiplied,
Leon Scroggins III8ff67f32019-03-13 09:01:40 -0400708 null /*ninePatchChunk*/, null /* layoutBounds */, true /* fromMalloc */);
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800709 }
710
Deepanshu Gupta835e2792013-09-09 12:32:19 -0700711 private static Set<BitmapCreateFlags> getPremultipliedBitmapCreateFlags(boolean isMutable) {
Deepanshu Gupta69fea482014-01-27 11:45:47 -0800712 Set<BitmapCreateFlags> createFlags = EnumSet.of(BitmapCreateFlags.PREMULTIPLIED);
Deepanshu Gupta835e2792013-09-09 12:32:19 -0700713 if (isMutable) {
714 createFlags.add(BitmapCreateFlags.MUTABLE);
715 }
716 return createFlags;
717 }
Deepanshu Gupta69fea482014-01-27 11:45:47 -0800718
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800719 /**
720 * Creates and returns a copy of a given BufferedImage.
721 * <p/>
722 * if alpha is different than 255, then it is applied to the alpha channel of each pixel.
723 *
724 * @param image the image to copy
725 * @param imageType the type of the new image
726 * @param alpha an optional alpha modifier
727 * @return a new BufferedImage
728 */
729 /*package*/ static BufferedImage createCopy(BufferedImage image, int imageType, int alpha) {
730 int w = image.getWidth();
731 int h = image.getHeight();
732
733 BufferedImage result = new BufferedImage(w, h, imageType);
734
735 int[] argb = new int[w * h];
736 image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
737
738 if (alpha != 255) {
739 final int length = argb.length;
740 for (int i = 0 ; i < length; i++) {
741 int a = (argb[i] >>> 24 * alpha) / 255;
742 argb[i] = (a << 24) | (argb[i] & 0x00FFFFFF);
743 }
744 }
745
746 result.setRGB(0, 0, w, h, argb, 0, w);
747
748 return result;
749 }
750
751}