First pass at using hardware bitmaps
Bug: 35428783
Change-Id: Ife67b85f6e7e268826597ed9bccd9659841f67de
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 9178d8a..9be0d32 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -17,6 +17,8 @@
package com.android.launcher3.uioverrides;
import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.view.View.AccessibilityDelegate;
import android.widget.PopupMenu;
import android.widget.Toast;
@@ -24,11 +26,16 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.BitmapRenderer;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.widget.WidgetsFullSheet;
+import com.android.systemui.shared.recents.view.RecentsTransition;
public class UiFactory {
+ public static final boolean USE_HARDWARE_BITMAP = FeatureFlags.IS_DOGFOOD_BUILD;
+
public static TouchController[] createTouchControllers(Launcher launcher) {
if (launcher.getDeviceProfile().isVerticalBarLayout()) {
@@ -77,4 +84,15 @@
}
menu.show();
}
+
+ public static Bitmap createFromRenderer(int width, int height, boolean forceSoftwareRenderer,
+ BitmapRenderer renderer) {
+ if (USE_HARDWARE_BITMAP && !forceSoftwareRenderer) {
+ return RecentsTransition.createHardwareBitmap(width, height, renderer::render);
+ } else {
+ Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ renderer.render(new Canvas(result));
+ return result;
+ }
+ }
}
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 573e8a2..ab853e5 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -41,17 +41,20 @@
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
+
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Provider;
import com.android.launcher3.util.SQLiteCacheHelper;
import com.android.launcher3.util.Thunk;
+
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -102,6 +105,7 @@
@Thunk final Handler mWorkerHandler;
private final BitmapFactory.Options mLowResOptions;
+ private final BitmapFactory.Options mHighResOptions;
public IconCache(Context context, InvariantDeviceProfile inv) {
mContext = context;
@@ -120,6 +124,13 @@
// Always prefer RGB_565 config for low res. If the bitmap has transparency, it will
// automatically be loaded as ALPHA_8888.
mLowResOptions.inPreferredConfig = Bitmap.Config.RGB_565;
+
+ if (UiFactory.USE_HARDWARE_BITMAP) {
+ mHighResOptions = new BitmapFactory.Options();
+ mHighResOptions.inPreferredConfig = Bitmap.Config.HARDWARE;
+ } else {
+ mHighResOptions = null;
+ }
}
private Drawable getFullResDefaultActivityIcon() {
@@ -625,7 +636,7 @@
Bitmap icon = LauncherIcons.createBadgedIconBitmap(
appInfo.loadIcon(mPackageManager), user, mContext, appInfo.targetSdkVersion);
if (mInstantAppResolver.isInstantApp(appInfo)) {
- icon = LauncherIcons.badgeWithDrawable(icon,
+ LauncherIcons.badgeWithDrawable(icon,
mContext.getDrawable(R.drawable.ic_instant_app_badge), mContext);
}
Bitmap lowResIcon = generateLowResIcon(icon);
@@ -665,7 +676,7 @@
new String[]{cacheKey.componentName.flattenToString(),
Long.toString(mUserManager.getSerialNumberForUser(cacheKey.user))});
if (c.moveToNext()) {
- entry.icon = loadIconNoResize(c, 0, lowRes ? mLowResOptions : null);
+ entry.icon = loadIconNoResize(c, 0, lowRes ? mLowResOptions : mHighResOptions);
entry.isLowResIcon = lowRes;
entry.title = c.getString(1);
if (entry.title == null) {
diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
index c905460..1c6f77c 100644
--- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -36,10 +36,9 @@
import com.android.launcher3.R;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.PreviewBackground;
+import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.Preconditions;
-import java.util.concurrent.Callable;
-
/**
* {@link AdaptiveIconDrawable} representation of a {@link FolderIcon}
*/
@@ -66,7 +65,7 @@
}
public static FolderAdaptiveIcon createFolderAdaptiveIcon(
- final Launcher launcher, final long folderId, Point dragViewSize) {
+ Launcher launcher, long folderId, Point dragViewSize) {
Preconditions.assertNonUiThread();
int margin = launcher.getResources()
.getDimensionPixelSize(R.dimen.blur_size_medium_outline);
@@ -75,21 +74,12 @@
final Bitmap badge = Bitmap.createBitmap(
dragViewSize.x - margin, dragViewSize.y - margin, Bitmap.Config.ARGB_8888);
- // The bitmap for the preview is generated larger than needed to allow for the spring effect
- float sizeScaleFactor = 1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction();
- final Bitmap preview = Bitmap.createBitmap(
- (int) (dragViewSize.x * sizeScaleFactor), (int) (dragViewSize.y * sizeScaleFactor),
- Bitmap.Config.ARGB_8888);
-
// Create the actual drawable on the UI thread to avoid race conditions with
// FolderIcon draw pass
try {
- return new MainThreadExecutor().submit(new Callable<FolderAdaptiveIcon>() {
- @Override
- public FolderAdaptiveIcon call() throws Exception {
- FolderIcon icon = launcher.findFolderIcon(folderId);
- return icon == null ? null : createDrawableOnUiThread(icon, badge, preview);
- }
+ return new MainThreadExecutor().submit(() -> {
+ FolderIcon icon = launcher.findFolderIcon(folderId);
+ return icon == null ? null : createDrawableOnUiThread(icon, badge, dragViewSize);
}).get();
} catch (Exception e) {
Log.e(TAG, "Unable to create folder icon", e);
@@ -101,7 +91,7 @@
* Initializes various bitmaps on the UI thread and returns the final drawable.
*/
private static FolderAdaptiveIcon createDrawableOnUiThread(FolderIcon icon,
- Bitmap badgeBitmap, Bitmap previewBitmap) {
+ Bitmap badgeBitmap, Point dragViewSize) {
Preconditions.assertUIThread();
float margin = icon.getResources().getDimension(R.dimen.blur_size_medium_outline) / 2;
@@ -115,15 +105,21 @@
icon.drawBadge(c);
// Initialize preview
- float shiftFactor = AdaptiveIconDrawable.getExtraInsetFraction() /
- (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction());
- float previewShiftX = shiftFactor * previewBitmap.getWidth();
- float previewShiftY = shiftFactor * previewBitmap.getHeight();
+ final float sizeScaleFactor = 1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction();
+ final int previewWidth = (int) (dragViewSize.x * sizeScaleFactor);
+ final int previewHeight = (int) (dragViewSize.y * sizeScaleFactor);
- c.setBitmap(previewBitmap);
- c.translate(previewShiftX, previewShiftY);
- icon.getPreviewItemManager().draw(c);
- c.setBitmap(null);
+ final float shiftFactor = AdaptiveIconDrawable.getExtraInsetFraction() / sizeScaleFactor;
+ final float previewShiftX = shiftFactor * previewWidth;
+ final float previewShiftY = shiftFactor * previewHeight;
+
+ Bitmap previewBitmap = UiFactory.createFromRenderer(previewWidth, previewHeight, false,
+ (canvas) -> {
+ int count = canvas.save();
+ canvas.translate(previewShiftX, previewShiftY);
+ icon.getPreviewItemManager().draw(canvas);
+ canvas.restoreToCount(count);
+ });
// Initialize mask
Path mask = new Path();
diff --git a/src/com/android/launcher3/graphics/BitmapRenderer.java b/src/com/android/launcher3/graphics/BitmapRenderer.java
new file mode 100644
index 0000000..4652ded
--- /dev/null
+++ b/src/com/android/launcher3/graphics/BitmapRenderer.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017 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 com.android.launcher3.graphics;
+
+import android.graphics.Canvas;
+
+public interface BitmapRenderer {
+
+ void render(Canvas out);
+}
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 355c231..902906f 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -35,6 +35,7 @@
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.UiThreadHelper;
import java.nio.ByteBuffer;
@@ -77,8 +78,10 @@
/**
* Draws the {@link #mView} into the given {@param destCanvas}.
*/
- private void drawDragView(Canvas destCanvas) {
+ private void drawDragView(Canvas destCanvas, float scale) {
destCanvas.save();
+ destCanvas.scale(scale, scale);
+
if (mView instanceof BubbleTextView) {
Drawable d = ((BubbleTextView) mView).getIcon();
Rect bounds = getDrawableBounds(d);
@@ -120,6 +123,7 @@
int width = mView.getWidth();
int height = mView.getHeight();
+ boolean forceSoftwareRenderer = false;
if (mView instanceof BubbleTextView) {
Drawable d = ((BubbleTextView) mView).getIcon();
Rect bounds = getDrawableBounds(d);
@@ -129,20 +133,14 @@
scale = ((LauncherAppWidgetHostView) mView).getScaleToFit();
width = (int) (mView.getWidth() * scale);
height = (int) (mView.getHeight() * scale);
+
+ // Use software renderer for widgets as we know that they already work
+ forceSoftwareRenderer = true;
}
- Bitmap b = Bitmap.createBitmap(width + blurSizeOutline, height + blurSizeOutline,
- Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(b);
-
- canvas.save();
- canvas.scale(scale, scale);
- drawDragView(canvas);
- canvas.restore();
-
- canvas.setBitmap(null);
-
- return b;
+ final float scaleFinal = scale;
+ return UiFactory.createFromRenderer(width + blurSizeOutline, height + blurSizeOutline,
+ forceSoftwareRenderer, (c) -> drawDragView(c, scaleFinal));
}
public final void generateDragOutline(Bitmap preview) {
diff --git a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
index fdf2d67..ebfe1e7 100644
--- a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
+++ b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
@@ -33,6 +33,7 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.R;
+import com.android.launcher3.uioverrides.UiFactory;
/**
* Utility class to generate shadow and outline effect, which are used for click feedback
@@ -106,7 +107,11 @@
int saveCount = mCanvas.save();
mCanvas.scale(scaleX, scaleY);
mCanvas.translate(-rect.left, -rect.top);
- drawable.draw(mCanvas);
+ if (!UiFactory.USE_HARDWARE_BITMAP) {
+ // TODO: Outline generation requires alpha extraction, which is costly for
+ // hardware bitmaps. Instead use canvas layer operations once its available.
+ drawable.draw(mCanvas);
+ }
mCanvas.restoreToCount(saveCount);
mCanvas.setBitmap(null);
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 1eddb2c..8c4738c 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -49,6 +49,7 @@
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.Provider;
/**
@@ -132,7 +133,12 @@
Bitmap bitmap = createIconBitmap(icon, context, scale);
if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO &&
icon instanceof AdaptiveIconDrawable) {
- bitmap = ShadowGenerator.getInstance(context).recreateIcon(bitmap);
+ synchronized (sCanvas) {
+ sCanvas.setBitmap(bitmap);
+ ShadowGenerator.getInstance(context).recreateIcon(
+ Bitmap.createBitmap(bitmap), sCanvas);
+ sCanvas.setBitmap(null);
+ }
}
if (user != null && !Process.myUserHandle().equals(user)) {
@@ -183,16 +189,25 @@
return createIconBitmap(icon, context, scale);
}
- public static Bitmap badgeWithDrawable(Bitmap srcTgt, Drawable badge, Context context) {
- int badgeSize = context.getResources().getDimensionPixelSize(R.dimen.profile_badge_size);
+ /**
+ * Adds the {@param badge} on top of {@param target} using the badge dimensions.
+ */
+ public static void badgeWithDrawable(Bitmap target, Drawable badge, Context context) {
synchronized (sCanvas) {
- sCanvas.setBitmap(srcTgt);
- int iconSize = srcTgt.getWidth();
- badge.setBounds(iconSize - badgeSize, iconSize - badgeSize, iconSize, iconSize);
- badge.draw(sCanvas);
+ sCanvas.setBitmap(target);
+ badgeWithDrawable(sCanvas, badge, context);
sCanvas.setBitmap(null);
}
- return srcTgt;
+ }
+
+ /**
+ * Adds the {@param badge} on top of {@param target} using the badge dimensions.
+ */
+ private static void badgeWithDrawable(Canvas target, Drawable badge, Context context) {
+ int badgeSize = context.getResources().getDimensionPixelSize(R.dimen.profile_badge_size);
+ int iconSize = LauncherAppState.getIDP(context).iconBitmapSize;
+ badge.setBounds(iconSize - badgeSize, iconSize - badgeSize, iconSize, iconSize);
+ badge.draw(target);
}
/**
@@ -326,9 +341,16 @@
if (!badged) {
return unbadgedBitmap;
}
- unbadgedBitmap = ShadowGenerator.getInstance(context).recreateIcon(unbadgedBitmap);
- return badgeWithDrawable(unbadgedBitmap,
- new FastBitmapDrawable(getShortcutInfoBadge(shortcutInfo, cache)), context);
+
+ int size = app.getInvariantDeviceProfile().iconBitmapSize;
+
+ final Bitmap unbadgedfinal = unbadgedBitmap;
+ final Bitmap badge = getShortcutInfoBadge(shortcutInfo, cache);
+
+ return UiFactory.createFromRenderer(size, size, false, (c) -> {
+ ShadowGenerator.getInstance(context).recreateIcon(unbadgedfinal, c);
+ badgeWithDrawable(c, new FastBitmapDrawable(badge), context);
+ });
}
public static Bitmap getShortcutInfoBadge(ShortcutInfoCompat shortcutInfo, IconCache cache) {
diff --git a/src/com/android/launcher3/graphics/ShadowGenerator.java b/src/com/android/launcher3/graphics/ShadowGenerator.java
index 60eeef5..08be4c9 100644
--- a/src/com/android/launcher3/graphics/ShadowGenerator.java
+++ b/src/com/android/launcher3/graphics/ShadowGenerator.java
@@ -50,49 +50,38 @@
private final int mIconSize;
- private final Canvas mCanvas;
private final Paint mBlurPaint;
private final Paint mDrawPaint;
private final BlurMaskFilter mDefaultBlurMaskFilter;
private ShadowGenerator(Context context) {
mIconSize = LauncherAppState.getIDP(context).iconBitmapSize;
- mCanvas = new Canvas();
mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
mDefaultBlurMaskFilter = new BlurMaskFilter(mIconSize * BLUR_FACTOR, Blur.NORMAL);
}
- public synchronized Bitmap recreateIcon(Bitmap icon) {
- return recreateIcon(icon, true, mDefaultBlurMaskFilter, AMBIENT_SHADOW_ALPHA,
- KEY_SHADOW_ALPHA);
+ public synchronized void recreateIcon(Bitmap icon, Canvas out) {
+ recreateIcon(icon, mDefaultBlurMaskFilter, AMBIENT_SHADOW_ALPHA, KEY_SHADOW_ALPHA, out);
}
- public synchronized Bitmap recreateIcon(Bitmap icon, boolean resize,
- BlurMaskFilter blurMaskFilter, int ambientAlpha, int keyAlpha) {
- int width = resize ? mIconSize : icon.getWidth();
- int height = resize ? mIconSize : icon.getHeight();
+ public synchronized void recreateIcon(Bitmap icon, BlurMaskFilter blurMaskFilter,
+ int ambientAlpha, int keyAlpha, Canvas out) {
int[] offset = new int[2];
-
mBlurPaint.setMaskFilter(blurMaskFilter);
Bitmap shadow = icon.extractAlpha(mBlurPaint, offset);
- Bitmap result = Bitmap.createBitmap(width, height, Config.ARGB_8888);
- mCanvas.setBitmap(result);
// Draw ambient shadow
mDrawPaint.setAlpha(ambientAlpha);
- mCanvas.drawBitmap(shadow, offset[0], offset[1], mDrawPaint);
+ out.drawBitmap(shadow, offset[0], offset[1], mDrawPaint);
// Draw key shadow
mDrawPaint.setAlpha(keyAlpha);
- mCanvas.drawBitmap(shadow, offset[0], offset[1] + KEY_SHADOW_DISTANCE * mIconSize, mDrawPaint);
+ out.drawBitmap(shadow, offset[0], offset[1] + KEY_SHADOW_DISTANCE * mIconSize, mDrawPaint);
// Draw the icon
mDrawPaint.setAlpha(255);
- mCanvas.drawBitmap(icon, 0, 0, mDrawPaint);
-
- mCanvas.setBitmap(null);
- return result;
+ out.drawBitmap(icon, 0, 0, mDrawPaint);
}
public static ShadowGenerator getInstance(Context context) {
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index 2e29015..fc81e80 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -18,14 +18,19 @@
import static com.android.launcher3.LauncherState.OVERVIEW;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.view.View.AccessibilityDelegate;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.graphics.BitmapRenderer;
import com.android.launcher3.util.TouchController;
public class UiFactory {
+ public static final boolean USE_HARDWARE_BITMAP = false;
+
public static TouchController[] createTouchControllers(Launcher launcher) {
return new TouchController[] {
new AllAppsSwipeController(launcher), new PinchToOverviewListener(launcher)};
@@ -44,4 +49,11 @@
public static void onWorkspaceLongPress(Launcher launcher) {
launcher.getStateManager().goToState(OVERVIEW);
}
+
+ public static Bitmap createFromRenderer(int width, int height, boolean forceSoftwareRenderer,
+ BitmapRenderer renderer) {
+ Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ renderer.render(new Canvas(result));
+ return result;
+ }
}