diff options
Diffstat (limited to 'libs')
362 files changed, 27790 insertions, 12023 deletions
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp new file mode 100644 index 000000000000..4f4364f72fef --- /dev/null +++ b/libs/WindowManager/Jetpack/Android.bp @@ -0,0 +1,38 @@ +// Copyright (C) 2020 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. + +android_library_import { + name: "window-sidecar", + aars: ["window-sidecar-release.aar"], + sdk_version: "current", +} + +java_library { + name: "androidx.window.sidecar", + srcs: ["src/**/*.java"], + static_libs: ["window-sidecar"], + installable: true, + sdk_version: "core_platform", + vendor: true, + libs: ["framework", "androidx.annotation_annotation",], + required: ["androidx.window.sidecar.xml",], +} + +prebuilt_etc { + name: "androidx.window.sidecar.xml", + vendor: true, + sub_dir: "permissions", + src: "androidx.window.sidecar.xml", + filename_from_src: true, +} diff --git a/libs/WindowManager/Jetpack/androidx.window.sidecar.xml b/libs/WindowManager/Jetpack/androidx.window.sidecar.xml new file mode 100644 index 000000000000..f88a5f4ae039 --- /dev/null +++ b/libs/WindowManager/Jetpack/androidx.window.sidecar.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 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. + --> +<permissions> + <library + name="androidx.window.sidecar" + file="/vendor/framework/androidx.window.sidecar.jar"/> +</permissions> diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java new file mode 100644 index 000000000000..92e575804bbe --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2020 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 androidx.window.sidecar; + +import static android.view.Display.DEFAULT_DISPLAY; + +import static androidx.window.sidecar.SidecarHelper.getWindowDisplay; +import static androidx.window.sidecar.SidecarHelper.isInMultiWindow; +import static androidx.window.sidecar.SidecarHelper.rotateRectToDisplayRotation; +import static androidx.window.sidecar.SidecarHelper.transformToWindowSpaceRect; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.graphics.Rect; +import android.net.Uri; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +class SettingsSidecarImpl extends StubSidecar { + private static final String TAG = "SettingsSidecar"; + + private static final String DEVICE_POSTURE = "device_posture"; + private static final String DISPLAY_FEATURES = "display_features"; + + private static final Pattern FEATURE_PATTERN = + Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]"); + + private static final String FEATURE_TYPE_FOLD = "fold"; + private static final String FEATURE_TYPE_HINGE = "hinge"; + + private Context mContext; + private SettingsObserver mSettingsObserver; + + final class SettingsObserver extends ContentObserver { + private final Uri mDevicePostureUri = + Settings.Global.getUriFor(DEVICE_POSTURE); + private final Uri mDisplayFeaturesUri = + Settings.Global.getUriFor(DISPLAY_FEATURES); + private final ContentResolver mResolver = mContext.getContentResolver(); + private boolean mRegisteredObservers; + + + private SettingsObserver() { + super(new Handler(Looper.getMainLooper())); + } + + private void registerObserversIfNeeded() { + if (mRegisteredObservers) { + return; + } + mRegisteredObservers = true; + mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendents */, + this /* ContentObserver */); + mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendents */, + this /* ContentObserver */); + } + + private void unregisterObserversIfNeeded() { + if (!mRegisteredObservers) { + return; + } + mRegisteredObservers = false; + mResolver.unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + if (uri == null) { + return; + } + + if (mDevicePostureUri.equals(uri)) { + updateDevicePosture(); + return; + } + if (mDisplayFeaturesUri.equals(uri)) { + updateDisplayFeatures(); + return; + } + } + } + + SettingsSidecarImpl(Context context) { + mContext = context; + mSettingsObserver = new SettingsObserver(); + } + + private void updateDevicePosture() { + updateDeviceState(getDeviceState()); + } + + /** Update display features with values read from settings. */ + private void updateDisplayFeatures() { + for (IBinder windowToken : getWindowsListeningForLayoutChanges()) { + SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken); + updateWindowLayout(windowToken, newLayout); + } + } + + @NonNull + @Override + public SidecarDeviceState getDeviceState() { + ContentResolver resolver = mContext.getContentResolver(); + int posture = Settings.Global.getInt(resolver, DEVICE_POSTURE, + SidecarDeviceState.POSTURE_UNKNOWN); + SidecarDeviceState deviceState = new SidecarDeviceState(); + deviceState.posture = posture; + return deviceState; + } + + @NonNull + @Override + public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) { + List<SidecarDisplayFeature> displayFeatures = readDisplayFeatures(windowToken); + SidecarWindowLayoutInfo windowLayoutInfo = new SidecarWindowLayoutInfo(); + windowLayoutInfo.displayFeatures = displayFeatures; + return windowLayoutInfo; + } + + private List<SidecarDisplayFeature> readDisplayFeatures(IBinder windowToken) { + List<SidecarDisplayFeature> features = new ArrayList<SidecarDisplayFeature>(); + int displayId = getWindowDisplay(windowToken); + if (displayId != DEFAULT_DISPLAY) { + Log.w(TAG, "This sample doesn't support display features on secondary displays"); + return features; + } + + ContentResolver resolver = mContext.getContentResolver(); + final String displayFeaturesString = Settings.Global.getString(resolver, DISPLAY_FEATURES); + if (isInMultiWindow(windowToken)) { + // It is recommended not to report any display features in multi-window mode, since it + // won't be possible to synchronize the display feature positions with window movement. + return features; + } + if (TextUtils.isEmpty(displayFeaturesString)) { + return features; + } + + String[] featureStrings = displayFeaturesString.split(";"); + for (String featureString : featureStrings) { + Matcher featureMatcher = FEATURE_PATTERN.matcher(featureString); + if (!featureMatcher.matches()) { + Log.e(TAG, "Malformed feature description format: " + featureString); + continue; + } + try { + String featureType = featureMatcher.group(1); + int type; + switch (featureType) { + case FEATURE_TYPE_FOLD: + type = SidecarDisplayFeature.TYPE_FOLD; + break; + case FEATURE_TYPE_HINGE: + type = SidecarDisplayFeature.TYPE_HINGE; + break; + default: { + Log.e(TAG, "Malformed feature type: " + featureType); + continue; + } + } + + int left = Integer.parseInt(featureMatcher.group(2)); + int top = Integer.parseInt(featureMatcher.group(3)); + int right = Integer.parseInt(featureMatcher.group(4)); + int bottom = Integer.parseInt(featureMatcher.group(5)); + Rect featureRect = new Rect(left, top, right, bottom); + rotateRectToDisplayRotation(featureRect, displayId); + transformToWindowSpaceRect(featureRect, windowToken); + if (!featureRect.isEmpty()) { + SidecarDisplayFeature feature = new SidecarDisplayFeature(); + feature.setRect(featureRect); + feature.setType(type); + features.add(feature); + } else { + Log.w(TAG, "Failed to adjust feature to window"); + } + } catch (NumberFormatException e) { + Log.e(TAG, "Malformed feature description: " + featureString); + } + } + return features; + } + + @Override + protected void onListenersChanged() { + if (mSettingsObserver == null) { + return; + } + + if (hasListeners()) { + mSettingsObserver.registerObserversIfNeeded(); + } else { + mSettingsObserver.unregisterObserversIfNeeded(); + } + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java new file mode 100644 index 000000000000..e5b6cff17b26 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2020 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 androidx.window.sidecar; + +import static android.view.Display.INVALID_DISPLAY; +import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; + +import android.app.Activity; +import android.app.ActivityThread; +import android.graphics.Rect; +import android.hardware.display.DisplayManagerGlobal; +import android.os.IBinder; +import android.view.DisplayInfo; +import android.view.Surface; + +import androidx.annotation.Nullable; + +class SidecarHelper { + /** + * Rotate the input rectangle specified in default display orientation to the current display + * rotation. + */ + static void rotateRectToDisplayRotation(Rect inOutRect, int displayId) { + DisplayManagerGlobal dmGlobal = DisplayManagerGlobal.getInstance(); + DisplayInfo displayInfo = dmGlobal.getDisplayInfo(displayId); + int rotation = displayInfo.rotation; + + boolean isSideRotation = rotation == ROTATION_90 || rotation == ROTATION_270; + int displayWidth = isSideRotation ? displayInfo.logicalHeight : displayInfo.logicalWidth; + int displayHeight = isSideRotation ? displayInfo.logicalWidth : displayInfo.logicalHeight; + + inOutRect.intersect(0, 0, displayWidth, displayHeight); + + rotateBounds(inOutRect, displayWidth, displayHeight, rotation); + } + + /** + * Rotate the input rectangle within parent bounds for a given delta. + */ + private static void rotateBounds(Rect inOutRect, int parentWidth, int parentHeight, + @Surface.Rotation int delta) { + int origLeft = inOutRect.left; + switch (delta) { + case ROTATION_0: + return; + case ROTATION_90: + inOutRect.left = inOutRect.top; + inOutRect.top = parentWidth - inOutRect.right; + inOutRect.right = inOutRect.bottom; + inOutRect.bottom = parentWidth - origLeft; + return; + case ROTATION_180: + inOutRect.left = parentWidth - inOutRect.right; + inOutRect.right = parentWidth - origLeft; + return; + case ROTATION_270: + inOutRect.left = parentHeight - inOutRect.bottom; + inOutRect.bottom = inOutRect.right; + inOutRect.right = parentHeight - inOutRect.top; + inOutRect.top = origLeft; + return; + } + } + + /** Transform rectangle from absolute coordinate space to the window coordinate space. */ + static void transformToWindowSpaceRect(Rect inOutRect, IBinder windowToken) { + Rect windowRect = getWindowBounds(windowToken); + if (windowRect == null) { + inOutRect.setEmpty(); + return; + } + if (!Rect.intersects(inOutRect, windowRect)) { + inOutRect.setEmpty(); + return; + } + inOutRect.intersect(windowRect); + inOutRect.offset(-windowRect.left, -windowRect.top); + } + + /** + * Get the current window bounds in absolute coordinates. + * NOTE: Only works with Activity windows. + */ + @Nullable + private static Rect getWindowBounds(IBinder windowToken) { + Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken); + return activity != null + ? activity.getWindowManager().getCurrentWindowMetrics().getBounds() + : null; + } + + /** + * Check if this window is an Activity window that is in multi-window mode. + */ + static boolean isInMultiWindow(IBinder windowToken) { + Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken); + return activity != null && activity.isInMultiWindowMode(); + } + + /** + * Get the id of the parent display for the window. + * NOTE: Only works with Activity windows. + */ + static int getWindowDisplay(IBinder windowToken) { + Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken); + return activity != null + ? activity.getWindowManager().getDefaultDisplay().getDisplayId() : INVALID_DISPLAY; + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java new file mode 100644 index 000000000000..0b4915ed5dac --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 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 androidx.window.sidecar; + +import android.content.Context; + +/** + * Provider class that will instantiate the library implementation. It must be included in the + * vendor library, and the vendor implementation must match the signature of this class. + */ +public class SidecarProvider { + /** + * Provide a simple implementation of {@link SidecarInterface} that can be replaced by + * an OEM by overriding this method. + */ + public static SidecarInterface getSidecarImpl(Context context) { + return new SettingsSidecarImpl(context); + } + + /** + * The support library will use this method to check API version compatibility. + * @return API version string in MAJOR.MINOR.PATCH-description format. + */ + public static String getApiVersion() { + return "0.1.0-settings_sample"; + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java new file mode 100644 index 000000000000..199c37315c07 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2020 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 androidx.window.sidecar; + +import android.os.IBinder; + +import androidx.annotation.NonNull; + +import java.util.HashSet; +import java.util.Set; + +/** + * Basic implementation of the {@link SidecarInterface}. An OEM can choose to use it as the base + * class for their implementation. + */ +abstract class StubSidecar implements SidecarInterface { + + private SidecarCallback mSidecarCallback; + private final Set<IBinder> mWindowLayoutChangeListenerTokens = new HashSet<>(); + private boolean mDeviceStateChangeListenerRegistered; + + StubSidecar() { + } + + @Override + public void setSidecarCallback(@NonNull SidecarCallback sidecarCallback) { + this.mSidecarCallback = sidecarCallback; + } + + @Override + public void onWindowLayoutChangeListenerAdded(@NonNull IBinder iBinder) { + this.mWindowLayoutChangeListenerTokens.add(iBinder); + this.onListenersChanged(); + } + + @Override + public void onWindowLayoutChangeListenerRemoved(@NonNull IBinder iBinder) { + this.mWindowLayoutChangeListenerTokens.remove(iBinder); + this.onListenersChanged(); + } + + @Override + public void onDeviceStateListenersChanged(boolean isEmpty) { + this.mDeviceStateChangeListenerRegistered = !isEmpty; + this.onListenersChanged(); + } + + void updateDeviceState(SidecarDeviceState newState) { + if (this.mSidecarCallback != null) { + mSidecarCallback.onDeviceStateChanged(newState); + } + } + + void updateWindowLayout(@NonNull IBinder windowToken, + @NonNull SidecarWindowLayoutInfo newLayout) { + if (this.mSidecarCallback != null) { + mSidecarCallback.onWindowLayoutChanged(windowToken, newLayout); + } + } + + @NonNull + Set<IBinder> getWindowsListeningForLayoutChanges() { + return mWindowLayoutChangeListenerTokens; + } + + protected boolean hasListeners() { + return !mWindowLayoutChangeListenerTokens.isEmpty() || mDeviceStateChangeListenerRegistered; + } + + protected abstract void onListenersChanged(); +} diff --git a/libs/WindowManager/Jetpack/window-sidecar-release.aar b/libs/WindowManager/Jetpack/window-sidecar-release.aar Binary files differnew file mode 100644 index 000000000000..50f101d7d181 --- /dev/null +++ b/libs/WindowManager/Jetpack/window-sidecar-release.aar diff --git a/libs/WindowManager/OWNERS b/libs/WindowManager/OWNERS new file mode 100644 index 000000000000..063d4594f2fa --- /dev/null +++ b/libs/WindowManager/OWNERS @@ -0,0 +1,3 @@ +set noparent + +include ../../services/core/java/com/android/server/wm/OWNERS
\ No newline at end of file diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp new file mode 100644 index 000000000000..b8934dc8c583 --- /dev/null +++ b/libs/WindowManager/Shell/Android.bp @@ -0,0 +1,29 @@ +// Copyright (C) 2019 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. + +android_library { + name: "WindowManager-Shell", + srcs: [ + "src/**/*.java", + "src/**/I*.aidl", + ], + resource_dirs: [ + "res", + ], + manifest: "AndroidManifest.xml", + + platform_apis: true, + sdk_version: "current", + min_sdk_version: "system_current", +} diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml new file mode 100644 index 000000000000..ea8a5c305029 --- /dev/null +++ b/libs/WindowManager/Shell/AndroidManifest.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.wm.shell"> +</manifest> diff --git a/libs/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS new file mode 100644 index 000000000000..4390004f5f93 --- /dev/null +++ b/libs/WindowManager/Shell/OWNERS @@ -0,0 +1,4 @@ +# sysui owners +hwwang@google.com +mrenouf@google.com +winsonc@google.com
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml new file mode 100644 index 000000000000..c894eb0133b5 --- /dev/null +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2019, 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. +*/ +--> + +<resources> +</resources>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java new file mode 100644 index 000000000000..273bd27a221b --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2019 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.wm.shell; + +/** + * Interface for the shell. + */ +public class WindowManagerShell { +} diff --git a/libs/WindowManager/Shell/tests/Android.bp b/libs/WindowManager/Shell/tests/Android.bp new file mode 100644 index 000000000000..78fa45ebdf94 --- /dev/null +++ b/libs/WindowManager/Shell/tests/Android.bp @@ -0,0 +1,45 @@ +// Copyright (C) 2019 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. + +android_test { + name: "WindowManagerShellTests", + + srcs: ["**/*.java"], + + static_libs: [ + "WindowManager-Shell", + "junit", + "androidx.test.runner", + "androidx.test.rules", + "androidx.test.ext.junit", + "mockito-target-extended-minus-junit4", + "truth-prebuilt", + ], + libs: [ + "android.test.mock", + "android.test.base", + "android.test.runner", + ], + jni_libs: [ + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + ], + + sdk_version: "current", + platform_apis: true, + + optimize: { + enabled: false, + }, +} diff --git a/libs/WindowManager/Shell/tests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/AndroidManifest.xml new file mode 100644 index 000000000000..a8f795ec8a8d --- /dev/null +++ b/libs/WindowManager/Shell/tests/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="com.android.wm.shell.tests"> + + <application android:debuggable="true" android:largeHeap="true"> + <uses-library android:name="android.test.mock" /> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:label="Tests for WindowManager-Shell" + android:targetPackage="com.android.wm.shell.tests"> + </instrumentation> +</manifest> diff --git a/libs/WindowManager/Shell/tests/AndroidTest.xml b/libs/WindowManager/Shell/tests/AndroidTest.xml new file mode 100644 index 000000000000..4dce4db360e4 --- /dev/null +++ b/libs/WindowManager/Shell/tests/AndroidTest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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. +--> +<configuration description="Runs Tests for WindowManagerShellLib"> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="install-arg" value="-t" /> + <option name="test-file-name" value="WindowManagerShellTests.apk" /> + </target_preparer> + + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="framework-base-presubmit" /> + <option name="test-tag" value="WindowManagerShellTests" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.wm.shell.tests" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/libs/WindowManager/Shell/tests/res/values/config.xml b/libs/WindowManager/Shell/tests/res/values/config.xml new file mode 100644 index 000000000000..c894eb0133b5 --- /dev/null +++ b/libs/WindowManager/Shell/tests/res/values/config.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2019, 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. +*/ +--> + +<resources> +</resources>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java new file mode 100644 index 000000000000..376875b143a1 --- /dev/null +++ b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 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.wm.shell.tests; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.WindowManagerShell; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for the shell. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class WindowManagerShellTest { + + WindowManagerShell mShell; + + @Test + public void testNothing() { + // Do nothing + } +} diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index a35143756d65..aa34edf487fe 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -67,7 +67,6 @@ cc_library { "BackupData.cpp", "BackupHelpers.cpp", "CursorWindow.cpp", - "DisplayEventDispatcher.cpp", ], shared_libs: [ "libziparchive", @@ -75,7 +74,6 @@ cc_library { "libbinder", "liblog", "libcutils", - "libgui", "libutils", "libz", ], @@ -166,7 +164,11 @@ cc_test { static_libs: common_test_libs + ["liblog", "libz"], }, }, - data: ["tests/data/**/*.apk"], + data: [ + "tests/data/**/*.apk", + "tests/data/**/*.arsc", + "tests/data/**/*.idmap", + ], test_suites: ["device-tests"], } @@ -188,4 +190,3 @@ cc_benchmark { shared_libs: common_test_libs, data: ["tests/data/**/*.apk"], } - diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 09705e1aae67..e15b42d46f53 100644..100755 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -21,6 +21,7 @@ #include "android-base/errors.h" #include "android-base/file.h" #include "android-base/logging.h" +#include "android-base/stringprintf.h" #include "android-base/unique_fd.h" #include "android-base/utf8.h" #include "utils/Compat.h" @@ -40,23 +41,342 @@ using base::unique_fd; static const std::string kResourcesArsc("resources.arsc"); -ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle, - const std::string& path, - time_t last_mod_time) - : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time) { +ApkAssets::ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider, + std::string path, + time_t last_mod_time, + package_property_t property_flags) + : assets_provider_(std::move(assets_provider)), + path_(std::move(path)), + last_mod_time_(last_mod_time), + property_flags_(property_flags) { } -std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system) { - return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, false /*load_as_shared_library*/); +// Provides asset files from a zip file. +class ZipAssetsProvider : public AssetsProvider { + public: + ~ZipAssetsProvider() override = default; + + static std::unique_ptr<const AssetsProvider> Create(const std::string& path) { + ::ZipArchiveHandle unmanaged_handle; + const int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle); + if (result != 0) { + LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result); + ::CloseArchive(unmanaged_handle); + return {}; + } + + return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider(path, path, unmanaged_handle)); + } + + static std::unique_ptr<const AssetsProvider> Create( + unique_fd fd, const std::string& friendly_name, const off64_t offset = 0, + const off64_t length = ApkAssets::kUnknownLength) { + + ::ZipArchiveHandle unmanaged_handle; + const int32_t result = (length == ApkAssets::kUnknownLength) + ? ::OpenArchiveFd(fd.release(), friendly_name.c_str(), &unmanaged_handle) + : ::OpenArchiveFdRange(fd.release(), friendly_name.c_str(), &unmanaged_handle, length, + offset); + + if (result != 0) { + LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset + << " and length " << length << ": " << ::ErrorCodeString(result); + ::CloseArchive(unmanaged_handle); + return {}; + } + + return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider({}, friendly_name, + unmanaged_handle)); + } + + // Iterate over all files and directories within the zip. The order of iteration is not + // guaranteed to be the same as the order of elements in the central directory but is stable for a + // given zip file. + bool ForEachFile(const std::string& root_path, + const std::function<void(const StringPiece&, FileType)>& f) const override { + // If this is a resource loader from an .arsc, there will be no zip handle + if (zip_handle_ == nullptr) { + return false; + } + + std::string root_path_full = root_path; + if (root_path_full.back() != '/') { + root_path_full += '/'; + } + + void* cookie; + if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) { + return false; + } + + std::string name; + ::ZipEntry entry{}; + + // We need to hold back directories because many paths will contain them and we want to only + // surface one. + std::set<std::string> dirs{}; + + int32_t result; + while ((result = ::Next(cookie, &entry, &name)) == 0) { + StringPiece full_file_path(name); + StringPiece leaf_file_path = full_file_path.substr(root_path_full.size()); + + if (!leaf_file_path.empty()) { + auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/'); + if (iter != leaf_file_path.end()) { + std::string dir = + leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string(); + dirs.insert(std::move(dir)); + } else { + f(leaf_file_path, kFileTypeRegular); + } + } + } + ::EndIteration(cookie); + + // Now present the unique directories. + for (const std::string& dir : dirs) { + f(dir, kFileTypeDirectory); + } + + // -1 is end of iteration, anything else is an error. + return result == -1; + } + + protected: + std::unique_ptr<Asset> OpenInternal( + const std::string& path, Asset::AccessMode mode, bool* file_exists) const override { + if (file_exists) { + *file_exists = false; + } + + ::ZipEntry entry; + int32_t result = ::FindEntry(zip_handle_.get(), path, &entry); + if (result != 0) { + return {}; + } + + if (file_exists) { + *file_exists = true; + } + + const int fd = ::GetFileDescriptor(zip_handle_.get()); + const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get()); + if (entry.method == kCompressDeflated) { + std::unique_ptr<FileMap> map = util::make_unique<FileMap>(); + if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.compressed_length, + true /*readOnly*/)) { + LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; + return {}; + } + + std::unique_ptr<Asset> asset = + Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode); + if (asset == nullptr) { + LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << friendly_name_ << "'"; + return {}; + } + return asset; + } else { + std::unique_ptr<FileMap> map = util::make_unique<FileMap>(); + if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.uncompressed_length, + true /*readOnly*/)) { + LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; + return {}; + } + + unique_fd ufd; + if (!GetPath()) { + // If the `path` is not set, create a new `fd` for the new Asset to own in order to create + // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used + // to create new file descriptors. + ufd = unique_fd(dup(fd)); + if (!ufd.ok()) { + LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'"; + return {}; + } + } + + std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map), + std::move(ufd), mode); + if (asset == nullptr) { + LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; + return {}; + } + return asset; + } + } + + private: + DISALLOW_COPY_AND_ASSIGN(ZipAssetsProvider); + + explicit ZipAssetsProvider(std::string path, + std::string friendly_name, + ZipArchiveHandle unmanaged_handle) + : zip_handle_(unmanaged_handle, ::CloseArchive), + path_(std::move(path)), + friendly_name_(std::move(friendly_name)) { } + + const char* GetPath() const { + return path_.empty() ? nullptr : path_.c_str(); + } + + using ZipArchivePtr = std::unique_ptr<ZipArchive, void (*)(ZipArchiveHandle)>; + ZipArchivePtr zip_handle_; + std::string path_; + std::string friendly_name_; +}; + +class DirectoryAssetsProvider : AssetsProvider { + public: + ~DirectoryAssetsProvider() override = default; + + static std::unique_ptr<const AssetsProvider> Create(const std::string& path) { + struct stat sb{}; + const int result = stat(path.c_str(), &sb); + if (result == -1) { + LOG(ERROR) << "Failed to find directory '" << path << "'."; + return nullptr; + } + + if (!S_ISDIR(sb.st_mode)) { + LOG(ERROR) << "Path '" << path << "' is not a directory."; + return nullptr; + } + + return std::unique_ptr<AssetsProvider>(new DirectoryAssetsProvider(path)); + } + + protected: + std::unique_ptr<Asset> OpenInternal( + const std::string& path, Asset::AccessMode /* mode */, bool* file_exists) const override { + const std::string resolved_path = ResolvePath(path); + if (file_exists) { + struct stat sb{}; + const int result = stat(resolved_path.c_str(), &sb); + *file_exists = result != -1 && S_ISREG(sb.st_mode); + } + + return ApkAssets::CreateAssetFromFile(resolved_path); + } + + private: + DISALLOW_COPY_AND_ASSIGN(DirectoryAssetsProvider); + + explicit DirectoryAssetsProvider(std::string path) : path_(std::move(path)) { } + + inline std::string ResolvePath(const std::string& path) const { + return base::StringPrintf("%s%c%s", path_.c_str(), OS_PATH_SEPARATOR, path.c_str()); + } + + const std::string path_; +}; + +// AssetProvider implementation that does not provide any assets. Used for ApkAssets::LoadEmpty. +class EmptyAssetsProvider : public AssetsProvider { + public: + EmptyAssetsProvider() = default; + ~EmptyAssetsProvider() override = default; + + protected: + std::unique_ptr<Asset> OpenInternal(const std::string& /*path */, + Asset::AccessMode /* mode */, + bool* file_exists) const override { + if (file_exists) { + *file_exists = false; + } + return nullptr; + } + + private: + DISALLOW_COPY_AND_ASSIGN(EmptyAssetsProvider); +}; + +// AssetProvider implementation +class MultiAssetsProvider : public AssetsProvider { + public: + ~MultiAssetsProvider() override = default; + + static std::unique_ptr<const AssetsProvider> Create( + std::unique_ptr<const AssetsProvider> child, std::unique_ptr<const AssetsProvider> parent) { + CHECK(parent != nullptr) << "parent provider must not be null"; + return (!child) ? std::move(parent) + : std::unique_ptr<const AssetsProvider>(new MultiAssetsProvider( + std::move(child), std::move(parent))); + } + + bool ForEachFile(const std::string& root_path, + const std::function<void(const StringPiece&, FileType)>& f) const override { + // TODO: Only call the function once for files defined in the parent and child + return child_->ForEachFile(root_path, f) && parent_->ForEachFile(root_path, f); + } + + protected: + std::unique_ptr<Asset> OpenInternal( + const std::string& path, Asset::AccessMode mode, bool* file_exists) const override { + auto asset = child_->Open(path, mode, file_exists); + return (asset) ? std::move(asset) : parent_->Open(path, mode, file_exists); + } + + private: + DISALLOW_COPY_AND_ASSIGN(MultiAssetsProvider); + + MultiAssetsProvider(std::unique_ptr<const AssetsProvider> child, + std::unique_ptr<const AssetsProvider> parent) + : child_(std::move(child)), parent_(std::move(parent)) { } + + std::unique_ptr<const AssetsProvider> child_; + std::unique_ptr<const AssetsProvider> parent_; +}; + +// Opens the archive using the file path. Calling CloseArchive on the zip handle will close the +// file. +std::unique_ptr<const ApkAssets> ApkAssets::Load( + const std::string& path, const package_property_t flags, + std::unique_ptr<const AssetsProvider> override_asset) { + auto assets = ZipAssetsProvider::Create(path); + return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset)) + : nullptr; } -std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path, - bool system) { - return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, true /*load_as_shared_library*/); +// Opens the archive using the file file descriptor with the specified file offset and read length. +// If the `assume_ownership` parameter is 'true' calling CloseArchive will close the file. +std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd( + unique_fd fd, const std::string& friendly_name, const package_property_t flags, + std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset, + const off64_t length) { + CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength; + CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is " + << kUnknownLength; + + auto assets = ZipAssetsProvider::Create(std::move(fd), friendly_name, offset, length); + return (assets) ? LoadImpl(std::move(assets), friendly_name, flags, std::move(override_asset)) + : nullptr; +} + +std::unique_ptr<const ApkAssets> ApkAssets::LoadTable( + const std::string& path, const package_property_t flags, + std::unique_ptr<const AssetsProvider> override_asset) { + + auto assets = CreateAssetFromFile(path); + return (assets) ? LoadTableImpl(std::move(assets), path, flags, std::move(override_asset)) + : nullptr; +} + +std::unique_ptr<const ApkAssets> ApkAssets::LoadTableFromFd( + unique_fd fd, const std::string& friendly_name, const package_property_t flags, + std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset, + const off64_t length) { + + auto assets = CreateAssetFromFd(std::move(fd), nullptr /* path */, offset, length); + return (assets) ? LoadTableImpl(std::move(assets), friendly_name, flags, + std::move(override_asset)) + : nullptr; } std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path, - bool system) { + const package_property_t flags) { + CHECK((flags & PROPERTY_LOADER) == 0U) << "Cannot load RROs through loaders"; std::unique_ptr<Asset> idmap_asset = CreateAssetFromFile(idmap_path); if (idmap_asset == nullptr) { return {}; @@ -65,97 +385,125 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap const StringPiece idmap_data( reinterpret_cast<const char*>(idmap_asset->getBuffer(true /*wordAligned*/)), static_cast<size_t>(idmap_asset->getLength())); - std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(idmap_data); + std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(idmap_path, idmap_data); if (loaded_idmap == nullptr) { LOG(ERROR) << "failed to load IDMAP " << idmap_path; return {}; } - auto apkPath = loaded_idmap->OverlayApkPath(); - return LoadImpl({} /*fd*/, apkPath, - std::move(idmap_asset), - std::move(loaded_idmap), - system, false /*load_as_shared_library*/); + + auto overlay_path = loaded_idmap->OverlayApkPath(); + auto assets = ZipAssetsProvider::Create(overlay_path); + return (assets) ? LoadImpl(std::move(assets), overlay_path, flags | PROPERTY_OVERLAY, + nullptr /* override_asset */, std::move(idmap_asset), + std::move(loaded_idmap)) + : nullptr; +} + +std::unique_ptr<const ApkAssets> ApkAssets::LoadFromDir( + const std::string& path, const package_property_t flags, + std::unique_ptr<const AssetsProvider> override_asset) { + + auto assets = DirectoryAssetsProvider::Create(path); + return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset)) + : nullptr; } -std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd, - const std::string& friendly_name, - bool system, bool force_shared_lib) { - return LoadImpl(std::move(fd), friendly_name, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/, - system, force_shared_lib); +std::unique_ptr<const ApkAssets> ApkAssets::LoadEmpty( + const package_property_t flags, std::unique_ptr<const AssetsProvider> override_asset) { + + auto assets = (override_asset) ? std::move(override_asset) + : std::unique_ptr<const AssetsProvider>(new EmptyAssetsProvider()); + std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(std::move(assets), "empty" /* path */, + -1 /* last_mod-time */, flags)); + loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty(); + // Need to force a move for mingw32. + return std::move(loaded_apk); } std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) { unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC)); - if (fd == -1) { + if (!fd.ok()) { LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno); return {}; } - const off64_t file_len = lseek64(fd, 0, SEEK_END); - if (file_len < 0) { - LOG(ERROR) << "Failed to get size of file '" << path << "': " << SystemErrorCodeToString(errno); - return {}; + return CreateAssetFromFd(std::move(fd), path.c_str()); +} + +std::unique_ptr<Asset> ApkAssets::CreateAssetFromFd(base::unique_fd fd, + const char* path, + off64_t offset, + off64_t length) { + CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength; + CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is " + << kUnknownLength; + if (length == kUnknownLength) { + length = lseek64(fd, 0, SEEK_END); + if (length < 0) { + LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': " + << SystemErrorCodeToString(errno); + return {}; + } } std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>(); - if (!file_map->create(path.c_str(), fd, 0, static_cast<size_t>(file_len), true /*readOnly*/)) { - LOG(ERROR) << "Failed to mmap file '" << path << "': " << SystemErrorCodeToString(errno); + if (!file_map->create(path, fd, offset, static_cast<size_t>(length), true /*readOnly*/)) { + LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': " + << SystemErrorCodeToString(errno); return {}; } - return Asset::createFromUncompressedMap(std::move(file_map), Asset::AccessMode::ACCESS_RANDOM); + + // If `path` is set, do not pass ownership of the `fd` to the new Asset since + // Asset::openFileDescriptor can use `path` to create new file descriptors. + return Asset::createFromUncompressedMap(std::move(file_map), + (path) ? base::unique_fd(-1) : std::move(fd), + Asset::AccessMode::ACCESS_RANDOM); } std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl( - unique_fd fd, const std::string& path, std::unique_ptr<Asset> idmap_asset, - std::unique_ptr<const LoadedIdmap> loaded_idmap, bool system, bool load_as_shared_library) { - ::ZipArchiveHandle unmanaged_handle; - int32_t result; - if (fd >= 0) { - result = - ::OpenArchiveFd(fd.release(), path.c_str(), &unmanaged_handle, true /*assume_ownership*/); - } else { - result = ::OpenArchive(path.c_str(), &unmanaged_handle); - } + std::unique_ptr<const AssetsProvider> assets, const std::string& path, + package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets, + std::unique_ptr<Asset> idmap_asset, std::unique_ptr<const LoadedIdmap> idmap) { - if (result != 0) { - LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result); - return {}; - } + const time_t last_mod_time = getFileModDate(path.c_str()); + + // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open. + bool resources_asset_exists = false; + auto resources_asset_ = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER, + &resources_asset_exists); - time_t last_mod_time = getFileModDate(path.c_str()); + assets = MultiAssetsProvider::Create(std::move(override_assets), std::move(assets)); // Wrap the handle in a unique_ptr so it gets automatically closed. - std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time)); + std::unique_ptr<ApkAssets> + loaded_apk(new ApkAssets(std::move(assets), path, last_mod_time, property_flags)); - // Find the resource table. - ::ZipEntry entry; - result = ::FindEntry(loaded_apk->zip_handle_.get(), kResourcesArsc, &entry); - if (result != 0) { - // There is no resources.arsc, so create an empty LoadedArsc and return. + if (!resources_asset_exists) { loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty(); return std::move(loaded_apk); } - if (entry.method == kCompressDeflated) { - LOG(WARNING) << kResourcesArsc << " in APK '" << path << "' is compressed."; - } - - // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open. - loaded_apk->resources_asset_ = loaded_apk->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER); - if (loaded_apk->resources_asset_ == nullptr) { + loaded_apk->resources_asset_ = std::move(resources_asset_); + if (!loaded_apk->resources_asset_) { LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'."; return {}; } // Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid. loaded_apk->idmap_asset_ = std::move(idmap_asset); + loaded_apk->loaded_idmap_ = std::move(idmap); const StringPiece data( reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), loaded_apk->resources_asset_->getLength()); - loaded_apk->loaded_arsc_ = - LoadedArsc::Load(data, loaded_idmap.get(), system, load_as_shared_library); - if (loaded_apk->loaded_arsc_ == nullptr) { + if (data.data() == nullptr || data.empty()) { + LOG(ERROR) << "Failed to read '" << kResourcesArsc << "' data in APK '" << path << "'."; + return {}; + } + + loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(), + property_flags); + if (!loaded_apk->loaded_arsc_) { LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'."; return {}; } @@ -164,97 +512,45 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl( return std::move(loaded_apk); } -std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const { - CHECK(zip_handle_ != nullptr); - - ::ZipEntry entry; - int32_t result = ::FindEntry(zip_handle_.get(), path, &entry); - if (result != 0) { - return {}; - } - - if (entry.method == kCompressDeflated) { - std::unique_ptr<FileMap> map = util::make_unique<FileMap>(); - if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset, - entry.compressed_length, true /*readOnly*/)) { - LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'"; - return {}; - } - - std::unique_ptr<Asset> asset = - Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode); - if (asset == nullptr) { - LOG(ERROR) << "Failed to decompress '" << path << "'."; - return {}; - } - return asset; - } else { - std::unique_ptr<FileMap> map = util::make_unique<FileMap>(); - if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset, - entry.uncompressed_length, true /*readOnly*/)) { - LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'"; - return {}; - } - - std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map), mode); - if (asset == nullptr) { - LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'"; - return {}; - } - return asset; - } -} +std::unique_ptr<const ApkAssets> ApkAssets::LoadTableImpl( + std::unique_ptr<Asset> resources_asset, const std::string& path, + package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets) { -bool ApkAssets::ForEachFile(const std::string& root_path, - const std::function<void(const StringPiece&, FileType)>& f) const { - CHECK(zip_handle_ != nullptr); + const time_t last_mod_time = getFileModDate(path.c_str()); - std::string root_path_full = root_path; - if (root_path_full.back() != '/') { - root_path_full += '/'; - } + auto assets = (override_assets) ? std::move(override_assets) + : std::unique_ptr<AssetsProvider>(new EmptyAssetsProvider()); - void* cookie; - if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) { - return false; - } + std::unique_ptr<ApkAssets> loaded_apk( + new ApkAssets(std::move(assets), path, last_mod_time, property_flags)); + loaded_apk->resources_asset_ = std::move(resources_asset); - std::string name; - ::ZipEntry entry; - - // We need to hold back directories because many paths will contain them and we want to only - // surface one. - std::set<std::string> dirs; - - int32_t result; - while ((result = ::Next(cookie, &entry, &name)) == 0) { - StringPiece full_file_path(name); - StringPiece leaf_file_path = full_file_path.substr(root_path_full.size()); - - if (!leaf_file_path.empty()) { - auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/'); - if (iter != leaf_file_path.end()) { - std::string dir = - leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string(); - dirs.insert(std::move(dir)); - } else { - f(leaf_file_path, kFileTypeRegular); - } - } + const StringPiece data( + reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), + loaded_apk->resources_asset_->getLength()); + if (data.data() == nullptr || data.empty()) { + LOG(ERROR) << "Failed to read resources table data in '" << path << "'."; + return {}; } - ::EndIteration(cookie); - // Now present the unique directories. - for (const std::string& dir : dirs) { - f(dir, kFileTypeDirectory); + loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, property_flags); + if (loaded_apk->loaded_arsc_ == nullptr) { + LOG(ERROR) << "Failed to read resources table in '" << path << "'."; + return {}; } - // -1 is end of iteration, anything else is an error. - return result == -1; + // Need to force a move for mingw32. + return std::move(loaded_apk); } bool ApkAssets::IsUpToDate() const { - return last_mod_time_ == getFileModDate(path_.c_str()); + if (IsLoader()) { + // Loaders are invalidated by the app, not the system, so assume they are up to date. + return true; + } + return (!loaded_idmap_ || loaded_idmap_->IsUpToDate()) && + last_mod_time_ == getFileModDate(path_.c_str()); + } } // namespace android diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp index 9a95fdf80cb5..cd30c184d5a4 100644 --- a/libs/androidfw/Asset.cpp +++ b/libs/androidfw/Asset.cpp @@ -133,14 +133,24 @@ Asset::Asset(void) */ /*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode) { + return createFromFd(open(fileName, O_RDONLY | O_BINARY), fileName, mode); +} + +/* + * Create a new Asset from a file on disk. There is a fair chance that + * the file doesn't actually exist. + * + * We can use "mode" to decide how we want to go about it. + */ +/*static*/ Asset* Asset::createFromFd(const int fd, const char* fileName, AccessMode mode) +{ + if (fd < 0) { + return NULL; + } + _FileAsset* pAsset; status_t result; off64_t length; - int fd; - - fd = open(fileName, O_RDONLY | O_BINARY); - if (fd < 0) - return NULL; /* * Under Linux, the lseek fails if we actually opened a directory. To @@ -253,8 +263,10 @@ Asset::Asset(void) pAsset = new _FileAsset; result = pAsset->openChunk(NULL, fd, offset, length); - if (result != NO_ERROR) + if (result != NO_ERROR) { + delete pAsset; return NULL; + } pAsset->mAccessMode = mode; return pAsset; @@ -273,8 +285,10 @@ Asset::Asset(void) pAsset = new _CompressedAsset; result = pAsset->openChunk(fd, offset, compressionMethod, uncompressedLen, compressedLen); - if (result != NO_ERROR) + if (result != NO_ERROR) { + delete pAsset; return NULL; + } pAsset->mAccessMode = mode; return pAsset; @@ -284,14 +298,13 @@ Asset::Asset(void) /* * Create a new Asset from a memory mapping. */ -/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, - AccessMode mode) +/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, AccessMode mode) { _FileAsset* pAsset; status_t result; pAsset = new _FileAsset; - result = pAsset->openChunk(dataMap); + result = pAsset->openChunk(dataMap, base::unique_fd(-1)); if (result != NO_ERROR) { delete pAsset; return NULL; @@ -302,11 +315,11 @@ Asset::Asset(void) } /*static*/ std::unique_ptr<Asset> Asset::createFromUncompressedMap(std::unique_ptr<FileMap> dataMap, - AccessMode mode) + base::unique_fd fd, AccessMode mode) { std::unique_ptr<_FileAsset> pAsset = util::make_unique<_FileAsset>(); - status_t result = pAsset->openChunk(dataMap.get()); + status_t result = pAsset->openChunk(dataMap.get(), std::move(fd)); if (result != NO_ERROR) { return NULL; } @@ -328,8 +341,10 @@ Asset::Asset(void) pAsset = new _CompressedAsset; result = pAsset->openChunk(dataMap, uncompressedLen); - if (result != NO_ERROR) + if (result != NO_ERROR) { + delete pAsset; return NULL; + } pAsset->mAccessMode = mode; return pAsset; @@ -399,7 +414,7 @@ off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t m * Constructor. */ _FileAsset::_FileAsset(void) - : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL) + : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mFd(-1), mMap(NULL), mBuf(NULL) { // Register the Asset with the global list here after it is fully constructed and its // vtable pointer points to this concrete type. b/31113965 @@ -469,7 +484,7 @@ status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, siz /* * Create the chunk from the map. */ -status_t _FileAsset::openChunk(FileMap* dataMap) +status_t _FileAsset::openChunk(FileMap* dataMap, base::unique_fd fd) { assert(mFp == NULL); // no reopen assert(mMap == NULL); @@ -478,6 +493,7 @@ status_t _FileAsset::openChunk(FileMap* dataMap) mMap = dataMap; mStart = -1; // not used mLength = dataMap->getDataLength(); + mFd = std::move(fd); assert(mOffset == 0); return NO_ERROR; @@ -676,6 +692,17 @@ const void* _FileAsset::getBuffer(bool wordAligned) int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const { if (mMap != NULL) { + if (mFd.ok()) { + *outStart = mMap->getDataOffset(); + *outLength = mMap->getDataLength(); + const int fd = dup(mFd); + if (fd < 0) { + ALOGE("Unable to dup fd (%d).", mFd.get()); + return -1; + } + lseek64(fd, 0, SEEK_SET); + return fd; + } const char* fname = mMap->getFileName(); if (fname == NULL) { fname = mFileName; diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index d20aecaaf0f6..b9765ea7212c 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -22,10 +22,11 @@ #include <iterator> #include <map> #include <set> -#include <sstream> #include "android-base/logging.h" #include "android-base/stringprintf.h" +#include "androidfw/ResourceUtils.h" +#include "androidfw/Util.h" #include "utils/ByteOrder.h" #include "utils/Trace.h" @@ -35,21 +36,13 @@ #endif #endif -#ifdef __ANDROID__ -#define ANDROID_LOG(x) LOG(x) -#else -#define ANDROID_LOG(x) std::stringstream() -#endif - -#include "androidfw/ResourceUtils.h" - namespace android { struct FindEntryResult { // A pointer to the resource table entry for this resource. // If the size of the entry is > sizeof(ResTable_entry), it can be cast to // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry; + ResTable_entry_handle entry; // The configuration for which the resulting entry was defined. This is already swapped to host // endianness. @@ -61,6 +54,9 @@ struct FindEntryResult { // The dynamic package ID map for the package from which this resource came from. const DynamicRefTable* dynamic_ref_table; + // The package name of the resource. + const std::string* package_name; + // The string pool reference to the type's name. This uses a different string pool than // the global string pool, but this is hidden from the caller. StringPoolRef type_string_ref; @@ -89,12 +85,27 @@ void AssetManager2::BuildDynamicRefTable() { package_groups_.clear(); package_ids_.fill(0xff); + // A mapping from apk assets path to the runtime package id of its first loaded package. + std::unordered_map<std::string, uint8_t> apk_assets_package_ids; + + // Overlay resources are not directly referenced by an application so their resource ids + // can change throughout the application's lifetime. Assign overlay package ids last. + std::vector<const ApkAssets*> sorted_apk_assets(apk_assets_); + std::stable_partition(sorted_apk_assets.begin(), sorted_apk_assets.end(), [](const ApkAssets* a) { + return !a->IsOverlay(); + }); + + // The assets cookie must map to the position of the apk assets in the unsorted apk assets list. + std::unordered_map<const ApkAssets*, ApkAssetsCookie> apk_assets_cookies; + apk_assets_cookies.reserve(apk_assets_.size()); + for (size_t i = 0, n = apk_assets_.size(); i < n; i++) { + apk_assets_cookies[apk_assets_[i]] = static_cast<ApkAssetsCookie>(i); + } + // 0x01 is reserved for the android package. int next_package_id = 0x02; - const size_t apk_assets_count = apk_assets_.size(); - for (size_t i = 0; i < apk_assets_count; i++) { - const LoadedArsc* loaded_arsc = apk_assets_[i]->GetLoadedArsc(); - + for (const ApkAssets* apk_assets : sorted_apk_assets) { + const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc(); for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) { // Get the package ID or assign one if a shared library. int package_id; @@ -109,22 +120,55 @@ void AssetManager2::BuildDynamicRefTable() { if (idx == 0xff) { package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size()); package_groups_.push_back({}); - DynamicRefTable& ref_table = package_groups_.back().dynamic_ref_table; - ref_table.mAssignedPackageId = package_id; - ref_table.mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f; + + if (apk_assets->IsOverlay()) { + // The target package must precede the overlay package in the apk assets paths in order + // to take effect. + const auto& loaded_idmap = apk_assets->GetLoadedIdmap(); + auto target_package_iter = apk_assets_package_ids.find(loaded_idmap->TargetApkPath()); + if (target_package_iter == apk_assets_package_ids.end()) { + LOG(INFO) << "failed to find target package for overlay " + << loaded_idmap->OverlayApkPath(); + } else { + const uint8_t target_package_id = target_package_iter->second; + const uint8_t target_idx = package_ids_[target_package_id]; + CHECK(target_idx != 0xff) << "overlay added to apk_assets_package_ids but does not" + << " have an assigned package group"; + + PackageGroup& target_package_group = package_groups_[target_idx]; + + // Create a special dynamic reference table for the overlay to rewrite references to + // overlay resources as references to the target resources they overlay. + auto overlay_table = std::make_shared<OverlayDynamicRefTable>( + loaded_idmap->GetOverlayDynamicRefTable(target_package_id)); + package_groups_.back().dynamic_ref_table = overlay_table; + + // Add the overlay resource map to the target package's set of overlays. + target_package_group.overlays_.push_back( + ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id, + overlay_table.get()), + apk_assets_cookies[apk_assets]}); + } + } + + DynamicRefTable* ref_table = package_groups_.back().dynamic_ref_table.get(); + ref_table->mAssignedPackageId = package_id; + ref_table->mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f; } PackageGroup* package_group = &package_groups_[idx]; // Add the package and to the set of packages with the same ID. package_group->packages_.push_back(ConfiguredPackage{package.get(), {}}); - package_group->cookies_.push_back(static_cast<ApkAssetsCookie>(i)); + package_group->cookies_.push_back(apk_assets_cookies[apk_assets]); // Add the package name -> build time ID mappings. for (const DynamicPackageEntry& entry : package->GetDynamicPackageMap()) { String16 package_name(entry.package_name.c_str(), entry.package_name.size()); - package_group->dynamic_ref_table.mEntries.replaceValueFor( + package_group->dynamic_ref_table->mEntries.replaceValueFor( package_name, static_cast<uint8_t>(entry.package_id)); } + + apk_assets_package_ids.insert(std::make_pair(apk_assets->GetPath(), package_id)); } } @@ -133,8 +177,8 @@ void AssetManager2::BuildDynamicRefTable() { for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) { const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName(); for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) { - iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()), - iter->dynamic_ref_table.mAssignedPackageId); + iter2->dynamic_ref_table->addMapping(String16(package_name.c_str(), package_name.size()), + iter->dynamic_ref_table->mAssignedPackageId); } } } @@ -167,13 +211,13 @@ void AssetManager2::DumpToLog() const { (loaded_package->IsDynamic() ? " dynamic" : "")); } LOG(INFO) << base::StringPrintf("PG (%02x): ", - package_group.dynamic_ref_table.mAssignedPackageId) + package_group.dynamic_ref_table->mAssignedPackageId) << list; for (size_t i = 0; i < 256; i++) { - if (package_group.dynamic_ref_table.mLookupTable[i] != 0) { + if (package_group.dynamic_ref_table->mLookupTable[i] != 0) { LOG(INFO) << base::StringPrintf(" e[0x%02x] -> 0x%02x", (uint8_t) i, - package_group.dynamic_ref_table.mLookupTable[i]); + package_group.dynamic_ref_table->mLookupTable[i]); } } } @@ -195,14 +239,15 @@ const DynamicRefTable* AssetManager2::GetDynamicRefTableForPackage(uint32_t pack if (idx == 0xff) { return nullptr; } - return &package_groups_[idx].dynamic_ref_table; + return package_groups_[idx].dynamic_ref_table.get(); } -const DynamicRefTable* AssetManager2::GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const { +std::shared_ptr<const DynamicRefTable> AssetManager2::GetDynamicRefTableForCookie( + ApkAssetsCookie cookie) const { for (const PackageGroup& package_group : package_groups_) { for (const ApkAssetsCookie& package_cookie : package_group.cookies_) { if (package_cookie == cookie) { - return &package_group.dynamic_ref_table; + return package_group.dynamic_ref_table; } } } @@ -230,6 +275,67 @@ const std::unordered_map<std::string, std::string>* return &loaded_package->GetOverlayableMap(); } +bool AssetManager2::GetOverlayablesToString(const android::StringPiece& package_name, + std::string* out) const { + uint8_t package_id = 0U; + for (const auto& apk_assets : apk_assets_) { + const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc(); + if (loaded_arsc == nullptr) { + continue; + } + + const auto& loaded_packages = loaded_arsc->GetPackages(); + if (loaded_packages.empty()) { + continue; + } + + const auto& loaded_package = loaded_packages[0]; + if (loaded_package->GetPackageName() == package_name) { + package_id = GetAssignedPackageId(loaded_package.get()); + break; + } + } + + if (package_id == 0U) { + ANDROID_LOG(ERROR) << base::StringPrintf("No package with name '%s", package_name.data()); + return false; + } + + const size_t idx = package_ids_[package_id]; + if (idx == 0xff) { + return false; + } + + std::string output; + for (const ConfiguredPackage& package : package_groups_[idx].packages_) { + const LoadedPackage* loaded_package = package.loaded_package_; + for (auto it = loaded_package->begin(); it != loaded_package->end(); it++) { + const OverlayableInfo* info = loaded_package->GetOverlayableInfo(*it); + if (info != nullptr) { + ResourceName res_name; + if (!GetResourceName(*it, &res_name)) { + ANDROID_LOG(ERROR) << base::StringPrintf( + "Unable to retrieve name of overlayable resource 0x%08x", *it); + return false; + } + + const std::string name = ToFormattedResourceString(&res_name); + output.append(base::StringPrintf( + "resource='%s' overlayable='%s' actor='%s' policy='0x%08x'\n", + name.c_str(), info->name.c_str(), info->actor.c_str(), info->policy_flags)); + } + } + } + + *out = std::move(output); + return true; +} + +bool AssetManager2::ContainsAllocatedTable() const { + return std::find_if(apk_assets_.begin(), apk_assets_.end(), + std::mem_fn(&ApkAssets::IsTableAllocated)) != apk_assets_.end(); +} + void AssetManager2::SetConfiguration(const ResTable_config& configuration) { const int diff = configuration_.diff(configuration); configuration_ = configuration; @@ -240,21 +346,45 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) { } } +std::set<std::string> AssetManager2::GetNonSystemOverlayPaths() const { + std::set<std::string> non_system_overlays; + for (const PackageGroup& package_group : package_groups_) { + bool found_system_package = false; + for (const ConfiguredPackage& package : package_group.packages_) { + if (package.loaded_package_->IsSystem()) { + found_system_package = true; + break; + } + } + + if (!found_system_package) { + for (const ConfiguredOverlay& overlay : package_group.overlays_) { + non_system_overlays.insert(apk_assets_[overlay.cookie]->GetPath()); + } + } + } + + return non_system_overlays; +} + std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system, bool exclude_mipmap) const { ATRACE_NAME("AssetManager::GetResourceConfigurations"); + const auto non_system_overlays = + (exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>(); + std::set<ResTable_config> configurations; for (const PackageGroup& package_group : package_groups_) { - bool found_system_package = false; - for (const ConfiguredPackage& package : package_group.packages_) { + for (size_t i = 0; i < package_group.packages_.size(); i++) { + const ConfiguredPackage& package = package_group.packages_[i]; if (exclude_system && package.loaded_package_->IsSystem()) { - found_system_package = true; continue; } - if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) { - // Overlays must appear after the target package to take effect. Any overlay found in the - // same package as a system package is able to overlay system resources. + auto apk_assets = apk_assets_[package_group.cookies_[i]]; + if (exclude_system && apk_assets->IsOverlay() + && non_system_overlays.find(apk_assets->GetPath()) == non_system_overlays.end()) { + // Exclude overlays that target system resources. continue; } @@ -268,17 +398,20 @@ std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system, bool merge_equivalent_languages) const { ATRACE_NAME("AssetManager::GetResourceLocales"); std::set<std::string> locales; + const auto non_system_overlays = + (exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>(); + for (const PackageGroup& package_group : package_groups_) { - bool found_system_package = false; - for (const ConfiguredPackage& package : package_group.packages_) { + for (size_t i = 0; i < package_group.packages_.size(); i++) { + const ConfiguredPackage& package = package_group.packages_[i]; if (exclude_system && package.loaded_package_->IsSystem()) { - found_system_package = true; continue; } - if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) { - // Overlays must appear after the target package to take effect. Any overlay found in the - // same package as a system package is able to overlay system resources. + auto apk_assets = apk_assets_[package_group.cookies_[i]]; + if (exclude_system && apk_assets->IsOverlay() + && non_system_overlays.find(apk_assets->GetPath()) == non_system_overlays.end()) { + // Exclude overlays that target system resources. continue; } @@ -322,7 +455,7 @@ std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) con files->add(info); }; - if (!apk_assets->ForEachFile(full_path, func)) { + if (!apk_assets->GetAssetsProvider()->ForEachFile(full_path, func)) { return {}; } } @@ -346,7 +479,7 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, continue; } - std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode); + std::unique_ptr<Asset> asset = apk_assets_[i]->GetAssetsProvider()->Open(filename, mode); if (asset) { if (out_cookie != nullptr) { *out_cookie = i; @@ -367,13 +500,19 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) { return {}; } - return apk_assets_[cookie]->Open(filename, mode); + return apk_assets_[cookie]->GetAssetsProvider()->Open(filename, mode); } ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, bool /*stop_at_first_match*/, bool ignore_configuration, FindEntryResult* out_entry) const { + if (resource_resolution_logging_enabled_) { + // Clear the last logged resource resolution. + ResetResourceResolution(); + last_resolution_.resid = resid; + } + // Might use this if density_override != 0. ResTable_config density_override_config; @@ -385,6 +524,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri desired_config = &density_override_config; } + // Retrieve the package group from the package id of the resource id. if (!is_valid_resid(resid)) { LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); return kInvalidCookie; @@ -393,8 +533,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri const uint32_t package_id = get_package_id(resid); const uint8_t type_idx = get_type_id(resid) - 1; const uint16_t entry_idx = get_entry_id(resid); - - const uint8_t package_idx = package_ids_[package_id]; + uint8_t package_idx = package_ids_[package_id]; if (package_idx == 0xff) { ANDROID_LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); @@ -402,8 +541,71 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri } const PackageGroup& package_group = package_groups_[package_idx]; - const size_t package_count = package_group.packages_.size(); + ApkAssetsCookie cookie = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config, + false /* stop_at_first_match */, + ignore_configuration, out_entry); + if (UNLIKELY(cookie == kInvalidCookie)) { + return kInvalidCookie; + } + + if (!apk_assets_[cookie]->IsLoader()) { + for (const auto& id_map : package_group.overlays_) { + auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid); + if (!overlay_entry) { + // No id map entry exists for this target resource. + continue; + } + + if (overlay_entry.IsTableEntry()) { + // The target resource is overlaid by an inline value not represented by a resource. + out_entry->entry = overlay_entry.GetTableEntry(); + out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); + cookie = id_map.cookie; + continue; + } + + FindEntryResult overlay_result; + ApkAssetsCookie overlay_cookie = FindEntry(overlay_entry.GetResourceId(), density_override, + false /* stop_at_first_match */, + ignore_configuration, &overlay_result); + if (UNLIKELY(overlay_cookie == kInvalidCookie)) { + continue; + } + + if (!overlay_result.config.isBetterThan(out_entry->config, desired_config) + && overlay_result.config.compare(out_entry->config) != 0) { + // The configuration of the entry for the overlay must be equal to or better than the target + // configuration to be chosen as the better value. + continue; + } + + cookie = overlay_cookie; + out_entry->entry = std::move(overlay_result.entry); + out_entry->config = overlay_result.config; + out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); + if (resource_resolution_logging_enabled_) { + last_resolution_.steps.push_back( + Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result.config.toString(), + overlay_result.package_name}); + } + } + } + if (resource_resolution_logging_enabled_) { + last_resolution_.cookie = cookie; + last_resolution_.type_string_ref = out_entry->type_string_ref; + last_resolution_.entry_string_ref = out_entry->entry_string_ref; + } + + return cookie; +} + +ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_group, + uint8_t type_idx, uint16_t entry_idx, + const ResTable_config& desired_config, + bool /*stop_at_first_match*/, + bool ignore_configuration, + FindEntryResult* out_entry) const { ApkAssetsCookie best_cookie = kInvalidCookie; const LoadedPackage* best_package = nullptr; const ResTable_type* best_type = nullptr; @@ -412,13 +614,14 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri uint32_t best_offset = 0u; uint32_t type_flags = 0u; - Resolution::Step::Type resolution_type; + Resolution::Step::Type resolution_type = Resolution::Step::Type::NO_ENTRY; std::vector<Resolution::Step> resolution_steps; // If desired_config is the same as the set configuration, then we can use our filtered list // and we don't need to match the configurations, since they already matched. - const bool use_fast_path = !ignore_configuration && desired_config == &configuration_; + const bool use_fast_path = !ignore_configuration && &desired_config == &configuration_; + const size_t package_count = package_group.packages_.size(); for (size_t pi = 0; pi < package_count; pi++) { const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi]; const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_; @@ -431,20 +634,10 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri continue; } - uint16_t local_entry_idx = entry_idx; - - // If there is an IDMAP supplied with this package, translate the entry ID. - if (type_spec->idmap_entries != nullptr) { - if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) { - // There is no mapping, so the resource is not meant to be in this overlay package. - continue; - } - } - - type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx); - - // If the package is an overlay, then even configurations that are the same MUST be chosen. - const bool package_is_overlay = loaded_package->IsOverlay(); + // If the package is an overlay or custom loader, + // then even configurations that are the same MUST be chosen. + const bool package_is_loader = loaded_package->IsCustomLoader(); + type_flags |= type_spec->GetFlagsForEntryIndex(entry_idx); if (use_fast_path) { const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; @@ -457,19 +650,37 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri // configurations that do NOT match have been filtered-out. if (best_config == nullptr) { resolution_type = Resolution::Step::Type::INITIAL; - } else if (this_config.isBetterThan(*best_config, desired_config)) { - resolution_type = Resolution::Step::Type::BETTER_MATCH; - } else if (package_is_overlay && this_config.compare(*best_config) == 0) { - resolution_type = Resolution::Step::Type::OVERLAID; + } else if (this_config.isBetterThan(*best_config, &desired_config)) { + resolution_type = (package_is_loader) ? Resolution::Step::Type::BETTER_MATCH_LOADER + : Resolution::Step::Type::BETTER_MATCH; + } else if (package_is_loader && this_config.compare(*best_config) == 0) { + resolution_type = Resolution::Step::Type::OVERLAID_LOADER; } else { + if (resource_resolution_logging_enabled_) { + resolution_type = (package_is_loader) ? Resolution::Step::Type::SKIPPED_LOADER + : Resolution::Step::Type::SKIPPED; + resolution_steps.push_back(Resolution::Step{resolution_type, + this_config.toString(), + &loaded_package->GetPackageName()}); + } continue; } // The configuration matches and is better than the previous selection. // Find the entry value if it exists for this configuration. const ResTable_type* type = filtered_group.types[i]; - const uint32_t offset = LoadedPackage::GetEntryOffset(type, local_entry_idx); + const uint32_t offset = LoadedPackage::GetEntryOffset(type, entry_idx); if (offset == ResTable_type::NO_ENTRY) { + if (resource_resolution_logging_enabled_) { + if (package_is_loader) { + resolution_type = Resolution::Step::Type::NO_ENTRY_LOADER; + } else { + resolution_type = Resolution::Step::Type::NO_ENTRY; + } + resolution_steps.push_back(Resolution::Step{resolution_type, + this_config.toString(), + &loaded_package->GetPackageName()}); + } continue; } @@ -480,9 +691,9 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri best_offset = offset; if (resource_resolution_logging_enabled_) { - resolution_steps.push_back(Resolution::Step{resolution_type, - this_config.toString(), - &loaded_package->GetPackageName()}); + last_resolution_.steps.push_back(Resolution::Step{resolution_type, + this_config.toString(), + &loaded_package->GetPackageName()}); } } } else { @@ -497,16 +708,17 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri if (!ignore_configuration) { this_config.copyFromDtoH((*iter)->config); - if (!this_config.match(*desired_config)) { + if (!this_config.match(desired_config)) { continue; } if (best_config == nullptr) { resolution_type = Resolution::Step::Type::INITIAL; - } else if (this_config.isBetterThan(*best_config, desired_config)) { - resolution_type = Resolution::Step::Type::BETTER_MATCH; - } else if (package_is_overlay && this_config.compare(*best_config) == 0) { - resolution_type = Resolution::Step::Type::OVERLAID; + } else if (this_config.isBetterThan(*best_config, &desired_config)) { + resolution_type = (package_is_loader) ? Resolution::Step::Type::BETTER_MATCH_LOADER + : Resolution::Step::Type::BETTER_MATCH; + } else if (package_is_loader && this_config.compare(*best_config) == 0) { + resolution_type = Resolution::Step::Type::OVERLAID_LOADER; } else { continue; } @@ -514,7 +726,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri // The configuration matches and is better than the previous selection. // Find the entry value if it exists for this configuration. - const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx); + const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, entry_idx); if (offset == ResTable_type::NO_ENTRY) { continue; } @@ -532,9 +744,9 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri } if (resource_resolution_logging_enabled_) { - resolution_steps.push_back(Resolution::Step{resolution_type, - this_config.toString(), - &loaded_package->GetPackageName()}); + last_resolution_.steps.push_back(Resolution::Step{resolution_type, + this_config.toString(), + &loaded_package->GetPackageName()}); } } } @@ -549,38 +761,30 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri return kInvalidCookie; } - out_entry->entry = best_entry; + out_entry->entry = ResTable_entry_handle::unmanaged(best_entry); out_entry->config = *best_config; out_entry->type_flags = type_flags; + out_entry->package_name = &best_package->GetPackageName(); out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); out_entry->entry_string_ref = - StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); - out_entry->dynamic_ref_table = &package_group.dynamic_ref_table; - - if (resource_resolution_logging_enabled_) { - last_resolution.resid = resid; - last_resolution.cookie = best_cookie; - last_resolution.steps = resolution_steps; - - // Cache only the type/entry refs since that's all that's needed to build name - last_resolution.type_string_ref = - StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); - last_resolution.entry_string_ref = - StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); - } + StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); + out_entry->dynamic_ref_table = package_group.dynamic_ref_table.get(); return best_cookie; } +void AssetManager2::ResetResourceResolution() const { + last_resolution_.cookie = kInvalidCookie; + last_resolution_.resid = 0; + last_resolution_.steps.clear(); + last_resolution_.type_string_ref = StringPoolRef(); + last_resolution_.entry_string_ref = StringPoolRef(); +} + void AssetManager2::SetResourceResolutionLoggingEnabled(bool enabled) { resource_resolution_logging_enabled_ = enabled; - if (!enabled) { - last_resolution.cookie = kInvalidCookie; - last_resolution.resid = 0; - last_resolution.steps.clear(); - last_resolution.type_string_ref = StringPoolRef(); - last_resolution.entry_string_ref = StringPoolRef(); + ResetResourceResolution(); } } @@ -590,24 +794,24 @@ std::string AssetManager2::GetLastResourceResolution() const { return std::string(); } - auto cookie = last_resolution.cookie; + auto cookie = last_resolution_.cookie; if (cookie == kInvalidCookie) { LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path."; return std::string(); } - uint32_t resid = last_resolution.resid; - std::vector<Resolution::Step>& steps = last_resolution.steps; + uint32_t resid = last_resolution_.resid; + std::vector<Resolution::Step>& steps = last_resolution_.steps; ResourceName resource_name; std::string resource_name_string; const LoadedPackage* package = - apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); + apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); if (package != nullptr) { - ToResourceName(last_resolution.type_string_ref, - last_resolution.entry_string_ref, + ToResourceName(last_resolution_.type_string_ref, + last_resolution_.entry_string_ref, package->GetPackageName(), &resource_name); resource_name_string = ToFormattedResourceString(&resource_name); @@ -628,9 +832,27 @@ std::string AssetManager2::GetLastResourceResolution() const { case Resolution::Step::Type::BETTER_MATCH: prefix = "Found better"; break; + case Resolution::Step::Type::BETTER_MATCH_LOADER: + prefix = "Found better in loader"; + break; case Resolution::Step::Type::OVERLAID: prefix = "Overlaid"; break; + case Resolution::Step::Type::OVERLAID_LOADER: + prefix = "Overlaid by loader"; + break; + case Resolution::Step::Type::SKIPPED: + prefix = "Skipped"; + break; + case Resolution::Step::Type::SKIPPED_LOADER: + prefix = "Skipped loader"; + break; + case Resolution::Step::Type::NO_ENTRY: + prefix = "No entry"; + break; + case Resolution::Step::Type::NO_ENTRY_LOADER: + prefix = "No entry for loader"; + break; } if (!prefix.empty()) { @@ -654,25 +876,9 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons return false; } - const uint8_t package_idx = package_ids_[get_package_id(resid)]; - if (package_idx == 0xff) { - LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", - get_package_id(resid), resid); - return false; - } - - const PackageGroup& package_group = package_groups_[package_idx]; - auto cookie_iter = std::find(package_group.cookies_.begin(), - package_group.cookies_.end(), cookie); - if (cookie_iter == package_group.cookies_.end()) { - return false; - } - - long package_pos = std::distance(package_group.cookies_.begin(), cookie_iter); - const LoadedPackage* package = package_group.packages_[package_pos].loaded_package_; return ToResourceName(entry.type_string_ref, entry.entry_string_ref, - package->GetPackageName(), + *entry.package_name, out_name); } @@ -699,7 +905,8 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, return kInvalidCookie; } - if (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) { + const ResTable_entry* table_entry = *entry.entry; + if (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) { if (!may_be_bag) { LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid); return kInvalidCookie; @@ -714,7 +921,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, } const Res_value* device_value = reinterpret_cast<const Res_value*>( - reinterpret_cast<const uint8_t*>(entry.entry) + dtohs(entry.entry->size)); + reinterpret_cast<const uint8_t*>(table_entry) + dtohs(table_entry->size)); out_value->copyFrom_dtoh(*device_value); // Convert the package ID to the runtime assigned package ID. @@ -777,6 +984,11 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { return bag; } +static bool compare_bag_entries(const ResolvedBag::Entry& entry1, + const ResolvedBag::Entry& entry2) { + return entry1.key < entry2.key; +} + const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& child_resids) { auto cached_iter = cached_bags_.find(resid); if (cached_iter != cached_bags_.end()) { @@ -795,13 +1007,14 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& // Check that the size of the entry header is at least as big as // the desired ResTable_map_entry. Also verify that the entry // was intended to be a map. - if (dtohs(entry.entry->size) < sizeof(ResTable_map_entry) || - (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) { + const ResTable_entry* table_entry = *entry.entry; + if (dtohs(table_entry->size) < sizeof(ResTable_map_entry) || + (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) { // Not a bag, nothing to do. return nullptr; } - const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry.entry); + const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(table_entry); const ResTable_map* map_entry = reinterpret_cast<const ResTable_map*>(reinterpret_cast<const uint8_t*>(map) + map->size); const ResTable_map* const map_entry_end = map_entry + dtohl(map->count); @@ -811,13 +1024,15 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& child_resids.push_back(resid); uint32_t parent_resid = dtohl(map->parent.ident); - if (parent_resid == 0 || std::find(child_resids.begin(), child_resids.end(), parent_resid) + if (parent_resid == 0U || std::find(child_resids.begin(), child_resids.end(), parent_resid) != child_resids.end()) { - // There is no parent or that a circular dependency exist, meaning there is nothing to - // inherit and we can do a simple copy of the entries in the map. + // There is no parent or a circular dependency exist, meaning there is nothing to inherit and + // we can do a simple copy of the entries in the map. const size_t entry_count = map_entry_end - map_entry; util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>( malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag::Entry))))}; + + bool sort_entries = false; ResolvedBag::Entry* new_entry = new_bag->entries; for (; map_entry != map_entry_end; ++map_entry) { uint32_t new_key = dtohl(map_entry->name.ident); @@ -843,8 +1058,15 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& new_entry->value.data, new_key); return nullptr; } + sort_entries = sort_entries || + (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key)); ++new_entry; } + + if (sort_entries) { + std::sort(new_bag->entries, new_bag->entries + entry_count, compare_bag_entries); + } + new_bag->type_spec_flags = entry.type_flags; new_bag->entry_count = static_cast<uint32_t>(entry_count); ResolvedBag* result = new_bag.get(); @@ -875,6 +1097,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& const ResolvedBag::Entry* const parent_entry_end = parent_entry + parent_bag->entry_count; // The keys are expected to be in sorted order. Merge the two bags. + bool sort_entries = false; while (map_entry != map_entry_end && parent_entry != parent_entry_end) { uint32_t child_key = dtohl(map_entry->name.ident); if (!is_internal_resid(child_key)) { @@ -907,6 +1130,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& memcpy(new_entry, parent_entry, sizeof(*new_entry)); } + sort_entries = sort_entries || + (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key)); if (child_key >= parent_entry->key) { // Move to the next parent entry if we used it or it was overridden. ++parent_entry; @@ -937,6 +1162,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& new_entry->value.dataType, new_entry->value.data, new_key); return nullptr; } + sort_entries = sort_entries || + (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key)); ++map_entry; ++new_entry; } @@ -956,6 +1183,10 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& new_bag.release(), sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry))))); } + if (sort_entries) { + std::sort(new_bag->entries, new_bag->entries + actual_count, compare_bag_entries); + } + // Combine flags from the parent and our own bag. new_bag->type_spec_flags = entry.type_flags | parent_bag->type_spec_flags; new_bag->entry_count = static_cast<uint32_t>(actual_count); @@ -1026,7 +1257,7 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, } if (resid != 0u) { - return fix_package_id(resid, package_group.dynamic_ref_table.mAssignedPackageId); + return fix_package_id(resid, package_group.dynamic_ref_table->mAssignedPackageId); } } } @@ -1079,11 +1310,11 @@ void AssetManager2::InvalidateCaches(uint32_t diff) { } } -uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) { +uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const { for (auto& package_group : package_groups_) { for (auto& package2 : package_group.packages_) { if (package2.loaded_package_ == package) { - return package_group.dynamic_ref_table.mAssignedPackageId; + return package_group.dynamic_ref_table->mAssignedPackageId; } } } diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp index e1067fcd4d3d..6f05cbd0ebb3 100644 --- a/libs/androidfw/CursorWindow.cpp +++ b/libs/androidfw/CursorWindow.cpp @@ -69,7 +69,7 @@ status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** o result = window->clear(); if (!result) { LOG_WINDOW("Created new CursorWindow: freeOffset=%d, " - "numRows=%d, numColumns=%d, mSize=%d, mData=%p", + "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", window->mHeader->freeOffset, window->mHeader->numRows, window->mHeader->numColumns, @@ -124,7 +124,7 @@ status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursor CursorWindow* window = new CursorWindow(name, dupAshmemFd, data, size, true /*readOnly*/); LOG_WINDOW("Created CursorWindow from parcel: freeOffset=%d, " - "numRows=%d, numColumns=%d, mSize=%d, mData=%p", + "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", window->mHeader->freeOffset, window->mHeader->numRows, window->mHeader->numColumns, @@ -200,7 +200,7 @@ status_t CursorWindow::allocRow() { FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(fieldDirOffset)); memset(fieldDir, 0, fieldDirSize); - LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n", + LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %zu bytes at offset %u\n", mHeader->numRows - 1, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset); rowSlot->offset = fieldDirOffset; return OK; diff --git a/libs/androidfw/DisplayEventDispatcher.cpp b/libs/androidfw/DisplayEventDispatcher.cpp deleted file mode 100644 index d8a3f42690f4..000000000000 --- a/libs/androidfw/DisplayEventDispatcher.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -#define LOG_TAG "DisplayEventDispatcher" - -#include <cinttypes> -#include <cstdint> - -#include <androidfw/DisplayEventDispatcher.h> -#include <gui/DisplayEventReceiver.h> -#include <utils/Log.h> -#include <utils/Looper.h> - -#include <utils/Timers.h> - -namespace android { - -// Number of events to read at a time from the DisplayEventDispatcher pipe. -// The value should be large enough that we can quickly drain the pipe -// using just a few large reads. -static const size_t EVENT_BUFFER_SIZE = 100; - -DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper, - ISurfaceComposer::VsyncSource vsyncSource, - ISurfaceComposer::ConfigChanged configChanged) : - mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) { - ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this); -} - -status_t DisplayEventDispatcher::initialize() { - status_t result = mReceiver.initCheck(); - if (result) { - ALOGW("Failed to initialize display event receiver, status=%d", result); - return result; - } - - int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, - this, NULL); - if (rc < 0) { - return UNKNOWN_ERROR; - } - return OK; -} - -void DisplayEventDispatcher::dispose() { - ALOGV("dispatcher %p ~ Disposing display event dispatcher.", this); - - if (!mReceiver.initCheck()) { - mLooper->removeFd(mReceiver.getFd()); - } -} - -status_t DisplayEventDispatcher::scheduleVsync() { - if (!mWaitingForVsync) { - ALOGV("dispatcher %p ~ Scheduling vsync.", this); - - // Drain all pending events. - nsecs_t vsyncTimestamp; - PhysicalDisplayId vsyncDisplayId; - uint32_t vsyncCount; - if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) { - ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", - this, ns2ms(static_cast<nsecs_t>(vsyncTimestamp))); - } - - status_t status = mReceiver.requestNextVsync(); - if (status) { - ALOGW("Failed to request next vsync, status=%d", status); - return status; - } - - mWaitingForVsync = true; - } - return OK; -} - -int DisplayEventDispatcher::handleEvent(int, int events, void*) { - if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { - ALOGE("Display event receiver pipe was closed or an error occurred. " - "events=0x%x", events); - return 0; // remove the callback - } - - if (!(events & Looper::EVENT_INPUT)) { - ALOGW("Received spurious callback for unhandled poll event. " - "events=0x%x", events); - return 1; // keep the callback - } - - // Drain all pending events, keep the last vsync. - nsecs_t vsyncTimestamp; - PhysicalDisplayId vsyncDisplayId; - uint32_t vsyncCount; - if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) { - ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", displayId=%" - ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d", - this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount); - mWaitingForVsync = false; - dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount); - } - - return 1; // keep the callback -} - -bool DisplayEventDispatcher::processPendingEvents( - nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount) { - bool gotVsync = false; - DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; - ssize_t n; - while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { - ALOGV("dispatcher %p ~ Read %d events.", this, int(n)); - for (ssize_t i = 0; i < n; i++) { - const DisplayEventReceiver::Event& ev = buf[i]; - switch (ev.header.type) { - case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: - // Later vsync events will just overwrite the info from earlier - // ones. That's fine, we only care about the most recent. - gotVsync = true; - *outTimestamp = ev.header.timestamp; - *outDisplayId = ev.header.displayId; - *outCount = ev.vsync.count; - break; - case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: - dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected); - break; - case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: - dispatchConfigChanged(ev.header.timestamp, ev.header.displayId, ev.config.configId); - break; - default: - ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type); - break; - } - } - } - if (n < 0) { - ALOGW("Failed to get events from display event dispatcher, status=%d", status_t(n)); - } - return gotVsync; -} -} diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index 7c1ee5cd7cfa..5f231ffe4786 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -20,6 +20,9 @@ #include "android-base/logging.h" #include "android-base/stringprintf.h" +#include "androidfw/misc.h" +#include "androidfw/ResourceTypes.h" +#include "androidfw/Util.h" #include "utils/ByteOrder.h" #include "utils/Trace.h" @@ -29,40 +32,132 @@ #endif #endif -#include "androidfw/ResourceTypes.h" - using ::android::base::StringPrintf; namespace android { -constexpr static inline bool is_valid_package_id(uint16_t id) { - return id != 0 && id <= 255; +static bool compare_target_entries(const Idmap_target_entry &e1, const uint32_t target_id) { + return dtohl(e1.target_id) < target_id; } -constexpr static inline bool is_valid_type_id(uint16_t id) { - // Type IDs and package IDs have the same constraints in the IDMAP. - return is_valid_package_id(id); +static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_t overlay_id) { + return dtohl(e1.overlay_id) < overlay_id; } -bool LoadedIdmap::Lookup(const IdmapEntry_header* header, uint16_t input_entry_id, - uint16_t* output_entry_id) { - if (input_entry_id < dtohs(header->entry_id_offset)) { - // After applying the offset, the entry is not present. - return false; +size_t Idmap_header::Size() const { + return sizeof(Idmap_header) + sizeof(uint8_t) * dtohl(debug_info_size); +} + +OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap) + : data_header_(loaded_idmap->data_header_), + idmap_string_pool_(loaded_idmap->string_pool_.get()) { }; + +OverlayStringPool::~OverlayStringPool() { + uninit(); +} + +const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const { + const size_t offset = dtohl(data_header_->string_pool_index_offset); + if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) { + return idmap_string_pool_->stringAt(idx - offset, outLen); } - input_entry_id -= dtohs(header->entry_id_offset); - if (input_entry_id >= dtohs(header->entry_count)) { - // The entry is not present. - return false; + return ResStringPool::stringAt(idx, outLen); +} + +const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const { + const size_t offset = dtohl(data_header_->string_pool_index_offset); + if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) { + return idmap_string_pool_->string8At(idx - offset, outLen); } - uint32_t result = dtohl(header->entries[input_entry_id]); - if (result == 0xffffffffu) { - return false; + return ResStringPool::string8At(idx, outLen); +} + +size_t OverlayStringPool::size() const { + return ResStringPool::size() + (idmap_string_pool_ != nullptr ? idmap_string_pool_->size() : 0U); +} + +OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header, + const Idmap_overlay_entry* entries, + uint8_t target_assigned_package_id) + : data_header_(data_header), + entries_(entries), + target_assigned_package_id_(target_assigned_package_id) { }; + +status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const { + const Idmap_overlay_entry* first_entry = entries_; + const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count); + auto entry = std::lower_bound(first_entry, end_entry, *resId, compare_overlay_entries); + + if (entry == end_entry || dtohl(entry->overlay_id) != *resId) { + // A mapping for the target resource id could not be found. + return DynamicRefTable::lookupResourceId(resId); } - *output_entry_id = static_cast<uint16_t>(result); - return true; + + *resId = (0x00FFFFFFU & dtohl(entry->target_id)) + | (((uint32_t) target_assigned_package_id_) << 24); + return NO_ERROR; +} + +status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) const { + return DynamicRefTable::lookupResourceId(resId); +} + +IdmapResMap::IdmapResMap(const Idmap_data_header* data_header, + const Idmap_target_entry* entries, + uint8_t target_assigned_package_id, + const OverlayDynamicRefTable* overlay_ref_table) + : data_header_(data_header), + entries_(entries), + target_assigned_package_id_(target_assigned_package_id), + overlay_ref_table_(overlay_ref_table) { }; + +IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const { + if ((target_res_id >> 24) != target_assigned_package_id_) { + // The resource id must have the same package id as the target package. + return {}; + } + + // The resource ids encoded within the idmap are build-time resource ids. + target_res_id = (0x00FFFFFFU & target_res_id) + | (((uint32_t) data_header_->target_package_id) << 24); + + const Idmap_target_entry* first_entry = entries_; + const Idmap_target_entry* end_entry = entries_ + dtohl(data_header_->target_entry_count); + auto entry = std::lower_bound(first_entry, end_entry, target_res_id, compare_target_entries); + + if (entry == end_entry || dtohl(entry->target_id) != target_res_id) { + // A mapping for the target resource id could not be found. + return {}; + } + + // A reference should be treated as an alias of the resource. Instead of returning the table + // entry, return the alias resource id to look up. The alias resource might not reside within the + // overlay package, so the resource id must be fixed with the dynamic reference table of the + // overlay before returning. + if (entry->type == Res_value::TYPE_REFERENCE + || entry->type == Res_value::TYPE_DYNAMIC_REFERENCE) { + uint32_t overlay_resource_id = dtohl(entry->value); + + // Lookup the resource without rewriting the overlay resource id back to the target resource id + // being looked up. + overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id); + return Result(overlay_resource_id); + } + + // Copy the type and value into the ResTable_entry structure needed by asset manager. + uint16_t malloc_size = sizeof(ResTable_entry) + sizeof(Res_value); + auto table_entry = reinterpret_cast<ResTable_entry*>(malloc(malloc_size)); + memset(table_entry, 0, malloc_size); + table_entry->size = htods(sizeof(ResTable_entry)); + + auto table_value = reinterpret_cast<Res_value*>(reinterpret_cast<uint8_t*>(table_entry) + + sizeof(ResTable_entry)); + table_value->dataType = entry->type; + table_value->data = entry->value; + + return Result(ResTable_entry_handle::managed(table_entry, [](auto p) { free(p); })); } static bool is_word_aligned(const void* data) { @@ -95,96 +190,105 @@ static bool IsValidIdmapHeader(const StringPiece& data) { return false; } - if (!is_valid_package_id(dtohs(header->target_package_id))) { - LOG(ERROR) << StringPrintf("Target package ID in Idmap is invalid: 0x%02x", - dtohs(header->target_package_id)); - return false; - } - - if (dtohs(header->type_count) > 255) { - LOG(ERROR) << StringPrintf("Idmap has too many type mappings (was %d, max 255)", - (int)dtohs(header->type_count)); - return false; - } return true; } -LoadedIdmap::LoadedIdmap(const Idmap_header* header) : header_(header) { +LoadedIdmap::LoadedIdmap(std::string&& idmap_path, + const time_t last_mod_time, + const Idmap_header* header, + const Idmap_data_header* data_header, + const Idmap_target_entry* target_entries, + const Idmap_overlay_entry* overlay_entries, + ResStringPool* string_pool) + : header_(header), + data_header_(data_header), + target_entries_(target_entries), + overlay_entries_(overlay_entries), + string_pool_(string_pool), + idmap_path_(std::move(idmap_path)), + idmap_last_mod_time_(last_mod_time) { + size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path), arraysize(header_->overlay_path)); overlay_apk_path_.assign(reinterpret_cast<const char*>(header_->overlay_path), length); + + length = strnlen(reinterpret_cast<const char*>(header_->target_path), + arraysize(header_->target_path)); + target_apk_path_.assign(reinterpret_cast<const char*>(header_->target_path), length); } -std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_data) { +std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path, + const StringPiece& idmap_data) { ATRACE_CALL(); if (!IsValidIdmapHeader(idmap_data)) { return {}; } - const Idmap_header* header = reinterpret_cast<const Idmap_header*>(idmap_data.data()); + auto header = reinterpret_cast<const Idmap_header*>(idmap_data.data()); + const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()) + header->Size(); + size_t data_size = idmap_data.size() - header->Size(); + + // Currently idmap2 can only generate one data block. + auto data_header = reinterpret_cast<const Idmap_data_header*>(data_ptr); + data_ptr += sizeof(*data_header); + data_size -= sizeof(*data_header); + + // Make sure there is enough space for the target entries declared in the header. + const auto target_entries = reinterpret_cast<const Idmap_target_entry*>(data_ptr); + if (data_size / sizeof(Idmap_target_entry) < + static_cast<size_t>(dtohl(data_header->target_entry_count))) { + LOG(ERROR) << StringPrintf("Idmap too small for the number of target entries (%d)", + (int)dtohl(data_header->target_entry_count)); + return {}; + } - // Can't use make_unique because LoadedImpl constructor is private. - std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(new LoadedIdmap(header)); + // Advance the data pointer past the target entries. + const size_t target_entry_size_bytes = + (dtohl(data_header->target_entry_count) * sizeof(Idmap_target_entry)); + data_ptr += target_entry_size_bytes; + data_size -= target_entry_size_bytes; + + // Make sure there is enough space for the overlay entries declared in the header. + const auto overlay_entries = reinterpret_cast<const Idmap_overlay_entry*>(data_ptr); + if (data_size / sizeof(Idmap_overlay_entry) < + static_cast<size_t>(dtohl(data_header->overlay_entry_count))) { + LOG(ERROR) << StringPrintf("Idmap too small for the number of overlay entries (%d)", + (int)dtohl(data_header->overlay_entry_count)); + return {}; + } - const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()) + sizeof(*header); - size_t data_size = idmap_data.size() - sizeof(*header); + // Advance the data pointer past the target entries. + const size_t overlay_entry_size_bytes = + (dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry)); + data_ptr += overlay_entry_size_bytes; + data_size -= overlay_entry_size_bytes; - size_t type_maps_encountered = 0u; - while (data_size >= sizeof(IdmapEntry_header)) { - if (!is_word_aligned(data_ptr)) { - LOG(ERROR) << "Type mapping in Idmap is not word aligned"; - return {}; - } - - // Validate the type IDs. - const IdmapEntry_header* entry_header = reinterpret_cast<const IdmapEntry_header*>(data_ptr); - if (!is_valid_type_id(dtohs(entry_header->target_type_id)) || !is_valid_type_id(dtohs(entry_header->overlay_type_id))) { - LOG(ERROR) << StringPrintf("Invalid type map (0x%02x -> 0x%02x)", - dtohs(entry_header->target_type_id), - dtohs(entry_header->overlay_type_id)); - return {}; - } + // Read the idmap string pool that holds the value of inline string entries. + if (data_size < dtohl(data_header->string_pool_length)) { + LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)", + (int)dtohl(data_header->string_pool_length)); + return {}; + } - // Make sure there is enough space for the entries declared in the header. - if ((data_size - sizeof(*entry_header)) / sizeof(uint32_t) < - static_cast<size_t>(dtohs(entry_header->entry_count))) { - LOG(ERROR) << StringPrintf("Idmap too small for the number of entries (%d)", - (int)dtohs(entry_header->entry_count)); + auto idmap_string_pool = util::make_unique<ResStringPool>(); + if (dtohl(data_header->string_pool_length) > 0) { + status_t err = idmap_string_pool->setTo(data_ptr, dtohl(data_header->string_pool_length)); + if (err != NO_ERROR) { + LOG(ERROR) << "idmap string pool corrupt."; return {}; } - - // Only add a non-empty overlay. - if (dtohs(entry_header->entry_count != 0)) { - loaded_idmap->type_map_[static_cast<uint8_t>(dtohs(entry_header->overlay_type_id))] = - entry_header; - } - - const size_t entry_size_bytes = - sizeof(*entry_header) + (dtohs(entry_header->entry_count) * sizeof(uint32_t)); - data_ptr += entry_size_bytes; - data_size -= entry_size_bytes; - type_maps_encountered++; } - // Verify that we parsed all the type maps. - if (type_maps_encountered != static_cast<size_t>(dtohs(header->type_count))) { - LOG(ERROR) << "Parsed " << type_maps_encountered << " type maps but expected " - << (int)dtohs(header->type_count); - return {}; - } - return std::move(loaded_idmap); -} + // Can't use make_unique because LoadedIdmap constructor is private. + std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>( + new LoadedIdmap(idmap_path.to_string(), getFileModDate(idmap_path.data()), header, + data_header, target_entries, overlay_entries, idmap_string_pool.release())); -uint8_t LoadedIdmap::TargetPackageId() const { - return static_cast<uint8_t>(dtohs(header_->target_package_id)); + return std::move(loaded_idmap); } -const IdmapEntry_header* LoadedIdmap::GetEntryMapForType(uint8_t type_id) const { - auto iter = type_map_.find(type_id); - if (iter != type_map_.end()) { - return iter->second; - } - return nullptr; +bool LoadedIdmap::IsUpToDate() const { + return idmap_last_mod_time_ == getFileModDate(idmap_path_.c_str()); } } // namespace android diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 72873abc6a42..70bb441f94cb 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -51,9 +51,8 @@ namespace { // the Type structs. class TypeSpecPtrBuilder { public: - explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header, - const IdmapEntry_header* idmap_header) - : header_(header), idmap_header_(idmap_header) { + explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header) + : header_(header) { } void AddType(const ResTable_type* type) { @@ -70,7 +69,6 @@ class TypeSpecPtrBuilder { TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType))); type_spec->type_spec = header_; - type_spec->idmap_entries = idmap_header_; type_spec->type_count = types_.size(); memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType)); return TypeSpecPtr(type_spec); @@ -80,7 +78,6 @@ class TypeSpecPtrBuilder { DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder); const ResTable_typeSpec* header_; - const IdmapEntry_header* idmap_header_; std::vector<const ResTable_type*> types_; }; @@ -400,8 +397,7 @@ const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { } std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, - const LoadedIdmap* loaded_idmap, - bool system, bool load_as_shared_library) { + package_property_t property_flags) { ATRACE_NAME("LoadedPackage::Load"); std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage()); @@ -415,19 +411,24 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, return {}; } - loaded_package->system_ = system; + if ((property_flags & PROPERTY_SYSTEM) != 0) { + loaded_package->property_flags_ |= PROPERTY_SYSTEM; + } - loaded_package->package_id_ = dtohl(header->id); - if (loaded_package->package_id_ == 0 || - (loaded_package->package_id_ == kAppPackageId && load_as_shared_library)) { - // Package ID of 0 means this is a shared library. - loaded_package->dynamic_ = true; + if ((property_flags & PROPERTY_LOADER) != 0) { + loaded_package->property_flags_ |= PROPERTY_LOADER; } - if (loaded_idmap != nullptr) { - // This is an overlay and so it needs to pretend to be the target package. - loaded_package->package_id_ = loaded_idmap->TargetPackageId(); - loaded_package->overlay_ = true; + if ((property_flags & PROPERTY_OVERLAY) != 0) { + // Overlay resources must have an exclusive resource id space for referencing internal + // resources. + loaded_package->property_flags_ |= PROPERTY_OVERLAY | PROPERTY_DYNAMIC; + } + + loaded_package->package_id_ = dtohl(header->id); + if (loaded_package->package_id_ == 0 || + (loaded_package->package_id_ == kAppPackageId && (property_flags & PROPERTY_DYNAMIC) != 0)) { + loaded_package->property_flags_ |= PROPERTY_DYNAMIC; } if (header->header.headerSize >= sizeof(ResTable_package)) { @@ -511,16 +512,9 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, return {}; } - // If this is an overlay, associate the mapping of this type to the target type - // from the IDMAP. - const IdmapEntry_header* idmap_entry_header = nullptr; - if (loaded_idmap != nullptr) { - idmap_entry_header = loaded_idmap->GetEntryMapForType(type_spec->id); - } - std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type_spec->id - 1]; if (builder_ptr == nullptr) { - builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec, idmap_entry_header); + builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec); loaded_package->resource_ids_.set(type_spec->id, entry_count); } else { LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x", @@ -681,28 +675,24 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, return {}; } - // We only add the type to the package if there is no IDMAP, or if the type is - // overlaying something. - if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) { - // If this is an overlay, insert it at the target type ID. - if (type_spec_ptr->idmap_entries != nullptr) { - type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; - } - loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr); - } + loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr); } return std::move(loaded_package); } bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, - bool load_as_shared_library) { + package_property_t property_flags) { const ResTable_header* header = chunk.header<ResTable_header>(); if (header == nullptr) { LOG(ERROR) << "RES_TABLE_TYPE too small."; return false; } + if (loaded_idmap != nullptr) { + global_string_pool_ = util::make_unique<OverlayStringPool>(loaded_idmap); + } + const size_t package_count = dtohl(header->packageCount); size_t packages_seen = 0; @@ -714,9 +704,9 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, switch (child_chunk.type()) { case RES_STRING_POOL_TYPE: // Only use the first string pool. Ignore others. - if (global_string_pool_.getError() == NO_INIT) { - status_t err = global_string_pool_.setTo(child_chunk.header<ResStringPool_header>(), - child_chunk.size()); + if (global_string_pool_->getError() == NO_INIT) { + status_t err = global_string_pool_->setTo(child_chunk.header<ResStringPool_header>(), + child_chunk.size()); if (err != NO_ERROR) { LOG(ERROR) << "RES_STRING_POOL_TYPE corrupt."; return false; @@ -735,7 +725,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, packages_seen++; std::unique_ptr<const LoadedPackage> loaded_package = - LoadedPackage::Load(child_chunk, loaded_idmap, system_, load_as_shared_library); + LoadedPackage::Load(child_chunk, property_flags); if (!loaded_package) { return false; } @@ -758,20 +748,19 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, } std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data, - const LoadedIdmap* loaded_idmap, bool system, - bool load_as_shared_library) { - ATRACE_NAME("LoadedArsc::LoadTable"); + const LoadedIdmap* loaded_idmap, + const package_property_t property_flags) { + ATRACE_NAME("LoadedArsc::Load"); // Not using make_unique because the constructor is private. std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc()); - loaded_arsc->system_ = system; ChunkIterator iter(data.data(), data.size()); while (iter.HasNext()) { const Chunk chunk = iter.Next(); switch (chunk.type()) { case RES_TABLE_TYPE: - if (!loaded_arsc->LoadTable(chunk, loaded_idmap, load_as_shared_library)) { + if (!loaded_arsc->LoadTable(chunk, loaded_idmap, property_flags)) { return {}; } break; diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 905de6be577b..dfb4009b07e2 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -1062,6 +1062,11 @@ size_t ResStringPool::bytes() const return (mError == NO_ERROR) ? mHeader->header.size : 0; } +const void* ResStringPool::data() const +{ + return mHeader; +} + bool ResStringPool::isSorted() const { return (mHeader->flags&ResStringPool_header::SORTED_FLAG)!=0; @@ -1357,11 +1362,10 @@ int32_t ResXMLParser::getAttributeData(size_t idx) const (((const uint8_t*)tag) + dtohs(tag->attributeStart) + (dtohs(tag->attributeSize)*idx)); - if (attr->typedValue.dataType != Res_value::TYPE_DYNAMIC_REFERENCE || - mTree.mDynamicRefTable == NULL) { + if (mTree.mDynamicRefTable == NULL || + !mTree.mDynamicRefTable->requiresLookup(&attr->typedValue)) { return dtohl(attr->typedValue.data); } - uint32_t data = dtohl(attr->typedValue.data); if (mTree.mDynamicRefTable->lookupResourceId(&data) == NO_ERROR) { return data; @@ -1607,10 +1611,9 @@ uint32_t ResXMLParser::getSourceResourceId() const static volatile int32_t gCount = 0; -ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable) +ResXMLTree::ResXMLTree(std::shared_ptr<const DynamicRefTable> dynamicRefTable) : ResXMLParser(*this) - , mDynamicRefTable((dynamicRefTable != nullptr) ? dynamicRefTable->clone() - : std::unique_ptr<DynamicRefTable>(nullptr)) + , mDynamicRefTable(std::move(dynamicRefTable)) , mError(NO_INIT), mOwnedData(NULL) { if (kDebugResXMLTree) { @@ -1621,7 +1624,7 @@ ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable) ResXMLTree::ResXMLTree() : ResXMLParser(*this) - , mDynamicRefTable(std::unique_ptr<DynamicRefTable>(nullptr)) + , mDynamicRefTable(nullptr) , mError(NO_INIT), mOwnedData(NULL) { if (kDebugResXMLTree) { @@ -4783,7 +4786,7 @@ void ResTable::setParameters(const ResTable_config* params) packageGroup->clearBagCache(); // Find which configurations match the set of parameters. This allows for a much - // faster lookup in getEntry() if the set of values is narrowed down. + // faster lookup in Lookup() if the set of values is narrowed down. for (size_t t = 0; t < packageGroup->types.size(); t++) { if (packageGroup->types[t].isEmpty()) { continue; @@ -6891,13 +6894,6 @@ DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib) mLookupTable[SYS_PACKAGE_ID] = SYS_PACKAGE_ID; } -std::unique_ptr<DynamicRefTable> DynamicRefTable::clone() const { - std::unique_ptr<DynamicRefTable> clone = std::unique_ptr<DynamicRefTable>( - new DynamicRefTable(mAssignedPackageId, mAppAsLib)); - clone->addMappings(*this); - return clone; -} - status_t DynamicRefTable::load(const ResTable_lib_header* const header) { const uint32_t entryCount = dtohl(header->count); @@ -7014,21 +7010,29 @@ status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const { return NO_ERROR; } +bool DynamicRefTable::requiresLookup(const Res_value* value) const { + // Only resolve non-dynamic references and attributes if the package is loaded as a + // library or if a shared library is attempting to retrieve its own resource + if ((value->dataType == Res_value::TYPE_REFERENCE || + value->dataType == Res_value::TYPE_ATTRIBUTE) && + (mAppAsLib || (Res_GETPACKAGE(value->data) + 1) == 0)) { + return true; + } + return value->dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE || + value->dataType == Res_value::TYPE_DYNAMIC_REFERENCE; +} + status_t DynamicRefTable::lookupResourceValue(Res_value* value) const { + if (!requiresLookup(value)) { + return NO_ERROR; + } + uint8_t resolvedType = Res_value::TYPE_REFERENCE; switch (value->dataType) { case Res_value::TYPE_ATTRIBUTE: resolvedType = Res_value::TYPE_ATTRIBUTE; FALLTHROUGH_INTENDED; case Res_value::TYPE_REFERENCE: - // Only resolve non-dynamic references and attributes if the package is loaded as a - // library or if a shared library is attempting to retrieve its own resource - if (!(mAppAsLib || (Res_GETPACKAGE(value->data) + 1) == 0)) { - return NO_ERROR; - } - - // If the package is loaded as shared library, the resource reference - // also need to be fixed. break; case Res_value::TYPE_DYNAMIC_ATTRIBUTE: resolvedType = Res_value::TYPE_ATTRIBUTE; diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING index a58b47fcff9d..777aa0b429e5 100644 --- a/libs/androidfw/TEST_MAPPING +++ b/libs/androidfw/TEST_MAPPING @@ -3,6 +3,9 @@ { "name": "libandroidfw_tests", "host": true + }, + { + "name": "CtsResourcesLoaderTests" } ] }
\ No newline at end of file diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h index 49fc82bff11e..e57490aab2d8 100644 --- a/libs/androidfw/include/androidfw/ApkAssets.h +++ b/libs/androidfw/include/androidfw/ApkAssets.h @@ -24,6 +24,7 @@ #include "android-base/unique_fd.h" #include "androidfw/Asset.h" +#include "androidfw/Idmap.h" #include "androidfw/LoadedArsc.h" #include "androidfw/misc.h" @@ -34,79 +35,162 @@ namespace android { class LoadedIdmap; +// Interface for retrieving assets provided by an ApkAssets. +class AssetsProvider { + public: + virtual ~AssetsProvider() = default; + + // Opens a file for reading. + std::unique_ptr<Asset> Open(const std::string& path, + Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM, + bool* file_exists = nullptr) const { + return OpenInternal(path, mode, file_exists); + } + + // Iterate over all files and directories provided by the zip. The order of iteration is stable. + virtual bool ForEachFile(const std::string& /* path */, + const std::function<void(const StringPiece&, FileType)>& /* f */) const { + return true; + } + + protected: + AssetsProvider() = default; + + virtual std::unique_ptr<Asset> OpenInternal(const std::string& path, + Asset::AccessMode mode, + bool* file_exists) const = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(AssetsProvider); +}; + +class ZipAssetsProvider; + // Holds an APK. class ApkAssets { public: + // This means the data extends to the end of the file. + static constexpr off64_t kUnknownLength = -1; + // Creates an ApkAssets. // If `system` is true, the package is marked as a system package, and allows some functions to // filter out this package when computing what configurations/resources are available. - static std::unique_ptr<const ApkAssets> Load(const std::string& path, bool system = false); + static std::unique_ptr<const ApkAssets> Load( + const std::string& path, package_property_t flags = 0U, + std::unique_ptr<const AssetsProvider> override_asset = nullptr); - // Creates an ApkAssets, but forces any package with ID 0x7f to be loaded as a shared library. - // If `system` is true, the package is marked as a system package, and allows some functions to - // filter out this package when computing what configurations/resources are available. - static std::unique_ptr<const ApkAssets> LoadAsSharedLibrary(const std::string& path, - bool system = false); + // Creates an ApkAssets from the given file descriptor, and takes ownership of the file + // descriptor. The `friendly_name` is some name that will be used to identify the source of + // this ApkAssets in log messages and other debug scenarios. + // If `length` equals kUnknownLength, offset must equal 0; otherwise, the apk data will be read + // using the `offset` into the file descriptor and will be `length` bytes long. + static std::unique_ptr<const ApkAssets> LoadFromFd( + base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U, + std::unique_ptr<const AssetsProvider> override_asset = nullptr, off64_t offset = 0, + off64_t length = kUnknownLength); + + // Creates an ApkAssets from the given path which points to a resources.arsc. + static std::unique_ptr<const ApkAssets> LoadTable( + const std::string& path, package_property_t flags = 0U, + std::unique_ptr<const AssetsProvider> override_asset = nullptr); + + // Creates an ApkAssets from the given file descriptor which points to an resources.arsc, and + // takes ownership of the file descriptor. + // If `length` equals kUnknownLength, offset must equal 0; otherwise, the .arsc data will be read + // using the `offset` into the file descriptor and will be `length` bytes long. + static std::unique_ptr<const ApkAssets> LoadTableFromFd( + base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U, + std::unique_ptr<const AssetsProvider> override_asset = nullptr, off64_t offset = 0, + off64_t length = kUnknownLength); // Creates an ApkAssets from an IDMAP, which contains the original APK path, and the overlay // data. - // If `system` is true, the package is marked as a system package, and allows some functions to - // filter out this package when computing what configurations/resources are available. static std::unique_ptr<const ApkAssets> LoadOverlay(const std::string& idmap_path, - bool system = false); - - // Creates an ApkAssets from the given file descriptor, and takes ownership of the file - // descriptor. The `friendly_name` is some name that will be used to identify the source of - // this ApkAssets in log messages and other debug scenarios. - // If `system` is true, the package is marked as a system package, and allows some functions to - // filter out this package when computing what configurations/resources are available. - // If `force_shared_lib` is true, any package with ID 0x7f is loaded as a shared library. - static std::unique_ptr<const ApkAssets> LoadFromFd(base::unique_fd fd, - const std::string& friendly_name, bool system, - bool force_shared_lib); + package_property_t flags = 0U); - std::unique_ptr<Asset> Open(const std::string& path, - Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const; + // Creates an ApkAssets from the directory path. File-based resources are read within the + // directory as if the directory is an APK. + static std::unique_ptr<const ApkAssets> LoadFromDir( + const std::string& path, package_property_t flags = 0U, + std::unique_ptr<const AssetsProvider> override_asset = nullptr); - bool ForEachFile(const std::string& path, - const std::function<void(const StringPiece&, FileType)>& f) const; + // Creates a totally empty ApkAssets with no resources table and no file entries. + static std::unique_ptr<const ApkAssets> LoadEmpty( + package_property_t flags = 0U, + std::unique_ptr<const AssetsProvider> override_asset = nullptr); - inline const std::string& GetPath() const { + const std::string& GetPath() const { return path_; } + const AssetsProvider* GetAssetsProvider() const { + return assets_provider_.get(); + } + // This is never nullptr. - inline const LoadedArsc* GetLoadedArsc() const { + const LoadedArsc* GetLoadedArsc() const { return loaded_arsc_.get(); } - inline bool IsOverlay() const { - return idmap_asset_.get() != nullptr; + const LoadedIdmap* GetLoadedIdmap() const { + return loaded_idmap_.get(); + } + + bool IsLoader() const { + return (property_flags_ & PROPERTY_LOADER) != 0; + } + + bool IsOverlay() const { + return loaded_idmap_ != nullptr; + } + + // Returns whether the resources.arsc is allocated in RAM (not mmapped). + bool IsTableAllocated() const { + return resources_asset_ && resources_asset_->isAllocated(); } bool IsUpToDate() const; + // Creates an Asset from a file on disk. + static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path); + + // Creates an Asset from a file descriptor. + // + // The asset takes ownership of the file descriptor. If `length` equals kUnknownLength, offset + // must equal 0; otherwise, the asset data will be read using the `offset` into the file + // descriptor and will be `length` bytes long. + static std::unique_ptr<Asset> CreateAssetFromFd(base::unique_fd fd, + const char* path, + off64_t offset = 0, + off64_t length = kUnknownLength); private: DISALLOW_COPY_AND_ASSIGN(ApkAssets); - static std::unique_ptr<const ApkAssets> LoadImpl(base::unique_fd fd, const std::string& path, - std::unique_ptr<Asset> idmap_asset, - std::unique_ptr<const LoadedIdmap> loaded_idmap, - bool system, bool load_as_shared_library); - - // Creates an Asset from any file on the file system. - static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path); + static std::unique_ptr<const ApkAssets> LoadImpl( + std::unique_ptr<const AssetsProvider> assets, const std::string& path, + package_property_t property_flags, + std::unique_ptr<const AssetsProvider> override_assets = nullptr, + std::unique_ptr<Asset> idmap_asset = nullptr, + std::unique_ptr<const LoadedIdmap> idmap = nullptr); - ApkAssets(ZipArchiveHandle unmanaged_handle, const std::string& path, time_t last_mod_time); + static std::unique_ptr<const ApkAssets> LoadTableImpl( + std::unique_ptr<Asset> resources_asset, const std::string& path, + package_property_t property_flags, + std::unique_ptr<const AssetsProvider> override_assets = nullptr); - using ZipArchivePtr = std::unique_ptr<ZipArchive, void(*)(ZipArchiveHandle)>; + ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider, + std::string path, + time_t last_mod_time, + package_property_t property_flags); - ZipArchivePtr zip_handle_; + std::unique_ptr<const AssetsProvider> assets_provider_; const std::string path_; time_t last_mod_time_; + package_property_t property_flags_ = 0U; std::unique_ptr<Asset> resources_asset_; std::unique_ptr<Asset> idmap_asset_; std::unique_ptr<const LoadedArsc> loaded_arsc_; + std::unique_ptr<const LoadedIdmap> loaded_idmap_; }; } // namespace android diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h index 9d12a35395c9..298509eb37a1 100644 --- a/libs/androidfw/include/androidfw/Asset.h +++ b/libs/androidfw/include/androidfw/Asset.h @@ -26,6 +26,7 @@ #include <memory> +#include <android-base/unique_fd.h> #include <utils/Compat.h> #include <utils/Errors.h> #include <utils/String8.h> @@ -121,6 +122,11 @@ public: */ const char* getAssetSource(void) const { return mAssetSource.string(); } + /* + * Create the asset from a file descriptor. + */ + static Asset* createFromFd(const int fd, const char* fileName, AccessMode mode); + protected: /* * Adds this Asset to the global Asset list for debugging and @@ -153,6 +159,7 @@ private: /* AssetManager needs access to our "create" functions */ friend class AssetManager; friend class ApkAssets; + friend class ZipAssetsProvider; /* * Create the asset from a named file on disk. @@ -197,8 +204,14 @@ private: */ static Asset* createFromUncompressedMap(FileMap* dataMap, AccessMode mode); + /* + * Create the asset from a memory-mapped file segment. + * + * The asset takes ownership of the FileMap and the file descriptor "fd". The file descriptor is + * used to request new file descriptors using "openFileDescriptor". + */ static std::unique_ptr<Asset> createFromUncompressedMap(std::unique_ptr<FileMap> dataMap, - AccessMode mode); + base::unique_fd fd, AccessMode mode); /* * Create the asset from a memory-mapped file segment with compressed @@ -251,9 +264,9 @@ public: /* * Use a memory-mapped region. * - * On success, the object takes ownership of "dataMap". + * On success, the object takes ownership of "dataMap" and "fd". */ - status_t openChunk(FileMap* dataMap); + status_t openChunk(FileMap* dataMap, base::unique_fd fd); /* * Standard Asset interfaces. @@ -268,11 +281,12 @@ public: virtual bool isAllocated(void) const { return mBuf != NULL; } private: - off64_t mStart; // absolute file offset of start of chunk - off64_t mLength; // length of the chunk - off64_t mOffset; // current local offset, 0 == mStart - FILE* mFp; // for read/seek - char* mFileName; // for opening + off64_t mStart; // absolute file offset of start of chunk + off64_t mLength; // length of the chunk + off64_t mOffset; // current local offset, 0 == mStart + FILE* mFp; // for read/seek + char* mFileName; // for opening + base::unique_fd mFd; // for opening file descriptors /* * To support getBuffer() we either need to read the entire thing into diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 1e2b36cb1703..30ef25c6a516 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -122,11 +122,21 @@ class AssetManager2 { // Returns the DynamicRefTable for the ApkAssets represented by the cookie. // This may be nullptr if the APK represented by `cookie` has no resource table. - const DynamicRefTable* GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const; + std::shared_ptr<const DynamicRefTable> GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const; + + // Retrieve the assigned package id of the package if loaded into this AssetManager + uint8_t GetAssignedPackageId(const LoadedPackage* package) const; + + // Returns a string representation of the overlayable API of a package. + bool GetOverlayablesToString(const android::StringPiece& package_name, + std::string* out) const; const std::unordered_map<std::string, std::string>* GetOverlayableMapForPackage(uint32_t package_id) const; + // Returns whether the resources.arsc of any loaded apk assets is allocated in RAM (not mmapped). + bool ContainsAllocatedTable() const; + // Sets/resets the configuration for this AssetManager. This will cause all // caches that are related to the configuration change to be invalidated. void SetConfiguration(const ResTable_config& configuration); @@ -232,12 +242,14 @@ class AssetManager2 { ResTable_config* in_out_selected_config, uint32_t* in_out_flags, uint32_t* out_last_reference) const; - // Enables or disables resource resolution logging. Clears stored steps when - // disabled. + // Resets the resource resolution structures in preparation for the next resource retrieval. + void ResetResourceResolution() const; + + // Enables or disables resource resolution logging. Clears stored steps when disabled. void SetResourceResolutionLoggingEnabled(bool enabled); - // Returns formatted log of last resource resolution path, or empty if no - // resource has been resolved yet. + // Returns formatted log of last resource resolution path, or empty if no resource has been + // resolved yet. std::string GetLastResourceResolution() const; const std::vector<uint32_t> GetBagResIdStack(uint32_t resid); @@ -257,10 +269,13 @@ class AssetManager2 { // Creates a new Theme from this AssetManager. std::unique_ptr<Theme> NewTheme(); - void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func) const { + void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func, + package_property_t excluded_property_flags = 0U) const { for (const PackageGroup& package_group : package_groups_) { - if (!func(package_group.packages_.front().loaded_package_->GetPackageName(), - package_group.dynamic_ref_table.mAssignedPackageId)) { + const auto loaded_package = package_group.packages_.front().loaded_package_; + if ((loaded_package->GetPropertyFlags() & excluded_property_flags) == 0U + && !func(loaded_package->GetPackageName(), + package_group.dynamic_ref_table->mAssignedPackageId)) { return; } } @@ -271,6 +286,50 @@ class AssetManager2 { private: DISALLOW_COPY_AND_ASSIGN(AssetManager2); + // A collection of configurations and their associated ResTable_type that match the current + // AssetManager configuration. + struct FilteredConfigGroup { + std::vector<ResTable_config> configurations; + std::vector<const ResTable_type*> types; + }; + + // Represents an single package. + struct ConfiguredPackage { + // A pointer to the immutable, loaded package info. + const LoadedPackage* loaded_package_; + + // A mutable AssetManager-specific list of configurations that match the AssetManager's + // current configuration. This is used as an optimization to avoid checking every single + // candidate configuration when looking up resources. + ByteBucketArray<FilteredConfigGroup> filtered_configs_; + }; + + // Represents a Runtime Resource Overlay that overlays resources in the logical package. + struct ConfiguredOverlay { + // The set of package groups that overlay this package group. + IdmapResMap overlay_res_maps_; + + // The cookie of the overlay assets. + ApkAssetsCookie cookie; + }; + + // Represents a logical package, which can be made up of many individual packages. Each package + // in a PackageGroup shares the same package name and package ID. + struct PackageGroup { + // The set of packages that make-up this group. + std::vector<ConfiguredPackage> packages_; + + // The cookies associated with each package in the group. They share the same order as + // packages_. + std::vector<ApkAssetsCookie> cookies_; + + // Runtime Resource Overlays that overlay resources in this package group. + std::vector<ConfiguredOverlay> overlays_; + + // A library reference table that contains build-package ID to runtime-package ID mappings. + std::shared_ptr<DynamicRefTable> dynamic_ref_table = std::make_shared<DynamicRefTable>(); + }; + // Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple // Res_value, or a complex map/bag type. If successful, it is available in `out_entry`. // Returns kInvalidCookie on failure. Otherwise, the return value is the cookie associated with @@ -291,6 +350,11 @@ class AssetManager2 { ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, bool ignore_configuration, FindEntryResult* out_entry) const; + ApkAssetsCookie FindEntryInternal(const PackageGroup& package_group, uint8_t type_idx, + uint16_t entry_idx, const ResTable_config& desired_config, + bool /*stop_at_first_match*/, + bool ignore_configuration, FindEntryResult* out_entry) const; + // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. void BuildDynamicRefTable(); @@ -303,49 +367,17 @@ class AssetManager2 { // This should always be called when mutating the AssetManager's configuration or ApkAssets set. void RebuildFilterList(bool filter_incompatible_configs = true); + // Retrieves the APK paths of overlays that overlay non-system packages. + std::set<std::string> GetNonSystemOverlayPaths() const; + // AssetManager2::GetBag(resid) wraps this function to track which resource ids have already // been seen while traversing bag parents. const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids); - // Retrieve the assigned package id of the package if loaded into this AssetManager - uint8_t GetAssignedPackageId(const LoadedPackage* package); - // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector<const ApkAssets*> apk_assets_; - // A collection of configurations and their associated ResTable_type that match the current - // AssetManager configuration. - struct FilteredConfigGroup { - std::vector<ResTable_config> configurations; - std::vector<const ResTable_type*> types; - }; - - // Represents an single package. - struct ConfiguredPackage { - // A pointer to the immutable, loaded package info. - const LoadedPackage* loaded_package_; - - // A mutable AssetManager-specific list of configurations that match the AssetManager's - // current configuration. This is used as an optimization to avoid checking every single - // candidate configuration when looking up resources. - ByteBucketArray<FilteredConfigGroup> filtered_configs_; - }; - - // Represents a logical package, which can be made up of many individual packages. Each package - // in a PackageGroup shares the same package name and package ID. - struct PackageGroup { - // The set of packages that make-up this group. - std::vector<ConfiguredPackage> packages_; - - // The cookies associated with each package in the group. They share the same order as - // packages_. - std::vector<ApkAssetsCookie> cookies_; - - // A library reference table that contains build-package ID to runtime-package ID mappings. - DynamicRefTable dynamic_ref_table; - }; - // DynamicRefTables for shared library package resolution. // These are ordered according to apk_assets_. The mappings may change depending on what is // in apk_assets_, therefore they must be stored in the AssetManager and not in the @@ -378,7 +410,13 @@ class AssetManager2 { enum class Type { INITIAL, BETTER_MATCH, - OVERLAID + BETTER_MATCH_LOADER, + OVERLAID, + OVERLAID_LOADER, + SKIPPED, + SKIPPED_LOADER, + NO_ENTRY, + NO_ENTRY_LOADER, }; // Marks what kind of override this step was. @@ -408,7 +446,7 @@ class AssetManager2 { }; // Record of the last resolved resource's resolution path. - mutable Resolution last_resolution; + mutable Resolution last_resolution_; }; class Theme { diff --git a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h deleted file mode 100644 index 8bc25202b3ab..000000000000 --- a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -#include <gui/DisplayEventReceiver.h> -#include <utils/Log.h> -#include <utils/Looper.h> - -namespace android { - -class DisplayEventDispatcher : public LooperCallback { -public: - explicit DisplayEventDispatcher(const sp<Looper>& looper, - ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp, - ISurfaceComposer::ConfigChanged configChanged = ISurfaceComposer::eConfigChangedSuppress); - - status_t initialize(); - void dispose(); - status_t scheduleVsync(); - -protected: - virtual ~DisplayEventDispatcher() = default; - -private: - sp<Looper> mLooper; - DisplayEventReceiver mReceiver; - bool mWaitingForVsync; - - virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0; - virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, - bool connected) = 0; - virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, - int32_t configId) = 0; - - virtual int handleEvent(int receiveFd, int events, void* data); - bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, - uint32_t* outCount); -}; -} diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index fd02e6f63b74..ecc1ce65d124 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -20,55 +20,190 @@ #include <memory> #include <string> #include <unordered_map> +#include <variant> #include "android-base/macros.h" - #include "androidfw/StringPiece.h" +#include "androidfw/ResourceTypes.h" +#include "utils/ByteOrder.h" namespace android { -struct Idmap_header; -struct IdmapEntry_header; +class LoadedIdmap; +class IdmapResMap; + +// A string pool for overlay apk assets. The string pool holds the strings of the overlay resources +// table and additionally allows for loading strings from the idmap string pool. The idmap string +// pool strings are offset after the end of the overlay resource table string pool entries so +// queries for strings defined inline in the idmap do not conflict with queries for overlay +// resource table strings. +class OverlayStringPool : public ResStringPool { + public: + virtual ~OverlayStringPool(); + const char16_t* stringAt(size_t idx, size_t* outLen) const override; + const char* string8At(size_t idx, size_t* outLen) const override; + size_t size() const override; + + explicit OverlayStringPool(const LoadedIdmap* loaded_idmap); + private: + const Idmap_data_header* data_header_; + const ResStringPool* idmap_string_pool_; +}; + +// A dynamic reference table for loaded overlay packages that rewrites the resource id of overlay +// resources to the resource id of corresponding target resources. +class OverlayDynamicRefTable : public DynamicRefTable { + public: + ~OverlayDynamicRefTable() override = default; + status_t lookupResourceId(uint32_t* resId) const override; + + private: + explicit OverlayDynamicRefTable(const Idmap_data_header* data_header, + const Idmap_overlay_entry* entries, + uint8_t target_assigned_package_id); + + // Rewrites a compile-time overlay resource id to the runtime resource id of corresponding target + // resource. + virtual status_t lookupResourceIdNoRewrite(uint32_t* resId) const; + + const Idmap_data_header* data_header_; + const Idmap_overlay_entry* entries_; + const int8_t target_assigned_package_id_; + + friend LoadedIdmap; + friend IdmapResMap; +}; + +// A mapping of target resource ids to a values or resource ids that should overlay the target. +class IdmapResMap { + public: + // Represents the result of a idmap lookup. The result can be one of three possibillities: + // 1) The result is a resource id which represents the overlay resource that should act as an + // alias of the target resource. + // 2) The result is a table entry which overlays the type and value of the target resource. + // 3) The result is neither and the target resource is not overlaid. + class Result { + public: + Result() : data_(nullptr) {}; + explicit Result(uint32_t value) : data_(value) {}; + explicit Result(ResTable_entry_handle&& value) : data_(value) { }; + + // Returns `true` if the resource is overlaid. + inline explicit operator bool() const { + return !std::get_if<nullptr_t>(&data_); + } + + inline bool IsResourceId() const { + return std::get_if<uint32_t>(&data_); + } + + inline uint32_t GetResourceId() const { + return *std::get_if<uint32_t>(&data_); + } + + inline bool IsTableEntry() const { + return std::get_if<ResTable_entry_handle>(&data_); + } + + inline const ResTable_entry_handle& GetTableEntry() const { + return *std::get_if<ResTable_entry_handle>(&data_); + } + + private: + std::variant<uint32_t, nullptr_t, ResTable_entry_handle> data_; + }; + + // Looks up the value that overlays the target resource id. + Result Lookup(uint32_t target_res_id) const; + + inline const OverlayDynamicRefTable* GetOverlayDynamicRefTable() const { + return overlay_ref_table_; + } + + private: + explicit IdmapResMap(const Idmap_data_header* data_header, + const Idmap_target_entry* entries, + uint8_t target_assigned_package_id, + const OverlayDynamicRefTable* overlay_ref_table); + + const Idmap_data_header* data_header_; + const Idmap_target_entry* entries_; + const uint8_t target_assigned_package_id_; + const OverlayDynamicRefTable* overlay_ref_table_; + + friend LoadedIdmap; +}; // Represents a loaded/parsed IDMAP for a Runtime Resource Overlay (RRO). -// An RRO and its target APK have different resource IDs assigned to their resources. Overlaying -// a resource is done by resource name. An IDMAP is a generated mapping between the resource IDs -// of the RRO and the target APK for each resource with the same name. +// An RRO and its target APK have different resource IDs assigned to their resources. +// An IDMAP is a generated mapping between the resource IDs of the RRO and the target APK. // A LoadedIdmap can be set alongside the overlay's LoadedArsc to allow the overlay ApkAssets to // masquerade as the target ApkAssets resources. class LoadedIdmap { public: // Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed. - static std::unique_ptr<const LoadedIdmap> Load(const StringPiece& idmap_data); - - // Performs a lookup of the expected entry ID for the given IDMAP entry header. - // Returns true if the mapping exists and fills `output_entry_id` with the result. - static bool Lookup(const IdmapEntry_header* header, uint16_t input_entry_id, - uint16_t* output_entry_id); + static std::unique_ptr<const LoadedIdmap> Load(const StringPiece& idmap_path, + const StringPiece& idmap_data); - // Returns the package ID for which this overlay should apply. - uint8_t TargetPackageId() const; + // Returns the path to the IDMAP. + inline const std::string& IdmapPath() const { + return idmap_path_; + } // Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated. inline const std::string& OverlayApkPath() const { return overlay_apk_path_; } - // Returns the mapping of target entry ID to overlay entry ID for the given target type. - const IdmapEntry_header* GetEntryMapForType(uint8_t type_id) const; + // Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated. + inline const std::string& TargetApkPath() const { + return target_apk_path_; + } + + // Returns a mapping from target resource ids to overlay values. + inline const IdmapResMap GetTargetResourcesMap( + uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const { + return IdmapResMap(data_header_, target_entries_, target_assigned_package_id, + overlay_ref_table); + } + + // Returns a dynamic reference table for a loaded overlay package. + inline const OverlayDynamicRefTable GetOverlayDynamicRefTable( + uint8_t target_assigned_package_id) const { + return OverlayDynamicRefTable(data_header_, overlay_entries_, target_assigned_package_id); + } + + // Returns whether the idmap file on disk has not been modified since the construction of this + // LoadedIdmap. + bool IsUpToDate() const; protected: // Exposed as protected so that tests can subclass and mock this class out. LoadedIdmap() = default; - const Idmap_header* header_ = nullptr; + const Idmap_header* header_; + const Idmap_data_header* data_header_; + const Idmap_target_entry* target_entries_; + const Idmap_overlay_entry* overlay_entries_; + const std::unique_ptr<ResStringPool> string_pool_; + + const std::string idmap_path_; std::string overlay_apk_path_; - std::unordered_map<uint8_t, const IdmapEntry_header*> type_map_; + std::string target_apk_path_; + const time_t idmap_last_mod_time_; private: DISALLOW_COPY_AND_ASSIGN(LoadedIdmap); - explicit LoadedIdmap(const Idmap_header* header); + explicit LoadedIdmap(std::string&& idmap_path, + time_t last_mod_time, + const Idmap_header* header, + const Idmap_data_header* data_header, + const Idmap_target_entry* target_entries, + const Idmap_overlay_entry* overlay_entries, + ResStringPool* string_pool); + + friend OverlayStringPool; }; } // namespace android diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 950f5413f550..89ff9f52125d 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -51,10 +51,6 @@ struct TypeSpec { // and under which configurations it varies. const ResTable_typeSpec* type_spec; - // Pointer to the mmapped data where the IDMAP mappings for this type - // exist. May be nullptr if no IDMAP exists. - const IdmapEntry_header* idmap_entries; - // The number of types that follow this struct. // There is a type for each configuration that entries are defined for. size_t type_count; @@ -73,6 +69,26 @@ struct TypeSpec { } }; +// Flags that change the behavior of loaded packages. +// Keep in sync with f/b/android/content/res/ApkAssets.java +using package_property_t = uint32_t; +enum : package_property_t { + // The package contains framework resource values specified by the system. + // This allows some functions to filter out this package when computing + // what configurations/resources are available. + PROPERTY_SYSTEM = 1U << 0U, + + // The package is a shared library or has a package id of 7f and is loaded as a shared library by + // force. + PROPERTY_DYNAMIC = 1U << 1U, + + // The package has been loaded dynamically using a ResourcesProvider. + PROPERTY_LOADER = 1U << 2U, + + // The package is a RRO. + PROPERTY_OVERLAY = 1U << 3U, +}; + // TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of // ResTable_type pointers. // TypeSpecPtr is a managed pointer that knows how to delete itself. @@ -136,8 +152,7 @@ class LoadedPackage { } static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk, - const LoadedIdmap* loaded_idmap, bool system, - bool load_as_shared_library); + package_property_t property_flags); ~LoadedPackage(); @@ -174,17 +189,26 @@ class LoadedPackage { // Returns true if this package is dynamic (shared library) and needs to have an ID assigned. inline bool IsDynamic() const { - return dynamic_; + return (property_flags_ & PROPERTY_DYNAMIC) != 0; + } + + // Returns true if this package is a Runtime Resource Overlay. + inline bool IsOverlay() const { + return (property_flags_ & PROPERTY_OVERLAY) != 0; } // Returns true if this package originates from a system provided resource. inline bool IsSystem() const { - return system_; + return (property_flags_ & PROPERTY_SYSTEM) != 0; } - // Returns true if this package is from an overlay ApkAssets. - inline bool IsOverlay() const { - return overlay_; + // Returns true if this package is a custom loader and should behave like an overlay. + inline bool IsCustomLoader() const { + return (property_flags_ & PROPERTY_LOADER) != 0; + } + + inline package_property_t GetPropertyFlags() const { + return property_flags_; } // Returns the map of package name to package ID used in this LoadedPackage. At runtime, a @@ -216,9 +240,6 @@ class LoadedPackage { const TypeSpecPtr& ptr = type_specs_[i]; if (ptr != nullptr) { uint8_t type_id = ptr->type_spec->id; - if (ptr->idmap_entries != nullptr) { - type_id = ptr->idmap_entries->target_type_id; - } f(ptr.get(), type_id - 1); } } @@ -255,17 +276,17 @@ class LoadedPackage { ResStringPool type_string_pool_; ResStringPool key_string_pool_; std::string package_name_; + bool defines_overlayable_ = false; int package_id_ = -1; int type_id_offset_ = 0; - bool dynamic_ = false; - bool system_ = false; - bool overlay_ = false; - bool defines_overlayable_ = false; + package_property_t property_flags_ = 0U; ByteBucketArray<TypeSpecPtr> type_specs_; ByteBucketArray<uint32_t> resource_ids_; std::vector<DynamicPackageEntry> dynamic_package_map_; std::vector<const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_; + + // A map of overlayable name to actor std::unordered_map<std::string, std::string> overlayable_map_; }; @@ -281,8 +302,7 @@ class LoadedArsc { // ID. static std::unique_ptr<const LoadedArsc> Load(const StringPiece& data, const LoadedIdmap* loaded_idmap = nullptr, - bool system = false, - bool load_as_shared_library = false); + package_property_t property_flags = 0U); // Create an empty LoadedArsc. This is used when an APK has no resources.arsc. static std::unique_ptr<const LoadedArsc> CreateEmpty(); @@ -290,7 +310,7 @@ class LoadedArsc { // Returns the string pool where all string resource values // (Res_value::dataType == Res_value::TYPE_STRING) are indexed. inline const ResStringPool* GetStringPool() const { - return &global_string_pool_; + return global_string_pool_.get(); } // Gets a pointer to the package with the specified package ID, or nullptr if no such package @@ -302,20 +322,15 @@ class LoadedArsc { return packages_; } - // Returns true if this is a system provided resource. - inline bool IsSystem() const { - return system_; - } - private: DISALLOW_COPY_AND_ASSIGN(LoadedArsc); LoadedArsc() = default; - bool LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library); + bool LoadTable( + const Chunk& chunk, const LoadedIdmap* loaded_idmap, package_property_t property_flags); - ResStringPool global_string_pool_; + std::unique_ptr<ResStringPool> global_string_pool_ = util::make_unique<ResStringPool>(); std::vector<std::unique_ptr<const LoadedPackage>> packages_; - bool system_ = false; }; } // namespace android diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index fc635aaeb0d8..e351a46d633a 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -22,6 +22,7 @@ #include <androidfw/Asset.h> #include <androidfw/LocaleData.h> +#include <androidfw/StringPiece.h> #include <utils/Errors.h> #include <utils/String16.h> #include <utils/Vector.h> @@ -34,12 +35,13 @@ #include <android/configuration.h> +#include <array> #include <memory> namespace android { constexpr const static uint32_t kIdmapMagic = 0x504D4449u; -constexpr const static uint32_t kIdmapCurrentVersion = 0x00000001u; +constexpr const static uint32_t kIdmapCurrentVersion = 0x00000004u; /** * In C++11, char16_t is defined as *at least* 16 bits. We do a lot of @@ -492,7 +494,7 @@ class ResStringPool public: ResStringPool(); ResStringPool(const void* data, size_t size, bool copyData=false); - ~ResStringPool(); + virtual ~ResStringPool(); void setToEmpty(); status_t setTo(const void* data, size_t size, bool copyData=false); @@ -506,10 +508,10 @@ public: inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const { return stringAt(ref.index, outLen); } - const char16_t* stringAt(size_t idx, size_t* outLen) const; + virtual const char16_t* stringAt(size_t idx, size_t* outLen) const; // Note: returns null if the string pool is not UTF8. - const char* string8At(size_t idx, size_t* outLen) const; + virtual const char* string8At(size_t idx, size_t* outLen) const; // Return string whether the pool is UTF8 or UTF16. Does not allow you // to distinguish null. @@ -520,9 +522,11 @@ public: ssize_t indexOfString(const char16_t* str, size_t strLen) const; - size_t size() const; + virtual size_t size() const; size_t styleCount() const; size_t bytes() const; + const void* data() const; + bool isSorted() const; bool isUTF8() const; @@ -810,7 +814,7 @@ public: * The tree stores a clone of the specified DynamicRefTable, so any changes to the original * DynamicRefTable will not affect this tree after instantiation. **/ - explicit ResXMLTree(const DynamicRefTable* dynamicRefTable); + explicit ResXMLTree(std::shared_ptr<const DynamicRefTable> dynamicRefTable); ResXMLTree(); ~ResXMLTree(); @@ -825,7 +829,7 @@ private: status_t validateNode(const ResXMLTree_node* node) const; - std::unique_ptr<const DynamicRefTable> mDynamicRefTable; + std::shared_ptr<const DynamicRefTable> mDynamicRefTable; status_t mError; void* mOwnedData; @@ -1582,6 +1586,50 @@ struct ResTable_map Res_value value; }; + +// A ResTable_entry variant that either holds an unmanaged pointer to a constant ResTable_entry or +// holds a ResTable_entry which is tied to the lifetime of the handle. +class ResTable_entry_handle { + public: + ResTable_entry_handle() = default; + + ResTable_entry_handle(const ResTable_entry_handle& handle) { + entry_ = handle.entry_; + } + + ResTable_entry_handle(ResTable_entry_handle&& handle) noexcept { + entry_ = handle.entry_; + } + + inline static ResTable_entry_handle managed(ResTable_entry* entry, void (*deleter)(void *)) { + return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry, deleter)); + } + + inline static ResTable_entry_handle unmanaged(const ResTable_entry* entry) { + return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry, [](auto /*p */){})); + } + + inline ResTable_entry_handle& operator=(const ResTable_entry_handle& handle) noexcept { + entry_ = handle.entry_; + return *this; + } + + inline ResTable_entry_handle& operator=(ResTable_entry_handle&& handle) noexcept { + entry_ = handle.entry_; + return *this; + } + + inline const ResTable_entry* operator*() & { + return entry_.get(); + } + + private: + explicit ResTable_entry_handle(std::shared_ptr<const ResTable_entry> entry) + : entry_(std::move(entry)) { } + + std::shared_ptr<const ResTable_entry> entry_; +}; + /** * A package-id to package name mapping for any shared libraries used * in this resource table. The package-id's encoded in this resource @@ -1630,43 +1678,66 @@ struct ResTable_overlayable_header */ struct ResTable_overlayable_policy_header { - struct ResChunk_header header; - + /** + * Flags for a bitmask for all possible overlayable policy options. + * + * Any changes to this set should also update aidl/android/os/OverlayablePolicy.aidl + */ enum PolicyFlags : uint32_t { + // Base + NONE = 0x00000000, + // Any overlay can overlay these resources. - POLICY_PUBLIC = 0x00000001, + PUBLIC = 0x00000001, // The overlay must reside of the system partition or must have existed on the system partition // before an upgrade to overlay these resources. - POLICY_SYSTEM_PARTITION = 0x00000002, + SYSTEM_PARTITION = 0x00000002, // The overlay must reside of the vendor partition or must have existed on the vendor partition // before an upgrade to overlay these resources. - POLICY_VENDOR_PARTITION = 0x00000004, + VENDOR_PARTITION = 0x00000004, // The overlay must reside of the product partition or must have existed on the product // partition before an upgrade to overlay these resources. - POLICY_PRODUCT_PARTITION = 0x00000008, + PRODUCT_PARTITION = 0x00000008, - // The overlay must be signed with the same signature as the actor of the target resource, - // which can be separate or the same as the target package with the resource. - POLICY_SIGNATURE = 0x00000010, + // The overlay must be signed with the same signature as the package containing the target + // resource + SIGNATURE = 0x00000010, // The overlay must reside of the odm partition or must have existed on the odm // partition before an upgrade to overlay these resources. - POLICY_ODM_PARTITION = 0x00000020, + ODM_PARTITION = 0x00000020, // The overlay must reside of the oem partition or must have existed on the oem // partition before an upgrade to overlay these resources. - POLICY_OEM_PARTITION = 0x00000040, + OEM_PARTITION = 0x00000040, + + // The overlay must be signed with the same signature as the actor declared for the target + // resource + ACTOR_SIGNATURE = 0x00000080, }; - uint32_t policy_flags; + + using PolicyBitmask = uint32_t; + + struct ResChunk_header header; + + PolicyFlags policy_flags; // The number of ResTable_ref that follow this header. uint32_t entry_count; }; -struct alignas(uint32_t) Idmap_header { +inline ResTable_overlayable_policy_header::PolicyFlags& operator |=( + ResTable_overlayable_policy_header::PolicyFlags& first, + ResTable_overlayable_policy_header::PolicyFlags second) { + first = static_cast<ResTable_overlayable_policy_header::PolicyFlags>(first | second); + return first; +} + +#pragma pack(push, 1) +struct Idmap_header { // Always 0x504D4449 ('IDMP') uint32_t magic; @@ -1675,20 +1746,38 @@ struct alignas(uint32_t) Idmap_header { uint32_t target_crc32; uint32_t overlay_crc32; + uint32_t fulfilled_policies; + uint8_t enforce_overlayable; + uint8_t target_path[256]; uint8_t overlay_path[256]; - uint16_t target_package_id; - uint16_t type_count; -} __attribute__((packed)); + uint32_t debug_info_size; + uint8_t debug_info[0]; -struct alignas(uint32_t) IdmapEntry_header { - uint16_t target_type_id; - uint16_t overlay_type_id; - uint16_t entry_count; - uint16_t entry_id_offset; - uint32_t entries[0]; -} __attribute__((packed)); + size_t Size() const; +}; + +struct Idmap_data_header { + uint8_t target_package_id; + uint8_t overlay_package_id; + uint32_t target_entry_count; + uint32_t overlay_entry_count; + uint32_t string_pool_index_offset; + uint32_t string_pool_length; +}; + +struct Idmap_target_entry { + uint32_t target_id; + uint8_t type; + uint32_t value; +}; + +struct Idmap_overlay_entry { + uint32_t overlay_id; + uint32_t target_id; +}; +#pragma pack(pop) class AssetManager2; @@ -1706,6 +1795,7 @@ class DynamicRefTable public: DynamicRefTable(); DynamicRefTable(uint8_t packageId, bool appAsLib); + virtual ~DynamicRefTable() = default; // Loads an unmapped reference table from the package. status_t load(const ResTable_lib_header* const header); @@ -1719,12 +1809,12 @@ public: void addMapping(uint8_t buildPackageId, uint8_t runtimePackageId); - // Creates a new clone of the reference table - std::unique_ptr<DynamicRefTable> clone() const; + // Returns whether or not the value must be looked up. + bool requiresLookup(const Res_value* value) const; // Performs the actual conversion of build-time resource ID to run-time // resource ID. - status_t lookupResourceId(uint32_t* resId) const; + virtual status_t lookupResourceId(uint32_t* resId) const; status_t lookupResourceValue(Res_value* value) const; inline const KeyedVector<String16, uint8_t>& entries() const { diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h index aa1466fde778..9a3646b49db8 100644 --- a/libs/androidfw/include/androidfw/Util.h +++ b/libs/androidfw/include/androidfw/Util.h @@ -19,12 +19,19 @@ #include <cstdlib> #include <memory> +#include <sstream> #include <vector> #include "android-base/macros.h" #include "androidfw/StringPiece.h" +#ifdef __ANDROID__ +#define ANDROID_LOG(x) LOG(x) +#else +#define ANDROID_LOG(x) std::stringstream() +#endif + namespace android { namespace util { diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index e2b9f0040989..19db25ce8811 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -42,7 +42,7 @@ TEST(ApkAssetsTest, LoadApk) { const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); ASSERT_THAT(loaded_arsc, NotNull()); ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); - ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); + ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml"), NotNull()); } TEST(ApkAssetsTest, LoadApkFromFd) { @@ -50,14 +50,13 @@ TEST(ApkAssetsTest, LoadApkFromFd) { unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY)); ASSERT_THAT(fd.get(), Ge(0)); - std::unique_ptr<const ApkAssets> loaded_apk = - ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/); + std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::LoadFromFd(std::move(fd), path); ASSERT_THAT(loaded_apk, NotNull()); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); ASSERT_THAT(loaded_arsc, NotNull()); ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); - ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); + ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml"), NotNull()); } TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { @@ -70,7 +69,7 @@ TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic()); - loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); + loaded_apk = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk", PROPERTY_DYNAMIC); ASSERT_THAT(loaded_apk, NotNull()); loaded_arsc = loaded_apk->GetLoadedArsc(); @@ -79,47 +78,16 @@ TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic()); } -TEST(ApkAssetsTest, LoadApkWithIdmap) { - std::string contents; - ResTable target_table; - const std::string target_path = GetTestDataPath() + "/basic/basic.apk"; - ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents)); - ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/), - Eq(NO_ERROR)); - - ResTable overlay_table; - const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk"; - ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents)); - ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/), - Eq(NO_ERROR)); - - util::unique_cptr<void> idmap_data; - void* temp_data; - size_t idmap_len; - - ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), - overlay_path.c_str(), &temp_data, &idmap_len), - Eq(NO_ERROR)); - idmap_data.reset(temp_data); - - TemporaryFile tf; - ASSERT_TRUE(base::WriteFully(tf.fd, idmap_data.get(), idmap_len)); - close(tf.fd); - - // Open something so that the destructor of TemporaryFile closes a valid fd. - tf.fd = open("/dev/null", O_WRONLY); - - ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull()); -} - TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) { std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); ASSERT_THAT(loaded_apk, NotNull()); - { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } + { ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml", + Asset::ACCESS_BUFFER), NotNull()); } - { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } + { ASSERT_THAT(loaded_apk->GetAssetsProvider()->Open("res/layout/main.xml", + Asset::ACCESS_BUFFER), NotNull()); } } TEST(ApkAssetsTest, OpenUncompressedAssetFd) { @@ -127,7 +95,8 @@ TEST(ApkAssetsTest, OpenUncompressedAssetFd) { ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); ASSERT_THAT(loaded_apk, NotNull()); - auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN); + auto asset = loaded_apk->GetAssetsProvider()->Open("assets/uncompressed.txt", + Asset::ACCESS_UNKNOWN); ASSERT_THAT(asset, NotNull()); off64_t start, length; diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 40c8e46e4d84..8c255d16fe1f 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -17,9 +17,9 @@ #include "androidfw/AssetManager2.h" #include "androidfw/AssetManager.h" -#include "android-base/logging.h" - #include "TestHelpers.h" +#include "android-base/file.h" +#include "android-base/logging.h" #include "androidfw/ResourceUtils.h" #include "data/appaslib/R.h" #include "data/basic/R.h" @@ -45,35 +45,43 @@ namespace android { class AssetManager2Test : public ::testing::Test { public: void SetUp() override { - basic_assets_ = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); + // Move to the test data directory so the idmap can locate the overlay APK. + std::string original_path = base::GetExecutableDirectory(); + chdir(GetTestDataPath().c_str()); + + basic_assets_ = ApkAssets::Load("basic/basic.apk"); ASSERT_NE(nullptr, basic_assets_); - basic_de_fr_assets_ = ApkAssets::Load(GetTestDataPath() + "/basic/basic_de_fr.apk"); + basic_de_fr_assets_ = ApkAssets::Load("basic/basic_de_fr.apk"); ASSERT_NE(nullptr, basic_de_fr_assets_); - style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + style_assets_ = ApkAssets::Load("styles/styles.apk"); ASSERT_NE(nullptr, style_assets_); - lib_one_assets_ = ApkAssets::Load(GetTestDataPath() + "/lib_one/lib_one.apk"); + lib_one_assets_ = ApkAssets::Load("lib_one/lib_one.apk"); ASSERT_NE(nullptr, lib_one_assets_); - lib_two_assets_ = ApkAssets::Load(GetTestDataPath() + "/lib_two/lib_two.apk"); + lib_two_assets_ = ApkAssets::Load("lib_two/lib_two.apk"); ASSERT_NE(nullptr, lib_two_assets_); - libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk"); + libclient_assets_ = ApkAssets::Load("libclient/libclient.apk"); ASSERT_NE(nullptr, libclient_assets_); - appaslib_assets_ = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); + appaslib_assets_ = ApkAssets::Load("appaslib/appaslib.apk", PROPERTY_DYNAMIC); ASSERT_NE(nullptr, appaslib_assets_); - system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/); + system_assets_ = ApkAssets::Load("system/system.apk", PROPERTY_SYSTEM); ASSERT_NE(nullptr, system_assets_); - app_assets_ = ApkAssets::Load(GetTestDataPath() + "/app/app.apk"); + app_assets_ = ApkAssets::Load("app/app.apk"); ASSERT_THAT(app_assets_, NotNull()); - overlayable_assets_ = ApkAssets::Load(GetTestDataPath() + "/overlayable/overlayable.apk"); + overlay_assets_ = ApkAssets::LoadOverlay("overlay/overlay.idmap"); + ASSERT_NE(nullptr, overlay_assets_); + + overlayable_assets_ = ApkAssets::Load("overlayable/overlayable.apk"); ASSERT_THAT(overlayable_assets_, NotNull()); + chdir(original_path.c_str()); } protected: @@ -86,6 +94,7 @@ class AssetManager2Test : public ::testing::Test { std::unique_ptr<const ApkAssets> appaslib_assets_; std::unique_ptr<const ApkAssets> system_assets_; std::unique_ptr<const ApkAssets> app_assets_; + std::unique_ptr<const ApkAssets> overlay_assets_; std::unique_ptr<const ApkAssets> overlayable_assets_; }; @@ -214,6 +223,26 @@ TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) { EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data); } +TEST_F(AssetManager2Test, AssignsOverlayPackageIdLast) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets( + {overlayable_assets_.get(), overlay_assets_.get(), lib_one_assets_.get()}); + + auto apk_assets = assetmanager.GetApkAssets(); + ASSERT_EQ(3, apk_assets.size()); + ASSERT_EQ(overlayable_assets_.get(), apk_assets[0]); + ASSERT_EQ(overlay_assets_.get(), apk_assets[1]); + ASSERT_EQ(lib_one_assets_.get(), apk_assets[2]); + + auto get_first_package_id = [&assetmanager](const ApkAssets* apkAssets) -> uint8_t { + return assetmanager.GetAssignedPackageId(apkAssets->GetLoadedArsc()->GetPackages()[0].get()); + }; + + ASSERT_EQ(get_first_package_id(overlayable_assets_.get()), 0x7f); + ASSERT_EQ(get_first_package_id(overlay_assets_.get()), 0x03); + ASSERT_EQ(get_first_package_id(lib_one_assets_.get()), 0x02); +} + TEST_F(AssetManager2Test, GetSharedLibraryResourceName) { AssetManager2 assetmanager; assetmanager.SetApkAssets({lib_one_assets_.get()}); @@ -266,6 +295,27 @@ TEST_F(AssetManager2Test, FindsBagResourceFromSharedLibrary) { EXPECT_EQ(0x03, get_package_id(bag->entries[1].key)); } +TEST_F(AssetManager2Test, FindsBagResourceFromMultipleSharedLibraries) { + AssetManager2 assetmanager; + + // libclient is built with lib_one and then lib_two in order. + // Reverse the order to test that proper package ID re-assignment is happening. + assetmanager.SetApkAssets( + {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); + + const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::ThemeMultiLib); + ASSERT_NE(nullptr, bag); + ASSERT_EQ(bag->entry_count, 2u); + + // First attribute comes from lib_two. + EXPECT_EQ(2, bag->entries[0].cookie); + EXPECT_EQ(0x02, get_package_id(bag->entries[0].key)); + + // The next two attributes come from lib_one. + EXPECT_EQ(2, bag->entries[1].cookie); + EXPECT_EQ(0x03, get_package_id(bag->entries[1].key)); +} + TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) { AssetManager2 assetmanager; @@ -707,7 +757,7 @@ TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) { EXPECT_EQ("", resultDisabled); } -TEST_F(AssetManager2Test, GetOverlayableMap) { +TEST_F(AssetManager2Test, GetOverlayablesToString) { ResTable_config desired_config; memset(&desired_config, 0, sizeof(desired_config)); @@ -718,9 +768,16 @@ TEST_F(AssetManager2Test, GetOverlayableMap) { const auto map = assetmanager.GetOverlayableMapForPackage(0x7f); ASSERT_NE(nullptr, map); - ASSERT_EQ(2, map->size()); + ASSERT_EQ(3, map->size()); ASSERT_EQ(map->at("OverlayableResources1"), "overlay://theme"); ASSERT_EQ(map->at("OverlayableResources2"), "overlay://com.android.overlayable"); + ASSERT_EQ(map->at("OverlayableResources3"), ""); + + std::string api; + ASSERT_TRUE(assetmanager.GetOverlayablesToString("com.android.overlayable", &api)); + ASSERT_EQ(api.find("not_overlayable"), std::string::npos); + ASSERT_NE(api.find("resource='com.android.overlayable:string/overlayable2' overlayable='OverlayableResources1' actor='overlay://theme' policy='0x0000000a'\n"), + std::string::npos); } } // namespace android diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index c8dbe205fee2..24361b5817f4 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -67,7 +67,7 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) { AssetManager2 assetmanager; - auto apk_assets = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/styles/styles.apk"); + auto apk_assets = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk", PROPERTY_DYNAMIC); ASSERT_NE(nullptr, apk_assets); assetmanager.SetApkAssets({apk_assets.get()}); diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp index 10b83a75304d..7aa0dbbafab3 100644 --- a/libs/androidfw/tests/Idmap_test.cpp +++ b/libs/androidfw/tests/Idmap_test.cpp @@ -14,114 +14,255 @@ * limitations under the License. */ +#include "android-base/file.h" +#include "androidfw/ApkAssets.h" +#include "androidfw/AssetManager2.h" #include "androidfw/ResourceTypes.h" #include "utils/String16.h" #include "utils/String8.h" #include "TestHelpers.h" -#include "data/basic/R.h" +#include "data/overlay/R.h" +#include "data/overlayable/R.h" +#include "data/system/R.h" -using ::com::android::basic::R; +namespace overlay = com::android::overlay; +namespace overlayable = com::android::overlayable; namespace android { +namespace { + class IdmapTest : public ::testing::Test { protected: void SetUp() override { - std::string contents; - ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", - &contents)); - ASSERT_EQ(NO_ERROR, target_table_.add(contents.data(), contents.size(), 0, true)); - - ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", - "resources.arsc", &overlay_data_)); - ResTable overlay_table; - ASSERT_EQ(NO_ERROR, overlay_table.add(overlay_data_.data(), overlay_data_.size())); - - char target_name[256] = "com.android.basic"; - ASSERT_EQ(NO_ERROR, overlay_table.createIdmap(target_table_, 0, 0, target_name, target_name, - &data_, &data_size_)); + // Move to the test data directory so the idmap can locate the overlay APK. + original_path = base::GetExecutableDirectory(); + chdir(GetTestDataPath().c_str()); + + system_assets_ = ApkAssets::Load("system/system.apk"); + ASSERT_NE(nullptr, system_assets_); + + overlay_assets_ = ApkAssets::LoadOverlay("overlay/overlay.idmap"); + ASSERT_NE(nullptr, overlay_assets_); + + overlayable_assets_ = ApkAssets::Load("overlayable/overlayable.apk"); + ASSERT_NE(nullptr, overlayable_assets_); } void TearDown() override { - ::free(data_); + chdir(original_path.c_str()); } - ResTable target_table_; - std::string overlay_data_; - void* data_ = nullptr; - size_t data_size_ = 0; + protected: + std::string original_path; + std::unique_ptr<const ApkAssets> system_assets_; + std::unique_ptr<const ApkAssets> overlay_assets_; + std::unique_ptr<const ApkAssets> overlayable_assets_; }; -TEST_F(IdmapTest, CanLoadIdmap) { - ASSERT_EQ(NO_ERROR, - target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_)); +std::string GetStringFromApkAssets(const AssetManager2& asset_manager, const Res_value& value, + ApkAssetsCookie cookie) { + auto assets = asset_manager.GetApkAssets(); + const ResStringPool* string_pool = assets[cookie]->GetLoadedArsc()->GetStringPool(); + return GetStringFromPool(string_pool, value.data); +} + } TEST_F(IdmapTest, OverlayOverridesResourceValue) { + AssetManager2 asset_manager; + asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), + overlay_assets_.get()}); Res_value val; - ssize_t block = target_table_.getResource(R::string::test2, &val, false); - ASSERT_GE(block, 0); - ASSERT_EQ(Res_value::TYPE_STRING, val.dataType); - const ResStringPool* pool = target_table_.getTableStringBlock(block); - ASSERT_TRUE(pool != NULL); - ASSERT_LT(val.data, pool->size()); - - size_t str_len; - const char16_t* target_str16 = pool->stringAt(val.data, &str_len); - ASSERT_TRUE(target_str16 != NULL); - ASSERT_EQ(String16("test2"), String16(target_str16, str_len)); - - ASSERT_EQ(NO_ERROR, - target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_)); - - ssize_t new_block = target_table_.getResource(R::string::test2, &val, false); - ASSERT_GE(new_block, 0); - ASSERT_NE(block, new_block); - ASSERT_EQ(Res_value::TYPE_STRING, val.dataType); - pool = target_table_.getTableStringBlock(new_block); - ASSERT_TRUE(pool != NULL); - ASSERT_LT(val.data, pool->size()); - - target_str16 = pool->stringAt(val.data, &str_len); - ASSERT_TRUE(target_str16 != NULL); - ASSERT_EQ(String16("test2-overlay"), String16(target_str16, str_len)); + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable5, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 2U); + ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); + ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Overlay One"); } -TEST_F(IdmapTest, OverlaidResourceHasSameName) { - ASSERT_EQ(NO_ERROR, - target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_)); +TEST_F(IdmapTest, OverlayOverridesResourceValueUsingDifferentPackage) { + AssetManager2 asset_manager; + asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), + overlay_assets_.get()}); + Res_value val; + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable10, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 0U); + ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); + ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "yes"); +} + +TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInternalResource) { + AssetManager2 asset_manager; + asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), + overlay_assets_.get()}); + Res_value val; + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable8, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 2U); + ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE); + ASSERT_EQ(val.data, (overlay::R::string::internal & 0x00ffffff) | (0x02 << 24)); +} + +TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineInteger) { + AssetManager2 asset_manager; + asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), + overlay_assets_.get()}); + Res_value val; + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::integer::config_integer, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 2U); + ASSERT_EQ(val.dataType, Res_value::TYPE_INT_DEC); + ASSERT_EQ(val.data, 42); +} + +TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineString) { + AssetManager2 asset_manager; + asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), + overlay_assets_.get()}); + Res_value val; + ResTable_config config; + uint32_t flags; + + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 2U); + ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); + ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Hardcoded string"); +} + +TEST_F(IdmapTest, OverlayOverridesResourceValueUsingOverlayingResource) { + AssetManager2 asset_manager; + asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), + overlay_assets_.get()}); + Res_value val; + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable9, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 2U); + ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE); + ASSERT_EQ(val.data, overlayable::R::string::overlayable7); +} + +TEST_F(IdmapTest, OverlayOverridesXmlParser) { + AssetManager2 asset_manager; + asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), + overlay_assets_.get()}); + Res_value val; + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::layout::hello_view, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 2U); + ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); + ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "res/layout/hello_view.xml"); + + auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", cookie, + Asset::ACCESS_RANDOM); + auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(cookie); + auto xml_tree = util::make_unique<ResXMLTree>(std::move(dynamic_ref_table)); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), false); + ASSERT_EQ(err, NO_ERROR); + + while (xml_tree->next() != ResXMLParser::START_TAG) { } - ResTable::resource_name res_name; - ASSERT_TRUE(target_table_.getResourceName(R::array::integerArray1, true, &res_name)); + // The resource id of @id/hello_view should be rewritten to the resource id/hello_view within the + // target. + ASSERT_EQ(xml_tree->getAttributeNameResID(0), 0x010100d0 /* android:attr/id */); + ASSERT_EQ(xml_tree->getAttributeDataType(0), Res_value::TYPE_REFERENCE); + ASSERT_EQ(xml_tree->getAttributeData(0), overlayable::R::id::hello_view); - ASSERT_TRUE(res_name.package != NULL); - ASSERT_TRUE(res_name.type != NULL); - ASSERT_TRUE(res_name.name8 != NULL); + // The resource id of @android:string/yes should not be rewritten even though it overlays + // string/overlayable10 in the target. + ASSERT_EQ(xml_tree->getAttributeNameResID(1), 0x0101014f /* android:attr/text */); + ASSERT_EQ(xml_tree->getAttributeDataType(1), Res_value::TYPE_REFERENCE); + ASSERT_EQ(xml_tree->getAttributeData(1), 0x01040013 /* android:string/yes */); - EXPECT_EQ(String16("com.android.basic"), String16(res_name.package, res_name.packageLen)); - EXPECT_EQ(String16("array"), String16(res_name.type, res_name.typeLen)); - EXPECT_EQ(String8("integerArray1"), String8(res_name.name8, res_name.nameLen)); + // The resource id of the attribute within the overlay should be rewritten to the resource id of + // the attribute in the target. + ASSERT_EQ(xml_tree->getAttributeNameResID(2), overlayable::R::attr::max_lines); + ASSERT_EQ(xml_tree->getAttributeDataType(2), Res_value::TYPE_INT_DEC); + ASSERT_EQ(xml_tree->getAttributeData(2), 4); } -constexpr const uint32_t kNonOverlaidResourceId = 0x7fff0000u; +TEST_F(IdmapTest, OverlaidResourceHasSameName) { + AssetManager2 asset_manager; + asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), + overlay_assets_.get()}); -TEST_F(IdmapTest, OverlayDoesNotIncludeNonOverlaidResources) { - // First check that the resource we're trying to not include when overlaid is present when - // the overlay is loaded as a standalone APK. - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(overlay_data_.data(), overlay_data_.size(), 0, true)); + AssetManager2::ResourceName name; + ASSERT_TRUE(asset_manager.GetResourceName(overlayable::R::string::overlayable9, &name)); + ASSERT_EQ(std::string(name.package), "com.android.overlayable"); + ASSERT_EQ(String16(name.type16), u"string"); + ASSERT_EQ(std::string(name.entry), "overlayable9"); +} + +TEST_F(IdmapTest, OverlayLoaderInterop) { + std::string contents; + auto loader_assets = ApkAssets::LoadTable("loader/resources.arsc", PROPERTY_LOADER); + + AssetManager2 asset_manager; + asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(), + overlay_assets_.get()}); Res_value val; - ssize_t block = table.getResource(kNonOverlaidResourceId, &val, false /*mayBeBag*/); - ASSERT_GE(block, 0); - - // Now add the overlay and verify that the unoverlaid resource is gone. - ASSERT_EQ(NO_ERROR, - target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_)); - block = target_table_.getResource(kNonOverlaidResourceId, &val, false /*mayBeBag*/); - ASSERT_LT(block, 0); + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + std::cout << asset_manager.GetLastResourceResolution(); + ASSERT_EQ(cookie, 1U); + ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); + ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "loader"); +} + +TEST_F(IdmapTest, OverlayAssetsIsUpToDate) { + std::string idmap_contents; + ASSERT_TRUE(base::ReadFileToString("overlay/overlay.idmap", &idmap_contents)); + + TemporaryFile temp_file; + ASSERT_TRUE(base::WriteStringToFile(idmap_contents, temp_file.path)); + + auto apk_assets = ApkAssets::LoadOverlay(temp_file.path); + ASSERT_NE(nullptr, apk_assets); + ASSERT_TRUE(apk_assets->IsUpToDate()); + + unlink(temp_file.path); + ASSERT_FALSE(apk_assets->IsUpToDate()); + sleep(2); + + base::WriteStringToFile("hello", temp_file.path); + sleep(2); + + ASSERT_FALSE(apk_assets->IsUpToDate()); } } // namespace diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index d58e8d20c8aa..2d69dfe4f429 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -25,6 +25,7 @@ #include "data/overlayable/R.h" #include "data/sparse/R.h" #include "data/styles/R.h" +#include "data/system/R.h" namespace app = com::android::app; namespace basic = com::android::basic; @@ -40,6 +41,8 @@ using ::testing::NotNull; using ::testing::SizeIs; using ::testing::StrEq; +using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; + namespace android { TEST(LoadedArscTest, LoadSinglePackageArsc) { @@ -143,8 +146,7 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { "resources.arsc", &contents)); std::unique_ptr<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, - true /*load_as_shared_library*/); + LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, PROPERTY_DYNAMIC); ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); @@ -221,68 +223,12 @@ TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) { ASSERT_THAT(type_spec->types[0], NotNull()); } -class MockLoadedIdmap : public LoadedIdmap { - public: - MockLoadedIdmap() : LoadedIdmap() { - local_header_.magic = kIdmapMagic; - local_header_.version = kIdmapCurrentVersion; - local_header_.target_package_id = 0x08; - local_header_.type_count = 1; - header_ = &local_header_; - - entry_header = util::unique_cptr<IdmapEntry_header>( - (IdmapEntry_header*)::malloc(sizeof(IdmapEntry_header) + sizeof(uint32_t))); - entry_header->target_type_id = 0x03; - entry_header->overlay_type_id = 0x02; - entry_header->entry_id_offset = 1; - entry_header->entry_count = 1; - entry_header->entries[0] = 0x00000000u; - type_map_[entry_header->overlay_type_id] = entry_header.get(); - } - - private: - Idmap_header local_header_; - util::unique_cptr<IdmapEntry_header> entry_header; -}; - -TEST(LoadedArscTest, LoadOverlay) { - std::string contents; - ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc", - &contents)); - - MockLoadedIdmap loaded_idmap; - - std::unique_ptr<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(StringPiece(contents), &loaded_idmap); - ASSERT_THAT(loaded_arsc, NotNull()); - - const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u); - ASSERT_THAT(package, NotNull()); - - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); - - // The entry being overlaid doesn't exist at the original entry index. - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull()); - - // Since this is an overlay, the actual entry ID must be mapped. - ASSERT_THAT(type_spec->idmap_entries, NotNull()); - uint16_t target_entry_id = 0u; - ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id)); - ASSERT_THAT(target_entry_id, Eq(0x0u)); - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull()); -} - TEST(LoadedArscTest, LoadOverlayable) { std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, - false /*load_as_shared_library*/); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = loaded_arsc->GetPackageById( @@ -296,29 +242,29 @@ TEST(LoadedArscTest, LoadOverlayable) { ASSERT_THAT(info, NotNull()); EXPECT_THAT(info->name, Eq("OverlayableResources1")); EXPECT_THAT(info->actor, Eq("overlay://theme")); - EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC)); + EXPECT_THAT(info->policy_flags, Eq(PolicyFlags::PUBLIC)); info = package->GetOverlayableInfo(overlayable::R::string::overlayable2); ASSERT_THAT(info, NotNull()); EXPECT_THAT(info->name, Eq("OverlayableResources1")); EXPECT_THAT(info->actor, Eq("overlay://theme")); EXPECT_THAT(info->policy_flags, - Eq(ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION - | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION)); + Eq(PolicyFlags::SYSTEM_PARTITION + | PolicyFlags::PRODUCT_PARTITION)); info = package->GetOverlayableInfo(overlayable::R::string::overlayable3); ASSERT_THAT(info, NotNull()); EXPECT_THAT(info->name, Eq("OverlayableResources2")); EXPECT_THAT(info->actor, Eq("overlay://com.android.overlayable")); EXPECT_THAT(info->policy_flags, - Eq(ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION - | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION)); + Eq(PolicyFlags::VENDOR_PARTITION + | PolicyFlags::PRODUCT_PARTITION)); info = package->GetOverlayableInfo(overlayable::R::string::overlayable4); EXPECT_THAT(info->name, Eq("OverlayableResources1")); EXPECT_THAT(info->actor, Eq("overlay://theme")); ASSERT_THAT(info, NotNull()); - EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC)); + EXPECT_THAT(info->policy_flags, Eq(PolicyFlags::PUBLIC)); } TEST(LoadedArscTest, ResourceIdentifierIterator) { @@ -382,9 +328,42 @@ TEST(LoadedArscTest, GetOverlayableMap) { ASSERT_EQ(std::string("com.android.overlayable"), packages[0]->GetPackageName()); const auto map = packages[0]->GetOverlayableMap(); - ASSERT_EQ(2, map.size()); + ASSERT_EQ(3, map.size()); ASSERT_EQ(map.at("OverlayableResources1"), "overlay://theme"); ASSERT_EQ(map.at("OverlayableResources2"), "overlay://com.android.overlayable"); + ASSERT_EQ(map.at("OverlayableResources3"), ""); +} + +TEST(LoadedArscTest, LoadCustomLoader) { + std::string contents; + + std::unique_ptr<Asset> + asset = ApkAssets::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc"); + + const StringPiece data( + reinterpret_cast<const char*>(asset->getBuffer(true /*wordAligned*/)), + asset->getLength()); + + std::unique_ptr<const LoadedArsc> loaded_arsc = + LoadedArsc::Load(data, nullptr, PROPERTY_LOADER); + ASSERT_THAT(loaded_arsc, NotNull()); + + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(overlayable::R::string::overlayable11)); + ASSERT_THAT(package, NotNull()); + EXPECT_THAT(package->GetPackageName(), StrEq("com.android.loader")); + EXPECT_THAT(package->GetPackageId(), Eq(0x7f)); + + const uint8_t type_index = get_type_id(overlayable::R::string::overlayable11) - 1; + const uint16_t entry_index = get_entry_id(overlayable::R::string::overlayable11); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + + const ResTable_type* type = type_spec->types[0]; + ASSERT_THAT(type, NotNull()); + ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); } // structs with size fields (like Res_value, ResTable_entry) should be diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp index be5ecd94a588..16b9c75982fb 100644 --- a/libs/androidfw/tests/Theme_test.cpp +++ b/libs/androidfw/tests/Theme_test.cpp @@ -36,7 +36,7 @@ namespace android { class ThemeTest : public ::testing::Test { public: void SetUp() override { - system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/); + system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", PROPERTY_SYSTEM); ASSERT_NE(nullptr, system_assets_); style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); diff --git a/libs/androidfw/tests/data/lib_two/R.h b/libs/androidfw/tests/data/lib_two/R.h index 92b9cc10e7a8..fd5a910961cb 100644 --- a/libs/androidfw/tests/data/lib_two/R.h +++ b/libs/androidfw/tests/data/lib_two/R.h @@ -30,16 +30,22 @@ struct R { }; }; + struct integer { + enum : uint32_t { + bar = 0x02020000, // default + }; + }; + struct string { enum : uint32_t { - LibraryString = 0x02020000, // default - foo = 0x02020001, // default + LibraryString = 0x02030000, // default + foo = 0x02030001, // default }; }; struct style { enum : uint32_t { - Theme = 0x02030000, // default + Theme = 0x02040000, // default }; }; }; diff --git a/libs/androidfw/tests/data/lib_two/lib_two.apk b/libs/androidfw/tests/data/lib_two/lib_two.apk Binary files differindex 486c23000276..8193db637eed 100644 --- a/libs/androidfw/tests/data/lib_two/lib_two.apk +++ b/libs/androidfw/tests/data/lib_two/lib_two.apk diff --git a/libs/androidfw/tests/data/lib_two/res/values/values.xml b/libs/androidfw/tests/data/lib_two/res/values/values.xml index 340d14c34c5d..4e1d69aa5a5a 100644 --- a/libs/androidfw/tests/data/lib_two/res/values/values.xml +++ b/libs/androidfw/tests/data/lib_two/res/values/values.xml @@ -18,14 +18,17 @@ <public type="attr" name="attr3" id="0x00010000" /> <attr name="attr3" format="integer" /> - <public type="string" name="LibraryString" id="0x00020000" /> + <public type="integer" name="bar" id="0x00020000" /> + <integer name="bar">1337</integer> + + <public type="string" name="LibraryString" id="0x00030000" /> <string name="LibraryString">Hi from library two</string> - <public type="string" name="foo" id="0x00020001" /> + <public type="string" name="foo" id="0x00030001" /> <string name="foo">Foo from lib_two</string> - <public type="style" name="Theme" id="0x02030000" /> + <public type="style" name="Theme" id="0x00040000" /> <style name="Theme"> - <item name="com.android.lib_two:attr3">800</item> + <item name="com.android.lib_two:attr3">@integer/bar</item> </style> </resources> diff --git a/libs/androidfw/tests/data/libclient/R.h b/libs/androidfw/tests/data/libclient/R.h index 43d1f9bb68e7..e21b3ebae826 100644 --- a/libs/androidfw/tests/data/libclient/R.h +++ b/libs/androidfw/tests/data/libclient/R.h @@ -34,6 +34,7 @@ struct R { struct style { enum : uint32_t { Theme = 0x7f020000, // default + ThemeMultiLib = 0x7f020001, // default }; }; diff --git a/libs/androidfw/tests/data/libclient/libclient.apk b/libs/androidfw/tests/data/libclient/libclient.apk Binary files differindex 17990248e862..4b9a8833c64a 100644 --- a/libs/androidfw/tests/data/libclient/libclient.apk +++ b/libs/androidfw/tests/data/libclient/libclient.apk diff --git a/libs/androidfw/tests/data/libclient/res/values/values.xml b/libs/androidfw/tests/data/libclient/res/values/values.xml index fead7c323767..a29f473e3c17 100644 --- a/libs/androidfw/tests/data/libclient/res/values/values.xml +++ b/libs/androidfw/tests/data/libclient/res/values/values.xml @@ -27,6 +27,12 @@ <item name="bar">@com.android.lib_one:string/foo</item> </style> + <public type="style" name="ThemeMultiLib" id="0x7f020001" /> + <style name="ThemeMultiLib" > + <item name="com.android.lib_one:attr1">@com.android.lib_one:string/foo</item> + <item name="com.android.lib_two:attr3">@com.android.lib_two:integer/bar</item> + </style> + <public type="string" name="foo_one" id="0x7f030000" /> <string name="foo_one">@com.android.lib_one:string/foo</string> diff --git a/libs/androidfw/tests/data/loader/AndroidManifest.xml b/libs/androidfw/tests/data/loader/AndroidManifest.xml new file mode 100644 index 000000000000..4c0bb47f59bf --- /dev/null +++ b/libs/androidfw/tests/data/loader/AndroidManifest.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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. +--> + +<manifest package="com.android.loader"> + <application> + </application> +</manifest> diff --git a/libs/androidfw/tests/data/loader/build b/libs/androidfw/tests/data/loader/build new file mode 100755 index 000000000000..457ec51ffe69 --- /dev/null +++ b/libs/androidfw/tests/data/loader/build @@ -0,0 +1,27 @@ +#!/bin/bash +# +# Copyright (C) 2018 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. +# + +set -e + +FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk + +rm resources.arsc +aapt2 compile --dir res -o compiled.flata +aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o loader.apk compiled.flata +unzip loader.apk resources.arsc +rm loader.apk +rm compiled.flata diff --git a/libs/androidfw/tests/data/loader/res/values/public.xml b/libs/androidfw/tests/data/loader/res/values/public.xml new file mode 100644 index 000000000000..3293229104ce --- /dev/null +++ b/libs/androidfw/tests/data/loader/res/values/public.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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. +--> + +<resources> + <public type="string" name="overlayable11" id="0x7f01000b" /> +</resources>
\ No newline at end of file diff --git a/libs/androidfw/tests/data/loader/res/values/values.xml b/libs/androidfw/tests/data/loader/res/values/values.xml new file mode 100644 index 000000000000..0653536508f8 --- /dev/null +++ b/libs/androidfw/tests/data/loader/res/values/values.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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. +--> + +<resources> + <string name="overlayable11">loader</string> +</resources> diff --git a/libs/androidfw/tests/data/loader/resources.arsc b/libs/androidfw/tests/data/loader/resources.arsc Binary files differnew file mode 100644 index 000000000000..2bdb288dbcab --- /dev/null +++ b/libs/androidfw/tests/data/loader/resources.arsc diff --git a/libs/androidfw/tests/data/overlay/AndroidManifest.xml b/libs/androidfw/tests/data/overlay/AndroidManifest.xml index a56ac18e900b..28a11489fae0 100644 --- a/libs/androidfw/tests/data/overlay/AndroidManifest.xml +++ b/libs/androidfw/tests/data/overlay/AndroidManifest.xml @@ -15,7 +15,9 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.test.basic"> - <application> - </application> + package="com.android.test.overlay"> + <overlay + android:targetPackage="com.android.test.basic" + android:targetName="OverlayableResources3" + android:resourcesMap="@xml/overlays"/> </manifest> diff --git a/libs/hwui/debug/DefaultGlesDriver.h b/libs/androidfw/tests/data/overlay/R.h index 8027ea284aaa..f3dbed29d7ee 100644 --- a/libs/hwui/debug/DefaultGlesDriver.h +++ b/libs/androidfw/tests/data/overlay/R.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2019 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. @@ -14,21 +14,25 @@ * limitations under the License. */ -#pragma once +#ifndef TESTS_DATA_OVERLAY_R_H_ +#define TESTS_DATA_OVERLAY_R_H_ -#include "GlesDriver.h" +#include <cstdint> +namespace com { namespace android { -namespace uirenderer { -namespace debug { +namespace overlay { -class DefaultGlesDriver : public GlesDriver { -public: -#define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) override; -#include "gles_decls.in" -#undef GL_ENTRY +struct R { + struct string { + enum : uint32_t { + internal = 0x7f040000, + }; + }; }; -} // namespace debug -} // namespace uirenderer +} // namespace overlay } // namespace android +} // namespace com + +#endif /* TESTS_DATA_OVERLAY_R_H_ */ diff --git a/libs/androidfw/tests/data/overlay/build b/libs/androidfw/tests/data/overlay/build index 716b1bd9db64..99dfd63051a7 100755 --- a/libs/androidfw/tests/data/overlay/build +++ b/libs/androidfw/tests/data/overlay/build @@ -17,6 +17,15 @@ set -e +FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk + aapt2 compile --dir res -o compiled.flata -aapt2 link --manifest AndroidManifest.xml -o overlay.apk compiled.flata +aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o overlay.apk compiled.flata \ + --no-auto-version rm compiled.flata + +# Navigate back a directory so the idmap can find the overlays in the test data directory when being +# loaded during testing. +cd ../ +idmap2 create --target-apk-path overlayable/overlayable.apk \ + --overlay-apk-path overlay/overlay.apk --idmap-path overlay/overlay.idmap diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk Binary files differindex d37874dcbb40..f1ed59279fdb 100644 --- a/libs/androidfw/tests/data/overlay/overlay.apk +++ b/libs/androidfw/tests/data/overlay/overlay.apk diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap Binary files differnew file mode 100644 index 000000000000..29c5eb6a9ccf --- /dev/null +++ b/libs/androidfw/tests/data/overlay/overlay.idmap diff --git a/libs/androidfw/tests/data/overlay/res/layout/hello_view.xml b/libs/androidfw/tests/data/overlay/res/layout/hello_view.xml new file mode 100644 index 000000000000..54dc6c09acf1 --- /dev/null +++ b/libs/androidfw/tests/data/overlay/res/layout/hello_view.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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. +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/hello_view" + android:text="@android:string/yes" + app:max_lines="4"/>
\ No newline at end of file diff --git a/libs/androidfw/tests/data/overlay/res/values/values.xml b/libs/androidfw/tests/data/overlay/res/values/values.xml index 8e4417e457d1..ba018ec19e9f 100644 --- a/libs/androidfw/tests/data/overlay/res/values/values.xml +++ b/libs/androidfw/tests/data/overlay/res/values/values.xml @@ -13,13 +13,11 @@ See the License for the specific language governing permissions and limitations under the License. --> - <resources> - <string name="test2">test2-overlay</string> - <integer-array name="integerArray1"> - <item>10</item> - <item>11</item> - </integer-array> - <public type="animator" name="unoverlaid" id="0x7fff0000" /> - <item type="animator" name="unoverlaid">@null</item> + <string name="overlay1">Overlay One</string> + <string name="overlay2">Overlay Two</string> + <string name="overlay3">@string/internal</string> + <string name="overlay4">@string/overlay2</string> + <string name="internal">Internal</string> + <attr name="max_lines" format="integer" /> </resources> diff --git a/libs/androidfw/tests/data/overlay/res/xml/overlays.xml b/libs/androidfw/tests/data/overlay/res/xml/overlays.xml new file mode 100644 index 000000000000..9eca2faa76f4 --- /dev/null +++ b/libs/androidfw/tests/data/overlay/res/xml/overlays.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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 language governing permissions and + limitations under the License. +--> +<overlay> + <!-- Overlays string/overlayable5 with the string "Overlay One". --> + <item target="string/overlayable5" value="@string/overlay1"/> + + <!-- Overlays string/overlayable6 and string/overlayable7 with the string "Overlay Two". --> + <item target="string/overlayable7" value="@string/overlay2" /> + <item target="string/overlayable6" value="@string/overlay2" /> + + <!-- Overlays string/overlayable8 with a reference to @string/internal. --> + <item target="string/overlayable8" value="@string/overlay3" /> + + <!-- Overlays string/overlayable9 with a reference to @string/overlay2. The reference to + @string/overlay2 should be rewritten to @string/overlayable7 in the target. --> + <item target="string/overlayable9" value="@string/overlay4" /> + + <!-- Overlays string/overlayable10 with the string "yes". --> + <item target="string/overlayable10" value="@android:string/yes" /> + + <!-- Overlays string/overlayable11 with the string "Hardcoded string". --> + <item target="string/overlayable11" value="Hardcoded string" /> + + <!-- Overlays string/overlayable10 with the string "yes". --> + <item target="integer/config_int" value="42" /> + + <!-- @attr/max_lines and @id/hello_view should be rewritten to @attr/max_lines and + @id/hello_view in the target. --> + <item target="layout/hello_view" value="@layout/hello_view" /> + <item target="attr/max_lines" value="@attr/max_lines" /> + <item target="id/hello_view" value="@id/hello_view" /> +</overlay> + + diff --git a/libs/androidfw/tests/data/overlayable/R.h b/libs/androidfw/tests/data/overlayable/R.h index e46e264da318..35125a62ff4b 100644 --- a/libs/androidfw/tests/data/overlayable/R.h +++ b/libs/androidfw/tests/data/overlayable/R.h @@ -31,6 +31,43 @@ struct R { overlayable2 = 0x7f010002, overlayable3 = 0x7f010003, overlayable4 = 0x7f010004, + overlayable5 = 0x7f010005, + overlayable6 = 0x7f010006, + overlayable7 = 0x7f010007, + overlayable8 = 0x7f010008, + overlayable9 = 0x7f010009, + overlayable10 = 0x7f01000a, + overlayable11 = 0x7f01000b, + }; + }; + + struct attr { + enum : uint32_t { + max_lines = 0x7f020000, + }; + }; + + struct boolean { + enum : uint32_t { + config_bool = 0x7f030000, + }; + }; + + struct id { + enum : uint32_t { + hello_view = 0x7f040000, + }; + }; + + struct integer { + enum : uint32_t { + config_integer = 0x7f050000, + }; + }; + + struct layout { + enum : uint32_t { + hello_view = 0x7f060000, }; }; }; diff --git a/libs/androidfw/tests/data/overlayable/build b/libs/androidfw/tests/data/overlayable/build index 98fdc5101160..0aa97d639c30 100755 --- a/libs/androidfw/tests/data/overlayable/build +++ b/libs/androidfw/tests/data/overlayable/build @@ -17,6 +17,9 @@ set -e +FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk + aapt2 compile --dir res -o compiled.flata -aapt2 link --manifest AndroidManifest.xml -o overlayable.apk compiled.flata +aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o overlayable.apk compiled.flata \ + --no-auto-version rm compiled.flata diff --git a/libs/androidfw/tests/data/overlayable/overlayable.apk b/libs/androidfw/tests/data/overlayable/overlayable.apk Binary files differindex 047e6afde86b..9dc9c15f68a9 100644 --- a/libs/androidfw/tests/data/overlayable/overlayable.apk +++ b/libs/androidfw/tests/data/overlayable/overlayable.apk diff --git a/libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml b/libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml new file mode 100644 index 000000000000..268118a91bff --- /dev/null +++ b/libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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. +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/hello_view" + android:text="None" + app:max_lines="0"/>
\ No newline at end of file diff --git a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml index fcdbe94466c1..b3e8f7d8e84b 100644 --- a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml +++ b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml @@ -15,27 +15,41 @@ --> <resources> -<overlayable name="OverlayableResources1" actor="overlay://theme"> - <!-- Any overlay can overlay the value of @string/overlayable1 --> - <item type="string" name="overlayable1" /> + <overlayable name="OverlayableResources1" actor="overlay://theme"> + <!-- Any overlay on the product or system partition can overlay the value of + @string/overlayable2 --> + <policy type="product|system"> + <item type="string" name="overlayable2" /> + </policy> - <!-- Any overlay on the product or system partition can overlay the value of - @string/overlayable2 --> - <policy type="product|system"> - <item type="string" name="overlayable2" /> - </policy> + <!-- Any overlay can overlay the value of @string/overlayable4 --> + <policy type="public"> + <item type="string" name="overlayable1" /> + <item type="string" name="overlayable4" /> + </policy> + </overlayable> - <!-- Any overlay can overlay the value of @string/overlayable4 --> - <policy type="public"> - <item type="string" name="overlayable4" /> - </policy> -</overlayable> + <overlayable name="OverlayableResources2" actor="overlay://com.android.overlayable"> + <!-- Any overlay on the vendor or product partition can overlay the value of + @string/overlayable3 --> + <policy type="vendor|product"> + <item type="string" name="overlayable3" /> + </policy> + </overlayable> -<overlayable name="OverlayableResources2" actor="overlay://com.android.overlayable"> - <!-- Any overlay on the vendor or product partition can overlay the value of - @string/overlayable3 --> - <policy type="vendor|product"> - <item type="string" name="overlayable3" /> - </policy> -</overlayable> + <overlayable name="OverlayableResources3"> + <policy type="public"> + <item type="string" name="overlayable5" /> + <item type="string" name="overlayable6" /> + <item type="string" name="overlayable7" /> + <item type="string" name="overlayable8" /> + <item type="string" name="overlayable9" /> + <item type="string" name="overlayable10" /> + <item type="string" name="overlayable11" /> + <item type="integer" name="config_int" /> + <item type="id" name="hello_view" /> + <item type="attr" name="max_lines" /> + <item type="layout" name="hello_view" /> + </policy> + </overlayable> </resources>
\ No newline at end of file diff --git a/libs/androidfw/tests/data/overlayable/res/values/public.xml b/libs/androidfw/tests/data/overlayable/res/values/public.xml index 5676d7cc64c9..042a311b2b88 100644 --- a/libs/androidfw/tests/data/overlayable/res/values/public.xml +++ b/libs/androidfw/tests/data/overlayable/res/values/public.xml @@ -20,4 +20,21 @@ <public type="string" name="overlayable2" id="0x7f010002" /> <public type="string" name="overlayable3" id="0x7f010003" /> <public type="string" name="overlayable4" id="0x7f010004" /> + <public type="string" name="overlayable5" id="0x7f010005" /> + <public type="string" name="overlayable6" id="0x7f010006" /> + <public type="string" name="overlayable7" id="0x7f010007" /> + <public type="string" name="overlayable8" id="0x7f010008" /> + <public type="string" name="overlayable9" id="0x7f010009" /> + <public type="string" name="overlayable10" id="0x7f01000a" /> + <public type="string" name="overlayable11" id="0x7f01000b" /> + + <public type="attr" name="max_lines" id="0x7f020000" /> + + <public type="bool" name="config_bool" id="0x7f030000" /> + + <public type="id" name="hello_view" id="0x7f040000" /> + + <public type="integer" name="config_int" id="0x7f050000" /> + + <public type="layout" name="hello_view" id="0x7f060000" /> </resources>
\ No newline at end of file diff --git a/libs/androidfw/tests/data/overlayable/res/values/values.xml b/libs/androidfw/tests/data/overlayable/res/values/values.xml index a86b31282bc9..235772d35fd0 100644 --- a/libs/androidfw/tests/data/overlayable/res/values/values.xml +++ b/libs/androidfw/tests/data/overlayable/res/values/values.xml @@ -20,4 +20,15 @@ <string name="overlayable2">Overlayable Two</string> <string name="overlayable3">Overlayable Three</string> <string name="overlayable4">Overlayable Four</string> + <string name="overlayable5">Overlayable Five</string> + <string name="overlayable6">Overlayable Six</string> + <string name="overlayable7">Overlayable Seven</string> + <string name="overlayable8">Overlayable Eight</string> + <string name="overlayable9">Overlayable Nine</string> + <string name="overlayable10">Overlayable Ten</string> + <string name="overlayable11">Overlayable Eleven</string> + + <integer name="config_int">0</integer> + <bool name="config_bool">false</bool> + <attr name="max_lines" format="integer" /> </resources> diff --git a/libs/androidfw/tests/data/system/R.h b/libs/androidfw/tests/data/system/R.h index becb38830fb3..c0160c0f78a9 100644 --- a/libs/androidfw/tests/data/system/R.h +++ b/libs/androidfw/tests/data/system/R.h @@ -40,6 +40,13 @@ struct R { number = 0x01030000, // sv }; }; + + struct string { + enum : uint32_t { + no = 0x01040009, + yes = 0x01040013, + }; + }; }; } // namespace android diff --git a/libs/androidfw/tests/data/system/res/values/public.xml b/libs/androidfw/tests/data/system/res/values/public.xml new file mode 100644 index 000000000000..077874d0b0fe --- /dev/null +++ b/libs/androidfw/tests/data/system/res/values/public.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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. +--> + +<resources> + <public type="string" name="no" id="0x01040009" /> + <public type="string" name="yes" id="0x01040013" /> +</resources>
\ No newline at end of file diff --git a/libs/androidfw/tests/data/system/res/values/values.xml b/libs/androidfw/tests/data/system/res/values/values.xml new file mode 100644 index 000000000000..0629c1d13795 --- /dev/null +++ b/libs/androidfw/tests/data/system/res/values/values.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 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. +--> + +<resources> + <string name="yes">yes</string> + <string name="no">no</string> +</resources>
\ No newline at end of file diff --git a/libs/androidfw/tests/data/system/system.apk b/libs/androidfw/tests/data/system/system.apk Binary files differindex 9045d6c4de21..1f7e00733366 100644 --- a/libs/androidfw/tests/data/system/system.apk +++ b/libs/androidfw/tests/data/system/system.apk diff --git a/libs/hostgraphics/Android.bp b/libs/hostgraphics/Android.bp new file mode 100644 index 000000000000..e713b98b867e --- /dev/null +++ b/libs/hostgraphics/Android.bp @@ -0,0 +1,31 @@ +cc_library_host_static { + name: "libhostgraphics", + + cflags: [ + "-Wno-unused-parameter", + ], + + srcs: [ + ":libui_host_common", + "Fence.cpp", + "HostBufferQueue.cpp", + "PublicFormat.cpp", + ], + + include_dirs: [ + // Here we override all the headers automatically included with frameworks/native/include. + // When frameworks/native/include will be removed from the list of automatic includes. + // We will have to copy necessary headers with a pre-build step (generated headers). + ".", + "frameworks/native/libs/nativebase/include", + "frameworks/native/libs/nativewindow/include", + "frameworks/native/libs/arect/include", + ], + export_include_dirs: ["."], + + target: { + windows: { + enabled: true, + } + }, +}
\ No newline at end of file diff --git a/libs/hostgraphics/Fence.cpp b/libs/hostgraphics/Fence.cpp new file mode 100644 index 000000000000..9e54816651c4 --- /dev/null +++ b/libs/hostgraphics/Fence.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2019 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. + */ + +#include <ui/Fence.h> + +namespace android { + +const sp<Fence> Fence::NO_FENCE = sp<Fence>(new Fence); + +} // namespace android
\ No newline at end of file diff --git a/libs/hostgraphics/HostBufferQueue.cpp b/libs/hostgraphics/HostBufferQueue.cpp new file mode 100644 index 000000000000..ec304378c6c4 --- /dev/null +++ b/libs/hostgraphics/HostBufferQueue.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2019 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. + */ + +#include <gui/BufferQueue.h> + +namespace android { + +class HostBufferQueue : public IGraphicBufferProducer, public IGraphicBufferConsumer { +public: + HostBufferQueue() : mWidth(0), mHeight(0) { } + + virtual status_t setConsumerIsProtected(bool isProtected) { return OK; } + + virtual status_t detachBuffer(int slot) { return OK; } + + virtual status_t getReleasedBuffers(uint64_t* slotMask) { return OK; } + + virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) { + mWidth = w; + mHeight = h; + mBuffer = sp<GraphicBuffer>(new GraphicBuffer(mWidth, mHeight)); + return OK; + } + + virtual status_t setDefaultBufferFormat(PixelFormat defaultFormat) { return OK; } + + virtual status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) { return OK; } + + virtual status_t discardFreeBuffers() { return OK; } + + virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen, + uint64_t maxFrameNumber = 0) { + buffer->mGraphicBuffer = mBuffer; + buffer->mSlot = 0; + return OK; + } + + virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) { return OK; } + + virtual status_t setConsumerUsageBits(uint64_t usage) { return OK; } +private: + sp<GraphicBuffer> mBuffer; + uint32_t mWidth; + uint32_t mHeight; +}; + +void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, + sp<IGraphicBufferConsumer>* outConsumer) { + + sp<HostBufferQueue> obj(new HostBufferQueue()); + + *outProducer = obj; + *outConsumer = obj; +} + +} // namespace android diff --git a/libs/hostgraphics/PublicFormat.cpp b/libs/hostgraphics/PublicFormat.cpp new file mode 100644 index 000000000000..af6d2738c801 --- /dev/null +++ b/libs/hostgraphics/PublicFormat.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2019 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. + */ + +#include <ui/PublicFormat.h> + +namespace android { + +android_dataspace mapPublicFormatToHalDataspace(PublicFormat f) { + return static_cast<android_dataspace>(0); +} + +int mapPublicFormatToHalFormat(PublicFormat f) { + return static_cast<int>(f); +} + +PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace dataSpace) { + return static_cast<PublicFormat>(format); +} + +} // namespace android
\ No newline at end of file diff --git a/libs/hostgraphics/gui/BufferItem.h b/libs/hostgraphics/gui/BufferItem.h new file mode 100644 index 000000000000..01409e19c715 --- /dev/null +++ b/libs/hostgraphics/gui/BufferItem.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2019 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. + */ + +#ifndef ANDROID_GUI_BUFFERITEM_H +#define ANDROID_GUI_BUFFERITEM_H + +#include <ui/Fence.h> +#include <ui/Rect.h> + +#include <system/graphics.h> + +#include <utils/StrongPointer.h> + +namespace android { + +class Fence; +class GraphicBuffer; + +// The only thing we need here for layoutlib is mGraphicBuffer. The rest of the fields are added +// just to satisfy the calls from the android_media_ImageReader.h + +class BufferItem { +public: + enum { INVALID_BUFFER_SLOT = -1 }; + + BufferItem() : mGraphicBuffer(nullptr), mFence(Fence::NO_FENCE) {} + ~BufferItem() {} + + sp<GraphicBuffer> mGraphicBuffer; + + sp<Fence> mFence; + + Rect mCrop; + + uint32_t mTransform; + + uint32_t mScalingMode; + + int64_t mTimestamp; + + android_dataspace mDataSpace; + + uint64_t mFrameNumber; + + int mSlot; + + bool mTransformToDisplayInverse; +}; + +} + +#endif // ANDROID_GUI_BUFFERITEM_H diff --git a/libs/hostgraphics/gui/BufferItemConsumer.h b/libs/hostgraphics/gui/BufferItemConsumer.h new file mode 100644 index 000000000000..707b313eb102 --- /dev/null +++ b/libs/hostgraphics/gui/BufferItemConsumer.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 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. + */ + +#ifndef ANDROID_GUI_BUFFERITEMCONSUMER_H +#define ANDROID_GUI_BUFFERITEMCONSUMER_H + +#include <utils/RefBase.h> + +#include <gui/ConsumerBase.h> +#include <gui/IGraphicBufferConsumer.h> + +namespace android { + +class BufferItemConsumer : public ConsumerBase { +public: + BufferItemConsumer( + const sp<IGraphicBufferConsumer>& consumer, + uint64_t consumerUsage, + int bufferCount, + bool controlledByApp) : mConsumer(consumer) { + } + + status_t acquireBuffer(BufferItem *item, nsecs_t presentWhen, bool waitForFence = true) { + return mConsumer->acquireBuffer(item, presentWhen, 0); + } + + status_t releaseBuffer( + const BufferItem &item, const sp<Fence>& releaseFence = Fence::NO_FENCE) { return OK; } + + void setName(const String8& name) { } + + void setFrameAvailableListener(const wp<FrameAvailableListener>& listener) { } + + status_t setDefaultBufferSize(uint32_t width, uint32_t height) { + return mConsumer->setDefaultBufferSize(width, height); + } + + status_t setDefaultBufferFormat(PixelFormat defaultFormat) { + return mConsumer->setDefaultBufferFormat(defaultFormat); + } + + status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) { + return mConsumer->setDefaultBufferDataSpace(defaultDataSpace); + } + + void abandon() { } + + status_t detachBuffer(int slot) { return OK; } + + status_t discardFreeBuffers() { return OK; } + + void freeBufferLocked(int slotIndex) { } + + status_t addReleaseFenceLocked( + int slot, const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence) { return OK; } +private: + sp<IGraphicBufferConsumer> mConsumer; +}; + +} // namespace android + +#endif // ANDROID_GUI_BUFFERITEMCONSUMER_H diff --git a/libs/hwui/debug/FatalBaseDriver.h b/libs/hostgraphics/gui/BufferQueue.h index 45353d0568aa..aa3e7268e11c 100644 --- a/libs/hwui/debug/FatalBaseDriver.h +++ b/libs/hostgraphics/gui/BufferQueue.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2019 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. @@ -14,24 +14,24 @@ * limitations under the License. */ -#pragma once +#ifndef ANDROID_GUI_BUFFERQUEUE_H +#define ANDROID_GUI_BUFFERQUEUE_H -#include "GlesDriver.h" +#include <gui/BufferItem.h> +#include <gui/IGraphicBufferConsumer.h> +#include <gui/IGraphicBufferProducer.h> namespace android { -namespace uirenderer { -namespace debug { -// A base driver that implements all the pure virtuals in the form of -// LOG_ALWAYS_FATALS. Suitable for selective-override implementations -// where only a known subset of methods need to be overridden -class FatalBaseDriver : public GlesDriver { +class BufferQueue { public: -#define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) override; -#include "gles_decls.in" -#undef GL_ENTRY + enum { INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT }; + enum { NO_BUFFER_AVAILABLE = IGraphicBufferConsumer::NO_BUFFER_AVAILABLE }; + + static void createBufferQueue(sp<IGraphicBufferProducer>* outProducer, + sp<IGraphicBufferConsumer>* outConsumer); }; -} // namespace debug -} // namespace uirenderer -} // namespace android +} // namespace android + +#endif // ANDROID_GUI_BUFFERQUEUE_H diff --git a/libs/hwui/debug/MockGlesDriver.h b/libs/hostgraphics/gui/ConsumerBase.h index e48ca193d48f..9002953c0848 100644 --- a/libs/hwui/debug/MockGlesDriver.h +++ b/libs/hostgraphics/gui/ConsumerBase.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2019 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. @@ -14,24 +14,24 @@ * limitations under the License. */ -#pragma once +#ifndef ANDROID_GUI_CONSUMERBASE_H +#define ANDROID_GUI_CONSUMERBASE_H -#include "FatalBaseDriver.h" +#include <gui/BufferItem.h> -#include <gmock/gmock.h> +#include <utils/RefBase.h> namespace android { -namespace uirenderer { -namespace debug { -class MockGlesDriver : public FatalBaseDriver { +class ConsumerBase : public virtual RefBase { public: - MOCK_METHOD2(glBindBuffer_, void(GLenum target, GLuint buffer)); - MOCK_METHOD4(glBufferData_, - void(GLenum target, GLsizeiptr size, const void* data, GLenum usage)); - MOCK_METHOD2(glGenBuffers_, void(GLsizei n, GLuint* buffers)); + struct FrameAvailableListener : public virtual RefBase { + // See IConsumerListener::onFrame{Available,Replaced} + virtual void onFrameAvailable(const BufferItem& item) = 0; + virtual void onFrameReplaced(const BufferItem& /* item */) {} + }; }; -} // namespace debug -} // namespace uirenderer -} // namespace android +} // namespace android + +#endif // ANDROID_GUI_CONSUMERBASE_H
\ No newline at end of file diff --git a/libs/hostgraphics/gui/IGraphicBufferConsumer.h b/libs/hostgraphics/gui/IGraphicBufferConsumer.h new file mode 100644 index 000000000000..9eb67b218800 --- /dev/null +++ b/libs/hostgraphics/gui/IGraphicBufferConsumer.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include <utils/RefBase.h> + +#include <ui/PixelFormat.h> + +#include <utils/Errors.h> + +namespace android { + +class BufferItem; +class Fence; +class GraphicBuffer; + +class IGraphicBufferConsumer : virtual public RefBase { +public: + enum { + // Returned by releaseBuffer, after which the consumer must free any references to the + // just-released buffer that it might have. + STALE_BUFFER_SLOT = 1, + // Returned by dequeueBuffer if there are no pending buffers available. + NO_BUFFER_AVAILABLE, + // Returned by dequeueBuffer if it's too early for the buffer to be acquired. + PRESENT_LATER, + }; + + virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen, + uint64_t maxFrameNumber = 0) = 0; + + virtual status_t detachBuffer(int slot) = 0; + + virtual status_t getReleasedBuffers(uint64_t* slotMask) = 0; + + virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0; + + virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0; + + virtual status_t setDefaultBufferFormat(PixelFormat defaultFormat) = 0; + + virtual status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) = 0; + + virtual status_t setConsumerUsageBits(uint64_t usage) = 0; + + virtual status_t setConsumerIsProtected(bool isProtected) = 0; + + virtual status_t discardFreeBuffers() = 0; +}; + +} // namespace android
\ No newline at end of file diff --git a/libs/hwui/debug/GlesErrorCheckWrapper.h b/libs/hostgraphics/gui/IGraphicBufferProducer.h index 791400bf30ec..a1efd0bcfa4c 100644 --- a/libs/hwui/debug/GlesErrorCheckWrapper.h +++ b/libs/hostgraphics/gui/IGraphicBufferProducer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2019 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. @@ -14,28 +14,25 @@ * limitations under the License. */ -#pragma once +#ifndef ANDROID_GUI_IGRAPHICBUFFERPRODUCER_H +#define ANDROID_GUI_IGRAPHICBUFFERPRODUCER_H -#include "GlesDriver.h" +#include <utils/RefBase.h> + +#include <ui/GraphicBuffer.h> namespace android { -namespace uirenderer { -namespace debug { -class GlesErrorCheckWrapper : public GlesDriver { +class IGraphicBufferProducer : virtual public RefBase { public: - explicit GlesErrorCheckWrapper(GlesDriver& base) : mBase(base) {} - -#define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) override; -#include "gles_decls.in" -#undef GL_ENTRY - -private: - void assertNoErrors(const char* apicall); - - GlesDriver& mBase; + enum class DisconnectMode { + // Disconnect only the specified API. + Api, + // Disconnect any API originally connected from the process calling disconnect. + AllLocal + }; }; -} // namespace debug -} // namespace uirenderer -} // namespace android +} // namespace android + +#endif // ANDROID_GUI_IGRAPHICBUFFERPRODUCER_H diff --git a/libs/hostgraphics/gui/Surface.h b/libs/hostgraphics/gui/Surface.h new file mode 100644 index 000000000000..de1ba00211d3 --- /dev/null +++ b/libs/hostgraphics/gui/Surface.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 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. + */ + +#ifndef ANDROID_GUI_SURFACE_H +#define ANDROID_GUI_SURFACE_H + +#include <gui/IGraphicBufferProducer.h> +#include <ui/ANativeObjectBase.h> +#include <utils/RefBase.h> +#include <system/window.h> + +namespace android { + +class Surface : public ANativeObjectBase<ANativeWindow, Surface, RefBase> { +public: + explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer, + bool controlledByApp = false) { + ANativeWindow::perform = hook_perform; + } + static bool isValid(const sp<Surface>& surface) { return surface != nullptr; } + void allocateBuffers() {} + + uint64_t getNextFrameNumber() const { return 0; } + + int setScalingMode(int mode) { return 0; } + + virtual int disconnect(int api, + IGraphicBufferProducer::DisconnectMode mode = + IGraphicBufferProducer::DisconnectMode::Api) { + return 0; + } + + virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds) { + // TODO: implement this + return 0; + } + virtual int unlockAndPost() { return 0; } + virtual int query(int what, int* value) const { return 0; } + +protected: + virtual ~Surface() {} + + static int hook_perform(ANativeWindow* window, int operation, ...) { return 0; } + +private: + // can't be copied + Surface& operator=(const Surface& rhs); + Surface(const Surface& rhs); +}; + +} // namespace android + +#endif // ANDROID_GUI_SURFACE_H diff --git a/libs/hostgraphics/ui/Fence.h b/libs/hostgraphics/ui/Fence.h new file mode 100644 index 000000000000..04d535c3a211 --- /dev/null +++ b/libs/hostgraphics/ui/Fence.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 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. + */ + +#ifndef ANDROID_FENCE_H +#define ANDROID_FENCE_H + +#include <utils/String8.h> +#include <utils/RefBase.h> + +typedef int64_t nsecs_t; + +namespace android { + +class Fence : public LightRefBase<Fence> { +public: + Fence() { } + Fence(int) { } + static const sp<Fence> NO_FENCE; + static constexpr nsecs_t SIGNAL_TIME_PENDING = INT64_MAX; + static constexpr nsecs_t SIGNAL_TIME_INVALID = -1; + static sp<Fence> merge(const char* name, const sp<Fence>& f1, const sp<Fence>& f2) { + return NO_FENCE; + } + + static sp<Fence> merge(const String8& name, const sp<Fence>& f1, const sp<Fence>& f2) { + return NO_FENCE; + } + + enum class Status { + Invalid, // Fence is invalid + Unsignaled, // Fence is valid but has not yet signaled + Signaled, // Fence is valid and has signaled + }; + + status_t wait(int timeout) { return OK; } + + status_t waitForever(const char* logname) { return OK; } + + int dup() const { return 0; } + + inline Status getStatus() { + // The sync_wait call underlying wait() has been measured to be + // significantly faster than the sync_fence_info call underlying + // getSignalTime(), which might otherwise appear to be the more obvious + // way to check whether a fence has signaled. + switch (wait(0)) { + case NO_ERROR: + return Status::Signaled; + case -ETIME: + return Status::Unsignaled; + default: + return Status::Invalid; + } + } +}; + +} // namespace android + +#endif // ANDROID_FENCE_H diff --git a/libs/hostgraphics/ui/GraphicBuffer.h b/libs/hostgraphics/ui/GraphicBuffer.h new file mode 100644 index 000000000000..ac88e44dbc65 --- /dev/null +++ b/libs/hostgraphics/ui/GraphicBuffer.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2019 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. + */ + +#ifndef ANDROID_GRAPHIC_BUFFER_H +#define ANDROID_GRAPHIC_BUFFER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <vector> + +#include <ui/PixelFormat.h> +#include <ui/Rect.h> + +#include <utils/RefBase.h> + +namespace android { + +class GraphicBuffer : virtual public RefBase { +public: + GraphicBuffer(uint32_t w, uint32_t h):width(w),height(h) { + data.resize(w*h); + } + uint32_t getWidth() const { return static_cast<uint32_t>(width); } + uint32_t getHeight() const { return static_cast<uint32_t>(height); } + uint32_t getStride() const { return static_cast<uint32_t>(width); } + uint64_t getUsage() const { return 0; } + PixelFormat getPixelFormat() const { return PIXEL_FORMAT_RGBA_8888; } + //uint32_t getLayerCount() const { return static_cast<uint32_t>(layerCount); } + Rect getBounds() const { return Rect(width, height); } + + status_t lockAsyncYCbCr(uint32_t inUsage, const Rect& rect, + android_ycbcr *ycbcr, int fenceFd) { return OK; } + + status_t lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr, int fenceFd, + int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr) { + *vaddr = data.data(); + return OK; + } + + status_t unlockAsync(int *fenceFd) { return OK; } + +private: + uint32_t width; + uint32_t height; + std::vector<uint32_t> data; +}; + +}; // namespace android + +#endif // ANDROID_GRAPHIC_BUFFER_H diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 98de9c3ea88e..aa842ff6a7b7 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -30,11 +30,6 @@ cc_defaults { include_dirs: [ "external/skia/include/private", "external/skia/src/core", - "external/skia/src/effects", - "external/skia/src/image", - "external/skia/src/utils", - "external/skia/src/gpu", - "external/skia/src/shaders", ], product_variables: { @@ -44,33 +39,73 @@ cc_defaults { }, }, }, + + target: { + android: { + include_dirs: [ + "external/skia/src/effects", + "external/skia/src/image", + "external/skia/src/utils", + "external/skia/src/gpu", + "external/skia/src/shaders", + ], + }, + host: { + include_dirs: [ + "external/vulkan-headers/include", + "frameworks/native/libs/math/include", + "frameworks/native/libs/ui/include", + ], + cflags: [ + "-Wno-unused-variable", + ], + } + } } cc_defaults { name: "hwui_static_deps", shared_libs: [ - "liblog", - "libcutils", - "libstatslog", - "libutils", - "libEGL", - "libGLESv1_CM", - "libGLESv2", - "libGLESv3", - "libvulkan", - "libui", - "libgui", - "libprotobuf-cpp-lite", + "libbase", "libharfbuzz_ng", - "libft2", "libminikin", - "libandroidfw", - "libcrypto", - "libsync", - ], - static_libs: [ - "libEGL_blobCache", ], + + target: { + android: { + shared_libs: [ + "liblog", + "libcutils", + "libstatslog", + "libutils", + "libEGL", + "libGLESv1_CM", + "libGLESv2", + "libGLESv3", + "libvulkan", + "libui", + "libnativedisplay", + "libnativewindow", + "libprotobuf-cpp-lite", + "libft2", + "libandroidfw", + "libcrypto", + "libsync", + "libstatspull", + "libstatssocket", + ], + static_libs: [ + "libEGL_blobCache", + "libprotoutil", + ], + }, + host: { + static_libs: [ + "libandroidfw", + "libutils", + ], + } + } } cc_defaults { @@ -88,27 +123,6 @@ cc_defaults { ], } -cc_defaults { - name: "hwui_debug", - cflags: ["-include debug/wrap_gles.h"], - srcs: [ - "debug/wrap_gles.cpp", - "debug/DefaultGlesDriver.cpp", - "debug/GlesErrorCheckWrapper.cpp", - "debug/GlesDriver.cpp", - "debug/FatalBaseDriver.cpp", - "debug/NullGlesDriver.cpp", - ], - include_dirs: ["frameworks/native/opengl/libs/GLES2"], -} - -cc_defaults { - name: "hwui_enable_opengl_validation", - defaults: ["hwui_debug"], - cflags: ["-DDEBUG_OPENGL=3"], - include_dirs: ["frameworks/native/opengl/libs/GLES2"], -} - // Build libhwui with PGO by default. // Location of PGO profile data is defined in build/soong/cc/pgo.go // and is separate from hwui. @@ -138,9 +152,233 @@ cc_defaults { } // ------------------------ +// APEX +// ------------------------ + +cc_library_headers { + name: "android_graphics_apex_headers", + + host_supported: true, + export_include_dirs: [ + "apex/include", + ], + target: { + windows: { + enabled: true, + }, + } +} + +cc_defaults { + name: "android_graphics_apex", + cflags: [ + "-Wno-unused-parameter", + "-Wno-non-virtual-dtor", + "-Wno-maybe-uninitialized", + "-Wno-parentheses", + "-Wall", + "-Werror", + "-Wno-error=deprecated-declarations", + "-Wunused", + "-Wunreachable-code", + ], + + cppflags: ["-Wno-conversion-null"], + + srcs: [ + "apex/android_matrix.cpp", + "apex/android_paint.cpp", + "apex/android_region.cpp", + ], + + header_libs: [ "android_graphics_apex_headers" ], + + target: { + android: { + srcs: [ // sources that depend on android only libraries + "apex/android_bitmap.cpp", + "apex/android_canvas.cpp", + "apex/jni_runtime.cpp", + "apex/renderthread.cpp", + ], + }, + host: { + srcs: [ + "apex/LayoutlibLoader.cpp", + ], + } + }, +} + +// ------------------------ +// Android Graphics JNI +// ------------------------ + +cc_library_headers { + name: "android_graphics_jni_headers", + + host_supported: true, + export_include_dirs: [ + "jni", + ], + target: { + windows: { + enabled: true, + }, + } +} + +cc_defaults { + name: "android_graphics_jni", + cflags: [ + "-Wno-unused-parameter", + "-Wno-non-virtual-dtor", + "-Wno-maybe-uninitialized", + "-Wno-parentheses", + + "-DGL_GLEXT_PROTOTYPES", + "-DEGL_EGLEXT_PROTOTYPES", + + "-DU_USING_ICU_NAMESPACE=0", + + "-Wall", + "-Werror", + "-Wno-error=deprecated-declarations", + "-Wunused", + "-Wunreachable-code", + ], + + cppflags: ["-Wno-conversion-null"], + + srcs: [ + "jni/android_graphics_animation_NativeInterpolatorFactory.cpp", + "jni/android_graphics_animation_RenderNodeAnimator.cpp", + "jni/android_graphics_Canvas.cpp", + "jni/android_graphics_ColorSpace.cpp", + "jni/android_graphics_drawable_AnimatedVectorDrawable.cpp", + "jni/android_graphics_drawable_VectorDrawable.cpp", + "jni/android_graphics_HardwareRendererObserver.cpp", + "jni/android_graphics_Matrix.cpp", + "jni/android_graphics_Picture.cpp", + "jni/android_graphics_DisplayListCanvas.cpp", + "jni/android_graphics_RenderNode.cpp", + "jni/android_nio_utils.cpp", + "jni/android_util_PathParser.cpp", + + "jni/Bitmap.cpp", + "jni/BitmapFactory.cpp", + "jni/ByteBufferStreamAdaptor.cpp", + "jni/Camera.cpp", + "jni/CanvasProperty.cpp", + "jni/ColorFilter.cpp", + "jni/CreateJavaOutputStreamAdaptor.cpp", + "jni/FontFamily.cpp", + "jni/FontUtils.cpp", + "jni/Graphics.cpp", + "jni/ImageDecoder.cpp", + "jni/Interpolator.cpp", + "jni/MaskFilter.cpp", + "jni/NinePatch.cpp", + "jni/NinePatchPeeker.cpp", + "jni/Paint.cpp", + "jni/PaintFilter.cpp", + "jni/Path.cpp", + "jni/PathEffect.cpp", + "jni/PathMeasure.cpp", + "jni/Picture.cpp", + "jni/Shader.cpp", + "jni/Typeface.cpp", + "jni/Utils.cpp", + "jni/YuvToJpegEncoder.cpp", + "jni/fonts/Font.cpp", + "jni/fonts/FontFamily.cpp", + "jni/text/LineBreaker.cpp", + "jni/text/MeasuredText.cpp", + ], + + header_libs: [ "android_graphics_jni_headers" ], + + include_dirs: [ + "external/skia/include/private", + "external/skia/src/codec", + "external/skia/src/core", + "external/skia/src/effects", + "external/skia/src/image", + "external/skia/src/images", + ], + + shared_libs: [ + "libbase", + "libcutils", + "libharfbuzz_ng", + "liblog", + "libminikin", + "libnativehelper", + "libz", + "libziparchive", + "libjpeg", + ], + + target: { + android: { + srcs: [ // sources that depend on android only libraries + "jni/AnimatedImageDrawable.cpp", + "jni/android_graphics_TextureLayer.cpp", + "jni/android_graphics_HardwareRenderer.cpp", + "jni/BitmapRegionDecoder.cpp", + "jni/GIFMovie.cpp", + "jni/GraphicsStatsService.cpp", + "jni/Movie.cpp", + "jni/MovieImpl.cpp", + "jni/Region.cpp", // requires libbinder_ndk + "jni/pdf/PdfDocument.cpp", + "jni/pdf/PdfEditor.cpp", + "jni/pdf/PdfRenderer.cpp", + "jni/pdf/PdfUtils.cpp", + ], + shared_libs: [ + "libandroidfw", + "libbinder", + "libbinder_ndk", + "libmediandk", + "libnativedisplay", + "libnativewindow", + "libstatspull", + "libstatssocket", + "libpdfium", + ], + static_libs: [ + "libgif", + "libstatslog", + ], + }, + host: { + cflags: [ + "-Wno-unused-const-variable", + "-Wno-unused-function", + ], + static_libs: [ + "libandroidfw", + ], + } + }, +} + +// ------------------------ // library // ------------------------ +cc_library_headers { + name: "libhwui_internal_headers", + + host_supported: true, + export_include_dirs: [ + ".", + ], + header_libs: [ "android_graphics_jni_headers" ], + export_header_lib_headers: [ "android_graphics_jni_headers" ], +} + cc_defaults { name: "libhwui_defaults", defaults: ["hwui_defaults"], @@ -148,119 +386,125 @@ cc_defaults { whole_static_libs: ["libskia"], srcs: [ + "pipeline/skia/SkiaDisplayList.cpp", + "pipeline/skia/SkiaRecordingCanvas.cpp", + "pipeline/skia/RenderNodeDrawable.cpp", + "pipeline/skia/ReorderBarrierDrawables.cpp", + "renderthread/Frame.cpp", + "renderthread/RenderTask.cpp", + "renderthread/TimeLord.cpp", "hwui/AnimatedImageDrawable.cpp", - "hwui/AnimatedImageThread.cpp", "hwui/Bitmap.cpp", "hwui/Canvas.cpp", + "hwui/ImageDecoder.cpp", "hwui/MinikinSkia.cpp", "hwui/MinikinUtils.cpp", "hwui/PaintImpl.cpp", "hwui/Typeface.cpp", - "pipeline/skia/GLFunctorDrawable.cpp", - "pipeline/skia/LayerDrawable.cpp", - "pipeline/skia/RenderNodeDrawable.cpp", - "pipeline/skia/ReorderBarrierDrawables.cpp", - "pipeline/skia/ShaderCache.cpp", - "pipeline/skia/SkiaDisplayList.cpp", - "pipeline/skia/SkiaMemoryTracer.cpp", - "pipeline/skia/SkiaOpenGLPipeline.cpp", - "pipeline/skia/SkiaPipeline.cpp", - "pipeline/skia/SkiaProfileRenderer.cpp", - "pipeline/skia/SkiaRecordingCanvas.cpp", - "pipeline/skia/SkiaVulkanPipeline.cpp", - "pipeline/skia/VectorDrawableAtlas.cpp", - "pipeline/skia/VkFunctorDrawable.cpp", - "pipeline/skia/VkInteropFunctorDrawable.cpp", - "renderstate/RenderState.cpp", - "renderthread/CacheManager.cpp", - "renderthread/CanvasContext.cpp", - "renderthread/DrawFrameTask.cpp", - "renderthread/EglManager.cpp", - "renderthread/ReliableSurface.cpp", - "renderthread/VulkanManager.cpp", - "renderthread/VulkanSurface.cpp", - "renderthread/RenderProxy.cpp", - "renderthread/RenderTask.cpp", - "renderthread/RenderThread.cpp", - "renderthread/TimeLord.cpp", - "renderthread/Frame.cpp", - "service/GraphicsStatsService.cpp", - "surfacetexture/EGLConsumer.cpp", - "surfacetexture/ImageConsumer.cpp", - "surfacetexture/SurfaceTexture.cpp", - "thread/CommonPool.cpp", "utils/Blur.cpp", "utils/Color.cpp", - "utils/GLUtils.cpp", "utils/LinearAllocator.cpp", - "utils/StringUtils.cpp", "utils/VectorDrawableUtils.cpp", "AnimationContext.cpp", "Animator.cpp", "AnimatorManager.cpp", "CanvasTransform.cpp", "DamageAccumulator.cpp", - "DeferredLayerUpdater.cpp", - "DeviceInfo.cpp", - "FrameInfo.cpp", - "FrameInfoVisualizer.cpp", - "GpuMemoryTracker.cpp", - "HardwareBitmapUploader.cpp", - "HWUIProperties.sysprop", "Interpolator.cpp", - "JankTracker.cpp", - "Layer.cpp", - "LayerUpdateQueue.cpp", + "LightingInfo.cpp", "Matrix.cpp", "PathParser.cpp", - "ProfileData.cpp", - "ProfileDataContainer.cpp", "Properties.cpp", "PropertyValuesAnimatorSet.cpp", "PropertyValuesHolder.cpp", - "Readback.cpp", "RecordingCanvas.cpp", "RenderNode.cpp", "RenderProperties.cpp", + "RootRenderNode.cpp", "SkiaCanvas.cpp", - "TreeInfo.cpp", - "WebViewFunctorManager.cpp", "VectorDrawable.cpp", - "protos/graphicsstats.proto", ], proto: { export_proto_headers: true, }, - export_include_dirs: ["."], + target: { + android: { + srcs: [ + "hwui/AnimatedImageThread.cpp", + "pipeline/skia/ATraceMemoryDump.cpp", + "pipeline/skia/GLFunctorDrawable.cpp", + "pipeline/skia/LayerDrawable.cpp", + "pipeline/skia/ShaderCache.cpp", + "pipeline/skia/SkiaMemoryTracer.cpp", + "pipeline/skia/SkiaOpenGLPipeline.cpp", + "pipeline/skia/SkiaPipeline.cpp", + "pipeline/skia/SkiaProfileRenderer.cpp", + "pipeline/skia/SkiaVulkanPipeline.cpp", + "pipeline/skia/VkFunctorDrawable.cpp", + "pipeline/skia/VkInteropFunctorDrawable.cpp", + "renderstate/RenderState.cpp", + "renderthread/CacheManager.cpp", + "renderthread/CanvasContext.cpp", + "renderthread/DrawFrameTask.cpp", + "renderthread/EglManager.cpp", + "renderthread/ReliableSurface.cpp", + "renderthread/VulkanManager.cpp", + "renderthread/VulkanSurface.cpp", + "renderthread/RenderProxy.cpp", + "renderthread/RenderThread.cpp", + "service/GraphicsStatsService.cpp", + "thread/CommonPool.cpp", + "utils/GLUtils.cpp", + "utils/StringUtils.cpp", + "AutoBackendTextureRelease.cpp", + "DeferredLayerUpdater.cpp", + "DeviceInfo.cpp", + "FrameInfo.cpp", + "FrameInfoVisualizer.cpp", + "HardwareBitmapUploader.cpp", + "HWUIProperties.sysprop", + "JankTracker.cpp", + "Layer.cpp", + "LayerUpdateQueue.cpp", + "ProfileData.cpp", + "ProfileDataContainer.cpp", + "Readback.cpp", + "TreeInfo.cpp", + "WebViewFunctorManager.cpp", + "protos/graphicsstats.proto", + ], + + // Allow implicit fallthroughs in HardwareBitmapUploader.cpp until they are fixed. + cflags: ["-Wno-implicit-fallthrough"], + }, + host: { + srcs: [ + "utils/HostColorSpace.cpp", + ], + export_static_lib_headers: [ + "libarect", + ], + } + } } cc_library { name: "libhwui", + host_supported: true, defaults: [ "libhwui_defaults", - - // Enables fine-grained GLES error checking - // If enabled, every GLES call is wrapped & error checked - // Has moderate overhead - //"hwui_enable_opengl_validation", + "android_graphics_apex", + "android_graphics_jni", ], + export_header_lib_headers: ["android_graphics_apex_headers"], } -// ------------------------ -// static library null gpu -// ------------------------ - cc_library_static { - name: "libhwui_static_debug", + name: "libhwui_static", defaults: [ "libhwui_defaults", - "hwui_debug", - ], - cflags: ["-DHWUI_NULL_GPU"], - srcs: [ - "debug/nullegl.cpp", ], } @@ -268,6 +512,13 @@ cc_defaults { name: "hwui_test_defaults", defaults: ["hwui_defaults"], test_suites: ["device-tests"], + target: { + android: { + shared_libs: [ + "libgui", + ], + } + }, srcs: [ "tests/common/scenes/*.cpp", "tests/common/LeakChecker.cpp", @@ -284,29 +535,29 @@ cc_defaults { cc_test { name: "hwui_unit_tests", - defaults: ["hwui_test_defaults"], + defaults: [ + "hwui_test_defaults", + "android_graphics_apex", + "android_graphics_jni", + ], static_libs: [ "libgmock", - "libhwui_static_debug", + "libhwui_static", ], shared_libs: [ "libmemunreachable", ], - cflags: [ - "-include debug/wrap_gles.h", - "-DHWUI_NULL_GPU", - ], srcs: [ "tests/unit/main.cpp", + "tests/unit/ABitmapTests.cpp", "tests/unit/CacheManagerTests.cpp", "tests/unit/CanvasContextTests.cpp", "tests/unit/CommonPoolTests.cpp", "tests/unit/DamageAccumulatorTests.cpp", "tests/unit/DeferredLayerUpdaterTests.cpp", "tests/unit/FatVectorTests.cpp", - "tests/unit/GpuMemoryTrackerTests.cpp", "tests/unit/GraphicsStatsServiceTests.cpp", "tests/unit/LayerUpdateQueueTests.cpp", "tests/unit/LinearAllocatorTests.cpp", @@ -327,7 +578,6 @@ cc_test { "tests/unit/ThreadBaseTests.cpp", "tests/unit/TypefaceTests.cpp", "tests/unit/VectorDrawableTests.cpp", - "tests/unit/VectorDrawableAtlasTests.cpp", "tests/unit/WebViewFunctorManagerTests.cpp", ], } @@ -340,8 +590,7 @@ cc_benchmark { name: "hwuimacro", defaults: ["hwui_test_defaults"], - // set to libhwui_static_debug to skip actual GL commands - whole_static_libs: ["libhwui"], + static_libs: ["libhwui"], shared_libs: [ "libmemunreachable", ], @@ -360,12 +609,7 @@ cc_benchmark { name: "hwuimicro", defaults: ["hwui_test_defaults"], - cflags: [ - "-include debug/wrap_gles.h", - "-DHWUI_NULL_GPU", - ], - - whole_static_libs: ["libhwui_static_debug"], + static_libs: ["libhwui_static"], shared_libs: [ "libmemunreachable", ], diff --git a/libs/hwui/AndroidTest.xml b/libs/hwui/AndroidTest.xml index eab32c5a67ce..381fb9f6c7bf 100644 --- a/libs/hwui/AndroidTest.xml +++ b/libs/hwui/AndroidTest.xml @@ -28,9 +28,11 @@ <test class="com.android.tradefed.testtype.GoogleBenchmarkTest" > <option name="native-benchmark-device-path" value="/data/benchmarktest" /> <option name="benchmark-module-name" value="hwuimicro" /> + <option name="file-exclusion-filter-regex" value=".*\.config$" /> </test> <test class="com.android.tradefed.testtype.GoogleBenchmarkTest" > <option name="native-benchmark-device-path" value="/data/benchmarktest" /> <option name="benchmark-module-name" value="hwuimacro" /> + <option name="file-exclusion-filter-regex" value=".*\.config$" /> </test> </configuration> diff --git a/libs/hwui/AutoBackendTextureRelease.cpp b/libs/hwui/AutoBackendTextureRelease.cpp new file mode 100644 index 000000000000..72747e8fa543 --- /dev/null +++ b/libs/hwui/AutoBackendTextureRelease.cpp @@ -0,0 +1,91 @@ +/* + * Copyright 2019 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. + */ + +#include "AutoBackendTextureRelease.h" + +#include "renderthread/RenderThread.h" +#include "utils/Color.h" +#include "utils/PaintUtils.h" + +using namespace android::uirenderer::renderthread; + +namespace android { +namespace uirenderer { + +AutoBackendTextureRelease::AutoBackendTextureRelease(GrContext* context, AHardwareBuffer* buffer) { + AHardwareBuffer_Desc desc; + AHardwareBuffer_describe(buffer, &desc); + bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT); + GrBackendFormat backendFormat = + GrAHardwareBufferUtils::GetBackendFormat(context, buffer, desc.format, false); + mBackendTexture = GrAHardwareBufferUtils::MakeBackendTexture( + context, buffer, desc.width, desc.height, &mDeleteProc, &mUpdateProc, &mImageCtx, + createProtectedImage, backendFormat, false); +} + +void AutoBackendTextureRelease::unref(bool releaseImage) { + if (!RenderThread::isCurrent()) { + // EGLImage needs to be destroyed on RenderThread to prevent memory leak. + // ~SkImage dtor for both pipelines needs to be invoked on RenderThread, because it is not + // thread safe. + RenderThread::getInstance().queue().post([this, releaseImage]() { unref(releaseImage); }); + return; + } + + if (releaseImage) { + mImage.reset(); + } + + mUsageCount--; + if (mUsageCount <= 0) { + if (mBackendTexture.isValid()) { + mDeleteProc(mImageCtx); + mBackendTexture = {}; + } + delete this; + } +} + +// releaseProc is invoked by SkImage, when texture is no longer in use. +// "releaseContext" contains an "AutoBackendTextureRelease*". +static void releaseProc(SkImage::ReleaseContext releaseContext) { + AutoBackendTextureRelease* textureRelease = + reinterpret_cast<AutoBackendTextureRelease*>(releaseContext); + textureRelease->unref(false); +} + +void AutoBackendTextureRelease::makeImage(AHardwareBuffer* buffer, android_dataspace dataspace, + GrContext* context) { + AHardwareBuffer_Desc desc; + AHardwareBuffer_describe(buffer, &desc); + SkColorType colorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format); + mImage = SkImage::MakeFromTexture( + context, mBackendTexture, kTopLeft_GrSurfaceOrigin, colorType, kPremul_SkAlphaType, + uirenderer::DataSpaceToColorSpace(dataspace), releaseProc, this); + if (mImage.get()) { + // The following ref will be counteracted by releaseProc, when SkImage is discarded. + ref(); + } +} + +void AutoBackendTextureRelease::newBufferContent(GrContext* context) { + if (mBackendTexture.isValid()) { + mUpdateProc(mImageCtx, context); + } +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/AutoBackendTextureRelease.h b/libs/hwui/AutoBackendTextureRelease.h new file mode 100644 index 000000000000..acdd63cb7921 --- /dev/null +++ b/libs/hwui/AutoBackendTextureRelease.h @@ -0,0 +1,67 @@ +/* + * Copyright 2019 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. + */ + +#pragma once + +#include <GrAHardwareBufferUtils.h> +#include <GrBackendSurface.h> +#include <SkImage.h> +#include <android/hardware_buffer.h> +#include <system/graphics.h> + +namespace android { +namespace uirenderer { + +/** + * AutoBackendTextureRelease manages EglImage/VkImage lifetime. It is a ref-counted object + * that keeps GPU resources alive until the last SkImage object using them is destroyed. + */ +class AutoBackendTextureRelease final { +public: + AutoBackendTextureRelease(GrContext* context, AHardwareBuffer* buffer); + + const GrBackendTexture& getTexture() const { return mBackendTexture; } + + // Only called on the RenderThread, so it need not be thread-safe. + void ref() { mUsageCount++; } + + void unref(bool releaseImage); + + inline sk_sp<SkImage> getImage() const { return mImage; } + + void makeImage(AHardwareBuffer* buffer, android_dataspace dataspace, GrContext* context); + + void newBufferContent(GrContext* context); + +private: + // The only way to invoke dtor is with unref, when mUsageCount is 0. + ~AutoBackendTextureRelease() {} + + GrBackendTexture mBackendTexture; + GrAHardwareBufferUtils::DeleteImageProc mDeleteProc; + GrAHardwareBufferUtils::UpdateImageProc mUpdateProc; + GrAHardwareBufferUtils::TexImageCtx mImageCtx; + + // Starting with refcount 1, because the first ref is held by SurfaceTexture. Additional refs + // are held by SkImages. + int mUsageCount = 1; + + // mImage is the SkImage created from mBackendTexture. + sk_sp<SkImage> mImage; +}; + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp index 0cfaa8c61279..8c37d73366c2 100644 --- a/libs/hwui/CanvasTransform.cpp +++ b/libs/hwui/CanvasTransform.cpp @@ -100,9 +100,9 @@ static void applyColorTransform(ColorTransform transform, SkPaint& paint) { SkBlendMode mode; SkColor color; // TODO: LRU this or something to avoid spamming new color mode filters - if (paint.getColorFilter()->asColorMode(&color, &mode)) { + if (paint.getColorFilter()->asAColorMode(&color, &mode)) { color = transformColor(transform, color); - paint.setColorFilter(SkColorFilter::MakeModeFilter(color, mode)); + paint.setColorFilter(SkColorFilters::Blend(color, mode)); } } } diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp index cca0032b230e..b39f4f20dc0d 100644 --- a/libs/hwui/DamageAccumulator.cpp +++ b/libs/hwui/DamageAccumulator.cpp @@ -130,27 +130,35 @@ static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) // calculations. Just give up and expand to DIRTY_MIN/DIRTY_MAX temp.set(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX); } - out->join(RECT_ARGS(temp)); + out->join({RECT_ARGS(temp)}); } void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) { mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty); } -static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) { - if (in.isEmpty()) return; - const SkMatrix* transform = props.getTransformMatrix(); - SkRect temp(in); +static inline void applyMatrix(const SkMatrix* transform, SkRect* rect) { if (transform && !transform->isIdentity()) { if (CC_LIKELY(!transform->hasPerspective())) { - transform->mapRect(&temp); + transform->mapRect(rect); } else { // Don't attempt to calculate damage for a perspective transform // as the numbers this works with can break the perspective // calculations. Just give up and expand to DIRTY_MIN/DIRTY_MAX - temp.set(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX); + rect->setLTRB(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX); } } +} + +static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) { + if (in.isEmpty()) return; + SkRect temp(in); + applyMatrix(props.getTransformMatrix(), &temp); + if (props.getStaticMatrix()) { + applyMatrix(props.getStaticMatrix(), &temp); + } else if (props.getAnimationMatrix()) { + applyMatrix(props.getAnimationMatrix(), &temp); + } temp.offset(props.getLeft(), props.getTop()); out->join(temp); } @@ -201,7 +209,7 @@ void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) { // Perform clipping if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) { - if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) { + if (!frame->pendingDirty.intersect(SkRect::MakeIWH(props.getWidth(), props.getHeight()))) { frame->pendingDirty.setEmpty(); } } @@ -225,7 +233,7 @@ void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) { } void DamageAccumulator::dirty(float left, float top, float right, float bottom) { - mHead->pendingDirty.join(left, top, right, bottom); + mHead->pendingDirty.join({left, top, right, bottom}); } void DamageAccumulator::peekAtDirty(SkRect* dest) const { diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index 3bee3018d36e..67d8c07e61de 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -15,8 +15,20 @@ */ #include "DeferredLayerUpdater.h" +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +// TODO: Use public SurfaceTexture APIs once available and include public NDK header file instead. +#include <surfacetexture/surface_texture_platform.h> +#include "AutoBackendTextureRelease.h" +#include "Matrix.h" +#include "Properties.h" #include "renderstate/RenderState.h" -#include "utils/PaintUtils.h" +#include "renderthread/EglManager.h" +#include "renderthread/RenderThread.h" +#include "renderthread/VulkanManager.h" + +using namespace android::uirenderer::renderthread; namespace android { namespace uirenderer { @@ -24,7 +36,7 @@ namespace uirenderer { DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState) : mRenderState(renderState) , mBlend(false) - , mSurfaceTexture(nullptr) + , mSurfaceTexture(nullptr, [](ASurfaceTexture*) {}) , mTransform(nullptr) , mGLContextAttached(false) , mUpdateTexImage(false) @@ -38,6 +50,14 @@ DeferredLayerUpdater::~DeferredLayerUpdater() { destroyLayer(); } +void DeferredLayerUpdater::setSurfaceTexture(AutoTextureRelease&& consumer) { + mSurfaceTexture = std::move(consumer); + + GLenum target = ASurfaceTexture_getCurrentTextureTarget(mSurfaceTexture.get()); + LOG_ALWAYS_FATAL_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES, + "set unsupported SurfaceTexture with target %x", target); +} + void DeferredLayerUpdater::onContextDestroyed() { destroyLayer(); } @@ -48,13 +68,15 @@ void DeferredLayerUpdater::destroyLayer() { } if (mSurfaceTexture.get() && mGLContextAttached) { - mSurfaceTexture->detachFromView(); + ASurfaceTexture_releaseConsumerOwnership(mSurfaceTexture.get()); mGLContextAttached = false; } mLayer->postDecStrong(); mLayer = nullptr; + + mImageSlots.clear(); } void DeferredLayerUpdater::setPaint(const SkPaint* paint) { @@ -67,6 +89,35 @@ void DeferredLayerUpdater::setPaint(const SkPaint* paint) { } } +static status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, EGLDisplay* display, + int* releaseFence, void* handle) { + *display = EGL_NO_DISPLAY; + RenderState* renderState = (RenderState*)handle; + status_t err; + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) { + EglManager& eglManager = renderState->getRenderThread().eglManager(); + *display = eglManager.eglDisplay(); + err = eglManager.createReleaseFence(useFenceSync, eglFence, releaseFence); + } else { + err = renderState->getRenderThread().vulkanManager().createReleaseFence( + releaseFence, renderState->getRenderThread().getGrContext()); + } + return err; +} + +static status_t fenceWait(int fence, void* handle) { + // Wait on the producer fence for the buffer to be ready. + status_t err; + RenderState* renderState = (RenderState*)handle; + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) { + err = renderState->getRenderThread().eglManager().fenceWait(fence); + } else { + err = renderState->getRenderThread().vulkanManager().fenceWait( + fence, renderState->getRenderThread().getGrContext()); + } + return err; +} + void DeferredLayerUpdater::apply() { if (!mLayer) { mLayer = new Layer(mRenderState, mColorFilter, mAlpha, mMode); @@ -79,24 +130,36 @@ void DeferredLayerUpdater::apply() { if (!mGLContextAttached) { mGLContextAttached = true; mUpdateTexImage = true; - mSurfaceTexture->attachToView(); + ASurfaceTexture_takeConsumerOwnership(mSurfaceTexture.get()); } if (mUpdateTexImage) { mUpdateTexImage = false; - sk_sp<SkImage> layerImage; - SkMatrix textureTransform; - bool queueEmpty = true; - // If the SurfaceTexture queue is in synchronous mode, need to discard all - // but latest frame. Since we can't tell which mode it is in, - // do this unconditionally. - do { - layerImage = mSurfaceTexture->dequeueImage(textureTransform, &queueEmpty, - mRenderState); - } while (layerImage.get() && (!queueEmpty)); - if (layerImage.get()) { - // force filtration if buffer size != layer size - bool forceFilter = mWidth != layerImage->width() || mHeight != layerImage->height(); - updateLayer(forceFilter, textureTransform, layerImage); + float transformMatrix[16]; + android_dataspace dataspace; + int slot; + bool newContent = false; + // Note: ASurfaceTexture_dequeueBuffer discards all but the last frame. This + // is necessary if the SurfaceTexture queue is in synchronous mode, and we + // cannot tell which mode it is in. + AHardwareBuffer* hardwareBuffer = ASurfaceTexture_dequeueBuffer( + mSurfaceTexture.get(), &slot, &dataspace, transformMatrix, &newContent, + createReleaseFence, fenceWait, &mRenderState); + + if (hardwareBuffer) { + sk_sp<SkImage> layerImage = mImageSlots[slot].createIfNeeded( + hardwareBuffer, dataspace, newContent, + mRenderState.getRenderThread().getGrContext()); + // unref to match the ref added by ASurfaceTexture_dequeueBuffer. eglCreateImageKHR + // (invoked by createIfNeeded) will add a ref to the AHardwareBuffer. + AHardwareBuffer_release(hardwareBuffer); + if (layerImage.get()) { + SkMatrix textureTransform; + mat4(transformMatrix).copyTo(textureTransform); + // force filtration if buffer size != layer size + bool forceFilter = + mWidth != layerImage->width() || mHeight != layerImage->height(); + updateLayer(forceFilter, textureTransform, layerImage); + } } } @@ -108,7 +171,7 @@ void DeferredLayerUpdater::apply() { } void DeferredLayerUpdater::updateLayer(bool forceFilter, const SkMatrix& textureTransform, - const sk_sp<SkImage>& layerImage) { + const sk_sp<SkImage>& layerImage) { mLayer->setBlend(mBlend); mLayer->setForceFilter(forceFilter); mLayer->setSize(mWidth, mHeight); @@ -123,5 +186,42 @@ void DeferredLayerUpdater::detachSurfaceTexture() { } } +sk_sp<SkImage> DeferredLayerUpdater::ImageSlot::createIfNeeded(AHardwareBuffer* buffer, + android_dataspace dataspace, + bool forceCreate, + GrContext* context) { + if (!mTextureRelease || !mTextureRelease->getImage().get() || dataspace != mDataspace || + forceCreate || mBuffer != buffer) { + if (buffer != mBuffer) { + clear(); + } + + if (!buffer) { + return nullptr; + } + + if (!mTextureRelease) { + mTextureRelease = new AutoBackendTextureRelease(context, buffer); + } else { + mTextureRelease->newBufferContent(context); + } + + mDataspace = dataspace; + mBuffer = buffer; + mTextureRelease->makeImage(buffer, dataspace, context); + } + return mTextureRelease ? mTextureRelease->getImage() : nullptr; +} + +void DeferredLayerUpdater::ImageSlot::clear() { + if (mTextureRelease) { + // The following unref counteracts the initial mUsageCount of 1, set by default initializer. + mTextureRelease->unref(true); + mTextureRelease = nullptr; + } + + mBuffer = nullptr; +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index a91c111933c4..c44c0d537fa7 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -19,24 +19,25 @@ #include <SkColorFilter.h> #include <SkImage.h> #include <SkMatrix.h> +#include <android/hardware_buffer.h> #include <cutils/compiler.h> -#include <map> -#include <system/graphics.h> -#include <utils/StrongPointer.h> +#include <android/surface_texture.h> -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> +#include <map> +#include <memory> -#include "renderstate/RenderState.h" -#include "surfacetexture/SurfaceTexture.h" #include "Layer.h" #include "Rect.h" +#include "renderstate/RenderState.h" namespace android { namespace uirenderer { +class AutoBackendTextureRelease; class RenderState; +typedef std::unique_ptr<ASurfaceTexture, decltype(&ASurfaceTexture_release)> AutoTextureRelease; + // Container to hold the properties a layer should be set to at the start // of a render pass class DeferredLayerUpdater : public VirtualLightRefBase, public IGpuContextCallback { @@ -67,15 +68,7 @@ public: return false; } - ANDROID_API void setSurfaceTexture(const sp<SurfaceTexture>& consumer) { - if (consumer.get() != mSurfaceTexture.get()) { - mSurfaceTexture = consumer; - - GLenum target = consumer->getCurrentTextureTarget(); - LOG_ALWAYS_FATAL_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES, - "set unsupported SurfaceTexture with target %x", target); - } - } + ANDROID_API void setSurfaceTexture(AutoTextureRelease&& consumer); ANDROID_API void updateTexImage() { mUpdateTexImage = true; } @@ -103,6 +96,39 @@ protected: void onContextDestroyed() override; private: + /** + * ImageSlot contains the information and object references that + * DeferredLayerUpdater maintains about a slot. Slot id comes from + * ASurfaceTexture_dequeueBuffer. Usually there are at most 3 slots active at a time. + */ + class ImageSlot { + public: + ~ImageSlot() { clear(); } + + sk_sp<SkImage> createIfNeeded(AHardwareBuffer* buffer, android_dataspace dataspace, + bool forceCreate, GrContext* context); + + private: + void clear(); + + // the dataspace associated with the current image + android_dataspace mDataspace = HAL_DATASPACE_UNKNOWN; + + AHardwareBuffer* mBuffer = nullptr; + + /** + * mTextureRelease may outlive DeferredLayerUpdater, if the last ref is held by an SkImage. + * DeferredLayerUpdater holds one ref to mTextureRelease, which is decremented by "clear". + */ + AutoBackendTextureRelease* mTextureRelease = nullptr; + }; + + /** + * DeferredLayerUpdater stores the SkImages that have been allocated by the BufferQueue + * for each buffer slot. + */ + std::map<int, ImageSlot> mImageSlots; + RenderState& mRenderState; // Generic properties @@ -112,7 +138,7 @@ private: sk_sp<SkColorFilter> mColorFilter; int mAlpha = 255; SkBlendMode mMode = SkBlendMode::kSrcOver; - sp<SurfaceTexture> mSurfaceTexture; + AutoTextureRelease mSurfaceTexture; SkMatrix* mTransform; bool mGLContextAttached; bool mUpdateTexImage; diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index 0a9d965d0444..c24224cbbd67 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -15,123 +15,29 @@ */ #include <DeviceInfo.h> +#include <log/log.h> +#include <utils/Errors.h> #include "Properties.h" -#include <gui/ISurfaceComposer.h> -#include <gui/SurfaceComposerClient.h> -#include <ui/GraphicTypes.h> - -#include <mutex> -#include <thread> - -#include <log/log.h> - namespace android { namespace uirenderer { -static constexpr android::DisplayInfo sDummyDisplay{ - 1080, // w - 1920, // h - 320.0, // xdpi - 320.0, // ydpi - 60.0, // fps - 2.0, // density - 0, // orientation - false, // secure? - 0, // appVsyncOffset - 0, // presentationDeadline - 1080, // viewportW - 1920, // viewportH -}; - DeviceInfo* DeviceInfo::get() { - static DeviceInfo sDeviceInfo; - return &sDeviceInfo; -} - -static DisplayInfo QueryDisplayInfo() { - if (Properties::isolatedProcess) { - return sDummyDisplay; - } - - const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken(); - LOG_ALWAYS_FATAL_IF(token == nullptr, - "Failed to get display info because internal display is disconnected"); - - DisplayInfo displayInfo; - status_t status = SurfaceComposerClient::getDisplayInfo(token, &displayInfo); - LOG_ALWAYS_FATAL_IF(status, "Failed to get display info, error %d", status); - return displayInfo; -} - -static float QueryMaxRefreshRate() { - if (Properties::isolatedProcess) { - return sDummyDisplay.fps; - } - - const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken(); - LOG_ALWAYS_FATAL_IF(token == nullptr, - "Failed to get display info because internal display is disconnected"); - - Vector<DisplayInfo> configs; - configs.reserve(10); - status_t status = SurfaceComposerClient::getDisplayConfigs(token, &configs); - LOG_ALWAYS_FATAL_IF(status, "Failed to getDisplayConfigs, error %d", status); - LOG_ALWAYS_FATAL_IF(configs.size() == 0, "getDisplayConfigs returned 0 configs?"); - float max = 0.0f; - for (auto& info : configs) { - max = std::max(max, info.fps); - } - return max; -} - -static void queryWideColorGamutPreference(sk_sp<SkColorSpace>* colorSpace, SkColorType* colorType) { - if (Properties::isolatedProcess) { - *colorSpace = SkColorSpace::MakeSRGB(); - *colorType = SkColorType::kN32_SkColorType; - return; - } - ui::Dataspace defaultDataspace, wcgDataspace; - ui::PixelFormat defaultPixelFormat, wcgPixelFormat; - status_t status = - SurfaceComposerClient::getCompositionPreference(&defaultDataspace, &defaultPixelFormat, - &wcgDataspace, &wcgPixelFormat); - LOG_ALWAYS_FATAL_IF(status, "Failed to get composition preference, error %d", status); - switch (wcgDataspace) { - case ui::Dataspace::DISPLAY_P3: - *colorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3); - break; - case ui::Dataspace::V0_SCRGB: - *colorSpace = SkColorSpace::MakeSRGB(); - break; - case ui::Dataspace::V0_SRGB: - // when sRGB is returned, it means wide color gamut is not supported. - *colorSpace = SkColorSpace::MakeSRGB(); - break; - default: - LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space."); - } - switch (wcgPixelFormat) { - case ui::PixelFormat::RGBA_8888: - *colorType = SkColorType::kN32_SkColorType; - break; - case ui::PixelFormat::RGBA_FP16: - *colorType = SkColorType::kRGBA_F16_SkColorType; - break; - default: - LOG_ALWAYS_FATAL("Unreachable: unsupported pixel format."); - } + static DeviceInfo sDeviceInfo; + return &sDeviceInfo; } -DeviceInfo::DeviceInfo() : mMaxRefreshRate(QueryMaxRefreshRate()) { +DeviceInfo::DeviceInfo() { #if HWUI_NULL_GPU mMaxTextureSize = NULL_GPU_MAX_TEXTURE_SIZE; #else mMaxTextureSize = -1; #endif - mDisplayInfo = QueryDisplayInfo(); - queryWideColorGamutPreference(&mWideColorSpace, &mWideColorType); + updateDisplayInfo(); +} +DeviceInfo::~DeviceInfo() { + ADisplay_release(mDisplays); } int DeviceInfo::maxTextureSize() const { @@ -143,8 +49,74 @@ void DeviceInfo::setMaxTextureSize(int maxTextureSize) { DeviceInfo::get()->mMaxTextureSize = maxTextureSize; } -void DeviceInfo::onDisplayConfigChanged() { - mDisplayInfo = QueryDisplayInfo(); +void DeviceInfo::onRefreshRateChanged(int64_t vsyncPeriod) { + mVsyncPeriod = vsyncPeriod; +} + +void DeviceInfo::updateDisplayInfo() { + if (Properties::isolatedProcess) { + return; + } + + if (mCurrentConfig == nullptr) { + mDisplaysSize = ADisplay_acquirePhysicalDisplays(&mDisplays); + LOG_ALWAYS_FATAL_IF(mDisplays == nullptr || mDisplaysSize <= 0, + "Failed to get physical displays: no connected display: %d!", mDisplaysSize); + for (size_t i = 0; i < mDisplaysSize; i++) { + ADisplayType type = ADisplay_getDisplayType(mDisplays[i]); + if (type == ADisplayType::DISPLAY_TYPE_INTERNAL) { + mPhysicalDisplayIndex = i; + break; + } + } + LOG_ALWAYS_FATAL_IF(mPhysicalDisplayIndex < 0, "Failed to find a connected physical display!"); + + + // Since we now just got the primary display for the first time, then + // store the primary display metadata here. + ADisplay* primaryDisplay = mDisplays[mPhysicalDisplayIndex]; + mMaxRefreshRate = ADisplay_getMaxSupportedFps(primaryDisplay); + ADataSpace dataspace; + AHardwareBuffer_Format format; + ADisplay_getPreferredWideColorFormat(primaryDisplay, &dataspace, &format); + switch (dataspace) { + case ADATASPACE_DISPLAY_P3: + mWideColorSpace = + SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3); + break; + case ADATASPACE_SCRGB: + mWideColorSpace = SkColorSpace::MakeSRGB(); + break; + case ADATASPACE_SRGB: + // when sRGB is returned, it means wide color gamut is not supported. + mWideColorSpace = SkColorSpace::MakeSRGB(); + break; + default: + LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space."); + } + switch (format) { + case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: + mWideColorType = SkColorType::kN32_SkColorType; + break; + case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT: + mWideColorType = SkColorType::kRGBA_F16_SkColorType; + break; + default: + LOG_ALWAYS_FATAL("Unreachable: unsupported pixel format."); + } + } + // This method may have been called when the display config changed, so + // sync with the current configuration. + ADisplay* primaryDisplay = mDisplays[mPhysicalDisplayIndex]; + status_t status = ADisplay_getCurrentConfig(primaryDisplay, &mCurrentConfig); + LOG_ALWAYS_FATAL_IF(status, "Failed to get display config, error %d", status); + + mWidth = ADisplayConfig_getWidth(mCurrentConfig); + mHeight = ADisplayConfig_getHeight(mCurrentConfig); + mDensity = ADisplayConfig_getDensity(mCurrentConfig); + mVsyncPeriod = static_cast<int64_t>(1000000000 / ADisplayConfig_getFps(mCurrentConfig)); + mCompositorOffset = ADisplayConfig_getCompositorOffsetNanos(mCurrentConfig); + mAppOffset = ADisplayConfig_getAppVsyncOffsetNanos(mCurrentConfig); } } /* namespace uirenderer */ diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h index 0e3f11960ddc..16a22f4706f5 100644 --- a/libs/hwui/DeviceInfo.h +++ b/libs/hwui/DeviceInfo.h @@ -16,8 +16,8 @@ #ifndef DEVICEINFO_H #define DEVICEINFO_H +#include <apex/display.h> #include <SkImageInfo.h> -#include <ui/DisplayInfo.h> #include "utils/Macros.h" @@ -33,28 +33,45 @@ class DeviceInfo { public: static DeviceInfo* get(); + static float getMaxRefreshRate() { return get()->mMaxRefreshRate; } + static int32_t getWidth() { return get()->mWidth; } + static int32_t getHeight() { return get()->mHeight; } + static float getDensity() { return get()->mDensity; } + static int64_t getVsyncPeriod() { return get()->mVsyncPeriod; } + static int64_t getCompositorOffset() { return get()->mCompositorOffset; } + static int64_t getAppOffset() { return get()->mAppOffset; } // this value is only valid after the GPU has been initialized and there is a valid graphics // context or if you are using the HWUI_NULL_GPU int maxTextureSize() const; - const DisplayInfo& displayInfo() const { return mDisplayInfo; } sk_sp<SkColorSpace> getWideColorSpace() const { return mWideColorSpace; } SkColorType getWideColorType() const { return mWideColorType; } - float getMaxRefreshRate() const { return mMaxRefreshRate; } - void onDisplayConfigChanged(); + // This method should be called whenever the display refresh rate changes. + void onRefreshRateChanged(int64_t vsyncPeriod); private: friend class renderthread::RenderThread; static void setMaxTextureSize(int maxTextureSize); + void updateDisplayInfo(); DeviceInfo(); + ~DeviceInfo(); int mMaxTextureSize; - DisplayInfo mDisplayInfo; - sk_sp<SkColorSpace> mWideColorSpace; - SkColorType mWideColorType; - const float mMaxRefreshRate; + sk_sp<SkColorSpace> mWideColorSpace = SkColorSpace::MakeSRGB(); + SkColorType mWideColorType = SkColorType::kN32_SkColorType; + ADisplayConfig* mCurrentConfig = nullptr; + ADisplay** mDisplays = nullptr; + int mDisplaysSize = 0; + int mPhysicalDisplayIndex = -1; + float mMaxRefreshRate = 60.0; + int32_t mWidth = 1080; + int32_t mHeight = 1920; + float mDensity = 2.0; + int64_t mVsyncPeriod = 16666666; + int64_t mCompositorOffset = 0; + int64_t mAppOffset = 0; }; } /* namespace uirenderer */ diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in index 2deb5657c877..49817925d9b4 100644 --- a/libs/hwui/DisplayListOps.in +++ b/libs/hwui/DisplayListOps.in @@ -14,38 +14,41 @@ * limitations under the License. */ -X(Flush) -X(Save) -X(Restore) +X(Flush) +X(Save) +X(Restore) X(SaveLayer) X(SaveBehind) -X(Concat) -X(SetMatrix) +X(Concat44) +X(Concat) +X(SetMatrix) +X(Scale) X(Translate) -X(ClipPath) -X(ClipRect) -X(ClipRRect) +X(ClipPath) +X(ClipRect) +X(ClipRRect) X(ClipRegion) X(DrawPaint) X(DrawBehind) -X(DrawPath) -X(DrawRect) -X(DrawRegion) -X(DrawOval) +X(DrawPath) +X(DrawRect) +X(DrawRegion) +X(DrawOval) X(DrawArc) -X(DrawRRect) -X(DrawDRRect) -X(DrawAnnotation) -X(DrawDrawable) +X(DrawRRect) +X(DrawDRRect) +X(DrawAnnotation) +X(DrawDrawable) X(DrawPicture) -X(DrawImage) -X(DrawImageNine) -X(DrawImageRect) +X(DrawImage) +X(DrawImageNine) +X(DrawImageRect) X(DrawImageLattice) X(DrawTextBlob) -X(DrawPatch) -X(DrawPoints) -X(DrawVertices) -X(DrawAtlas) +X(DrawPatch) +X(DrawPoints) +X(DrawVertices) +X(DrawAtlas) X(DrawShadowRec) X(DrawVectorDrawable) +X(DrawWebView) diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp index 71cc9a81a09f..0698775b0021 100644 --- a/libs/hwui/FrameInfo.cpp +++ b/libs/hwui/FrameInfo.cpp @@ -37,13 +37,14 @@ const std::string FrameInfoNames[] = { "FrameCompleted", "DequeueBufferDuration", "QueueBufferDuration", + "GpuCompleted", }; static_assert((sizeof(FrameInfoNames) / sizeof(FrameInfoNames[0])) == static_cast<int>(FrameInfoIndex::NumIndexes), "size mismatch: FrameInfoNames doesn't match the enum!"); -static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 16, +static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 17, "Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)"); void FrameInfo::importUiThreadInfo(int64_t* info) { diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h index 0aab58c38ba0..51674fbd557e 100644 --- a/libs/hwui/FrameInfo.h +++ b/libs/hwui/FrameInfo.h @@ -51,6 +51,8 @@ enum class FrameInfoIndex { DequeueBufferDuration, QueueBufferDuration, + GpuCompleted, + // Must be the last value! // Also must be kept in sync with FrameMetrics.java#FRAME_STATS_COUNT NumIndexes @@ -100,15 +102,15 @@ class FrameInfo { public: void importUiThreadInfo(int64_t* info); - void markSyncStart() { set(FrameInfoIndex::SyncStart) = systemTime(CLOCK_MONOTONIC); } + void markSyncStart() { set(FrameInfoIndex::SyncStart) = systemTime(SYSTEM_TIME_MONOTONIC); } void markIssueDrawCommandsStart() { - set(FrameInfoIndex::IssueDrawCommandsStart) = systemTime(CLOCK_MONOTONIC); + set(FrameInfoIndex::IssueDrawCommandsStart) = systemTime(SYSTEM_TIME_MONOTONIC); } - void markSwapBuffers() { set(FrameInfoIndex::SwapBuffers) = systemTime(CLOCK_MONOTONIC); } + void markSwapBuffers() { set(FrameInfoIndex::SwapBuffers) = systemTime(SYSTEM_TIME_MONOTONIC); } - void markFrameCompleted() { set(FrameInfoIndex::FrameCompleted) = systemTime(CLOCK_MONOTONIC); } + void markFrameCompleted() { set(FrameInfoIndex::FrameCompleted) = systemTime(SYSTEM_TIME_MONOTONIC); } void addFlag(int frameInfoFlag) { set(FrameInfoIndex::Flags) |= static_cast<uint64_t>(frameInfoFlag); @@ -143,6 +145,13 @@ public: return duration(FrameInfoIndex::IntendedVsync, FrameInfoIndex::FrameCompleted); } + inline int64_t gpuDrawTime() const { + // GPU start time is approximated to the moment before swapBuffer is invoked. + // We could add an EGLSyncKHR fence at the beginning of the frame, but that is an overhead. + int64_t endTime = get(FrameInfoIndex::GpuCompleted); + return endTime > 0 ? endTime - get(FrameInfoIndex::SwapBuffers) : -1; + } + inline int64_t& set(FrameInfoIndex index) { return mFrameInfo[static_cast<int>(index)]; } inline int64_t get(FrameInfoIndex index) const { diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp deleted file mode 100644 index a9a7af8f22f3..000000000000 --- a/libs/hwui/GpuMemoryTracker.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#include "utils/StringUtils.h" - -#include <GpuMemoryTracker.h> -#include <cutils/compiler.h> -#include <utils/Trace.h> -#include <array> -#include <sstream> -#include <unordered_set> -#include <vector> - -namespace android { -namespace uirenderer { - -pthread_t gGpuThread = 0; - -#define NUM_TYPES static_cast<int>(GpuObjectType::TypeCount) - -const char* TYPE_NAMES[] = { - "Texture", "OffscreenBuffer", "Layer", -}; - -struct TypeStats { - int totalSize = 0; - int count = 0; -}; - -static std::array<TypeStats, NUM_TYPES> gObjectStats; -static std::unordered_set<GpuMemoryTracker*> gObjectSet; - -void GpuMemoryTracker::notifySizeChanged(int newSize) { - int delta = newSize - mSize; - mSize = newSize; - gObjectStats[static_cast<int>(mType)].totalSize += delta; -} - -void GpuMemoryTracker::startTrackingObject() { - auto result = gObjectSet.insert(this); - LOG_ALWAYS_FATAL_IF(!result.second, - "startTrackingObject() on %p failed, already being tracked!", this); - gObjectStats[static_cast<int>(mType)].count++; -} - -void GpuMemoryTracker::stopTrackingObject() { - size_t removed = gObjectSet.erase(this); - LOG_ALWAYS_FATAL_IF(removed != 1, "stopTrackingObject removed %zd, is %p not being tracked?", - removed, this); - gObjectStats[static_cast<int>(mType)].count--; -} - -void GpuMemoryTracker::onGpuContextCreated() { - LOG_ALWAYS_FATAL_IF(gGpuThread != 0, - "We already have a gpu thread? " - "current = %lu, gpu thread = %lu", - pthread_self(), gGpuThread); - gGpuThread = pthread_self(); -} - -void GpuMemoryTracker::onGpuContextDestroyed() { - gGpuThread = 0; - if (CC_UNLIKELY(gObjectSet.size() > 0)) { - std::stringstream os; - dump(os); - ALOGE("%s", os.str().c_str()); - LOG_ALWAYS_FATAL("Leaked %zd GPU objects!", gObjectSet.size()); - } -} - -void GpuMemoryTracker::dump() { - std::stringstream strout; - dump(strout); - ALOGD("%s", strout.str().c_str()); -} - -void GpuMemoryTracker::dump(std::ostream& stream) { - for (int type = 0; type < NUM_TYPES; type++) { - const TypeStats& stats = gObjectStats[type]; - stream << TYPE_NAMES[type]; - stream << " is using " << SizePrinter{stats.totalSize}; - stream << ", count = " << stats.count; - stream << std::endl; - } -} - -int GpuMemoryTracker::getInstanceCount(GpuObjectType type) { - return gObjectStats[static_cast<int>(type)].count; -} - -int GpuMemoryTracker::getTotalSize(GpuObjectType type) { - return gObjectStats[static_cast<int>(type)].totalSize; -} - -void GpuMemoryTracker::onFrameCompleted() { - if (ATRACE_ENABLED()) { - char buf[128]; - for (int type = 0; type < NUM_TYPES; type++) { - snprintf(buf, 128, "hwui_%s", TYPE_NAMES[type]); - const TypeStats& stats = gObjectStats[type]; - ATRACE_INT(buf, stats.totalSize); - snprintf(buf, 128, "hwui_%s_count", TYPE_NAMES[type]); - ATRACE_INT(buf, stats.count); - } - } -} - -} // namespace uirenderer -} // namespace android; diff --git a/libs/hwui/GpuMemoryTracker.h b/libs/hwui/GpuMemoryTracker.h deleted file mode 100644 index de3ca99ef14b..000000000000 --- a/libs/hwui/GpuMemoryTracker.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ -#pragma once - -#include <pthread.h> -#include <ostream> - -#include <log/log.h> - -namespace android { -namespace uirenderer { - -extern pthread_t gGpuThread; - -#define ASSERT_GPU_THREAD() \ - LOG_ALWAYS_FATAL_IF(!pthread_equal(gGpuThread, pthread_self()), \ - "Error, %p of type %d (size=%d) used on wrong thread! cur thread %lu " \ - "!= gpu thread %lu", \ - this, static_cast<int>(mType), mSize, pthread_self(), gGpuThread) - -enum class GpuObjectType { - Texture = 0, - OffscreenBuffer, - Layer, - - TypeCount, -}; - -class GpuMemoryTracker { -public: - GpuObjectType objectType() { return mType; } - int objectSize() { return mSize; } - - static void onGpuContextCreated(); - static void onGpuContextDestroyed(); - static void dump(); - static void dump(std::ostream& stream); - static int getInstanceCount(GpuObjectType type); - static int getTotalSize(GpuObjectType type); - static void onFrameCompleted(); - -protected: - explicit GpuMemoryTracker(GpuObjectType type) : mType(type) { - ASSERT_GPU_THREAD(); - startTrackingObject(); - } - - ~GpuMemoryTracker() { - notifySizeChanged(0); - stopTrackingObject(); - } - - void notifySizeChanged(int newSize); - -private: - void startTrackingObject(); - void stopTrackingObject(); - - int mSize = 0; - GpuObjectType mType; -}; - -} // namespace uirenderer -} // namespace android; diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp index b64588e0dcd2..a3d552faeb0a 100644 --- a/libs/hwui/HardwareBitmapUploader.cpp +++ b/libs/hwui/HardwareBitmapUploader.cpp @@ -187,7 +187,9 @@ private: EGLSyncKHR fence = mUploadThread->queue().runSync([&]() -> EGLSyncKHR { AutoSkiaGlTexture glTexture; glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image); - GL_CHECKPOINT(MODERATE); + if (GLUtils::dumpGLErrors()) { + return EGL_NO_SYNC_KHR; + } // glTexSubImage2D is synchronous in sense that it memcpy() from pointer that we // provide. @@ -195,19 +197,26 @@ private: // when we first use it in drawing glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(), format.format, format.type, bitmap.getPixels()); - GL_CHECKPOINT(MODERATE); + if (GLUtils::dumpGLErrors()) { + return EGL_NO_SYNC_KHR; + } EGLSyncKHR uploadFence = eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL); - LOG_ALWAYS_FATAL_IF(uploadFence == EGL_NO_SYNC_KHR, - "Could not create sync fence %#x", eglGetError()); + if (uploadFence == EGL_NO_SYNC_KHR) { + ALOGW("Could not create sync fence %#x", eglGetError()); + }; glFlush(); + GLUtils::dumpGLErrors(); return uploadFence; }); + if (fence == EGL_NO_SYNC_KHR) { + return false; + } EGLint waitStatus = eglClientWaitSyncKHR(display, fence, 0, FENCE_TIMEOUT); - LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR, - "Failed to wait for the fence %#x", eglGetError()); + ALOGE_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR, + "Failed to wait for the fence %#x", eglGetError()); eglDestroySyncKHR(display, fence); } @@ -404,8 +413,9 @@ sk_sp<Bitmap> HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sou if (!sUploader->uploadHardwareBitmap(bitmap, format, buffer)) { return nullptr; } - return Bitmap::createFrom(buffer, bitmap.colorType(), bitmap.refColorSpace(), - bitmap.alphaType(), Bitmap::computePalette(bitmap)); + return Bitmap::createFrom(buffer->toAHardwareBuffer(), bitmap.colorType(), + bitmap.refColorSpace(), bitmap.alphaType(), + Bitmap::computePalette(bitmap)); } void HardwareBitmapUploader::initialize() { diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h index c300593d47a1..72243d23dd35 100644 --- a/libs/hwui/HardwareBitmapUploader.h +++ b/libs/hwui/HardwareBitmapUploader.h @@ -27,7 +27,13 @@ public: static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& sourceBitmap); +#ifdef __ANDROID__ static bool hasFP16Support(); +#else + static bool hasFP16Support() { + return true; + } +#endif }; } // namespace android::uirenderer diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index 53c5ad8eff3c..b2c39c90071a 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -16,8 +16,10 @@ #include "JankTracker.h" +#include <cutils/ashmem.h> #include <errno.h> #include <inttypes.h> +#include <log/log.h> #include <statslog.h> #include <sys/mman.h> @@ -25,11 +27,9 @@ #include <cmath> #include <cstdio> #include <limits> - -#include <cutils/ashmem.h> -#include <log/log.h> #include <sstream> +#include "DeviceInfo.h" #include "Properties.h" #include "utils/TimeUtils.h" #include "utils/Trace.h" @@ -79,11 +79,11 @@ static const int64_t EXEMPT_FRAMES_FLAGS = FrameInfoFlags::SurfaceCanvas; // and filter it out of the frame profile data static FrameInfoIndex sFrameStart = FrameInfoIndex::IntendedVsync; -JankTracker::JankTracker(ProfileDataContainer* globalData, const DisplayInfo& displayInfo) { +JankTracker::JankTracker(ProfileDataContainer* globalData) { mGlobalData = globalData; - nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1_s / displayInfo.fps); - nsecs_t sfOffset = frameIntervalNanos - (displayInfo.presentationDeadline - 1_ms); - nsecs_t offsetDelta = sfOffset - displayInfo.appVsyncOffset; + nsecs_t frameIntervalNanos = DeviceInfo::getVsyncPeriod(); + nsecs_t sfOffset = DeviceInfo::getCompositorOffset(); + nsecs_t offsetDelta = sfOffset - DeviceInfo::getAppOffset(); // There are two different offset cases. If the offsetDelta is positive // and small, then the intention is to give apps extra time by leveraging // pipelining between the UI & RT threads. If the offsetDelta is large or @@ -139,6 +139,9 @@ void JankTracker::finishFrame(const FrameInfo& frame) { (*mGlobalData)->reportJank(); } + if (mSwapDeadline < 0) { + mSwapDeadline = frame[FrameInfoIndex::IntendedVsync] + mFrameInterval; + } bool isTripleBuffered = (mSwapDeadline - frame[FrameInfoIndex::IntendedVsync]) > (mFrameInterval * 0.1); mSwapDeadline = std::max(mSwapDeadline + mFrameInterval, @@ -232,5 +235,13 @@ void JankTracker::reset() { : FrameInfoIndex::IntendedVsync; } +void JankTracker::finishGpuDraw(const FrameInfo& frame) { + int64_t totalGPUDrawTime = frame.gpuDrawTime(); + if (totalGPUDrawTime >= 0) { + mData->reportGPUFrame(totalGPUDrawTime); + (*mGlobalData)->reportGPUFrame(totalGPUDrawTime); + } +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h index 110211eda23a..b3fbbfe98669 100644 --- a/libs/hwui/JankTracker.h +++ b/libs/hwui/JankTracker.h @@ -23,7 +23,6 @@ #include "utils/RingBuffer.h" #include <cutils/compiler.h> -#include <ui/DisplayInfo.h> #include <array> #include <memory> @@ -49,7 +48,7 @@ struct ProfileDataDescription { // TODO: Replace DrawProfiler with this class JankTracker { public: - explicit JankTracker(ProfileDataContainer* globalData, const DisplayInfo& displayInfo); + explicit JankTracker(ProfileDataContainer* globalData); void setDescription(JankTrackerType type, const std::string&& name) { mDescription.type = type; @@ -58,6 +57,7 @@ public: FrameInfo* startFrame() { return &mFrames.next(); } void finishFrame(const FrameInfo& frame); + void finishGpuDraw(const FrameInfo& frame); void dumpStats(int fd) { dumpData(fd, &mDescription, mData.get()); } void dumpFrames(int fd); @@ -75,7 +75,7 @@ private: std::array<int64_t, NUM_BUCKETS> mThresholds; int64_t mFrameInterval; - nsecs_t mSwapDeadline; + nsecs_t mSwapDeadline = -1; // The amount of time we will erase from the total duration to account // for SF vsync offsets with HWC2 blocking dequeueBuffers. // (Vsync + mDequeueBlockTolerance) is the point at which we expect diff --git a/libs/hwui/debug/wrap_gles.cpp b/libs/hwui/LightingInfo.cpp index 8dc946e5667b..83bb255f3c3b 100644 --- a/libs/hwui/debug/wrap_gles.cpp +++ b/libs/hwui/LightingInfo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2019 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. @@ -14,20 +14,18 @@ * limitations under the License. */ -#include "GlesDriver.h" +#include "LightingInfo.h" -using namespace android::uirenderer::debug; +#include <float.h> -#undef API_ENTRY -#undef CALL_GL_API -#undef CALL_GL_API_RETURN +namespace android { +namespace uirenderer { -#define API_ENTRY(x) x -#define CALL_GL_API(api, ...) GlesDriver::get()->api##_(__VA_ARGS__) -#define CALL_GL_API_RETURN(api, ...) return GlesDriver::get()->api##_(__VA_ARGS__) +float LightingInfo::mLightRadius = 0; +uint8_t LightingInfo::mAmbientShadowAlpha = 0; +uint8_t LightingInfo::mSpotShadowAlpha = 0; -#include "gles_stubs.in" +Vector3 LightingInfo::mLightCenter = {FLT_MIN, FLT_MIN, FLT_MIN}; -#undef API_ENTRY -#undef CALL_GL_API -#undef CALL_GL_API_RETURN +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/LightingInfo.h b/libs/hwui/LightingInfo.h new file mode 100644 index 000000000000..3112eb3e0d73 --- /dev/null +++ b/libs/hwui/LightingInfo.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include "Lighting.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +class LightingInfo { +public: + + static float getLightRadius() { + if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) { + return Properties::overrideLightRadius; + } + return mLightRadius; + } + + static uint8_t getAmbientShadowAlpha() { + if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) { + return Properties::overrideAmbientShadowStrength; + } + return mAmbientShadowAlpha; + } + + static uint8_t getSpotShadowAlpha() { + if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) { + return Properties::overrideSpotShadowStrength; + } + return mSpotShadowAlpha; + } + + static Vector3 getLightCenter() { + if (CC_UNLIKELY(Properties::overrideLightPosY > 0 || Properties::overrideLightPosZ > 0)) { + Vector3 adjustedLightCenter = mLightCenter; + if (CC_UNLIKELY(Properties::overrideLightPosY > 0)) { + // negated since this shifts up + adjustedLightCenter.y = -Properties::overrideLightPosY; + } + if (CC_UNLIKELY(Properties::overrideLightPosZ > 0)) { + adjustedLightCenter.z = Properties::overrideLightPosZ; + } + return adjustedLightCenter; + } + return mLightCenter; + } + + static Vector3 getLightCenterRaw() { + return mLightCenter; + } + + static void setLightCenterRaw(const Vector3& lightCenter) { + mLightCenter = lightCenter; + } + + static void updateLighting(const LightGeometry& lightGeometry, const LightInfo& lightInfo) { + mLightRadius = lightGeometry.radius; + mAmbientShadowAlpha = lightInfo.ambientShadowAlpha; + mSpotShadowAlpha = lightInfo.spotShadowAlpha; + mLightCenter = lightGeometry.center; + } +private: + static float mLightRadius; + static uint8_t mAmbientShadowAlpha; + static uint8_t mSpotShadowAlpha; + static Vector3 mLightCenter; +}; + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h index f1c38031980e..2eb2c7c7e299 100644 --- a/libs/hwui/Outline.h +++ b/libs/hwui/Outline.h @@ -26,7 +26,7 @@ namespace uirenderer { class Outline { public: - enum class Type { None = 0, Empty = 1, ConvexPath = 2, RoundRect = 3 }; + enum class Type { None = 0, Empty = 1, Path = 2, RoundRect = 3 }; Outline() : mShouldClip(false), mType(Type::None), mRadius(0), mAlpha(0.0f) {} @@ -57,12 +57,12 @@ public: } } - void setConvexPath(const SkPath* outline, float alpha) { + void setPath(const SkPath* outline, float alpha) { if (!outline) { setEmpty(); return; } - mType = Type::ConvexPath; + mType = Type::Path; mPath = *outline; mBounds.set(outline->getBounds()); mAlpha = alpha; diff --git a/libs/hwui/ProfileData.cpp b/libs/hwui/ProfileData.cpp index 70ca4e3e8074..a8e36e37905d 100644 --- a/libs/hwui/ProfileData.cpp +++ b/libs/hwui/ProfileData.cpp @@ -15,6 +15,7 @@ */ #include "ProfileData.h" +#include "Properties.h" #include <cinttypes> @@ -98,6 +99,11 @@ void ProfileData::mergeWith(const ProfileData& other) { if (mStatStartTime > other.mStatStartTime || mStatStartTime == 0) { mStatStartTime = other.mStatStartTime; } + for (size_t i = 0; i < other.mGPUFrameCounts.size(); i++) { + mGPUFrameCounts[i] >>= divider; + mGPUFrameCounts[i] += other.mGPUFrameCounts[i]; + } + mPipelineType = other.mPipelineType; } void ProfileData::dump(int fd) const { @@ -117,6 +123,14 @@ void ProfileData::dump(int fd) const { histogramForEach([fd](HistogramEntry entry) { dprintf(fd, " %ums=%u", entry.renderTimeMs, entry.frameCount); }); + dprintf(fd, "\n50th gpu percentile: %ums", findGPUPercentile(50)); + dprintf(fd, "\n90th gpu percentile: %ums", findGPUPercentile(90)); + dprintf(fd, "\n95th gpu percentile: %ums", findGPUPercentile(95)); + dprintf(fd, "\n99th gpu percentile: %ums", findGPUPercentile(99)); + dprintf(fd, "\nGPU HISTOGRAM:"); + histogramGPUForEach([fd](HistogramEntry entry) { + dprintf(fd, " %ums=%u", entry.renderTimeMs, entry.frameCount); + }); } uint32_t ProfileData::findPercentile(int percentile) const { @@ -140,10 +154,12 @@ uint32_t ProfileData::findPercentile(int percentile) const { void ProfileData::reset() { mJankTypeCounts.fill(0); mFrameCounts.fill(0); + mGPUFrameCounts.fill(0); mSlowFrameCounts.fill(0); mTotalFrameCount = 0; mJankFrameCount = 0; - mStatStartTime = systemTime(CLOCK_MONOTONIC); + mStatStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + mPipelineType = Properties::getRenderPipelineType(); } void ProfileData::reportFrame(int64_t duration) { @@ -167,5 +183,40 @@ void ProfileData::histogramForEach(const std::function<void(HistogramEntry)>& ca } } +uint32_t ProfileData::findGPUPercentile(int percentile) const { + uint32_t totalGPUFrameCount = 0; // this is usually mTotalFrameCount - 3. + for (int i = mGPUFrameCounts.size() - 1; i >= 0; i--) { + totalGPUFrameCount += mGPUFrameCounts[i]; + } + int pos = percentile * totalGPUFrameCount / 100; + int remaining = totalGPUFrameCount - pos; + for (int i = mGPUFrameCounts.size() - 1; i >= 0; i--) { + remaining -= mGPUFrameCounts[i]; + if (remaining <= 0) { + return GPUFrameTimeForFrameCountIndex(i); + } + } + return 0; +} + +uint32_t ProfileData::GPUFrameTimeForFrameCountIndex(uint32_t index) { + return index != 25 ? index + 1 : 4950; +} + +void ProfileData::reportGPUFrame(int64_t duration) { + uint32_t index = static_cast<uint32_t>(ns2ms(duration)); + if (index > 25) { + index = 25; + } + + mGPUFrameCounts[index]++; +} + +void ProfileData::histogramGPUForEach(const std::function<void(HistogramEntry)>& callback) const { + for (size_t i = 0; i < mGPUFrameCounts.size(); i++) { + callback(HistogramEntry{GPUFrameTimeForFrameCountIndex(i), mGPUFrameCounts[i]}); + } +} + } /* namespace uirenderer */ } /* namespace android */
\ No newline at end of file diff --git a/libs/hwui/ProfileData.h b/libs/hwui/ProfileData.h index 564920b60328..dd3ba661dd29 100644 --- a/libs/hwui/ProfileData.h +++ b/libs/hwui/ProfileData.h @@ -16,6 +16,7 @@ #pragma once +#include "Properties.h" #include "utils/Macros.h" #include <utils/Timers.h> @@ -54,8 +55,10 @@ public: void mergeWith(const ProfileData& other); void dump(int fd) const; uint32_t findPercentile(int percentile) const; + uint32_t findGPUPercentile(int percentile) const; void reportFrame(int64_t duration); + void reportGPUFrame(int64_t duration); void reportJank() { mJankFrameCount++; } void reportJankType(JankType type) { mJankTypeCounts[static_cast<int>(type)]++; } @@ -63,21 +66,28 @@ public: uint32_t jankFrameCount() const { return mJankFrameCount; } nsecs_t statsStartTime() const { return mStatStartTime; } uint32_t jankTypeCount(JankType type) const { return mJankTypeCounts[static_cast<int>(type)]; } + RenderPipelineType pipelineType() const { return mPipelineType; } struct HistogramEntry { uint32_t renderTimeMs; uint32_t frameCount; }; void histogramForEach(const std::function<void(HistogramEntry)>& callback) const; + void histogramGPUForEach(const std::function<void(HistogramEntry)>& callback) const; constexpr static int HistogramSize() { return std::tuple_size<decltype(ProfileData::mFrameCounts)>::value + std::tuple_size<decltype(ProfileData::mSlowFrameCounts)>::value; } + constexpr static int GPUHistogramSize() { + return std::tuple_size<decltype(ProfileData::mGPUFrameCounts)>::value; + } + // Visible for testing static uint32_t frameTimeForFrameCountIndex(uint32_t index); static uint32_t frameTimeForSlowFrameCountIndex(uint32_t index); + static uint32_t GPUFrameTimeForFrameCountIndex(uint32_t index); private: // Open our guts up to unit tests @@ -88,10 +98,16 @@ private: std::array<uint32_t, 57> mFrameCounts; // Holds a histogram of frame times in 50ms increments from 150ms to 5s std::array<uint16_t, 97> mSlowFrameCounts; + // Holds a histogram of GPU draw times in 1ms increments. Frames longer than 25ms are placed in + // last bucket. + std::array<uint32_t, 26> mGPUFrameCounts; uint32_t mTotalFrameCount; uint32_t mJankFrameCount; nsecs_t mStatStartTime; + + // true if HWUI renders with Vulkan pipeline + RenderPipelineType mPipelineType; }; // For testing diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index af20c4f4f20e..446e81e65bb8 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -16,20 +16,32 @@ #include "Properties.h" #include "Debug.h" -#include "DeviceInfo.h" +#ifdef __ANDROID__ #include "HWUIProperties.sysprop.h" +#endif #include "SkTraceEventCommon.h" #include <algorithm> #include <cstdlib> +#include <optional> +#include <android-base/properties.h> #include <cutils/compiler.h> -#include <cutils/properties.h> #include <log/log.h> namespace android { namespace uirenderer { +#ifndef __ANDROID__ // Layoutlib does not compile HWUIProperties.sysprop as it depends on cutils properties +std::optional<bool> use_vulkan() { + return base::GetBoolProperty("ro.hwui.use_vulkan", false); +} + +std::optional<std::int32_t> render_ahead() { + return base::GetIntProperty("ro.hwui.render_ahead", 0); +} +#endif + bool Properties::debugLayersUpdates = false; bool Properties::debugOverdraw = false; bool Properties::showDirtyRegions = false; @@ -67,64 +79,54 @@ bool Properties::isolatedProcess = false; int Properties::contextPriority = 0; int Properties::defaultRenderAhead = -1; -static int property_get_int(const char* key, int defaultValue) { - char buf[PROPERTY_VALUE_MAX] = { - '\0', - }; - - if (property_get(key, buf, "") > 0) { - return atoi(buf); - } - return defaultValue; -} - bool Properties::load() { - char property[PROPERTY_VALUE_MAX]; bool prevDebugLayersUpdates = debugLayersUpdates; bool prevDebugOverdraw = debugOverdraw; debugOverdraw = false; - if (property_get(PROPERTY_DEBUG_OVERDRAW, property, nullptr) > 0) { - INIT_LOGD(" Overdraw debug enabled: %s", property); - if (!strcmp(property, "show")) { + std::string debugOverdrawProperty = base::GetProperty(PROPERTY_DEBUG_OVERDRAW, ""); + if (debugOverdrawProperty != "") { + INIT_LOGD(" Overdraw debug enabled: %s", debugOverdrawProperty); + if (debugOverdrawProperty == "show") { debugOverdraw = true; overdrawColorSet = OverdrawColorSet::Default; - } else if (!strcmp(property, "show_deuteranomaly")) { + } else if (debugOverdrawProperty == "show_deuteranomaly") { debugOverdraw = true; overdrawColorSet = OverdrawColorSet::Deuteranomaly; } } sProfileType = ProfileType::None; - if (property_get(PROPERTY_PROFILE, property, "") > 0) { - if (!strcmp(property, PROPERTY_PROFILE_VISUALIZE_BARS)) { + std::string profileProperty = base::GetProperty(PROPERTY_PROFILE, ""); + if (profileProperty != "") { + if (profileProperty == PROPERTY_PROFILE_VISUALIZE_BARS) { sProfileType = ProfileType::Bars; - } else if (!strcmp(property, "true")) { + } else if (profileProperty == "true") { sProfileType = ProfileType::Console; } } - debugLayersUpdates = property_get_bool(PROPERTY_DEBUG_LAYERS_UPDATES, false); + debugLayersUpdates = base::GetBoolProperty(PROPERTY_DEBUG_LAYERS_UPDATES, false); INIT_LOGD(" Layers updates debug enabled: %d", debugLayersUpdates); - showDirtyRegions = property_get_bool(PROPERTY_DEBUG_SHOW_DIRTY_REGIONS, false); + showDirtyRegions = base::GetBoolProperty(PROPERTY_DEBUG_SHOW_DIRTY_REGIONS, false); - debugLevel = (DebugLevel)property_get_int(PROPERTY_DEBUG, kDebugDisabled); + debugLevel = (DebugLevel)base::GetIntProperty(PROPERTY_DEBUG, (int)kDebugDisabled); - skipEmptyFrames = property_get_bool(PROPERTY_SKIP_EMPTY_DAMAGE, true); - useBufferAge = property_get_bool(PROPERTY_USE_BUFFER_AGE, true); - enablePartialUpdates = property_get_bool(PROPERTY_ENABLE_PARTIAL_UPDATES, true); + skipEmptyFrames = base::GetBoolProperty(PROPERTY_SKIP_EMPTY_DAMAGE, true); + useBufferAge = base::GetBoolProperty(PROPERTY_USE_BUFFER_AGE, true); + enablePartialUpdates = base::GetBoolProperty(PROPERTY_ENABLE_PARTIAL_UPDATES, true); - filterOutTestOverhead = property_get_bool(PROPERTY_FILTER_TEST_OVERHEAD, false); + filterOutTestOverhead = base::GetBoolProperty(PROPERTY_FILTER_TEST_OVERHEAD, false); - skpCaptureEnabled = debuggingEnabled && property_get_bool(PROPERTY_CAPTURE_SKP_ENABLED, false); + skpCaptureEnabled = debuggingEnabled && base::GetBoolProperty(PROPERTY_CAPTURE_SKP_ENABLED, false); SkAndroidFrameworkTraceUtil::setEnableTracing( - property_get_bool(PROPERTY_SKIA_ATRACE_ENABLED, false)); + base::GetBoolProperty(PROPERTY_SKIA_ATRACE_ENABLED, false)); - runningInEmulator = property_get_bool(PROPERTY_QEMU_KERNEL, false); + runningInEmulator = base::GetBoolProperty(PROPERTY_QEMU_KERNEL, false); - defaultRenderAhead = std::max(-1, std::min(2, property_get_int(PROPERTY_RENDERAHEAD, + defaultRenderAhead = std::max(-1, std::min(2, base::GetIntProperty(PROPERTY_RENDERAHEAD, render_ahead().value_or(0)))); return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw); @@ -175,9 +177,8 @@ RenderPipelineType Properties::peekRenderPipelineType() { return sRenderPipelineType; } bool useVulkan = use_vulkan().value_or(false); - char prop[PROPERTY_VALUE_MAX]; - property_get(PROPERTY_RENDERER, prop, useVulkan ? "skiavk" : "skiagl"); - if (!strcmp(prop, "skiavk")) { + std::string rendererProperty = base::GetProperty(PROPERTY_RENDERER, useVulkan ? "skiavk" : "skiagl"); + if (rendererProperty == "skiavk") { return RenderPipelineType::SkiaVulkan; } return RenderPipelineType::SkiaGL; @@ -189,14 +190,14 @@ RenderPipelineType Properties::getRenderPipelineType() { } void Properties::overrideRenderPipelineType(RenderPipelineType type) { -#if !defined(HWUI_GLES_WRAP_ENABLED) // If we're doing actual rendering then we can't change the renderer after it's been set. // Unit tests can freely change this as often as it wants, though, as there's no actual // GL rendering happening if (sRenderPipelineType != RenderPipelineType::NotInitialized) { + LOG_ALWAYS_FATAL_IF(sRenderPipelineType != type, + "Trying to change pipeline but it's already set"); return; } -#endif sRenderPipelineType = type; } diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 71b07c947716..d3ecb54d94f6 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -18,7 +18,6 @@ #define ANDROID_HWUI_PROPERTIES_H #include <cutils/compiler.h> -#include <cutils/properties.h> /** * This file contains the list of system properties used to configure libhwui. diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp index 89a9b997af97..39900e65cb8a 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -16,16 +16,16 @@ #include "Readback.h" -#include "pipeline/skia/LayerDrawable.h" -#include "renderthread/EglManager.h" -#include "renderthread/VulkanManager.h" - -#include <gui/Surface.h> -#include <ui/Fence.h> +#include <sync/sync.h> +#include <system/window.h> #include <ui/GraphicBuffer.h> + #include "DeferredLayerUpdater.h" #include "Properties.h" #include "hwui/Bitmap.h" +#include "pipeline/skia/LayerDrawable.h" +#include "renderthread/EglManager.h" +#include "renderthread/VulkanManager.h" #include "utils/Color.h" #include "utils/MathUtils.h" #include "utils/TraceUtils.h" @@ -35,40 +35,43 @@ using namespace android::uirenderer::renderthread; namespace android { namespace uirenderer { -CopyResult Readback::copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) { +CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap) { ATRACE_CALL(); // Setup the source - sp<GraphicBuffer> sourceBuffer; - sp<Fence> sourceFence; + AHardwareBuffer* rawSourceBuffer; + int rawSourceFence; Matrix4 texTransform; - status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence, texTransform.data); + status_t err = ANativeWindow_getLastQueuedBuffer(window, &rawSourceBuffer, &rawSourceFence, + texTransform.data); + base::unique_fd sourceFence(rawSourceFence); texTransform.invalidateType(); if (err != NO_ERROR) { ALOGW("Failed to get last queued buffer, error = %d", err); return CopyResult::UnknownError; } - if (!sourceBuffer.get()) { + if (rawSourceBuffer == nullptr) { ALOGW("Surface doesn't have any previously queued frames, nothing to readback from"); return CopyResult::SourceEmpty; } - if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) { + + std::unique_ptr<AHardwareBuffer, decltype(&AHardwareBuffer_release)> sourceBuffer( + rawSourceBuffer, AHardwareBuffer_release); + AHardwareBuffer_Desc description; + AHardwareBuffer_describe(sourceBuffer.get(), &description); + if (description.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) { ALOGW("Surface is protected, unable to copy from it"); return CopyResult::SourceInvalid; } - err = sourceFence->wait(500 /* ms */); - if (err != NO_ERROR) { + + if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) { ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt"); return CopyResult::Timeout; } - if (!sourceBuffer.get()) { - return CopyResult::UnknownError; - } - sk_sp<SkColorSpace> colorSpace = - DataSpaceToColorSpace(static_cast<android_dataspace>(surface.getBuffersDataSpace())); - sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer( - reinterpret_cast<AHardwareBuffer*>(sourceBuffer.get()), - kPremul_SkAlphaType, colorSpace); + sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace( + static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window))); + sk_sp<SkImage> image = + SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace); return copyImageInto(image, texTransform, srcRect, bitmap); } @@ -143,12 +146,11 @@ CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTran } Layer layer(mRenderThread.renderState(), nullptr, 255, SkBlendMode::kSrc); - bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width()) && - MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height()); - layer.setForceFilter(!disableFilter); layer.setSize(displayedWidth, displayedHeight); texTransform.copyTo(layer.getTexTransform()); layer.setImage(image); + // Scaling filter is not explicitly set here, because it is done inside copyLayerInfo + // after checking the necessity based on the src/dest rect size and the transformation. if (copyLayerInto(&layer, &skiaSrcRect, &skiaDestRect, bitmap)) { copyResult = CopyResult::Success; } diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h index e86a8136cfa3..e36f1ff6a072 100644 --- a/libs/hwui/Readback.h +++ b/libs/hwui/Readback.h @@ -47,7 +47,7 @@ public: /** * Copies the surface's most recently queued buffer into the provided bitmap. */ - CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap); + CopyResult copySurfaceInto(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap); CopyResult copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap); diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index e58fbbe8e667..dc467c41baed 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -16,6 +16,7 @@ #include "RecordingCanvas.h" +#include "pipeline/skia/FunctorDrawable.h" #include "VectorDrawable.h" #include "SkAndroidFrameworkUtils.h" @@ -129,6 +130,12 @@ struct SaveBehind final : Op { } }; +struct Concat44 final : Op { + static const auto kType = Type::Concat44; + Concat44(const SkScalar m[16]) { memcpy(colMajor, m, sizeof(colMajor)); } + SkScalar colMajor[16]; + void draw(SkCanvas* c, const SkMatrix&) const { c->experimental_concat44(colMajor); } +}; struct Concat final : Op { static const auto kType = Type::Concat; Concat(const SkMatrix& matrix) : matrix(matrix) {} @@ -143,6 +150,12 @@ struct SetMatrix final : Op { c->setMatrix(SkMatrix::Concat(original, matrix)); } }; +struct Scale final : Op { + static const auto kType = Type::Scale; + Scale(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {} + SkScalar sx, sy; + void draw(SkCanvas* c, const SkMatrix&) const { c->scale(sx, sy); } +}; struct Translate final : Op { static const auto kType = Type::Translate; Translate(SkScalar dx, SkScalar dy) : dx(dx), dy(dy) {} @@ -274,7 +287,12 @@ struct DrawDrawable final : Op { } sk_sp<SkDrawable> drawable; SkMatrix matrix = SkMatrix::I(); - void draw(SkCanvas* c, const SkMatrix&) const { c->drawDrawable(drawable.get(), &matrix); } + // It is important that we call drawable->draw(c) here instead of c->drawDrawable(drawable). + // Drawables are mutable and in cases, like RenderNodeDrawable, are not expected to produce the + // same content if retained outside the duration of the frame. Therefore we resolve + // them now and do not allow the canvas to take a reference to the drawable and potentially + // keep it alive for longer than the frames duration (e.g. SKP serialization). + void draw(SkCanvas* c, const SkMatrix&) const { drawable->draw(c, &matrix); } }; struct DrawPicture final : Op { static const auto kType = Type::DrawPicture; @@ -491,6 +509,16 @@ struct DrawVectorDrawable final : Op { SkPaint paint; BitmapPalette palette; }; +struct DrawWebView final : Op { + static const auto kType = Type::DrawWebView; + DrawWebView(skiapipeline::FunctorDrawable* drawable) : drawable(sk_ref_sp(drawable)) {} + sk_sp<skiapipeline::FunctorDrawable> drawable; + // We can't invoke SkDrawable::draw directly, because VkFunctorDrawable expects + // SkDrawable::onSnapGpuDrawHandler callback instead of SkDrawable::onDraw. + // SkCanvas::drawDrawable/SkGpuDevice::drawDrawable has the logic to invoke + // onSnapGpuDrawHandler. + void draw(SkCanvas* c, const SkMatrix&) const { c->drawDrawable(drawable.get()); } +}; } template <typename T, typename... Args> @@ -546,12 +574,18 @@ void DisplayListData::saveBehind(const SkRect* subset) { this->push<SaveBehind>(0, subset); } +void DisplayListData::concat44(const SkScalar colMajor[16]) { + this->push<Concat44>(0, colMajor); +} void DisplayListData::concat(const SkMatrix& matrix) { this->push<Concat>(0, matrix); } void DisplayListData::setMatrix(const SkMatrix& matrix) { this->push<SetMatrix>(0, matrix); } +void DisplayListData::scale(SkScalar sx, SkScalar sy) { + this->push<Scale>(0, sx, sy); +} void DisplayListData::translate(SkScalar dx, SkScalar dy) { this->push<Translate>(0, dx, dy); } @@ -675,6 +709,9 @@ void DisplayListData::drawShadowRec(const SkPath& path, const SkDrawShadowRec& r void DisplayListData::drawVectorDrawable(VectorDrawableRoot* tree) { this->push<DrawVectorDrawable>(0, tree); } +void DisplayListData::drawWebView(skiapipeline::FunctorDrawable* drawable) { + this->push<DrawWebView>(0, drawable); +} typedef void (*draw_fn)(const void*, SkCanvas*, const SkMatrix&); typedef void (*void_fn)(const void*); @@ -804,12 +841,18 @@ bool RecordingCanvas::onDoSaveBehind(const SkRect* subset) { return false; } +void RecordingCanvas::didConcat44(const SkScalar colMajor[16]) { + fDL->concat44(colMajor); +} void RecordingCanvas::didConcat(const SkMatrix& matrix) { fDL->concat(matrix); } void RecordingCanvas::didSetMatrix(const SkMatrix& matrix) { fDL->setMatrix(matrix); } +void RecordingCanvas::didScale(SkScalar sx, SkScalar sy) { + fDL->scale(sx, sy); +} void RecordingCanvas::didTranslate(SkScalar dx, SkScalar dy) { fDL->translate(dx, dy); } @@ -981,5 +1024,9 @@ void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { fDL->drawVectorDrawable(tree); } +void RecordingCanvas::drawWebView(skiapipeline::FunctorDrawable* drawable) { + fDL->drawWebView(drawable); +} + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 7269bcad3d7a..7eb1ce3eb18a 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -29,7 +29,6 @@ #include "SkPaint.h" #include "SkPath.h" #include "SkRect.h" -#include "SkTDArray.h" #include "SkTemplates.h" #include <vector> @@ -37,6 +36,10 @@ namespace android { namespace uirenderer { +namespace skiapipeline { +class FunctorDrawable; +} + enum class DisplayListOpType : uint8_t { #define X(T) T, #include "DisplayListOps.in" @@ -66,6 +69,7 @@ public: bool hasText() const { return mHasText; } size_t usedSize() const { return fUsed; } + size_t allocatedSize() const { return fReserved; } private: friend class RecordingCanvas; @@ -78,8 +82,10 @@ private: void saveBehind(const SkRect*); void restore(); + void concat44(const SkScalar colMajor[16]); void concat(const SkMatrix&); void setMatrix(const SkMatrix&); + void scale(SkScalar, SkScalar); void translate(SkScalar, SkScalar); void translateZ(SkScalar); @@ -120,6 +126,7 @@ private: SkBlendMode, const SkRect*, const SkPaint*); void drawShadowRec(const SkPath&, const SkDrawShadowRec&); void drawVectorDrawable(VectorDrawableRoot* tree); + void drawWebView(skiapipeline::FunctorDrawable*); template <typename T, typename... Args> void* push(size_t, Args&&...); @@ -148,8 +155,10 @@ public: void onFlush() override; + void didConcat44(const SkScalar[16]) override; void didConcat(const SkMatrix&) override; void didSetMatrix(const SkMatrix&) override; + void didScale(SkScalar, SkScalar) override; void didTranslate(SkScalar, SkScalar) override; void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override; @@ -204,6 +213,7 @@ public: void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override; void drawVectorDrawable(VectorDrawableRoot* tree); + void drawWebView(skiapipeline::FunctorDrawable*); /** * If "isClipMayBeComplex" returns false, it is guaranteed the current clip is a rectangle. diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index b73347b233d7..31e45558139d 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -20,9 +20,13 @@ #include "Debug.h" #include "TreeInfo.h" #include "VectorDrawable.h" -#include "renderstate/RenderState.h" +#include "private/hwui/WebViewFunctor.h" +#ifdef __ANDROID__ #include "renderthread/CanvasContext.h" -#include "utils/FatVector.h" +#else +#include "DamageAccumulator.h" +#include "pipeline/skia/SkiaDisplayList.h" +#endif #include "utils/MathUtils.h" #include "utils/StringUtils.h" #include "utils/TraceUtils.h" @@ -32,6 +36,7 @@ #include <atomic> #include <sstream> #include <string> +#include <ui/FatVector.h> namespace android { namespace uirenderer { @@ -103,7 +108,7 @@ void RenderNode::output(std::ostream& output, uint32_t level) { output << std::endl; } -int RenderNode::getDebugSize() { +int RenderNode::getUsageSize() { int size = sizeof(RenderNode); if (mStagingDisplayList) { size += mStagingDisplayList->getUsedSize(); @@ -114,6 +119,18 @@ int RenderNode::getDebugSize() { return size; } +int RenderNode::getAllocatedSize() { + int size = sizeof(RenderNode); + if (mStagingDisplayList) { + size += mStagingDisplayList->getAllocatedSize(); + } + if (mDisplayList && mDisplayList != mStagingDisplayList) { + size += mDisplayList->getAllocatedSize(); + } + return size; +} + + void RenderNode::prepareTree(TreeInfo& info) { ATRACE_CALL(); LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing"); @@ -161,6 +178,7 @@ void RenderNode::prepareLayer(TreeInfo& info, uint32_t dirtyMask) { } void RenderNode::pushLayerUpdate(TreeInfo& info) { +#ifdef __ANDROID__ // Layoutlib does not support CanvasContext and Layers LayerType layerType = properties().effectiveLayerType(); // If we are not a layer OR we cannot be rendered (eg, view was detached) // we need to destroy any Layers we may have had previously @@ -189,6 +207,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) { // That might be us, so tell CanvasContext that this layer is in the // tree and should not be destroyed. info.canvasContext.markLayerInUse(this); +#endif } /** diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index c6db7f1ba60d..c0ec2174bb35 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -27,6 +27,8 @@ #include <androidfw/ResourceTypes.h> +#include <ui/FatVector.h> + #include "AnimatorManager.h" #include "CanvasTransform.h" #include "Debug.h" @@ -35,7 +37,6 @@ #include "RenderProperties.h" #include "pipeline/skia/SkiaDisplayList.h" #include "pipeline/skia/SkiaLayer.h" -#include "utils/FatVector.h" #include <vector> @@ -102,7 +103,8 @@ public: ANDROID_API void setStagingDisplayList(DisplayList* newData); ANDROID_API void output(); - ANDROID_API int getDebugSize(); + ANDROID_API int getUsageSize(); + ANDROID_API int getAllocatedSize(); bool isRenderable() const { return mDisplayList && !mDisplayList->isEmpty(); } diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index e6710cc8f950..24f6035b6708 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -16,7 +16,10 @@ #pragma once +#ifdef __ANDROID__ // Layoutlib does not support device info #include "DeviceInfo.h" +#endif // __ANDROID__ + #include "Outline.h" #include "Rect.h" #include "RevealClip.h" @@ -526,9 +529,13 @@ public: } bool fitsOnLayer() const { +#ifdef __ANDROID__ // Layoutlib does not support device info const DeviceInfo* deviceInfo = DeviceInfo::get(); return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize() && mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize(); +#else + return mPrimitiveFields.mWidth <= 4096 && mPrimitiveFields.mHeight <= 4096; +#endif } bool promotedToLayer() const { diff --git a/libs/hwui/RootRenderNode.cpp b/libs/hwui/RootRenderNode.cpp new file mode 100644 index 000000000000..ddbbf58b3071 --- /dev/null +++ b/libs/hwui/RootRenderNode.cpp @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2019 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. + */ + +#include "RootRenderNode.h" + +#ifdef __ANDROID__ // Layoutlib does not support Looper (windows) +#include <utils/Looper.h> +#endif + +namespace android::uirenderer { + +#ifdef __ANDROID__ // Layoutlib does not support Looper +class FinishAndInvokeListener : public MessageHandler { +public: + explicit FinishAndInvokeListener(PropertyValuesAnimatorSet* anim) : mAnimator(anim) { + mListener = anim->getOneShotListener(); + mRequestId = anim->getRequestId(); + } + + virtual void handleMessage(const Message& message) { + if (mAnimator->getRequestId() == mRequestId) { + // Request Id has not changed, meaning there's no animation lifecyle change since the + // message is posted, so go ahead and call finish to make sure the PlayState is properly + // updated. This is needed because before the next frame comes in from UI thread to + // trigger an animation update, there could be reverse/cancel etc. So we need to update + // the playstate in time to ensure all the subsequent events get chained properly. + mAnimator->end(); + } + mListener->onAnimationFinished(nullptr); + } + +private: + sp<PropertyValuesAnimatorSet> mAnimator; + sp<AnimationListener> mListener; + uint32_t mRequestId; +}; + +void RootRenderNode::prepareTree(TreeInfo& info) { + info.errorHandler = mErrorHandler.get(); + + for (auto& anim : mRunningVDAnimators) { + // Assume that the property change in VD from the animators will not be consumed. Mark + // otherwise if the VDs are found in the display list tree. For VDs that are not in + // the display list tree, we stop providing animation pulses by 1) removing them from + // the animation list, 2) post a delayed message to end them at end time so their + // listeners can receive the corresponding callbacks. + anim->getVectorDrawable()->setPropertyChangeWillBeConsumed(false); + // Mark the VD dirty so it will damage itself during prepareTree. + anim->getVectorDrawable()->markDirty(); + } + if (info.mode == TreeInfo::MODE_FULL) { + for (auto& anim : mPausedVDAnimators) { + anim->getVectorDrawable()->setPropertyChangeWillBeConsumed(false); + anim->getVectorDrawable()->markDirty(); + } + } + // TODO: This is hacky + info.updateWindowPositions = true; + RenderNode::prepareTree(info); + info.updateWindowPositions = false; + info.errorHandler = nullptr; +} + +void RootRenderNode::attachAnimatingNode(RenderNode* animatingNode) { + mPendingAnimatingRenderNodes.push_back(animatingNode); +} + +void RootRenderNode::attachPendingVectorDrawableAnimators() { + mRunningVDAnimators.insert(mPendingVectorDrawableAnimators.begin(), + mPendingVectorDrawableAnimators.end()); + mPendingVectorDrawableAnimators.clear(); +} + +void RootRenderNode::detachAnimators() { + // Remove animators from the list and post a delayed message in future to end the animator + // For infinite animators, remove the listener so we no longer hold a global ref to the AVD + // java object, and therefore the AVD objects in both native and Java can be properly + // released. + for (auto& anim : mRunningVDAnimators) { + detachVectorDrawableAnimator(anim.get()); + anim->clearOneShotListener(); + } + for (auto& anim : mPausedVDAnimators) { + anim->clearOneShotListener(); + } + mRunningVDAnimators.clear(); + mPausedVDAnimators.clear(); +} + +// Move all the animators to the paused list, and send a delayed message to notify the finished +// listener. +void RootRenderNode::pauseAnimators() { + mPausedVDAnimators.insert(mRunningVDAnimators.begin(), mRunningVDAnimators.end()); + for (auto& anim : mRunningVDAnimators) { + detachVectorDrawableAnimator(anim.get()); + } + mRunningVDAnimators.clear(); +} + +void RootRenderNode::doAttachAnimatingNodes(AnimationContext* context) { + for (size_t i = 0; i < mPendingAnimatingRenderNodes.size(); i++) { + RenderNode* node = mPendingAnimatingRenderNodes[i].get(); + context->addAnimatingRenderNode(*node); + } + mPendingAnimatingRenderNodes.clear(); +} + +// Run VectorDrawable animators after prepareTree. +void RootRenderNode::runVectorDrawableAnimators(AnimationContext* context, TreeInfo& info) { + // Push staging. + if (info.mode == TreeInfo::MODE_FULL) { + pushStagingVectorDrawableAnimators(context); + } + + // Run the animators in the running list. + for (auto it = mRunningVDAnimators.begin(); it != mRunningVDAnimators.end();) { + if ((*it)->animate(*context)) { + it = mRunningVDAnimators.erase(it); + } else { + it++; + } + } + + // Run the animators in paused list during full sync. + if (info.mode == TreeInfo::MODE_FULL) { + // During full sync we also need to pulse paused animators, in case their targets + // have been added back to the display list. All the animators that passed the + // scheduled finish time will be removed from the paused list. + for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) { + if ((*it)->animate(*context)) { + // Animator has finished, remove from the list. + it = mPausedVDAnimators.erase(it); + } else { + it++; + } + } + } + + // Move the animators with a target not in DisplayList to paused list. + for (auto it = mRunningVDAnimators.begin(); it != mRunningVDAnimators.end();) { + if (!(*it)->getVectorDrawable()->getPropertyChangeWillBeConsumed()) { + // Vector Drawable is not in the display list, we should remove this animator from + // the list, put it in the paused list, and post a delayed message to end the + // animator. + detachVectorDrawableAnimator(it->get()); + mPausedVDAnimators.insert(*it); + it = mRunningVDAnimators.erase(it); + } else { + it++; + } + } + + // Move the animators with a target in DisplayList from paused list to running list, and + // trim paused list. + if (info.mode == TreeInfo::MODE_FULL) { + // Check whether any paused animator's target is back in Display List. If so, put the + // animator back in the running list. + for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) { + if ((*it)->getVectorDrawable()->getPropertyChangeWillBeConsumed()) { + mRunningVDAnimators.insert(*it); + it = mPausedVDAnimators.erase(it); + } else { + it++; + } + } + // Trim paused VD animators at full sync, so that when Java loses reference to an + // animator, we know we won't be requested to animate it any more, then we remove such + // animators from the paused list so they can be properly freed. We also remove the + // animators from paused list when the time elapsed since start has exceeded duration. + trimPausedVDAnimators(context); + } + + info.out.hasAnimations |= !mRunningVDAnimators.empty(); +} + +void RootRenderNode::trimPausedVDAnimators(AnimationContext* context) { + // Trim paused vector drawable animator list. + for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) { + // Remove paused VD animator if no one else is referencing it. Note that animators that + // have passed scheduled finish time are removed from list when they are being pulsed + // before prepare tree. + // TODO: this is a bit hacky, need to figure out a better way to track when the paused + // animators should be freed. + if ((*it)->getStrongCount() == 1) { + it = mPausedVDAnimators.erase(it); + } else { + it++; + } + } +} + +void RootRenderNode::pushStagingVectorDrawableAnimators(AnimationContext* context) { + for (auto& anim : mRunningVDAnimators) { + anim->pushStaging(*context); + } +} + +void RootRenderNode::destroy() { + for (auto& renderNode : mPendingAnimatingRenderNodes) { + renderNode->animators().endAllStagingAnimators(); + } + mPendingAnimatingRenderNodes.clear(); + mPendingVectorDrawableAnimators.clear(); +} + +void RootRenderNode::addVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) { + mPendingVectorDrawableAnimators.insert(anim); +} + +void RootRenderNode::detachVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) { + if (anim->isInfinite() || !anim->isRunning()) { + // Do not need to post anything if the animation is infinite (i.e. no meaningful + // end listener action), or if the animation has already ended. + return; + } + nsecs_t remainingTimeInMs = anim->getRemainingPlayTime(); + // Post a delayed onFinished event that is scheduled to be handled when the animator ends. + if (anim->getOneShotListener()) { + // VectorDrawable's oneshot listener is updated when there are user triggered animation + // lifecycle changes, such as start(), end(), etc. By using checking and clearing + // one shot listener, we ensure the same end listener event gets posted only once. + // Therefore no duplicates. Another benefit of using one shot listener is that no + // removal is necessary: the end time of animation will not change unless triggered by + // user events, in which case the already posted listener's id will become stale, and + // the onFinished callback will then be ignored. + sp<FinishAndInvokeListener> message = new FinishAndInvokeListener(anim); + auto looper = Looper::getForThread(); + LOG_ALWAYS_FATAL_IF(looper == nullptr, "Not on a looper thread?"); + looper->sendMessageDelayed(ms2ns(remainingTimeInMs), message, 0); + anim->clearOneShotListener(); + } +} + +class AnimationContextBridge : public AnimationContext { +public: + AnimationContextBridge(renderthread::TimeLord& clock, RootRenderNode* rootNode) + : AnimationContext(clock), mRootNode(rootNode) {} + + virtual ~AnimationContextBridge() {} + + // Marks the start of a frame, which will update the frame time and move all + // next frame animations into the current frame + virtual void startFrame(TreeInfo::TraversalMode mode) { + if (mode == TreeInfo::MODE_FULL) { + mRootNode->doAttachAnimatingNodes(this); + mRootNode->attachPendingVectorDrawableAnimators(); + } + AnimationContext::startFrame(mode); + } + + // Runs any animations still left in mCurrentFrameAnimations + virtual void runRemainingAnimations(TreeInfo& info) { + AnimationContext::runRemainingAnimations(info); + mRootNode->runVectorDrawableAnimators(this, info); + } + + virtual void pauseAnimators() override { mRootNode->pauseAnimators(); } + + virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) { + listener->onAnimationFinished(animator); + } + + virtual void destroy() { + AnimationContext::destroy(); + mRootNode->detachAnimators(); + } + +private: + sp<RootRenderNode> mRootNode; +}; + +AnimationContext* ContextFactoryImpl::createAnimationContext(renderthread::TimeLord& clock) { + return new AnimationContextBridge(clock, mRootNode); +} +#else + +void RootRenderNode::prepareTree(TreeInfo& info) { + info.errorHandler = mErrorHandler.get(); + info.updateWindowPositions = true; + RenderNode::prepareTree(info); + info.updateWindowPositions = false; + info.errorHandler = nullptr; +} + +void RootRenderNode::attachAnimatingNode(RenderNode* animatingNode) { } + +void RootRenderNode::destroy() { } + +void RootRenderNode::addVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) { } + +#endif + +} // namespace android::uirenderer diff --git a/libs/hwui/RootRenderNode.h b/libs/hwui/RootRenderNode.h new file mode 100644 index 000000000000..12de4ecac94b --- /dev/null +++ b/libs/hwui/RootRenderNode.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include <set> +#include <vector> + +#include "AnimationContext.h" +#include "Animator.h" +#include <IContextFactory.h> +#include "PropertyValuesAnimatorSet.h" +#include "RenderNode.h" + +namespace android::uirenderer { + +class ANDROID_API RootRenderNode : public RenderNode { +public: + ANDROID_API explicit RootRenderNode(std::unique_ptr<ErrorHandler> errorHandler) + : RenderNode(), mErrorHandler(std::move(errorHandler)) {} + + ANDROID_API virtual ~RootRenderNode() {} + + virtual void prepareTree(TreeInfo& info) override; + + ANDROID_API void attachAnimatingNode(RenderNode* animatingNode); + + void attachPendingVectorDrawableAnimators(); + + void detachAnimators(); + + void pauseAnimators(); + + void doAttachAnimatingNodes(AnimationContext* context); + + // Run VectorDrawable animators after prepareTree. + void runVectorDrawableAnimators(AnimationContext* context, TreeInfo& info); + + void trimPausedVDAnimators(AnimationContext* context); + + void pushStagingVectorDrawableAnimators(AnimationContext* context); + + ANDROID_API void destroy(); + + ANDROID_API void addVectorDrawableAnimator(PropertyValuesAnimatorSet* anim); + +private: + const std::unique_ptr<ErrorHandler> mErrorHandler; + std::vector<sp<RenderNode> > mPendingAnimatingRenderNodes; + std::set<sp<PropertyValuesAnimatorSet> > mPendingVectorDrawableAnimators; + std::set<sp<PropertyValuesAnimatorSet> > mRunningVDAnimators; + // mPausedVDAnimators stores a list of animators that have not yet passed the finish time, but + // their VectorDrawable targets are no longer in the DisplayList. We skip these animators when + // render thread runs animators independent of UI thread (i.e. RT_ONLY mode). These animators + // need to be re-activated once their VD target is added back into DisplayList. Since that could + // only happen when we do a full sync, we need to make sure to pulse these paused animators at + // full sync. If any animator's VD target is found in DisplayList during a full sync, we move + // the animator back to the running list. + std::set<sp<PropertyValuesAnimatorSet> > mPausedVDAnimators; + + void detachVectorDrawableAnimator(PropertyValuesAnimatorSet* anim); +}; + +#ifdef __ANDROID__ // Layoutlib does not support Animations +class ANDROID_API ContextFactoryImpl : public IContextFactory { +public: + ANDROID_API explicit ContextFactoryImpl(RootRenderNode* rootNode) : mRootNode(rootNode) {} + + ANDROID_API virtual AnimationContext* createAnimationContext( + renderthread::TimeLord& clock) override; + +private: + RootRenderNode* mRootNode; +}; +#endif + +} // namespace android::uirenderer diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index bebda8527def..941437998838 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -163,7 +163,7 @@ static inline SkCanvas::SaveLayerFlags layerFlags(SaveFlags::Flags flags) { SkCanvas::SaveLayerFlags layerFlags = 0; if (!(flags & SaveFlags::ClipToLayer)) { - layerFlags |= SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag; + layerFlags |= SkCanvasPriv::kDontClipToLayer_SaveLayerFlag; } return layerFlags; @@ -217,13 +217,16 @@ public: canvas->setMatrix(mMatrix); switch (mType) { case Type::Rect: - canvas->clipRect(mRRect.rect(), mOp); + // Don't anti-alias rectangular clips + canvas->clipRect(mRRect.rect(), mOp, false); break; case Type::RRect: - canvas->clipRRect(mRRect, mOp); + // Ensure rounded rectangular clips are anti-aliased + canvas->clipRRect(mRRect, mOp, true); break; case Type::Path: - canvas->clipPath(mPath.value(), mOp); + // Ensure path clips are anti-aliased + canvas->clipPath(mPath.value(), mOp, true); break; } } @@ -392,7 +395,7 @@ bool SkiaCanvas::clipRect(float left, float top, float right, float bottom, SkCl bool SkiaCanvas::clipPath(const SkPath* path, SkClipOp op) { this->recordClip(*path, op); - mCanvas->clipPath(*path, op); + mCanvas->clipPath(*path, op, true); return !mCanvas->isClipEmpty(); } @@ -454,7 +457,7 @@ void SkiaCanvas::drawPaint(const SkPaint& paint) { // Canvas draw operations: Geometry // ---------------------------------------------------------------------------- -void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint, +void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint, SkCanvas::PointMode mode) { if (CC_UNLIKELY(count < 2 || paint.nothingToDraw())) return; // convert the floats into SkPoints @@ -464,109 +467,142 @@ void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint pts[i].set(points[0], points[1]); points += 2; } - mCanvas->drawPoints(mode, count, pts.get(), *filterPaint(paint)); + + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawPoints(mode, count, pts.get(), p); + }); } -void SkiaCanvas::drawPoint(float x, float y, const SkPaint& paint) { - mCanvas->drawPoint(x, y, *filterPaint(paint)); +void SkiaCanvas::drawPoint(float x, float y, const Paint& paint) { + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawPoint(x, y, p); + }); } -void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint) { - this->drawPoints(points, count, *filterPaint(paint), SkCanvas::kPoints_PointMode); +void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint) { + this->drawPoints(points, count, paint, SkCanvas::kPoints_PointMode); } void SkiaCanvas::drawLine(float startX, float startY, float stopX, float stopY, - const SkPaint& paint) { - mCanvas->drawLine(startX, startY, stopX, stopY, *filterPaint(paint)); + const Paint& paint) { + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawLine(startX, startY, stopX, stopY, p); + }); } -void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) { +void SkiaCanvas::drawLines(const float* points, int count, const Paint& paint) { if (CC_UNLIKELY(count < 4 || paint.nothingToDraw())) return; - this->drawPoints(points, count, *filterPaint(paint), SkCanvas::kLines_PointMode); + this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode); } -void SkiaCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) { +void SkiaCanvas::drawRect(float left, float top, float right, float bottom, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; - mCanvas->drawRect({left, top, right, bottom}, *filterPaint(paint)); + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawRect({left, top, right, bottom}, p); + }); } -void SkiaCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) { +void SkiaCanvas::drawRegion(const SkRegion& region, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; - mCanvas->drawRegion(region, *filterPaint(paint)); + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawRegion(region, p); + }); } void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, - const SkPaint& paint) { + const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); - mCanvas->drawRoundRect(rect, rx, ry, *filterPaint(paint)); + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawRoundRect(rect, rx, ry, p); + }); } void SkiaCanvas::drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner, - const SkPaint& paint) { - mCanvas->drawDRRect(outer, inner, *filterPaint(paint)); + const Paint& paint) { + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawDRRect(outer, inner, p); + }); } -void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) { +void SkiaCanvas::drawCircle(float x, float y, float radius, const Paint& paint) { if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return; - mCanvas->drawCircle(x, y, radius, *filterPaint(paint)); + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawCircle(x, y, radius, p); + }); } -void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) { +void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect oval = SkRect::MakeLTRB(left, top, right, bottom); - mCanvas->drawOval(oval, *filterPaint(paint)); + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawOval(oval, p); + }); } void SkiaCanvas::drawArc(float left, float top, float right, float bottom, float startAngle, - float sweepAngle, bool useCenter, const SkPaint& paint) { + float sweepAngle, bool useCenter, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect arc = SkRect::MakeLTRB(left, top, right, bottom); - if (fabs(sweepAngle) >= 360.0f) { - mCanvas->drawOval(arc, *filterPaint(paint)); - } else { - mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, *filterPaint(paint)); - } + apply_looper(&paint, [&](const SkPaint& p) { + if (fabs(sweepAngle) >= 360.0f) { + mCanvas->drawOval(arc, p); + } else { + mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, p); + } + }); } -void SkiaCanvas::drawPath(const SkPath& path, const SkPaint& paint) { +void SkiaCanvas::drawPath(const SkPath& path, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; if (CC_UNLIKELY(path.isEmpty() && (!path.isInverseFillType()))) { return; } - mCanvas->drawPath(path, *filterPaint(paint)); + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawPath(path, p); + }); } -void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) { - mCanvas->drawVertices(vertices, mode, *filterPaint(paint)); +void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const Paint& paint) { + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawVertices(vertices, mode, p); + }); } // ---------------------------------------------------------------------------- // Canvas draw operations: Bitmaps // ---------------------------------------------------------------------------- -void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) { - mCanvas->drawImage(bitmap.makeImage(), left, top, filterPaint(paint)); +void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) { + auto image = bitmap.makeImage(); + apply_looper(paint, [&](const SkPaint& p) { + mCanvas->drawImage(image, left, top, &p); + }); } -void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) { +void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) { + auto image = bitmap.makeImage(); SkAutoCanvasRestore acr(mCanvas, true); mCanvas->concat(matrix); - mCanvas->drawImage(bitmap.makeImage(), 0, 0, filterPaint(paint)); + apply_looper(paint, [&](const SkPaint& p) { + mCanvas->drawImage(image, 0, 0, &p); + }); } void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, - float dstBottom, const SkPaint* paint) { + float dstBottom, const Paint* paint) { + auto image = bitmap.makeImage(); SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom); SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); - mCanvas->drawImageRect(bitmap.makeImage(), srcRect, dstRect, filterPaint(paint), - SkCanvas::kFast_SrcRectConstraint); + apply_looper(paint, [&](const SkPaint& p) { + mCanvas->drawImageRect(image, srcRect, dstRect, &p, SkCanvas::kFast_SrcRectConstraint); + }); } void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint) { + const float* vertices, const int* colors, const Paint* paint) { const int ptCount = (meshWidth + 1) * (meshHeight + 1); const int indexCount = meshWidth * meshHeight * 6; uint32_t flags = SkVertices::kHasTexCoords_BuilderFlag; @@ -640,21 +676,20 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, #endif // cons-up a shader for the bitmap - PaintCoW paintCoW(paint); - SkPaint& tmpPaint = paintCoW.writeable(); - - sk_sp<SkImage> image = bitmap.makeImage(); - sk_sp<SkShader> shader = - image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); - tmpPaint.setShader(std::move(shader)); - - mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate, - *filterPaint(std::move(paintCoW))); + Paint pnt; + if (paint) { + pnt = *paint; + } + pnt.setShader(bitmap.makeImage()->makeShader()); + auto v = builder.detach(); + apply_looper(&pnt, [&](const SkPaint& p) { + mCanvas->drawVertices(v, SkBlendMode::kModulate, p); + }); } void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) { + const Paint* paint) { SkCanvas::Lattice lattice; NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height()); @@ -675,8 +710,10 @@ void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, floa lattice.fBounds = nullptr; SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); - - mCanvas->drawImageLattice(bitmap.makeImage().get(), lattice, dst, filterPaint(paint)); + auto image = bitmap.makeImage(); + apply_looper(paint, [&](const SkPaint& p) { + mCanvas->drawImageLattice(image.get(), lattice, dst, &p); + }); } double SkiaCanvas::drawAnimatedImage(AnimatedImageDrawable* imgDrawable) { @@ -712,7 +749,10 @@ void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& pai glyphFunc(buffer.glyphs, buffer.pos); sk_sp<SkTextBlob> textBlob(builder.make()); - mCanvas->drawTextBlob(textBlob, 0, 0, paintCopy); + + apply_looper(&paintCopy, [&](const SkPaint& p) { + mCanvas->drawTextBlob(textBlob, 0, 0, p); + }); drawTextDecorations(x, y, totalAdvance, paintCopy); } @@ -750,7 +790,11 @@ void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, xform[i - start].fTy = pos.y() + tan.x() * y - halfWidth * tan.y(); } - this->asSkCanvas()->drawTextBlob(builder.make(), 0, 0, paintCopy); + sk_sp<SkTextBlob> textBlob(builder.make()); + + apply_looper(&paintCopy, [&](const SkPaint& p) { + mCanvas->drawTextBlob(textBlob, 0, 0, p); + }); } // ---------------------------------------------------------------------------- @@ -779,6 +823,13 @@ void SkiaCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x, mCanvas->drawDrawable(drawable.get()); } +void SkiaCanvas::drawPicture(const SkPicture& picture) { + // TODO: Change to mCanvas->drawPicture()? SkCanvas::drawPicture seems to be + // where the logic is for playback vs. ref picture. Using picture.playback here + // to stay behavior-identical for now, but should revisit this at some point. + picture.playback(mCanvas); +} + // ---------------------------------------------------------------------------- // Canvas draw operations: View System // ---------------------------------------------------------------------------- diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index bbe91eb2fbc4..1eb089d8764c 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -16,12 +16,16 @@ #pragma once #include "CanvasProperty.h" +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration #include "DeferredLayerUpdater.h" +#endif #include "RenderNode.h" #include "VectorDrawable.h" #include "hwui/Canvas.h" +#include "hwui/Paint.h" #include <SkCanvas.h> +#include "src/core/SkArenaAlloc.h" #include <cassert> #include <optional> @@ -44,8 +48,6 @@ public: virtual ~SkiaCanvas(); - virtual SkCanvas* asSkCanvas() override { return mCanvas; } - virtual void resetRecording(int width, int height, uirenderer::RenderNode* renderNode) override { LOG_ALWAYS_FATAL("SkiaCanvas cannot be reset as a recording canvas"); @@ -99,42 +101,41 @@ public: virtual void drawColor(int color, SkBlendMode mode) override; virtual void drawPaint(const SkPaint& paint) override; - virtual void drawPoint(float x, float y, const SkPaint& paint) override; - virtual void drawPoints(const float* points, int count, const SkPaint& paint) override; + virtual void drawPoint(float x, float y, const Paint& paint) override; + virtual void drawPoints(const float* points, int count, const Paint& paint) override; virtual void drawLine(float startX, float startY, float stopX, float stopY, - const SkPaint& paint) override; - virtual void drawLines(const float* points, int count, const SkPaint& paint) override; + const Paint& paint) override; + virtual void drawLines(const float* points, int count, const Paint& paint) override; virtual void drawRect(float left, float top, float right, float bottom, - const SkPaint& paint) override; - virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override; + const Paint& paint) override; + virtual void drawRegion(const SkRegion& region, const Paint& paint) override; virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, - const SkPaint& paint) override; + const Paint& paint) override; virtual void drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner, - const SkPaint& paint) override; + const Paint& paint) override; - virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override; + virtual void drawCircle(float x, float y, float radius, const Paint& paint) override; virtual void drawOval(float left, float top, float right, float bottom, - const SkPaint& paint) override; + const Paint& paint) override; virtual void drawArc(float left, float top, float right, float bottom, float startAngle, - float sweepAngle, bool useCenter, const SkPaint& paint) override; - virtual void drawPath(const SkPath& path, const SkPaint& paint) override; - virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint& paint) override; + float sweepAngle, bool useCenter, const Paint& paint) override; + virtual void drawPath(const SkPath& path, const Paint& paint) override; + virtual void drawVertices(const SkVertices*, SkBlendMode, const Paint& paint) override; - virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override; - virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override; + virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) override; + virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) override; virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, - float dstBottom, const SkPaint* paint) override; + float dstBottom, const Paint* paint) override; virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, const float* vertices, const int* colors, - const SkPaint* paint) override; + const Paint* paint) override; virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) override; + const Paint* paint) override; virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) override; - virtual bool drawTextAbsolutePos() const override { return true; } virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override; virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left, @@ -153,9 +154,11 @@ public: virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override; virtual void callDrawGLFunction(Functor* functor, uirenderer::GlFunctorLifecycleListener* listener) override; + virtual void drawPicture(const SkPicture& picture) override; protected: SkiaCanvas(); + SkCanvas* asSkCanvas() { return mCanvas; } void reset(SkCanvas* skiaCanvas); void drawDrawable(SkDrawable* drawable) { mCanvas->drawDrawable(drawable); } @@ -206,6 +209,35 @@ protected: */ PaintCoW&& filterPaint(PaintCoW&& paint) const; + template <typename Proc> void apply_looper(const Paint* paint, Proc proc) { + SkPaint skp; + SkDrawLooper* looper = nullptr; + if (paint) { + skp = *filterPaint(paint); + looper = paint->getLooper(); + } + if (looper) { + SkSTArenaAlloc<256> alloc; + SkDrawLooper::Context* ctx = looper->makeContext(&alloc); + if (ctx) { + SkDrawLooper::Context::Info info; + for (;;) { + SkPaint p = skp; + if (!ctx->next(&info, &p)) { + break; + } + mCanvas->save(); + mCanvas->translate(info.fTranslate.fX, info.fTranslate.fY); + proc(p); + mCanvas->restore(); + } + } + } else { + proc(skp); + } + } + + private: struct SaveRec { int saveCount; @@ -220,17 +252,7 @@ private: void recordClip(const T&, SkClipOp); void applyPersistentClips(size_t clipStartIndex); - void drawPoints(const float* points, int count, const SkPaint& paint, SkCanvas::PointMode mode); - - /** Filters the paint for bitmap drawing. - * - * After filtering the paint for bitmap drawing, - * also calls filterPaint on the paint. - * - * @param paint the paint to filter. Will be initialized with the default - * SkPaint before filtering if filtering is required. - */ - PaintCoW&& filterBitmap(PaintCoW&& paint, sk_sp<SkColorFilter> colorSpaceFilter) const; + void drawPoints(const float* points, int count, const Paint& paint, SkCanvas::PointMode mode); class Clip; diff --git a/libs/hwui/TEST_MAPPING b/libs/hwui/TEST_MAPPING index d9f2acbb49d2..b1719a979ce5 100644 --- a/libs/hwui/TEST_MAPPING +++ b/libs/hwui/TEST_MAPPING @@ -1,13 +1,15 @@ { "presubmit": [ { - "name": "CtsUiRenderingTestCases" - }, - { "name": "CtsGraphicsTestCases" }, { "name": "CtsAccelerationTestCases" } + ], + "imports": [ + { + "path": "cts/tests/tests/uirendering" + } ] }
\ No newline at end of file diff --git a/libs/hwui/TreeInfo.cpp b/libs/hwui/TreeInfo.cpp index dc53dd6c27c3..750f869e2551 100644 --- a/libs/hwui/TreeInfo.cpp +++ b/libs/hwui/TreeInfo.cpp @@ -24,7 +24,6 @@ TreeInfo::TreeInfo(TraversalMode mode, renderthread::CanvasContext& canvasContex : mode(mode) , prepareTextures(mode == MODE_FULL) , canvasContext(canvasContext) - , damageGenerationId(canvasContext.getFrameNumber()) , disableForceDark(canvasContext.useForceDark() ? 0 : 1) , screenSize(canvasContext.getNextFrameSize()) {} diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index 7e8d12fd4597..f2481f83767d 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -40,7 +40,6 @@ class ErrorHandler { public: virtual void onError(const std::string& message) = 0; -protected: virtual ~ErrorHandler() = default; }; diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 5418b337c371..cd908354aea5 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -16,18 +16,24 @@ #include "VectorDrawable.h" +#include <math.h> +#include <string.h> #include <utils/Log.h> + #include "PathParser.h" #include "SkColorFilter.h" #include "SkImageInfo.h" #include "SkShader.h" +#include "hwui/Paint.h" + +#ifdef __ANDROID__ +#include "renderthread/RenderThread.h" +#endif + #include "utils/Macros.h" #include "utils/TraceUtils.h" #include "utils/VectorDrawableUtils.h" -#include <math.h> -#include <string.h> - namespace android { namespace uirenderer { namespace VectorDrawable { @@ -129,8 +135,7 @@ const SkPath& FullPath::getUpdatedPath(bool useStagingData, SkPath* tempStagingP bool setFillPath = properties.getFillGradient() != nullptr || properties.getFillColor() != SK_ColorTRANSPARENT; if (setFillPath) { - SkPath::FillType ft = static_cast<SkPath::FillType>(properties.getFillType()); - outPath->setFillType(ft); + outPath->setFillType(static_cast<SkPathFillType>(properties.getFillType())); } return *outPath; } @@ -458,8 +463,12 @@ void Tree::drawStaging(Canvas* outCanvas) { mStagingCache.dirty = false; } - SkPaint paint; - getPaintFor(&paint, mStagingProperties); + SkPaint skp; + getPaintFor(&skp, mStagingProperties); + Paint paint; + paint.setFilterQuality(skp.getFilterQuality()); + paint.setColorFilter(skp.refColorFilter()); + paint.setAlpha(skp.getAlpha()); outCanvas->drawBitmap(*mStagingCache.bitmap, 0, 0, mStagingCache.bitmap->width(), mStagingCache.bitmap->height(), mStagingProperties.getBounds().left(), mStagingProperties.getBounds().top(), @@ -467,7 +476,7 @@ void Tree::drawStaging(Canvas* outCanvas) { mStagingProperties.getBounds().bottom(), &paint); } -void Tree::getPaintFor(SkPaint* outPaint, const TreeProperties &prop) const { +void Tree::getPaintFor(SkPaint* outPaint, const TreeProperties& prop) const { // HWUI always draws VD with bilinear filtering. outPaint->setFilterQuality(kLow_SkFilterQuality); if (prop.getColorFilter() != nullptr) { @@ -486,66 +495,6 @@ Bitmap& Tree::getBitmapUpdateIfDirty() { return *mCache.bitmap; } -void Tree::updateCache(sp<skiapipeline::VectorDrawableAtlas>& atlas, GrContext* context) { - SkRect dst; - sk_sp<SkSurface> surface = mCache.getSurface(&dst); - bool canReuseSurface = surface && dst.width() >= mProperties.getScaledWidth() && - dst.height() >= mProperties.getScaledHeight(); - if (!canReuseSurface) { - int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth()); - int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight()); - auto atlasEntry = atlas->requestNewEntry(scaledWidth, scaledHeight, context); - if (INVALID_ATLAS_KEY != atlasEntry.key) { - dst = atlasEntry.rect; - surface = atlasEntry.surface; - mCache.setAtlas(atlas, atlasEntry.key); - } else { - // don't draw, if we failed to allocate an offscreen buffer - mCache.clear(); - surface.reset(); - } - } - if (!canReuseSurface || mCache.dirty) { - if (surface) { - Bitmap& bitmap = getBitmapUpdateIfDirty(); - SkBitmap skiaBitmap; - bitmap.getSkBitmap(&skiaBitmap); - surface->writePixels(skiaBitmap, dst.fLeft, dst.fTop); - } - mCache.dirty = false; - } -} - -void Tree::Cache::setAtlas(sp<skiapipeline::VectorDrawableAtlas> newAtlas, - skiapipeline::AtlasKey newAtlasKey) { - LOG_ALWAYS_FATAL_IF(newAtlasKey == INVALID_ATLAS_KEY); - clear(); - mAtlas = newAtlas; - mAtlasKey = newAtlasKey; -} - -sk_sp<SkSurface> Tree::Cache::getSurface(SkRect* bounds) { - sk_sp<SkSurface> surface; - sp<skiapipeline::VectorDrawableAtlas> atlas = mAtlas.promote(); - if (atlas.get() && mAtlasKey != INVALID_ATLAS_KEY) { - auto atlasEntry = atlas->getEntry(mAtlasKey); - *bounds = atlasEntry.rect; - surface = atlasEntry.surface; - mAtlasKey = atlasEntry.key; - } - - return surface; -} - -void Tree::Cache::clear() { - sp<skiapipeline::VectorDrawableAtlas> lockAtlas = mAtlas.promote(); - if (lockAtlas.get()) { - lockAtlas->releaseEntry(mAtlasKey); - } - mAtlas = nullptr; - mAtlasKey = INVALID_ATLAS_KEY; -} - void Tree::draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& inPaint) { if (canvas->quickReject(bounds)) { // The RenderNode is on screen, but the AVD is not. @@ -556,39 +505,14 @@ void Tree::draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& inPaint) SkPaint paint = inPaint; paint.setAlpha(mProperties.getRootAlpha() * 255); - if (canvas->getGrContext() == nullptr) { - // Recording to picture, don't use the SkSurface which won't work off of renderthread. - Bitmap& bitmap = getBitmapUpdateIfDirty(); - SkBitmap skiaBitmap; - bitmap.getSkBitmap(&skiaBitmap); + Bitmap& bitmap = getBitmapUpdateIfDirty(); + SkBitmap skiaBitmap; + bitmap.getSkBitmap(&skiaBitmap); - int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth()); - int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight()); - canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds, - &paint, SkCanvas::kFast_SrcRectConstraint); - return; - } - - SkRect src; - sk_sp<SkSurface> vdSurface = mCache.getSurface(&src); - if (vdSurface) { - canvas->drawImageRect(vdSurface->makeImageSnapshot().get(), src, bounds, &paint, - SkCanvas::kFast_SrcRectConstraint); - } else { - // Handle the case when VectorDrawableAtlas has been destroyed, because of memory pressure. - // We render the VD into a temporary standalone buffer and mark the frame as dirty. Next - // frame will be cached into the atlas. - Bitmap& bitmap = getBitmapUpdateIfDirty(); - SkBitmap skiaBitmap; - bitmap.getSkBitmap(&skiaBitmap); - - int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth()); - int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight()); - canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds, - &paint, SkCanvas::kFast_SrcRectConstraint); - mCache.clear(); - markDirty(); - } + int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth()); + int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight()); + canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds, + &paint, SkCanvas::kFast_SrcRectConstraint); } void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) { diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h index 9c0bb161380c..e1b6f2adde74 100644 --- a/libs/hwui/VectorDrawable.h +++ b/libs/hwui/VectorDrawable.h @@ -651,46 +651,13 @@ public: void getPaintFor(SkPaint* outPaint, const TreeProperties &props) const; BitmapPalette computePalette(); - /** - * Draws VD into a GPU backed surface. - * This should always be called from RT and it works with Skia pipeline only. - */ - void updateCache(sp<skiapipeline::VectorDrawableAtlas>& atlas, GrContext* context); - void setAntiAlias(bool aa) { mRootNode->setAntiAlias(aa); } private: class Cache { public: sk_sp<Bitmap> bitmap; // used by HWUI pipeline and software - // TODO: use surface instead of bitmap when drawing in software canvas bool dirty = true; - - // the rest of the code in Cache is used by Skia pipelines only - - ~Cache() { clear(); } - - /** - * Stores a weak pointer to the atlas and a key. - */ - void setAtlas(sp<skiapipeline::VectorDrawableAtlas> atlas, - skiapipeline::AtlasKey newAtlasKey); - - /** - * Gets a surface and bounds from the atlas. - * - * @return nullptr if the altas has been deleted. - */ - sk_sp<SkSurface> getSurface(SkRect* bounds); - - /** - * Releases atlas key from the atlas, which makes it available for reuse. - */ - void clear(); - - private: - wp<skiapipeline::VectorDrawableAtlas> mAtlas; - skiapipeline::AtlasKey mAtlasKey = INVALID_ATLAS_KEY; }; bool allocateBitmapIfNeeded(Cache& cache, int width, int height); diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h index 2846cb1f087b..675b738c6406 100644 --- a/libs/hwui/WebViewFunctorManager.h +++ b/libs/hwui/WebViewFunctorManager.h @@ -17,7 +17,11 @@ #pragma once #include <private/hwui/WebViewFunctor.h> +#ifdef __ANDROID__ // Layoutlib does not support render thread #include <renderthread/RenderProxy.h> +#else +#include <utils/Log.h> +#endif #include <utils/LightRefBase.h> #include <mutex> @@ -34,7 +38,11 @@ public: class Handle : public LightRefBase<Handle> { public: - ~Handle() { renderthread::RenderProxy::destroyFunctor(id()); } + ~Handle() { +#ifdef __ANDROID__ // Layoutlib does not support render thread + renderthread::RenderProxy::destroyFunctor(id()); +#endif + } int id() const { return mReference.id(); } diff --git a/libs/hwui/apex/LayoutlibLoader.cpp b/libs/hwui/apex/LayoutlibLoader.cpp new file mode 100644 index 000000000000..4bbf1214bdcf --- /dev/null +++ b/libs/hwui/apex/LayoutlibLoader.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2020 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. + */ + +#include "graphics_jni_helpers.h" + +#include <GraphicsJNI.h> +#include <SkGraphics.h> + +#include <sstream> +#include <iostream> +#include <unicode/putil.h> +#include <unordered_map> +#include <vector> + +using namespace std; + +/* + * This is responsible for setting up the JNI environment for communication between + * the Java and native parts of layoutlib, including registering native methods. + * This is mostly achieved by copying the way it is done in the platform + * (see AndroidRuntime.cpp). + */ + +static JavaVM* javaVM; + +extern int register_android_graphics_Bitmap(JNIEnv*); +extern int register_android_graphics_BitmapFactory(JNIEnv*); +extern int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env); +extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env); +extern int register_android_graphics_Graphics(JNIEnv* env); +extern int register_android_graphics_ImageDecoder(JNIEnv*); +extern int register_android_graphics_Interpolator(JNIEnv* env); +extern int register_android_graphics_MaskFilter(JNIEnv* env); +extern int register_android_graphics_NinePatch(JNIEnv*); +extern int register_android_graphics_PathEffect(JNIEnv* env); +extern int register_android_graphics_Shader(JNIEnv* env); +extern int register_android_graphics_Typeface(JNIEnv* env); + +namespace android { + +extern int register_android_graphics_Canvas(JNIEnv* env); +extern int register_android_graphics_ColorFilter(JNIEnv* env); +extern int register_android_graphics_ColorSpace(JNIEnv* env); +extern int register_android_graphics_DrawFilter(JNIEnv* env); +extern int register_android_graphics_FontFamily(JNIEnv* env); +extern int register_android_graphics_Matrix(JNIEnv* env); +extern int register_android_graphics_Paint(JNIEnv* env); +extern int register_android_graphics_Path(JNIEnv* env); +extern int register_android_graphics_PathMeasure(JNIEnv* env); +extern int register_android_graphics_Picture(JNIEnv* env); +//extern int register_android_graphics_Region(JNIEnv* env); +extern int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env); +extern int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env); +extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env); +extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env); +extern int register_android_graphics_fonts_Font(JNIEnv* env); +extern int register_android_graphics_fonts_FontFamily(JNIEnv* env); +extern int register_android_graphics_text_LineBreaker(JNIEnv* env); +extern int register_android_graphics_text_MeasuredText(JNIEnv* env); +extern int register_android_util_PathParser(JNIEnv* env); +extern int register_android_view_RenderNode(JNIEnv* env); +extern int register_android_view_DisplayListCanvas(JNIEnv* env); + +#define REG_JNI(name) { name } +struct RegJNIRec { + int (*mProc)(JNIEnv*); +}; + +// Map of all possible class names to register to their corresponding JNI registration function pointer +// The actual list of registered classes will be determined at runtime via the 'native_classes' System property +static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = { + {"android.graphics.Bitmap", REG_JNI(register_android_graphics_Bitmap)}, + {"android.graphics.BitmapFactory", REG_JNI(register_android_graphics_BitmapFactory)}, + {"android.graphics.ByteBufferStreamAdaptor", + REG_JNI(register_android_graphics_ByteBufferStreamAdaptor)}, + {"android.graphics.Canvas", REG_JNI(register_android_graphics_Canvas)}, + {"android.graphics.RenderNode", REG_JNI(register_android_view_RenderNode)}, + {"android.graphics.ColorFilter", REG_JNI(register_android_graphics_ColorFilter)}, + {"android.graphics.ColorSpace", REG_JNI(register_android_graphics_ColorSpace)}, + {"android.graphics.CreateJavaOutputStreamAdaptor", + REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor)}, + {"android.graphics.DrawFilter", REG_JNI(register_android_graphics_DrawFilter)}, + {"android.graphics.FontFamily", REG_JNI(register_android_graphics_FontFamily)}, + {"android.graphics.Graphics", REG_JNI(register_android_graphics_Graphics)}, + {"android.graphics.ImageDecoder", REG_JNI(register_android_graphics_ImageDecoder)}, + {"android.graphics.Interpolator", REG_JNI(register_android_graphics_Interpolator)}, + {"android.graphics.MaskFilter", REG_JNI(register_android_graphics_MaskFilter)}, + {"android.graphics.Matrix", REG_JNI(register_android_graphics_Matrix)}, + {"android.graphics.NinePatch", REG_JNI(register_android_graphics_NinePatch)}, + {"android.graphics.Paint", REG_JNI(register_android_graphics_Paint)}, + {"android.graphics.Path", REG_JNI(register_android_graphics_Path)}, + {"android.graphics.PathEffect", REG_JNI(register_android_graphics_PathEffect)}, + {"android.graphics.PathMeasure", REG_JNI(register_android_graphics_PathMeasure)}, + {"android.graphics.Picture", REG_JNI(register_android_graphics_Picture)}, + {"android.graphics.RecordingCanvas", REG_JNI(register_android_view_DisplayListCanvas)}, +// {"android.graphics.Region", REG_JNI(register_android_graphics_Region)}, + {"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)}, + {"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)}, + {"android.graphics.animation.NativeInterpolatorFactory", + REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory)}, + {"android.graphics.animation.RenderNodeAnimator", + REG_JNI(register_android_graphics_animation_RenderNodeAnimator)}, + {"android.graphics.drawable.AnimatedVectorDrawable", + REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable)}, + {"android.graphics.drawable.VectorDrawable", + REG_JNI(register_android_graphics_drawable_VectorDrawable)}, + {"android.graphics.fonts.Font", REG_JNI(register_android_graphics_fonts_Font)}, + {"android.graphics.fonts.FontFamily", REG_JNI(register_android_graphics_fonts_FontFamily)}, + {"android.graphics.text.LineBreaker", REG_JNI(register_android_graphics_text_LineBreaker)}, + {"android.graphics.text.MeasuredText", + REG_JNI(register_android_graphics_text_MeasuredText)}, + {"android.util.PathParser", REG_JNI(register_android_util_PathParser)}, +}; + +static int register_jni_procs(const std::unordered_map<std::string, RegJNIRec>& jniRegMap, + const vector<string>& classesToRegister, JNIEnv* env) { + + for (const string& className : classesToRegister) { + if (jniRegMap.at(className).mProc(env) < 0) { + return -1; + } + } + return 0; +} + +static vector<string> parseCsv(const string& csvString) { + vector<string> result; + istringstream stream(csvString); + string segment; + while(getline(stream, segment, ',')) + { + result.push_back(segment); + } + return result; +} + +static vector<string> parseCsv(JNIEnv* env, jstring csvJString) { + const char* charArray = env->GetStringUTFChars(csvJString, 0); + string csvString(charArray); + vector<string> result = parseCsv(csvString); + env->ReleaseStringUTFChars(csvJString, charArray); + return result; +} + +} // namespace android + +using namespace android; + +void init_android_graphics() { + SkGraphics::Init(); +} + +int register_android_graphics_classes(JNIEnv *env) { + JavaVM* vm = nullptr; + env->GetJavaVM(&vm); + GraphicsJNI::setJavaVM(vm); + + // Configuration is stored as java System properties. + // Get a reference to System.getProperty + jclass system = FindClassOrDie(env, "java/lang/System"); + jmethodID getPropertyMethod = GetStaticMethodIDOrDie(env, system, "getProperty", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); + + // Get the names of classes that need to register their native methods + auto nativesClassesJString = + (jstring) env->CallStaticObjectMethod(system, + getPropertyMethod, env->NewStringUTF("native_classes"), + env->NewStringUTF("")); + vector<string> classesToRegister = parseCsv(env, nativesClassesJString); + + if (register_jni_procs(gRegJNIMap, classesToRegister, env) < 0) { + return JNI_ERR; + } + + return 0; +} + +void zygote_preload_graphics() { } diff --git a/libs/hwui/apex/TypeCast.h b/libs/hwui/apex/TypeCast.h new file mode 100644 index 000000000000..96721d007951 --- /dev/null +++ b/libs/hwui/apex/TypeCast.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2019 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. + */ + +#ifndef ANDROID_GRAPHICS_TYPECAST_H +#define ANDROID_GRAPHICS_TYPECAST_H + +struct ABitmap; +struct ACanvas; +struct APaint; + +namespace android { + + class Bitmap; + class Canvas; + class Paint; + + class TypeCast { + public: + static inline Bitmap& toBitmapRef(const ABitmap* bitmap) { + return const_cast<Bitmap&>(reinterpret_cast<const Bitmap&>(*bitmap)); + } + + static inline Bitmap* toBitmap(ABitmap* bitmap) { + return reinterpret_cast<Bitmap*>(bitmap); + } + + static inline ABitmap* toABitmap(Bitmap* bitmap) { + return reinterpret_cast<ABitmap*>(bitmap); + } + + static inline Canvas* toCanvas(ACanvas* canvas) { + return reinterpret_cast<Canvas*>(canvas); + } + + static inline ACanvas* toACanvas(Canvas* canvas) { + return reinterpret_cast<ACanvas *>(canvas); + } + + static inline const Paint& toPaintRef(const APaint* paint) { + return reinterpret_cast<const Paint&>(*paint); + } + + static inline const Paint* toPaint(const APaint* paint) { + return reinterpret_cast<const Paint*>(paint); + } + + static inline Paint* toPaint(APaint* paint) { + return reinterpret_cast<Paint*>(paint); + } + + static inline APaint* toAPaint(Paint* paint) { + return reinterpret_cast<APaint*>(paint); + } + }; +}; // namespace android + +#endif // ANDROID_GRAPHICS_TYPECAST_H diff --git a/libs/hwui/apex/android_bitmap.cpp b/libs/hwui/apex/android_bitmap.cpp new file mode 100644 index 000000000000..3780ba072308 --- /dev/null +++ b/libs/hwui/apex/android_bitmap.cpp @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2019 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. + */ + +#undef LOG_TAG +#define LOG_TAG "Bitmap" +#include <log/log.h> + +#include "android/graphics/bitmap.h" +#include "TypeCast.h" +#include "GraphicsJNI.h" + +#include <GraphicsJNI.h> +#include <hwui/Bitmap.h> +#include <utils/Color.h> + +using namespace android; + +ABitmap* ABitmap_acquireBitmapFromJava(JNIEnv* env, jobject bitmapObj) { + Bitmap* bitmap = GraphicsJNI::getNativeBitmap(env, bitmapObj); + if (bitmap) { + bitmap->ref(); + return TypeCast::toABitmap(bitmap); + } + return nullptr; +} + +void ABitmap_acquireRef(ABitmap* bitmap) { + SkSafeRef(TypeCast::toBitmap(bitmap)); +} + +void ABitmap_releaseRef(ABitmap* bitmap) { + SkSafeUnref(TypeCast::toBitmap(bitmap)); +} + +static AndroidBitmapFormat getFormat(const SkImageInfo& info) { + switch (info.colorType()) { + case kN32_SkColorType: + return ANDROID_BITMAP_FORMAT_RGBA_8888; + case kRGB_565_SkColorType: + return ANDROID_BITMAP_FORMAT_RGB_565; + case kARGB_4444_SkColorType: + return ANDROID_BITMAP_FORMAT_RGBA_4444; + case kAlpha_8_SkColorType: + return ANDROID_BITMAP_FORMAT_A_8; + case kRGBA_F16_SkColorType: + return ANDROID_BITMAP_FORMAT_RGBA_F16; + default: + return ANDROID_BITMAP_FORMAT_NONE; + } +} + +static SkColorType getColorType(AndroidBitmapFormat format) { + switch (format) { + case ANDROID_BITMAP_FORMAT_RGBA_8888: + return kN32_SkColorType; + case ANDROID_BITMAP_FORMAT_RGB_565: + return kRGB_565_SkColorType; + case ANDROID_BITMAP_FORMAT_RGBA_4444: + return kARGB_4444_SkColorType; + case ANDROID_BITMAP_FORMAT_A_8: + return kAlpha_8_SkColorType; + case ANDROID_BITMAP_FORMAT_RGBA_F16: + return kRGBA_F16_SkColorType; + default: + return kUnknown_SkColorType; + } +} + +static uint32_t getAlphaFlags(const SkImageInfo& info) { + switch (info.alphaType()) { + case kUnknown_SkAlphaType: + LOG_ALWAYS_FATAL("Bitmap has no alpha type"); + break; + case kOpaque_SkAlphaType: + return ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE; + case kPremul_SkAlphaType: + return ANDROID_BITMAP_FLAGS_ALPHA_PREMUL; + case kUnpremul_SkAlphaType: + return ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL; + } +} + +static uint32_t getInfoFlags(const SkImageInfo& info, bool isHardware) { + uint32_t flags = getAlphaFlags(info); + if (isHardware) { + flags |= ANDROID_BITMAP_FLAGS_IS_HARDWARE; + } + return flags; +} + +ABitmap* ABitmap_copy(ABitmap* srcBitmapHandle, AndroidBitmapFormat dstFormat) { + SkColorType dstColorType = getColorType(dstFormat); + if (srcBitmapHandle && dstColorType != kUnknown_SkColorType) { + SkBitmap srcBitmap; + TypeCast::toBitmap(srcBitmapHandle)->getSkBitmap(&srcBitmap); + + sk_sp<Bitmap> dstBitmap = + Bitmap::allocateHeapBitmap(srcBitmap.info().makeColorType(dstColorType)); + if (dstBitmap && srcBitmap.readPixels(dstBitmap->info(), dstBitmap->pixels(), + dstBitmap->rowBytes(), 0, 0)) { + return TypeCast::toABitmap(dstBitmap.release()); + } + } + return nullptr; +} + +static AndroidBitmapInfo getInfo(const SkImageInfo& imageInfo, uint32_t rowBytes, bool isHardware) { + AndroidBitmapInfo info; + info.width = imageInfo.width(); + info.height = imageInfo.height(); + info.stride = rowBytes; + info.format = getFormat(imageInfo); + info.flags = getInfoFlags(imageInfo, isHardware); + return info; +} + +AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmapHandle) { + Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle); + return getInfo(bitmap->info(), bitmap->rowBytes(), bitmap->isHardware()); +} + +ADataSpace ABitmap_getDataSpace(ABitmap* bitmapHandle) { + Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle); + const SkImageInfo& info = bitmap->info(); + return (ADataSpace)uirenderer::ColorSpaceToADataSpace(info.colorSpace(), info.colorType()); +} + +AndroidBitmapInfo ABitmap_getInfoFromJava(JNIEnv* env, jobject bitmapObj) { + uint32_t rowBytes = 0; + bool isHardware = false; + SkImageInfo imageInfo = GraphicsJNI::getBitmapInfo(env, bitmapObj, &rowBytes, &isHardware); + return getInfo(imageInfo, rowBytes, isHardware); +} + +void* ABitmap_getPixels(ABitmap* bitmapHandle) { + Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle); + if (bitmap->isHardware()) { + return nullptr; + } + return bitmap->pixels(); +} + +AndroidBitmapFormat ABitmapConfig_getFormatFromConfig(JNIEnv* env, jobject bitmapConfigObj) { + return GraphicsJNI::getFormatFromConfig(env, bitmapConfigObj); +} + +jobject ABitmapConfig_getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format) { + return GraphicsJNI::getConfigFromFormat(env, format); +} + +void ABitmap_notifyPixelsChanged(ABitmap* bitmapHandle) { + Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle); + if (!bitmap->isImmutable()) { + bitmap->notifyPixelsChanged(); + } +} + +namespace { +SkAlphaType getAlphaType(const AndroidBitmapInfo* info) { + switch (info->flags & ANDROID_BITMAP_FLAGS_ALPHA_MASK) { + case ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE: + return kOpaque_SkAlphaType; + case ANDROID_BITMAP_FLAGS_ALPHA_PREMUL: + return kPremul_SkAlphaType; + case ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL: + return kUnpremul_SkAlphaType; + default: + return kUnknown_SkAlphaType; + } +} + +class CompressWriter : public SkWStream { +public: + CompressWriter(void* userContext, AndroidBitmap_CompressWriteFunc fn) + : mUserContext(userContext), mFn(fn), mBytesWritten(0) {} + + bool write(const void* buffer, size_t size) override { + if (mFn(mUserContext, buffer, size)) { + mBytesWritten += size; + return true; + } + return false; + } + + size_t bytesWritten() const override { return mBytesWritten; } + +private: + void* mUserContext; + AndroidBitmap_CompressWriteFunc mFn; + size_t mBytesWritten; +}; + +} // anonymous namespace + +int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const void* pixels, + AndroidBitmapCompressFormat inFormat, int32_t quality, void* userContext, + AndroidBitmap_CompressWriteFunc fn) { + Bitmap::JavaCompressFormat format; + switch (inFormat) { + case ANDROID_BITMAP_COMPRESS_FORMAT_JPEG: + format = Bitmap::JavaCompressFormat::Jpeg; + break; + case ANDROID_BITMAP_COMPRESS_FORMAT_PNG: + format = Bitmap::JavaCompressFormat::Png; + break; + case ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSY: + format = Bitmap::JavaCompressFormat::WebpLossy; + break; + case ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSLESS: + format = Bitmap::JavaCompressFormat::WebpLossless; + break; + default: + // kWEBP_JavaEncodeFormat is a valid parameter for Bitmap::compress, + // for the deprecated Bitmap.CompressFormat.WEBP, but it should not + // be provided via the NDK. Other integers are likewise invalid. + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + + SkColorType colorType; + switch (info->format) { + case ANDROID_BITMAP_FORMAT_RGBA_8888: + colorType = kN32_SkColorType; + break; + case ANDROID_BITMAP_FORMAT_RGB_565: + colorType = kRGB_565_SkColorType; + break; + case ANDROID_BITMAP_FORMAT_A_8: + // FIXME b/146637821: Should this encode as grayscale? We should + // make the same decision as for encoding an android.graphics.Bitmap. + // Note that encoding kAlpha_8 as WebP or JPEG will fail. Encoding + // it to PNG encodes as GRAY+ALPHA with a secret handshake that we + // only care about the alpha. I'm not sure whether Android decoding + // APIs respect that handshake. + colorType = kAlpha_8_SkColorType; + break; + case ANDROID_BITMAP_FORMAT_RGBA_F16: + colorType = kRGBA_F16_SkColorType; + break; + default: + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + + auto alphaType = getAlphaType(info); + if (alphaType == kUnknown_SkAlphaType) { + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + + sk_sp<SkColorSpace> cs; + if (info->format == ANDROID_BITMAP_FORMAT_A_8) { + // FIXME: A Java Bitmap with ALPHA_8 never has a ColorSpace. So should + // we force that here (as I'm doing now) or should we treat anything + // besides ADATASPACE_UNKNOWN as an error? + cs = nullptr; + } else { + cs = uirenderer::DataSpaceToColorSpace((android_dataspace) dataSpace); + // DataSpaceToColorSpace treats UNKNOWN as SRGB, but compress forces the + // client to specify SRGB if that is what they want. + if (!cs || dataSpace == ADATASPACE_UNKNOWN) { + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + } + + { + size_t size; + if (!Bitmap::computeAllocationSize(info->stride, info->height, &size)) { + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + } + + auto imageInfo = + SkImageInfo::Make(info->width, info->height, colorType, alphaType, std::move(cs)); + SkBitmap bitmap; + // We are not going to modify the pixels, but installPixels expects them to + // not be const, since for all it knows we might want to draw to the SkBitmap. + if (!bitmap.installPixels(imageInfo, const_cast<void*>(pixels), info->stride)) { + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + + CompressWriter stream(userContext, fn); + return Bitmap::compress(bitmap, format, quality, &stream) ? ANDROID_BITMAP_RESULT_SUCCESS + : ANDROID_BITMAP_RESULT_JNI_EXCEPTION; +} + +AHardwareBuffer* ABitmap_getHardwareBuffer(ABitmap* bitmapHandle) { + Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle); + AHardwareBuffer* buffer = bitmap->hardwareBuffer(); + if (buffer) { + AHardwareBuffer_acquire(buffer); + } + return buffer; +} diff --git a/libs/hwui/apex/android_canvas.cpp b/libs/hwui/apex/android_canvas.cpp new file mode 100644 index 000000000000..2a939efed9bb --- /dev/null +++ b/libs/hwui/apex/android_canvas.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2019 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. + */ + +#include "android/graphics/canvas.h" + +#include "TypeCast.h" +#include "GraphicsJNI.h" + +#include <hwui/Canvas.h> +#include <utils/Color.h> + +#include <SkBitmap.h> +#include <SkSurface.h> + +using namespace android; + +/* + * Converts a buffer and dataspace into an SkBitmap only if the resulting bitmap can be treated as a + * rendering destination for a Canvas. If the buffer is null or the format is one that we cannot + * render into with a Canvas then false is returned and the outBitmap param is unmodified. + */ +static bool convert(const ANativeWindow_Buffer* buffer, + int32_t /*android_dataspace_t*/ dataspace, + SkBitmap* outBitmap) { + if (buffer == nullptr) { + return false; + } + + sk_sp<SkColorSpace> cs(uirenderer::DataSpaceToColorSpace((android_dataspace)dataspace)); + SkImageInfo imageInfo = uirenderer::ANativeWindowToImageInfo(*buffer, cs); + size_t rowBytes = buffer->stride * imageInfo.bytesPerPixel(); + + // If SkSurface::MakeRasterDirect fails then we should as well as we will not be able to + // draw into the canvas. + sk_sp<SkSurface> surface = SkSurface::MakeRasterDirect(imageInfo, buffer->bits, rowBytes); + if (surface.get() != nullptr) { + if (outBitmap) { + outBitmap->setInfo(imageInfo, rowBytes); + outBitmap->setPixels(buffer->bits); + } + return true; + } + return false; +} + +bool ACanvas_isSupportedPixelFormat(int32_t bufferFormat) { + char pixels[8]; + ANativeWindow_Buffer buffer { 1, 1, 1, bufferFormat, pixels, {0} }; + return convert(&buffer, HAL_DATASPACE_UNKNOWN, nullptr); +} + +ACanvas* ACanvas_getNativeHandleFromJava(JNIEnv* env, jobject canvasObj) { + return TypeCast::toACanvas(GraphicsJNI::getNativeCanvas(env, canvasObj)); +} + +ACanvas* ACanvas_createCanvas(const ANativeWindow_Buffer* buffer, + int32_t /*android_dataspace_t*/ dataspace) { + SkBitmap bitmap; + bool isValidBuffer = convert(buffer, dataspace, &bitmap); + return isValidBuffer ? TypeCast::toACanvas(Canvas::create_canvas(bitmap)) : nullptr; +} + +void ACanvas_destroyCanvas(ACanvas* canvas) { + delete TypeCast::toCanvas(canvas); +} + +bool ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer, + int32_t /*android_dataspace_t*/ dataspace) { + SkBitmap bitmap; + bool isValidBuffer = (buffer == nullptr) ? false : convert(buffer, dataspace, &bitmap); + TypeCast::toCanvas(canvas)->setBitmap(bitmap); + return isValidBuffer; +} + +void ACanvas_clipRect(ACanvas* canvas, const ARect* clipRect, bool /*doAA*/) { + //TODO update Canvas to take antialias param + TypeCast::toCanvas(canvas)->clipRect(clipRect->left, clipRect->top, clipRect->right, + clipRect->bottom, SkClipOp::kIntersect); +} + +void ACanvas_clipOutRect(ACanvas* canvas, const ARect* clipRect, bool /*doAA*/) { + //TODO update Canvas to take antialias param + TypeCast::toCanvas(canvas)->clipRect(clipRect->left, clipRect->top, clipRect->right, + clipRect->bottom, SkClipOp::kDifference); +} + +void ACanvas_drawRect(ACanvas* canvas, const ARect* rect, const APaint* paint) { + TypeCast::toCanvas(canvas)->drawRect(rect->left, rect->top, rect->right, rect->bottom, + TypeCast::toPaintRef(paint)); +} + +void ACanvas_drawBitmap(ACanvas* canvas, const ABitmap* bitmap, float left, float top, + const APaint* paint) { + TypeCast::toCanvas(canvas)->drawBitmap(TypeCast::toBitmapRef(bitmap), left, top, + TypeCast::toPaint(paint)); +} diff --git a/libs/hwui/apex/android_matrix.cpp b/libs/hwui/apex/android_matrix.cpp new file mode 100644 index 000000000000..693b22b62663 --- /dev/null +++ b/libs/hwui/apex/android_matrix.cpp @@ -0,0 +1,37 @@ +/* + * Copyright 2020 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. + */ + +#include "android/graphics/matrix.h" +#include "android_graphics_Matrix.h" + +bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]) { + static_assert(SkMatrix::kMScaleX == 0, "SkMatrix unexpected index"); + static_assert(SkMatrix::kMSkewX == 1, "SkMatrix unexpected index"); + static_assert(SkMatrix::kMTransX == 2, "SkMatrix unexpected index"); + static_assert(SkMatrix::kMSkewY == 3, "SkMatrix unexpected index"); + static_assert(SkMatrix::kMScaleY == 4, "SkMatrix unexpected index"); + static_assert(SkMatrix::kMTransY == 5, "SkMatrix unexpected index"); + static_assert(SkMatrix::kMPersp0 == 6, "SkMatrix unexpected index"); + static_assert(SkMatrix::kMPersp1 == 7, "SkMatrix unexpected index"); + static_assert(SkMatrix::kMPersp2 == 8, "SkMatrix unexpected index"); + + SkMatrix* m = android::android_graphics_Matrix_getSkMatrix(env, matrixObj); + if (m != nullptr) { + m->get9(values); + return true; + } + return false; +} diff --git a/libs/hwui/apex/android_paint.cpp b/libs/hwui/apex/android_paint.cpp new file mode 100644 index 000000000000..70bd085343ce --- /dev/null +++ b/libs/hwui/apex/android_paint.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019 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. + */ + +#include "android/graphics/paint.h" + +#include "TypeCast.h" + +#include <hwui/Paint.h> + +using namespace android; + + +APaint* APaint_createPaint() { + return TypeCast::toAPaint(new Paint()); +} + +void APaint_destroyPaint(APaint* paint) { + delete TypeCast::toPaint(paint); +} + +static SkBlendMode convertBlendMode(ABlendMode blendMode) { + switch (blendMode) { + case ABLEND_MODE_CLEAR: + return SkBlendMode::kClear; + case ABLEND_MODE_SRC_OVER: + return SkBlendMode::kSrcOver; + case ABLEND_MODE_SRC: + return SkBlendMode::kSrc; + } +} + +void APaint_setBlendMode(APaint* paint, ABlendMode blendMode) { + TypeCast::toPaint(paint)->setBlendMode(convertBlendMode(blendMode)); +} diff --git a/libs/hwui/apex/android_region.cpp b/libs/hwui/apex/android_region.cpp new file mode 100644 index 000000000000..2030e7e69861 --- /dev/null +++ b/libs/hwui/apex/android_region.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 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. + */ + +#include "android/graphics/region.h" + +#include "GraphicsJNI.h" + +#include <SkRegion.h> + +static inline SkRegion::Iterator* ARegionIter_to_SkRegionIter(ARegionIterator* iterator) { + return reinterpret_cast<SkRegion::Iterator*>(iterator); +} + +static inline ARegionIterator* SkRegionIter_to_ARegionIter(SkRegion::Iterator* iterator) { + return reinterpret_cast<ARegionIterator*>(iterator); +} + +ARegionIterator* ARegionIterator_acquireIterator(JNIEnv* env, jobject regionObj) { + SkRegion* region = GraphicsJNI::getNativeRegion(env, regionObj); + return (!region) ? nullptr : SkRegionIter_to_ARegionIter(new SkRegion::Iterator(*region)); +} + +void ARegionIterator_releaseIterator(ARegionIterator* iterator) { + delete ARegionIter_to_SkRegionIter(iterator); +} + +bool ARegionIterator_isComplex(ARegionIterator* iterator) { + return ARegionIter_to_SkRegionIter(iterator)->rgn()->isComplex(); +} + +bool ARegionIterator_isDone(ARegionIterator* iterator) { + return ARegionIter_to_SkRegionIter(iterator)->done(); +} + +void ARegionIterator_next(ARegionIterator* iterator) { + ARegionIter_to_SkRegionIter(iterator)->next(); +} + +ARect ARegionIterator_getRect(ARegionIterator* iterator) { + const SkIRect& rect = ARegionIter_to_SkRegionIter(iterator)->rect(); + return { rect.fLeft, rect.fTop, rect.fRight, rect.fBottom }; +} + +ARect ARegionIterator_getTotalBounds(ARegionIterator* iterator) { + const SkIRect& bounds = ARegionIter_to_SkRegionIter(iterator)->rgn()->getBounds(); + return { bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom }; +} diff --git a/libs/hwui/apex/include/android/graphics/bitmap.h b/libs/hwui/apex/include/android/graphics/bitmap.h new file mode 100644 index 000000000000..8c4b439d2a2b --- /dev/null +++ b/libs/hwui/apex/include/android/graphics/bitmap.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2019 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. + */ +#ifndef ANDROID_GRAPHICS_BITMAP_H +#define ANDROID_GRAPHICS_BITMAP_H + +#include <android/bitmap.h> +#include <android/data_space.h> +#include <cutils/compiler.h> +#include <jni.h> +#include <sys/cdefs.h> + +struct AHardwareBuffer; + +__BEGIN_DECLS + +/** + * Opaque handle for a native graphics bitmap. + */ +typedef struct ABitmap ABitmap; + +/** + * Retrieve bitmapInfo for the provided java bitmap even if it has been recycled. In the case of a + * recycled bitmap the values contained in the bitmap before it was recycled are returned. + * + * NOTE: This API does not need to remain as an APEX API if/when we pull libjnigraphics into the + * UI module. + */ +ANDROID_API AndroidBitmapInfo ABitmap_getInfoFromJava(JNIEnv* env, jobject bitmapObj); + +/** + * + * @return ptr to an opaque handle to the native bitmap or null if the java bitmap has been recycled + * or does not exist. + */ +ANDROID_API ABitmap* ABitmap_acquireBitmapFromJava(JNIEnv* env, jobject bitmapObj); + +ANDROID_API ABitmap* ABitmap_copy(ABitmap* srcBitmap, AndroidBitmapFormat dstFormat); + +ANDROID_API void ABitmap_acquireRef(ABitmap* bitmap); +ANDROID_API void ABitmap_releaseRef(ABitmap* bitmap); + +ANDROID_API AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmap); +ANDROID_API ADataSpace ABitmap_getDataSpace(ABitmap* bitmap); + +ANDROID_API void* ABitmap_getPixels(ABitmap* bitmap); +ANDROID_API void ABitmap_notifyPixelsChanged(ABitmap* bitmap); + +ANDROID_API AndroidBitmapFormat ABitmapConfig_getFormatFromConfig(JNIEnv* env, jobject bitmapConfigObj); +ANDROID_API jobject ABitmapConfig_getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format); + +// NDK access +ANDROID_API int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const void* pixels, + AndroidBitmapCompressFormat format, int32_t quality, void* userContext, + AndroidBitmap_CompressWriteFunc); +/** + * Retrieve the native object associated with a HARDWARE Bitmap. + * + * Client must not modify it while a Bitmap is wrapping it. + * + * @param bitmap Handle to an android.graphics.Bitmap. + * @return on success, a pointer to the + * AHardwareBuffer associated with bitmap. This acquires + * a reference on the buffer, and the client must call + * AHardwareBuffer_release when finished with it. + */ +ANDROID_API AHardwareBuffer* ABitmap_getHardwareBuffer(ABitmap* bitmap); + +__END_DECLS + +#ifdef __cplusplus +namespace android { +namespace graphics { + class Bitmap { + public: + Bitmap() : mBitmap(nullptr) {} + Bitmap(JNIEnv* env, jobject bitmapObj) : + mBitmap(ABitmap_acquireBitmapFromJava(env, bitmapObj)) {} + Bitmap(const Bitmap& src) : mBitmap(src.mBitmap) { ABitmap_acquireRef(src.mBitmap); } + ~Bitmap() { ABitmap_releaseRef(mBitmap); } + + // copy operator + Bitmap& operator=(const Bitmap& other) { + if (&other != this) { + ABitmap_releaseRef(mBitmap); + mBitmap = other.mBitmap; + ABitmap_acquireRef(mBitmap); + } + return *this; + } + + // move operator + Bitmap& operator=(Bitmap&& other) { + if (&other != this) { + ABitmap_releaseRef(mBitmap); + mBitmap = other.mBitmap; + other.mBitmap = nullptr; + } + return *this; + } + + Bitmap copy(AndroidBitmapFormat dstFormat) const { + return Bitmap(ABitmap_copy(mBitmap, dstFormat)); + } + + bool isValid() const { return mBitmap != nullptr; } + bool isEmpty() const { + AndroidBitmapInfo info = getInfo(); + return info.width <= 0 || info.height <= 0; + } + void reset() { + ABitmap_releaseRef(mBitmap); + mBitmap = nullptr; + } + + ABitmap* get() const { return mBitmap; } + + AndroidBitmapInfo getInfo() const { return ABitmap_getInfo(mBitmap); } + ADataSpace getDataSpace() const { return ABitmap_getDataSpace(mBitmap); } + void* getPixels() const { return ABitmap_getPixels(mBitmap); } + void notifyPixelsChanged() const { ABitmap_notifyPixelsChanged(mBitmap); } + AHardwareBuffer* getHardwareBuffer() const { return ABitmap_getHardwareBuffer(mBitmap); } + + private: + // takes ownership of the provided ABitmap + Bitmap(ABitmap* bitmap) : mBitmap(bitmap) {} + + ABitmap* mBitmap; + }; +}; // namespace graphics +}; // namespace android +#endif // __cplusplus + +#endif // ANDROID_GRAPHICS_BITMAP_H diff --git a/libs/hwui/apex/include/android/graphics/canvas.h b/libs/hwui/apex/include/android/graphics/canvas.h new file mode 100644 index 000000000000..a0cecc04a7e5 --- /dev/null +++ b/libs/hwui/apex/include/android/graphics/canvas.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2019 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. + */ +#ifndef ANDROID_GRAPHICS_CANVAS_H +#define ANDROID_GRAPHICS_CANVAS_H + +#include <android/graphics/bitmap.h> +#include <android/graphics/paint.h> +#include <android/native_window.h> +#include <android/rect.h> +#include <cutils/compiler.h> +#include <jni.h> + +__BEGIN_DECLS + +/** + * Opaque handle for a native graphics canvas. + */ +typedef struct ACanvas ACanvas; + +// One of AHardwareBuffer_Format. +ANDROID_API bool ACanvas_isSupportedPixelFormat(int32_t bufferFormat); + +/** + * Returns a native handle to a Java android.graphics.Canvas + * + * @return ACanvas* that is only valid for the life of the jobject. + */ +ANDROID_API ACanvas* ACanvas_getNativeHandleFromJava(JNIEnv* env, jobject canvas); + +/** + * Creates a canvas that wraps the buffer + * + * @param buffer is a required param. If no buffer is provided a nullptr will be returned. + */ +ANDROID_API ACanvas* ACanvas_createCanvas(const ANativeWindow_Buffer* buffer, + int32_t /*android_dataspace_t*/ dataspace); + +ANDROID_API void ACanvas_destroyCanvas(ACanvas* canvas); + +/** + * Updates the canvas to render into the pixels in the provided buffer + * + * @param buffer The buffer that will provide the backing store for this canvas. The buffer must + * remain valid until the this method is called again with either another active + * buffer or nullptr. If nullptr is given the canvas will release the previous buffer + * and set an empty backing store. + * @return A boolean value indicating whether or not the buffer was successfully set. If false the + * method will behave as if nullptr were passed as the input buffer and the previous buffer + * will still be released. + */ +ANDROID_API bool ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer, + int32_t /*android_dataspace_t*/ dataspace); + +/** + * Clips operations on the canvas to the intersection of the current clip and the provided clipRect. + * + * @param clipRect required + */ +ANDROID_API void ACanvas_clipRect(ACanvas* canvas, const ARect* clipRect, bool doAntiAlias = false); + +/** + * Clips operations on the canvas to the difference of the current clip and the provided clipRect. + * + * @param clipRect required + */ +ANDROID_API void ACanvas_clipOutRect(ACanvas* canvas, const ARect* clipRect, bool doAntiAlias = false); + +/** + * + * @param rect required + * @param paint required + */ +ANDROID_API void ACanvas_drawRect(ACanvas* canvas, const ARect* rect, const APaint* paint); + +/** + * + * @param bitmap required + * @param left + * @param top + * @param paint + */ +ANDROID_API void ACanvas_drawBitmap(ACanvas* canvas, const ABitmap* bitmap, float left, float top, + const APaint* paint); + +__END_DECLS + +#ifdef __cplusplus +namespace android { +namespace graphics { + class Canvas { + public: + Canvas(JNIEnv* env, jobject canvasObj) : + mCanvas(ACanvas_getNativeHandleFromJava(env, canvasObj)), + mOwnedPtr(false) {} + Canvas(const ANativeWindow_Buffer& buffer, int32_t /*android_dataspace_t*/ dataspace) : + mCanvas(ACanvas_createCanvas(&buffer, dataspace)), + mOwnedPtr(true) {} + ~Canvas() { + if (mOwnedPtr) { + ACanvas_destroyCanvas(mCanvas); + } + } + + bool setBuffer(const ANativeWindow_Buffer* buffer, + int32_t /*android_dataspace_t*/ dataspace) { + return ACanvas_setBuffer(mCanvas, buffer, dataspace); + } + + void clipRect(const ARect& clipRect, bool doAntiAlias = false) { + ACanvas_clipRect(mCanvas, &clipRect, doAntiAlias); + } + + void drawRect(const ARect& rect, const Paint& paint) { + ACanvas_drawRect(mCanvas, &rect, &paint.get()); + } + void drawBitmap(const Bitmap& bitmap, float left, float top, const Paint* paint) { + const APaint* aPaint = (paint) ? &paint->get() : nullptr; + ACanvas_drawBitmap(mCanvas, bitmap.get(), left, top, aPaint); + } + + private: + ACanvas* mCanvas; + const bool mOwnedPtr; + }; +}; // namespace graphics +}; // namespace android +#endif // __cplusplus + +#endif // ANDROID_GRAPHICS_CANVAS_H
\ No newline at end of file diff --git a/libs/hwui/apex/include/android/graphics/jni_runtime.h b/libs/hwui/apex/include/android/graphics/jni_runtime.h new file mode 100644 index 000000000000..487383ed50d5 --- /dev/null +++ b/libs/hwui/apex/include/android/graphics/jni_runtime.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2019 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. + */ +#ifndef ANDROID_GRAPHICS_JNI_RUNTIME_H +#define ANDROID_GRAPHICS_JNI_RUNTIME_H + +#include <cutils/compiler.h> +#include <jni.h> + +__BEGIN_DECLS + +ANDROID_API void init_android_graphics(); + +ANDROID_API int register_android_graphics_classes(JNIEnv* env); + +ANDROID_API int register_android_graphics_GraphicsStatsService(JNIEnv* env); + +ANDROID_API void zygote_preload_graphics(); + +__END_DECLS + + +#endif // ANDROID_GRAPHICS_JNI_RUNTIME_H
\ No newline at end of file diff --git a/libs/hwui/apex/include/android/graphics/matrix.h b/libs/hwui/apex/include/android/graphics/matrix.h new file mode 100644 index 000000000000..987ad13f7635 --- /dev/null +++ b/libs/hwui/apex/include/android/graphics/matrix.h @@ -0,0 +1,39 @@ +/* + * Copyright 2020 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. + */ + +#ifndef ANDROID_GRAPHICS_MATRIX_H +#define ANDROID_GRAPHICS_MATRIX_H + +#include <jni.h> +#include <cutils/compiler.h> +#include <sys/cdefs.h> + +__BEGIN_DECLS + +/** + * Returns an array of floats that represents the 3x3 matrix of the java object. + * @param values The 9 values of the 3x3 matrix in the following order. + * values[0] = scaleX values[1] = skewX values[2] = transX + * values[3] = skewY values[4] = scaleY values[5] = transY + * values[6] = persp0 values[7] = persp1 values[8] = persp2 + * @return true if the values param was populated and false otherwise. + + */ +ANDROID_API bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]); + +__END_DECLS + +#endif // ANDROID_GRAPHICS_MATRIX_H diff --git a/libs/hwui/apex/include/android/graphics/paint.h b/libs/hwui/apex/include/android/graphics/paint.h new file mode 100644 index 000000000000..058db8d37619 --- /dev/null +++ b/libs/hwui/apex/include/android/graphics/paint.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2019 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. + */ +#ifndef ANDROID_GRAPHICS_PAINT_H +#define ANDROID_GRAPHICS_PAINT_H + +#include <cutils/compiler.h> +#include <sys/cdefs.h> + +__BEGIN_DECLS + +/** + * Opaque handle for a native graphics canvas. + */ +typedef struct APaint APaint; + +/** Bitmap pixel format. */ +enum ABlendMode { + /** replaces destination with zero: fully transparent */ + ABLEND_MODE_CLEAR = 0, + /** source over destination */ + ABLEND_MODE_SRC_OVER = 1, + /** replaces destination **/ + ABLEND_MODE_SRC = 2, +}; + +ANDROID_API APaint* APaint_createPaint(); + +ANDROID_API void APaint_destroyPaint(APaint* paint); + +ANDROID_API void APaint_setBlendMode(APaint* paint, ABlendMode blendMode); + +__END_DECLS + +#ifdef __cplusplus +namespace android { +namespace graphics { + class Paint { + public: + Paint() : mPaint(APaint_createPaint()) {} + ~Paint() { APaint_destroyPaint(mPaint); } + + void setBlendMode(ABlendMode blendMode) { APaint_setBlendMode(mPaint, blendMode); } + + const APaint& get() const { return *mPaint; } + + private: + APaint* mPaint; + }; +}; // namespace graphics +}; // namespace android +#endif // __cplusplus + + +#endif // ANDROID_GRAPHICS_PAINT_H
\ No newline at end of file diff --git a/libs/hwui/apex/include/android/graphics/region.h b/libs/hwui/apex/include/android/graphics/region.h new file mode 100644 index 000000000000..0756d6dd63f6 --- /dev/null +++ b/libs/hwui/apex/include/android/graphics/region.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2019 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. + */ +#ifndef ANDROID_GRAPHICS_REGION_H +#define ANDROID_GRAPHICS_REGION_H + +#include <cutils/compiler.h> +#include <android/rect.h> +#include <sys/cdefs.h> +#include <jni.h> + +__BEGIN_DECLS + +/** +* Opaque handle for a native graphics region iterator. +*/ +typedef struct ARegionIterator ARegionIterator; + +/** + * Returns a iterator for a Java android.graphics.Region + * + * @param env + * @param region + * @return ARegionIterator that must be closed and must not live longer than the life + * of the jobject. It returns nullptr if the region is not a valid object. + */ +ANDROID_API ARegionIterator* ARegionIterator_acquireIterator(JNIEnv* env, jobject region); + +ANDROID_API void ARegionIterator_releaseIterator(ARegionIterator* iterator); + +ANDROID_API bool ARegionIterator_isComplex(ARegionIterator* iterator); + +ANDROID_API bool ARegionIterator_isDone(ARegionIterator* iterator); + +ANDROID_API void ARegionIterator_next(ARegionIterator* iterator); + +ANDROID_API ARect ARegionIterator_getRect(ARegionIterator* iterator); + +ANDROID_API ARect ARegionIterator_getTotalBounds(ARegionIterator* iterator); + +__END_DECLS + +#ifdef __cplusplus +namespace android { +namespace graphics { + class RegionIterator { + public: + RegionIterator(JNIEnv* env, jobject region) + : mIterator(ARegionIterator_acquireIterator(env, region)) {} + ~RegionIterator() { ARegionIterator_releaseIterator(mIterator); } + + bool isValid() const { return mIterator != nullptr; } + bool isComplex() { return ARegionIterator_isComplex(mIterator); } + bool isDone() { return ARegionIterator_isDone(mIterator); } + void next() { ARegionIterator_next(mIterator); } + ARect getRect() { return ARegionIterator_getRect(mIterator); } + ARect getTotalBounds() const { return ARegionIterator_getTotalBounds(mIterator); } + private: + ARegionIterator* mIterator; + }; +}; // namespace graphics +}; // namespace android + +#endif // __cplusplus +#endif // ANDROID_GRAPHICS_REGION_H
\ No newline at end of file diff --git a/libs/hwui/apex/include/android/graphics/renderthread.h b/libs/hwui/apex/include/android/graphics/renderthread.h new file mode 100644 index 000000000000..50280a6dd1fb --- /dev/null +++ b/libs/hwui/apex/include/android/graphics/renderthread.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 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. + */ +#ifndef ANDROID_GRAPHICS_RENDERTHREAD_H +#define ANDROID_GRAPHICS_RENDERTHREAD_H + +#include <cutils/compiler.h> +#include <sys/cdefs.h> + +__BEGIN_DECLS + +/** + * Dumps a textual representation of the graphics stats for this process. + * @param fd The file descriptor that the available graphics stats will be appended to. The + * function requires a valid fd, but does not persist or assume ownership of the fd + * outside the scope of this function. + */ +ANDROID_API void ARenderThread_dumpGraphicsMemory(int fd); + +__END_DECLS + +#endif // ANDROID_GRAPHICS_RENDERTHREAD_H diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp new file mode 100644 index 000000000000..a114e2f42157 --- /dev/null +++ b/libs/hwui/apex/jni_runtime.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2019 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. + */ + +#include "android/graphics/jni_runtime.h" + +#include <android/log.h> +#include <nativehelper/JNIHelp.h> +#include <sys/cdefs.h> + +#include <EGL/egl.h> +#include <GraphicsJNI.h> +#include <Properties.h> +#include <SkGraphics.h> + +#undef LOG_TAG +#define LOG_TAG "AndroidGraphicsJNI" + +extern int register_android_graphics_Bitmap(JNIEnv*); +extern int register_android_graphics_BitmapFactory(JNIEnv*); +extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*); +extern int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env); +extern int register_android_graphics_Camera(JNIEnv* env); +extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env); +extern int register_android_graphics_Graphics(JNIEnv* env); +extern int register_android_graphics_ImageDecoder(JNIEnv*); +extern int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv*); +extern int register_android_graphics_Interpolator(JNIEnv* env); +extern int register_android_graphics_MaskFilter(JNIEnv* env); +extern int register_android_graphics_Movie(JNIEnv* env); +extern int register_android_graphics_NinePatch(JNIEnv*); +extern int register_android_graphics_PathEffect(JNIEnv* env); +extern int register_android_graphics_Shader(JNIEnv* env); +extern int register_android_graphics_Typeface(JNIEnv* env); +extern int register_android_graphics_YuvImage(JNIEnv* env); + +namespace android { + +extern int register_android_graphics_Canvas(JNIEnv* env); +extern int register_android_graphics_CanvasProperty(JNIEnv* env); +extern int register_android_graphics_ColorFilter(JNIEnv* env); +extern int register_android_graphics_ColorSpace(JNIEnv* env); +extern int register_android_graphics_DrawFilter(JNIEnv* env); +extern int register_android_graphics_FontFamily(JNIEnv* env); +extern int register_android_graphics_HardwareRendererObserver(JNIEnv* env); +extern int register_android_graphics_Matrix(JNIEnv* env); +extern int register_android_graphics_Paint(JNIEnv* env); +extern int register_android_graphics_Path(JNIEnv* env); +extern int register_android_graphics_PathMeasure(JNIEnv* env); +extern int register_android_graphics_Picture(JNIEnv*); +extern int register_android_graphics_Region(JNIEnv* env); +extern int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env); +extern int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env); +extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env); +extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env); +extern int register_android_graphics_fonts_Font(JNIEnv* env); +extern int register_android_graphics_fonts_FontFamily(JNIEnv* env); +extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env); +extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env); +extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env); +extern int register_android_graphics_text_MeasuredText(JNIEnv* env); +extern int register_android_graphics_text_LineBreaker(JNIEnv *env); + +extern int register_android_util_PathParser(JNIEnv* env); +extern int register_android_view_DisplayListCanvas(JNIEnv* env); +extern int register_android_view_RenderNode(JNIEnv* env); +extern int register_android_view_TextureLayer(JNIEnv* env); +extern int register_android_view_ThreadedRenderer(JNIEnv* env); + +#ifdef NDEBUG + #define REG_JNI(name) { name } + struct RegJNIRec { + int (*mProc)(JNIEnv*); + }; +#else + #define REG_JNI(name) { name, #name } + struct RegJNIRec { + int (*mProc)(JNIEnv*); + const char* mName; + }; +#endif + +static const RegJNIRec gRegJNI[] = { + REG_JNI(register_android_graphics_Canvas), + // This needs to be before register_android_graphics_Graphics, or the latter + // will not be able to find the jmethodID for ColorSpace.get(). + REG_JNI(register_android_graphics_ColorSpace), + REG_JNI(register_android_graphics_Graphics), + REG_JNI(register_android_graphics_Bitmap), + REG_JNI(register_android_graphics_BitmapFactory), + REG_JNI(register_android_graphics_BitmapRegionDecoder), + REG_JNI(register_android_graphics_ByteBufferStreamAdaptor), + REG_JNI(register_android_graphics_Camera), + REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor), + REG_JNI(register_android_graphics_CanvasProperty), + REG_JNI(register_android_graphics_ColorFilter), + REG_JNI(register_android_graphics_DrawFilter), + REG_JNI(register_android_graphics_FontFamily), + REG_JNI(register_android_graphics_HardwareRendererObserver), + REG_JNI(register_android_graphics_ImageDecoder), + REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable), + REG_JNI(register_android_graphics_Interpolator), + REG_JNI(register_android_graphics_MaskFilter), + REG_JNI(register_android_graphics_Matrix), + REG_JNI(register_android_graphics_Movie), + REG_JNI(register_android_graphics_NinePatch), + REG_JNI(register_android_graphics_Paint), + REG_JNI(register_android_graphics_Path), + REG_JNI(register_android_graphics_PathMeasure), + REG_JNI(register_android_graphics_PathEffect), + REG_JNI(register_android_graphics_Picture), + REG_JNI(register_android_graphics_Region), + REG_JNI(register_android_graphics_Shader), + REG_JNI(register_android_graphics_Typeface), + REG_JNI(register_android_graphics_YuvImage), + REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory), + REG_JNI(register_android_graphics_animation_RenderNodeAnimator), + REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable), + REG_JNI(register_android_graphics_drawable_VectorDrawable), + REG_JNI(register_android_graphics_fonts_Font), + REG_JNI(register_android_graphics_fonts_FontFamily), + REG_JNI(register_android_graphics_pdf_PdfDocument), + REG_JNI(register_android_graphics_pdf_PdfEditor), + REG_JNI(register_android_graphics_pdf_PdfRenderer), + REG_JNI(register_android_graphics_text_MeasuredText), + REG_JNI(register_android_graphics_text_LineBreaker), + + REG_JNI(register_android_util_PathParser), + REG_JNI(register_android_view_RenderNode), + REG_JNI(register_android_view_DisplayListCanvas), + REG_JNI(register_android_view_TextureLayer), + REG_JNI(register_android_view_ThreadedRenderer), +}; + +} // namespace android + +void init_android_graphics() { + SkGraphics::Init(); +} + +int register_android_graphics_classes(JNIEnv *env) { + JavaVM* vm = nullptr; + env->GetJavaVM(&vm); + GraphicsJNI::setJavaVM(vm); + + for (size_t i = 0; i < NELEM(android::gRegJNI); i++) { + if (android::gRegJNI[i].mProc(env) < 0) { +#ifndef NDEBUG + __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, "JNI Error!!! %s failed to load\n", + android::gRegJNI[i].mName); +#endif + return -1; + } + } + return 0; +} + +using android::uirenderer::Properties; +using android::uirenderer::RenderPipelineType; + +void zygote_preload_graphics() { + if (Properties::peekRenderPipelineType() == RenderPipelineType::SkiaGL) { + eglGetDisplay(EGL_DEFAULT_DISPLAY); + } +}
\ No newline at end of file diff --git a/libs/hwui/apex/renderthread.cpp b/libs/hwui/apex/renderthread.cpp new file mode 100644 index 000000000000..5d26afe7a2ab --- /dev/null +++ b/libs/hwui/apex/renderthread.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019 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. + */ + +#include "android/graphics/renderthread.h" + +#include <renderthread/RenderProxy.h> + +using namespace android; + +void ARenderThread_dumpGraphicsMemory(int fd) { + uirenderer::renderthread::RenderProxy::dumpGraphicsMemory(fd); +} diff --git a/libs/hwui/debug/FatalBaseDriver.cpp b/libs/hwui/debug/FatalBaseDriver.cpp deleted file mode 100644 index ed0f8316d208..000000000000 --- a/libs/hwui/debug/FatalBaseDriver.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#include "FatalBaseDriver.h" - -#include <log/log.h> - -namespace android { -namespace uirenderer { -namespace debug { - -// Generate the proxy -#define API_ENTRY(x) FatalBaseDriver::x##_ -#define CALL_GL_API(x, ...) LOG_ALWAYS_FATAL("Not Implemented"); -#define CALL_GL_API_RETURN(x, ...) \ - LOG_ALWAYS_FATAL("Not Implemented"); \ - return static_cast<decltype(x(__VA_ARGS__))>(0); - -#include "gles_stubs.in" - -#undef API_ENTRY -#undef CALL_GL_API -#undef CALL_GL_API_RETURN - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/GlesDriver.cpp b/libs/hwui/debug/GlesDriver.cpp deleted file mode 100644 index 98f06b0cb4f0..000000000000 --- a/libs/hwui/debug/GlesDriver.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#include "GlesDriver.h" -#include "DefaultGlesDriver.h" -#include "GlesErrorCheckWrapper.h" - -namespace android { -namespace uirenderer { -namespace debug { - -static DefaultGlesDriver sDefaultDriver; - -static std::unique_ptr<GlesDriver> sGlesDriver(new GlesErrorCheckWrapper(sDefaultDriver)); - -GlesDriver* GlesDriver::get() { - return sGlesDriver.get(); -} - -std::unique_ptr<GlesDriver> GlesDriver::replace(std::unique_ptr<GlesDriver>&& driver) { - std::unique_ptr<GlesDriver> ret = std::move(sGlesDriver); - sGlesDriver = std::move(driver); - return ret; -} - -sk_sp<const GrGLInterface> GlesDriver::getSkiaInterface() { - sk_sp<const GrGLInterface> skiaInterface(GrGLCreateNativeInterface()); - return skiaInterface; -} - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/GlesDriver.h b/libs/hwui/debug/GlesDriver.h deleted file mode 100644 index 1c77c1a45b82..000000000000 --- a/libs/hwui/debug/GlesDriver.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#pragma once - -#ifndef HWUI_GLES_WRAP_ENABLED -#error Wrapping wasn't enabled, can't use this! -#endif - -#include <GLES/gl.h> -#include <GLES/glext.h> -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> -#include <GLES3/gl3.h> -#include <GLES3/gl31.h> -#include <GLES3/gl32.h> - -#include <gl/GrGLInterface.h> -#include <memory> - -namespace android { -namespace uirenderer { -namespace debug { - -// All the gl methods on GlesDriver have a trailing underscore -// This is to avoid collision with gles_redefine/gles_undefine -class GlesDriver { -public: - virtual ~GlesDriver() {} - virtual sk_sp<const GrGLInterface> getSkiaInterface(); - -#define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) = 0; -#include "gles_decls.in" -#undef GL_ENTRY - - static GlesDriver* get(); - static std::unique_ptr<GlesDriver> replace(std::unique_ptr<GlesDriver>&& driver); -}; - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/GlesErrorCheckWrapper.cpp b/libs/hwui/debug/GlesErrorCheckWrapper.cpp deleted file mode 100644 index 8d11c1905da3..000000000000 --- a/libs/hwui/debug/GlesErrorCheckWrapper.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#include "GlesErrorCheckWrapper.h" - -#include <log/log.h> - -namespace android { -namespace uirenderer { -namespace debug { - -void GlesErrorCheckWrapper::assertNoErrors(const char* apicall) { - GLenum status = GL_NO_ERROR; - GLenum lastError = GL_NO_ERROR; - const char* lastErrorName = nullptr; - while ((status = mBase.glGetError_()) != GL_NO_ERROR) { - lastError = status; - switch (status) { - case GL_INVALID_ENUM: - ALOGE("GL error: GL_INVALID_ENUM"); - lastErrorName = "GL_INVALID_ENUM"; - break; - case GL_INVALID_VALUE: - ALOGE("GL error: GL_INVALID_VALUE"); - lastErrorName = "GL_INVALID_VALUE"; - break; - case GL_INVALID_OPERATION: - ALOGE("GL error: GL_INVALID_OPERATION"); - lastErrorName = "GL_INVALID_OPERATION"; - break; - case GL_OUT_OF_MEMORY: - ALOGE("GL error: Out of memory!"); - lastErrorName = "GL_OUT_OF_MEMORY"; - break; - default: - ALOGE("GL error: 0x%x", status); - lastErrorName = "UNKNOWN"; - } - } - LOG_ALWAYS_FATAL_IF(lastError != GL_NO_ERROR, "%s error! %s (0x%x)", apicall, lastErrorName, - lastError); -} - -#define API_ENTRY(x) GlesErrorCheckWrapper::x##_ -#define CALL_GL_API(x, ...) \ - mBase.x##_(__VA_ARGS__); \ - assertNoErrors(#x) - -#define CALL_GL_API_RETURN(x, ...) \ - auto ret = mBase.x##_(__VA_ARGS__); \ - assertNoErrors(#x); \ - return ret - -#include "gles_stubs.in" - -#undef API_ENTRY -#undef CALL_GL_API -#undef CALL_GL_API_RETURN - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/NullGlesDriver.cpp b/libs/hwui/debug/NullGlesDriver.cpp deleted file mode 100644 index 212b24290e22..000000000000 --- a/libs/hwui/debug/NullGlesDriver.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#include <debug/NullGlesDriver.h> - -namespace android { -namespace uirenderer { -namespace debug { - -sk_sp<const GrGLInterface> NullGlesDriver::getSkiaInterface() { - sk_sp<const GrGLInterface> skiaInterface(GrGLCreateNullInterface()); - return skiaInterface; -} - -struct { - GLboolean scissorEnabled; -} gState; - -static void nullglGenCommon(GLsizei n, GLuint* buffers) { - static GLuint nextId = 0; - int i; - for (i = 0; i < n; i++) { - buffers[i] = ++nextId; - } -} - -void NullGlesDriver::glGenBuffers_(GLsizei n, GLuint* buffers) { - nullglGenCommon(n, buffers); -} - -void NullGlesDriver::glGenFramebuffers_(GLsizei n, GLuint* framebuffers) { - nullglGenCommon(n, framebuffers); -} - -void NullGlesDriver::glGenRenderbuffers_(GLsizei n, GLuint* renderbuffers) { - nullglGenCommon(n, renderbuffers); -} - -void NullGlesDriver::glGenTextures_(GLsizei n, GLuint* textures) { - nullglGenCommon(n, textures); -} - -GLuint NullGlesDriver::glCreateProgram_(void) { - static GLuint nextProgram = 0; - return ++nextProgram; -} - -GLuint NullGlesDriver::glCreateShader_(GLenum type) { - static GLuint nextShader = 0; - return ++nextShader; -} - -void NullGlesDriver::glGetProgramiv_(GLuint program, GLenum pname, GLint* params) { - switch (pname) { - case GL_DELETE_STATUS: - case GL_LINK_STATUS: - case GL_VALIDATE_STATUS: - *params = GL_TRUE; - break; - case GL_INFO_LOG_LENGTH: - *params = 16; - break; - } -} - -void NullGlesDriver::glGetProgramInfoLog_(GLuint program, GLsizei bufSize, GLsizei* length, - GLchar* infoLog) { - *length = snprintf(infoLog, bufSize, "success"); - if (*length >= bufSize) { - *length = bufSize - 1; - } -} - -void NullGlesDriver::glGetShaderiv_(GLuint shader, GLenum pname, GLint* params) { - switch (pname) { - case GL_COMPILE_STATUS: - case GL_DELETE_STATUS: - *params = GL_TRUE; - } -} - -void NullGlesDriver::glGetShaderInfoLog_(GLuint shader, GLsizei bufSize, GLsizei* length, - GLchar* infoLog) { - *length = snprintf(infoLog, bufSize, "success"); - if (*length >= bufSize) { - *length = bufSize - 1; - } -} - -void setBooleanState(GLenum cap, GLboolean value) { - switch (cap) { - case GL_SCISSOR_TEST: - gState.scissorEnabled = value; - break; - } -} - -void NullGlesDriver::glEnable_(GLenum cap) { - setBooleanState(cap, GL_TRUE); -} - -void NullGlesDriver::glDisable_(GLenum cap) { - setBooleanState(cap, GL_FALSE); -} - -GLboolean NullGlesDriver::glIsEnabled_(GLenum cap) { - switch (cap) { - case GL_SCISSOR_TEST: - return gState.scissorEnabled; - default: - return GL_FALSE; - } -} - -void NullGlesDriver::glGetIntegerv_(GLenum pname, GLint* data) { - switch (pname) { - case GL_MAX_TEXTURE_SIZE: - *data = 2048; - break; - case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: - *data = 4; - break; - default: - *data = 0; - } -} - -GLenum NullGlesDriver::glCheckFramebufferStatus_(GLenum target) { - switch (target) { - case GL_FRAMEBUFFER: - return GL_FRAMEBUFFER_COMPLETE; - default: - return 0; // error case - } -} - -static const char* getString(GLenum name) { - switch (name) { - case GL_VENDOR: - return "android"; - case GL_RENDERER: - return "null"; - case GL_VERSION: - return "OpenGL ES 2.0 rev1"; - case GL_SHADING_LANGUAGE_VERSION: - return "OpenGL ES GLSL ES 2.0 rev1"; - case GL_EXTENSIONS: - default: - return ""; - } -} - -const GLubyte* NullGlesDriver::glGetString_(GLenum name) { - return (GLubyte*)getString(name); -} - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/NullGlesDriver.h b/libs/hwui/debug/NullGlesDriver.h deleted file mode 100644 index 1a27dbc35299..000000000000 --- a/libs/hwui/debug/NullGlesDriver.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#pragma once - -#include "FatalBaseDriver.h" - -namespace android { -namespace uirenderer { -namespace debug { - -class NullGlesDriver : public FatalBaseDriver { -public: - virtual sk_sp<const GrGLInterface> getSkiaInterface() override; - - virtual void glGenBuffers_(GLsizei n, GLuint* buffers) override; - virtual void glGenFramebuffers_(GLsizei n, GLuint* framebuffers) override; - virtual void glGenRenderbuffers_(GLsizei n, GLuint* renderbuffers) override; - virtual void glGenTextures_(GLsizei n, GLuint* textures) override; - virtual GLuint glCreateProgram_(void) override; - virtual GLuint glCreateShader_(GLenum type) override; - virtual void glGetProgramiv_(GLuint program, GLenum pname, GLint* params) override; - virtual void glGetProgramInfoLog_(GLuint program, GLsizei bufSize, GLsizei* length, - GLchar* infoLog) override; - virtual void glGetShaderiv_(GLuint shader, GLenum pname, GLint* params) override; - virtual void glGetShaderInfoLog_(GLuint shader, GLsizei bufSize, GLsizei* length, - GLchar* infoLog) override; - virtual void glEnable_(GLenum cap) override; - virtual void glDisable_(GLenum cap) override; - virtual GLboolean glIsEnabled_(GLenum cap) override; - virtual void glGetIntegerv_(GLenum pname, GLint* data) override; - virtual const GLubyte* glGetString_(GLenum name) override; - virtual GLenum glCheckFramebufferStatus_(GLenum target) override; - - virtual void glActiveTexture_(GLenum texture) override {} - virtual void glAttachShader_(GLuint program, GLuint shader) override {} - virtual void glBindAttribLocation_(GLuint program, GLuint index, const GLchar* name) override {} - virtual void glBindBuffer_(GLenum target, GLuint buffer) override {} - virtual void glBindFramebuffer_(GLenum target, GLuint framebuffer) override {} - virtual void glBindRenderbuffer_(GLenum target, GLuint renderbuffer) override {} - virtual void glBindTexture_(GLenum target, GLuint texture) override {} - virtual void glBlendColor_(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) override {} - virtual void glBlendEquation_(GLenum mode) override {} - virtual void glBlendEquationSeparate_(GLenum modeRGB, GLenum modeAlpha) override {} - virtual void glBlendFunc_(GLenum sfactor, GLenum dfactor) override {} - virtual void glBlendFuncSeparate_(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, - GLenum dfactorAlpha) override {} - virtual void glBufferData_(GLenum target, GLsizeiptr size, const void* data, - GLenum usage) override {} - virtual void glBufferSubData_(GLenum target, GLintptr offset, GLsizeiptr size, - const void* data) override {} - virtual void glClear_(GLbitfield mask) override {} - virtual void glClearColor_(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) override {} - virtual void glClearDepthf_(GLfloat d) override {} - virtual void glClearStencil_(GLint s) override {} - virtual void glColorMask_(GLboolean red, GLboolean green, GLboolean blue, - GLboolean alpha) override {} - virtual void glCompileShader_(GLuint shader) override {} - virtual void glCompressedTexImage2D_(GLenum target, GLint level, GLenum internalformat, - GLsizei width, GLsizei height, GLint border, - GLsizei imageSize, const void* data) override {} - virtual void glCompressedTexSubImage2D_(GLenum target, GLint level, GLint xoffset, - GLint yoffset, GLsizei width, GLsizei height, - GLenum format, GLsizei imageSize, - const void* data) override {} - virtual void glCopyTexImage2D_(GLenum target, GLint level, GLenum internalformat, GLint x, - GLint y, GLsizei width, GLsizei height, GLint border) override {} - virtual void glCopyTexSubImage2D_(GLenum target, GLint level, GLint xoffset, GLint yoffset, - GLint x, GLint y, GLsizei width, GLsizei height) override {} - virtual void glCullFace_(GLenum mode) override {} - virtual void glDeleteBuffers_(GLsizei n, const GLuint* buffers) override {} - virtual void glDeleteFramebuffers_(GLsizei n, const GLuint* framebuffers) override {} - virtual void glDeleteProgram_(GLuint program) override {} - virtual void glDeleteRenderbuffers_(GLsizei n, const GLuint* renderbuffers) override {} - virtual void glDeleteShader_(GLuint shader) override {} - virtual void glDeleteTextures_(GLsizei n, const GLuint* textures) override {} - virtual void glDepthFunc_(GLenum func) override {} - virtual void glDepthMask_(GLboolean flag) override {} - virtual void glDepthRangef_(GLfloat n, GLfloat f) override {} - virtual void glDetachShader_(GLuint program, GLuint shader) override {} - virtual void glDisableVertexAttribArray_(GLuint index) override {} - virtual void glDrawArrays_(GLenum mode, GLint first, GLsizei count) override {} - virtual void glDrawElements_(GLenum mode, GLsizei count, GLenum type, - const void* indices) override {} - virtual void glEnableVertexAttribArray_(GLuint index) override {} - virtual void glFinish_(void) override {} - virtual void glFlush_(void) override {} - virtual void glFramebufferRenderbuffer_(GLenum target, GLenum attachment, - GLenum renderbuffertarget, - GLuint renderbuffer) override {} - virtual void glFramebufferTexture2D_(GLenum target, GLenum attachment, GLenum textarget, - GLuint texture, GLint level) override {} - virtual void glFrontFace_(GLenum mode) override {} - virtual void glGenerateMipmap_(GLenum target) override {} - virtual GLint glGetAttribLocation_(GLuint program, const GLchar* name) override { return 1; } - virtual GLenum glGetError_(void) override { return GL_NO_ERROR; } - virtual GLint glGetUniformLocation_(GLuint program, const GLchar* name) override { return 2; } - virtual void glHint_(GLenum target, GLenum mode) override {} - virtual void glLineWidth_(GLfloat width) override {} - virtual void glLinkProgram_(GLuint program) override {} - virtual void glPixelStorei_(GLenum pname, GLint param) override {} - virtual void glPolygonOffset_(GLfloat factor, GLfloat units) override {} - virtual void glReadPixels_(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, - GLenum type, void* pixels) override {} - virtual void glReleaseShaderCompiler_(void) override {} - virtual void glRenderbufferStorage_(GLenum target, GLenum internalformat, GLsizei width, - GLsizei height) override {} - virtual void glSampleCoverage_(GLfloat value, GLboolean invert) override {} - virtual void glScissor_(GLint x, GLint y, GLsizei width, GLsizei height) override {} - virtual void glShaderBinary_(GLsizei count, const GLuint* shaders, GLenum binaryformat, - const void* binary, GLsizei length) override {} - virtual void glShaderSource_(GLuint shader, GLsizei count, const GLchar* const* string, - const GLint* length) override {} - virtual void glStencilFunc_(GLenum func, GLint ref, GLuint mask) override {} - virtual void glStencilFuncSeparate_(GLenum face, GLenum func, GLint ref, GLuint mask) override { - } - virtual void glStencilMask_(GLuint mask) override {} - virtual void glStencilMaskSeparate_(GLenum face, GLuint mask) override {} - virtual void glStencilOp_(GLenum fail, GLenum zfail, GLenum zpass) override {} - virtual void glStencilOpSeparate_(GLenum face, GLenum sfail, GLenum dpfail, - GLenum dppass) override {} - virtual void glTexImage2D_(GLenum target, GLint level, GLint internalformat, GLsizei width, - GLsizei height, GLint border, GLenum format, GLenum type, - const void* pixels) override {} - virtual void glTexParameterf_(GLenum target, GLenum pname, GLfloat param) override {} - virtual void glTexParameterfv_(GLenum target, GLenum pname, const GLfloat* params) override {} - virtual void glTexParameteri_(GLenum target, GLenum pname, GLint param) override {} - virtual void glTexParameteriv_(GLenum target, GLenum pname, const GLint* params) override {} - virtual void glTexSubImage2D_(GLenum target, GLint level, GLint xoffset, GLint yoffset, - GLsizei width, GLsizei height, GLenum format, GLenum type, - const void* pixels) override {} - virtual void glUniform1f_(GLint location, GLfloat v0) override {} - virtual void glUniform1fv_(GLint location, GLsizei count, const GLfloat* value) override {} - virtual void glUniform1i_(GLint location, GLint v0) override {} - virtual void glUniform1iv_(GLint location, GLsizei count, const GLint* value) override {} - virtual void glUniform2f_(GLint location, GLfloat v0, GLfloat v1) override {} - virtual void glUniform2fv_(GLint location, GLsizei count, const GLfloat* value) override {} - virtual void glUniform2i_(GLint location, GLint v0, GLint v1) override {} - virtual void glUniform2iv_(GLint location, GLsizei count, const GLint* value) override {} - virtual void glUniform3f_(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) override {} - virtual void glUniform3fv_(GLint location, GLsizei count, const GLfloat* value) override {} - virtual void glUniform3i_(GLint location, GLint v0, GLint v1, GLint v2) override {} - virtual void glUniform3iv_(GLint location, GLsizei count, const GLint* value) override {} - virtual void glUniform4f_(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, - GLfloat v3) override {} - virtual void glUniform4fv_(GLint location, GLsizei count, const GLfloat* value) override {} - virtual void glUniform4i_(GLint location, GLint v0, GLint v1, GLint v2, GLint v3) override {} - virtual void glUniform4iv_(GLint location, GLsizei count, const GLint* value) override {} - virtual void glUniformMatrix2fv_(GLint location, GLsizei count, GLboolean transpose, - const GLfloat* value) override {} - virtual void glUniformMatrix3fv_(GLint location, GLsizei count, GLboolean transpose, - const GLfloat* value) override {} - virtual void glUniformMatrix4fv_(GLint location, GLsizei count, GLboolean transpose, - const GLfloat* value) override {} - virtual void glUseProgram_(GLuint program) override {} - virtual void glValidateProgram_(GLuint program) override {} - virtual void glVertexAttrib1f_(GLuint index, GLfloat x) override {} - virtual void glVertexAttrib1fv_(GLuint index, const GLfloat* v) override {} - virtual void glVertexAttrib2f_(GLuint index, GLfloat x, GLfloat y) override {} - virtual void glVertexAttrib2fv_(GLuint index, const GLfloat* v) override {} - virtual void glVertexAttrib3f_(GLuint index, GLfloat x, GLfloat y, GLfloat z) override {} - virtual void glVertexAttrib3fv_(GLuint index, const GLfloat* v) override {} - virtual void glVertexAttrib4f_(GLuint index, GLfloat x, GLfloat y, GLfloat z, - GLfloat w) override {} - virtual void glVertexAttrib4fv_(GLuint index, const GLfloat* v) override {} - virtual void glVertexAttribPointer_(GLuint index, GLint size, GLenum type, GLboolean normalized, - GLsizei stride, const void* pointer) override {} - virtual void glViewport_(GLint x, GLint y, GLsizei width, GLsizei height) override {} - - // gles2 ext - virtual void glInsertEventMarkerEXT_(GLsizei length, const GLchar* marker) override {} - virtual void glPushGroupMarkerEXT_(GLsizei length, const GLchar* marker) override {} - virtual void glPopGroupMarkerEXT_(void) override {} - virtual void glDiscardFramebufferEXT_(GLenum target, GLsizei numAttachments, - const GLenum* attachments) override {} - virtual void glEGLImageTargetTexture2DOES_(GLenum target, GLeglImageOES image) override {} - - // GLES3 - virtual void* glMapBufferRange_(GLenum target, GLintptr offset, GLsizeiptr length, - GLbitfield access) override { - return 0; - } - - virtual GLboolean glUnmapBuffer_(GLenum target) override { return GL_FALSE; } -}; - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/ScopedReplaceDriver.h b/libs/hwui/debug/ScopedReplaceDriver.h deleted file mode 100644 index 110196fb2a01..000000000000 --- a/libs/hwui/debug/ScopedReplaceDriver.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#pragma once - -#include "GlesDriver.h" - -namespace android { -namespace uirenderer { -namespace debug { - -template <typename Driver> -class ScopedReplaceDriver { -public: - ScopedReplaceDriver() { - std::unique_ptr<Driver> glDriver = std::make_unique<Driver>(); - mCurrentDriver = glDriver.get(); - mOldDriver = GlesDriver::replace(std::move(glDriver)); - } - - Driver& get() { return *mCurrentDriver; } - - ~ScopedReplaceDriver() { GlesDriver::replace(std::move(mOldDriver)); } - -private: - std::unique_ptr<GlesDriver> mOldDriver; - Driver* mCurrentDriver; -}; - -} // namespace debug -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/debug/gles_decls.in b/libs/hwui/debug/gles_decls.in deleted file mode 100644 index 3900959c29de..000000000000 --- a/libs/hwui/debug/gles_decls.in +++ /dev/null @@ -1,543 +0,0 @@ -GL_ENTRY(void, glActiveTexture, GLenum texture) -GL_ENTRY(void, glAttachShader, GLuint program, GLuint shader) -GL_ENTRY(void, glBindAttribLocation, GLuint program, GLuint index, const GLchar *name) -GL_ENTRY(void, glBindBuffer, GLenum target, GLuint buffer) -GL_ENTRY(void, glBindFramebuffer, GLenum target, GLuint framebuffer) -GL_ENTRY(void, glBindRenderbuffer, GLenum target, GLuint renderbuffer) -GL_ENTRY(void, glBindTexture, GLenum target, GLuint texture) -GL_ENTRY(void, glBlendColor, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) -GL_ENTRY(void, glBlendEquation, GLenum mode) -GL_ENTRY(void, glBlendEquationSeparate, GLenum modeRGB, GLenum modeAlpha) -GL_ENTRY(void, glBlendFunc, GLenum sfactor, GLenum dfactor) -GL_ENTRY(void, glBlendFuncSeparate, GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) -GL_ENTRY(void, glBufferData, GLenum target, GLsizeiptr size, const void *data, GLenum usage) -GL_ENTRY(void, glBufferSubData, GLenum target, GLintptr offset, GLsizeiptr size, const void *data) -GL_ENTRY(GLenum, glCheckFramebufferStatus, GLenum target) -GL_ENTRY(void, glClear, GLbitfield mask) -GL_ENTRY(void, glClearColor, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) -GL_ENTRY(void, glClearDepthf, GLfloat d) -GL_ENTRY(void, glClearStencil, GLint s) -GL_ENTRY(void, glColorMask, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) -GL_ENTRY(void, glCompileShader, GLuint shader) -GL_ENTRY(void, glCompressedTexImage2D, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data) -GL_ENTRY(void, glCompressedTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data) -GL_ENTRY(void, glCopyTexImage2D, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) -GL_ENTRY(void, glCopyTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) -GL_ENTRY(GLuint, glCreateProgram, void) -GL_ENTRY(GLuint, glCreateShader, GLenum type) -GL_ENTRY(void, glCullFace, GLenum mode) -GL_ENTRY(void, glDeleteBuffers, GLsizei n, const GLuint *buffers) -GL_ENTRY(void, glDeleteFramebuffers, GLsizei n, const GLuint *framebuffers) -GL_ENTRY(void, glDeleteProgram, GLuint program) -GL_ENTRY(void, glDeleteRenderbuffers, GLsizei n, const GLuint *renderbuffers) -GL_ENTRY(void, glDeleteShader, GLuint shader) -GL_ENTRY(void, glDeleteTextures, GLsizei n, const GLuint *textures) -GL_ENTRY(void, glDepthFunc, GLenum func) -GL_ENTRY(void, glDepthMask, GLboolean flag) -GL_ENTRY(void, glDepthRangef, GLfloat n, GLfloat f) -GL_ENTRY(void, glDetachShader, GLuint program, GLuint shader) -GL_ENTRY(void, glDisable, GLenum cap) -GL_ENTRY(void, glDisableVertexAttribArray, GLuint index) -GL_ENTRY(void, glDrawArrays, GLenum mode, GLint first, GLsizei count) -GL_ENTRY(void, glDrawElements, GLenum mode, GLsizei count, GLenum type, const void *indices) -GL_ENTRY(void, glEnable, GLenum cap) -GL_ENTRY(void, glEnableVertexAttribArray, GLuint index) -GL_ENTRY(void, glFinish, void) -GL_ENTRY(void, glFlush, void) -GL_ENTRY(void, glFramebufferRenderbuffer, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) -GL_ENTRY(void, glFramebufferTexture2D, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) -GL_ENTRY(void, glFrontFace, GLenum mode) -GL_ENTRY(void, glGenBuffers, GLsizei n, GLuint *buffers) -GL_ENTRY(void, glGenerateMipmap, GLenum target) -GL_ENTRY(void, glGenFramebuffers, GLsizei n, GLuint *framebuffers) -GL_ENTRY(void, glGenRenderbuffers, GLsizei n, GLuint *renderbuffers) -GL_ENTRY(void, glGenTextures, GLsizei n, GLuint *textures) -GL_ENTRY(void, glGetActiveAttrib, GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) -GL_ENTRY(void, glGetActiveUniform, GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) -GL_ENTRY(void, glGetAttachedShaders, GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders) -GL_ENTRY(GLint, glGetAttribLocation, GLuint program, const GLchar *name) -GL_ENTRY(void, glGetBooleanv, GLenum pname, GLboolean *data) -GL_ENTRY(void, glGetBufferParameteriv, GLenum target, GLenum pname, GLint *params) -GL_ENTRY(GLenum, glGetError, void) -GL_ENTRY(void, glGetFloatv, GLenum pname, GLfloat *data) -GL_ENTRY(void, glGetFramebufferAttachmentParameteriv, GLenum target, GLenum attachment, GLenum pname, GLint *params) -GL_ENTRY(void, glGetIntegerv, GLenum pname, GLint *data) -GL_ENTRY(void, glGetProgramiv, GLuint program, GLenum pname, GLint *params) -GL_ENTRY(void, glGetProgramInfoLog, GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog) -GL_ENTRY(void, glGetRenderbufferParameteriv, GLenum target, GLenum pname, GLint *params) -GL_ENTRY(void, glGetShaderiv, GLuint shader, GLenum pname, GLint *params) -GL_ENTRY(void, glGetShaderInfoLog, GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) -GL_ENTRY(void, glGetShaderPrecisionFormat, GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision) -GL_ENTRY(void, glGetShaderSource, GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source) -GL_ENTRY(const GLubyte *, glGetString, GLenum name) -GL_ENTRY(void, glGetTexParameterfv, GLenum target, GLenum pname, GLfloat *params) -GL_ENTRY(void, glGetTexParameteriv, GLenum target, GLenum pname, GLint *params) -GL_ENTRY(void, glGetUniformfv, GLuint program, GLint location, GLfloat *params) -GL_ENTRY(void, glGetUniformiv, GLuint program, GLint location, GLint *params) -GL_ENTRY(GLint, glGetUniformLocation, GLuint program, const GLchar *name) -GL_ENTRY(void, glGetVertexAttribfv, GLuint index, GLenum pname, GLfloat *params) -GL_ENTRY(void, glGetVertexAttribiv, GLuint index, GLenum pname, GLint *params) -GL_ENTRY(void, glGetVertexAttribPointerv, GLuint index, GLenum pname, void **pointer) -GL_ENTRY(void, glHint, GLenum target, GLenum mode) -GL_ENTRY(GLboolean, glIsBuffer, GLuint buffer) -GL_ENTRY(GLboolean, glIsEnabled, GLenum cap) -GL_ENTRY(GLboolean, glIsFramebuffer, GLuint framebuffer) -GL_ENTRY(GLboolean, glIsProgram, GLuint program) -GL_ENTRY(GLboolean, glIsRenderbuffer, GLuint renderbuffer) -GL_ENTRY(GLboolean, glIsShader, GLuint shader) -GL_ENTRY(GLboolean, glIsTexture, GLuint texture) -GL_ENTRY(void, glLineWidth, GLfloat width) -GL_ENTRY(void, glLinkProgram, GLuint program) -GL_ENTRY(void, glPixelStorei, GLenum pname, GLint param) -GL_ENTRY(void, glPolygonOffset, GLfloat factor, GLfloat units) -GL_ENTRY(void, glReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) -GL_ENTRY(void, glReleaseShaderCompiler, void) -GL_ENTRY(void, glRenderbufferStorage, GLenum target, GLenum internalformat, GLsizei width, GLsizei height) -GL_ENTRY(void, glSampleCoverage, GLfloat value, GLboolean invert) -GL_ENTRY(void, glScissor, GLint x, GLint y, GLsizei width, GLsizei height) -GL_ENTRY(void, glShaderBinary, GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length) -GL_ENTRY(void, glShaderSource, GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length) -GL_ENTRY(void, glStencilFunc, GLenum func, GLint ref, GLuint mask) -GL_ENTRY(void, glStencilFuncSeparate, GLenum face, GLenum func, GLint ref, GLuint mask) -GL_ENTRY(void, glStencilMask, GLuint mask) -GL_ENTRY(void, glStencilMaskSeparate, GLenum face, GLuint mask) -GL_ENTRY(void, glStencilOp, GLenum fail, GLenum zfail, GLenum zpass) -GL_ENTRY(void, glStencilOpSeparate, GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) -GL_ENTRY(void, glTexImage2D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) -GL_ENTRY(void, glTexParameterf, GLenum target, GLenum pname, GLfloat param) -GL_ENTRY(void, glTexParameterfv, GLenum target, GLenum pname, const GLfloat *params) -GL_ENTRY(void, glTexParameteri, GLenum target, GLenum pname, GLint param) -GL_ENTRY(void, glTexParameteriv, GLenum target, GLenum pname, const GLint *params) -GL_ENTRY(void, glTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) -GL_ENTRY(void, glUniform1f, GLint location, GLfloat v0) -GL_ENTRY(void, glUniform1fv, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glUniform1i, GLint location, GLint v0) -GL_ENTRY(void, glUniform1iv, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glUniform2f, GLint location, GLfloat v0, GLfloat v1) -GL_ENTRY(void, glUniform2fv, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glUniform2i, GLint location, GLint v0, GLint v1) -GL_ENTRY(void, glUniform2iv, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glUniform3f, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) -GL_ENTRY(void, glUniform3fv, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glUniform3i, GLint location, GLint v0, GLint v1, GLint v2) -GL_ENTRY(void, glUniform3iv, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glUniform4f, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) -GL_ENTRY(void, glUniform4fv, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glUniform4i, GLint location, GLint v0, GLint v1, GLint v2, GLint v3) -GL_ENTRY(void, glUniform4iv, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glUniformMatrix2fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glUniformMatrix3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glUniformMatrix4fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glUseProgram, GLuint program) -GL_ENTRY(void, glValidateProgram, GLuint program) -GL_ENTRY(void, glVertexAttrib1f, GLuint index, GLfloat x) -GL_ENTRY(void, glVertexAttrib1fv, GLuint index, const GLfloat *v) -GL_ENTRY(void, glVertexAttrib2f, GLuint index, GLfloat x, GLfloat y) -GL_ENTRY(void, glVertexAttrib2fv, GLuint index, const GLfloat *v) -GL_ENTRY(void, glVertexAttrib3f, GLuint index, GLfloat x, GLfloat y, GLfloat z) -GL_ENTRY(void, glVertexAttrib3fv, GLuint index, const GLfloat *v) -GL_ENTRY(void, glVertexAttrib4f, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) -GL_ENTRY(void, glVertexAttrib4fv, GLuint index, const GLfloat *v) -GL_ENTRY(void, glVertexAttribPointer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) -GL_ENTRY(void, glViewport, GLint x, GLint y, GLsizei width, GLsizei height) -GL_ENTRY(void, glReadBuffer, GLenum src) -GL_ENTRY(void, glDrawRangeElements, GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices) -GL_ENTRY(void, glTexImage3D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) -GL_ENTRY(void, glTexSubImage3D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) -GL_ENTRY(void, glCopyTexSubImage3D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) -GL_ENTRY(void, glCompressedTexImage3D, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data) -GL_ENTRY(void, glCompressedTexSubImage3D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data) -GL_ENTRY(void, glGenQueries, GLsizei n, GLuint *ids) -GL_ENTRY(void, glDeleteQueries, GLsizei n, const GLuint *ids) -GL_ENTRY(GLboolean, glIsQuery, GLuint id) -GL_ENTRY(void, glBeginQuery, GLenum target, GLuint id) -GL_ENTRY(void, glEndQuery, GLenum target) -GL_ENTRY(void, glGetQueryiv, GLenum target, GLenum pname, GLint *params) -GL_ENTRY(void, glGetQueryObjectuiv, GLuint id, GLenum pname, GLuint *params) -GL_ENTRY(GLboolean, glUnmapBuffer, GLenum target) -GL_ENTRY(void, glGetBufferPointerv, GLenum target, GLenum pname, void **params) -GL_ENTRY(void, glDrawBuffers, GLsizei n, const GLenum *bufs) -GL_ENTRY(void, glUniformMatrix2x3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glUniformMatrix3x2fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glUniformMatrix2x4fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glUniformMatrix4x2fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glUniformMatrix3x4fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glUniformMatrix4x3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glBlitFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) -GL_ENTRY(void, glRenderbufferStorageMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) -GL_ENTRY(void, glFramebufferTextureLayer, GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) -GL_ENTRY(void *, glMapBufferRange, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) -GL_ENTRY(void, glFlushMappedBufferRange, GLenum target, GLintptr offset, GLsizeiptr length) -GL_ENTRY(void, glBindVertexArray, GLuint array) -GL_ENTRY(void, glDeleteVertexArrays, GLsizei n, const GLuint *arrays) -GL_ENTRY(void, glGenVertexArrays, GLsizei n, GLuint *arrays) -GL_ENTRY(GLboolean, glIsVertexArray, GLuint array) -GL_ENTRY(void, glGetIntegeri_v, GLenum target, GLuint index, GLint *data) -GL_ENTRY(void, glBeginTransformFeedback, GLenum primitiveMode) -GL_ENTRY(void, glEndTransformFeedback, void) -GL_ENTRY(void, glBindBufferRange, GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size) -GL_ENTRY(void, glBindBufferBase, GLenum target, GLuint index, GLuint buffer) -GL_ENTRY(void, glTransformFeedbackVaryings, GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode) -GL_ENTRY(void, glGetTransformFeedbackVarying, GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name) -GL_ENTRY(void, glVertexAttribIPointer, GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer) -GL_ENTRY(void, glGetVertexAttribIiv, GLuint index, GLenum pname, GLint *params) -GL_ENTRY(void, glGetVertexAttribIuiv, GLuint index, GLenum pname, GLuint *params) -GL_ENTRY(void, glVertexAttribI4i, GLuint index, GLint x, GLint y, GLint z, GLint w) -GL_ENTRY(void, glVertexAttribI4ui, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w) -GL_ENTRY(void, glVertexAttribI4iv, GLuint index, const GLint *v) -GL_ENTRY(void, glVertexAttribI4uiv, GLuint index, const GLuint *v) -GL_ENTRY(void, glGetUniformuiv, GLuint program, GLint location, GLuint *params) -GL_ENTRY(GLint, glGetFragDataLocation, GLuint program, const GLchar *name) -GL_ENTRY(void, glUniform1ui, GLint location, GLuint v0) -GL_ENTRY(void, glUniform2ui, GLint location, GLuint v0, GLuint v1) -GL_ENTRY(void, glUniform3ui, GLint location, GLuint v0, GLuint v1, GLuint v2) -GL_ENTRY(void, glUniform4ui, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) -GL_ENTRY(void, glUniform1uiv, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glUniform2uiv, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glUniform3uiv, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glUniform4uiv, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glClearBufferiv, GLenum buffer, GLint drawbuffer, const GLint *value) -GL_ENTRY(void, glClearBufferuiv, GLenum buffer, GLint drawbuffer, const GLuint *value) -GL_ENTRY(void, glClearBufferfv, GLenum buffer, GLint drawbuffer, const GLfloat *value) -GL_ENTRY(void, glClearBufferfi, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) -GL_ENTRY(const GLubyte *, glGetStringi, GLenum name, GLuint index) -GL_ENTRY(void, glCopyBufferSubData, GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) -GL_ENTRY(void, glGetUniformIndices, GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices) -GL_ENTRY(void, glGetActiveUniformsiv, GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params) -GL_ENTRY(GLuint, glGetUniformBlockIndex, GLuint program, const GLchar *uniformBlockName) -GL_ENTRY(void, glGetActiveUniformBlockiv, GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params) -GL_ENTRY(void, glGetActiveUniformBlockName, GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName) -GL_ENTRY(void, glUniformBlockBinding, GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) -GL_ENTRY(void, glDrawArraysInstanced, GLenum mode, GLint first, GLsizei count, GLsizei instancecount) -GL_ENTRY(void, glDrawElementsInstanced, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount) -GL_ENTRY(GLsync, glFenceSync, GLenum condition, GLbitfield flags) -GL_ENTRY(GLboolean, glIsSync, GLsync sync) -GL_ENTRY(void, glDeleteSync, GLsync sync) -GL_ENTRY(GLenum, glClientWaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout) -GL_ENTRY(void, glWaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout) -GL_ENTRY(void, glGetInteger64v, GLenum pname, GLint64 *data) -GL_ENTRY(void, glGetSynciv, GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values) -GL_ENTRY(void, glGetInteger64i_v, GLenum target, GLuint index, GLint64 *data) -GL_ENTRY(void, glGetBufferParameteri64v, GLenum target, GLenum pname, GLint64 *params) -GL_ENTRY(void, glGenSamplers, GLsizei count, GLuint *samplers) -GL_ENTRY(void, glDeleteSamplers, GLsizei count, const GLuint *samplers) -GL_ENTRY(GLboolean, glIsSampler, GLuint sampler) -GL_ENTRY(void, glBindSampler, GLuint unit, GLuint sampler) -GL_ENTRY(void, glSamplerParameteri, GLuint sampler, GLenum pname, GLint param) -GL_ENTRY(void, glSamplerParameteriv, GLuint sampler, GLenum pname, const GLint *param) -GL_ENTRY(void, glSamplerParameterf, GLuint sampler, GLenum pname, GLfloat param) -GL_ENTRY(void, glSamplerParameterfv, GLuint sampler, GLenum pname, const GLfloat *param) -GL_ENTRY(void, glGetSamplerParameteriv, GLuint sampler, GLenum pname, GLint *params) -GL_ENTRY(void, glGetSamplerParameterfv, GLuint sampler, GLenum pname, GLfloat *params) -GL_ENTRY(void, glVertexAttribDivisor, GLuint index, GLuint divisor) -GL_ENTRY(void, glBindTransformFeedback, GLenum target, GLuint id) -GL_ENTRY(void, glDeleteTransformFeedbacks, GLsizei n, const GLuint *ids) -GL_ENTRY(void, glGenTransformFeedbacks, GLsizei n, GLuint *ids) -GL_ENTRY(GLboolean, glIsTransformFeedback, GLuint id) -GL_ENTRY(void, glPauseTransformFeedback, void) -GL_ENTRY(void, glResumeTransformFeedback, void) -GL_ENTRY(void, glGetProgramBinary, GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary) -GL_ENTRY(void, glProgramBinary, GLuint program, GLenum binaryFormat, const void *binary, GLsizei length) -GL_ENTRY(void, glProgramParameteri, GLuint program, GLenum pname, GLint value) -GL_ENTRY(void, glInvalidateFramebuffer, GLenum target, GLsizei numAttachments, const GLenum *attachments) -GL_ENTRY(void, glInvalidateSubFramebuffer, GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height) -GL_ENTRY(void, glTexStorage2D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) -GL_ENTRY(void, glTexStorage3D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) -GL_ENTRY(void, glGetInternalformativ, GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params) -GL_ENTRY(void, glDispatchCompute, GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z) -GL_ENTRY(void, glDispatchComputeIndirect, GLintptr indirect) -GL_ENTRY(void, glDrawArraysIndirect, GLenum mode, const void *indirect) -GL_ENTRY(void, glDrawElementsIndirect, GLenum mode, GLenum type, const void *indirect) -GL_ENTRY(void, glFramebufferParameteri, GLenum target, GLenum pname, GLint param) -GL_ENTRY(void, glGetFramebufferParameteriv, GLenum target, GLenum pname, GLint *params) -GL_ENTRY(void, glGetProgramInterfaceiv, GLuint program, GLenum programInterface, GLenum pname, GLint *params) -GL_ENTRY(GLuint, glGetProgramResourceIndex, GLuint program, GLenum programInterface, const GLchar *name) -GL_ENTRY(void, glGetProgramResourceName, GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name) -GL_ENTRY(void, glGetProgramResourceiv, GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params) -GL_ENTRY(GLint, glGetProgramResourceLocation, GLuint program, GLenum programInterface, const GLchar *name) -GL_ENTRY(void, glUseProgramStages, GLuint pipeline, GLbitfield stages, GLuint program) -GL_ENTRY(void, glActiveShaderProgram, GLuint pipeline, GLuint program) -GL_ENTRY(GLuint, glCreateShaderProgramv, GLenum type, GLsizei count, const GLchar *const*strings) -GL_ENTRY(void, glBindProgramPipeline, GLuint pipeline) -GL_ENTRY(void, glDeleteProgramPipelines, GLsizei n, const GLuint *pipelines) -GL_ENTRY(void, glGenProgramPipelines, GLsizei n, GLuint *pipelines) -GL_ENTRY(GLboolean, glIsProgramPipeline, GLuint pipeline) -GL_ENTRY(void, glGetProgramPipelineiv, GLuint pipeline, GLenum pname, GLint *params) -GL_ENTRY(void, glProgramUniform1i, GLuint program, GLint location, GLint v0) -GL_ENTRY(void, glProgramUniform2i, GLuint program, GLint location, GLint v0, GLint v1) -GL_ENTRY(void, glProgramUniform3i, GLuint program, GLint location, GLint v0, GLint v1, GLint v2) -GL_ENTRY(void, glProgramUniform4i, GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3) -GL_ENTRY(void, glProgramUniform1ui, GLuint program, GLint location, GLuint v0) -GL_ENTRY(void, glProgramUniform2ui, GLuint program, GLint location, GLuint v0, GLuint v1) -GL_ENTRY(void, glProgramUniform3ui, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2) -GL_ENTRY(void, glProgramUniform4ui, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) -GL_ENTRY(void, glProgramUniform1f, GLuint program, GLint location, GLfloat v0) -GL_ENTRY(void, glProgramUniform2f, GLuint program, GLint location, GLfloat v0, GLfloat v1) -GL_ENTRY(void, glProgramUniform3f, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) -GL_ENTRY(void, glProgramUniform4f, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) -GL_ENTRY(void, glProgramUniform1iv, GLuint program, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glProgramUniform2iv, GLuint program, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glProgramUniform3iv, GLuint program, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glProgramUniform4iv, GLuint program, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glProgramUniform1uiv, GLuint program, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glProgramUniform2uiv, GLuint program, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glProgramUniform3uiv, GLuint program, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glProgramUniform4uiv, GLuint program, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glProgramUniform1fv, GLuint program, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glProgramUniform2fv, GLuint program, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glProgramUniform3fv, GLuint program, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glProgramUniform4fv, GLuint program, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix2fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix3fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix4fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix2x3fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix3x2fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix2x4fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix4x2fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix3x4fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix4x3fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glValidateProgramPipeline, GLuint pipeline) -GL_ENTRY(void, glGetProgramPipelineInfoLog, GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog) -GL_ENTRY(void, glBindImageTexture, GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) -GL_ENTRY(void, glGetBooleani_v, GLenum target, GLuint index, GLboolean *data) -GL_ENTRY(void, glMemoryBarrier, GLbitfield barriers) -GL_ENTRY(void, glMemoryBarrierByRegion, GLbitfield barriers) -GL_ENTRY(void, glTexStorage2DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) -GL_ENTRY(void, glGetMultisamplefv, GLenum pname, GLuint index, GLfloat *val) -GL_ENTRY(void, glSampleMaski, GLuint maskNumber, GLbitfield mask) -GL_ENTRY(void, glGetTexLevelParameteriv, GLenum target, GLint level, GLenum pname, GLint *params) -GL_ENTRY(void, glGetTexLevelParameterfv, GLenum target, GLint level, GLenum pname, GLfloat *params) -GL_ENTRY(void, glBindVertexBuffer, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride) -GL_ENTRY(void, glVertexAttribFormat, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset) -GL_ENTRY(void, glVertexAttribIFormat, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) -GL_ENTRY(void, glVertexAttribBinding, GLuint attribindex, GLuint bindingindex) -GL_ENTRY(void, glVertexBindingDivisor, GLuint bindingindex, GLuint divisor) -GL_ENTRY(void, glBlendBarrier, void) -GL_ENTRY(void, glCopyImageSubData, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) -GL_ENTRY(void, glDebugMessageControl, GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled) -GL_ENTRY(void, glDebugMessageInsert, GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf) -GL_ENTRY(void, glDebugMessageCallback, GLDEBUGPROC callback, const void *userParam) -GL_ENTRY(GLuint, glGetDebugMessageLog, GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog) -GL_ENTRY(void, glPushDebugGroup, GLenum source, GLuint id, GLsizei length, const GLchar *message) -GL_ENTRY(void, glPopDebugGroup, void) -GL_ENTRY(void, glObjectLabel, GLenum identifier, GLuint name, GLsizei length, const GLchar *label) -GL_ENTRY(void, glGetObjectLabel, GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label) -GL_ENTRY(void, glObjectPtrLabel, const void *ptr, GLsizei length, const GLchar *label) -GL_ENTRY(void, glGetObjectPtrLabel, const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label) -GL_ENTRY(void, glGetPointerv, GLenum pname, void **params) -GL_ENTRY(void, glEnablei, GLenum target, GLuint index) -GL_ENTRY(void, glDisablei, GLenum target, GLuint index) -GL_ENTRY(void, glBlendEquationi, GLuint buf, GLenum mode) -GL_ENTRY(void, glBlendEquationSeparatei, GLuint buf, GLenum modeRGB, GLenum modeAlpha) -GL_ENTRY(void, glBlendFunci, GLuint buf, GLenum src, GLenum dst) -GL_ENTRY(void, glBlendFuncSeparatei, GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) -GL_ENTRY(void, glColorMaski, GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) -GL_ENTRY(GLboolean, glIsEnabledi, GLenum target, GLuint index) -GL_ENTRY(void, glDrawElementsBaseVertex, GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) -GL_ENTRY(void, glDrawRangeElementsBaseVertex, GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) -GL_ENTRY(void, glDrawElementsInstancedBaseVertex, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) -GL_ENTRY(void, glFramebufferTexture, GLenum target, GLenum attachment, GLuint texture, GLint level) -GL_ENTRY(void, glPrimitiveBoundingBox, GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) -GL_ENTRY(GLenum, glGetGraphicsResetStatus, void) -GL_ENTRY(void, glReadnPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) -GL_ENTRY(void, glGetnUniformfv, GLuint program, GLint location, GLsizei bufSize, GLfloat *params) -GL_ENTRY(void, glGetnUniformiv, GLuint program, GLint location, GLsizei bufSize, GLint *params) -GL_ENTRY(void, glGetnUniformuiv, GLuint program, GLint location, GLsizei bufSize, GLuint *params) -GL_ENTRY(void, glMinSampleShading, GLfloat value) -GL_ENTRY(void, glPatchParameteri, GLenum pname, GLint value) -GL_ENTRY(void, glTexParameterIiv, GLenum target, GLenum pname, const GLint *params) -GL_ENTRY(void, glTexParameterIuiv, GLenum target, GLenum pname, const GLuint *params) -GL_ENTRY(void, glGetTexParameterIiv, GLenum target, GLenum pname, GLint *params) -GL_ENTRY(void, glGetTexParameterIuiv, GLenum target, GLenum pname, GLuint *params) -GL_ENTRY(void, glSamplerParameterIiv, GLuint sampler, GLenum pname, const GLint *param) -GL_ENTRY(void, glSamplerParameterIuiv, GLuint sampler, GLenum pname, const GLuint *param) -GL_ENTRY(void, glGetSamplerParameterIiv, GLuint sampler, GLenum pname, GLint *params) -GL_ENTRY(void, glGetSamplerParameterIuiv, GLuint sampler, GLenum pname, GLuint *params) -GL_ENTRY(void, glTexBuffer, GLenum target, GLenum internalformat, GLuint buffer) -GL_ENTRY(void, glTexBufferRange, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) -GL_ENTRY(void, glTexStorage3DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) -GL_ENTRY(void, glBlendBarrierKHR, void) -GL_ENTRY(void, glDebugMessageControlKHR, GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled) -GL_ENTRY(void, glDebugMessageInsertKHR, GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf) -GL_ENTRY(void, glDebugMessageCallbackKHR, GLDEBUGPROCKHR callback, const void *userParam) -GL_ENTRY(GLuint, glGetDebugMessageLogKHR, GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog) -GL_ENTRY(void, glPushDebugGroupKHR, GLenum source, GLuint id, GLsizei length, const GLchar *message) -GL_ENTRY(void, glPopDebugGroupKHR, void) -GL_ENTRY(void, glObjectLabelKHR, GLenum identifier, GLuint name, GLsizei length, const GLchar *label) -GL_ENTRY(void, glGetObjectLabelKHR, GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label) -GL_ENTRY(void, glObjectPtrLabelKHR, const void *ptr, GLsizei length, const GLchar *label) -GL_ENTRY(void, glGetObjectPtrLabelKHR, const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label) -GL_ENTRY(void, glGetPointervKHR, GLenum pname, void **params) -GL_ENTRY(GLenum, glGetGraphicsResetStatusKHR, void) -GL_ENTRY(void, glReadnPixelsKHR, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) -GL_ENTRY(void, glGetnUniformfvKHR, GLuint program, GLint location, GLsizei bufSize, GLfloat *params) -GL_ENTRY(void, glGetnUniformivKHR, GLuint program, GLint location, GLsizei bufSize, GLint *params) -GL_ENTRY(void, glGetnUniformuivKHR, GLuint program, GLint location, GLsizei bufSize, GLuint *params) -GL_ENTRY(void, glEGLImageTargetTexture2DOES, GLenum target, GLeglImageOES image) -GL_ENTRY(void, glEGLImageTargetRenderbufferStorageOES, GLenum target, GLeglImageOES image) -GL_ENTRY(void, glCopyImageSubDataOES, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) -GL_ENTRY(void, glEnableiOES, GLenum target, GLuint index) -GL_ENTRY(void, glDisableiOES, GLenum target, GLuint index) -GL_ENTRY(void, glBlendEquationiOES, GLuint buf, GLenum mode) -GL_ENTRY(void, glBlendEquationSeparateiOES, GLuint buf, GLenum modeRGB, GLenum modeAlpha) -GL_ENTRY(void, glBlendFunciOES, GLuint buf, GLenum src, GLenum dst) -GL_ENTRY(void, glBlendFuncSeparateiOES, GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) -GL_ENTRY(void, glColorMaskiOES, GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) -GL_ENTRY(GLboolean, glIsEnablediOES, GLenum target, GLuint index) -GL_ENTRY(void, glDrawElementsBaseVertexOES, GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) -GL_ENTRY(void, glDrawRangeElementsBaseVertexOES, GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) -GL_ENTRY(void, glDrawElementsInstancedBaseVertexOES, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) -GL_ENTRY(void, glFramebufferTextureOES, GLenum target, GLenum attachment, GLuint texture, GLint level) -GL_ENTRY(void, glGetProgramBinaryOES, GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary) -GL_ENTRY(void, glProgramBinaryOES, GLuint program, GLenum binaryFormat, const void *binary, GLint length) -GL_ENTRY(void *, glMapBufferOES, GLenum target, GLenum access) -GL_ENTRY(GLboolean, glUnmapBufferOES, GLenum target) -GL_ENTRY(void, glGetBufferPointervOES, GLenum target, GLenum pname, void **params) -GL_ENTRY(void, glPrimitiveBoundingBoxOES, GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) -GL_ENTRY(void, glMinSampleShadingOES, GLfloat value) -GL_ENTRY(void, glPatchParameteriOES, GLenum pname, GLint value) -GL_ENTRY(void, glTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) -GL_ENTRY(void, glTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) -GL_ENTRY(void, glCopyTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) -GL_ENTRY(void, glCompressedTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data) -GL_ENTRY(void, glCompressedTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data) -GL_ENTRY(void, glFramebufferTexture3DOES, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) -GL_ENTRY(void, glTexParameterIivOES, GLenum target, GLenum pname, const GLint *params) -GL_ENTRY(void, glTexParameterIuivOES, GLenum target, GLenum pname, const GLuint *params) -GL_ENTRY(void, glGetTexParameterIivOES, GLenum target, GLenum pname, GLint *params) -GL_ENTRY(void, glGetTexParameterIuivOES, GLenum target, GLenum pname, GLuint *params) -GL_ENTRY(void, glSamplerParameterIivOES, GLuint sampler, GLenum pname, const GLint *param) -GL_ENTRY(void, glSamplerParameterIuivOES, GLuint sampler, GLenum pname, const GLuint *param) -GL_ENTRY(void, glGetSamplerParameterIivOES, GLuint sampler, GLenum pname, GLint *params) -GL_ENTRY(void, glGetSamplerParameterIuivOES, GLuint sampler, GLenum pname, GLuint *params) -GL_ENTRY(void, glTexBufferOES, GLenum target, GLenum internalformat, GLuint buffer) -GL_ENTRY(void, glTexBufferRangeOES, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) -GL_ENTRY(void, glTexStorage3DMultisampleOES, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) -GL_ENTRY(void, glTextureViewOES, GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) -GL_ENTRY(void, glBindVertexArrayOES, GLuint array) -GL_ENTRY(void, glDeleteVertexArraysOES, GLsizei n, const GLuint *arrays) -GL_ENTRY(void, glGenVertexArraysOES, GLsizei n, GLuint *arrays) -GL_ENTRY(GLboolean, glIsVertexArrayOES, GLuint array) -GL_ENTRY(void, glDrawArraysInstancedBaseInstanceEXT, GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance) -GL_ENTRY(void, glDrawElementsInstancedBaseInstanceEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance) -GL_ENTRY(void, glDrawElementsInstancedBaseVertexBaseInstanceEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance) -GL_ENTRY(void, glBindFragDataLocationIndexedEXT, GLuint program, GLuint colorNumber, GLuint index, const GLchar *name) -GL_ENTRY(void, glBindFragDataLocationEXT, GLuint program, GLuint color, const GLchar *name) -GL_ENTRY(GLint, glGetProgramResourceLocationIndexEXT, GLuint program, GLenum programInterface, const GLchar *name) -GL_ENTRY(GLint, glGetFragDataIndexEXT, GLuint program, const GLchar *name) -GL_ENTRY(void, glBufferStorageEXT, GLenum target, GLsizeiptr size, const void *data, GLbitfield flags) -GL_ENTRY(void, glCopyImageSubDataEXT, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) -GL_ENTRY(void, glLabelObjectEXT, GLenum type, GLuint object, GLsizei length, const GLchar *label) -GL_ENTRY(void, glGetObjectLabelEXT, GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label) -GL_ENTRY(void, glInsertEventMarkerEXT, GLsizei length, const GLchar *marker) -GL_ENTRY(void, glPushGroupMarkerEXT, GLsizei length, const GLchar *marker) -GL_ENTRY(void, glPopGroupMarkerEXT, void) -GL_ENTRY(void, glDiscardFramebufferEXT, GLenum target, GLsizei numAttachments, const GLenum *attachments) -GL_ENTRY(void, glGenQueriesEXT, GLsizei n, GLuint *ids) -GL_ENTRY(void, glDeleteQueriesEXT, GLsizei n, const GLuint *ids) -GL_ENTRY(GLboolean, glIsQueryEXT, GLuint id) -GL_ENTRY(void, glBeginQueryEXT, GLenum target, GLuint id) -GL_ENTRY(void, glEndQueryEXT, GLenum target) -GL_ENTRY(void, glQueryCounterEXT, GLuint id, GLenum target) -GL_ENTRY(void, glGetQueryivEXT, GLenum target, GLenum pname, GLint *params) -GL_ENTRY(void, glGetQueryObjectivEXT, GLuint id, GLenum pname, GLint *params) -GL_ENTRY(void, glGetQueryObjectuivEXT, GLuint id, GLenum pname, GLuint *params) -GL_ENTRY(void, glGetQueryObjecti64vEXT, GLuint id, GLenum pname, GLint64 *params) -GL_ENTRY(void, glGetQueryObjectui64vEXT, GLuint id, GLenum pname, GLuint64 *params) -GL_ENTRY(void, glDrawBuffersEXT, GLsizei n, const GLenum *bufs) -GL_ENTRY(void, glEnableiEXT, GLenum target, GLuint index) -GL_ENTRY(void, glDisableiEXT, GLenum target, GLuint index) -GL_ENTRY(void, glBlendEquationiEXT, GLuint buf, GLenum mode) -GL_ENTRY(void, glBlendEquationSeparateiEXT, GLuint buf, GLenum modeRGB, GLenum modeAlpha) -GL_ENTRY(void, glBlendFunciEXT, GLuint buf, GLenum src, GLenum dst) -GL_ENTRY(void, glBlendFuncSeparateiEXT, GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) -GL_ENTRY(void, glColorMaskiEXT, GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) -GL_ENTRY(GLboolean, glIsEnablediEXT, GLenum target, GLuint index) -GL_ENTRY(void, glDrawElementsBaseVertexEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) -GL_ENTRY(void, glDrawRangeElementsBaseVertexEXT, GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) -GL_ENTRY(void, glDrawElementsInstancedBaseVertexEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) -GL_ENTRY(void, glMultiDrawElementsBaseVertexEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex) -GL_ENTRY(void, glDrawArraysInstancedEXT, GLenum mode, GLint start, GLsizei count, GLsizei primcount) -GL_ENTRY(void, glDrawElementsInstancedEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount) -GL_ENTRY(void, glFramebufferTextureEXT, GLenum target, GLenum attachment, GLuint texture, GLint level) -GL_ENTRY(void, glVertexAttribDivisorEXT, GLuint index, GLuint divisor) -GL_ENTRY(void *, glMapBufferRangeEXT, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) -GL_ENTRY(void, glFlushMappedBufferRangeEXT, GLenum target, GLintptr offset, GLsizeiptr length) -GL_ENTRY(void, glMultiDrawArraysEXT, GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount) -GL_ENTRY(void, glMultiDrawElementsEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount) -GL_ENTRY(void, glMultiDrawArraysIndirectEXT, GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride) -GL_ENTRY(void, glMultiDrawElementsIndirectEXT, GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride) -GL_ENTRY(void, glRenderbufferStorageMultisampleEXT, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) -GL_ENTRY(void, glFramebufferTexture2DMultisampleEXT, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) -GL_ENTRY(void, glReadBufferIndexedEXT, GLenum src, GLint index) -GL_ENTRY(void, glDrawBuffersIndexedEXT, GLint n, const GLenum *location, const GLint *indices) -GL_ENTRY(void, glGetIntegeri_vEXT, GLenum target, GLuint index, GLint *data) -GL_ENTRY(void, glPrimitiveBoundingBoxEXT, GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) -GL_ENTRY(void, glRasterSamplesEXT, GLuint samples, GLboolean fixedsamplelocations) -GL_ENTRY(GLenum, glGetGraphicsResetStatusEXT, void) -GL_ENTRY(void, glReadnPixelsEXT, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) -GL_ENTRY(void, glGetnUniformfvEXT, GLuint program, GLint location, GLsizei bufSize, GLfloat *params) -GL_ENTRY(void, glGetnUniformivEXT, GLuint program, GLint location, GLsizei bufSize, GLint *params) -GL_ENTRY(void, glActiveShaderProgramEXT, GLuint pipeline, GLuint program) -GL_ENTRY(void, glBindProgramPipelineEXT, GLuint pipeline) -GL_ENTRY(GLuint, glCreateShaderProgramvEXT, GLenum type, GLsizei count, const GLchar **strings) -GL_ENTRY(void, glDeleteProgramPipelinesEXT, GLsizei n, const GLuint *pipelines) -GL_ENTRY(void, glGenProgramPipelinesEXT, GLsizei n, GLuint *pipelines) -GL_ENTRY(void, glGetProgramPipelineInfoLogEXT, GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog) -GL_ENTRY(void, glGetProgramPipelineivEXT, GLuint pipeline, GLenum pname, GLint *params) -GL_ENTRY(GLboolean, glIsProgramPipelineEXT, GLuint pipeline) -GL_ENTRY(void, glProgramParameteriEXT, GLuint program, GLenum pname, GLint value) -GL_ENTRY(void, glProgramUniform1fEXT, GLuint program, GLint location, GLfloat v0) -GL_ENTRY(void, glProgramUniform1fvEXT, GLuint program, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glProgramUniform1iEXT, GLuint program, GLint location, GLint v0) -GL_ENTRY(void, glProgramUniform1ivEXT, GLuint program, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glProgramUniform2fEXT, GLuint program, GLint location, GLfloat v0, GLfloat v1) -GL_ENTRY(void, glProgramUniform2fvEXT, GLuint program, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glProgramUniform2iEXT, GLuint program, GLint location, GLint v0, GLint v1) -GL_ENTRY(void, glProgramUniform2ivEXT, GLuint program, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glProgramUniform3fEXT, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) -GL_ENTRY(void, glProgramUniform3fvEXT, GLuint program, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glProgramUniform3iEXT, GLuint program, GLint location, GLint v0, GLint v1, GLint v2) -GL_ENTRY(void, glProgramUniform3ivEXT, GLuint program, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glProgramUniform4fEXT, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) -GL_ENTRY(void, glProgramUniform4fvEXT, GLuint program, GLint location, GLsizei count, const GLfloat *value) -GL_ENTRY(void, glProgramUniform4iEXT, GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3) -GL_ENTRY(void, glProgramUniform4ivEXT, GLuint program, GLint location, GLsizei count, const GLint *value) -GL_ENTRY(void, glProgramUniformMatrix2fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix3fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix4fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glUseProgramStagesEXT, GLuint pipeline, GLbitfield stages, GLuint program) -GL_ENTRY(void, glValidateProgramPipelineEXT, GLuint pipeline) -GL_ENTRY(void, glProgramUniform1uiEXT, GLuint program, GLint location, GLuint v0) -GL_ENTRY(void, glProgramUniform2uiEXT, GLuint program, GLint location, GLuint v0, GLuint v1) -GL_ENTRY(void, glProgramUniform3uiEXT, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2) -GL_ENTRY(void, glProgramUniform4uiEXT, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) -GL_ENTRY(void, glProgramUniform1uivEXT, GLuint program, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glProgramUniform2uivEXT, GLuint program, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glProgramUniform3uivEXT, GLuint program, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glProgramUniform4uivEXT, GLuint program, GLint location, GLsizei count, const GLuint *value) -GL_ENTRY(void, glProgramUniformMatrix2x3fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix3x2fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix2x4fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix4x2fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix3x4fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glProgramUniformMatrix4x3fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) -GL_ENTRY(void, glTexPageCommitmentEXT, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit) -GL_ENTRY(void, glPatchParameteriEXT, GLenum pname, GLint value) -GL_ENTRY(void, glTexParameterIivEXT, GLenum target, GLenum pname, const GLint *params) -GL_ENTRY(void, glTexParameterIuivEXT, GLenum target, GLenum pname, const GLuint *params) -GL_ENTRY(void, glGetTexParameterIivEXT, GLenum target, GLenum pname, GLint *params) -GL_ENTRY(void, glGetTexParameterIuivEXT, GLenum target, GLenum pname, GLuint *params) -GL_ENTRY(void, glSamplerParameterIivEXT, GLuint sampler, GLenum pname, const GLint *param) -GL_ENTRY(void, glSamplerParameterIuivEXT, GLuint sampler, GLenum pname, const GLuint *param) -GL_ENTRY(void, glGetSamplerParameterIivEXT, GLuint sampler, GLenum pname, GLint *params) -GL_ENTRY(void, glGetSamplerParameterIuivEXT, GLuint sampler, GLenum pname, GLuint *params) -GL_ENTRY(void, glTexBufferEXT, GLenum target, GLenum internalformat, GLuint buffer) -GL_ENTRY(void, glTexBufferRangeEXT, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) -GL_ENTRY(void, glTexStorage1DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) -GL_ENTRY(void, glTexStorage2DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) -GL_ENTRY(void, glTexStorage3DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) -GL_ENTRY(void, glTextureStorage1DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) -GL_ENTRY(void, glTextureStorage2DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) -GL_ENTRY(void, glTextureStorage3DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) -GL_ENTRY(void, glTextureViewEXT, GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) diff --git a/libs/hwui/debug/gles_redefine.h b/libs/hwui/debug/gles_redefine.h deleted file mode 100644 index 49b506918372..000000000000 --- a/libs/hwui/debug/gles_redefine.h +++ /dev/null @@ -1,914 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#define glActiveShaderProgram wrap_glActiveShaderProgram -#define glActiveShaderProgramEXT wrap_glActiveShaderProgramEXT -#define glActiveTexture wrap_glActiveTexture -#define glAlphaFunc wrap_glAlphaFunc -#define glAlphaFuncQCOM wrap_glAlphaFuncQCOM -#define glAlphaFuncx wrap_glAlphaFuncx -#define glAlphaFuncxOES wrap_glAlphaFuncxOES -#define glApplyFramebufferAttachmentCMAAINTEL wrap_glApplyFramebufferAttachmentCMAAINTEL -#define glAttachShader wrap_glAttachShader -#define glBeginConditionalRenderNV wrap_glBeginConditionalRenderNV -#define glBeginPerfMonitorAMD wrap_glBeginPerfMonitorAMD -#define glBeginPerfQueryINTEL wrap_glBeginPerfQueryINTEL -#define glBeginQuery wrap_glBeginQuery -#define glBeginQueryEXT wrap_glBeginQueryEXT -#define glBeginTransformFeedback wrap_glBeginTransformFeedback -#define glBindAttribLocation wrap_glBindAttribLocation -#define glBindBuffer wrap_glBindBuffer -#define glBindBufferBase wrap_glBindBufferBase -#define glBindBufferRange wrap_glBindBufferRange -#define glBindFragDataLocationEXT wrap_glBindFragDataLocationEXT -#define glBindFragDataLocationIndexedEXT wrap_glBindFragDataLocationIndexedEXT -#define glBindFramebuffer wrap_glBindFramebuffer -#define glBindFramebufferOES wrap_glBindFramebufferOES -#define glBindImageTexture wrap_glBindImageTexture -#define glBindProgramPipeline wrap_glBindProgramPipeline -#define glBindProgramPipelineEXT wrap_glBindProgramPipelineEXT -#define glBindRenderbuffer wrap_glBindRenderbuffer -#define glBindRenderbufferOES wrap_glBindRenderbufferOES -#define glBindSampler wrap_glBindSampler -#define glBindTexture wrap_glBindTexture -#define glBindTransformFeedback wrap_glBindTransformFeedback -#define glBindVertexArray wrap_glBindVertexArray -#define glBindVertexArrayOES wrap_glBindVertexArrayOES -#define glBindVertexBuffer wrap_glBindVertexBuffer -#define glBlendBarrier wrap_glBlendBarrier -#define glBlendBarrierKHR wrap_glBlendBarrierKHR -#define glBlendBarrierNV wrap_glBlendBarrierNV -#define glBlendColor wrap_glBlendColor -#define glBlendEquation wrap_glBlendEquation -#define glBlendEquationOES wrap_glBlendEquationOES -#define glBlendEquationSeparate wrap_glBlendEquationSeparate -#define glBlendEquationSeparateOES wrap_glBlendEquationSeparateOES -#define glBlendEquationSeparatei wrap_glBlendEquationSeparatei -#define glBlendEquationSeparateiEXT wrap_glBlendEquationSeparateiEXT -#define glBlendEquationSeparateiOES wrap_glBlendEquationSeparateiOES -#define glBlendEquationi wrap_glBlendEquationi -#define glBlendEquationiEXT wrap_glBlendEquationiEXT -#define glBlendEquationiOES wrap_glBlendEquationiOES -#define glBlendFunc wrap_glBlendFunc -#define glBlendFuncSeparate wrap_glBlendFuncSeparate -#define glBlendFuncSeparateOES wrap_glBlendFuncSeparateOES -#define glBlendFuncSeparatei wrap_glBlendFuncSeparatei -#define glBlendFuncSeparateiEXT wrap_glBlendFuncSeparateiEXT -#define glBlendFuncSeparateiOES wrap_glBlendFuncSeparateiOES -#define glBlendFunci wrap_glBlendFunci -#define glBlendFunciEXT wrap_glBlendFunciEXT -#define glBlendFunciOES wrap_glBlendFunciOES -#define glBlendParameteriNV wrap_glBlendParameteriNV -#define glBlitFramebuffer wrap_glBlitFramebuffer -#define glBlitFramebufferANGLE wrap_glBlitFramebufferANGLE -#define glBlitFramebufferNV wrap_glBlitFramebufferNV -#define glBufferData wrap_glBufferData -#define glBufferStorageEXT wrap_glBufferStorageEXT -#define glBufferSubData wrap_glBufferSubData -#define glCheckFramebufferStatus wrap_glCheckFramebufferStatus -#define glCheckFramebufferStatusOES wrap_glCheckFramebufferStatusOES -#define glClear wrap_glClear -#define glClearBufferfi wrap_glClearBufferfi -#define glClearBufferfv wrap_glClearBufferfv -#define glClearBufferiv wrap_glClearBufferiv -#define glClearBufferuiv wrap_glClearBufferuiv -#define glClearColor wrap_glClearColor -#define glClearColorx wrap_glClearColorx -#define glClearColorxOES wrap_glClearColorxOES -#define glClearDepthf wrap_glClearDepthf -#define glClearDepthfOES wrap_glClearDepthfOES -#define glClearDepthx wrap_glClearDepthx -#define glClearDepthxOES wrap_glClearDepthxOES -#define glClearStencil wrap_glClearStencil -#define glClientActiveTexture wrap_glClientActiveTexture -#define glClientWaitSync wrap_glClientWaitSync -#define glClientWaitSyncAPPLE wrap_glClientWaitSyncAPPLE -#define glClipPlanef wrap_glClipPlanef -#define glClipPlanefIMG wrap_glClipPlanefIMG -#define glClipPlanefOES wrap_glClipPlanefOES -#define glClipPlanex wrap_glClipPlanex -#define glClipPlanexIMG wrap_glClipPlanexIMG -#define glClipPlanexOES wrap_glClipPlanexOES -#define glColor4f wrap_glColor4f -#define glColor4ub wrap_glColor4ub -#define glColor4x wrap_glColor4x -#define glColor4xOES wrap_glColor4xOES -#define glColorMask wrap_glColorMask -#define glColorMaski wrap_glColorMaski -#define glColorMaskiEXT wrap_glColorMaskiEXT -#define glColorMaskiOES wrap_glColorMaskiOES -#define glColorPointer wrap_glColorPointer -#define glCompileShader wrap_glCompileShader -#define glCompressedTexImage2D wrap_glCompressedTexImage2D -#define glCompressedTexImage3D wrap_glCompressedTexImage3D -#define glCompressedTexImage3DOES wrap_glCompressedTexImage3DOES -#define glCompressedTexSubImage2D wrap_glCompressedTexSubImage2D -#define glCompressedTexSubImage3D wrap_glCompressedTexSubImage3D -#define glCompressedTexSubImage3DOES wrap_glCompressedTexSubImage3DOES -#define glCopyBufferSubData wrap_glCopyBufferSubData -#define glCopyBufferSubDataNV wrap_glCopyBufferSubDataNV -#define glCopyImageSubData wrap_glCopyImageSubData -#define glCopyImageSubDataEXT wrap_glCopyImageSubDataEXT -#define glCopyImageSubDataOES wrap_glCopyImageSubDataOES -#define glCopyPathNV wrap_glCopyPathNV -#define glCopyTexImage2D wrap_glCopyTexImage2D -#define glCopyTexSubImage2D wrap_glCopyTexSubImage2D -#define glCopyTexSubImage3D wrap_glCopyTexSubImage3D -#define glCopyTexSubImage3DOES wrap_glCopyTexSubImage3DOES -#define glCopyTextureLevelsAPPLE wrap_glCopyTextureLevelsAPPLE -#define glCoverFillPathInstancedNV wrap_glCoverFillPathInstancedNV -#define glCoverFillPathNV wrap_glCoverFillPathNV -#define glCoverStrokePathInstancedNV wrap_glCoverStrokePathInstancedNV -#define glCoverStrokePathNV wrap_glCoverStrokePathNV -#define glCoverageMaskNV wrap_glCoverageMaskNV -#define glCoverageModulationNV wrap_glCoverageModulationNV -#define glCoverageModulationTableNV wrap_glCoverageModulationTableNV -#define glCoverageOperationNV wrap_glCoverageOperationNV -#define glCreatePerfQueryINTEL wrap_glCreatePerfQueryINTEL -#define glCreateProgram wrap_glCreateProgram -#define glCreateShader wrap_glCreateShader -#define glCreateShaderProgramv wrap_glCreateShaderProgramv -#define glCreateShaderProgramvEXT wrap_glCreateShaderProgramvEXT -#define glCullFace wrap_glCullFace -#define glCurrentPaletteMatrixOES wrap_glCurrentPaletteMatrixOES -#define glDebugMessageCallback wrap_glDebugMessageCallback -#define glDebugMessageCallbackKHR wrap_glDebugMessageCallbackKHR -#define glDebugMessageControl wrap_glDebugMessageControl -#define glDebugMessageControlKHR wrap_glDebugMessageControlKHR -#define glDebugMessageInsert wrap_glDebugMessageInsert -#define glDebugMessageInsertKHR wrap_glDebugMessageInsertKHR -#define glDeleteBuffers wrap_glDeleteBuffers -#define glDeleteFencesNV wrap_glDeleteFencesNV -#define glDeleteFramebuffers wrap_glDeleteFramebuffers -#define glDeleteFramebuffersOES wrap_glDeleteFramebuffersOES -#define glDeletePathsNV wrap_glDeletePathsNV -#define glDeletePerfMonitorsAMD wrap_glDeletePerfMonitorsAMD -#define glDeletePerfQueryINTEL wrap_glDeletePerfQueryINTEL -#define glDeleteProgram wrap_glDeleteProgram -#define glDeleteProgramPipelines wrap_glDeleteProgramPipelines -#define glDeleteProgramPipelinesEXT wrap_glDeleteProgramPipelinesEXT -#define glDeleteQueries wrap_glDeleteQueries -#define glDeleteQueriesEXT wrap_glDeleteQueriesEXT -#define glDeleteRenderbuffers wrap_glDeleteRenderbuffers -#define glDeleteRenderbuffersOES wrap_glDeleteRenderbuffersOES -#define glDeleteSamplers wrap_glDeleteSamplers -#define glDeleteShader wrap_glDeleteShader -#define glDeleteSync wrap_glDeleteSync -#define glDeleteSyncAPPLE wrap_glDeleteSyncAPPLE -#define glDeleteTextures wrap_glDeleteTextures -#define glDeleteTransformFeedbacks wrap_glDeleteTransformFeedbacks -#define glDeleteVertexArrays wrap_glDeleteVertexArrays -#define glDeleteVertexArraysOES wrap_glDeleteVertexArraysOES -#define glDepthFunc wrap_glDepthFunc -#define glDepthMask wrap_glDepthMask -#define glDepthRangeArrayfvNV wrap_glDepthRangeArrayfvNV -#define glDepthRangeIndexedfNV wrap_glDepthRangeIndexedfNV -#define glDepthRangef wrap_glDepthRangef -#define glDepthRangefOES wrap_glDepthRangefOES -#define glDepthRangex wrap_glDepthRangex -#define glDepthRangexOES wrap_glDepthRangexOES -#define glDetachShader wrap_glDetachShader -#define glDisable wrap_glDisable -#define glDisableClientState wrap_glDisableClientState -#define glDisableDriverControlQCOM wrap_glDisableDriverControlQCOM -#define glDisableVertexAttribArray wrap_glDisableVertexAttribArray -#define glDisablei wrap_glDisablei -#define glDisableiEXT wrap_glDisableiEXT -#define glDisableiNV wrap_glDisableiNV -#define glDisableiOES wrap_glDisableiOES -#define glDiscardFramebufferEXT wrap_glDiscardFramebufferEXT -#define glDispatchCompute wrap_glDispatchCompute -#define glDispatchComputeIndirect wrap_glDispatchComputeIndirect -#define glDrawArrays wrap_glDrawArrays -#define glDrawArraysIndirect wrap_glDrawArraysIndirect -#define glDrawArraysInstanced wrap_glDrawArraysInstanced -#define glDrawArraysInstancedANGLE wrap_glDrawArraysInstancedANGLE -#define glDrawArraysInstancedBaseInstanceEXT wrap_glDrawArraysInstancedBaseInstanceEXT -#define glDrawArraysInstancedEXT wrap_glDrawArraysInstancedEXT -#define glDrawArraysInstancedNV wrap_glDrawArraysInstancedNV -#define glDrawBuffers wrap_glDrawBuffers -#define glDrawBuffersEXT wrap_glDrawBuffersEXT -#define glDrawBuffersIndexedEXT wrap_glDrawBuffersIndexedEXT -#define glDrawBuffersNV wrap_glDrawBuffersNV -#define glDrawElements wrap_glDrawElements -#define glDrawElementsBaseVertex wrap_glDrawElementsBaseVertex -#define glDrawElementsBaseVertexEXT wrap_glDrawElementsBaseVertexEXT -#define glDrawElementsBaseVertexOES wrap_glDrawElementsBaseVertexOES -#define glDrawElementsIndirect wrap_glDrawElementsIndirect -#define glDrawElementsInstanced wrap_glDrawElementsInstanced -#define glDrawElementsInstancedANGLE wrap_glDrawElementsInstancedANGLE -#define glDrawElementsInstancedBaseInstanceEXT wrap_glDrawElementsInstancedBaseInstanceEXT -#define glDrawElementsInstancedBaseVertex wrap_glDrawElementsInstancedBaseVertex -#define glDrawElementsInstancedBaseVertexBaseInstanceEXT \ - wrap_glDrawElementsInstancedBaseVertexBaseInstanceEXT -#define glDrawElementsInstancedBaseVertexEXT wrap_glDrawElementsInstancedBaseVertexEXT -#define glDrawElementsInstancedBaseVertexOES wrap_glDrawElementsInstancedBaseVertexOES -#define glDrawElementsInstancedEXT wrap_glDrawElementsInstancedEXT -#define glDrawElementsInstancedNV wrap_glDrawElementsInstancedNV -#define glDrawRangeElements wrap_glDrawRangeElements -#define glDrawRangeElementsBaseVertex wrap_glDrawRangeElementsBaseVertex -#define glDrawRangeElementsBaseVertexEXT wrap_glDrawRangeElementsBaseVertexEXT -#define glDrawRangeElementsBaseVertexOES wrap_glDrawRangeElementsBaseVertexOES -#define glDrawTexfOES wrap_glDrawTexfOES -#define glDrawTexfvOES wrap_glDrawTexfvOES -#define glDrawTexiOES wrap_glDrawTexiOES -#define glDrawTexivOES wrap_glDrawTexivOES -#define glDrawTexsOES wrap_glDrawTexsOES -#define glDrawTexsvOES wrap_glDrawTexsvOES -#define glDrawTexxOES wrap_glDrawTexxOES -#define glDrawTexxvOES wrap_glDrawTexxvOES -#define glEGLImageTargetRenderbufferStorageOES wrap_glEGLImageTargetRenderbufferStorageOES -#define glEGLImageTargetTexture2DOES wrap_glEGLImageTargetTexture2DOES -#define glEnable wrap_glEnable -#define glEnableClientState wrap_glEnableClientState -#define glEnableDriverControlQCOM wrap_glEnableDriverControlQCOM -#define glEnableVertexAttribArray wrap_glEnableVertexAttribArray -#define glEnablei wrap_glEnablei -#define glEnableiEXT wrap_glEnableiEXT -#define glEnableiNV wrap_glEnableiNV -#define glEnableiOES wrap_glEnableiOES -#define glEndConditionalRenderNV wrap_glEndConditionalRenderNV -#define glEndPerfMonitorAMD wrap_glEndPerfMonitorAMD -#define glEndPerfQueryINTEL wrap_glEndPerfQueryINTEL -#define glEndQuery wrap_glEndQuery -#define glEndQueryEXT wrap_glEndQueryEXT -#define glEndTilingQCOM wrap_glEndTilingQCOM -#define glEndTransformFeedback wrap_glEndTransformFeedback -#define glExtGetBufferPointervQCOM wrap_glExtGetBufferPointervQCOM -#define glExtGetBuffersQCOM wrap_glExtGetBuffersQCOM -#define glExtGetFramebuffersQCOM wrap_glExtGetFramebuffersQCOM -#define glExtGetProgramBinarySourceQCOM wrap_glExtGetProgramBinarySourceQCOM -#define glExtGetProgramsQCOM wrap_glExtGetProgramsQCOM -#define glExtGetRenderbuffersQCOM wrap_glExtGetRenderbuffersQCOM -#define glExtGetShadersQCOM wrap_glExtGetShadersQCOM -#define glExtGetTexLevelParameterivQCOM wrap_glExtGetTexLevelParameterivQCOM -#define glExtGetTexSubImageQCOM wrap_glExtGetTexSubImageQCOM -#define glExtGetTexturesQCOM wrap_glExtGetTexturesQCOM -#define glExtIsProgramBinaryQCOM wrap_glExtIsProgramBinaryQCOM -#define glExtTexObjectStateOverrideiQCOM wrap_glExtTexObjectStateOverrideiQCOM -#define glFenceSync wrap_glFenceSync -#define glFenceSyncAPPLE wrap_glFenceSyncAPPLE -#define glFinish wrap_glFinish -#define glFinishFenceNV wrap_glFinishFenceNV -#define glFlush wrap_glFlush -#define glFlushMappedBufferRange wrap_glFlushMappedBufferRange -#define glFlushMappedBufferRangeEXT wrap_glFlushMappedBufferRangeEXT -#define glFogf wrap_glFogf -#define glFogfv wrap_glFogfv -#define glFogx wrap_glFogx -#define glFogxOES wrap_glFogxOES -#define glFogxv wrap_glFogxv -#define glFogxvOES wrap_glFogxvOES -#define glFragmentCoverageColorNV wrap_glFragmentCoverageColorNV -#define glFramebufferParameteri wrap_glFramebufferParameteri -#define glFramebufferRenderbuffer wrap_glFramebufferRenderbuffer -#define glFramebufferRenderbufferOES wrap_glFramebufferRenderbufferOES -#define glFramebufferSampleLocationsfvNV wrap_glFramebufferSampleLocationsfvNV -#define glFramebufferTexture wrap_glFramebufferTexture -#define glFramebufferTexture2D wrap_glFramebufferTexture2D -#define glFramebufferTexture2DMultisampleEXT wrap_glFramebufferTexture2DMultisampleEXT -#define glFramebufferTexture2DMultisampleIMG wrap_glFramebufferTexture2DMultisampleIMG -#define glFramebufferTexture2DOES wrap_glFramebufferTexture2DOES -#define glFramebufferTexture3DOES wrap_glFramebufferTexture3DOES -#define glFramebufferTextureEXT wrap_glFramebufferTextureEXT -#define glFramebufferTextureLayer wrap_glFramebufferTextureLayer -#define glFramebufferTextureMultisampleMultiviewOVR wrap_glFramebufferTextureMultisampleMultiviewOVR -#define glFramebufferTextureMultiviewOVR wrap_glFramebufferTextureMultiviewOVR -#define glFramebufferTextureOES wrap_glFramebufferTextureOES -#define glFrontFace wrap_glFrontFace -#define glFrustumf wrap_glFrustumf -#define glFrustumfOES wrap_glFrustumfOES -#define glFrustumx wrap_glFrustumx -#define glFrustumxOES wrap_glFrustumxOES -#define glGenBuffers wrap_glGenBuffers -#define glGenFencesNV wrap_glGenFencesNV -#define glGenFramebuffers wrap_glGenFramebuffers -#define glGenFramebuffersOES wrap_glGenFramebuffersOES -#define glGenPathsNV wrap_glGenPathsNV -#define glGenPerfMonitorsAMD wrap_glGenPerfMonitorsAMD -#define glGenProgramPipelines wrap_glGenProgramPipelines -#define glGenProgramPipelinesEXT wrap_glGenProgramPipelinesEXT -#define glGenQueries wrap_glGenQueries -#define glGenQueriesEXT wrap_glGenQueriesEXT -#define glGenRenderbuffers wrap_glGenRenderbuffers -#define glGenRenderbuffersOES wrap_glGenRenderbuffersOES -#define glGenSamplers wrap_glGenSamplers -#define glGenTextures wrap_glGenTextures -#define glGenTransformFeedbacks wrap_glGenTransformFeedbacks -#define glGenVertexArrays wrap_glGenVertexArrays -#define glGenVertexArraysOES wrap_glGenVertexArraysOES -#define glGenerateMipmap wrap_glGenerateMipmap -#define glGenerateMipmapOES wrap_glGenerateMipmapOES -#define glGetActiveAttrib wrap_glGetActiveAttrib -#define glGetActiveUniform wrap_glGetActiveUniform -#define glGetActiveUniformBlockName wrap_glGetActiveUniformBlockName -#define glGetActiveUniformBlockiv wrap_glGetActiveUniformBlockiv -#define glGetActiveUniformsiv wrap_glGetActiveUniformsiv -#define glGetAttachedShaders wrap_glGetAttachedShaders -#define glGetAttribLocation wrap_glGetAttribLocation -#define glGetBooleani_v wrap_glGetBooleani_v -#define glGetBooleanv wrap_glGetBooleanv -#define glGetBufferParameteri64v wrap_glGetBufferParameteri64v -#define glGetBufferParameteriv wrap_glGetBufferParameteriv -#define glGetBufferPointerv wrap_glGetBufferPointerv -#define glGetBufferPointervOES wrap_glGetBufferPointervOES -#define glGetClipPlanef wrap_glGetClipPlanef -#define glGetClipPlanefOES wrap_glGetClipPlanefOES -#define glGetClipPlanex wrap_glGetClipPlanex -#define glGetClipPlanexOES wrap_glGetClipPlanexOES -#define glGetCoverageModulationTableNV wrap_glGetCoverageModulationTableNV -#define glGetDebugMessageLog wrap_glGetDebugMessageLog -#define glGetDebugMessageLogKHR wrap_glGetDebugMessageLogKHR -#define glGetDriverControlStringQCOM wrap_glGetDriverControlStringQCOM -#define glGetDriverControlsQCOM wrap_glGetDriverControlsQCOM -#define glGetError wrap_glGetError -#define glGetFenceivNV wrap_glGetFenceivNV -#define glGetFirstPerfQueryIdINTEL wrap_glGetFirstPerfQueryIdINTEL -#define glGetFixedv wrap_glGetFixedv -#define glGetFixedvOES wrap_glGetFixedvOES -#define glGetFloati_vNV wrap_glGetFloati_vNV -#define glGetFloatv wrap_glGetFloatv -#define glGetFragDataIndexEXT wrap_glGetFragDataIndexEXT -#define glGetFragDataLocation wrap_glGetFragDataLocation -#define glGetFramebufferAttachmentParameteriv wrap_glGetFramebufferAttachmentParameteriv -#define glGetFramebufferAttachmentParameterivOES wrap_glGetFramebufferAttachmentParameterivOES -#define glGetFramebufferParameteriv wrap_glGetFramebufferParameteriv -#define glGetGraphicsResetStatus wrap_glGetGraphicsResetStatus -#define glGetGraphicsResetStatusEXT wrap_glGetGraphicsResetStatusEXT -#define glGetGraphicsResetStatusKHR wrap_glGetGraphicsResetStatusKHR -#define glGetImageHandleNV wrap_glGetImageHandleNV -#define glGetInteger64i_v wrap_glGetInteger64i_v -#define glGetInteger64v wrap_glGetInteger64v -#define glGetInteger64vAPPLE wrap_glGetInteger64vAPPLE -#define glGetIntegeri_v wrap_glGetIntegeri_v -#define glGetIntegeri_vEXT wrap_glGetIntegeri_vEXT -#define glGetIntegerv wrap_glGetIntegerv -#define glGetInternalformatSampleivNV wrap_glGetInternalformatSampleivNV -#define glGetInternalformativ wrap_glGetInternalformativ -#define glGetLightfv wrap_glGetLightfv -#define glGetLightxv wrap_glGetLightxv -#define glGetLightxvOES wrap_glGetLightxvOES -#define glGetMaterialfv wrap_glGetMaterialfv -#define glGetMaterialxv wrap_glGetMaterialxv -#define glGetMaterialxvOES wrap_glGetMaterialxvOES -#define glGetMultisamplefv wrap_glGetMultisamplefv -#define glGetNextPerfQueryIdINTEL wrap_glGetNextPerfQueryIdINTEL -#define glGetObjectLabel wrap_glGetObjectLabel -#define glGetObjectLabelEXT wrap_glGetObjectLabelEXT -#define glGetObjectLabelKHR wrap_glGetObjectLabelKHR -#define glGetObjectPtrLabel wrap_glGetObjectPtrLabel -#define glGetObjectPtrLabelKHR wrap_glGetObjectPtrLabelKHR -#define glGetPathCommandsNV wrap_glGetPathCommandsNV -#define glGetPathCoordsNV wrap_glGetPathCoordsNV -#define glGetPathDashArrayNV wrap_glGetPathDashArrayNV -#define glGetPathLengthNV wrap_glGetPathLengthNV -#define glGetPathMetricRangeNV wrap_glGetPathMetricRangeNV -#define glGetPathMetricsNV wrap_glGetPathMetricsNV -#define glGetPathParameterfvNV wrap_glGetPathParameterfvNV -#define glGetPathParameterivNV wrap_glGetPathParameterivNV -#define glGetPathSpacingNV wrap_glGetPathSpacingNV -#define glGetPerfCounterInfoINTEL wrap_glGetPerfCounterInfoINTEL -#define glGetPerfMonitorCounterDataAMD wrap_glGetPerfMonitorCounterDataAMD -#define glGetPerfMonitorCounterInfoAMD wrap_glGetPerfMonitorCounterInfoAMD -#define glGetPerfMonitorCounterStringAMD wrap_glGetPerfMonitorCounterStringAMD -#define glGetPerfMonitorCountersAMD wrap_glGetPerfMonitorCountersAMD -#define glGetPerfMonitorGroupStringAMD wrap_glGetPerfMonitorGroupStringAMD -#define glGetPerfMonitorGroupsAMD wrap_glGetPerfMonitorGroupsAMD -#define glGetPerfQueryDataINTEL wrap_glGetPerfQueryDataINTEL -#define glGetPerfQueryIdByNameINTEL wrap_glGetPerfQueryIdByNameINTEL -#define glGetPerfQueryInfoINTEL wrap_glGetPerfQueryInfoINTEL -#define glGetPointerv wrap_glGetPointerv -#define glGetPointervKHR wrap_glGetPointervKHR -#define glGetProgramBinary wrap_glGetProgramBinary -#define glGetProgramBinaryOES wrap_glGetProgramBinaryOES -#define glGetProgramInfoLog wrap_glGetProgramInfoLog -#define glGetProgramInterfaceiv wrap_glGetProgramInterfaceiv -#define glGetProgramPipelineInfoLog wrap_glGetProgramPipelineInfoLog -#define glGetProgramPipelineInfoLogEXT wrap_glGetProgramPipelineInfoLogEXT -#define glGetProgramPipelineiv wrap_glGetProgramPipelineiv -#define glGetProgramPipelineivEXT wrap_glGetProgramPipelineivEXT -#define glGetProgramResourceIndex wrap_glGetProgramResourceIndex -#define glGetProgramResourceLocation wrap_glGetProgramResourceLocation -#define glGetProgramResourceLocationIndexEXT wrap_glGetProgramResourceLocationIndexEXT -#define glGetProgramResourceName wrap_glGetProgramResourceName -#define glGetProgramResourcefvNV wrap_glGetProgramResourcefvNV -#define glGetProgramResourceiv wrap_glGetProgramResourceiv -#define glGetProgramiv wrap_glGetProgramiv -#define glGetQueryObjecti64vEXT wrap_glGetQueryObjecti64vEXT -#define glGetQueryObjectivEXT wrap_glGetQueryObjectivEXT -#define glGetQueryObjectui64vEXT wrap_glGetQueryObjectui64vEXT -#define glGetQueryObjectuiv wrap_glGetQueryObjectuiv -#define glGetQueryObjectuivEXT wrap_glGetQueryObjectuivEXT -#define glGetQueryiv wrap_glGetQueryiv -#define glGetQueryivEXT wrap_glGetQueryivEXT -#define glGetRenderbufferParameteriv wrap_glGetRenderbufferParameteriv -#define glGetRenderbufferParameterivOES wrap_glGetRenderbufferParameterivOES -#define glGetSamplerParameterIiv wrap_glGetSamplerParameterIiv -#define glGetSamplerParameterIivEXT wrap_glGetSamplerParameterIivEXT -#define glGetSamplerParameterIivOES wrap_glGetSamplerParameterIivOES -#define glGetSamplerParameterIuiv wrap_glGetSamplerParameterIuiv -#define glGetSamplerParameterIuivEXT wrap_glGetSamplerParameterIuivEXT -#define glGetSamplerParameterIuivOES wrap_glGetSamplerParameterIuivOES -#define glGetSamplerParameterfv wrap_glGetSamplerParameterfv -#define glGetSamplerParameteriv wrap_glGetSamplerParameteriv -#define glGetShaderInfoLog wrap_glGetShaderInfoLog -#define glGetShaderPrecisionFormat wrap_glGetShaderPrecisionFormat -#define glGetShaderSource wrap_glGetShaderSource -#define glGetShaderiv wrap_glGetShaderiv -#define glGetString wrap_glGetString -#define glGetStringi wrap_glGetStringi -#define glGetSynciv wrap_glGetSynciv -#define glGetSyncivAPPLE wrap_glGetSyncivAPPLE -#define glGetTexEnvfv wrap_glGetTexEnvfv -#define glGetTexEnviv wrap_glGetTexEnviv -#define glGetTexEnvxv wrap_glGetTexEnvxv -#define glGetTexEnvxvOES wrap_glGetTexEnvxvOES -#define glGetTexGenfvOES wrap_glGetTexGenfvOES -#define glGetTexGenivOES wrap_glGetTexGenivOES -#define glGetTexGenxvOES wrap_glGetTexGenxvOES -#define glGetTexLevelParameterfv wrap_glGetTexLevelParameterfv -#define glGetTexLevelParameteriv wrap_glGetTexLevelParameteriv -#define glGetTexParameterIiv wrap_glGetTexParameterIiv -#define glGetTexParameterIivEXT wrap_glGetTexParameterIivEXT -#define glGetTexParameterIivOES wrap_glGetTexParameterIivOES -#define glGetTexParameterIuiv wrap_glGetTexParameterIuiv -#define glGetTexParameterIuivEXT wrap_glGetTexParameterIuivEXT -#define glGetTexParameterIuivOES wrap_glGetTexParameterIuivOES -#define glGetTexParameterfv wrap_glGetTexParameterfv -#define glGetTexParameteriv wrap_glGetTexParameteriv -#define glGetTexParameterxv wrap_glGetTexParameterxv -#define glGetTexParameterxvOES wrap_glGetTexParameterxvOES -#define glGetTextureHandleNV wrap_glGetTextureHandleNV -#define glGetTextureSamplerHandleNV wrap_glGetTextureSamplerHandleNV -#define glGetTransformFeedbackVarying wrap_glGetTransformFeedbackVarying -#define glGetTranslatedShaderSourceANGLE wrap_glGetTranslatedShaderSourceANGLE -#define glGetUniformBlockIndex wrap_glGetUniformBlockIndex -#define glGetUniformIndices wrap_glGetUniformIndices -#define glGetUniformLocation wrap_glGetUniformLocation -#define glGetUniformfv wrap_glGetUniformfv -#define glGetUniformiv wrap_glGetUniformiv -#define glGetUniformuiv wrap_glGetUniformuiv -#define glGetVertexAttribIiv wrap_glGetVertexAttribIiv -#define glGetVertexAttribIuiv wrap_glGetVertexAttribIuiv -#define glGetVertexAttribPointerv wrap_glGetVertexAttribPointerv -#define glGetVertexAttribfv wrap_glGetVertexAttribfv -#define glGetVertexAttribiv wrap_glGetVertexAttribiv -#define glGetnUniformfv wrap_glGetnUniformfv -#define glGetnUniformfvEXT wrap_glGetnUniformfvEXT -#define glGetnUniformfvKHR wrap_glGetnUniformfvKHR -#define glGetnUniformiv wrap_glGetnUniformiv -#define glGetnUniformivEXT wrap_glGetnUniformivEXT -#define glGetnUniformivKHR wrap_glGetnUniformivKHR -#define glGetnUniformuiv wrap_glGetnUniformuiv -#define glGetnUniformuivKHR wrap_glGetnUniformuivKHR -#define glHint wrap_glHint -#define glInsertEventMarkerEXT wrap_glInsertEventMarkerEXT -#define glInterpolatePathsNV wrap_glInterpolatePathsNV -#define glInvalidateFramebuffer wrap_glInvalidateFramebuffer -#define glInvalidateSubFramebuffer wrap_glInvalidateSubFramebuffer -#define glIsBuffer wrap_glIsBuffer -#define glIsEnabled wrap_glIsEnabled -#define glIsEnabledi wrap_glIsEnabledi -#define glIsEnablediEXT wrap_glIsEnablediEXT -#define glIsEnablediNV wrap_glIsEnablediNV -#define glIsEnablediOES wrap_glIsEnablediOES -#define glIsFenceNV wrap_glIsFenceNV -#define glIsFramebuffer wrap_glIsFramebuffer -#define glIsFramebufferOES wrap_glIsFramebufferOES -#define glIsImageHandleResidentNV wrap_glIsImageHandleResidentNV -#define glIsPathNV wrap_glIsPathNV -#define glIsPointInFillPathNV wrap_glIsPointInFillPathNV -#define glIsPointInStrokePathNV wrap_glIsPointInStrokePathNV -#define glIsProgram wrap_glIsProgram -#define glIsProgramPipeline wrap_glIsProgramPipeline -#define glIsProgramPipelineEXT wrap_glIsProgramPipelineEXT -#define glIsQuery wrap_glIsQuery -#define glIsQueryEXT wrap_glIsQueryEXT -#define glIsRenderbuffer wrap_glIsRenderbuffer -#define glIsRenderbufferOES wrap_glIsRenderbufferOES -#define glIsSampler wrap_glIsSampler -#define glIsShader wrap_glIsShader -#define glIsSync wrap_glIsSync -#define glIsSyncAPPLE wrap_glIsSyncAPPLE -#define glIsTexture wrap_glIsTexture -#define glIsTextureHandleResidentNV wrap_glIsTextureHandleResidentNV -#define glIsTransformFeedback wrap_glIsTransformFeedback -#define glIsVertexArray wrap_glIsVertexArray -#define glIsVertexArrayOES wrap_glIsVertexArrayOES -#define glLabelObjectEXT wrap_glLabelObjectEXT -#define glLightModelf wrap_glLightModelf -#define glLightModelfv wrap_glLightModelfv -#define glLightModelx wrap_glLightModelx -#define glLightModelxOES wrap_glLightModelxOES -#define glLightModelxv wrap_glLightModelxv -#define glLightModelxvOES wrap_glLightModelxvOES -#define glLightf wrap_glLightf -#define glLightfv wrap_glLightfv -#define glLightx wrap_glLightx -#define glLightxOES wrap_glLightxOES -#define glLightxv wrap_glLightxv -#define glLightxvOES wrap_glLightxvOES -#define glLineWidth wrap_glLineWidth -#define glLineWidthx wrap_glLineWidthx -#define glLineWidthxOES wrap_glLineWidthxOES -#define glLinkProgram wrap_glLinkProgram -#define glLoadIdentity wrap_glLoadIdentity -#define glLoadMatrixf wrap_glLoadMatrixf -#define glLoadMatrixx wrap_glLoadMatrixx -#define glLoadMatrixxOES wrap_glLoadMatrixxOES -#define glLoadPaletteFromModelViewMatrixOES wrap_glLoadPaletteFromModelViewMatrixOES -#define glLogicOp wrap_glLogicOp -#define glMakeImageHandleNonResidentNV wrap_glMakeImageHandleNonResidentNV -#define glMakeImageHandleResidentNV wrap_glMakeImageHandleResidentNV -#define glMakeTextureHandleNonResidentNV wrap_glMakeTextureHandleNonResidentNV -#define glMakeTextureHandleResidentNV wrap_glMakeTextureHandleResidentNV -#define glMapBufferOES wrap_glMapBufferOES -#define glMapBufferRange wrap_glMapBufferRange -#define glMapBufferRangeEXT wrap_glMapBufferRangeEXT -#define glMaterialf wrap_glMaterialf -#define glMaterialfv wrap_glMaterialfv -#define glMaterialx wrap_glMaterialx -#define glMaterialxOES wrap_glMaterialxOES -#define glMaterialxv wrap_glMaterialxv -#define glMaterialxvOES wrap_glMaterialxvOES -#define glMatrixIndexPointerOES wrap_glMatrixIndexPointerOES -#define glMatrixLoad3x2fNV wrap_glMatrixLoad3x2fNV -#define glMatrixLoad3x3fNV wrap_glMatrixLoad3x3fNV -#define glMatrixLoadTranspose3x3fNV wrap_glMatrixLoadTranspose3x3fNV -#define glMatrixMode wrap_glMatrixMode -#define glMatrixMult3x2fNV wrap_glMatrixMult3x2fNV -#define glMatrixMult3x3fNV wrap_glMatrixMult3x3fNV -#define glMatrixMultTranspose3x3fNV wrap_glMatrixMultTranspose3x3fNV -#define glMemoryBarrier wrap_glMemoryBarrier -#define glMemoryBarrierByRegion wrap_glMemoryBarrierByRegion -#define glMinSampleShading wrap_glMinSampleShading -#define glMinSampleShadingOES wrap_glMinSampleShadingOES -#define glMultMatrixf wrap_glMultMatrixf -#define glMultMatrixx wrap_glMultMatrixx -#define glMultMatrixxOES wrap_glMultMatrixxOES -#define glMultiDrawArraysEXT wrap_glMultiDrawArraysEXT -#define glMultiDrawArraysIndirectEXT wrap_glMultiDrawArraysIndirectEXT -#define glMultiDrawElementsBaseVertexEXT wrap_glMultiDrawElementsBaseVertexEXT -#define glMultiDrawElementsBaseVertexOES wrap_glMultiDrawElementsBaseVertexOES -#define glMultiDrawElementsEXT wrap_glMultiDrawElementsEXT -#define glMultiDrawElementsIndirectEXT wrap_glMultiDrawElementsIndirectEXT -#define glMultiTexCoord4f wrap_glMultiTexCoord4f -#define glMultiTexCoord4x wrap_glMultiTexCoord4x -#define glMultiTexCoord4xOES wrap_glMultiTexCoord4xOES -#define glNamedFramebufferSampleLocationsfvNV wrap_glNamedFramebufferSampleLocationsfvNV -#define glNormal3f wrap_glNormal3f -#define glNormal3x wrap_glNormal3x -#define glNormal3xOES wrap_glNormal3xOES -#define glNormalPointer wrap_glNormalPointer -#define glObjectLabel wrap_glObjectLabel -#define glObjectLabelKHR wrap_glObjectLabelKHR -#define glObjectPtrLabel wrap_glObjectPtrLabel -#define glObjectPtrLabelKHR wrap_glObjectPtrLabelKHR -#define glOrthof wrap_glOrthof -#define glOrthofOES wrap_glOrthofOES -#define glOrthox wrap_glOrthox -#define glOrthoxOES wrap_glOrthoxOES -#define glPatchParameteri wrap_glPatchParameteri -#define glPatchParameteriEXT wrap_glPatchParameteriEXT -#define glPatchParameteriOES wrap_glPatchParameteriOES -#define glPathCommandsNV wrap_glPathCommandsNV -#define glPathCoordsNV wrap_glPathCoordsNV -#define glPathCoverDepthFuncNV wrap_glPathCoverDepthFuncNV -#define glPathDashArrayNV wrap_glPathDashArrayNV -#define glPathGlyphIndexArrayNV wrap_glPathGlyphIndexArrayNV -#define glPathGlyphIndexRangeNV wrap_glPathGlyphIndexRangeNV -#define glPathGlyphRangeNV wrap_glPathGlyphRangeNV -#define glPathGlyphsNV wrap_glPathGlyphsNV -#define glPathMemoryGlyphIndexArrayNV wrap_glPathMemoryGlyphIndexArrayNV -#define glPathParameterfNV wrap_glPathParameterfNV -#define glPathParameterfvNV wrap_glPathParameterfvNV -#define glPathParameteriNV wrap_glPathParameteriNV -#define glPathParameterivNV wrap_glPathParameterivNV -#define glPathStencilDepthOffsetNV wrap_glPathStencilDepthOffsetNV -#define glPathStencilFuncNV wrap_glPathStencilFuncNV -#define glPathStringNV wrap_glPathStringNV -#define glPathSubCommandsNV wrap_glPathSubCommandsNV -#define glPathSubCoordsNV wrap_glPathSubCoordsNV -#define glPauseTransformFeedback wrap_glPauseTransformFeedback -#define glPixelStorei wrap_glPixelStorei -#define glPointAlongPathNV wrap_glPointAlongPathNV -#define glPointParameterf wrap_glPointParameterf -#define glPointParameterfv wrap_glPointParameterfv -#define glPointParameterx wrap_glPointParameterx -#define glPointParameterxOES wrap_glPointParameterxOES -#define glPointParameterxv wrap_glPointParameterxv -#define glPointParameterxvOES wrap_glPointParameterxvOES -#define glPointSize wrap_glPointSize -#define glPointSizePointerOES wrap_glPointSizePointerOES -#define glPointSizex wrap_glPointSizex -#define glPointSizexOES wrap_glPointSizexOES -#define glPolygonModeNV wrap_glPolygonModeNV -#define glPolygonOffset wrap_glPolygonOffset -#define glPolygonOffsetx wrap_glPolygonOffsetx -#define glPolygonOffsetxOES wrap_glPolygonOffsetxOES -#define glPopDebugGroup wrap_glPopDebugGroup -#define glPopDebugGroupKHR wrap_glPopDebugGroupKHR -#define glPopGroupMarkerEXT wrap_glPopGroupMarkerEXT -#define glPopMatrix wrap_glPopMatrix -#define glPrimitiveBoundingBox wrap_glPrimitiveBoundingBox -#define glPrimitiveBoundingBoxEXT wrap_glPrimitiveBoundingBoxEXT -#define glPrimitiveBoundingBoxOES wrap_glPrimitiveBoundingBoxOES -#define glProgramBinary wrap_glProgramBinary -#define glProgramBinaryOES wrap_glProgramBinaryOES -#define glProgramParameteri wrap_glProgramParameteri -#define glProgramParameteriEXT wrap_glProgramParameteriEXT -#define glProgramPathFragmentInputGenNV wrap_glProgramPathFragmentInputGenNV -#define glProgramUniform1f wrap_glProgramUniform1f -#define glProgramUniform1fEXT wrap_glProgramUniform1fEXT -#define glProgramUniform1fv wrap_glProgramUniform1fv -#define glProgramUniform1fvEXT wrap_glProgramUniform1fvEXT -#define glProgramUniform1i wrap_glProgramUniform1i -#define glProgramUniform1iEXT wrap_glProgramUniform1iEXT -#define glProgramUniform1iv wrap_glProgramUniform1iv -#define glProgramUniform1ivEXT wrap_glProgramUniform1ivEXT -#define glProgramUniform1ui wrap_glProgramUniform1ui -#define glProgramUniform1uiEXT wrap_glProgramUniform1uiEXT -#define glProgramUniform1uiv wrap_glProgramUniform1uiv -#define glProgramUniform1uivEXT wrap_glProgramUniform1uivEXT -#define glProgramUniform2f wrap_glProgramUniform2f -#define glProgramUniform2fEXT wrap_glProgramUniform2fEXT -#define glProgramUniform2fv wrap_glProgramUniform2fv -#define glProgramUniform2fvEXT wrap_glProgramUniform2fvEXT -#define glProgramUniform2i wrap_glProgramUniform2i -#define glProgramUniform2iEXT wrap_glProgramUniform2iEXT -#define glProgramUniform2iv wrap_glProgramUniform2iv -#define glProgramUniform2ivEXT wrap_glProgramUniform2ivEXT -#define glProgramUniform2ui wrap_glProgramUniform2ui -#define glProgramUniform2uiEXT wrap_glProgramUniform2uiEXT -#define glProgramUniform2uiv wrap_glProgramUniform2uiv -#define glProgramUniform2uivEXT wrap_glProgramUniform2uivEXT -#define glProgramUniform3f wrap_glProgramUniform3f -#define glProgramUniform3fEXT wrap_glProgramUniform3fEXT -#define glProgramUniform3fv wrap_glProgramUniform3fv -#define glProgramUniform3fvEXT wrap_glProgramUniform3fvEXT -#define glProgramUniform3i wrap_glProgramUniform3i -#define glProgramUniform3iEXT wrap_glProgramUniform3iEXT -#define glProgramUniform3iv wrap_glProgramUniform3iv -#define glProgramUniform3ivEXT wrap_glProgramUniform3ivEXT -#define glProgramUniform3ui wrap_glProgramUniform3ui -#define glProgramUniform3uiEXT wrap_glProgramUniform3uiEXT -#define glProgramUniform3uiv wrap_glProgramUniform3uiv -#define glProgramUniform3uivEXT wrap_glProgramUniform3uivEXT -#define glProgramUniform4f wrap_glProgramUniform4f -#define glProgramUniform4fEXT wrap_glProgramUniform4fEXT -#define glProgramUniform4fv wrap_glProgramUniform4fv -#define glProgramUniform4fvEXT wrap_glProgramUniform4fvEXT -#define glProgramUniform4i wrap_glProgramUniform4i -#define glProgramUniform4iEXT wrap_glProgramUniform4iEXT -#define glProgramUniform4iv wrap_glProgramUniform4iv -#define glProgramUniform4ivEXT wrap_glProgramUniform4ivEXT -#define glProgramUniform4ui wrap_glProgramUniform4ui -#define glProgramUniform4uiEXT wrap_glProgramUniform4uiEXT -#define glProgramUniform4uiv wrap_glProgramUniform4uiv -#define glProgramUniform4uivEXT wrap_glProgramUniform4uivEXT -#define glProgramUniformHandleui64NV wrap_glProgramUniformHandleui64NV -#define glProgramUniformHandleui64vNV wrap_glProgramUniformHandleui64vNV -#define glProgramUniformMatrix2fv wrap_glProgramUniformMatrix2fv -#define glProgramUniformMatrix2fvEXT wrap_glProgramUniformMatrix2fvEXT -#define glProgramUniformMatrix2x3fv wrap_glProgramUniformMatrix2x3fv -#define glProgramUniformMatrix2x3fvEXT wrap_glProgramUniformMatrix2x3fvEXT -#define glProgramUniformMatrix2x4fv wrap_glProgramUniformMatrix2x4fv -#define glProgramUniformMatrix2x4fvEXT wrap_glProgramUniformMatrix2x4fvEXT -#define glProgramUniformMatrix3fv wrap_glProgramUniformMatrix3fv -#define glProgramUniformMatrix3fvEXT wrap_glProgramUniformMatrix3fvEXT -#define glProgramUniformMatrix3x2fv wrap_glProgramUniformMatrix3x2fv -#define glProgramUniformMatrix3x2fvEXT wrap_glProgramUniformMatrix3x2fvEXT -#define glProgramUniformMatrix3x4fv wrap_glProgramUniformMatrix3x4fv -#define glProgramUniformMatrix3x4fvEXT wrap_glProgramUniformMatrix3x4fvEXT -#define glProgramUniformMatrix4fv wrap_glProgramUniformMatrix4fv -#define glProgramUniformMatrix4fvEXT wrap_glProgramUniformMatrix4fvEXT -#define glProgramUniformMatrix4x2fv wrap_glProgramUniformMatrix4x2fv -#define glProgramUniformMatrix4x2fvEXT wrap_glProgramUniformMatrix4x2fvEXT -#define glProgramUniformMatrix4x3fv wrap_glProgramUniformMatrix4x3fv -#define glProgramUniformMatrix4x3fvEXT wrap_glProgramUniformMatrix4x3fvEXT -#define glPushDebugGroup wrap_glPushDebugGroup -#define glPushDebugGroupKHR wrap_glPushDebugGroupKHR -#define glPushGroupMarkerEXT wrap_glPushGroupMarkerEXT -#define glPushMatrix wrap_glPushMatrix -#define glQueryCounterEXT wrap_glQueryCounterEXT -#define glQueryMatrixxOES wrap_glQueryMatrixxOES -#define glRasterSamplesEXT wrap_glRasterSamplesEXT -#define glReadBuffer wrap_glReadBuffer -#define glReadBufferIndexedEXT wrap_glReadBufferIndexedEXT -#define glReadBufferNV wrap_glReadBufferNV -#define glReadPixels wrap_glReadPixels -#define glReadnPixels wrap_glReadnPixels -#define glReadnPixelsEXT wrap_glReadnPixelsEXT -#define glReadnPixelsKHR wrap_glReadnPixelsKHR -#define glReleaseShaderCompiler wrap_glReleaseShaderCompiler -#define glRenderbufferStorage wrap_glRenderbufferStorage -#define glRenderbufferStorageMultisample wrap_glRenderbufferStorageMultisample -#define glRenderbufferStorageMultisampleANGLE wrap_glRenderbufferStorageMultisampleANGLE -#define glRenderbufferStorageMultisampleAPPLE wrap_glRenderbufferStorageMultisampleAPPLE -#define glRenderbufferStorageMultisampleEXT wrap_glRenderbufferStorageMultisampleEXT -#define glRenderbufferStorageMultisampleIMG wrap_glRenderbufferStorageMultisampleIMG -#define glRenderbufferStorageMultisampleNV wrap_glRenderbufferStorageMultisampleNV -#define glRenderbufferStorageOES wrap_glRenderbufferStorageOES -#define glResolveDepthValuesNV wrap_glResolveDepthValuesNV -#define glResolveMultisampleFramebufferAPPLE wrap_glResolveMultisampleFramebufferAPPLE -#define glResumeTransformFeedback wrap_glResumeTransformFeedback -#define glRotatef wrap_glRotatef -#define glRotatex wrap_glRotatex -#define glRotatexOES wrap_glRotatexOES -#define glSampleCoverage wrap_glSampleCoverage -#define glSampleCoveragex wrap_glSampleCoveragex -#define glSampleCoveragexOES wrap_glSampleCoveragexOES -#define glSampleMaski wrap_glSampleMaski -#define glSamplerParameterIiv wrap_glSamplerParameterIiv -#define glSamplerParameterIivEXT wrap_glSamplerParameterIivEXT -#define glSamplerParameterIivOES wrap_glSamplerParameterIivOES -#define glSamplerParameterIuiv wrap_glSamplerParameterIuiv -#define glSamplerParameterIuivEXT wrap_glSamplerParameterIuivEXT -#define glSamplerParameterIuivOES wrap_glSamplerParameterIuivOES -#define glSamplerParameterf wrap_glSamplerParameterf -#define glSamplerParameterfv wrap_glSamplerParameterfv -#define glSamplerParameteri wrap_glSamplerParameteri -#define glSamplerParameteriv wrap_glSamplerParameteriv -#define glScalef wrap_glScalef -#define glScalex wrap_glScalex -#define glScalexOES wrap_glScalexOES -#define glScissor wrap_glScissor -#define glScissorArrayvNV wrap_glScissorArrayvNV -#define glScissorIndexedNV wrap_glScissorIndexedNV -#define glScissorIndexedvNV wrap_glScissorIndexedvNV -#define glSelectPerfMonitorCountersAMD wrap_glSelectPerfMonitorCountersAMD -#define glSetFenceNV wrap_glSetFenceNV -#define glShadeModel wrap_glShadeModel -#define glShaderBinary wrap_glShaderBinary -#define glShaderSource wrap_glShaderSource -#define glStartTilingQCOM wrap_glStartTilingQCOM -#define glStencilFillPathInstancedNV wrap_glStencilFillPathInstancedNV -#define glStencilFillPathNV wrap_glStencilFillPathNV -#define glStencilFunc wrap_glStencilFunc -#define glStencilFuncSeparate wrap_glStencilFuncSeparate -#define glStencilMask wrap_glStencilMask -#define glStencilMaskSeparate wrap_glStencilMaskSeparate -#define glStencilOp wrap_glStencilOp -#define glStencilOpSeparate wrap_glStencilOpSeparate -#define glStencilStrokePathInstancedNV wrap_glStencilStrokePathInstancedNV -#define glStencilStrokePathNV wrap_glStencilStrokePathNV -#define glStencilThenCoverFillPathInstancedNV wrap_glStencilThenCoverFillPathInstancedNV -#define glStencilThenCoverFillPathNV wrap_glStencilThenCoverFillPathNV -#define glStencilThenCoverStrokePathInstancedNV wrap_glStencilThenCoverStrokePathInstancedNV -#define glStencilThenCoverStrokePathNV wrap_glStencilThenCoverStrokePathNV -#define glSubpixelPrecisionBiasNV wrap_glSubpixelPrecisionBiasNV -#define glTestFenceNV wrap_glTestFenceNV -#define glTexBuffer wrap_glTexBuffer -#define glTexBufferEXT wrap_glTexBufferEXT -#define glTexBufferOES wrap_glTexBufferOES -#define glTexBufferRange wrap_glTexBufferRange -#define glTexBufferRangeEXT wrap_glTexBufferRangeEXT -#define glTexBufferRangeOES wrap_glTexBufferRangeOES -#define glTexCoordPointer wrap_glTexCoordPointer -#define glTexEnvf wrap_glTexEnvf -#define glTexEnvfv wrap_glTexEnvfv -#define glTexEnvi wrap_glTexEnvi -#define glTexEnviv wrap_glTexEnviv -#define glTexEnvx wrap_glTexEnvx -#define glTexEnvxOES wrap_glTexEnvxOES -#define glTexEnvxv wrap_glTexEnvxv -#define glTexEnvxvOES wrap_glTexEnvxvOES -#define glTexGenfOES wrap_glTexGenfOES -#define glTexGenfvOES wrap_glTexGenfvOES -#define glTexGeniOES wrap_glTexGeniOES -#define glTexGenivOES wrap_glTexGenivOES -#define glTexGenxOES wrap_glTexGenxOES -#define glTexGenxvOES wrap_glTexGenxvOES -#define glTexImage2D wrap_glTexImage2D -#define glTexImage3D wrap_glTexImage3D -#define glTexImage3DOES wrap_glTexImage3DOES -#define glTexPageCommitmentEXT wrap_glTexPageCommitmentEXT -#define glTexParameterIiv wrap_glTexParameterIiv -#define glTexParameterIivEXT wrap_glTexParameterIivEXT -#define glTexParameterIivOES wrap_glTexParameterIivOES -#define glTexParameterIuiv wrap_glTexParameterIuiv -#define glTexParameterIuivEXT wrap_glTexParameterIuivEXT -#define glTexParameterIuivOES wrap_glTexParameterIuivOES -#define glTexParameterf wrap_glTexParameterf -#define glTexParameterfv wrap_glTexParameterfv -#define glTexParameteri wrap_glTexParameteri -#define glTexParameteriv wrap_glTexParameteriv -#define glTexParameterx wrap_glTexParameterx -#define glTexParameterxOES wrap_glTexParameterxOES -#define glTexParameterxv wrap_glTexParameterxv -#define glTexParameterxvOES wrap_glTexParameterxvOES -#define glTexStorage1DEXT wrap_glTexStorage1DEXT -#define glTexStorage2D wrap_glTexStorage2D -#define glTexStorage2DEXT wrap_glTexStorage2DEXT -#define glTexStorage2DMultisample wrap_glTexStorage2DMultisample -#define glTexStorage3D wrap_glTexStorage3D -#define glTexStorage3DEXT wrap_glTexStorage3DEXT -#define glTexStorage3DMultisample wrap_glTexStorage3DMultisample -#define glTexStorage3DMultisampleOES wrap_glTexStorage3DMultisampleOES -#define glTexSubImage2D wrap_glTexSubImage2D -#define glTexSubImage3D wrap_glTexSubImage3D -#define glTexSubImage3DOES wrap_glTexSubImage3DOES -#define glTextureStorage1DEXT wrap_glTextureStorage1DEXT -#define glTextureStorage2DEXT wrap_glTextureStorage2DEXT -#define glTextureStorage3DEXT wrap_glTextureStorage3DEXT -#define glTextureViewEXT wrap_glTextureViewEXT -#define glTextureViewOES wrap_glTextureViewOES -#define glTransformFeedbackVaryings wrap_glTransformFeedbackVaryings -#define glTransformPathNV wrap_glTransformPathNV -#define glTranslatef wrap_glTranslatef -#define glTranslatex wrap_glTranslatex -#define glTranslatexOES wrap_glTranslatexOES -#define glUniform1f wrap_glUniform1f -#define glUniform1fv wrap_glUniform1fv -#define glUniform1i wrap_glUniform1i -#define glUniform1iv wrap_glUniform1iv -#define glUniform1ui wrap_glUniform1ui -#define glUniform1uiv wrap_glUniform1uiv -#define glUniform2f wrap_glUniform2f -#define glUniform2fv wrap_glUniform2fv -#define glUniform2i wrap_glUniform2i -#define glUniform2iv wrap_glUniform2iv -#define glUniform2ui wrap_glUniform2ui -#define glUniform2uiv wrap_glUniform2uiv -#define glUniform3f wrap_glUniform3f -#define glUniform3fv wrap_glUniform3fv -#define glUniform3i wrap_glUniform3i -#define glUniform3iv wrap_glUniform3iv -#define glUniform3ui wrap_glUniform3ui -#define glUniform3uiv wrap_glUniform3uiv -#define glUniform4f wrap_glUniform4f -#define glUniform4fv wrap_glUniform4fv -#define glUniform4i wrap_glUniform4i -#define glUniform4iv wrap_glUniform4iv -#define glUniform4ui wrap_glUniform4ui -#define glUniform4uiv wrap_glUniform4uiv -#define glUniformBlockBinding wrap_glUniformBlockBinding -#define glUniformHandleui64NV wrap_glUniformHandleui64NV -#define glUniformHandleui64vNV wrap_glUniformHandleui64vNV -#define glUniformMatrix2fv wrap_glUniformMatrix2fv -#define glUniformMatrix2x3fv wrap_glUniformMatrix2x3fv -#define glUniformMatrix2x3fvNV wrap_glUniformMatrix2x3fvNV -#define glUniformMatrix2x4fv wrap_glUniformMatrix2x4fv -#define glUniformMatrix2x4fvNV wrap_glUniformMatrix2x4fvNV -#define glUniformMatrix3fv wrap_glUniformMatrix3fv -#define glUniformMatrix3x2fv wrap_glUniformMatrix3x2fv -#define glUniformMatrix3x2fvNV wrap_glUniformMatrix3x2fvNV -#define glUniformMatrix3x4fv wrap_glUniformMatrix3x4fv -#define glUniformMatrix3x4fvNV wrap_glUniformMatrix3x4fvNV -#define glUniformMatrix4fv wrap_glUniformMatrix4fv -#define glUniformMatrix4x2fv wrap_glUniformMatrix4x2fv -#define glUniformMatrix4x2fvNV wrap_glUniformMatrix4x2fvNV -#define glUniformMatrix4x3fv wrap_glUniformMatrix4x3fv -#define glUniformMatrix4x3fvNV wrap_glUniformMatrix4x3fvNV -#define glUnmapBuffer wrap_glUnmapBuffer -#define glUnmapBufferOES wrap_glUnmapBufferOES -#define glUseProgram wrap_glUseProgram -#define glUseProgramStages wrap_glUseProgramStages -#define glUseProgramStagesEXT wrap_glUseProgramStagesEXT -#define glValidateProgram wrap_glValidateProgram -#define glValidateProgramPipeline wrap_glValidateProgramPipeline -#define glValidateProgramPipelineEXT wrap_glValidateProgramPipelineEXT -#define glVertexAttrib1f wrap_glVertexAttrib1f -#define glVertexAttrib1fv wrap_glVertexAttrib1fv -#define glVertexAttrib2f wrap_glVertexAttrib2f -#define glVertexAttrib2fv wrap_glVertexAttrib2fv -#define glVertexAttrib3f wrap_glVertexAttrib3f -#define glVertexAttrib3fv wrap_glVertexAttrib3fv -#define glVertexAttrib4f wrap_glVertexAttrib4f -#define glVertexAttrib4fv wrap_glVertexAttrib4fv -#define glVertexAttribBinding wrap_glVertexAttribBinding -#define glVertexAttribDivisor wrap_glVertexAttribDivisor -#define glVertexAttribDivisorANGLE wrap_glVertexAttribDivisorANGLE -#define glVertexAttribDivisorEXT wrap_glVertexAttribDivisorEXT -#define glVertexAttribDivisorNV wrap_glVertexAttribDivisorNV -#define glVertexAttribFormat wrap_glVertexAttribFormat -#define glVertexAttribI4i wrap_glVertexAttribI4i -#define glVertexAttribI4iv wrap_glVertexAttribI4iv -#define glVertexAttribI4ui wrap_glVertexAttribI4ui -#define glVertexAttribI4uiv wrap_glVertexAttribI4uiv -#define glVertexAttribIFormat wrap_glVertexAttribIFormat -#define glVertexAttribIPointer wrap_glVertexAttribIPointer -#define glVertexAttribPointer wrap_glVertexAttribPointer -#define glVertexBindingDivisor wrap_glVertexBindingDivisor -#define glVertexPointer wrap_glVertexPointer -#define glViewport wrap_glViewport -#define glViewportArrayvNV wrap_glViewportArrayvNV -#define glViewportIndexedfNV wrap_glViewportIndexedfNV -#define glViewportIndexedfvNV wrap_glViewportIndexedfvNV -#define glWaitSync wrap_glWaitSync -#define glWaitSyncAPPLE wrap_glWaitSyncAPPLE -#define glWeightPathsNV wrap_glWeightPathsNV -#define glWeightPointerOES wrap_glWeightPointerOES
\ No newline at end of file diff --git a/libs/hwui/debug/gles_stubs.in b/libs/hwui/debug/gles_stubs.in deleted file mode 100644 index 7cba0c115814..000000000000 --- a/libs/hwui/debug/gles_stubs.in +++ /dev/null @@ -1,1629 +0,0 @@ -void API_ENTRY(glActiveTexture)(GLenum texture) { - CALL_GL_API(glActiveTexture, texture); -} -void API_ENTRY(glAttachShader)(GLuint program, GLuint shader) { - CALL_GL_API(glAttachShader, program, shader); -} -void API_ENTRY(glBindAttribLocation)(GLuint program, GLuint index, const GLchar *name) { - CALL_GL_API(glBindAttribLocation, program, index, name); -} -void API_ENTRY(glBindBuffer)(GLenum target, GLuint buffer) { - CALL_GL_API(glBindBuffer, target, buffer); -} -void API_ENTRY(glBindFramebuffer)(GLenum target, GLuint framebuffer) { - CALL_GL_API(glBindFramebuffer, target, framebuffer); -} -void API_ENTRY(glBindRenderbuffer)(GLenum target, GLuint renderbuffer) { - CALL_GL_API(glBindRenderbuffer, target, renderbuffer); -} -void API_ENTRY(glBindTexture)(GLenum target, GLuint texture) { - CALL_GL_API(glBindTexture, target, texture); -} -void API_ENTRY(glBlendColor)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) { - CALL_GL_API(glBlendColor, red, green, blue, alpha); -} -void API_ENTRY(glBlendEquation)(GLenum mode) { - CALL_GL_API(glBlendEquation, mode); -} -void API_ENTRY(glBlendEquationSeparate)(GLenum modeRGB, GLenum modeAlpha) { - CALL_GL_API(glBlendEquationSeparate, modeRGB, modeAlpha); -} -void API_ENTRY(glBlendFunc)(GLenum sfactor, GLenum dfactor) { - CALL_GL_API(glBlendFunc, sfactor, dfactor); -} -void API_ENTRY(glBlendFuncSeparate)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) { - CALL_GL_API(glBlendFuncSeparate, sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha); -} -void API_ENTRY(glBufferData)(GLenum target, GLsizeiptr size, const void *data, GLenum usage) { - CALL_GL_API(glBufferData, target, size, data, usage); -} -void API_ENTRY(glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const void *data) { - CALL_GL_API(glBufferSubData, target, offset, size, data); -} -GLenum API_ENTRY(glCheckFramebufferStatus)(GLenum target) { - CALL_GL_API_RETURN(glCheckFramebufferStatus, target); -} -void API_ENTRY(glClear)(GLbitfield mask) { - CALL_GL_API(glClear, mask); -} -void API_ENTRY(glClearColor)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) { - CALL_GL_API(glClearColor, red, green, blue, alpha); -} -void API_ENTRY(glClearDepthf)(GLfloat d) { - CALL_GL_API(glClearDepthf, d); -} -void API_ENTRY(glClearStencil)(GLint s) { - CALL_GL_API(glClearStencil, s); -} -void API_ENTRY(glColorMask)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) { - CALL_GL_API(glColorMask, red, green, blue, alpha); -} -void API_ENTRY(glCompileShader)(GLuint shader) { - CALL_GL_API(glCompileShader, shader); -} -void API_ENTRY(glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data) { - CALL_GL_API(glCompressedTexImage2D, target, level, internalformat, width, height, border, imageSize, data); -} -void API_ENTRY(glCompressedTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data) { - CALL_GL_API(glCompressedTexSubImage2D, target, level, xoffset, yoffset, width, height, format, imageSize, data); -} -void API_ENTRY(glCopyTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) { - CALL_GL_API(glCopyTexImage2D, target, level, internalformat, x, y, width, height, border); -} -void API_ENTRY(glCopyTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) { - CALL_GL_API(glCopyTexSubImage2D, target, level, xoffset, yoffset, x, y, width, height); -} -GLuint API_ENTRY(glCreateProgram)(void) { - CALL_GL_API_RETURN(glCreateProgram); -} -GLuint API_ENTRY(glCreateShader)(GLenum type) { - CALL_GL_API_RETURN(glCreateShader, type); -} -void API_ENTRY(glCullFace)(GLenum mode) { - CALL_GL_API(glCullFace, mode); -} -void API_ENTRY(glDeleteBuffers)(GLsizei n, const GLuint *buffers) { - CALL_GL_API(glDeleteBuffers, n, buffers); -} -void API_ENTRY(glDeleteFramebuffers)(GLsizei n, const GLuint *framebuffers) { - CALL_GL_API(glDeleteFramebuffers, n, framebuffers); -} -void API_ENTRY(glDeleteProgram)(GLuint program) { - CALL_GL_API(glDeleteProgram, program); -} -void API_ENTRY(glDeleteRenderbuffers)(GLsizei n, const GLuint *renderbuffers) { - CALL_GL_API(glDeleteRenderbuffers, n, renderbuffers); -} -void API_ENTRY(glDeleteShader)(GLuint shader) { - CALL_GL_API(glDeleteShader, shader); -} -void API_ENTRY(glDeleteTextures)(GLsizei n, const GLuint *textures) { - CALL_GL_API(glDeleteTextures, n, textures); -} -void API_ENTRY(glDepthFunc)(GLenum func) { - CALL_GL_API(glDepthFunc, func); -} -void API_ENTRY(glDepthMask)(GLboolean flag) { - CALL_GL_API(glDepthMask, flag); -} -void API_ENTRY(glDepthRangef)(GLfloat n, GLfloat f) { - CALL_GL_API(glDepthRangef, n, f); -} -void API_ENTRY(glDetachShader)(GLuint program, GLuint shader) { - CALL_GL_API(glDetachShader, program, shader); -} -void API_ENTRY(glDisable)(GLenum cap) { - CALL_GL_API(glDisable, cap); -} -void API_ENTRY(glDisableVertexAttribArray)(GLuint index) { - CALL_GL_API(glDisableVertexAttribArray, index); -} -void API_ENTRY(glDrawArrays)(GLenum mode, GLint first, GLsizei count) { - CALL_GL_API(glDrawArrays, mode, first, count); -} -void API_ENTRY(glDrawElements)(GLenum mode, GLsizei count, GLenum type, const void *indices) { - CALL_GL_API(glDrawElements, mode, count, type, indices); -} -void API_ENTRY(glEnable)(GLenum cap) { - CALL_GL_API(glEnable, cap); -} -void API_ENTRY(glEnableVertexAttribArray)(GLuint index) { - CALL_GL_API(glEnableVertexAttribArray, index); -} -void API_ENTRY(glFinish)(void) { - CALL_GL_API(glFinish); -} -void API_ENTRY(glFlush)(void) { - CALL_GL_API(glFlush); -} -void API_ENTRY(glFramebufferRenderbuffer)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) { - CALL_GL_API(glFramebufferRenderbuffer, target, attachment, renderbuffertarget, renderbuffer); -} -void API_ENTRY(glFramebufferTexture2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) { - CALL_GL_API(glFramebufferTexture2D, target, attachment, textarget, texture, level); -} -void API_ENTRY(glFrontFace)(GLenum mode) { - CALL_GL_API(glFrontFace, mode); -} -void API_ENTRY(glGenBuffers)(GLsizei n, GLuint *buffers) { - CALL_GL_API(glGenBuffers, n, buffers); -} -void API_ENTRY(glGenerateMipmap)(GLenum target) { - CALL_GL_API(glGenerateMipmap, target); -} -void API_ENTRY(glGenFramebuffers)(GLsizei n, GLuint *framebuffers) { - CALL_GL_API(glGenFramebuffers, n, framebuffers); -} -void API_ENTRY(glGenRenderbuffers)(GLsizei n, GLuint *renderbuffers) { - CALL_GL_API(glGenRenderbuffers, n, renderbuffers); -} -void API_ENTRY(glGenTextures)(GLsizei n, GLuint *textures) { - CALL_GL_API(glGenTextures, n, textures); -} -void API_ENTRY(glGetActiveAttrib)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) { - CALL_GL_API(glGetActiveAttrib, program, index, bufSize, length, size, type, name); -} -void API_ENTRY(glGetActiveUniform)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) { - CALL_GL_API(glGetActiveUniform, program, index, bufSize, length, size, type, name); -} -void API_ENTRY(glGetAttachedShaders)(GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders) { - CALL_GL_API(glGetAttachedShaders, program, maxCount, count, shaders); -} -GLint API_ENTRY(glGetAttribLocation)(GLuint program, const GLchar *name) { - CALL_GL_API_RETURN(glGetAttribLocation, program, name); -} -void API_ENTRY(glGetBooleanv)(GLenum pname, GLboolean *data) { - CALL_GL_API(glGetBooleanv, pname, data); -} -void API_ENTRY(glGetBufferParameteriv)(GLenum target, GLenum pname, GLint *params) { - CALL_GL_API(glGetBufferParameteriv, target, pname, params); -} -GLenum API_ENTRY(glGetError)(void) { - CALL_GL_API_RETURN(glGetError); -} -void API_ENTRY(glGetFloatv)(GLenum pname, GLfloat *data) { - CALL_GL_API(glGetFloatv, pname, data); -} -void API_ENTRY(glGetFramebufferAttachmentParameteriv)(GLenum target, GLenum attachment, GLenum pname, GLint *params) { - CALL_GL_API(glGetFramebufferAttachmentParameteriv, target, attachment, pname, params); -} -void API_ENTRY(glGetIntegerv)(GLenum pname, GLint *data) { - CALL_GL_API(glGetIntegerv, pname, data); -} -void API_ENTRY(glGetProgramiv)(GLuint program, GLenum pname, GLint *params) { - CALL_GL_API(glGetProgramiv, program, pname, params); -} -void API_ENTRY(glGetProgramInfoLog)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog) { - CALL_GL_API(glGetProgramInfoLog, program, bufSize, length, infoLog); -} -void API_ENTRY(glGetRenderbufferParameteriv)(GLenum target, GLenum pname, GLint *params) { - CALL_GL_API(glGetRenderbufferParameteriv, target, pname, params); -} -void API_ENTRY(glGetShaderiv)(GLuint shader, GLenum pname, GLint *params) { - CALL_GL_API(glGetShaderiv, shader, pname, params); -} -void API_ENTRY(glGetShaderInfoLog)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) { - CALL_GL_API(glGetShaderInfoLog, shader, bufSize, length, infoLog); -} -void API_ENTRY(glGetShaderPrecisionFormat)(GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision) { - CALL_GL_API(glGetShaderPrecisionFormat, shadertype, precisiontype, range, precision); -} -void API_ENTRY(glGetShaderSource)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source) { - CALL_GL_API(glGetShaderSource, shader, bufSize, length, source); -} -const GLubyte * API_ENTRY(glGetString)(GLenum name) { - CALL_GL_API_RETURN(glGetString, name); -} -void API_ENTRY(glGetTexParameterfv)(GLenum target, GLenum pname, GLfloat *params) { - CALL_GL_API(glGetTexParameterfv, target, pname, params); -} -void API_ENTRY(glGetTexParameteriv)(GLenum target, GLenum pname, GLint *params) { - CALL_GL_API(glGetTexParameteriv, target, pname, params); -} -void API_ENTRY(glGetUniformfv)(GLuint program, GLint location, GLfloat *params) { - CALL_GL_API(glGetUniformfv, program, location, params); -} -void API_ENTRY(glGetUniformiv)(GLuint program, GLint location, GLint *params) { - CALL_GL_API(glGetUniformiv, program, location, params); -} -GLint API_ENTRY(glGetUniformLocation)(GLuint program, const GLchar *name) { - CALL_GL_API_RETURN(glGetUniformLocation, program, name); -} -void API_ENTRY(glGetVertexAttribfv)(GLuint index, GLenum pname, GLfloat *params) { - CALL_GL_API(glGetVertexAttribfv, index, pname, params); -} -void API_ENTRY(glGetVertexAttribiv)(GLuint index, GLenum pname, GLint *params) { - CALL_GL_API(glGetVertexAttribiv, index, pname, params); -} -void API_ENTRY(glGetVertexAttribPointerv)(GLuint index, GLenum pname, void **pointer) { - CALL_GL_API(glGetVertexAttribPointerv, index, pname, pointer); -} -void API_ENTRY(glHint)(GLenum target, GLenum mode) { - CALL_GL_API(glHint, target, mode); -} -GLboolean API_ENTRY(glIsBuffer)(GLuint buffer) { - CALL_GL_API_RETURN(glIsBuffer, buffer); -} -GLboolean API_ENTRY(glIsEnabled)(GLenum cap) { - CALL_GL_API_RETURN(glIsEnabled, cap); -} -GLboolean API_ENTRY(glIsFramebuffer)(GLuint framebuffer) { - CALL_GL_API_RETURN(glIsFramebuffer, framebuffer); -} -GLboolean API_ENTRY(glIsProgram)(GLuint program) { - CALL_GL_API_RETURN(glIsProgram, program); -} -GLboolean API_ENTRY(glIsRenderbuffer)(GLuint renderbuffer) { - CALL_GL_API_RETURN(glIsRenderbuffer, renderbuffer); -} -GLboolean API_ENTRY(glIsShader)(GLuint shader) { - CALL_GL_API_RETURN(glIsShader, shader); -} -GLboolean API_ENTRY(glIsTexture)(GLuint texture) { - CALL_GL_API_RETURN(glIsTexture, texture); -} -void API_ENTRY(glLineWidth)(GLfloat width) { - CALL_GL_API(glLineWidth, width); -} -void API_ENTRY(glLinkProgram)(GLuint program) { - CALL_GL_API(glLinkProgram, program); -} -void API_ENTRY(glPixelStorei)(GLenum pname, GLint param) { - CALL_GL_API(glPixelStorei, pname, param); -} -void API_ENTRY(glPolygonOffset)(GLfloat factor, GLfloat units) { - CALL_GL_API(glPolygonOffset, factor, units); -} -void API_ENTRY(glReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) { - CALL_GL_API(glReadPixels, x, y, width, height, format, type, pixels); -} -void API_ENTRY(glReleaseShaderCompiler)(void) { - CALL_GL_API(glReleaseShaderCompiler); -} -void API_ENTRY(glRenderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) { - CALL_GL_API(glRenderbufferStorage, target, internalformat, width, height); -} -void API_ENTRY(glSampleCoverage)(GLfloat value, GLboolean invert) { - CALL_GL_API(glSampleCoverage, value, invert); -} -void API_ENTRY(glScissor)(GLint x, GLint y, GLsizei width, GLsizei height) { - CALL_GL_API(glScissor, x, y, width, height); -} -void API_ENTRY(glShaderBinary)(GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length) { - CALL_GL_API(glShaderBinary, count, shaders, binaryformat, binary, length); -} -void API_ENTRY(glShaderSource)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length) { - CALL_GL_API(glShaderSource, shader, count, string, length); -} -void API_ENTRY(glStencilFunc)(GLenum func, GLint ref, GLuint mask) { - CALL_GL_API(glStencilFunc, func, ref, mask); -} -void API_ENTRY(glStencilFuncSeparate)(GLenum face, GLenum func, GLint ref, GLuint mask) { - CALL_GL_API(glStencilFuncSeparate, face, func, ref, mask); -} -void API_ENTRY(glStencilMask)(GLuint mask) { - CALL_GL_API(glStencilMask, mask); -} -void API_ENTRY(glStencilMaskSeparate)(GLenum face, GLuint mask) { - CALL_GL_API(glStencilMaskSeparate, face, mask); -} -void API_ENTRY(glStencilOp)(GLenum fail, GLenum zfail, GLenum zpass) { - CALL_GL_API(glStencilOp, fail, zfail, zpass); -} -void API_ENTRY(glStencilOpSeparate)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) { - CALL_GL_API(glStencilOpSeparate, face, sfail, dpfail, dppass); -} -void API_ENTRY(glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) { - CALL_GL_API(glTexImage2D, target, level, internalformat, width, height, border, format, type, pixels); -} -void API_ENTRY(glTexParameterf)(GLenum target, GLenum pname, GLfloat param) { - CALL_GL_API(glTexParameterf, target, pname, param); -} -void API_ENTRY(glTexParameterfv)(GLenum target, GLenum pname, const GLfloat *params) { - CALL_GL_API(glTexParameterfv, target, pname, params); -} -void API_ENTRY(glTexParameteri)(GLenum target, GLenum pname, GLint param) { - CALL_GL_API(glTexParameteri, target, pname, param); -} -void API_ENTRY(glTexParameteriv)(GLenum target, GLenum pname, const GLint *params) { - CALL_GL_API(glTexParameteriv, target, pname, params); -} -void API_ENTRY(glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) { - CALL_GL_API(glTexSubImage2D, target, level, xoffset, yoffset, width, height, format, type, pixels); -} -void API_ENTRY(glUniform1f)(GLint location, GLfloat v0) { - CALL_GL_API(glUniform1f, location, v0); -} -void API_ENTRY(glUniform1fv)(GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glUniform1fv, location, count, value); -} -void API_ENTRY(glUniform1i)(GLint location, GLint v0) { - CALL_GL_API(glUniform1i, location, v0); -} -void API_ENTRY(glUniform1iv)(GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glUniform1iv, location, count, value); -} -void API_ENTRY(glUniform2f)(GLint location, GLfloat v0, GLfloat v1) { - CALL_GL_API(glUniform2f, location, v0, v1); -} -void API_ENTRY(glUniform2fv)(GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glUniform2fv, location, count, value); -} -void API_ENTRY(glUniform2i)(GLint location, GLint v0, GLint v1) { - CALL_GL_API(glUniform2i, location, v0, v1); -} -void API_ENTRY(glUniform2iv)(GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glUniform2iv, location, count, value); -} -void API_ENTRY(glUniform3f)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) { - CALL_GL_API(glUniform3f, location, v0, v1, v2); -} -void API_ENTRY(glUniform3fv)(GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glUniform3fv, location, count, value); -} -void API_ENTRY(glUniform3i)(GLint location, GLint v0, GLint v1, GLint v2) { - CALL_GL_API(glUniform3i, location, v0, v1, v2); -} -void API_ENTRY(glUniform3iv)(GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glUniform3iv, location, count, value); -} -void API_ENTRY(glUniform4f)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) { - CALL_GL_API(glUniform4f, location, v0, v1, v2, v3); -} -void API_ENTRY(glUniform4fv)(GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glUniform4fv, location, count, value); -} -void API_ENTRY(glUniform4i)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3) { - CALL_GL_API(glUniform4i, location, v0, v1, v2, v3); -} -void API_ENTRY(glUniform4iv)(GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glUniform4iv, location, count, value); -} -void API_ENTRY(glUniformMatrix2fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glUniformMatrix2fv, location, count, transpose, value); -} -void API_ENTRY(glUniformMatrix3fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glUniformMatrix3fv, location, count, transpose, value); -} -void API_ENTRY(glUniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glUniformMatrix4fv, location, count, transpose, value); -} -void API_ENTRY(glUseProgram)(GLuint program) { - CALL_GL_API(glUseProgram, program); -} -void API_ENTRY(glValidateProgram)(GLuint program) { - CALL_GL_API(glValidateProgram, program); -} -void API_ENTRY(glVertexAttrib1f)(GLuint index, GLfloat x) { - CALL_GL_API(glVertexAttrib1f, index, x); -} -void API_ENTRY(glVertexAttrib1fv)(GLuint index, const GLfloat *v) { - CALL_GL_API(glVertexAttrib1fv, index, v); -} -void API_ENTRY(glVertexAttrib2f)(GLuint index, GLfloat x, GLfloat y) { - CALL_GL_API(glVertexAttrib2f, index, x, y); -} -void API_ENTRY(glVertexAttrib2fv)(GLuint index, const GLfloat *v) { - CALL_GL_API(glVertexAttrib2fv, index, v); -} -void API_ENTRY(glVertexAttrib3f)(GLuint index, GLfloat x, GLfloat y, GLfloat z) { - CALL_GL_API(glVertexAttrib3f, index, x, y, z); -} -void API_ENTRY(glVertexAttrib3fv)(GLuint index, const GLfloat *v) { - CALL_GL_API(glVertexAttrib3fv, index, v); -} -void API_ENTRY(glVertexAttrib4f)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) { - CALL_GL_API(glVertexAttrib4f, index, x, y, z, w); -} -void API_ENTRY(glVertexAttrib4fv)(GLuint index, const GLfloat *v) { - CALL_GL_API(glVertexAttrib4fv, index, v); -} -void API_ENTRY(glVertexAttribPointer)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) { - CALL_GL_API(glVertexAttribPointer, index, size, type, normalized, stride, pointer); -} -void API_ENTRY(glViewport)(GLint x, GLint y, GLsizei width, GLsizei height) { - CALL_GL_API(glViewport, x, y, width, height); -} -void API_ENTRY(glReadBuffer)(GLenum src) { - CALL_GL_API(glReadBuffer, src); -} -void API_ENTRY(glDrawRangeElements)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices) { - CALL_GL_API(glDrawRangeElements, mode, start, end, count, type, indices); -} -void API_ENTRY(glTexImage3D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) { - CALL_GL_API(glTexImage3D, target, level, internalformat, width, height, depth, border, format, type, pixels); -} -void API_ENTRY(glTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) { - CALL_GL_API(glTexSubImage3D, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); -} -void API_ENTRY(glCopyTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) { - CALL_GL_API(glCopyTexSubImage3D, target, level, xoffset, yoffset, zoffset, x, y, width, height); -} -void API_ENTRY(glCompressedTexImage3D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data) { - CALL_GL_API(glCompressedTexImage3D, target, level, internalformat, width, height, depth, border, imageSize, data); -} -void API_ENTRY(glCompressedTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data) { - CALL_GL_API(glCompressedTexSubImage3D, target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data); -} -void API_ENTRY(glGenQueries)(GLsizei n, GLuint *ids) { - CALL_GL_API(glGenQueries, n, ids); -} -void API_ENTRY(glDeleteQueries)(GLsizei n, const GLuint *ids) { - CALL_GL_API(glDeleteQueries, n, ids); -} -GLboolean API_ENTRY(glIsQuery)(GLuint id) { - CALL_GL_API_RETURN(glIsQuery, id); -} -void API_ENTRY(glBeginQuery)(GLenum target, GLuint id) { - CALL_GL_API(glBeginQuery, target, id); -} -void API_ENTRY(glEndQuery)(GLenum target) { - CALL_GL_API(glEndQuery, target); -} -void API_ENTRY(glGetQueryiv)(GLenum target, GLenum pname, GLint *params) { - CALL_GL_API(glGetQueryiv, target, pname, params); -} -void API_ENTRY(glGetQueryObjectuiv)(GLuint id, GLenum pname, GLuint *params) { - CALL_GL_API(glGetQueryObjectuiv, id, pname, params); -} -GLboolean API_ENTRY(glUnmapBuffer)(GLenum target) { - CALL_GL_API_RETURN(glUnmapBuffer, target); -} -void API_ENTRY(glGetBufferPointerv)(GLenum target, GLenum pname, void **params) { - CALL_GL_API(glGetBufferPointerv, target, pname, params); -} -void API_ENTRY(glDrawBuffers)(GLsizei n, const GLenum *bufs) { - CALL_GL_API(glDrawBuffers, n, bufs); -} -void API_ENTRY(glUniformMatrix2x3fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glUniformMatrix2x3fv, location, count, transpose, value); -} -void API_ENTRY(glUniformMatrix3x2fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glUniformMatrix3x2fv, location, count, transpose, value); -} -void API_ENTRY(glUniformMatrix2x4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glUniformMatrix2x4fv, location, count, transpose, value); -} -void API_ENTRY(glUniformMatrix4x2fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glUniformMatrix4x2fv, location, count, transpose, value); -} -void API_ENTRY(glUniformMatrix3x4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glUniformMatrix3x4fv, location, count, transpose, value); -} -void API_ENTRY(glUniformMatrix4x3fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glUniformMatrix4x3fv, location, count, transpose, value); -} -void API_ENTRY(glBlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { - CALL_GL_API(glBlitFramebuffer, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); -} -void API_ENTRY(glRenderbufferStorageMultisample)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { - CALL_GL_API(glRenderbufferStorageMultisample, target, samples, internalformat, width, height); -} -void API_ENTRY(glFramebufferTextureLayer)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) { - CALL_GL_API(glFramebufferTextureLayer, target, attachment, texture, level, layer); -} -void * API_ENTRY(glMapBufferRange)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) { - CALL_GL_API_RETURN(glMapBufferRange, target, offset, length, access); -} -void API_ENTRY(glFlushMappedBufferRange)(GLenum target, GLintptr offset, GLsizeiptr length) { - CALL_GL_API(glFlushMappedBufferRange, target, offset, length); -} -void API_ENTRY(glBindVertexArray)(GLuint array) { - CALL_GL_API(glBindVertexArray, array); -} -void API_ENTRY(glDeleteVertexArrays)(GLsizei n, const GLuint *arrays) { - CALL_GL_API(glDeleteVertexArrays, n, arrays); -} -void API_ENTRY(glGenVertexArrays)(GLsizei n, GLuint *arrays) { - CALL_GL_API(glGenVertexArrays, n, arrays); -} -GLboolean API_ENTRY(glIsVertexArray)(GLuint array) { - CALL_GL_API_RETURN(glIsVertexArray, array); -} -void API_ENTRY(glGetIntegeri_v)(GLenum target, GLuint index, GLint *data) { - CALL_GL_API(glGetIntegeri_v, target, index, data); -} -void API_ENTRY(glBeginTransformFeedback)(GLenum primitiveMode) { - CALL_GL_API(glBeginTransformFeedback, primitiveMode); -} -void API_ENTRY(glEndTransformFeedback)(void) { - CALL_GL_API(glEndTransformFeedback); -} -void API_ENTRY(glBindBufferRange)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size) { - CALL_GL_API(glBindBufferRange, target, index, buffer, offset, size); -} -void API_ENTRY(glBindBufferBase)(GLenum target, GLuint index, GLuint buffer) { - CALL_GL_API(glBindBufferBase, target, index, buffer); -} -void API_ENTRY(glTransformFeedbackVaryings)(GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode) { - CALL_GL_API(glTransformFeedbackVaryings, program, count, varyings, bufferMode); -} -void API_ENTRY(glGetTransformFeedbackVarying)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name) { - CALL_GL_API(glGetTransformFeedbackVarying, program, index, bufSize, length, size, type, name); -} -void API_ENTRY(glVertexAttribIPointer)(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer) { - CALL_GL_API(glVertexAttribIPointer, index, size, type, stride, pointer); -} -void API_ENTRY(glGetVertexAttribIiv)(GLuint index, GLenum pname, GLint *params) { - CALL_GL_API(glGetVertexAttribIiv, index, pname, params); -} -void API_ENTRY(glGetVertexAttribIuiv)(GLuint index, GLenum pname, GLuint *params) { - CALL_GL_API(glGetVertexAttribIuiv, index, pname, params); -} -void API_ENTRY(glVertexAttribI4i)(GLuint index, GLint x, GLint y, GLint z, GLint w) { - CALL_GL_API(glVertexAttribI4i, index, x, y, z, w); -} -void API_ENTRY(glVertexAttribI4ui)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w) { - CALL_GL_API(glVertexAttribI4ui, index, x, y, z, w); -} -void API_ENTRY(glVertexAttribI4iv)(GLuint index, const GLint *v) { - CALL_GL_API(glVertexAttribI4iv, index, v); -} -void API_ENTRY(glVertexAttribI4uiv)(GLuint index, const GLuint *v) { - CALL_GL_API(glVertexAttribI4uiv, index, v); -} -void API_ENTRY(glGetUniformuiv)(GLuint program, GLint location, GLuint *params) { - CALL_GL_API(glGetUniformuiv, program, location, params); -} -GLint API_ENTRY(glGetFragDataLocation)(GLuint program, const GLchar *name) { - CALL_GL_API_RETURN(glGetFragDataLocation, program, name); -} -void API_ENTRY(glUniform1ui)(GLint location, GLuint v0) { - CALL_GL_API(glUniform1ui, location, v0); -} -void API_ENTRY(glUniform2ui)(GLint location, GLuint v0, GLuint v1) { - CALL_GL_API(glUniform2ui, location, v0, v1); -} -void API_ENTRY(glUniform3ui)(GLint location, GLuint v0, GLuint v1, GLuint v2) { - CALL_GL_API(glUniform3ui, location, v0, v1, v2); -} -void API_ENTRY(glUniform4ui)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) { - CALL_GL_API(glUniform4ui, location, v0, v1, v2, v3); -} -void API_ENTRY(glUniform1uiv)(GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glUniform1uiv, location, count, value); -} -void API_ENTRY(glUniform2uiv)(GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glUniform2uiv, location, count, value); -} -void API_ENTRY(glUniform3uiv)(GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glUniform3uiv, location, count, value); -} -void API_ENTRY(glUniform4uiv)(GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glUniform4uiv, location, count, value); -} -void API_ENTRY(glClearBufferiv)(GLenum buffer, GLint drawbuffer, const GLint *value) { - CALL_GL_API(glClearBufferiv, buffer, drawbuffer, value); -} -void API_ENTRY(glClearBufferuiv)(GLenum buffer, GLint drawbuffer, const GLuint *value) { - CALL_GL_API(glClearBufferuiv, buffer, drawbuffer, value); -} -void API_ENTRY(glClearBufferfv)(GLenum buffer, GLint drawbuffer, const GLfloat *value) { - CALL_GL_API(glClearBufferfv, buffer, drawbuffer, value); -} -void API_ENTRY(glClearBufferfi)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) { - CALL_GL_API(glClearBufferfi, buffer, drawbuffer, depth, stencil); -} -const GLubyte * API_ENTRY(glGetStringi)(GLenum name, GLuint index) { - CALL_GL_API_RETURN(glGetStringi, name, index); -} -void API_ENTRY(glCopyBufferSubData)(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) { - CALL_GL_API(glCopyBufferSubData, readTarget, writeTarget, readOffset, writeOffset, size); -} -void API_ENTRY(glGetUniformIndices)(GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices) { - CALL_GL_API(glGetUniformIndices, program, uniformCount, uniformNames, uniformIndices); -} -void API_ENTRY(glGetActiveUniformsiv)(GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params) { - CALL_GL_API(glGetActiveUniformsiv, program, uniformCount, uniformIndices, pname, params); -} -GLuint API_ENTRY(glGetUniformBlockIndex)(GLuint program, const GLchar *uniformBlockName) { - CALL_GL_API_RETURN(glGetUniformBlockIndex, program, uniformBlockName); -} -void API_ENTRY(glGetActiveUniformBlockiv)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params) { - CALL_GL_API(glGetActiveUniformBlockiv, program, uniformBlockIndex, pname, params); -} -void API_ENTRY(glGetActiveUniformBlockName)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName) { - CALL_GL_API(glGetActiveUniformBlockName, program, uniformBlockIndex, bufSize, length, uniformBlockName); -} -void API_ENTRY(glUniformBlockBinding)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) { - CALL_GL_API(glUniformBlockBinding, program, uniformBlockIndex, uniformBlockBinding); -} -void API_ENTRY(glDrawArraysInstanced)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) { - CALL_GL_API(glDrawArraysInstanced, mode, first, count, instancecount); -} -void API_ENTRY(glDrawElementsInstanced)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount) { - CALL_GL_API(glDrawElementsInstanced, mode, count, type, indices, instancecount); -} -GLsync API_ENTRY(glFenceSync)(GLenum condition, GLbitfield flags) { - CALL_GL_API_RETURN(glFenceSync, condition, flags); -} -GLboolean API_ENTRY(glIsSync)(GLsync sync) { - CALL_GL_API_RETURN(glIsSync, sync); -} -void API_ENTRY(glDeleteSync)(GLsync sync) { - CALL_GL_API(glDeleteSync, sync); -} -GLenum API_ENTRY(glClientWaitSync)(GLsync sync, GLbitfield flags, GLuint64 timeout) { - CALL_GL_API_RETURN(glClientWaitSync, sync, flags, timeout); -} -void API_ENTRY(glWaitSync)(GLsync sync, GLbitfield flags, GLuint64 timeout) { - CALL_GL_API(glWaitSync, sync, flags, timeout); -} -void API_ENTRY(glGetInteger64v)(GLenum pname, GLint64 *data) { - CALL_GL_API(glGetInteger64v, pname, data); -} -void API_ENTRY(glGetSynciv)(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values) { - CALL_GL_API(glGetSynciv, sync, pname, bufSize, length, values); -} -void API_ENTRY(glGetInteger64i_v)(GLenum target, GLuint index, GLint64 *data) { - CALL_GL_API(glGetInteger64i_v, target, index, data); -} -void API_ENTRY(glGetBufferParameteri64v)(GLenum target, GLenum pname, GLint64 *params) { - CALL_GL_API(glGetBufferParameteri64v, target, pname, params); -} -void API_ENTRY(glGenSamplers)(GLsizei count, GLuint *samplers) { - CALL_GL_API(glGenSamplers, count, samplers); -} -void API_ENTRY(glDeleteSamplers)(GLsizei count, const GLuint *samplers) { - CALL_GL_API(glDeleteSamplers, count, samplers); -} -GLboolean API_ENTRY(glIsSampler)(GLuint sampler) { - CALL_GL_API_RETURN(glIsSampler, sampler); -} -void API_ENTRY(glBindSampler)(GLuint unit, GLuint sampler) { - CALL_GL_API(glBindSampler, unit, sampler); -} -void API_ENTRY(glSamplerParameteri)(GLuint sampler, GLenum pname, GLint param) { - CALL_GL_API(glSamplerParameteri, sampler, pname, param); -} -void API_ENTRY(glSamplerParameteriv)(GLuint sampler, GLenum pname, const GLint *param) { - CALL_GL_API(glSamplerParameteriv, sampler, pname, param); -} -void API_ENTRY(glSamplerParameterf)(GLuint sampler, GLenum pname, GLfloat param) { - CALL_GL_API(glSamplerParameterf, sampler, pname, param); -} -void API_ENTRY(glSamplerParameterfv)(GLuint sampler, GLenum pname, const GLfloat *param) { - CALL_GL_API(glSamplerParameterfv, sampler, pname, param); -} -void API_ENTRY(glGetSamplerParameteriv)(GLuint sampler, GLenum pname, GLint *params) { - CALL_GL_API(glGetSamplerParameteriv, sampler, pname, params); -} -void API_ENTRY(glGetSamplerParameterfv)(GLuint sampler, GLenum pname, GLfloat *params) { - CALL_GL_API(glGetSamplerParameterfv, sampler, pname, params); -} -void API_ENTRY(glVertexAttribDivisor)(GLuint index, GLuint divisor) { - CALL_GL_API(glVertexAttribDivisor, index, divisor); -} -void API_ENTRY(glBindTransformFeedback)(GLenum target, GLuint id) { - CALL_GL_API(glBindTransformFeedback, target, id); -} -void API_ENTRY(glDeleteTransformFeedbacks)(GLsizei n, const GLuint *ids) { - CALL_GL_API(glDeleteTransformFeedbacks, n, ids); -} -void API_ENTRY(glGenTransformFeedbacks)(GLsizei n, GLuint *ids) { - CALL_GL_API(glGenTransformFeedbacks, n, ids); -} -GLboolean API_ENTRY(glIsTransformFeedback)(GLuint id) { - CALL_GL_API_RETURN(glIsTransformFeedback, id); -} -void API_ENTRY(glPauseTransformFeedback)(void) { - CALL_GL_API(glPauseTransformFeedback); -} -void API_ENTRY(glResumeTransformFeedback)(void) { - CALL_GL_API(glResumeTransformFeedback); -} -void API_ENTRY(glGetProgramBinary)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary) { - CALL_GL_API(glGetProgramBinary, program, bufSize, length, binaryFormat, binary); -} -void API_ENTRY(glProgramBinary)(GLuint program, GLenum binaryFormat, const void *binary, GLsizei length) { - CALL_GL_API(glProgramBinary, program, binaryFormat, binary, length); -} -void API_ENTRY(glProgramParameteri)(GLuint program, GLenum pname, GLint value) { - CALL_GL_API(glProgramParameteri, program, pname, value); -} -void API_ENTRY(glInvalidateFramebuffer)(GLenum target, GLsizei numAttachments, const GLenum *attachments) { - CALL_GL_API(glInvalidateFramebuffer, target, numAttachments, attachments); -} -void API_ENTRY(glInvalidateSubFramebuffer)(GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height) { - CALL_GL_API(glInvalidateSubFramebuffer, target, numAttachments, attachments, x, y, width, height); -} -void API_ENTRY(glTexStorage2D)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) { - CALL_GL_API(glTexStorage2D, target, levels, internalformat, width, height); -} -void API_ENTRY(glTexStorage3D)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) { - CALL_GL_API(glTexStorage3D, target, levels, internalformat, width, height, depth); -} -void API_ENTRY(glGetInternalformativ)(GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params) { - CALL_GL_API(glGetInternalformativ, target, internalformat, pname, bufSize, params); -} -void API_ENTRY(glDispatchCompute)(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z) { - CALL_GL_API(glDispatchCompute, num_groups_x, num_groups_y, num_groups_z); -} -void API_ENTRY(glDispatchComputeIndirect)(GLintptr indirect) { - CALL_GL_API(glDispatchComputeIndirect, indirect); -} -void API_ENTRY(glDrawArraysIndirect)(GLenum mode, const void *indirect) { - CALL_GL_API(glDrawArraysIndirect, mode, indirect); -} -void API_ENTRY(glDrawElementsIndirect)(GLenum mode, GLenum type, const void *indirect) { - CALL_GL_API(glDrawElementsIndirect, mode, type, indirect); -} -void API_ENTRY(glFramebufferParameteri)(GLenum target, GLenum pname, GLint param) { - CALL_GL_API(glFramebufferParameteri, target, pname, param); -} -void API_ENTRY(glGetFramebufferParameteriv)(GLenum target, GLenum pname, GLint *params) { - CALL_GL_API(glGetFramebufferParameteriv, target, pname, params); -} -void API_ENTRY(glGetProgramInterfaceiv)(GLuint program, GLenum programInterface, GLenum pname, GLint *params) { - CALL_GL_API(glGetProgramInterfaceiv, program, programInterface, pname, params); -} -GLuint API_ENTRY(glGetProgramResourceIndex)(GLuint program, GLenum programInterface, const GLchar *name) { - CALL_GL_API_RETURN(glGetProgramResourceIndex, program, programInterface, name); -} -void API_ENTRY(glGetProgramResourceName)(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name) { - CALL_GL_API(glGetProgramResourceName, program, programInterface, index, bufSize, length, name); -} -void API_ENTRY(glGetProgramResourceiv)(GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params) { - CALL_GL_API(glGetProgramResourceiv, program, programInterface, index, propCount, props, bufSize, length, params); -} -GLint API_ENTRY(glGetProgramResourceLocation)(GLuint program, GLenum programInterface, const GLchar *name) { - CALL_GL_API_RETURN(glGetProgramResourceLocation, program, programInterface, name); -} -void API_ENTRY(glUseProgramStages)(GLuint pipeline, GLbitfield stages, GLuint program) { - CALL_GL_API(glUseProgramStages, pipeline, stages, program); -} -void API_ENTRY(glActiveShaderProgram)(GLuint pipeline, GLuint program) { - CALL_GL_API(glActiveShaderProgram, pipeline, program); -} -GLuint API_ENTRY(glCreateShaderProgramv)(GLenum type, GLsizei count, const GLchar *const*strings) { - CALL_GL_API_RETURN(glCreateShaderProgramv, type, count, strings); -} -void API_ENTRY(glBindProgramPipeline)(GLuint pipeline) { - CALL_GL_API(glBindProgramPipeline, pipeline); -} -void API_ENTRY(glDeleteProgramPipelines)(GLsizei n, const GLuint *pipelines) { - CALL_GL_API(glDeleteProgramPipelines, n, pipelines); -} -void API_ENTRY(glGenProgramPipelines)(GLsizei n, GLuint *pipelines) { - CALL_GL_API(glGenProgramPipelines, n, pipelines); -} -GLboolean API_ENTRY(glIsProgramPipeline)(GLuint pipeline) { - CALL_GL_API_RETURN(glIsProgramPipeline, pipeline); -} -void API_ENTRY(glGetProgramPipelineiv)(GLuint pipeline, GLenum pname, GLint *params) { - CALL_GL_API(glGetProgramPipelineiv, pipeline, pname, params); -} -void API_ENTRY(glProgramUniform1i)(GLuint program, GLint location, GLint v0) { - CALL_GL_API(glProgramUniform1i, program, location, v0); -} -void API_ENTRY(glProgramUniform2i)(GLuint program, GLint location, GLint v0, GLint v1) { - CALL_GL_API(glProgramUniform2i, program, location, v0, v1); -} -void API_ENTRY(glProgramUniform3i)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2) { - CALL_GL_API(glProgramUniform3i, program, location, v0, v1, v2); -} -void API_ENTRY(glProgramUniform4i)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3) { - CALL_GL_API(glProgramUniform4i, program, location, v0, v1, v2, v3); -} -void API_ENTRY(glProgramUniform1ui)(GLuint program, GLint location, GLuint v0) { - CALL_GL_API(glProgramUniform1ui, program, location, v0); -} -void API_ENTRY(glProgramUniform2ui)(GLuint program, GLint location, GLuint v0, GLuint v1) { - CALL_GL_API(glProgramUniform2ui, program, location, v0, v1); -} -void API_ENTRY(glProgramUniform3ui)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2) { - CALL_GL_API(glProgramUniform3ui, program, location, v0, v1, v2); -} -void API_ENTRY(glProgramUniform4ui)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) { - CALL_GL_API(glProgramUniform4ui, program, location, v0, v1, v2, v3); -} -void API_ENTRY(glProgramUniform1f)(GLuint program, GLint location, GLfloat v0) { - CALL_GL_API(glProgramUniform1f, program, location, v0); -} -void API_ENTRY(glProgramUniform2f)(GLuint program, GLint location, GLfloat v0, GLfloat v1) { - CALL_GL_API(glProgramUniform2f, program, location, v0, v1); -} -void API_ENTRY(glProgramUniform3f)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) { - CALL_GL_API(glProgramUniform3f, program, location, v0, v1, v2); -} -void API_ENTRY(glProgramUniform4f)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) { - CALL_GL_API(glProgramUniform4f, program, location, v0, v1, v2, v3); -} -void API_ENTRY(glProgramUniform1iv)(GLuint program, GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glProgramUniform1iv, program, location, count, value); -} -void API_ENTRY(glProgramUniform2iv)(GLuint program, GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glProgramUniform2iv, program, location, count, value); -} -void API_ENTRY(glProgramUniform3iv)(GLuint program, GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glProgramUniform3iv, program, location, count, value); -} -void API_ENTRY(glProgramUniform4iv)(GLuint program, GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glProgramUniform4iv, program, location, count, value); -} -void API_ENTRY(glProgramUniform1uiv)(GLuint program, GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glProgramUniform1uiv, program, location, count, value); -} -void API_ENTRY(glProgramUniform2uiv)(GLuint program, GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glProgramUniform2uiv, program, location, count, value); -} -void API_ENTRY(glProgramUniform3uiv)(GLuint program, GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glProgramUniform3uiv, program, location, count, value); -} -void API_ENTRY(glProgramUniform4uiv)(GLuint program, GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glProgramUniform4uiv, program, location, count, value); -} -void API_ENTRY(glProgramUniform1fv)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glProgramUniform1fv, program, location, count, value); -} -void API_ENTRY(glProgramUniform2fv)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glProgramUniform2fv, program, location, count, value); -} -void API_ENTRY(glProgramUniform3fv)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glProgramUniform3fv, program, location, count, value); -} -void API_ENTRY(glProgramUniform4fv)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glProgramUniform4fv, program, location, count, value); -} -void API_ENTRY(glProgramUniformMatrix2fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix2fv, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix3fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix3fv, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix4fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix4fv, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix2x3fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix2x3fv, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix3x2fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix3x2fv, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix2x4fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix2x4fv, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix4x2fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix4x2fv, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix3x4fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix3x4fv, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix4x3fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix4x3fv, program, location, count, transpose, value); -} -void API_ENTRY(glValidateProgramPipeline)(GLuint pipeline) { - CALL_GL_API(glValidateProgramPipeline, pipeline); -} -void API_ENTRY(glGetProgramPipelineInfoLog)(GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog) { - CALL_GL_API(glGetProgramPipelineInfoLog, pipeline, bufSize, length, infoLog); -} -void API_ENTRY(glBindImageTexture)(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) { - CALL_GL_API(glBindImageTexture, unit, texture, level, layered, layer, access, format); -} -void API_ENTRY(glGetBooleani_v)(GLenum target, GLuint index, GLboolean *data) { - CALL_GL_API(glGetBooleani_v, target, index, data); -} -void API_ENTRY(glMemoryBarrier)(GLbitfield barriers) { - CALL_GL_API(glMemoryBarrier, barriers); -} -void API_ENTRY(glMemoryBarrierByRegion)(GLbitfield barriers) { - CALL_GL_API(glMemoryBarrierByRegion, barriers); -} -void API_ENTRY(glTexStorage2DMultisample)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) { - CALL_GL_API(glTexStorage2DMultisample, target, samples, internalformat, width, height, fixedsamplelocations); -} -void API_ENTRY(glGetMultisamplefv)(GLenum pname, GLuint index, GLfloat *val) { - CALL_GL_API(glGetMultisamplefv, pname, index, val); -} -void API_ENTRY(glSampleMaski)(GLuint maskNumber, GLbitfield mask) { - CALL_GL_API(glSampleMaski, maskNumber, mask); -} -void API_ENTRY(glGetTexLevelParameteriv)(GLenum target, GLint level, GLenum pname, GLint *params) { - CALL_GL_API(glGetTexLevelParameteriv, target, level, pname, params); -} -void API_ENTRY(glGetTexLevelParameterfv)(GLenum target, GLint level, GLenum pname, GLfloat *params) { - CALL_GL_API(glGetTexLevelParameterfv, target, level, pname, params); -} -void API_ENTRY(glBindVertexBuffer)(GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride) { - CALL_GL_API(glBindVertexBuffer, bindingindex, buffer, offset, stride); -} -void API_ENTRY(glVertexAttribFormat)(GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset) { - CALL_GL_API(glVertexAttribFormat, attribindex, size, type, normalized, relativeoffset); -} -void API_ENTRY(glVertexAttribIFormat)(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) { - CALL_GL_API(glVertexAttribIFormat, attribindex, size, type, relativeoffset); -} -void API_ENTRY(glVertexAttribBinding)(GLuint attribindex, GLuint bindingindex) { - CALL_GL_API(glVertexAttribBinding, attribindex, bindingindex); -} -void API_ENTRY(glVertexBindingDivisor)(GLuint bindingindex, GLuint divisor) { - CALL_GL_API(glVertexBindingDivisor, bindingindex, divisor); -} -void API_ENTRY(glBlendBarrier)(void) { - CALL_GL_API(glBlendBarrier); -} -void API_ENTRY(glCopyImageSubData)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) { - CALL_GL_API(glCopyImageSubData, srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth); -} -void API_ENTRY(glDebugMessageControl)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled) { - CALL_GL_API(glDebugMessageControl, source, type, severity, count, ids, enabled); -} -void API_ENTRY(glDebugMessageInsert)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf) { - CALL_GL_API(glDebugMessageInsert, source, type, id, severity, length, buf); -} -void API_ENTRY(glDebugMessageCallback)(GLDEBUGPROC callback, const void *userParam) { - CALL_GL_API(glDebugMessageCallback, callback, userParam); -} -GLuint API_ENTRY(glGetDebugMessageLog)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog) { - CALL_GL_API_RETURN(glGetDebugMessageLog, count, bufSize, sources, types, ids, severities, lengths, messageLog); -} -void API_ENTRY(glPushDebugGroup)(GLenum source, GLuint id, GLsizei length, const GLchar *message) { - CALL_GL_API(glPushDebugGroup, source, id, length, message); -} -void API_ENTRY(glPopDebugGroup)(void) { - CALL_GL_API(glPopDebugGroup); -} -void API_ENTRY(glObjectLabel)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label) { - CALL_GL_API(glObjectLabel, identifier, name, length, label); -} -void API_ENTRY(glGetObjectLabel)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label) { - CALL_GL_API(glGetObjectLabel, identifier, name, bufSize, length, label); -} -void API_ENTRY(glObjectPtrLabel)(const void *ptr, GLsizei length, const GLchar *label) { - CALL_GL_API(glObjectPtrLabel, ptr, length, label); -} -void API_ENTRY(glGetObjectPtrLabel)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label) { - CALL_GL_API(glGetObjectPtrLabel, ptr, bufSize, length, label); -} -void API_ENTRY(glGetPointerv)(GLenum pname, void **params) { - CALL_GL_API(glGetPointerv, pname, params); -} -void API_ENTRY(glEnablei)(GLenum target, GLuint index) { - CALL_GL_API(glEnablei, target, index); -} -void API_ENTRY(glDisablei)(GLenum target, GLuint index) { - CALL_GL_API(glDisablei, target, index); -} -void API_ENTRY(glBlendEquationi)(GLuint buf, GLenum mode) { - CALL_GL_API(glBlendEquationi, buf, mode); -} -void API_ENTRY(glBlendEquationSeparatei)(GLuint buf, GLenum modeRGB, GLenum modeAlpha) { - CALL_GL_API(glBlendEquationSeparatei, buf, modeRGB, modeAlpha); -} -void API_ENTRY(glBlendFunci)(GLuint buf, GLenum src, GLenum dst) { - CALL_GL_API(glBlendFunci, buf, src, dst); -} -void API_ENTRY(glBlendFuncSeparatei)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) { - CALL_GL_API(glBlendFuncSeparatei, buf, srcRGB, dstRGB, srcAlpha, dstAlpha); -} -void API_ENTRY(glColorMaski)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) { - CALL_GL_API(glColorMaski, index, r, g, b, a); -} -GLboolean API_ENTRY(glIsEnabledi)(GLenum target, GLuint index) { - CALL_GL_API_RETURN(glIsEnabledi, target, index); -} -void API_ENTRY(glDrawElementsBaseVertex)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) { - CALL_GL_API(glDrawElementsBaseVertex, mode, count, type, indices, basevertex); -} -void API_ENTRY(glDrawRangeElementsBaseVertex)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) { - CALL_GL_API(glDrawRangeElementsBaseVertex, mode, start, end, count, type, indices, basevertex); -} -void API_ENTRY(glDrawElementsInstancedBaseVertex)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) { - CALL_GL_API(glDrawElementsInstancedBaseVertex, mode, count, type, indices, instancecount, basevertex); -} -void API_ENTRY(glFramebufferTexture)(GLenum target, GLenum attachment, GLuint texture, GLint level) { - CALL_GL_API(glFramebufferTexture, target, attachment, texture, level); -} -void API_ENTRY(glPrimitiveBoundingBox)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) { - CALL_GL_API(glPrimitiveBoundingBox, minX, minY, minZ, minW, maxX, maxY, maxZ, maxW); -} -GLenum API_ENTRY(glGetGraphicsResetStatus)(void) { - CALL_GL_API_RETURN(glGetGraphicsResetStatus); -} -void API_ENTRY(glReadnPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) { - CALL_GL_API(glReadnPixels, x, y, width, height, format, type, bufSize, data); -} -void API_ENTRY(glGetnUniformfv)(GLuint program, GLint location, GLsizei bufSize, GLfloat *params) { - CALL_GL_API(glGetnUniformfv, program, location, bufSize, params); -} -void API_ENTRY(glGetnUniformiv)(GLuint program, GLint location, GLsizei bufSize, GLint *params) { - CALL_GL_API(glGetnUniformiv, program, location, bufSize, params); -} -void API_ENTRY(glGetnUniformuiv)(GLuint program, GLint location, GLsizei bufSize, GLuint *params) { - CALL_GL_API(glGetnUniformuiv, program, location, bufSize, params); -} -void API_ENTRY(glMinSampleShading)(GLfloat value) { - CALL_GL_API(glMinSampleShading, value); -} -void API_ENTRY(glPatchParameteri)(GLenum pname, GLint value) { - CALL_GL_API(glPatchParameteri, pname, value); -} -void API_ENTRY(glTexParameterIiv)(GLenum target, GLenum pname, const GLint *params) { - CALL_GL_API(glTexParameterIiv, target, pname, params); -} -void API_ENTRY(glTexParameterIuiv)(GLenum target, GLenum pname, const GLuint *params) { - CALL_GL_API(glTexParameterIuiv, target, pname, params); -} -void API_ENTRY(glGetTexParameterIiv)(GLenum target, GLenum pname, GLint *params) { - CALL_GL_API(glGetTexParameterIiv, target, pname, params); -} -void API_ENTRY(glGetTexParameterIuiv)(GLenum target, GLenum pname, GLuint *params) { - CALL_GL_API(glGetTexParameterIuiv, target, pname, params); -} -void API_ENTRY(glSamplerParameterIiv)(GLuint sampler, GLenum pname, const GLint *param) { - CALL_GL_API(glSamplerParameterIiv, sampler, pname, param); -} -void API_ENTRY(glSamplerParameterIuiv)(GLuint sampler, GLenum pname, const GLuint *param) { - CALL_GL_API(glSamplerParameterIuiv, sampler, pname, param); -} -void API_ENTRY(glGetSamplerParameterIiv)(GLuint sampler, GLenum pname, GLint *params) { - CALL_GL_API(glGetSamplerParameterIiv, sampler, pname, params); -} -void API_ENTRY(glGetSamplerParameterIuiv)(GLuint sampler, GLenum pname, GLuint *params) { - CALL_GL_API(glGetSamplerParameterIuiv, sampler, pname, params); -} -void API_ENTRY(glTexBuffer)(GLenum target, GLenum internalformat, GLuint buffer) { - CALL_GL_API(glTexBuffer, target, internalformat, buffer); -} -void API_ENTRY(glTexBufferRange)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) { - CALL_GL_API(glTexBufferRange, target, internalformat, buffer, offset, size); -} -void API_ENTRY(glTexStorage3DMultisample)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) { - CALL_GL_API(glTexStorage3DMultisample, target, samples, internalformat, width, height, depth, fixedsamplelocations); -} -void API_ENTRY(glBlendBarrierKHR)(void) { - CALL_GL_API(glBlendBarrierKHR); -} -void API_ENTRY(glDebugMessageControlKHR)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled) { - CALL_GL_API(glDebugMessageControlKHR, source, type, severity, count, ids, enabled); -} -void API_ENTRY(glDebugMessageInsertKHR)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf) { - CALL_GL_API(glDebugMessageInsertKHR, source, type, id, severity, length, buf); -} -void API_ENTRY(glDebugMessageCallbackKHR)(GLDEBUGPROCKHR callback, const void *userParam) { - CALL_GL_API(glDebugMessageCallbackKHR, callback, userParam); -} -GLuint API_ENTRY(glGetDebugMessageLogKHR)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog) { - CALL_GL_API_RETURN(glGetDebugMessageLogKHR, count, bufSize, sources, types, ids, severities, lengths, messageLog); -} -void API_ENTRY(glPushDebugGroupKHR)(GLenum source, GLuint id, GLsizei length, const GLchar *message) { - CALL_GL_API(glPushDebugGroupKHR, source, id, length, message); -} -void API_ENTRY(glPopDebugGroupKHR)(void) { - CALL_GL_API(glPopDebugGroupKHR); -} -void API_ENTRY(glObjectLabelKHR)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label) { - CALL_GL_API(glObjectLabelKHR, identifier, name, length, label); -} -void API_ENTRY(glGetObjectLabelKHR)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label) { - CALL_GL_API(glGetObjectLabelKHR, identifier, name, bufSize, length, label); -} -void API_ENTRY(glObjectPtrLabelKHR)(const void *ptr, GLsizei length, const GLchar *label) { - CALL_GL_API(glObjectPtrLabelKHR, ptr, length, label); -} -void API_ENTRY(glGetObjectPtrLabelKHR)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label) { - CALL_GL_API(glGetObjectPtrLabelKHR, ptr, bufSize, length, label); -} -void API_ENTRY(glGetPointervKHR)(GLenum pname, void **params) { - CALL_GL_API(glGetPointervKHR, pname, params); -} -GLenum API_ENTRY(glGetGraphicsResetStatusKHR)(void) { - CALL_GL_API_RETURN(glGetGraphicsResetStatusKHR); -} -void API_ENTRY(glReadnPixelsKHR)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) { - CALL_GL_API(glReadnPixelsKHR, x, y, width, height, format, type, bufSize, data); -} -void API_ENTRY(glGetnUniformfvKHR)(GLuint program, GLint location, GLsizei bufSize, GLfloat *params) { - CALL_GL_API(glGetnUniformfvKHR, program, location, bufSize, params); -} -void API_ENTRY(glGetnUniformivKHR)(GLuint program, GLint location, GLsizei bufSize, GLint *params) { - CALL_GL_API(glGetnUniformivKHR, program, location, bufSize, params); -} -void API_ENTRY(glGetnUniformuivKHR)(GLuint program, GLint location, GLsizei bufSize, GLuint *params) { - CALL_GL_API(glGetnUniformuivKHR, program, location, bufSize, params); -} -void API_ENTRY(glEGLImageTargetTexture2DOES)(GLenum target, GLeglImageOES image) { - CALL_GL_API(glEGLImageTargetTexture2DOES, target, image); -} -void API_ENTRY(glEGLImageTargetRenderbufferStorageOES)(GLenum target, GLeglImageOES image) { - CALL_GL_API(glEGLImageTargetRenderbufferStorageOES, target, image); -} -void API_ENTRY(glCopyImageSubDataOES)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) { - CALL_GL_API(glCopyImageSubDataOES, srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth); -} -void API_ENTRY(glEnableiOES)(GLenum target, GLuint index) { - CALL_GL_API(glEnableiOES, target, index); -} -void API_ENTRY(glDisableiOES)(GLenum target, GLuint index) { - CALL_GL_API(glDisableiOES, target, index); -} -void API_ENTRY(glBlendEquationiOES)(GLuint buf, GLenum mode) { - CALL_GL_API(glBlendEquationiOES, buf, mode); -} -void API_ENTRY(glBlendEquationSeparateiOES)(GLuint buf, GLenum modeRGB, GLenum modeAlpha) { - CALL_GL_API(glBlendEquationSeparateiOES, buf, modeRGB, modeAlpha); -} -void API_ENTRY(glBlendFunciOES)(GLuint buf, GLenum src, GLenum dst) { - CALL_GL_API(glBlendFunciOES, buf, src, dst); -} -void API_ENTRY(glBlendFuncSeparateiOES)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) { - CALL_GL_API(glBlendFuncSeparateiOES, buf, srcRGB, dstRGB, srcAlpha, dstAlpha); -} -void API_ENTRY(glColorMaskiOES)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) { - CALL_GL_API(glColorMaskiOES, index, r, g, b, a); -} -GLboolean API_ENTRY(glIsEnablediOES)(GLenum target, GLuint index) { - CALL_GL_API_RETURN(glIsEnablediOES, target, index); -} -void API_ENTRY(glDrawElementsBaseVertexOES)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) { - CALL_GL_API(glDrawElementsBaseVertexOES, mode, count, type, indices, basevertex); -} -void API_ENTRY(glDrawRangeElementsBaseVertexOES)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) { - CALL_GL_API(glDrawRangeElementsBaseVertexOES, mode, start, end, count, type, indices, basevertex); -} -void API_ENTRY(glDrawElementsInstancedBaseVertexOES)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) { - CALL_GL_API(glDrawElementsInstancedBaseVertexOES, mode, count, type, indices, instancecount, basevertex); -} -void API_ENTRY(glFramebufferTextureOES)(GLenum target, GLenum attachment, GLuint texture, GLint level) { - CALL_GL_API(glFramebufferTextureOES, target, attachment, texture, level); -} -void API_ENTRY(glGetProgramBinaryOES)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary) { - CALL_GL_API(glGetProgramBinaryOES, program, bufSize, length, binaryFormat, binary); -} -void API_ENTRY(glProgramBinaryOES)(GLuint program, GLenum binaryFormat, const void *binary, GLint length) { - CALL_GL_API(glProgramBinaryOES, program, binaryFormat, binary, length); -} -void * API_ENTRY(glMapBufferOES)(GLenum target, GLenum access) { - CALL_GL_API_RETURN(glMapBufferOES, target, access); -} -GLboolean API_ENTRY(glUnmapBufferOES)(GLenum target) { - CALL_GL_API_RETURN(glUnmapBufferOES, target); -} -void API_ENTRY(glGetBufferPointervOES)(GLenum target, GLenum pname, void **params) { - CALL_GL_API(glGetBufferPointervOES, target, pname, params); -} -void API_ENTRY(glPrimitiveBoundingBoxOES)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) { - CALL_GL_API(glPrimitiveBoundingBoxOES, minX, minY, minZ, minW, maxX, maxY, maxZ, maxW); -} -void API_ENTRY(glMinSampleShadingOES)(GLfloat value) { - CALL_GL_API(glMinSampleShadingOES, value); -} -void API_ENTRY(glPatchParameteriOES)(GLenum pname, GLint value) { - CALL_GL_API(glPatchParameteriOES, pname, value); -} -void API_ENTRY(glTexImage3DOES)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) { - CALL_GL_API(glTexImage3DOES, target, level, internalformat, width, height, depth, border, format, type, pixels); -} -void API_ENTRY(glTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) { - CALL_GL_API(glTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); -} -void API_ENTRY(glCopyTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) { - CALL_GL_API(glCopyTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, x, y, width, height); -} -void API_ENTRY(glCompressedTexImage3DOES)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data) { - CALL_GL_API(glCompressedTexImage3DOES, target, level, internalformat, width, height, depth, border, imageSize, data); -} -void API_ENTRY(glCompressedTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data) { - CALL_GL_API(glCompressedTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data); -} -void API_ENTRY(glFramebufferTexture3DOES)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) { - CALL_GL_API(glFramebufferTexture3DOES, target, attachment, textarget, texture, level, zoffset); -} -void API_ENTRY(glTexParameterIivOES)(GLenum target, GLenum pname, const GLint *params) { - CALL_GL_API(glTexParameterIivOES, target, pname, params); -} -void API_ENTRY(glTexParameterIuivOES)(GLenum target, GLenum pname, const GLuint *params) { - CALL_GL_API(glTexParameterIuivOES, target, pname, params); -} -void API_ENTRY(glGetTexParameterIivOES)(GLenum target, GLenum pname, GLint *params) { - CALL_GL_API(glGetTexParameterIivOES, target, pname, params); -} -void API_ENTRY(glGetTexParameterIuivOES)(GLenum target, GLenum pname, GLuint *params) { - CALL_GL_API(glGetTexParameterIuivOES, target, pname, params); -} -void API_ENTRY(glSamplerParameterIivOES)(GLuint sampler, GLenum pname, const GLint *param) { - CALL_GL_API(glSamplerParameterIivOES, sampler, pname, param); -} -void API_ENTRY(glSamplerParameterIuivOES)(GLuint sampler, GLenum pname, const GLuint *param) { - CALL_GL_API(glSamplerParameterIuivOES, sampler, pname, param); -} -void API_ENTRY(glGetSamplerParameterIivOES)(GLuint sampler, GLenum pname, GLint *params) { - CALL_GL_API(glGetSamplerParameterIivOES, sampler, pname, params); -} -void API_ENTRY(glGetSamplerParameterIuivOES)(GLuint sampler, GLenum pname, GLuint *params) { - CALL_GL_API(glGetSamplerParameterIuivOES, sampler, pname, params); -} -void API_ENTRY(glTexBufferOES)(GLenum target, GLenum internalformat, GLuint buffer) { - CALL_GL_API(glTexBufferOES, target, internalformat, buffer); -} -void API_ENTRY(glTexBufferRangeOES)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) { - CALL_GL_API(glTexBufferRangeOES, target, internalformat, buffer, offset, size); -} -void API_ENTRY(glTexStorage3DMultisampleOES)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) { - CALL_GL_API(glTexStorage3DMultisampleOES, target, samples, internalformat, width, height, depth, fixedsamplelocations); -} -void API_ENTRY(glTextureViewOES)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) { - CALL_GL_API(glTextureViewOES, texture, target, origtexture, internalformat, minlevel, numlevels, minlayer, numlayers); -} -void API_ENTRY(glBindVertexArrayOES)(GLuint array) { - CALL_GL_API(glBindVertexArrayOES, array); -} -void API_ENTRY(glDeleteVertexArraysOES)(GLsizei n, const GLuint *arrays) { - CALL_GL_API(glDeleteVertexArraysOES, n, arrays); -} -void API_ENTRY(glGenVertexArraysOES)(GLsizei n, GLuint *arrays) { - CALL_GL_API(glGenVertexArraysOES, n, arrays); -} -GLboolean API_ENTRY(glIsVertexArrayOES)(GLuint array) { - CALL_GL_API_RETURN(glIsVertexArrayOES, array); -} -void API_ENTRY(glDrawArraysInstancedBaseInstanceEXT)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance) { - CALL_GL_API(glDrawArraysInstancedBaseInstanceEXT, mode, first, count, instancecount, baseinstance); -} -void API_ENTRY(glDrawElementsInstancedBaseInstanceEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance) { - CALL_GL_API(glDrawElementsInstancedBaseInstanceEXT, mode, count, type, indices, instancecount, baseinstance); -} -void API_ENTRY(glDrawElementsInstancedBaseVertexBaseInstanceEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance) { - CALL_GL_API(glDrawElementsInstancedBaseVertexBaseInstanceEXT, mode, count, type, indices, instancecount, basevertex, baseinstance); -} -void API_ENTRY(glBindFragDataLocationIndexedEXT)(GLuint program, GLuint colorNumber, GLuint index, const GLchar *name) { - CALL_GL_API(glBindFragDataLocationIndexedEXT, program, colorNumber, index, name); -} -void API_ENTRY(glBindFragDataLocationEXT)(GLuint program, GLuint color, const GLchar *name) { - CALL_GL_API(glBindFragDataLocationEXT, program, color, name); -} -GLint API_ENTRY(glGetProgramResourceLocationIndexEXT)(GLuint program, GLenum programInterface, const GLchar *name) { - CALL_GL_API_RETURN(glGetProgramResourceLocationIndexEXT, program, programInterface, name); -} -GLint API_ENTRY(glGetFragDataIndexEXT)(GLuint program, const GLchar *name) { - CALL_GL_API_RETURN(glGetFragDataIndexEXT, program, name); -} -void API_ENTRY(glBufferStorageEXT)(GLenum target, GLsizeiptr size, const void *data, GLbitfield flags) { - CALL_GL_API(glBufferStorageEXT, target, size, data, flags); -} -void API_ENTRY(glCopyImageSubDataEXT)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) { - CALL_GL_API(glCopyImageSubDataEXT, srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth); -} -void API_ENTRY(glLabelObjectEXT)(GLenum type, GLuint object, GLsizei length, const GLchar *label) { - CALL_GL_API(glLabelObjectEXT, type, object, length, label); -} -void API_ENTRY(glGetObjectLabelEXT)(GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label) { - CALL_GL_API(glGetObjectLabelEXT, type, object, bufSize, length, label); -} -void API_ENTRY(glInsertEventMarkerEXT)(GLsizei length, const GLchar *marker) { - CALL_GL_API(glInsertEventMarkerEXT, length, marker); -} -void API_ENTRY(glPushGroupMarkerEXT)(GLsizei length, const GLchar *marker) { - CALL_GL_API(glPushGroupMarkerEXT, length, marker); -} -void API_ENTRY(glPopGroupMarkerEXT)(void) { - CALL_GL_API(glPopGroupMarkerEXT); -} -void API_ENTRY(glDiscardFramebufferEXT)(GLenum target, GLsizei numAttachments, const GLenum *attachments) { - CALL_GL_API(glDiscardFramebufferEXT, target, numAttachments, attachments); -} -void API_ENTRY(glGenQueriesEXT)(GLsizei n, GLuint *ids) { - CALL_GL_API(glGenQueriesEXT, n, ids); -} -void API_ENTRY(glDeleteQueriesEXT)(GLsizei n, const GLuint *ids) { - CALL_GL_API(glDeleteQueriesEXT, n, ids); -} -GLboolean API_ENTRY(glIsQueryEXT)(GLuint id) { - CALL_GL_API_RETURN(glIsQueryEXT, id); -} -void API_ENTRY(glBeginQueryEXT)(GLenum target, GLuint id) { - CALL_GL_API(glBeginQueryEXT, target, id); -} -void API_ENTRY(glEndQueryEXT)(GLenum target) { - CALL_GL_API(glEndQueryEXT, target); -} -void API_ENTRY(glQueryCounterEXT)(GLuint id, GLenum target) { - CALL_GL_API(glQueryCounterEXT, id, target); -} -void API_ENTRY(glGetQueryivEXT)(GLenum target, GLenum pname, GLint *params) { - CALL_GL_API(glGetQueryivEXT, target, pname, params); -} -void API_ENTRY(glGetQueryObjectivEXT)(GLuint id, GLenum pname, GLint *params) { - CALL_GL_API(glGetQueryObjectivEXT, id, pname, params); -} -void API_ENTRY(glGetQueryObjectuivEXT)(GLuint id, GLenum pname, GLuint *params) { - CALL_GL_API(glGetQueryObjectuivEXT, id, pname, params); -} -void API_ENTRY(glGetQueryObjecti64vEXT)(GLuint id, GLenum pname, GLint64 *params) { - CALL_GL_API(glGetQueryObjecti64vEXT, id, pname, params); -} -void API_ENTRY(glGetQueryObjectui64vEXT)(GLuint id, GLenum pname, GLuint64 *params) { - CALL_GL_API(glGetQueryObjectui64vEXT, id, pname, params); -} -void API_ENTRY(glDrawBuffersEXT)(GLsizei n, const GLenum *bufs) { - CALL_GL_API(glDrawBuffersEXT, n, bufs); -} -void API_ENTRY(glEnableiEXT)(GLenum target, GLuint index) { - CALL_GL_API(glEnableiEXT, target, index); -} -void API_ENTRY(glDisableiEXT)(GLenum target, GLuint index) { - CALL_GL_API(glDisableiEXT, target, index); -} -void API_ENTRY(glBlendEquationiEXT)(GLuint buf, GLenum mode) { - CALL_GL_API(glBlendEquationiEXT, buf, mode); -} -void API_ENTRY(glBlendEquationSeparateiEXT)(GLuint buf, GLenum modeRGB, GLenum modeAlpha) { - CALL_GL_API(glBlendEquationSeparateiEXT, buf, modeRGB, modeAlpha); -} -void API_ENTRY(glBlendFunciEXT)(GLuint buf, GLenum src, GLenum dst) { - CALL_GL_API(glBlendFunciEXT, buf, src, dst); -} -void API_ENTRY(glBlendFuncSeparateiEXT)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) { - CALL_GL_API(glBlendFuncSeparateiEXT, buf, srcRGB, dstRGB, srcAlpha, dstAlpha); -} -void API_ENTRY(glColorMaskiEXT)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) { - CALL_GL_API(glColorMaskiEXT, index, r, g, b, a); -} -GLboolean API_ENTRY(glIsEnablediEXT)(GLenum target, GLuint index) { - CALL_GL_API_RETURN(glIsEnablediEXT, target, index); -} -void API_ENTRY(glDrawElementsBaseVertexEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) { - CALL_GL_API(glDrawElementsBaseVertexEXT, mode, count, type, indices, basevertex); -} -void API_ENTRY(glDrawRangeElementsBaseVertexEXT)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) { - CALL_GL_API(glDrawRangeElementsBaseVertexEXT, mode, start, end, count, type, indices, basevertex); -} -void API_ENTRY(glDrawElementsInstancedBaseVertexEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) { - CALL_GL_API(glDrawElementsInstancedBaseVertexEXT, mode, count, type, indices, instancecount, basevertex); -} -void API_ENTRY(glMultiDrawElementsBaseVertexEXT)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex) { - CALL_GL_API(glMultiDrawElementsBaseVertexEXT, mode, count, type, indices, primcount, basevertex); -} -void API_ENTRY(glDrawArraysInstancedEXT)(GLenum mode, GLint start, GLsizei count, GLsizei primcount) { - CALL_GL_API(glDrawArraysInstancedEXT, mode, start, count, primcount); -} -void API_ENTRY(glDrawElementsInstancedEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount) { - CALL_GL_API(glDrawElementsInstancedEXT, mode, count, type, indices, primcount); -} -void API_ENTRY(glFramebufferTextureEXT)(GLenum target, GLenum attachment, GLuint texture, GLint level) { - CALL_GL_API(glFramebufferTextureEXT, target, attachment, texture, level); -} -void API_ENTRY(glVertexAttribDivisorEXT)(GLuint index, GLuint divisor) { - CALL_GL_API(glVertexAttribDivisorEXT, index, divisor); -} -void * API_ENTRY(glMapBufferRangeEXT)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) { - CALL_GL_API_RETURN(glMapBufferRangeEXT, target, offset, length, access); -} -void API_ENTRY(glFlushMappedBufferRangeEXT)(GLenum target, GLintptr offset, GLsizeiptr length) { - CALL_GL_API(glFlushMappedBufferRangeEXT, target, offset, length); -} -void API_ENTRY(glMultiDrawArraysEXT)(GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount) { - CALL_GL_API(glMultiDrawArraysEXT, mode, first, count, primcount); -} -void API_ENTRY(glMultiDrawElementsEXT)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount) { - CALL_GL_API(glMultiDrawElementsEXT, mode, count, type, indices, primcount); -} -void API_ENTRY(glMultiDrawArraysIndirectEXT)(GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride) { - CALL_GL_API(glMultiDrawArraysIndirectEXT, mode, indirect, drawcount, stride); -} -void API_ENTRY(glMultiDrawElementsIndirectEXT)(GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride) { - CALL_GL_API(glMultiDrawElementsIndirectEXT, mode, type, indirect, drawcount, stride); -} -void API_ENTRY(glRenderbufferStorageMultisampleEXT)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { - CALL_GL_API(glRenderbufferStorageMultisampleEXT, target, samples, internalformat, width, height); -} -void API_ENTRY(glFramebufferTexture2DMultisampleEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) { - CALL_GL_API(glFramebufferTexture2DMultisampleEXT, target, attachment, textarget, texture, level, samples); -} -void API_ENTRY(glReadBufferIndexedEXT)(GLenum src, GLint index) { - CALL_GL_API(glReadBufferIndexedEXT, src, index); -} -void API_ENTRY(glDrawBuffersIndexedEXT)(GLint n, const GLenum *location, const GLint *indices) { - CALL_GL_API(glDrawBuffersIndexedEXT, n, location, indices); -} -void API_ENTRY(glGetIntegeri_vEXT)(GLenum target, GLuint index, GLint *data) { - CALL_GL_API(glGetIntegeri_vEXT, target, index, data); -} -void API_ENTRY(glPrimitiveBoundingBoxEXT)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) { - CALL_GL_API(glPrimitiveBoundingBoxEXT, minX, minY, minZ, minW, maxX, maxY, maxZ, maxW); -} -void API_ENTRY(glRasterSamplesEXT)(GLuint samples, GLboolean fixedsamplelocations) { - CALL_GL_API(glRasterSamplesEXT, samples, fixedsamplelocations); -} -GLenum API_ENTRY(glGetGraphicsResetStatusEXT)(void) { - CALL_GL_API_RETURN(glGetGraphicsResetStatusEXT); -} -void API_ENTRY(glReadnPixelsEXT)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) { - CALL_GL_API(glReadnPixelsEXT, x, y, width, height, format, type, bufSize, data); -} -void API_ENTRY(glGetnUniformfvEXT)(GLuint program, GLint location, GLsizei bufSize, GLfloat *params) { - CALL_GL_API(glGetnUniformfvEXT, program, location, bufSize, params); -} -void API_ENTRY(glGetnUniformivEXT)(GLuint program, GLint location, GLsizei bufSize, GLint *params) { - CALL_GL_API(glGetnUniformivEXT, program, location, bufSize, params); -} -void API_ENTRY(glActiveShaderProgramEXT)(GLuint pipeline, GLuint program) { - CALL_GL_API(glActiveShaderProgramEXT, pipeline, program); -} -void API_ENTRY(glBindProgramPipelineEXT)(GLuint pipeline) { - CALL_GL_API(glBindProgramPipelineEXT, pipeline); -} -GLuint API_ENTRY(glCreateShaderProgramvEXT)(GLenum type, GLsizei count, const GLchar **strings) { - CALL_GL_API_RETURN(glCreateShaderProgramvEXT, type, count, strings); -} -void API_ENTRY(glDeleteProgramPipelinesEXT)(GLsizei n, const GLuint *pipelines) { - CALL_GL_API(glDeleteProgramPipelinesEXT, n, pipelines); -} -void API_ENTRY(glGenProgramPipelinesEXT)(GLsizei n, GLuint *pipelines) { - CALL_GL_API(glGenProgramPipelinesEXT, n, pipelines); -} -void API_ENTRY(glGetProgramPipelineInfoLogEXT)(GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog) { - CALL_GL_API(glGetProgramPipelineInfoLogEXT, pipeline, bufSize, length, infoLog); -} -void API_ENTRY(glGetProgramPipelineivEXT)(GLuint pipeline, GLenum pname, GLint *params) { - CALL_GL_API(glGetProgramPipelineivEXT, pipeline, pname, params); -} -GLboolean API_ENTRY(glIsProgramPipelineEXT)(GLuint pipeline) { - CALL_GL_API_RETURN(glIsProgramPipelineEXT, pipeline); -} -void API_ENTRY(glProgramParameteriEXT)(GLuint program, GLenum pname, GLint value) { - CALL_GL_API(glProgramParameteriEXT, program, pname, value); -} -void API_ENTRY(glProgramUniform1fEXT)(GLuint program, GLint location, GLfloat v0) { - CALL_GL_API(glProgramUniform1fEXT, program, location, v0); -} -void API_ENTRY(glProgramUniform1fvEXT)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glProgramUniform1fvEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform1iEXT)(GLuint program, GLint location, GLint v0) { - CALL_GL_API(glProgramUniform1iEXT, program, location, v0); -} -void API_ENTRY(glProgramUniform1ivEXT)(GLuint program, GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glProgramUniform1ivEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform2fEXT)(GLuint program, GLint location, GLfloat v0, GLfloat v1) { - CALL_GL_API(glProgramUniform2fEXT, program, location, v0, v1); -} -void API_ENTRY(glProgramUniform2fvEXT)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glProgramUniform2fvEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform2iEXT)(GLuint program, GLint location, GLint v0, GLint v1) { - CALL_GL_API(glProgramUniform2iEXT, program, location, v0, v1); -} -void API_ENTRY(glProgramUniform2ivEXT)(GLuint program, GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glProgramUniform2ivEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform3fEXT)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) { - CALL_GL_API(glProgramUniform3fEXT, program, location, v0, v1, v2); -} -void API_ENTRY(glProgramUniform3fvEXT)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glProgramUniform3fvEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform3iEXT)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2) { - CALL_GL_API(glProgramUniform3iEXT, program, location, v0, v1, v2); -} -void API_ENTRY(glProgramUniform3ivEXT)(GLuint program, GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glProgramUniform3ivEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform4fEXT)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) { - CALL_GL_API(glProgramUniform4fEXT, program, location, v0, v1, v2, v3); -} -void API_ENTRY(glProgramUniform4fvEXT)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { - CALL_GL_API(glProgramUniform4fvEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform4iEXT)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3) { - CALL_GL_API(glProgramUniform4iEXT, program, location, v0, v1, v2, v3); -} -void API_ENTRY(glProgramUniform4ivEXT)(GLuint program, GLint location, GLsizei count, const GLint *value) { - CALL_GL_API(glProgramUniform4ivEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniformMatrix2fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix2fvEXT, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix3fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix3fvEXT, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix4fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix4fvEXT, program, location, count, transpose, value); -} -void API_ENTRY(glUseProgramStagesEXT)(GLuint pipeline, GLbitfield stages, GLuint program) { - CALL_GL_API(glUseProgramStagesEXT, pipeline, stages, program); -} -void API_ENTRY(glValidateProgramPipelineEXT)(GLuint pipeline) { - CALL_GL_API(glValidateProgramPipelineEXT, pipeline); -} -void API_ENTRY(glProgramUniform1uiEXT)(GLuint program, GLint location, GLuint v0) { - CALL_GL_API(glProgramUniform1uiEXT, program, location, v0); -} -void API_ENTRY(glProgramUniform2uiEXT)(GLuint program, GLint location, GLuint v0, GLuint v1) { - CALL_GL_API(glProgramUniform2uiEXT, program, location, v0, v1); -} -void API_ENTRY(glProgramUniform3uiEXT)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2) { - CALL_GL_API(glProgramUniform3uiEXT, program, location, v0, v1, v2); -} -void API_ENTRY(glProgramUniform4uiEXT)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) { - CALL_GL_API(glProgramUniform4uiEXT, program, location, v0, v1, v2, v3); -} -void API_ENTRY(glProgramUniform1uivEXT)(GLuint program, GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glProgramUniform1uivEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform2uivEXT)(GLuint program, GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glProgramUniform2uivEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform3uivEXT)(GLuint program, GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glProgramUniform3uivEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniform4uivEXT)(GLuint program, GLint location, GLsizei count, const GLuint *value) { - CALL_GL_API(glProgramUniform4uivEXT, program, location, count, value); -} -void API_ENTRY(glProgramUniformMatrix2x3fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix2x3fvEXT, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix3x2fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix3x2fvEXT, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix2x4fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix2x4fvEXT, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix4x2fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix4x2fvEXT, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix3x4fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix3x4fvEXT, program, location, count, transpose, value); -} -void API_ENTRY(glProgramUniformMatrix4x3fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { - CALL_GL_API(glProgramUniformMatrix4x3fvEXT, program, location, count, transpose, value); -} -void API_ENTRY(glTexPageCommitmentEXT)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit) { - CALL_GL_API(glTexPageCommitmentEXT, target, level, xoffset, yoffset, zoffset, width, height, depth, commit); -} -void API_ENTRY(glPatchParameteriEXT)(GLenum pname, GLint value) { - CALL_GL_API(glPatchParameteriEXT, pname, value); -} -void API_ENTRY(glTexParameterIivEXT)(GLenum target, GLenum pname, const GLint *params) { - CALL_GL_API(glTexParameterIivEXT, target, pname, params); -} -void API_ENTRY(glTexParameterIuivEXT)(GLenum target, GLenum pname, const GLuint *params) { - CALL_GL_API(glTexParameterIuivEXT, target, pname, params); -} -void API_ENTRY(glGetTexParameterIivEXT)(GLenum target, GLenum pname, GLint *params) { - CALL_GL_API(glGetTexParameterIivEXT, target, pname, params); -} -void API_ENTRY(glGetTexParameterIuivEXT)(GLenum target, GLenum pname, GLuint *params) { - CALL_GL_API(glGetTexParameterIuivEXT, target, pname, params); -} -void API_ENTRY(glSamplerParameterIivEXT)(GLuint sampler, GLenum pname, const GLint *param) { - CALL_GL_API(glSamplerParameterIivEXT, sampler, pname, param); -} -void API_ENTRY(glSamplerParameterIuivEXT)(GLuint sampler, GLenum pname, const GLuint *param) { - CALL_GL_API(glSamplerParameterIuivEXT, sampler, pname, param); -} -void API_ENTRY(glGetSamplerParameterIivEXT)(GLuint sampler, GLenum pname, GLint *params) { - CALL_GL_API(glGetSamplerParameterIivEXT, sampler, pname, params); -} -void API_ENTRY(glGetSamplerParameterIuivEXT)(GLuint sampler, GLenum pname, GLuint *params) { - CALL_GL_API(glGetSamplerParameterIuivEXT, sampler, pname, params); -} -void API_ENTRY(glTexBufferEXT)(GLenum target, GLenum internalformat, GLuint buffer) { - CALL_GL_API(glTexBufferEXT, target, internalformat, buffer); -} -void API_ENTRY(glTexBufferRangeEXT)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) { - CALL_GL_API(glTexBufferRangeEXT, target, internalformat, buffer, offset, size); -} -void API_ENTRY(glTexStorage1DEXT)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) { - CALL_GL_API(glTexStorage1DEXT, target, levels, internalformat, width); -} -void API_ENTRY(glTexStorage2DEXT)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) { - CALL_GL_API(glTexStorage2DEXT, target, levels, internalformat, width, height); -} -void API_ENTRY(glTexStorage3DEXT)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) { - CALL_GL_API(glTexStorage3DEXT, target, levels, internalformat, width, height, depth); -} -void API_ENTRY(glTextureStorage1DEXT)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) { - CALL_GL_API(glTextureStorage1DEXT, texture, target, levels, internalformat, width); -} -void API_ENTRY(glTextureStorage2DEXT)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) { - CALL_GL_API(glTextureStorage2DEXT, texture, target, levels, internalformat, width, height); -} -void API_ENTRY(glTextureStorage3DEXT)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) { - CALL_GL_API(glTextureStorage3DEXT, texture, target, levels, internalformat, width, height, depth); -} -void API_ENTRY(glTextureViewEXT)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) { - CALL_GL_API(glTextureViewEXT, texture, target, origtexture, internalformat, minlevel, numlevels, minlayer, numlayers); -} diff --git a/libs/hwui/debug/gles_undefine.h b/libs/hwui/debug/gles_undefine.h deleted file mode 100644 index e43829d98e38..000000000000 --- a/libs/hwui/debug/gles_undefine.h +++ /dev/null @@ -1,913 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#undef glActiveShaderProgram -#undef glActiveShaderProgramEXT -#undef glActiveTexture -#undef glAlphaFunc -#undef glAlphaFuncQCOM -#undef glAlphaFuncx -#undef glAlphaFuncxOES -#undef glApplyFramebufferAttachmentCMAAINTEL -#undef glAttachShader -#undef glBeginConditionalRenderNV -#undef glBeginPerfMonitorAMD -#undef glBeginPerfQueryINTEL -#undef glBeginQuery -#undef glBeginQueryEXT -#undef glBeginTransformFeedback -#undef glBindAttribLocation -#undef glBindBuffer -#undef glBindBufferBase -#undef glBindBufferRange -#undef glBindFragDataLocationEXT -#undef glBindFragDataLocationIndexedEXT -#undef glBindFramebuffer -#undef glBindFramebufferOES -#undef glBindImageTexture -#undef glBindProgramPipeline -#undef glBindProgramPipelineEXT -#undef glBindRenderbuffer -#undef glBindRenderbufferOES -#undef glBindSampler -#undef glBindTexture -#undef glBindTransformFeedback -#undef glBindVertexArray -#undef glBindVertexArrayOES -#undef glBindVertexBuffer -#undef glBlendBarrier -#undef glBlendBarrierKHR -#undef glBlendBarrierNV -#undef glBlendColor -#undef glBlendEquation -#undef glBlendEquationOES -#undef glBlendEquationSeparate -#undef glBlendEquationSeparateOES -#undef glBlendEquationSeparatei -#undef glBlendEquationSeparateiEXT -#undef glBlendEquationSeparateiOES -#undef glBlendEquationi -#undef glBlendEquationiEXT -#undef glBlendEquationiOES -#undef glBlendFunc -#undef glBlendFuncSeparate -#undef glBlendFuncSeparateOES -#undef glBlendFuncSeparatei -#undef glBlendFuncSeparateiEXT -#undef glBlendFuncSeparateiOES -#undef glBlendFunci -#undef glBlendFunciEXT -#undef glBlendFunciOES -#undef glBlendParameteriNV -#undef glBlitFramebuffer -#undef glBlitFramebufferANGLE -#undef glBlitFramebufferNV -#undef glBufferData -#undef glBufferStorageEXT -#undef glBufferSubData -#undef glCheckFramebufferStatus -#undef glCheckFramebufferStatusOES -#undef glClear -#undef glClearBufferfi -#undef glClearBufferfv -#undef glClearBufferiv -#undef glClearBufferuiv -#undef glClearColor -#undef glClearColorx -#undef glClearColorxOES -#undef glClearDepthf -#undef glClearDepthfOES -#undef glClearDepthx -#undef glClearDepthxOES -#undef glClearStencil -#undef glClientActiveTexture -#undef glClientWaitSync -#undef glClientWaitSyncAPPLE -#undef glClipPlanef -#undef glClipPlanefIMG -#undef glClipPlanefOES -#undef glClipPlanex -#undef glClipPlanexIMG -#undef glClipPlanexOES -#undef glColor4f -#undef glColor4ub -#undef glColor4x -#undef glColor4xOES -#undef glColorMask -#undef glColorMaski -#undef glColorMaskiEXT -#undef glColorMaskiOES -#undef glColorPointer -#undef glCompileShader -#undef glCompressedTexImage2D -#undef glCompressedTexImage3D -#undef glCompressedTexImage3DOES -#undef glCompressedTexSubImage2D -#undef glCompressedTexSubImage3D -#undef glCompressedTexSubImage3DOES -#undef glCopyBufferSubData -#undef glCopyBufferSubDataNV -#undef glCopyImageSubData -#undef glCopyImageSubDataEXT -#undef glCopyImageSubDataOES -#undef glCopyPathNV -#undef glCopyTexImage2D -#undef glCopyTexSubImage2D -#undef glCopyTexSubImage3D -#undef glCopyTexSubImage3DOES -#undef glCopyTextureLevelsAPPLE -#undef glCoverFillPathInstancedNV -#undef glCoverFillPathNV -#undef glCoverStrokePathInstancedNV -#undef glCoverStrokePathNV -#undef glCoverageMaskNV -#undef glCoverageModulationNV -#undef glCoverageModulationTableNV -#undef glCoverageOperationNV -#undef glCreatePerfQueryINTEL -#undef glCreateProgram -#undef glCreateShader -#undef glCreateShaderProgramv -#undef glCreateShaderProgramvEXT -#undef glCullFace -#undef glCurrentPaletteMatrixOES -#undef glDebugMessageCallback -#undef glDebugMessageCallbackKHR -#undef glDebugMessageControl -#undef glDebugMessageControlKHR -#undef glDebugMessageInsert -#undef glDebugMessageInsertKHR -#undef glDeleteBuffers -#undef glDeleteFencesNV -#undef glDeleteFramebuffers -#undef glDeleteFramebuffersOES -#undef glDeletePathsNV -#undef glDeletePerfMonitorsAMD -#undef glDeletePerfQueryINTEL -#undef glDeleteProgram -#undef glDeleteProgramPipelines -#undef glDeleteProgramPipelinesEXT -#undef glDeleteQueries -#undef glDeleteQueriesEXT -#undef glDeleteRenderbuffers -#undef glDeleteRenderbuffersOES -#undef glDeleteSamplers -#undef glDeleteShader -#undef glDeleteSync -#undef glDeleteSyncAPPLE -#undef glDeleteTextures -#undef glDeleteTransformFeedbacks -#undef glDeleteVertexArrays -#undef glDeleteVertexArraysOES -#undef glDepthFunc -#undef glDepthMask -#undef glDepthRangeArrayfvNV -#undef glDepthRangeIndexedfNV -#undef glDepthRangef -#undef glDepthRangefOES -#undef glDepthRangex -#undef glDepthRangexOES -#undef glDetachShader -#undef glDisable -#undef glDisableClientState -#undef glDisableDriverControlQCOM -#undef glDisableVertexAttribArray -#undef glDisablei -#undef glDisableiEXT -#undef glDisableiNV -#undef glDisableiOES -#undef glDiscardFramebufferEXT -#undef glDispatchCompute -#undef glDispatchComputeIndirect -#undef glDrawArrays -#undef glDrawArraysIndirect -#undef glDrawArraysInstanced -#undef glDrawArraysInstancedANGLE -#undef glDrawArraysInstancedBaseInstanceEXT -#undef glDrawArraysInstancedEXT -#undef glDrawArraysInstancedNV -#undef glDrawBuffers -#undef glDrawBuffersEXT -#undef glDrawBuffersIndexedEXT -#undef glDrawBuffersNV -#undef glDrawElements -#undef glDrawElementsBaseVertex -#undef glDrawElementsBaseVertexEXT -#undef glDrawElementsBaseVertexOES -#undef glDrawElementsIndirect -#undef glDrawElementsInstanced -#undef glDrawElementsInstancedANGLE -#undef glDrawElementsInstancedBaseInstanceEXT -#undef glDrawElementsInstancedBaseVertex -#undef glDrawElementsInstancedBaseVertexBaseInstanceEXT -#undef glDrawElementsInstancedBaseVertexEXT -#undef glDrawElementsInstancedBaseVertexOES -#undef glDrawElementsInstancedEXT -#undef glDrawElementsInstancedNV -#undef glDrawRangeElements -#undef glDrawRangeElementsBaseVertex -#undef glDrawRangeElementsBaseVertexEXT -#undef glDrawRangeElementsBaseVertexOES -#undef glDrawTexfOES -#undef glDrawTexfvOES -#undef glDrawTexiOES -#undef glDrawTexivOES -#undef glDrawTexsOES -#undef glDrawTexsvOES -#undef glDrawTexxOES -#undef glDrawTexxvOES -#undef glEGLImageTargetRenderbufferStorageOES -#undef glEGLImageTargetTexture2DOES -#undef glEnable -#undef glEnableClientState -#undef glEnableDriverControlQCOM -#undef glEnableVertexAttribArray -#undef glEnablei -#undef glEnableiEXT -#undef glEnableiNV -#undef glEnableiOES -#undef glEndConditionalRenderNV -#undef glEndPerfMonitorAMD -#undef glEndPerfQueryINTEL -#undef glEndQuery -#undef glEndQueryEXT -#undef glEndTilingQCOM -#undef glEndTransformFeedback -#undef glExtGetBufferPointervQCOM -#undef glExtGetBuffersQCOM -#undef glExtGetFramebuffersQCOM -#undef glExtGetProgramBinarySourceQCOM -#undef glExtGetProgramsQCOM -#undef glExtGetRenderbuffersQCOM -#undef glExtGetShadersQCOM -#undef glExtGetTexLevelParameterivQCOM -#undef glExtGetTexSubImageQCOM -#undef glExtGetTexturesQCOM -#undef glExtIsProgramBinaryQCOM -#undef glExtTexObjectStateOverrideiQCOM -#undef glFenceSync -#undef glFenceSyncAPPLE -#undef glFinish -#undef glFinishFenceNV -#undef glFlush -#undef glFlushMappedBufferRange -#undef glFlushMappedBufferRangeEXT -#undef glFogf -#undef glFogfv -#undef glFogx -#undef glFogxOES -#undef glFogxv -#undef glFogxvOES -#undef glFragmentCoverageColorNV -#undef glFramebufferParameteri -#undef glFramebufferRenderbuffer -#undef glFramebufferRenderbufferOES -#undef glFramebufferSampleLocationsfvNV -#undef glFramebufferTexture -#undef glFramebufferTexture2D -#undef glFramebufferTexture2DMultisampleEXT -#undef glFramebufferTexture2DMultisampleIMG -#undef glFramebufferTexture2DOES -#undef glFramebufferTexture3DOES -#undef glFramebufferTextureEXT -#undef glFramebufferTextureLayer -#undef glFramebufferTextureMultisampleMultiviewOVR -#undef glFramebufferTextureMultiviewOVR -#undef glFramebufferTextureOES -#undef glFrontFace -#undef glFrustumf -#undef glFrustumfOES -#undef glFrustumx -#undef glFrustumxOES -#undef glGenBuffers -#undef glGenFencesNV -#undef glGenFramebuffers -#undef glGenFramebuffersOES -#undef glGenPathsNV -#undef glGenPerfMonitorsAMD -#undef glGenProgramPipelines -#undef glGenProgramPipelinesEXT -#undef glGenQueries -#undef glGenQueriesEXT -#undef glGenRenderbuffers -#undef glGenRenderbuffersOES -#undef glGenSamplers -#undef glGenTextures -#undef glGenTransformFeedbacks -#undef glGenVertexArrays -#undef glGenVertexArraysOES -#undef glGenerateMipmap -#undef glGenerateMipmapOES -#undef glGetActiveAttrib -#undef glGetActiveUniform -#undef glGetActiveUniformBlockName -#undef glGetActiveUniformBlockiv -#undef glGetActiveUniformsiv -#undef glGetAttachedShaders -#undef glGetAttribLocation -#undef glGetBooleani_v -#undef glGetBooleanv -#undef glGetBufferParameteri64v -#undef glGetBufferParameteriv -#undef glGetBufferPointerv -#undef glGetBufferPointervOES -#undef glGetClipPlanef -#undef glGetClipPlanefOES -#undef glGetClipPlanex -#undef glGetClipPlanexOES -#undef glGetCoverageModulationTableNV -#undef glGetDebugMessageLog -#undef glGetDebugMessageLogKHR -#undef glGetDriverControlStringQCOM -#undef glGetDriverControlsQCOM -#undef glGetError -#undef glGetFenceivNV -#undef glGetFirstPerfQueryIdINTEL -#undef glGetFixedv -#undef glGetFixedvOES -#undef glGetFloati_vNV -#undef glGetFloatv -#undef glGetFragDataIndexEXT -#undef glGetFragDataLocation -#undef glGetFramebufferAttachmentParameteriv -#undef glGetFramebufferAttachmentParameterivOES -#undef glGetFramebufferParameteriv -#undef glGetGraphicsResetStatus -#undef glGetGraphicsResetStatusEXT -#undef glGetGraphicsResetStatusKHR -#undef glGetImageHandleNV -#undef glGetInteger64i_v -#undef glGetInteger64v -#undef glGetInteger64vAPPLE -#undef glGetIntegeri_v -#undef glGetIntegeri_vEXT -#undef glGetIntegerv -#undef glGetInternalformatSampleivNV -#undef glGetInternalformativ -#undef glGetLightfv -#undef glGetLightxv -#undef glGetLightxvOES -#undef glGetMaterialfv -#undef glGetMaterialxv -#undef glGetMaterialxvOES -#undef glGetMultisamplefv -#undef glGetNextPerfQueryIdINTEL -#undef glGetObjectLabel -#undef glGetObjectLabelEXT -#undef glGetObjectLabelKHR -#undef glGetObjectPtrLabel -#undef glGetObjectPtrLabelKHR -#undef glGetPathCommandsNV -#undef glGetPathCoordsNV -#undef glGetPathDashArrayNV -#undef glGetPathLengthNV -#undef glGetPathMetricRangeNV -#undef glGetPathMetricsNV -#undef glGetPathParameterfvNV -#undef glGetPathParameterivNV -#undef glGetPathSpacingNV -#undef glGetPerfCounterInfoINTEL -#undef glGetPerfMonitorCounterDataAMD -#undef glGetPerfMonitorCounterInfoAMD -#undef glGetPerfMonitorCounterStringAMD -#undef glGetPerfMonitorCountersAMD -#undef glGetPerfMonitorGroupStringAMD -#undef glGetPerfMonitorGroupsAMD -#undef glGetPerfQueryDataINTEL -#undef glGetPerfQueryIdByNameINTEL -#undef glGetPerfQueryInfoINTEL -#undef glGetPointerv -#undef glGetPointervKHR -#undef glGetProgramBinary -#undef glGetProgramBinaryOES -#undef glGetProgramInfoLog -#undef glGetProgramInterfaceiv -#undef glGetProgramPipelineInfoLog -#undef glGetProgramPipelineInfoLogEXT -#undef glGetProgramPipelineiv -#undef glGetProgramPipelineivEXT -#undef glGetProgramResourceIndex -#undef glGetProgramResourceLocation -#undef glGetProgramResourceLocationIndexEXT -#undef glGetProgramResourceName -#undef glGetProgramResourcefvNV -#undef glGetProgramResourceiv -#undef glGetProgramiv -#undef glGetQueryObjecti64vEXT -#undef glGetQueryObjectivEXT -#undef glGetQueryObjectui64vEXT -#undef glGetQueryObjectuiv -#undef glGetQueryObjectuivEXT -#undef glGetQueryiv -#undef glGetQueryivEXT -#undef glGetRenderbufferParameteriv -#undef glGetRenderbufferParameterivOES -#undef glGetSamplerParameterIiv -#undef glGetSamplerParameterIivEXT -#undef glGetSamplerParameterIivOES -#undef glGetSamplerParameterIuiv -#undef glGetSamplerParameterIuivEXT -#undef glGetSamplerParameterIuivOES -#undef glGetSamplerParameterfv -#undef glGetSamplerParameteriv -#undef glGetShaderInfoLog -#undef glGetShaderPrecisionFormat -#undef glGetShaderSource -#undef glGetShaderiv -#undef glGetString -#undef glGetStringi -#undef glGetSynciv -#undef glGetSyncivAPPLE -#undef glGetTexEnvfv -#undef glGetTexEnviv -#undef glGetTexEnvxv -#undef glGetTexEnvxvOES -#undef glGetTexGenfvOES -#undef glGetTexGenivOES -#undef glGetTexGenxvOES -#undef glGetTexLevelParameterfv -#undef glGetTexLevelParameteriv -#undef glGetTexParameterIiv -#undef glGetTexParameterIivEXT -#undef glGetTexParameterIivOES -#undef glGetTexParameterIuiv -#undef glGetTexParameterIuivEXT -#undef glGetTexParameterIuivOES -#undef glGetTexParameterfv -#undef glGetTexParameteriv -#undef glGetTexParameterxv -#undef glGetTexParameterxvOES -#undef glGetTextureHandleNV -#undef glGetTextureSamplerHandleNV -#undef glGetTransformFeedbackVarying -#undef glGetTranslatedShaderSourceANGLE -#undef glGetUniformBlockIndex -#undef glGetUniformIndices -#undef glGetUniformLocation -#undef glGetUniformfv -#undef glGetUniformiv -#undef glGetUniformuiv -#undef glGetVertexAttribIiv -#undef glGetVertexAttribIuiv -#undef glGetVertexAttribPointerv -#undef glGetVertexAttribfv -#undef glGetVertexAttribiv -#undef glGetnUniformfv -#undef glGetnUniformfvEXT -#undef glGetnUniformfvKHR -#undef glGetnUniformiv -#undef glGetnUniformivEXT -#undef glGetnUniformivKHR -#undef glGetnUniformuiv -#undef glGetnUniformuivKHR -#undef glHint -#undef glInsertEventMarkerEXT -#undef glInterpolatePathsNV -#undef glInvalidateFramebuffer -#undef glInvalidateSubFramebuffer -#undef glIsBuffer -#undef glIsEnabled -#undef glIsEnabledi -#undef glIsEnablediEXT -#undef glIsEnablediNV -#undef glIsEnablediOES -#undef glIsFenceNV -#undef glIsFramebuffer -#undef glIsFramebufferOES -#undef glIsImageHandleResidentNV -#undef glIsPathNV -#undef glIsPointInFillPathNV -#undef glIsPointInStrokePathNV -#undef glIsProgram -#undef glIsProgramPipeline -#undef glIsProgramPipelineEXT -#undef glIsQuery -#undef glIsQueryEXT -#undef glIsRenderbuffer -#undef glIsRenderbufferOES -#undef glIsSampler -#undef glIsShader -#undef glIsSync -#undef glIsSyncAPPLE -#undef glIsTexture -#undef glIsTextureHandleResidentNV -#undef glIsTransformFeedback -#undef glIsVertexArray -#undef glIsVertexArrayOES -#undef glLabelObjectEXT -#undef glLightModelf -#undef glLightModelfv -#undef glLightModelx -#undef glLightModelxOES -#undef glLightModelxv -#undef glLightModelxvOES -#undef glLightf -#undef glLightfv -#undef glLightx -#undef glLightxOES -#undef glLightxv -#undef glLightxvOES -#undef glLineWidth -#undef glLineWidthx -#undef glLineWidthxOES -#undef glLinkProgram -#undef glLoadIdentity -#undef glLoadMatrixf -#undef glLoadMatrixx -#undef glLoadMatrixxOES -#undef glLoadPaletteFromModelViewMatrixOES -#undef glLogicOp -#undef glMakeImageHandleNonResidentNV -#undef glMakeImageHandleResidentNV -#undef glMakeTextureHandleNonResidentNV -#undef glMakeTextureHandleResidentNV -#undef glMapBufferOES -#undef glMapBufferRange -#undef glMapBufferRangeEXT -#undef glMaterialf -#undef glMaterialfv -#undef glMaterialx -#undef glMaterialxOES -#undef glMaterialxv -#undef glMaterialxvOES -#undef glMatrixIndexPointerOES -#undef glMatrixLoad3x2fNV -#undef glMatrixLoad3x3fNV -#undef glMatrixLoadTranspose3x3fNV -#undef glMatrixMode -#undef glMatrixMult3x2fNV -#undef glMatrixMult3x3fNV -#undef glMatrixMultTranspose3x3fNV -#undef glMemoryBarrier -#undef glMemoryBarrierByRegion -#undef glMinSampleShading -#undef glMinSampleShadingOES -#undef glMultMatrixf -#undef glMultMatrixx -#undef glMultMatrixxOES -#undef glMultiDrawArraysEXT -#undef glMultiDrawArraysIndirectEXT -#undef glMultiDrawElementsBaseVertexEXT -#undef glMultiDrawElementsBaseVertexOES -#undef glMultiDrawElementsEXT -#undef glMultiDrawElementsIndirectEXT -#undef glMultiTexCoord4f -#undef glMultiTexCoord4x -#undef glMultiTexCoord4xOES -#undef glNamedFramebufferSampleLocationsfvNV -#undef glNormal3f -#undef glNormal3x -#undef glNormal3xOES -#undef glNormalPointer -#undef glObjectLabel -#undef glObjectLabelKHR -#undef glObjectPtrLabel -#undef glObjectPtrLabelKHR -#undef glOrthof -#undef glOrthofOES -#undef glOrthox -#undef glOrthoxOES -#undef glPatchParameteri -#undef glPatchParameteriEXT -#undef glPatchParameteriOES -#undef glPathCommandsNV -#undef glPathCoordsNV -#undef glPathCoverDepthFuncNV -#undef glPathDashArrayNV -#undef glPathGlyphIndexArrayNV -#undef glPathGlyphIndexRangeNV -#undef glPathGlyphRangeNV -#undef glPathGlyphsNV -#undef glPathMemoryGlyphIndexArrayNV -#undef glPathParameterfNV -#undef glPathParameterfvNV -#undef glPathParameteriNV -#undef glPathParameterivNV -#undef glPathStencilDepthOffsetNV -#undef glPathStencilFuncNV -#undef glPathStringNV -#undef glPathSubCommandsNV -#undef glPathSubCoordsNV -#undef glPauseTransformFeedback -#undef glPixelStorei -#undef glPointAlongPathNV -#undef glPointParameterf -#undef glPointParameterfv -#undef glPointParameterx -#undef glPointParameterxOES -#undef glPointParameterxv -#undef glPointParameterxvOES -#undef glPointSize -#undef glPointSizePointerOES -#undef glPointSizex -#undef glPointSizexOES -#undef glPolygonModeNV -#undef glPolygonOffset -#undef glPolygonOffsetx -#undef glPolygonOffsetxOES -#undef glPopDebugGroup -#undef glPopDebugGroupKHR -#undef glPopGroupMarkerEXT -#undef glPopMatrix -#undef glPrimitiveBoundingBox -#undef glPrimitiveBoundingBoxEXT -#undef glPrimitiveBoundingBoxOES -#undef glProgramBinary -#undef glProgramBinaryOES -#undef glProgramParameteri -#undef glProgramParameteriEXT -#undef glProgramPathFragmentInputGenNV -#undef glProgramUniform1f -#undef glProgramUniform1fEXT -#undef glProgramUniform1fv -#undef glProgramUniform1fvEXT -#undef glProgramUniform1i -#undef glProgramUniform1iEXT -#undef glProgramUniform1iv -#undef glProgramUniform1ivEXT -#undef glProgramUniform1ui -#undef glProgramUniform1uiEXT -#undef glProgramUniform1uiv -#undef glProgramUniform1uivEXT -#undef glProgramUniform2f -#undef glProgramUniform2fEXT -#undef glProgramUniform2fv -#undef glProgramUniform2fvEXT -#undef glProgramUniform2i -#undef glProgramUniform2iEXT -#undef glProgramUniform2iv -#undef glProgramUniform2ivEXT -#undef glProgramUniform2ui -#undef glProgramUniform2uiEXT -#undef glProgramUniform2uiv -#undef glProgramUniform2uivEXT -#undef glProgramUniform3f -#undef glProgramUniform3fEXT -#undef glProgramUniform3fv -#undef glProgramUniform3fvEXT -#undef glProgramUniform3i -#undef glProgramUniform3iEXT -#undef glProgramUniform3iv -#undef glProgramUniform3ivEXT -#undef glProgramUniform3ui -#undef glProgramUniform3uiEXT -#undef glProgramUniform3uiv -#undef glProgramUniform3uivEXT -#undef glProgramUniform4f -#undef glProgramUniform4fEXT -#undef glProgramUniform4fv -#undef glProgramUniform4fvEXT -#undef glProgramUniform4i -#undef glProgramUniform4iEXT -#undef glProgramUniform4iv -#undef glProgramUniform4ivEXT -#undef glProgramUniform4ui -#undef glProgramUniform4uiEXT -#undef glProgramUniform4uiv -#undef glProgramUniform4uivEXT -#undef glProgramUniformHandleui64NV -#undef glProgramUniformHandleui64vNV -#undef glProgramUniformMatrix2fv -#undef glProgramUniformMatrix2fvEXT -#undef glProgramUniformMatrix2x3fv -#undef glProgramUniformMatrix2x3fvEXT -#undef glProgramUniformMatrix2x4fv -#undef glProgramUniformMatrix2x4fvEXT -#undef glProgramUniformMatrix3fv -#undef glProgramUniformMatrix3fvEXT -#undef glProgramUniformMatrix3x2fv -#undef glProgramUniformMatrix3x2fvEXT -#undef glProgramUniformMatrix3x4fv -#undef glProgramUniformMatrix3x4fvEXT -#undef glProgramUniformMatrix4fv -#undef glProgramUniformMatrix4fvEXT -#undef glProgramUniformMatrix4x2fv -#undef glProgramUniformMatrix4x2fvEXT -#undef glProgramUniformMatrix4x3fv -#undef glProgramUniformMatrix4x3fvEXT -#undef glPushDebugGroup -#undef glPushDebugGroupKHR -#undef glPushGroupMarkerEXT -#undef glPushMatrix -#undef glQueryCounterEXT -#undef glQueryMatrixxOES -#undef glRasterSamplesEXT -#undef glReadBuffer -#undef glReadBufferIndexedEXT -#undef glReadBufferNV -#undef glReadPixels -#undef glReadnPixels -#undef glReadnPixelsEXT -#undef glReadnPixelsKHR -#undef glReleaseShaderCompiler -#undef glRenderbufferStorage -#undef glRenderbufferStorageMultisample -#undef glRenderbufferStorageMultisampleANGLE -#undef glRenderbufferStorageMultisampleAPPLE -#undef glRenderbufferStorageMultisampleEXT -#undef glRenderbufferStorageMultisampleIMG -#undef glRenderbufferStorageMultisampleNV -#undef glRenderbufferStorageOES -#undef glResolveDepthValuesNV -#undef glResolveMultisampleFramebufferAPPLE -#undef glResumeTransformFeedback -#undef glRotatef -#undef glRotatex -#undef glRotatexOES -#undef glSampleCoverage -#undef glSampleCoveragex -#undef glSampleCoveragexOES -#undef glSampleMaski -#undef glSamplerParameterIiv -#undef glSamplerParameterIivEXT -#undef glSamplerParameterIivOES -#undef glSamplerParameterIuiv -#undef glSamplerParameterIuivEXT -#undef glSamplerParameterIuivOES -#undef glSamplerParameterf -#undef glSamplerParameterfv -#undef glSamplerParameteri -#undef glSamplerParameteriv -#undef glScalef -#undef glScalex -#undef glScalexOES -#undef glScissor -#undef glScissorArrayvNV -#undef glScissorIndexedNV -#undef glScissorIndexedvNV -#undef glSelectPerfMonitorCountersAMD -#undef glSetFenceNV -#undef glShadeModel -#undef glShaderBinary -#undef glShaderSource -#undef glStartTilingQCOM -#undef glStencilFillPathInstancedNV -#undef glStencilFillPathNV -#undef glStencilFunc -#undef glStencilFuncSeparate -#undef glStencilMask -#undef glStencilMaskSeparate -#undef glStencilOp -#undef glStencilOpSeparate -#undef glStencilStrokePathInstancedNV -#undef glStencilStrokePathNV -#undef glStencilThenCoverFillPathInstancedNV -#undef glStencilThenCoverFillPathNV -#undef glStencilThenCoverStrokePathInstancedNV -#undef glStencilThenCoverStrokePathNV -#undef glSubpixelPrecisionBiasNV -#undef glTestFenceNV -#undef glTexBuffer -#undef glTexBufferEXT -#undef glTexBufferOES -#undef glTexBufferRange -#undef glTexBufferRangeEXT -#undef glTexBufferRangeOES -#undef glTexCoordPointer -#undef glTexEnvf -#undef glTexEnvfv -#undef glTexEnvi -#undef glTexEnviv -#undef glTexEnvx -#undef glTexEnvxOES -#undef glTexEnvxv -#undef glTexEnvxvOES -#undef glTexGenfOES -#undef glTexGenfvOES -#undef glTexGeniOES -#undef glTexGenivOES -#undef glTexGenxOES -#undef glTexGenxvOES -#undef glTexImage2D -#undef glTexImage3D -#undef glTexImage3DOES -#undef glTexPageCommitmentEXT -#undef glTexParameterIiv -#undef glTexParameterIivEXT -#undef glTexParameterIivOES -#undef glTexParameterIuiv -#undef glTexParameterIuivEXT -#undef glTexParameterIuivOES -#undef glTexParameterf -#undef glTexParameterfv -#undef glTexParameteri -#undef glTexParameteriv -#undef glTexParameterx -#undef glTexParameterxOES -#undef glTexParameterxv -#undef glTexParameterxvOES -#undef glTexStorage1DEXT -#undef glTexStorage2D -#undef glTexStorage2DEXT -#undef glTexStorage2DMultisample -#undef glTexStorage3D -#undef glTexStorage3DEXT -#undef glTexStorage3DMultisample -#undef glTexStorage3DMultisampleOES -#undef glTexSubImage2D -#undef glTexSubImage3D -#undef glTexSubImage3DOES -#undef glTextureStorage1DEXT -#undef glTextureStorage2DEXT -#undef glTextureStorage3DEXT -#undef glTextureViewEXT -#undef glTextureViewOES -#undef glTransformFeedbackVaryings -#undef glTransformPathNV -#undef glTranslatef -#undef glTranslatex -#undef glTranslatexOES -#undef glUniform1f -#undef glUniform1fv -#undef glUniform1i -#undef glUniform1iv -#undef glUniform1ui -#undef glUniform1uiv -#undef glUniform2f -#undef glUniform2fv -#undef glUniform2i -#undef glUniform2iv -#undef glUniform2ui -#undef glUniform2uiv -#undef glUniform3f -#undef glUniform3fv -#undef glUniform3i -#undef glUniform3iv -#undef glUniform3ui -#undef glUniform3uiv -#undef glUniform4f -#undef glUniform4fv -#undef glUniform4i -#undef glUniform4iv -#undef glUniform4ui -#undef glUniform4uiv -#undef glUniformBlockBinding -#undef glUniformHandleui64NV -#undef glUniformHandleui64vNV -#undef glUniformMatrix2fv -#undef glUniformMatrix2x3fv -#undef glUniformMatrix2x3fvNV -#undef glUniformMatrix2x4fv -#undef glUniformMatrix2x4fvNV -#undef glUniformMatrix3fv -#undef glUniformMatrix3x2fv -#undef glUniformMatrix3x2fvNV -#undef glUniformMatrix3x4fv -#undef glUniformMatrix3x4fvNV -#undef glUniformMatrix4fv -#undef glUniformMatrix4x2fv -#undef glUniformMatrix4x2fvNV -#undef glUniformMatrix4x3fv -#undef glUniformMatrix4x3fvNV -#undef glUnmapBuffer -#undef glUnmapBufferOES -#undef glUseProgram -#undef glUseProgramStages -#undef glUseProgramStagesEXT -#undef glValidateProgram -#undef glValidateProgramPipeline -#undef glValidateProgramPipelineEXT -#undef glVertexAttrib1f -#undef glVertexAttrib1fv -#undef glVertexAttrib2f -#undef glVertexAttrib2fv -#undef glVertexAttrib3f -#undef glVertexAttrib3fv -#undef glVertexAttrib4f -#undef glVertexAttrib4fv -#undef glVertexAttribBinding -#undef glVertexAttribDivisor -#undef glVertexAttribDivisorANGLE -#undef glVertexAttribDivisorEXT -#undef glVertexAttribDivisorNV -#undef glVertexAttribFormat -#undef glVertexAttribI4i -#undef glVertexAttribI4iv -#undef glVertexAttribI4ui -#undef glVertexAttribI4uiv -#undef glVertexAttribIFormat -#undef glVertexAttribIPointer -#undef glVertexAttribPointer -#undef glVertexBindingDivisor -#undef glVertexPointer -#undef glViewport -#undef glViewportArrayvNV -#undef glViewportIndexedfNV -#undef glViewportIndexedfvNV -#undef glWaitSync -#undef glWaitSyncAPPLE -#undef glWeightPathsNV -#undef glWeightPointerOES diff --git a/libs/hwui/debug/nullegl.cpp b/libs/hwui/debug/nullegl.cpp deleted file mode 100644 index ca47f8fd22ef..000000000000 --- a/libs/hwui/debug/nullegl.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -#include <EGL/egl.h> -#include <EGL/eglext.h> - -#include <pthread.h> -#include <stdlib.h> -#include <string.h> - -static EGLDisplay gDisplay = (EGLDisplay)1; -static EGLSyncKHR gFence = (EGLSyncKHR)1; - -typedef struct { - EGLSurface surface; - EGLContext context; -} ThreadState; - -static pthread_key_t ThreadStateKey; -static pthread_once_t ThreadStateSetupOnce = PTHREAD_ONCE_INIT; - -static void destroyThreadState(void* state) { - free(state); -} - -static void makeThreadState() { - pthread_key_create(&ThreadStateKey, destroyThreadState); -} - -ThreadState* getThreadState() { - ThreadState* ptr; - pthread_once(&ThreadStateSetupOnce, makeThreadState); - if ((ptr = (ThreadState*)pthread_getspecific(ThreadStateKey)) == NULL) { - ptr = (ThreadState*)calloc(1, sizeof(ThreadState)); - ptr->context = EGL_NO_CONTEXT; - ptr->surface = EGL_NO_SURFACE; - pthread_setspecific(ThreadStateKey, ptr); - } - return ptr; -} - -EGLint eglGetError(void) { - return EGL_SUCCESS; -} - -EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id) { - return gDisplay; -} - -EGLBoolean eglInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor) { - return EGL_TRUE; -} - -EGLBoolean eglTerminate(EGLDisplay dpy) { - return EGL_TRUE; -} - -const char* eglQueryString(EGLDisplay dpy, EGLint name) { - if (name == EGL_EXTENSIONS) { - return "EGL_KHR_swap_buffers_with_damage"; - } - return ""; -} - -EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs, - EGLint config_size, EGLint* num_config) { - memset(configs, 9, sizeof(EGLConfig) * config_size); - *num_config = config_size; - return EGL_TRUE; -} - -EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, - const EGLint* attrib_list) { - return (EGLSurface)malloc(sizeof(void*)); -} - -EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list) { - return (EGLSurface)malloc(sizeof(void*)); -} - -EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) { - free(surface); - return EGL_TRUE; -} - -EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint* value) { - *value = 1000; - return EGL_TRUE; -} - -EGLBoolean eglReleaseThread(void) { - return EGL_TRUE; -} - -EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) { - return EGL_TRUE; -} - -EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) { - return EGL_TRUE; -} - -EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, - const EGLint* attrib_list) { - return (EGLContext)malloc(sizeof(void*)); -} -EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) { - free(ctx); - return EGL_TRUE; -} - -EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) { - ThreadState* state = getThreadState(); - state->surface = draw; - state->context = ctx; - return EGL_TRUE; -} - -EGLContext eglGetCurrentContext(void) { - return getThreadState()->context; -} - -EGLSurface eglGetCurrentSurface(EGLint readdraw) { - return getThreadState()->surface; -} - -EGLDisplay eglGetCurrentDisplay(void) { - return gDisplay; -} - -EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) { - return EGL_TRUE; -} - -EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface surface, EGLint* rects, - EGLint rectCount) { - return EGL_TRUE; -} - -EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, - EGLClientBuffer buffer, const EGLint* attrib_list) { - return (EGLImageKHR)malloc(sizeof(EGLImageKHR)); -} - -EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list) { - return gFence; -} - -EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) { - return EGL_TRUE; -} - -EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) { - return EGL_CONDITION_SATISFIED_KHR; -} - -EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR image) { - free(image); - return EGL_TRUE; -} - -void eglBeginFrame(EGLDisplay dpy, EGLSurface surface) {} diff --git a/libs/hwui/debug/wrap_gles.h b/libs/hwui/debug/wrap_gles.h deleted file mode 100644 index 3da6e802d178..000000000000 --- a/libs/hwui/debug/wrap_gles.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -// #include'ing this file is bad, bad things should be compile errors -#ifdef HWUI_GLES_WRAP_ENABLED -#error wrap_gles.h should only be used as an auto-included header, don't directly #include it -#endif -#define HWUI_GLES_WRAP_ENABLED - -#include <GLES/gl.h> -#include <GLES/glext.h> -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> -#include <GLES3/gl3.h> -#include <GLES3/gl31.h> -#include <GLES3/gl32.h> - -// constant used by the NULL GPU implementation as well as HWUI's unit tests -constexpr int NULL_GPU_MAX_TEXTURE_SIZE = 2048; - -// Generate stubs that route all the calls to our function table -#include "gles_redefine.h" - -#define GL_ENTRY(ret, api, ...) ret api(__VA_ARGS__); - -#include "gles_decls.in" -#undef GL_ENTRY diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp index 8d4e7e09b458..638de850a6c5 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.cpp +++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp @@ -15,7 +15,9 @@ */ #include "AnimatedImageDrawable.h" +#ifdef __ANDROID__ // Layoutlib does not support AnimatedImageThread #include "AnimatedImageThread.h" +#endif #include "utils/TraceUtils.h" @@ -64,7 +66,7 @@ bool AnimatedImageDrawable::nextSnapshotReady() const { // Only called on the RenderThread while UI thread is locked. bool AnimatedImageDrawable::isDirty(nsecs_t* outDelay) { *outDelay = 0; - const nsecs_t currentTime = systemTime(CLOCK_MONOTONIC); + const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); const nsecs_t lastWallTime = mLastWallTime; mLastWallTime = currentTime; @@ -160,8 +162,10 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { } else if (starting) { // The image has animated, and now is being reset. Queue up the first // frame, but keep showing the current frame until the first is ready. +#ifdef __ANDROID__ // Layoutlib does not support AnimatedImageThread auto& thread = uirenderer::AnimatedImageThread::getInstance(); mNextSnapshot = thread.reset(sk_ref_sp(this)); +#endif } bool finalFrame = false; @@ -187,8 +191,10 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { } if (mRunning && !mNextSnapshot.valid()) { +#ifdef __ANDROID__ // Layoutlib does not support AnimatedImageThread auto& thread = uirenderer::AnimatedImageThread::getInstance(); mNextSnapshot = thread.decodeNextFrame(sk_ref_sp(this)); +#endif } if (!drawDirectly) { @@ -244,7 +250,7 @@ int AnimatedImageDrawable::drawStaging(SkCanvas* canvas) { bool update = false; { - const nsecs_t currentTime = systemTime(CLOCK_MONOTONIC); + const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); std::unique_lock lock{mSwapLock}; // mLastWallTime starts off at 0. If it is still 0, just update it to // the current time and avoid updating diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 7352061bd2cd..60ef4371d38d 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -17,30 +17,33 @@ #include "HardwareBitmapUploader.h" #include "Properties.h" +#ifdef __ANDROID__ // Layoutlib does not support render thread #include "renderthread/RenderProxy.h" +#endif #include "utils/Color.h" #include <utils/Trace.h> +#ifndef _WIN32 #include <sys/mman.h> +#endif #include <cutils/ashmem.h> #include <log/log.h> +#ifndef _WIN32 #include <binder/IServiceManager.h> -#include <private/gui/ComposerService.h> +#endif #include <ui/PixelFormat.h> #include <SkCanvas.h> #include <SkImagePriv.h> - +#include <SkWebpEncoder.h> #include <SkHighContrastFilter.h> #include <limits> namespace android { -// returns true if rowBytes * height can be represented by a positive int32_t value -// and places that value in size. -static bool computeAllocationSize(size_t rowBytes, int height, size_t* size) { +bool Bitmap::computeAllocationSize(size_t rowBytes, int height, size_t* size) { return 0 <= height && height <= std::numeric_limits<size_t>::max() && !__builtin_mul_overflow(rowBytes, (size_t)height, size) && *size <= std::numeric_limits<int32_t>::max(); @@ -60,7 +63,7 @@ static sk_sp<Bitmap> allocateBitmap(SkBitmap* bitmap, AllocPixelRef alloc) { // we must respect the rowBytes value already set on the bitmap instead of // attempting to compute our own. const size_t rowBytes = bitmap->rowBytes(); - if (!computeAllocationSize(rowBytes, bitmap->height(), &size)) { + if (!Bitmap::computeAllocationSize(rowBytes, bitmap->height(), &size)) { return nullptr; } @@ -76,6 +79,7 @@ sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(SkBitmap* bitmap) { } sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) { +#ifdef __ANDROID__ // Create new ashmem region with read/write priv int fd = ashmem_create_region("bitmap", size); if (fd < 0) { @@ -94,10 +98,17 @@ sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, return nullptr; } return sk_sp<Bitmap>(new Bitmap(addr, fd, size, info, rowBytes)); +#else + return Bitmap::allocateHeapBitmap(size, info, rowBytes); +#endif } sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(const SkBitmap& bitmap) { +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap); +#else + return Bitmap::allocateHeapBitmap(bitmap.info()); +#endif } sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap) { @@ -133,16 +144,39 @@ sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, SkPixelRef& pixelRef) } -sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer, SkColorType colorType, +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration +sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, sk_sp<SkColorSpace> colorSpace, + BitmapPalette palette) { + AHardwareBuffer_Desc bufferDesc; + AHardwareBuffer_describe(hardwareBuffer, &bufferDesc); + SkImageInfo info = uirenderer::BufferDescriptionToImageInfo(bufferDesc, colorSpace); + return createFrom(hardwareBuffer, info, bufferDesc, palette); +} + +sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, SkColorType colorType, sk_sp<SkColorSpace> colorSpace, SkAlphaType alphaType, BitmapPalette palette) { - SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(), + AHardwareBuffer_Desc bufferDesc; + AHardwareBuffer_describe(hardwareBuffer, &bufferDesc); + SkImageInfo info = SkImageInfo::Make(bufferDesc.width, bufferDesc.height, colorType, alphaType, colorSpace); - return sk_sp<Bitmap>(new Bitmap(graphicBuffer.get(), info, palette)); + return createFrom(hardwareBuffer, info, bufferDesc, palette); } +sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, const SkImageInfo& info, + const AHardwareBuffer_Desc& bufferDesc, BitmapPalette palette) { + // If the stride is 0 we have to use the width as an approximation (eg, compressed buffer) + const auto bufferStride = bufferDesc.stride > 0 ? bufferDesc.stride : bufferDesc.width; + const size_t rowBytes = info.bytesPerPixel() * bufferStride; + return sk_sp<Bitmap>(new Bitmap(hardwareBuffer, info, rowBytes, palette)); +} +#endif + sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, size_t rowBytes, int fd, void* addr, size_t size, bool readOnly) { +#ifdef _WIN32 // ashmem not implemented on Windows + return nullptr; +#else if (info.colorType() == kUnknown_SkColorType) { LOG_ALWAYS_FATAL("unknown bitmap configuration"); return nullptr; @@ -163,6 +197,7 @@ sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, size_t rowBytes, int f bitmap->setImmutable(); } return bitmap; +#endif } void Bitmap::setColorSpace(sk_sp<SkColorSpace> colorSpace) { @@ -214,19 +249,20 @@ Bitmap::Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info mPixelStorage.ashmem.size = mappedSize; } -Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette) - : SkPixelRef(info.width(), info.height(), nullptr, - bytesPerPixel(buffer->getPixelFormat()) * (buffer->getStride() > 0 ? buffer->getStride() : buffer->getWidth())) +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration +Bitmap::Bitmap(AHardwareBuffer* buffer, const SkImageInfo& info, size_t rowBytes, + BitmapPalette palette) + : SkPixelRef(info.width(), info.height(), nullptr, rowBytes) , mInfo(validateAlpha(info)) , mPixelStorageType(PixelStorageType::Hardware) , mPalette(palette) , mPaletteGenerationId(getGenerationID()) { mPixelStorage.hardware.buffer = buffer; - buffer->incStrong(buffer); + AHardwareBuffer_acquire(buffer); setImmutable(); // HW bitmaps are always immutable - mImage = SkImage::MakeFromAHardwareBuffer(reinterpret_cast<AHardwareBuffer*>(buffer), - mInfo.alphaType(), mInfo.refColorSpace()); + mImage = SkImage::MakeFromAHardwareBuffer(buffer, mInfo.alphaType(), mInfo.refColorSpace()); } +#endif Bitmap::~Bitmap() { switch (mPixelStorageType) { @@ -235,17 +271,23 @@ Bitmap::~Bitmap() { mPixelStorage.external.context); break; case PixelStorageType::Ashmem: +#ifndef _WIN32 // ashmem not implemented on Windows munmap(mPixelStorage.ashmem.address, mPixelStorage.ashmem.size); +#endif close(mPixelStorage.ashmem.fd); break; case PixelStorageType::Heap: free(mPixelStorage.heap.address); +#ifdef __ANDROID__ mallopt(M_PURGE, 0); +#endif break; case PixelStorageType::Hardware: +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration auto buffer = mPixelStorage.hardware.buffer; - buffer->decStrong(buffer); + AHardwareBuffer_release(buffer); mPixelStorage.hardware.buffer = nullptr; +#endif break; } } @@ -304,26 +346,30 @@ void Bitmap::setAlphaType(SkAlphaType alphaType) { } void Bitmap::getSkBitmap(SkBitmap* outBitmap) { +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration if (isHardware()) { outBitmap->allocPixels(mInfo); uirenderer::renderthread::RenderProxy::copyHWBitmapInto(this, outBitmap); return; } +#endif outBitmap->setInfo(mInfo, rowBytes()); outBitmap->setPixelRef(sk_ref_sp(this), 0, 0); } void Bitmap::getBounds(SkRect* bounds) const { SkASSERT(bounds); - bounds->set(0, 0, SkIntToScalar(width()), SkIntToScalar(height())); + bounds->setIWH(width(), height()); } -GraphicBuffer* Bitmap::graphicBuffer() { +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration +AHardwareBuffer* Bitmap::hardwareBuffer() { if (isHardware()) { return mPixelStorage.hardware.buffer; } return nullptr; } +#endif sk_sp<SkImage> Bitmap::makeImage() { sk_sp<SkImage> image = mImage; @@ -423,4 +469,43 @@ BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr, return BitmapPalette::Unknown; } +bool Bitmap::compress(JavaCompressFormat format, int32_t quality, SkWStream* stream) { + SkBitmap skbitmap; + getSkBitmap(&skbitmap); + return compress(skbitmap, format, quality, stream); +} + +bool Bitmap::compress(const SkBitmap& bitmap, JavaCompressFormat format, + int32_t quality, SkWStream* stream) { + if (bitmap.colorType() == kAlpha_8_SkColorType) { + // None of the JavaCompressFormats have a sensible way to compress an + // ALPHA_8 Bitmap. SkPngEncoder will compress one, but it uses a non- + // standard format that most decoders do not understand, so this is + // likely not useful. + return false; + } + + SkEncodedImageFormat fm; + switch (format) { + case JavaCompressFormat::Jpeg: + fm = SkEncodedImageFormat::kJPEG; + break; + case JavaCompressFormat::Png: + fm = SkEncodedImageFormat::kPNG; + break; + case JavaCompressFormat::Webp: + fm = SkEncodedImageFormat::kWEBP; + break; + case JavaCompressFormat::WebpLossy: + case JavaCompressFormat::WebpLossless: { + SkWebpEncoder::Options options; + options.fQuality = quality; + options.fCompression = format == JavaCompressFormat::WebpLossy ? + SkWebpEncoder::Compression::kLossy : SkWebpEncoder::Compression::kLossless; + return SkWebpEncoder::Encode(stream, bitmap.pixmap(), options); + } + } + + return SkEncodeImage(stream, bitmap, fm, quality); +} } // namespace android diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index dd98b25ac7e8..b8b59947a57b 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -23,7 +23,11 @@ #include <SkImageInfo.h> #include <SkPixelRef.h> #include <cutils/compiler.h> -#include <ui/GraphicBuffer.h> +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration +#include <android/hardware_buffer.h> +#endif + +class SkWStream; namespace android { @@ -71,11 +75,17 @@ public: /* The createFrom factories construct a new Bitmap object by wrapping the already allocated * memory that is provided as an input param. */ - static sk_sp<Bitmap> createFrom(sp<GraphicBuffer> graphicBuffer, - SkColorType colorType, +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration + static sk_sp<Bitmap> createFrom(AHardwareBuffer* hardwareBuffer, sk_sp<SkColorSpace> colorSpace, - SkAlphaType alphaType = kPremul_SkAlphaType, BitmapPalette palette = BitmapPalette::Unknown); + + static sk_sp<Bitmap> createFrom(AHardwareBuffer* hardwareBuffer, + SkColorType colorType, + sk_sp<SkColorSpace> colorSpace, + SkAlphaType alphaType, + BitmapPalette palette); +#endif static sk_sp<Bitmap> createFrom(const SkImageInfo& info, size_t rowBytes, int fd, void* addr, size_t size, bool readOnly); static sk_sp<Bitmap> createFrom(const SkImageInfo&, SkPixelRef&); @@ -105,7 +115,9 @@ public: PixelStorageType pixelStorageType() const { return mPixelStorageType; } - GraphicBuffer* graphicBuffer(); +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration + AHardwareBuffer* hardwareBuffer(); +#endif /** * Creates or returns a cached SkImage and is safe to be invoked from either @@ -128,6 +140,24 @@ public: return mPalette; } + // returns true if rowBytes * height can be represented by a positive int32_t value + // and places that value in size. + static bool computeAllocationSize(size_t rowBytes, int height, size_t* size); + + // These must match the int values of CompressFormat in Bitmap.java, as well as + // AndroidBitmapCompressFormat. + enum class JavaCompressFormat { + Jpeg = 0, + Png = 1, + Webp = 2, + WebpLossy = 3, + WebpLossless = 4, + }; + + bool compress(JavaCompressFormat format, int32_t quality, SkWStream* stream); + + static bool compress(const SkBitmap& bitmap, JavaCompressFormat format, + int32_t quality, SkWStream* stream); private: static sk_sp<Bitmap> allocateAshmemBitmap(size_t size, const SkImageInfo& i, size_t rowBytes); static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& i, size_t rowBytes); @@ -136,7 +166,16 @@ private: Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info, size_t rowBytes); Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes); - Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette); +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration + Bitmap(AHardwareBuffer* buffer, const SkImageInfo& info, size_t rowBytes, + BitmapPalette palette); + + // Common code for the two public facing createFrom(AHardwareBuffer*, ...) + // methods. + // bufferDesc is only used to compute rowBytes. + static sk_sp<Bitmap> createFrom(AHardwareBuffer* hardwareBuffer, const SkImageInfo& info, + const AHardwareBuffer_Desc& bufferDesc, BitmapPalette palette); +#endif virtual ~Bitmap(); void* getStorage() const; @@ -165,9 +204,11 @@ private: void* address; size_t size; } heap; +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration struct { - GraphicBuffer* buffer; + AHardwareBuffer* buffer; } hardware; +#endif } mPixelStorage; sk_sp<SkImage> mImage; // Cache is used only for HW Bitmaps with Skia pipeline. diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index 68aa7374fec4..c138a32eacc2 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -34,7 +34,7 @@ Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::Rende } static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness, - const SkPaint& paint, Canvas* canvas) { + const Paint& paint, Canvas* canvas) { const SkScalar strokeWidth = fmax(thickness, 1.0f); const SkScalar bottom = top + strokeWidth; canvas->drawRect(left, top, right, bottom, paint); @@ -95,18 +95,10 @@ public: void operator()(size_t start, size_t end) { auto glyphFunc = [&](uint16_t* text, float* positions) { - if (canvas->drawTextAbsolutePos()) { - for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) { - text[textIndex++] = layout.getGlyphId(i); - positions[posIndex++] = x + layout.getX(i); - positions[posIndex++] = y + layout.getY(i); - } - } else { - for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) { - text[textIndex++] = layout.getGlyphId(i); - positions[posIndex++] = layout.getX(i); - positions[posIndex++] = layout.getY(i); - } + for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) { + text[textIndex++] = layout.getGlyphId(i); + positions[posIndex++] = x + layout.getX(i); + positions[posIndex++] = y + layout.getY(i); } }; @@ -154,6 +146,11 @@ void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, // minikin may modify the original paint Paint paint(origPaint); + // interpret 'linear metrics' flag as 'linear', forcing no-hinting when drawing + if (paint.getSkFont().isLinearMetrics()) { + paint.getSkFont().setHinting(SkFontHinting::kNone); + } + minikin::Layout layout = MinikinUtils::doLayout(&paint, bidiFlags, typeface, text, textSize, start, count, contextStart, contextCount, mt); @@ -161,9 +158,6 @@ void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, minikin::MinikinRect bounds; layout.getBounds(&bounds); - if (!drawTextAbsolutePos()) { - bounds.offset(x, y); - } // Set align to left for drawing, as we don't want individual // glyphs centered or right-aligned; the offset above takes @@ -177,7 +171,7 @@ void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, void Canvas::drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight, float outerBottom, float outerRx, float outerRy, float innerLeft, float innerTop, float innerRight, float innerBottom, float innerRx, - float innerRy, const SkPaint& paint) { + float innerRy, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect outer = SkRect::MakeLTRB(outerLeft, outerTop, outerRight, outerBottom); SkRect inner = SkRect::MakeLTRB(innerLeft, innerTop, innerRight, innerBottom); @@ -193,7 +187,7 @@ void Canvas::drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerR void Canvas::drawDoubleRoundRectRadii(float outerLeft, float outerTop, float outerRight, float outerBottom, const float* outerRadii, float innerLeft, float innerTop, float innerRight, float innerBottom, - const float* innerRadii, const SkPaint& paint) { + const float* innerRadii, const Paint& paint) { static_assert(sizeof(SkVector) == sizeof(float) * 2); if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect outer = SkRect::MakeLTRB(outerLeft, outerTop, outerRight, outerBottom); @@ -234,23 +228,30 @@ private: }; void Canvas::drawTextOnPath(const uint16_t* text, int count, minikin::Bidi bidiFlags, - const SkPath& path, float hOffset, float vOffset, const Paint& paint, - const Typeface* typeface) { - Paint paintCopy(paint); + const SkPath& path, float hOffset, float vOffset, + const Paint& origPaint, const Typeface* typeface) { + // minikin may modify the original paint + Paint paint(origPaint); + + // interpret 'linear metrics' flag as 'linear', forcing no-hinting when drawing + if (paint.getSkFont().isLinearMetrics()) { + paint.getSkFont().setHinting(SkFontHinting::kNone); + } + minikin::Layout layout = - MinikinUtils::doLayout(&paintCopy, bidiFlags, typeface, text, count, // text buffer - 0, count, // draw range - 0, count, // context range + MinikinUtils::doLayout(&paint, bidiFlags, typeface, text, count, // text buffer + 0, count, // draw range + 0, count, // context range nullptr); - hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path); + hOffset += MinikinUtils::hOffsetForTextAlign(&paint, layout, path); // Set align to left for drawing, as we don't want individual // glyphs centered or right-aligned; the offset above takes // care of all alignment. - paintCopy.setTextAlign(Paint::kLeft_Align); + paint.setTextAlign(Paint::kLeft_Align); - DrawTextOnPathFunctor f(layout, this, hOffset, vOffset, paintCopy, path); - MinikinUtils::forFontRun(layout, &paintCopy, f); + DrawTextOnPathFunctor f(layout, this, hOffset, vOffset, paint, path); + MinikinUtils::forFontRun(layout, &paint, f); } int Canvas::sApiLevel = 1; diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index ac8db216b059..27dfed305a94 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -131,20 +131,6 @@ public: */ static void setCompatibilityVersion(int apiLevel); - /** - * Provides a Skia SkCanvas interface that acts as a proxy to this Canvas. - * It is useful for testing and clients (e.g. Picture/Movie) that expect to - * draw their contents into an SkCanvas. - * - * The SkCanvas returned is *only* valid until another Canvas call is made - * that would change state (e.g. matrix or clip). Clients of asSkCanvas() - * are responsible for *not* persisting this pointer. - * - * Further, the returned SkCanvas should NOT be unref'd and is valid until - * this canvas is destroyed or a new bitmap is set. - */ - virtual SkCanvas* asSkCanvas() = 0; - virtual void setBitmap(const SkBitmap& bitmap) = 0; virtual bool isOpaque() = 0; @@ -231,48 +217,40 @@ public: virtual void drawPaint(const SkPaint& paint) = 0; // Geometry - virtual void drawPoint(float x, float y, const SkPaint& paint) = 0; - virtual void drawPoints(const float* points, int floatCount, const SkPaint& paint) = 0; + virtual void drawPoint(float x, float y, const Paint& paint) = 0; + virtual void drawPoints(const float* points, int floatCount, const Paint& paint) = 0; virtual void drawLine(float startX, float startY, float stopX, float stopY, - const SkPaint& paint) = 0; - virtual void drawLines(const float* points, int floatCount, const SkPaint& paint) = 0; + const Paint& paint) = 0; + virtual void drawLines(const float* points, int floatCount, const Paint& paint) = 0; virtual void drawRect(float left, float top, float right, float bottom, - const SkPaint& paint) = 0; - virtual void drawRegion(const SkRegion& region, const SkPaint& paint) = 0; + const Paint& paint) = 0; + virtual void drawRegion(const SkRegion& region, const Paint& paint) = 0; virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, - const SkPaint& paint) = 0; + const Paint& paint) = 0; virtual void drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner, - const SkPaint& paint) = 0; - virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) = 0; + const Paint& paint) = 0; + virtual void drawCircle(float x, float y, float radius, const Paint& paint) = 0; virtual void drawOval(float left, float top, float right, float bottom, - const SkPaint& paint) = 0; + const Paint& paint) = 0; virtual void drawArc(float left, float top, float right, float bottom, float startAngle, - float sweepAngle, bool useCenter, const SkPaint& paint) = 0; - virtual void drawPath(const SkPath& path, const SkPaint& paint) = 0; - virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint& paint) = 0; + float sweepAngle, bool useCenter, const Paint& paint) = 0; + virtual void drawPath(const SkPath& path, const Paint& paint) = 0; + virtual void drawVertices(const SkVertices*, SkBlendMode, const Paint& paint) = 0; // Bitmap-based - virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) = 0; - virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) = 0; + virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) = 0; + virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) = 0; virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, - float dstBottom, const SkPaint* paint) = 0; + float dstBottom, const Paint* paint) = 0; virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint) = 0; + const float* vertices, const int* colors, const Paint* paint) = 0; virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) = 0; + const Paint* paint) = 0; virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) = 0; - - /** - * Specifies if the positions passed to ::drawText are absolute or relative - * to the (x,y) value provided. - * - * If true the (x,y) values are ignored. Otherwise, those (x,y) values need - * to be added to each glyph's position to get its absolute position. - */ - virtual bool drawTextAbsolutePos() const = 0; + virtual void drawPicture(const SkPicture& picture) = 0; /** * Draws a VectorDrawable onto the canvas. @@ -294,12 +272,12 @@ public: void drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight, float outerBottom, float outerRx, float outerRy, float innerLeft, float innerTop, float innerRight, float innerBottom, float innerRx, - float innerRy, const SkPaint& paint); + float innerRy, const Paint& paint); void drawDoubleRoundRectRadii(float outerLeft, float outerTop, float outerRight, float outerBottom, const float* outerRadii, float innerLeft, float innerTop, float innerRight, float innerBottom, - const float* innerRadii, const SkPaint& paint); + const float* innerRadii, const Paint& paint); static int GetApiLevel() { return sApiLevel; } diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp new file mode 100644 index 000000000000..43cc4f244f71 --- /dev/null +++ b/libs/hwui/hwui/ImageDecoder.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2019 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. + */ + +#include "ImageDecoder.h" + +#include <hwui/Bitmap.h> + +#include <SkAndroidCodec.h> +#include <SkCanvas.h> +#include <SkPaint.h> + +using namespace android; + +sk_sp<SkColorSpace> ImageDecoder::getDefaultColorSpace() const { + const skcms_ICCProfile* encodedProfile = mCodec->getICCProfile(); + if (encodedProfile) { + // If the profile maps directly to an SkColorSpace, that SkColorSpace + // will be returned. Otherwise, nullptr will be returned. In either + // case, using this SkColorSpace results in doing no color correction. + return SkColorSpace::Make(*encodedProfile); + } + + // The image has no embedded color profile, and should be treated as SRGB. + return SkColorSpace::MakeSRGB(); +} + +ImageDecoder::ImageDecoder(std::unique_ptr<SkAndroidCodec> codec, sk_sp<SkPngChunkReader> peeker) + : mCodec(std::move(codec)) + , mPeeker(std::move(peeker)) + , mTargetSize(mCodec->getInfo().dimensions()) + , mDecodeSize(mTargetSize) + , mOutColorType(mCodec->computeOutputColorType(kN32_SkColorType)) + , mUnpremultipliedRequired(false) + , mOutColorSpace(getDefaultColorSpace()) + , mSampleSize(1) +{ +} + +SkAlphaType ImageDecoder::getOutAlphaType() const { + return opaque() ? kOpaque_SkAlphaType + : mUnpremultipliedRequired ? kUnpremul_SkAlphaType : kPremul_SkAlphaType; +} + +bool ImageDecoder::setTargetSize(int width, int height) { + if (width <= 0 || height <= 0) { + return false; + } + + auto info = SkImageInfo::Make(width, height, mOutColorType, getOutAlphaType()); + size_t rowBytes = info.minRowBytes(); + if (rowBytes == 0) { + // This would have overflowed. + return false; + } + + size_t pixelMemorySize; + if (!Bitmap::computeAllocationSize(rowBytes, height, &pixelMemorySize)) { + return false; + } + + if (mCropRect) { + if (mCropRect->right() > width || mCropRect->bottom() > height) { + return false; + } + } + + SkISize targetSize = { width, height }, decodeSize = targetSize; + int sampleSize = mCodec->computeSampleSize(&decodeSize); + + if (decodeSize != targetSize && mUnpremultipliedRequired && !opaque()) { + return false; + } + + mTargetSize = targetSize; + mDecodeSize = decodeSize; + mSampleSize = sampleSize; + return true; +} + +bool ImageDecoder::setCropRect(const SkIRect* crop) { + if (!crop) { + mCropRect.reset(); + return true; + } + + if (crop->left() >= crop->right() || crop->top() >= crop->bottom()) { + return false; + } + + const auto& size = mTargetSize; + if (crop->left() < 0 || crop->top() < 0 + || crop->right() > size.width() || crop->bottom() > size.height()) { + return false; + } + + mCropRect.emplace(*crop); + return true; +} + +bool ImageDecoder::setOutColorType(SkColorType colorType) { + switch (colorType) { + case kRGB_565_SkColorType: + if (!opaque()) { + return false; + } + break; + case kGray_8_SkColorType: + if (!gray()) { + return false; + } + break; + case kN32_SkColorType: + break; + case kRGBA_F16_SkColorType: + break; + default: + return false; + } + + mOutColorType = colorType; + return true; +} + +bool ImageDecoder::setUnpremultipliedRequired(bool required) { + if (required && !opaque() && mDecodeSize != mTargetSize) { + return false; + } + mUnpremultipliedRequired = required; + return true; +} + +void ImageDecoder::setOutColorSpace(sk_sp<SkColorSpace> colorSpace) { + mOutColorSpace = std::move(colorSpace); +} + +sk_sp<SkColorSpace> ImageDecoder::getOutputColorSpace() const { + // kGray_8 is used for ALPHA_8, which ignores the color space. + return mOutColorType == kGray_8_SkColorType ? nullptr : mOutColorSpace; +} + + +SkImageInfo ImageDecoder::getOutputInfo() const { + SkISize size = mCropRect ? mCropRect->size() : mTargetSize; + return SkImageInfo::Make(size, mOutColorType, getOutAlphaType(), getOutputColorSpace()); +} + +bool ImageDecoder::opaque() const { + return mCodec->getInfo().alphaType() == kOpaque_SkAlphaType; +} + +bool ImageDecoder::gray() const { + return mCodec->getInfo().colorType() == kGray_8_SkColorType; +} + +SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) { + void* decodePixels = pixels; + size_t decodeRowBytes = rowBytes; + auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, getOutAlphaType(), + getOutputColorSpace()); + // Used if we need a temporary before scaling or subsetting. + // FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380. + SkBitmap tmp; + const bool scale = mDecodeSize != mTargetSize; + if (scale || mCropRect) { + if (!tmp.setInfo(decodeInfo)) { + return SkCodec::kInternalError; + } + if (!Bitmap::allocateHeapBitmap(&tmp)) { + return SkCodec::kInternalError; + } + decodePixels = tmp.getPixels(); + decodeRowBytes = tmp.rowBytes(); + } + + SkAndroidCodec::AndroidOptions options; + options.fSampleSize = mSampleSize; + auto result = mCodec->getAndroidPixels(decodeInfo, decodePixels, decodeRowBytes, &options); + + if (scale || mCropRect) { + SkBitmap scaledBm; + if (!scaledBm.installPixels(getOutputInfo(), pixels, rowBytes)) { + return SkCodec::kInternalError; + } + + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); + paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering + + SkCanvas canvas(scaledBm, SkCanvas::ColorBehavior::kLegacy); + if (mCropRect) { + canvas.translate(-mCropRect->fLeft, -mCropRect->fTop); + } + if (scale) { + float scaleX = (float) mTargetSize.width() / mDecodeSize.width(); + float scaleY = (float) mTargetSize.height() / mDecodeSize.height(); + canvas.scale(scaleX, scaleY); + } + + canvas.drawBitmap(tmp, 0.0f, 0.0f, &paint); + } + + return result; +} + diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h new file mode 100644 index 000000000000..a1b51573db3f --- /dev/null +++ b/libs/hwui/hwui/ImageDecoder.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019 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. + */ +#pragma once + +#include <SkCodec.h> +#include <SkImageInfo.h> +#include <SkPngChunkReader.h> +#include <SkRect.h> +#include <SkSize.h> +#include <cutils/compiler.h> + +#include <optional> + +class SkAndroidCodec; + +namespace android { + +class ANDROID_API ImageDecoder { +public: + std::unique_ptr<SkAndroidCodec> mCodec; + sk_sp<SkPngChunkReader> mPeeker; + + ImageDecoder(std::unique_ptr<SkAndroidCodec> codec, + sk_sp<SkPngChunkReader> peeker = nullptr); + + bool setTargetSize(int width, int height); + bool setCropRect(const SkIRect*); + + bool setOutColorType(SkColorType outColorType); + + bool setUnpremultipliedRequired(bool unpremultipliedRequired); + + sk_sp<SkColorSpace> getDefaultColorSpace() const; + void setOutColorSpace(sk_sp<SkColorSpace> cs); + + // The size is the final size after scaling and cropping. + SkImageInfo getOutputInfo() const; + + bool opaque() const; + bool gray() const; + + SkCodec::Result decode(void* pixels, size_t rowBytes); + +private: + SkISize mTargetSize; + SkISize mDecodeSize; + SkColorType mOutColorType; + bool mUnpremultipliedRequired; + sk_sp<SkColorSpace> mOutColorSpace; + int mSampleSize; + std::optional<SkIRect> mCropRect; + + ImageDecoder(const ImageDecoder&) = delete; + ImageDecoder& operator=(const ImageDecoder&) = delete; + + SkAlphaType getOutAlphaType() const; + sk_sp<SkColorSpace> getOutputColorSpace() const; +}; + +} // namespace android diff --git a/libs/hwui/hwui/MinikinSkia.h b/libs/hwui/hwui/MinikinSkia.h index 90f7d48a47ee..298967689cd9 100644 --- a/libs/hwui/hwui/MinikinSkia.h +++ b/libs/hwui/hwui/MinikinSkia.h @@ -20,6 +20,8 @@ #include <SkRefCnt.h> #include <cutils/compiler.h> #include <minikin/MinikinFont.h> +#include <string> +#include <string_view> class SkFont; class SkTypeface; diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h index 9b2fa9df1e8f..281ecd27d780 100644 --- a/libs/hwui/hwui/Paint.h +++ b/libs/hwui/hwui/Paint.h @@ -21,6 +21,7 @@ #include <cutils/compiler.h> +#include <SkDrawLooper.h> #include <SkFont.h> #include <SkPaint.h> #include <string> @@ -58,12 +59,17 @@ public: SkFont& getSkFont() { return mFont; } const SkFont& getSkFont() const { return mFont; } + SkDrawLooper* getLooper() const { return mLooper.get(); } + void setLooper(sk_sp<SkDrawLooper> looper) { mLooper = std::move(looper); } + // These shadow the methods on SkPaint, but we need to so we can keep related // attributes in-sync. void reset(); void setAntiAlias(bool); + bool nothingToDraw() const { return !mLooper && SkPaint::nothingToDraw(); } + // End method shadowing void setLetterSpacing(float letterSpacing) { mLetterSpacing = letterSpacing; } @@ -146,6 +152,7 @@ public: private: SkFont mFont; + sk_sp<SkDrawLooper> mLooper; float mLetterSpacing = 0; float mWordSpacing = 0; diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp index 2f2d575bca29..fa2674fc2f5e 100644 --- a/libs/hwui/hwui/PaintImpl.cpp +++ b/libs/hwui/hwui/PaintImpl.cpp @@ -34,6 +34,7 @@ Paint::Paint() Paint::Paint(const Paint& paint) : SkPaint(paint) , mFont(paint.mFont) + , mLooper(paint.mLooper) , mLetterSpacing(paint.mLetterSpacing) , mWordSpacing(paint.mWordSpacing) , mFontFeatureSettings(paint.mFontFeatureSettings) @@ -52,6 +53,7 @@ Paint::~Paint() {} Paint& Paint::operator=(const Paint& other) { SkPaint::operator=(other); mFont = other.mFont; + mLooper = other.mLooper; mLetterSpacing = other.mLetterSpacing; mWordSpacing = other.mWordSpacing; mFontFeatureSettings = other.mFontFeatureSettings; @@ -69,6 +71,7 @@ Paint& Paint::operator=(const Paint& other) { bool operator==(const Paint& a, const Paint& b) { return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b) && a.mFont == b.mFont && + a.mLooper == b.mLooper && a.mLetterSpacing == b.mLetterSpacing && a.mWordSpacing == b.mWordSpacing && a.mFontFeatureSettings == b.mFontFeatureSettings && a.mMinikinLocaleListId == b.mMinikinLocaleListId && @@ -83,6 +86,7 @@ void Paint::reset() { mFont = SkFont(); mFont.setEdging(SkFont::Edging::kAlias); + mLooper.reset(); mStrikeThru = false; mUnderline = false; diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp index c4d8aa6c8fad..ccc328c702db 100644 --- a/libs/hwui/hwui/Typeface.cpp +++ b/libs/hwui/hwui/Typeface.cpp @@ -18,7 +18,9 @@ #include <fcntl.h> // For tests. #include <pthread.h> +#ifndef _WIN32 #include <sys/mman.h> // For tests. +#endif #include <sys/stat.h> // For tests. #include "MinikinSkia.h" @@ -171,6 +173,7 @@ void Typeface::setDefault(const Typeface* face) { } void Typeface::setRobotoTypefaceForTest() { +#ifndef _WIN32 const char* kRobotoFont = "/system/fonts/Roboto-Regular.ttf"; int fd = open(kRobotoFont, O_RDONLY); @@ -198,5 +201,6 @@ void Typeface::setRobotoTypefaceForTest() { hwTypeface->fStyle = minikin::FontStyle(); Typeface::setDefault(hwTypeface); +#endif } } // namespace android diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp new file mode 100644 index 000000000000..1ff156593c41 --- /dev/null +++ b/libs/hwui/jni/AnimatedImageDrawable.cpp @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2018 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. + */ + +#include "GraphicsJNI.h" +#include "ImageDecoder.h" +#include "Utils.h" + +#include <SkAndroidCodec.h> +#include <SkAnimatedImage.h> +#include <SkColorFilter.h> +#include <SkPicture.h> +#include <SkPictureRecorder.h> +#include <hwui/AnimatedImageDrawable.h> +#include <hwui/ImageDecoder.h> +#include <hwui/Canvas.h> +#include <utils/Looper.h> + +using namespace android; + +static jmethodID gAnimatedImageDrawable_onAnimationEndMethodID; + +// Note: jpostProcess holds a handle to the ImageDecoder. +static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/, + jlong nativeImageDecoder, jobject jpostProcess, + jint width, jint height, jlong colorSpaceHandle, + jboolean extended, jobject jsubset) { + if (nativeImageDecoder == 0) { + doThrowIOE(env, "Cannot create AnimatedImageDrawable from null!"); + return 0; + } + + auto* imageDecoder = reinterpret_cast<ImageDecoder*>(nativeImageDecoder); + SkIRect subset; + if (jsubset) { + GraphicsJNI::jrect_to_irect(env, jsubset, &subset); + } else { + subset = SkIRect::MakeWH(width, height); + } + + bool hasRestoreFrame = false; + if (imageDecoder->mCodec->getEncodedFormat() != SkEncodedImageFormat::kWEBP) { + const int frameCount = imageDecoder->mCodec->codec()->getFrameCount(); + for (int i = 0; i < frameCount; ++i) { + SkCodec::FrameInfo frameInfo; + if (!imageDecoder->mCodec->codec()->getFrameInfo(i, &frameInfo)) { + doThrowIOE(env, "Failed to read frame info!"); + return 0; + } + if (frameInfo.fDisposalMethod == SkCodecAnimation::DisposalMethod::kRestorePrevious) { + hasRestoreFrame = true; + break; + } + } + } + + auto info = imageDecoder->mCodec->getInfo().makeWH(width, height) + .makeColorSpace(GraphicsJNI::getNativeColorSpace(colorSpaceHandle)); + if (extended) { + info = info.makeColorType(kRGBA_F16_SkColorType); + } + + size_t bytesUsed = info.computeMinByteSize(); + // SkAnimatedImage has one SkBitmap for decoding, plus an extra one if there is a + // kRestorePrevious frame. AnimatedImageDrawable has two SkPictures storing the current + // frame and the next frame. (The former assumes that the image is animated, and the + // latter assumes that it is drawn to a hardware canvas.) + bytesUsed *= hasRestoreFrame ? 4 : 3; + sk_sp<SkPicture> picture; + if (jpostProcess) { + SkRect bounds = SkRect::MakeWH(subset.width(), subset.height()); + + SkPictureRecorder recorder; + SkCanvas* skcanvas = recorder.beginRecording(bounds); + std::unique_ptr<Canvas> canvas(Canvas::create_canvas(skcanvas)); + postProcessAndRelease(env, jpostProcess, std::move(canvas)); + if (env->ExceptionCheck()) { + return 0; + } + picture = recorder.finishRecordingAsPicture(); + bytesUsed += picture->approximateBytesUsed(); + } + + + sk_sp<SkAnimatedImage> animatedImg = SkAnimatedImage::Make(std::move(imageDecoder->mCodec), + info, subset, + std::move(picture)); + if (!animatedImg) { + doThrowIOE(env, "Failed to create drawable"); + return 0; + } + + bytesUsed += sizeof(animatedImg.get()); + + sk_sp<AnimatedImageDrawable> drawable(new AnimatedImageDrawable(std::move(animatedImg), + bytesUsed)); + return reinterpret_cast<jlong>(drawable.release()); +} + +static void AnimatedImageDrawable_destruct(AnimatedImageDrawable* drawable) { + SkSafeUnref(drawable); +} + +static jlong AnimatedImageDrawable_nGetNativeFinalizer(JNIEnv* /*env*/, jobject /*clazz*/) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&AnimatedImageDrawable_destruct)); +} + +// Java's FINISHED relies on this being -1 +static_assert(SkAnimatedImage::kFinished == -1); + +static jlong AnimatedImageDrawable_nDraw(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jlong canvasPtr) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + auto* canvas = reinterpret_cast<Canvas*>(canvasPtr); + return (jlong) canvas->drawAnimatedImage(drawable); +} + +static void AnimatedImageDrawable_nSetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jint alpha) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + drawable->setStagingAlpha(alpha); +} + +static jlong AnimatedImageDrawable_nGetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + return drawable->getStagingAlpha(); +} + +static void AnimatedImageDrawable_nSetColorFilter(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jlong nativeFilter) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + auto* filter = reinterpret_cast<SkColorFilter*>(nativeFilter); + drawable->setStagingColorFilter(sk_ref_sp(filter)); +} + +static jboolean AnimatedImageDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + return drawable->isRunning(); +} + +static jboolean AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + return drawable->start(); +} + +static jboolean AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + return drawable->stop(); +} + +// Java's LOOP_INFINITE relies on this being the same. +static_assert(SkCodec::kRepetitionCountInfinite == -1); + +static jint AnimatedImageDrawable_nGetRepeatCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + return drawable->getRepetitionCount(); +} + +static void AnimatedImageDrawable_nSetRepeatCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jint loopCount) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + drawable->setRepetitionCount(loopCount); +} + +class InvokeListener : public MessageHandler { +public: + InvokeListener(JNIEnv* env, jobject javaObject) { + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJvm) != JNI_OK); + // Hold a weak reference to break a cycle that would prevent GC. + mWeakRef = env->NewWeakGlobalRef(javaObject); + } + + ~InvokeListener() override { + auto* env = requireEnv(mJvm); + env->DeleteWeakGlobalRef(mWeakRef); + } + + virtual void handleMessage(const Message&) override { + auto* env = get_env_or_die(mJvm); + jobject localRef = env->NewLocalRef(mWeakRef); + if (localRef) { + env->CallVoidMethod(localRef, gAnimatedImageDrawable_onAnimationEndMethodID); + } + } + +private: + JavaVM* mJvm; + jweak mWeakRef; +}; + +class JniAnimationEndListener : public OnAnimationEndListener { +public: + JniAnimationEndListener(sp<Looper>&& looper, JNIEnv* env, jobject javaObject) { + mListener = new InvokeListener(env, javaObject); + mLooper = std::move(looper); + } + + void onAnimationEnd() override { mLooper->sendMessage(mListener, 0); } + +private: + sp<InvokeListener> mListener; + sp<Looper> mLooper; +}; + +static void AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv* env, jobject /*clazz*/, + jlong nativePtr, jobject jdrawable) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + if (!jdrawable) { + drawable->setOnAnimationEndListener(nullptr); + } else { + sp<Looper> looper = Looper::getForThread(); + if (!looper.get()) { + doThrowISE(env, + "Must set AnimatedImageDrawable's AnimationCallback on a thread with a " + "looper!"); + return; + } + + drawable->setOnAnimationEndListener( + std::make_unique<JniAnimationEndListener>(std::move(looper), env, jdrawable)); + } +} + +static jlong AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + return drawable->byteSize(); +} + +static void AnimatedImageDrawable_nSetMirrored(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jboolean mirrored) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + drawable->setStagingMirrored(mirrored); +} + +static const JNINativeMethod gAnimatedImageDrawableMethods[] = { + { "nCreate", "(JLandroid/graphics/ImageDecoder;IIJZLandroid/graphics/Rect;)J",(void*) AnimatedImageDrawable_nCreate }, + { "nGetNativeFinalizer", "()J", (void*) AnimatedImageDrawable_nGetNativeFinalizer }, + { "nDraw", "(JJ)J", (void*) AnimatedImageDrawable_nDraw }, + { "nSetAlpha", "(JI)V", (void*) AnimatedImageDrawable_nSetAlpha }, + { "nGetAlpha", "(J)I", (void*) AnimatedImageDrawable_nGetAlpha }, + { "nSetColorFilter", "(JJ)V", (void*) AnimatedImageDrawable_nSetColorFilter }, + { "nIsRunning", "(J)Z", (void*) AnimatedImageDrawable_nIsRunning }, + { "nStart", "(J)Z", (void*) AnimatedImageDrawable_nStart }, + { "nStop", "(J)Z", (void*) AnimatedImageDrawable_nStop }, + { "nGetRepeatCount", "(J)I", (void*) AnimatedImageDrawable_nGetRepeatCount }, + { "nSetRepeatCount", "(JI)V", (void*) AnimatedImageDrawable_nSetRepeatCount }, + { "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener }, + { "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize }, + { "nSetMirrored", "(JZ)V", (void*) AnimatedImageDrawable_nSetMirrored }, +}; + +int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) { + jclass animatedImageDrawable_class = FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable"); + gAnimatedImageDrawable_onAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "onAnimationEnd", "()V"); + + return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable", + gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods)); +} + diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp new file mode 100755 index 000000000000..c0663a9bc699 --- /dev/null +++ b/libs/hwui/jni/Bitmap.cpp @@ -0,0 +1,1189 @@ +#undef LOG_TAG +#define LOG_TAG "Bitmap" +#include "Bitmap.h" + +#include "SkBitmap.h" +#include "SkPixelRef.h" +#include "SkImageEncoder.h" +#include "SkImageInfo.h" +#include "SkColor.h" +#include "SkColorSpace.h" +#include "GraphicsJNI.h" +#include "SkStream.h" +#include "SkWebpEncoder.h" + +#include "android_nio_utils.h" +#include "CreateJavaOutputStreamAdaptor.h" +#include <hwui/Paint.h> +#include <hwui/Bitmap.h> +#include <utils/Color.h> + +#ifdef __ANDROID__ // Layoutlib does not support graphic buffer, parcel or render thread +#include <private/android/AHardwareBufferHelpers.h> +#include <binder/Parcel.h> +#include <dlfcn.h> +#include <renderthread/RenderProxy.h> +#endif + +#include <string.h> +#include <memory> +#include <string> + +#define DEBUG_PARCEL 0 + +static jclass gBitmap_class; +static jfieldID gBitmap_nativePtr; +static jmethodID gBitmap_constructorMethodID; +static jmethodID gBitmap_reinitMethodID; + +namespace android { + +class BitmapWrapper { +public: + explicit BitmapWrapper(Bitmap* bitmap) + : mBitmap(bitmap) { } + + void freePixels() { + mInfo = mBitmap->info(); + mHasHardwareMipMap = mBitmap->hasHardwareMipMap(); + mAllocationSize = mBitmap->getAllocationByteCount(); + mRowBytes = mBitmap->rowBytes(); + mGenerationId = mBitmap->getGenerationID(); + mIsHardware = mBitmap->isHardware(); + mBitmap.reset(); + } + + bool valid() { + return mBitmap != nullptr; + } + + Bitmap& bitmap() { + assertValid(); + return *mBitmap; + } + + void assertValid() { + LOG_ALWAYS_FATAL_IF(!valid(), "Error, cannot access an invalid/free'd bitmap here!"); + } + + void getSkBitmap(SkBitmap* outBitmap) { + assertValid(); + mBitmap->getSkBitmap(outBitmap); + } + + bool hasHardwareMipMap() { + if (mBitmap) { + return mBitmap->hasHardwareMipMap(); + } + return mHasHardwareMipMap; + } + + void setHasHardwareMipMap(bool hasMipMap) { + assertValid(); + mBitmap->setHasHardwareMipMap(hasMipMap); + } + + void setAlphaType(SkAlphaType alphaType) { + assertValid(); + mBitmap->setAlphaType(alphaType); + } + + void setColorSpace(sk_sp<SkColorSpace> colorSpace) { + assertValid(); + mBitmap->setColorSpace(colorSpace); + } + + const SkImageInfo& info() { + if (mBitmap) { + return mBitmap->info(); + } + return mInfo; + } + + size_t getAllocationByteCount() const { + if (mBitmap) { + return mBitmap->getAllocationByteCount(); + } + return mAllocationSize; + } + + size_t rowBytes() const { + if (mBitmap) { + return mBitmap->rowBytes(); + } + return mRowBytes; + } + + uint32_t getGenerationID() const { + if (mBitmap) { + return mBitmap->getGenerationID(); + } + return mGenerationId; + } + + bool isHardware() { + if (mBitmap) { + return mBitmap->isHardware(); + } + return mIsHardware; + } + + ~BitmapWrapper() { } + +private: + sk_sp<Bitmap> mBitmap; + SkImageInfo mInfo; + bool mHasHardwareMipMap; + size_t mAllocationSize; + size_t mRowBytes; + uint32_t mGenerationId; + bool mIsHardware; +}; + +// Convenience class that does not take a global ref on the pixels, relying +// on the caller already having a local JNI ref +class LocalScopedBitmap { +public: + explicit LocalScopedBitmap(jlong bitmapHandle) + : mBitmapWrapper(reinterpret_cast<BitmapWrapper*>(bitmapHandle)) {} + + BitmapWrapper* operator->() { + return mBitmapWrapper; + } + + void* pixels() { + return mBitmapWrapper->bitmap().pixels(); + } + + bool valid() { + return mBitmapWrapper && mBitmapWrapper->valid(); + } + +private: + BitmapWrapper* mBitmapWrapper; +}; + +namespace bitmap { + +// Assert that bitmap's SkAlphaType is consistent with isPremultiplied. +static void assert_premultiplied(const SkImageInfo& info, bool isPremultiplied) { + // kOpaque_SkAlphaType and kIgnore_SkAlphaType mean that isPremultiplied is + // irrelevant. This just tests to ensure that the SkAlphaType is not + // opposite of isPremultiplied. + if (isPremultiplied) { + SkASSERT(info.alphaType() != kUnpremul_SkAlphaType); + } else { + SkASSERT(info.alphaType() != kPremul_SkAlphaType); + } +} + +void reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info, + bool isPremultiplied) +{ + // The caller needs to have already set the alpha type properly, so the + // native SkBitmap stays in sync with the Java Bitmap. + assert_premultiplied(info, isPremultiplied); + + env->CallVoidMethod(javaBitmap, gBitmap_reinitMethodID, + info.width(), info.height(), isPremultiplied); +} + +jobject createBitmap(JNIEnv* env, Bitmap* bitmap, + int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets, + int density) { + bool isMutable = bitmapCreateFlags & kBitmapCreateFlag_Mutable; + bool isPremultiplied = bitmapCreateFlags & kBitmapCreateFlag_Premultiplied; + // The caller needs to have already set the alpha type properly, so the + // native SkBitmap stays in sync with the Java Bitmap. + assert_premultiplied(bitmap->info(), isPremultiplied); + bool fromMalloc = bitmap->pixelStorageType() == PixelStorageType::Heap; + BitmapWrapper* bitmapWrapper = new BitmapWrapper(bitmap); + if (!isMutable) { + bitmapWrapper->bitmap().setImmutable(); + } + jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID, + reinterpret_cast<jlong>(bitmapWrapper), bitmap->width(), bitmap->height(), density, + isPremultiplied, ninePatchChunk, ninePatchInsets, fromMalloc); + + if (env->ExceptionCheck() != 0) { + ALOGE("*** Uncaught exception returned from Java call!\n"); + env->ExceptionDescribe(); + } + return obj; +} + +void toSkBitmap(jlong bitmapHandle, SkBitmap* outBitmap) { + LocalScopedBitmap bitmap(bitmapHandle); + bitmap->getSkBitmap(outBitmap); +} + +Bitmap& toBitmap(jlong bitmapHandle) { + LocalScopedBitmap localBitmap(bitmapHandle); + return localBitmap->bitmap(); +} + +} // namespace bitmap + +} // namespace android + +using namespace android; +using namespace android::bitmap; + +Bitmap* GraphicsJNI::getNativeBitmap(JNIEnv* env, jobject bitmap) { + SkASSERT(env); + SkASSERT(bitmap); + SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class)); + jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr); + LocalScopedBitmap localBitmap(bitmapHandle); + return localBitmap.valid() ? &localBitmap->bitmap() : nullptr; +} + +SkImageInfo GraphicsJNI::getBitmapInfo(JNIEnv* env, jobject bitmap, uint32_t* outRowBytes, + bool* isHardware) { + SkASSERT(env); + SkASSERT(bitmap); + SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class)); + jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr); + LocalScopedBitmap localBitmap(bitmapHandle); + if (outRowBytes) { + *outRowBytes = localBitmap->rowBytes(); + } + if (isHardware) { + *isHardware = localBitmap->isHardware(); + } + return localBitmap->info(); +} + +bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int srcStride, + int x, int y, int width, int height, SkBitmap* dstBitmap) { + const jint* array = env->GetIntArrayElements(srcColors, NULL); + const SkColor* src = (const SkColor*)array + srcOffset; + + auto sRGB = SkColorSpace::MakeSRGB(); + SkImageInfo srcInfo = SkImageInfo::Make( + width, height, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, sRGB); + SkPixmap srcPM(srcInfo, src, srcStride * 4); + + dstBitmap->writePixels(srcPM, x, y); + + env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array), JNI_ABORT); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int getPremulBitmapCreateFlags(bool isMutable) { + int flags = android::bitmap::kBitmapCreateFlag_Premultiplied; + if (isMutable) flags |= android::bitmap::kBitmapCreateFlag_Mutable; + return flags; +} + +static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, + jint offset, jint stride, jint width, jint height, + jint configHandle, jboolean isMutable, + jlong colorSpacePtr) { + SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle); + if (NULL != jColors) { + size_t n = env->GetArrayLength(jColors); + if (n < SkAbs32(stride) * (size_t)height) { + doThrowAIOOBE(env); + return NULL; + } + } + + // ARGB_4444 is a deprecated format, convert automatically to 8888 + if (colorType == kARGB_4444_SkColorType) { + colorType = kN32_SkColorType; + } + + sk_sp<SkColorSpace> colorSpace; + if (colorType == kAlpha_8_SkColorType) { + colorSpace = nullptr; + } else { + colorSpace = GraphicsJNI::getNativeColorSpace(colorSpacePtr); + } + + SkBitmap bitmap; + bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType, + colorSpace)); + + sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(&bitmap); + if (!nativeBitmap) { + ALOGE("OOM allocating Bitmap with dimensions %i x %i", width, height); + doThrowOOME(env); + return NULL; + } + + if (jColors != NULL) { + GraphicsJNI::SetPixels(env, jColors, offset, stride, 0, 0, width, height, &bitmap); + } + + return createBitmap(env, nativeBitmap.release(), getPremulBitmapCreateFlags(isMutable)); +} + +static bool bitmapCopyTo(SkBitmap* dst, SkColorType dstCT, const SkBitmap& src, + SkBitmap::Allocator* alloc) { + SkPixmap srcPM; + if (!src.peekPixels(&srcPM)) { + return false; + } + + SkImageInfo dstInfo = srcPM.info().makeColorType(dstCT); + switch (dstCT) { + case kRGB_565_SkColorType: + dstInfo = dstInfo.makeAlphaType(kOpaque_SkAlphaType); + break; + case kAlpha_8_SkColorType: + dstInfo = dstInfo.makeColorSpace(nullptr); + break; + default: + break; + } + + if (!dstInfo.colorSpace() && dstCT != kAlpha_8_SkColorType) { + dstInfo = dstInfo.makeColorSpace(SkColorSpace::MakeSRGB()); + } + + if (!dst->setInfo(dstInfo)) { + return false; + } + if (!dst->tryAllocPixels(alloc)) { + return false; + } + + SkPixmap dstPM; + if (!dst->peekPixels(&dstPM)) { + return false; + } + + return srcPM.readPixels(dstPM); +} + +static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle, + jint dstConfigHandle, jboolean isMutable) { + SkBitmap src; + reinterpret_cast<BitmapWrapper*>(srcHandle)->getSkBitmap(&src); + if (dstConfigHandle == GraphicsJNI::hardwareLegacyBitmapConfig()) { + sk_sp<Bitmap> bitmap(Bitmap::allocateHardwareBitmap(src)); + if (!bitmap.get()) { + return NULL; + } + return createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(isMutable)); + } + + SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle); + SkBitmap result; + HeapAllocator allocator; + + if (!bitmapCopyTo(&result, dstCT, src, &allocator)) { + return NULL; + } + auto bitmap = allocator.getStorageObjAndReset(); + return createBitmap(env, bitmap, getPremulBitmapCreateFlags(isMutable)); +} + +static Bitmap* Bitmap_copyAshmemImpl(JNIEnv* env, SkBitmap& src, SkColorType& dstCT) { + SkBitmap result; + + AshmemPixelAllocator allocator(env); + if (!bitmapCopyTo(&result, dstCT, src, &allocator)) { + return NULL; + } + auto bitmap = allocator.getStorageObjAndReset(); + bitmap->setImmutable(); + return bitmap; +} + +static jobject Bitmap_copyAshmem(JNIEnv* env, jobject, jlong srcHandle) { + SkBitmap src; + reinterpret_cast<BitmapWrapper*>(srcHandle)->getSkBitmap(&src); + SkColorType dstCT = src.colorType(); + auto bitmap = Bitmap_copyAshmemImpl(env, src, dstCT); + jobject ret = createBitmap(env, bitmap, getPremulBitmapCreateFlags(false)); + return ret; +} + +static jobject Bitmap_copyAshmemConfig(JNIEnv* env, jobject, jlong srcHandle, jint dstConfigHandle) { + SkBitmap src; + reinterpret_cast<BitmapWrapper*>(srcHandle)->getSkBitmap(&src); + SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle); + auto bitmap = Bitmap_copyAshmemImpl(env, src, dstCT); + jobject ret = createBitmap(env, bitmap, getPremulBitmapCreateFlags(false)); + return ret; +} + +static void Bitmap_destruct(BitmapWrapper* bitmap) { + delete bitmap; +} + +static jlong Bitmap_getNativeFinalizer(JNIEnv*, jobject) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Bitmap_destruct)); +} + +static void Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmap(bitmapHandle); + bitmap->freePixels(); +} + +static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle, + jint width, jint height, jint configHandle, jboolean requestPremul) { + LocalScopedBitmap bitmap(bitmapHandle); + bitmap->assertValid(); + SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle); + + // ARGB_4444 is a deprecated format, convert automatically to 8888 + if (colorType == kARGB_4444_SkColorType) { + colorType = kN32_SkColorType; + } + size_t requestedSize = width * height * SkColorTypeBytesPerPixel(colorType); + if (requestedSize > bitmap->getAllocationByteCount()) { + // done in native as there's no way to get BytesPerPixel in Java + doThrowIAE(env, "Bitmap not large enough to support new configuration"); + return; + } + SkAlphaType alphaType; + if (bitmap->info().colorType() != kRGB_565_SkColorType + && bitmap->info().alphaType() == kOpaque_SkAlphaType) { + // If the original bitmap was set to opaque, keep that setting, unless it + // was 565, which is required to be opaque. + alphaType = kOpaque_SkAlphaType; + } else { + // Otherwise respect the premultiplied request. + alphaType = requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType; + } + bitmap->bitmap().reconfigure(SkImageInfo::Make(width, height, colorType, alphaType, + sk_ref_sp(bitmap->info().colorSpace()))); +} + +static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle, + jint format, jint quality, + jobject jstream, jbyteArray jstorage) { + LocalScopedBitmap bitmap(bitmapHandle); + if (!bitmap.valid()) { + return JNI_FALSE; + } + + std::unique_ptr<SkWStream> strm(CreateJavaOutputStreamAdaptor(env, jstream, jstorage)); + if (!strm.get()) { + return JNI_FALSE; + } + + auto fm = static_cast<Bitmap::JavaCompressFormat>(format); + return bitmap->bitmap().compress(fm, quality, strm.get()) ? JNI_TRUE : JNI_FALSE; +} + +static inline void bitmapErase(SkBitmap bitmap, const SkColor4f& color, + const sk_sp<SkColorSpace>& colorSpace) { + SkPaint p; + p.setColor4f(color, colorSpace.get()); + p.setBlendMode(SkBlendMode::kSrc); + SkCanvas canvas(bitmap); + canvas.drawPaint(p); +} + +static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) { + LocalScopedBitmap bitmap(bitmapHandle); + SkBitmap skBitmap; + bitmap->getSkBitmap(&skBitmap); + bitmapErase(skBitmap, SkColor4f::FromColor(color), SkColorSpace::MakeSRGB()); +} + +static void Bitmap_eraseLong(JNIEnv* env, jobject, jlong bitmapHandle, + jlong colorSpaceHandle, jlong colorLong) { + LocalScopedBitmap bitmap(bitmapHandle); + SkBitmap skBitmap; + bitmap->getSkBitmap(&skBitmap); + + SkColor4f color = GraphicsJNI::convertColorLong(colorLong); + sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); + bitmapErase(skBitmap, color, cs); +} + +static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmap(bitmapHandle); + return static_cast<jint>(bitmap->rowBytes()); +} + +static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmap(bitmapHandle); + if (bitmap->isHardware()) { + return GraphicsJNI::hardwareLegacyBitmapConfig(); + } + return GraphicsJNI::colorTypeToLegacyBitmapConfig(bitmap->info().colorType()); +} + +static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmap(bitmapHandle); + return static_cast<jint>(bitmap->getGenerationID()); +} + +static jboolean Bitmap_isPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmap(bitmapHandle); + if (bitmap->info().alphaType() == kPremul_SkAlphaType) { + return JNI_TRUE; + } + return JNI_FALSE; +} + +static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmap(bitmapHandle); + return !bitmap->info().isOpaque() ? JNI_TRUE : JNI_FALSE; +} + +static void Bitmap_setHasAlpha(JNIEnv* env, jobject, jlong bitmapHandle, + jboolean hasAlpha, jboolean requestPremul) { + LocalScopedBitmap bitmap(bitmapHandle); + if (hasAlpha) { + bitmap->setAlphaType( + requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType); + } else { + bitmap->setAlphaType(kOpaque_SkAlphaType); + } +} + +static void Bitmap_setPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle, + jboolean isPremul) { + LocalScopedBitmap bitmap(bitmapHandle); + if (!bitmap->info().isOpaque()) { + if (isPremul) { + bitmap->setAlphaType(kPremul_SkAlphaType); + } else { + bitmap->setAlphaType(kUnpremul_SkAlphaType); + } + } +} + +static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmap(bitmapHandle); + return bitmap->hasHardwareMipMap() ? JNI_TRUE : JNI_FALSE; +} + +static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle, + jboolean hasMipMap) { + LocalScopedBitmap bitmap(bitmapHandle); + bitmap->setHasHardwareMipMap(hasMipMap); +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef __ANDROID__ // Layoutlib does not support parcel +static struct parcel_offsets_t +{ + jclass clazz; + jfieldID mNativePtr; +} gParcelOffsets; + +static Parcel* parcelForJavaObject(JNIEnv* env, jobject obj) { + if (obj) { + Parcel* p = (Parcel*)env->GetLongField(obj, gParcelOffsets.mNativePtr); + if (p != NULL) { + return p; + } + jniThrowException(env, "java/lang/IllegalStateException", "Parcel has been finalized!"); + } + return NULL; +} +#endif + +// This is the maximum possible size because the SkColorSpace must be +// representable (and therefore serializable) using a matrix and numerical +// transfer function. If we allow more color space representations in the +// framework, we may need to update this maximum size. +static constexpr uint32_t kMaxColorSpaceSerializedBytes = 80; + +static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { +#ifdef __ANDROID__ // Layoutlib does not support parcel + if (parcel == NULL) { + SkDebugf("-------- unparcel parcel is NULL\n"); + return NULL; + } + + android::Parcel* p = parcelForJavaObject(env, parcel); + + const bool isMutable = p->readInt32() != 0; + const SkColorType colorType = (SkColorType)p->readInt32(); + const SkAlphaType alphaType = (SkAlphaType)p->readInt32(); + const uint32_t colorSpaceSize = p->readUint32(); + sk_sp<SkColorSpace> colorSpace; + if (colorSpaceSize > 0) { + if (colorSpaceSize > kMaxColorSpaceSerializedBytes) { + ALOGD("Bitmap_createFromParcel: Serialized SkColorSpace is larger than expected: " + "%d bytes\n", colorSpaceSize); + } + + const void* data = p->readInplace(colorSpaceSize); + if (data) { + colorSpace = SkColorSpace::Deserialize(data, colorSpaceSize); + } else { + ALOGD("Bitmap_createFromParcel: Unable to read serialized SkColorSpace data\n"); + } + } + const int width = p->readInt32(); + const int height = p->readInt32(); + const int rowBytes = p->readInt32(); + const int density = p->readInt32(); + + if (kN32_SkColorType != colorType && + kRGBA_F16_SkColorType != colorType && + kRGB_565_SkColorType != colorType && + kARGB_4444_SkColorType != colorType && + kAlpha_8_SkColorType != colorType) { + SkDebugf("Bitmap_createFromParcel unknown colortype: %d\n", colorType); + return NULL; + } + + std::unique_ptr<SkBitmap> bitmap(new SkBitmap); + if (!bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType, colorSpace), + rowBytes)) { + return NULL; + } + + // Read the bitmap blob. + size_t size = bitmap->computeByteSize(); + android::Parcel::ReadableBlob blob; + android::status_t status = p->readBlob(size, &blob); + if (status) { + doThrowRE(env, "Could not read bitmap blob."); + return NULL; + } + + // Map the bitmap in place from the ashmem region if possible otherwise copy. + sk_sp<Bitmap> nativeBitmap; + // If the blob is mutable we have ownership of the region and can always use it + // If the blob is immutable _and_ we're immutable, we can then still use it + if (blob.fd() >= 0 && (blob.isMutable() || !isMutable)) { +#if DEBUG_PARCEL + ALOGD("Bitmap.createFromParcel: mapped contents of bitmap from %s blob " + "(fds %s)", + blob.isMutable() ? "mutable" : "immutable", + p->allowFds() ? "allowed" : "forbidden"); +#endif + // Dup the file descriptor so we can keep a reference to it after the Parcel + // is disposed. + int dupFd = fcntl(blob.fd(), F_DUPFD_CLOEXEC, 0); + if (dupFd < 0) { + ALOGE("Error allocating dup fd. Error:%d", errno); + blob.release(); + doThrowRE(env, "Could not allocate dup blob fd."); + return NULL; + } + + // Map the pixels in place and take ownership of the ashmem region. We must also respect the + // rowBytes value already set on the bitmap instead of attempting to compute our own. + nativeBitmap = Bitmap::createFrom(bitmap->info(), bitmap->rowBytes(), dupFd, + const_cast<void*>(blob.data()), size, !isMutable); + if (!nativeBitmap) { + close(dupFd); + blob.release(); + doThrowRE(env, "Could not allocate ashmem pixel ref."); + return NULL; + } + + // Clear the blob handle, don't release it. + blob.clear(); + } else { +#if DEBUG_PARCEL + if (blob.fd() >= 0) { + ALOGD("Bitmap.createFromParcel: copied contents of mutable bitmap " + "from immutable blob (fds %s)", + p->allowFds() ? "allowed" : "forbidden"); + } else { + ALOGD("Bitmap.createFromParcel: copied contents from %s blob " + "(fds %s)", + blob.isMutable() ? "mutable" : "immutable", + p->allowFds() ? "allowed" : "forbidden"); + } +#endif + + // Copy the pixels into a new buffer. + nativeBitmap = Bitmap::allocateHeapBitmap(bitmap.get()); + if (!nativeBitmap) { + blob.release(); + doThrowRE(env, "Could not allocate java pixel ref."); + return NULL; + } + memcpy(bitmap->getPixels(), blob.data(), size); + + // Release the blob handle. + blob.release(); + } + + return createBitmap(env, nativeBitmap.release(), + getPremulBitmapCreateFlags(isMutable), NULL, NULL, density); +#else + doThrowRE(env, "Cannot use parcels outside of Android"); + return NULL; +#endif +} + +static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, + jlong bitmapHandle, jint density, jobject parcel) { +#ifdef __ANDROID__ // Layoutlib does not support parcel + if (parcel == NULL) { + SkDebugf("------- writeToParcel null parcel\n"); + return JNI_FALSE; + } + + android::Parcel* p = parcelForJavaObject(env, parcel); + SkBitmap bitmap; + + auto bitmapWrapper = reinterpret_cast<BitmapWrapper*>(bitmapHandle); + bitmapWrapper->getSkBitmap(&bitmap); + + p->writeInt32(!bitmap.isImmutable()); + p->writeInt32(bitmap.colorType()); + p->writeInt32(bitmap.alphaType()); + SkColorSpace* colorSpace = bitmap.colorSpace(); + if (colorSpace != nullptr) { + sk_sp<SkData> data = colorSpace->serialize(); + size_t size = data->size(); + p->writeUint32(size); + if (size > 0) { + if (size > kMaxColorSpaceSerializedBytes) { + ALOGD("Bitmap_writeToParcel: Serialized SkColorSpace is larger than expected: " + "%zu bytes\n", size); + } + + p->write(data->data(), size); + } + } else { + p->writeUint32(0); + } + p->writeInt32(bitmap.width()); + p->writeInt32(bitmap.height()); + p->writeInt32(bitmap.rowBytes()); + p->writeInt32(density); + + // Transfer the underlying ashmem region if we have one and it's immutable. + android::status_t status; + int fd = bitmapWrapper->bitmap().getAshmemFd(); + if (fd >= 0 && bitmap.isImmutable() && p->allowFds()) { +#if DEBUG_PARCEL + ALOGD("Bitmap.writeToParcel: transferring immutable bitmap's ashmem fd as " + "immutable blob (fds %s)", + p->allowFds() ? "allowed" : "forbidden"); +#endif + + status = p->writeDupImmutableBlobFileDescriptor(fd); + if (status) { + doThrowRE(env, "Could not write bitmap blob file descriptor."); + return JNI_FALSE; + } + return JNI_TRUE; + } + + // Copy the bitmap to a new blob. +#if DEBUG_PARCEL + ALOGD("Bitmap.writeToParcel: copying bitmap into new blob (fds %s)", + p->allowFds() ? "allowed" : "forbidden"); +#endif + + const bool mutableCopy = !bitmap.isImmutable(); + size_t size = bitmap.computeByteSize(); + android::Parcel::WritableBlob blob; + status = p->writeBlob(size, mutableCopy, &blob); + if (status) { + doThrowRE(env, "Could not copy bitmap to parcel blob."); + return JNI_FALSE; + } + + const void* pSrc = bitmap.getPixels(); + if (pSrc == NULL) { + memset(blob.data(), 0, size); + } else { + memcpy(blob.data(), pSrc, size); + } + + blob.release(); + return JNI_TRUE; +#else + doThrowRE(env, "Cannot use parcels outside of Android"); + return JNI_FALSE; +#endif +} + +static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, + jlong srcHandle, jlong paintHandle, + jintArray offsetXY) { + SkBitmap src; + reinterpret_cast<BitmapWrapper*>(srcHandle)->getSkBitmap(&src); + const android::Paint* paint = reinterpret_cast<android::Paint*>(paintHandle); + SkIPoint offset; + SkBitmap dst; + HeapAllocator allocator; + + src.extractAlpha(&dst, paint, &allocator, &offset); + // If Skia can't allocate pixels for destination bitmap, it resets + // it, that is set its pixels buffer to NULL, and zero width and height. + if (dst.getPixels() == NULL && src.getPixels() != NULL) { + doThrowOOME(env, "failed to allocate pixels for alpha"); + return NULL; + } + if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) { + int* array = env->GetIntArrayElements(offsetXY, NULL); + array[0] = offset.fX; + array[1] = offset.fY; + env->ReleaseIntArrayElements(offsetXY, array, 0); + } + + return createBitmap(env, allocator.getStorageObjAndReset(), + getPremulBitmapCreateFlags(true)); +} + +/////////////////////////////////////////////////////////////////////////////// + +static jboolean Bitmap_isSRGB(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmapHolder(bitmapHandle); + if (!bitmapHolder.valid()) return JNI_TRUE; + + SkColorSpace* colorSpace = bitmapHolder->info().colorSpace(); + return colorSpace == nullptr || colorSpace->isSRGB(); +} + +static jboolean Bitmap_isSRGBLinear(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmapHolder(bitmapHandle); + if (!bitmapHolder.valid()) return JNI_FALSE; + + SkColorSpace* colorSpace = bitmapHolder->info().colorSpace(); + sk_sp<SkColorSpace> srgbLinear = SkColorSpace::MakeSRGBLinear(); + return colorSpace == srgbLinear.get() ? JNI_TRUE : JNI_FALSE; +} + +static jobject Bitmap_computeColorSpace(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmapHolder(bitmapHandle); + if (!bitmapHolder.valid()) return nullptr; + + SkColorSpace* colorSpace = bitmapHolder->info().colorSpace(); + if (colorSpace == nullptr) return nullptr; + + return GraphicsJNI::getColorSpace(env, colorSpace, bitmapHolder->info().colorType()); +} + +static void Bitmap_setColorSpace(JNIEnv* env, jobject, jlong bitmapHandle, jlong colorSpacePtr) { + LocalScopedBitmap bitmapHolder(bitmapHandle); + sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpacePtr); + bitmapHolder->setColorSpace(cs); +} + +/////////////////////////////////////////////////////////////////////////////// + +static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle, + jint x, jint y) { + SkBitmap bitmap; + reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap); + + auto sRGB = SkColorSpace::MakeSRGB(); + SkImageInfo dstInfo = SkImageInfo::Make( + 1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, sRGB); + + SkColor dst; + bitmap.readPixels(dstInfo, &dst, dstInfo.minRowBytes(), x, y); + return static_cast<jint>(dst); +} + +static jlong Bitmap_getColor(JNIEnv* env, jobject, jlong bitmapHandle, + jint x, jint y) { + SkBitmap bitmap; + reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap); + + SkImageInfo dstInfo = SkImageInfo::Make( + 1, 1, kRGBA_F16_SkColorType, kUnpremul_SkAlphaType, bitmap.refColorSpace()); + + uint64_t dst; + bitmap.readPixels(dstInfo, &dst, dstInfo.minRowBytes(), x, y); + return static_cast<jlong>(dst); +} + +static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle, + jintArray pixelArray, jint offset, jint stride, + jint x, jint y, jint width, jint height) { + SkBitmap bitmap; + reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap); + + auto sRGB = SkColorSpace::MakeSRGB(); + SkImageInfo dstInfo = SkImageInfo::Make( + width, height, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, sRGB); + + jint* dst = env->GetIntArrayElements(pixelArray, NULL); + bitmap.readPixels(dstInfo, dst + offset, stride * 4, x, y); + env->ReleaseIntArrayElements(pixelArray, dst, 0); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle, + jint x, jint y, jint colorHandle) { + SkBitmap bitmap; + reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap); + SkColor color = static_cast<SkColor>(colorHandle); + + auto sRGB = SkColorSpace::MakeSRGB(); + SkImageInfo srcInfo = SkImageInfo::Make( + 1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, sRGB); + SkPixmap srcPM(srcInfo, &color, srcInfo.minRowBytes()); + + bitmap.writePixels(srcPM, x, y); +} + +static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle, + jintArray pixelArray, jint offset, jint stride, + jint x, jint y, jint width, jint height) { + SkBitmap bitmap; + reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap); + GraphicsJNI::SetPixels(env, pixelArray, offset, stride, + x, y, width, height, &bitmap); +} + +static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject, + jlong bitmapHandle, jobject jbuffer) { + SkBitmap bitmap; + reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap); + const void* src = bitmap.getPixels(); + + if (NULL != src) { + android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE); + + // the java side has already checked that buffer is large enough + memcpy(abp.pointer(), src, bitmap.computeByteSize()); + } +} + +static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject, + jlong bitmapHandle, jobject jbuffer) { + SkBitmap bitmap; + reinterpret_cast<BitmapWrapper*>(bitmapHandle)->getSkBitmap(&bitmap); + void* dst = bitmap.getPixels(); + + if (NULL != dst) { + android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE); + // the java side has already checked that buffer is large enough + memcpy(dst, abp.pointer(), bitmap.computeByteSize()); + bitmap.notifyPixelsChanged(); + } +} + +static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, jlong bm1Handle) { + SkBitmap bm0; + SkBitmap bm1; + + LocalScopedBitmap bitmap0(bm0Handle); + LocalScopedBitmap bitmap1(bm1Handle); + + // Paying the price for making Hardware Bitmap as Config: + // later check for colorType will pass successfully, + // because Hardware Config internally may be RGBA8888 or smth like that. + if (bitmap0->isHardware() != bitmap1->isHardware()) { + return JNI_FALSE; + } + + bitmap0->bitmap().getSkBitmap(&bm0); + bitmap1->bitmap().getSkBitmap(&bm1); + if (bm0.width() != bm1.width() + || bm0.height() != bm1.height() + || bm0.colorType() != bm1.colorType() + || bm0.alphaType() != bm1.alphaType() + || !SkColorSpace::Equals(bm0.colorSpace(), bm1.colorSpace())) { + return JNI_FALSE; + } + + // if we can't load the pixels, return false + if (NULL == bm0.getPixels() || NULL == bm1.getPixels()) { + return JNI_FALSE; + } + + // now compare each scanline. We can't do the entire buffer at once, + // since we don't care about the pixel values that might extend beyond + // the width (since the scanline might be larger than the logical width) + const int h = bm0.height(); + const size_t size = bm0.width() * bm0.bytesPerPixel(); + for (int y = 0; y < h; y++) { + // SkBitmap::getAddr(int, int) may return NULL due to unrecognized config + // (ex: kRLE_Index8_Config). This will cause memcmp method to crash. Since bm0 + // and bm1 both have pixel data() (have passed NULL == getPixels() check), + // those 2 bitmaps should be valid (only unrecognized), we return JNI_FALSE + // to warn user those 2 unrecognized config bitmaps may be different. + void *bm0Addr = bm0.getAddr(0, y); + void *bm1Addr = bm1.getAddr(0, y); + + if(bm0Addr == NULL || bm1Addr == NULL) { + return JNI_FALSE; + } + + if (memcmp(bm0Addr, bm1Addr, size) != 0) { + return JNI_FALSE; + } + } + return JNI_TRUE; +} + +static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapPtr) { +#ifdef __ANDROID__ // Layoutlib does not support render thread + LocalScopedBitmap bitmapHandle(bitmapPtr); + if (!bitmapHandle.valid()) return; + android::uirenderer::renderthread::RenderProxy::prepareToDraw(bitmapHandle->bitmap()); +#endif +} + +static jint Bitmap_getAllocationByteCount(JNIEnv* env, jobject, jlong bitmapPtr) { + LocalScopedBitmap bitmapHandle(bitmapPtr); + return static_cast<jint>(bitmapHandle->getAllocationByteCount()); +} + +static jobject Bitmap_copyPreserveInternalConfig(JNIEnv* env, jobject, jlong bitmapPtr) { + LocalScopedBitmap bitmapHandle(bitmapPtr); + LOG_ALWAYS_FATAL_IF(!bitmapHandle->isHardware(), + "Hardware config is only supported config in Bitmap_nativeCopyPreserveInternalConfig"); + Bitmap& hwuiBitmap = bitmapHandle->bitmap(); + SkBitmap src; + hwuiBitmap.getSkBitmap(&src); + + if (src.pixelRef() == nullptr) { + doThrowRE(env, "Could not copy a hardware bitmap."); + return NULL; + } + + sk_sp<Bitmap> bitmap = Bitmap::createFrom(src.info(), *src.pixelRef()); + return createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false)); +} + +#ifdef __ANDROID__ // Layoutlib does not support graphic buffer +typedef AHardwareBuffer* (*AHB_from_HB)(JNIEnv*, jobject); +AHB_from_HB AHardwareBuffer_fromHardwareBuffer; + +typedef jobject (*AHB_to_HB)(JNIEnv*, AHardwareBuffer*); +AHB_to_HB AHardwareBuffer_toHardwareBuffer; +#endif + +static jobject Bitmap_wrapHardwareBufferBitmap(JNIEnv* env, jobject, jobject hardwareBuffer, + jlong colorSpacePtr) { +#ifdef __ANDROID__ // Layoutlib does not support graphic buffer + AHardwareBuffer* buffer = AHardwareBuffer_fromHardwareBuffer(env, hardwareBuffer); + sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, + GraphicsJNI::getNativeColorSpace(colorSpacePtr)); + if (!bitmap.get()) { + ALOGW("failed to create hardware bitmap from hardware buffer"); + return NULL; + } + return bitmap::createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false)); +#else + return NULL; +#endif +} + +static jobject Bitmap_getHardwareBuffer(JNIEnv* env, jobject, jlong bitmapPtr) { +#ifdef __ANDROID__ // Layoutlib does not support graphic buffer + LocalScopedBitmap bitmapHandle(bitmapPtr); + LOG_ALWAYS_FATAL_IF(!bitmapHandle->isHardware(), + "Hardware config is only supported config in Bitmap_getHardwareBuffer"); + + Bitmap& bitmap = bitmapHandle->bitmap(); + return AHardwareBuffer_toHardwareBuffer(env, bitmap.hardwareBuffer()); +#else + return NULL; +#endif +} + +static jboolean Bitmap_isImmutable(CRITICAL_JNI_PARAMS_COMMA jlong bitmapHandle) { + LocalScopedBitmap bitmapHolder(bitmapHandle); + if (!bitmapHolder.valid()) return JNI_FALSE; + + return bitmapHolder->bitmap().isImmutable() ? JNI_TRUE : JNI_FALSE; +} + +static void Bitmap_setImmutable(JNIEnv* env, jobject, jlong bitmapHandle) { + LocalScopedBitmap bitmapHolder(bitmapHandle); + if (!bitmapHolder.valid()) return; + + return bitmapHolder->bitmap().setImmutable(); +} + +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gBitmapMethods[] = { + { "nativeCreate", "([IIIIIIZJ)Landroid/graphics/Bitmap;", + (void*)Bitmap_creator }, + { "nativeCopy", "(JIZ)Landroid/graphics/Bitmap;", + (void*)Bitmap_copy }, + { "nativeCopyAshmem", "(J)Landroid/graphics/Bitmap;", + (void*)Bitmap_copyAshmem }, + { "nativeCopyAshmemConfig", "(JI)Landroid/graphics/Bitmap;", + (void*)Bitmap_copyAshmemConfig }, + { "nativeGetNativeFinalizer", "()J", (void*)Bitmap_getNativeFinalizer }, + { "nativeRecycle", "(J)V", (void*)Bitmap_recycle }, + { "nativeReconfigure", "(JIIIZ)V", (void*)Bitmap_reconfigure }, + { "nativeCompress", "(JIILjava/io/OutputStream;[B)Z", + (void*)Bitmap_compress }, + { "nativeErase", "(JI)V", (void*)Bitmap_erase }, + { "nativeErase", "(JJJ)V", (void*)Bitmap_eraseLong }, + { "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes }, + { "nativeConfig", "(J)I", (void*)Bitmap_config }, + { "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha }, + { "nativeIsPremultiplied", "(J)Z", (void*)Bitmap_isPremultiplied}, + { "nativeSetHasAlpha", "(JZZ)V", (void*)Bitmap_setHasAlpha}, + { "nativeSetPremultiplied", "(JZ)V", (void*)Bitmap_setPremultiplied}, + { "nativeHasMipMap", "(J)Z", (void*)Bitmap_hasMipMap }, + { "nativeSetHasMipMap", "(JZ)V", (void*)Bitmap_setHasMipMap }, + { "nativeCreateFromParcel", + "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;", + (void*)Bitmap_createFromParcel }, + { "nativeWriteToParcel", "(JILandroid/os/Parcel;)Z", + (void*)Bitmap_writeToParcel }, + { "nativeExtractAlpha", "(JJ[I)Landroid/graphics/Bitmap;", + (void*)Bitmap_extractAlpha }, + { "nativeGenerationId", "(J)I", (void*)Bitmap_getGenerationId }, + { "nativeGetPixel", "(JII)I", (void*)Bitmap_getPixel }, + { "nativeGetColor", "(JII)J", (void*)Bitmap_getColor }, + { "nativeGetPixels", "(J[IIIIIII)V", (void*)Bitmap_getPixels }, + { "nativeSetPixel", "(JIII)V", (void*)Bitmap_setPixel }, + { "nativeSetPixels", "(J[IIIIIII)V", (void*)Bitmap_setPixels }, + { "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V", + (void*)Bitmap_copyPixelsToBuffer }, + { "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V", + (void*)Bitmap_copyPixelsFromBuffer }, + { "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs }, + { "nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw }, + { "nativeGetAllocationByteCount", "(J)I", (void*)Bitmap_getAllocationByteCount }, + { "nativeCopyPreserveInternalConfig", "(J)Landroid/graphics/Bitmap;", + (void*)Bitmap_copyPreserveInternalConfig }, + { "nativeWrapHardwareBufferBitmap", "(Landroid/hardware/HardwareBuffer;J)Landroid/graphics/Bitmap;", + (void*) Bitmap_wrapHardwareBufferBitmap }, + { "nativeGetHardwareBuffer", "(J)Landroid/hardware/HardwareBuffer;", + (void*) Bitmap_getHardwareBuffer }, + { "nativeComputeColorSpace", "(J)Landroid/graphics/ColorSpace;", (void*)Bitmap_computeColorSpace }, + { "nativeSetColorSpace", "(JJ)V", (void*)Bitmap_setColorSpace }, + { "nativeIsSRGB", "(J)Z", (void*)Bitmap_isSRGB }, + { "nativeIsSRGBLinear", "(J)Z", (void*)Bitmap_isSRGBLinear}, + { "nativeSetImmutable", "(J)V", (void*)Bitmap_setImmutable}, + + // ------------ @CriticalNative ---------------- + { "nativeIsImmutable", "(J)Z", (void*)Bitmap_isImmutable} + +}; + +const char* const kParcelPathName = "android/os/Parcel"; + +int register_android_graphics_Bitmap(JNIEnv* env) +{ + gBitmap_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Bitmap")); + gBitmap_nativePtr = GetFieldIDOrDie(env, gBitmap_class, "mNativePtr", "J"); + gBitmap_constructorMethodID = GetMethodIDOrDie(env, gBitmap_class, "<init>", "(JIIIZ[BLandroid/graphics/NinePatch$InsetStruct;Z)V"); + gBitmap_reinitMethodID = GetMethodIDOrDie(env, gBitmap_class, "reinit", "(IIZ)V"); + +#ifdef __ANDROID__ // Layoutlib does not support graphic buffer or parcel + void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE); + AHardwareBuffer_fromHardwareBuffer = + (AHB_from_HB)dlsym(handle_, "AHardwareBuffer_fromHardwareBuffer"); + LOG_ALWAYS_FATAL_IF(AHardwareBuffer_fromHardwareBuffer == nullptr, + "Failed to find required symbol AHardwareBuffer_fromHardwareBuffer!"); + + AHardwareBuffer_toHardwareBuffer = (AHB_to_HB)dlsym(handle_, "AHardwareBuffer_toHardwareBuffer"); + LOG_ALWAYS_FATAL_IF(AHardwareBuffer_toHardwareBuffer == nullptr, + " Failed to find required symbol AHardwareBuffer_toHardwareBuffer!"); + + gParcelOffsets.clazz = MakeGlobalRefOrDie(env, FindClassOrDie(env, kParcelPathName)); + gParcelOffsets.mNativePtr = GetFieldIDOrDie(env, gParcelOffsets.clazz, "mNativePtr", "J"); +#endif + return android::RegisterMethodsOrDie(env, "android/graphics/Bitmap", gBitmapMethods, + NELEM(gBitmapMethods)); +} diff --git a/libs/hwui/jni/Bitmap.h b/libs/hwui/jni/Bitmap.h new file mode 100644 index 000000000000..73eca3aa8ef8 --- /dev/null +++ b/libs/hwui/jni/Bitmap.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 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. + */ +#ifndef BITMAP_H_ +#define BITMAP_H_ + +#include <jni.h> +#include <android/bitmap.h> + +class SkBitmap; +struct SkImageInfo; + +namespace android { + +class Bitmap; + +namespace bitmap { + +enum BitmapCreateFlags { + kBitmapCreateFlag_None = 0x0, + kBitmapCreateFlag_Mutable = 0x1, + kBitmapCreateFlag_Premultiplied = 0x2, +}; + +jobject createBitmap(JNIEnv* env, Bitmap* bitmap, + int bitmapCreateFlags, jbyteArray ninePatchChunk = nullptr, + jobject ninePatchInsets = nullptr, int density = -1); + +Bitmap& toBitmap(jlong bitmapHandle); + +/** Reinitialize a bitmap. bitmap must already have its SkAlphaType set in + sync with isPremultiplied +*/ +void reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info, + bool isPremultiplied); + +} // namespace bitmap + +} // namespace android + +#endif /* BITMAP_H_ */ diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp new file mode 100644 index 000000000000..e8e89d81bdb7 --- /dev/null +++ b/libs/hwui/jni/BitmapFactory.cpp @@ -0,0 +1,667 @@ +#undef LOG_TAG +#define LOG_TAG "BitmapFactory" + +#include "BitmapFactory.h" +#include "CreateJavaOutputStreamAdaptor.h" +#include "GraphicsJNI.h" +#include "MimeType.h" +#include "NinePatchPeeker.h" +#include "SkAndroidCodec.h" +#include "SkBRDAllocator.h" +#include "SkFrontBufferedStream.h" +#include "SkMath.h" +#include "SkPixelRef.h" +#include "SkStream.h" +#include "SkUtils.h" +#include "Utils.h" + +#include <HardwareBitmapUploader.h> +#include <nativehelper/JNIPlatformHelp.h> +#include <androidfw/Asset.h> +#include <androidfw/ResourceTypes.h> +#include <cutils/compiler.h> +#include <fcntl.h> +#include <memory> +#include <stdio.h> +#include <sys/stat.h> + +jfieldID gOptions_justBoundsFieldID; +jfieldID gOptions_sampleSizeFieldID; +jfieldID gOptions_configFieldID; +jfieldID gOptions_colorSpaceFieldID; +jfieldID gOptions_premultipliedFieldID; +jfieldID gOptions_mutableFieldID; +jfieldID gOptions_ditherFieldID; +jfieldID gOptions_preferQualityOverSpeedFieldID; +jfieldID gOptions_scaledFieldID; +jfieldID gOptions_densityFieldID; +jfieldID gOptions_screenDensityFieldID; +jfieldID gOptions_targetDensityFieldID; +jfieldID gOptions_widthFieldID; +jfieldID gOptions_heightFieldID; +jfieldID gOptions_mimeFieldID; +jfieldID gOptions_outConfigFieldID; +jfieldID gOptions_outColorSpaceFieldID; +jfieldID gOptions_mCancelID; +jfieldID gOptions_bitmapFieldID; + +jfieldID gBitmap_ninePatchInsetsFieldID; + +jclass gBitmapConfig_class; +jmethodID gBitmapConfig_nativeToConfigMethodID; + +using namespace android; + +const char* getMimeType(SkEncodedImageFormat format) { + switch (format) { + case SkEncodedImageFormat::kBMP: + return "image/bmp"; + case SkEncodedImageFormat::kGIF: + return "image/gif"; + case SkEncodedImageFormat::kICO: + return "image/x-ico"; + case SkEncodedImageFormat::kJPEG: + return "image/jpeg"; + case SkEncodedImageFormat::kPNG: + return "image/png"; + case SkEncodedImageFormat::kWEBP: + return "image/webp"; + case SkEncodedImageFormat::kHEIF: + return "image/heif"; + case SkEncodedImageFormat::kWBMP: + return "image/vnd.wap.wbmp"; + case SkEncodedImageFormat::kDNG: + return "image/x-adobe-dng"; + default: + return nullptr; + } +} + +jstring getMimeTypeAsJavaString(JNIEnv* env, SkEncodedImageFormat format) { + jstring jstr = nullptr; + const char* mimeType = getMimeType(format); + if (mimeType) { + // NOTE: Caller should env->ExceptionCheck() for OOM + // (can't check for nullptr as it's a valid return value) + jstr = env->NewStringUTF(mimeType); + } + return jstr; +} + +class ScaleCheckingAllocator : public SkBitmap::HeapAllocator { +public: + ScaleCheckingAllocator(float scale, int size) + : mScale(scale), mSize(size) { + } + + virtual bool allocPixelRef(SkBitmap* bitmap) { + // accounts for scale in final allocation, using eventual size and config + const int bytesPerPixel = SkColorTypeBytesPerPixel(bitmap->colorType()); + const int requestedSize = bytesPerPixel * + int(bitmap->width() * mScale + 0.5f) * + int(bitmap->height() * mScale + 0.5f); + if (requestedSize > mSize) { + ALOGW("bitmap for alloc reuse (%d bytes) can't fit scaled bitmap (%d bytes)", + mSize, requestedSize); + return false; + } + return SkBitmap::HeapAllocator::allocPixelRef(bitmap); + } +private: + const float mScale; + const int mSize; +}; + +class RecyclingPixelAllocator : public SkBitmap::Allocator { +public: + RecyclingPixelAllocator(android::Bitmap* bitmap, unsigned int size) + : mBitmap(bitmap), mSize(size) { + } + + ~RecyclingPixelAllocator() { + } + + virtual bool allocPixelRef(SkBitmap* bitmap) { + const SkImageInfo& info = bitmap->info(); + if (info.colorType() == kUnknown_SkColorType) { + ALOGW("unable to reuse a bitmap as the target has an unknown bitmap configuration"); + return false; + } + + const size_t size = info.computeByteSize(bitmap->rowBytes()); + if (size > SK_MaxS32) { + ALOGW("bitmap is too large"); + return false; + } + + if (size > mSize) { + ALOGW("bitmap marked for reuse (%u bytes) can't fit new bitmap " + "(%zu bytes)", mSize, size); + return false; + } + + mBitmap->reconfigure(info, bitmap->rowBytes()); + bitmap->setPixelRef(sk_ref_sp(mBitmap), 0, 0); + return true; + } + +private: + android::Bitmap* const mBitmap; + const unsigned int mSize; +}; + +// Necessary for decodes when the native decoder cannot scale to appropriately match the sampleSize +// (for example, RAW). If the sampleSize divides evenly into the dimension, we require that the +// scale matches exactly. If sampleSize does not divide evenly, we allow the decoder to choose how +// best to round. +static bool needsFineScale(const int fullSize, const int decodedSize, const int sampleSize) { + if (fullSize % sampleSize == 0 && fullSize / sampleSize != decodedSize) { + return true; + } else if ((fullSize / sampleSize + 1) != decodedSize && + (fullSize / sampleSize) != decodedSize) { + return true; + } + return false; +} + +static bool needsFineScale(const SkISize fullSize, const SkISize decodedSize, + const int sampleSize) { + return needsFineScale(fullSize.width(), decodedSize.width(), sampleSize) || + needsFineScale(fullSize.height(), decodedSize.height(), sampleSize); +} + +static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream, + jobject padding, jobject options, jlong inBitmapHandle, + jlong colorSpaceHandle) { + // Set default values for the options parameters. + int sampleSize = 1; + bool onlyDecodeSize = false; + SkColorType prefColorType = kN32_SkColorType; + bool isHardware = false; + bool isMutable = false; + float scale = 1.0f; + bool requireUnpremultiplied = false; + jobject javaBitmap = NULL; + sk_sp<SkColorSpace> prefColorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); + + // Update with options supplied by the client. + if (options != NULL) { + sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); + // Correct a non-positive sampleSize. sampleSize defaults to zero within the + // options object, which is strange. + if (sampleSize <= 0) { + sampleSize = 1; + } + + if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) { + onlyDecodeSize = true; + } + + // initialize these, in case we fail later on + env->SetIntField(options, gOptions_widthFieldID, -1); + env->SetIntField(options, gOptions_heightFieldID, -1); + env->SetObjectField(options, gOptions_mimeFieldID, 0); + env->SetObjectField(options, gOptions_outConfigFieldID, 0); + env->SetObjectField(options, gOptions_outColorSpaceFieldID, 0); + + jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); + prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig); + isHardware = GraphicsJNI::isHardwareConfig(env, jconfig); + isMutable = env->GetBooleanField(options, gOptions_mutableFieldID); + requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID); + javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); + + if (env->GetBooleanField(options, gOptions_scaledFieldID)) { + const int density = env->GetIntField(options, gOptions_densityFieldID); + const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID); + const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID); + if (density != 0 && targetDensity != 0 && density != screenDensity) { + scale = (float) targetDensity / density; + } + } + } + + if (isMutable && isHardware) { + doThrowIAE(env, "Bitmaps with Config.HARDWARE are always immutable"); + return nullObjectReturn("Cannot create mutable hardware bitmap"); + } + + // Create the codec. + NinePatchPeeker peeker; + std::unique_ptr<SkAndroidCodec> codec; + { + SkCodec::Result result; + std::unique_ptr<SkCodec> c = SkCodec::MakeFromStream(std::move(stream), &result, + &peeker); + if (!c) { + SkString msg; + msg.printf("Failed to create image decoder with message '%s'", + SkCodec::ResultToString(result)); + return nullObjectReturn(msg.c_str()); + } + + codec = SkAndroidCodec::MakeFromCodec(std::move(c)); + if (!codec) { + return nullObjectReturn("SkAndroidCodec::MakeFromCodec returned null"); + } + } + + // Do not allow ninepatch decodes to 565. In the past, decodes to 565 + // would dither, and we do not want to pre-dither ninepatches, since we + // know that they will be stretched. We no longer dither 565 decodes, + // but we continue to prevent ninepatches from decoding to 565, in order + // to maintain the old behavior. + if (peeker.mPatch && kRGB_565_SkColorType == prefColorType) { + prefColorType = kN32_SkColorType; + } + + // Determine the output size. + SkISize size = codec->getSampledDimensions(sampleSize); + + int scaledWidth = size.width(); + int scaledHeight = size.height(); + bool willScale = false; + + // Apply a fine scaling step if necessary. + if (needsFineScale(codec->getInfo().dimensions(), size, sampleSize)) { + willScale = true; + scaledWidth = codec->getInfo().width() / sampleSize; + scaledHeight = codec->getInfo().height() / sampleSize; + } + + // Set the decode colorType + SkColorType decodeColorType = codec->computeOutputColorType(prefColorType); + if (decodeColorType == kRGBA_F16_SkColorType && isHardware && + !uirenderer::HardwareBitmapUploader::hasFP16Support()) { + decodeColorType = kN32_SkColorType; + } + + sk_sp<SkColorSpace> decodeColorSpace = codec->computeOutputColorSpace( + decodeColorType, prefColorSpace); + + // Set the options and return if the client only wants the size. + if (options != NULL) { + jstring mimeType = getMimeTypeAsJavaString(env, codec->getEncodedFormat()); + if (env->ExceptionCheck()) { + return nullObjectReturn("OOM in getMimeTypeAsJavaString()"); + } + env->SetIntField(options, gOptions_widthFieldID, scaledWidth); + env->SetIntField(options, gOptions_heightFieldID, scaledHeight); + env->SetObjectField(options, gOptions_mimeFieldID, mimeType); + + jint configID = GraphicsJNI::colorTypeToLegacyBitmapConfig(decodeColorType); + if (isHardware) { + configID = GraphicsJNI::kHardware_LegacyBitmapConfig; + } + jobject config = env->CallStaticObjectMethod(gBitmapConfig_class, + gBitmapConfig_nativeToConfigMethodID, configID); + env->SetObjectField(options, gOptions_outConfigFieldID, config); + + env->SetObjectField(options, gOptions_outColorSpaceFieldID, + GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType)); + + if (onlyDecodeSize) { + return nullptr; + } + } + + // Scale is necessary due to density differences. + if (scale != 1.0f) { + willScale = true; + scaledWidth = static_cast<int>(scaledWidth * scale + 0.5f); + scaledHeight = static_cast<int>(scaledHeight * scale + 0.5f); + } + + android::Bitmap* reuseBitmap = nullptr; + unsigned int existingBufferSize = 0; + if (javaBitmap != nullptr) { + reuseBitmap = &bitmap::toBitmap(inBitmapHandle); + if (reuseBitmap->isImmutable()) { + ALOGW("Unable to reuse an immutable bitmap as an image decoder target."); + javaBitmap = nullptr; + reuseBitmap = nullptr; + } else { + existingBufferSize = reuseBitmap->getAllocationByteCount(); + } + } + + HeapAllocator defaultAllocator; + RecyclingPixelAllocator recyclingAllocator(reuseBitmap, existingBufferSize); + ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize); + SkBitmap::HeapAllocator heapAllocator; + SkBitmap::Allocator* decodeAllocator; + if (javaBitmap != nullptr && willScale) { + // This will allocate pixels using a HeapAllocator, since there will be an extra + // scaling step that copies these pixels into Java memory. This allocator + // also checks that the recycled javaBitmap is large enough. + decodeAllocator = &scaleCheckingAllocator; + } else if (javaBitmap != nullptr) { + decodeAllocator = &recyclingAllocator; + } else if (willScale || isHardware) { + // This will allocate pixels using a HeapAllocator, + // for scale case: there will be an extra scaling step. + // for hardware case: there will be extra swizzling & upload to gralloc step. + decodeAllocator = &heapAllocator; + } else { + decodeAllocator = &defaultAllocator; + } + + SkAlphaType alphaType = codec->computeOutputAlphaType(requireUnpremultiplied); + + const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(), + decodeColorType, alphaType, decodeColorSpace); + + SkImageInfo bitmapInfo = decodeInfo; + if (decodeColorType == kGray_8_SkColorType) { + // The legacy implementation of BitmapFactory used kAlpha8 for + // grayscale images (before kGray8 existed). While the codec + // recognizes kGray8, we need to decode into a kAlpha8 bitmap + // in order to avoid a behavior change. + bitmapInfo = + bitmapInfo.makeColorType(kAlpha_8_SkColorType).makeAlphaType(kPremul_SkAlphaType); + } + SkBitmap decodingBitmap; + if (!decodingBitmap.setInfo(bitmapInfo) || + !decodingBitmap.tryAllocPixels(decodeAllocator)) { + // SkAndroidCodec should recommend a valid SkImageInfo, so setInfo() + // should only only fail if the calculated value for rowBytes is too + // large. + // tryAllocPixels() can fail due to OOM on the Java heap, OOM on the + // native heap, or the recycled javaBitmap being too small to reuse. + return nullptr; + } + + // Use SkAndroidCodec to perform the decode. + SkAndroidCodec::AndroidOptions codecOptions; + codecOptions.fZeroInitialized = decodeAllocator == &defaultAllocator ? + SkCodec::kYes_ZeroInitialized : SkCodec::kNo_ZeroInitialized; + codecOptions.fSampleSize = sampleSize; + SkCodec::Result result = codec->getAndroidPixels(decodeInfo, decodingBitmap.getPixels(), + decodingBitmap.rowBytes(), &codecOptions); + switch (result) { + case SkCodec::kSuccess: + case SkCodec::kIncompleteInput: + break; + default: + return nullObjectReturn("codec->getAndroidPixels() failed."); + } + + // This is weird so let me explain: we could use the scale parameter + // directly, but for historical reasons this is how the corresponding + // Dalvik code has always behaved. We simply recreate the behavior here. + // The result is slightly different from simply using scale because of + // the 0.5f rounding bias applied when computing the target image size + const float scaleX = scaledWidth / float(decodingBitmap.width()); + const float scaleY = scaledHeight / float(decodingBitmap.height()); + + jbyteArray ninePatchChunk = NULL; + if (peeker.mPatch != NULL) { + if (willScale) { + peeker.scale(scaleX, scaleY, scaledWidth, scaledHeight); + } + + size_t ninePatchArraySize = peeker.mPatch->serializedSize(); + ninePatchChunk = env->NewByteArray(ninePatchArraySize); + if (ninePatchChunk == NULL) { + return nullObjectReturn("ninePatchChunk == null"); + } + + jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL); + if (array == NULL) { + return nullObjectReturn("primitive array == null"); + } + + memcpy(array, peeker.mPatch, peeker.mPatchSize); + env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); + } + + jobject ninePatchInsets = NULL; + if (peeker.mHasInsets) { + ninePatchInsets = peeker.createNinePatchInsets(env, scale); + if (ninePatchInsets == NULL) { + return nullObjectReturn("nine patch insets == null"); + } + if (javaBitmap != NULL) { + env->SetObjectField(javaBitmap, gBitmap_ninePatchInsetsFieldID, ninePatchInsets); + } + } + + SkBitmap outputBitmap; + if (willScale) { + // Set the allocator for the outputBitmap. + SkBitmap::Allocator* outputAllocator; + if (javaBitmap != nullptr) { + outputAllocator = &recyclingAllocator; + } else { + outputAllocator = &defaultAllocator; + } + + SkColorType scaledColorType = decodingBitmap.colorType(); + // FIXME: If the alphaType is kUnpremul and the image has alpha, the + // colors may not be correct, since Skia does not yet support drawing + // to/from unpremultiplied bitmaps. + outputBitmap.setInfo( + bitmapInfo.makeWH(scaledWidth, scaledHeight).makeColorType(scaledColorType)); + if (!outputBitmap.tryAllocPixels(outputAllocator)) { + // This should only fail on OOM. The recyclingAllocator should have + // enough memory since we check this before decoding using the + // scaleCheckingAllocator. + return nullObjectReturn("allocation failed for scaled bitmap"); + } + + SkPaint paint; + // kSrc_Mode instructs us to overwrite the uninitialized pixels in + // outputBitmap. Otherwise we would blend by default, which is not + // what we want. + paint.setBlendMode(SkBlendMode::kSrc); + paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering + + SkCanvas canvas(outputBitmap, SkCanvas::ColorBehavior::kLegacy); + canvas.scale(scaleX, scaleY); + canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint); + } else { + outputBitmap.swap(decodingBitmap); + } + + if (padding) { + peeker.getPadding(env, padding); + } + + // If we get here, the outputBitmap should have an installed pixelref. + if (outputBitmap.pixelRef() == NULL) { + return nullObjectReturn("Got null SkPixelRef"); + } + + if (!isMutable && javaBitmap == NULL) { + // promise we will never change our pixels (great for sharing and pictures) + outputBitmap.setImmutable(); + } + + bool isPremultiplied = !requireUnpremultiplied; + if (javaBitmap != nullptr) { + bitmap::reinitBitmap(env, javaBitmap, outputBitmap.info(), isPremultiplied); + outputBitmap.notifyPixelsChanged(); + // If a java bitmap was passed in for reuse, pass it back + return javaBitmap; + } + + int bitmapCreateFlags = 0x0; + if (isMutable) bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Mutable; + if (isPremultiplied) bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Premultiplied; + + if (isHardware) { + sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(outputBitmap); + if (!hardwareBitmap.get()) { + return nullObjectReturn("Failed to allocate a hardware bitmap"); + } + return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags, + ninePatchChunk, ninePatchInsets, -1); + } + + // now create the java bitmap + return bitmap::createBitmap(env, defaultAllocator.getStorageObjAndReset(), + bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1); +} + +static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage, + jobject padding, jobject options, jlong inBitmapHandle, jlong colorSpaceHandle) { + + jobject bitmap = NULL; + std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage)); + + if (stream.get()) { + std::unique_ptr<SkStreamRewindable> bufferedStream( + SkFrontBufferedStream::Make(std::move(stream), SkCodec::MinBufferedBytesNeeded())); + SkASSERT(bufferedStream.get() != NULL); + bitmap = doDecode(env, std::move(bufferedStream), padding, options, inBitmapHandle, + colorSpaceHandle); + } + return bitmap; +} + +static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor, + jobject padding, jobject bitmapFactoryOptions, jlong inBitmapHandle, jlong colorSpaceHandle) { +#ifndef __ANDROID__ // LayoutLib for Windows does not support F_DUPFD_CLOEXEC + return nullObjectReturn("Not supported on Windows"); +#else + NPE_CHECK_RETURN_ZERO(env, fileDescriptor); + + int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); + + struct stat fdStat; + if (fstat(descriptor, &fdStat) == -1) { + doThrowIOE(env, "broken file descriptor"); + return nullObjectReturn("fstat return -1"); + } + + // Restore the descriptor's offset on exiting this function. Even though + // we dup the descriptor, both the original and dup refer to the same open + // file description and changes to the file offset in one impact the other. + AutoFDSeek autoRestore(descriptor); + + // Duplicate the descriptor here to prevent leaking memory. A leak occurs + // if we only close the file descriptor and not the file object it is used to + // create. If we don't explicitly clean up the file (which in turn closes the + // descriptor) the buffers allocated internally by fseek will be leaked. + int dupDescriptor = fcntl(descriptor, F_DUPFD_CLOEXEC, 0); + + FILE* file = fdopen(dupDescriptor, "r"); + if (file == NULL) { + // cleanup the duplicated descriptor since it will not be closed when the + // file is cleaned up (fclose). + close(dupDescriptor); + return nullObjectReturn("Could not open file"); + } + + std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file)); + + // If there is no offset for the file descriptor, we use SkFILEStream directly. + if (::lseek(descriptor, 0, SEEK_CUR) == 0) { + assert(isSeekable(dupDescriptor)); + return doDecode(env, std::move(fileStream), padding, bitmapFactoryOptions, + inBitmapHandle, colorSpaceHandle); + } + + // Use a buffered stream. Although an SkFILEStream can be rewound, this + // ensures that SkImageDecoder::Factory never rewinds beyond the + // current position of the file descriptor. + std::unique_ptr<SkStreamRewindable> stream(SkFrontBufferedStream::Make(std::move(fileStream), + SkCodec::MinBufferedBytesNeeded())); + + return doDecode(env, std::move(stream), padding, bitmapFactoryOptions, inBitmapHandle, + colorSpaceHandle); +#endif +} + +static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset, + jobject padding, jobject options, jlong inBitmapHandle, jlong colorSpaceHandle) { + + Asset* asset = reinterpret_cast<Asset*>(native_asset); + // since we know we'll be done with the asset when we return, we can + // just use a simple wrapper + return doDecode(env, std::make_unique<AssetStreamAdaptor>(asset), padding, options, + inBitmapHandle, colorSpaceHandle); +} + +static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, + jint offset, jint length, jobject options, jlong inBitmapHandle, jlong colorSpaceHandle) { + + AutoJavaByteArray ar(env, byteArray); + return doDecode(env, std::make_unique<SkMemoryStream>(ar.ptr() + offset, length, false), + nullptr, options, inBitmapHandle, colorSpaceHandle); +} + +static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) { + jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); + return isSeekable(descriptor) ? JNI_TRUE : JNI_FALSE; +} + +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gMethods[] = { + { "nativeDecodeStream", + "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;", + (void*)nativeDecodeStream + }, + + { "nativeDecodeFileDescriptor", + "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;", + (void*)nativeDecodeFileDescriptor + }, + + { "nativeDecodeAsset", + "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;", + (void*)nativeDecodeAsset + }, + + { "nativeDecodeByteArray", + "([BIILandroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;", + (void*)nativeDecodeByteArray + }, + + { "nativeIsSeekable", + "(Ljava/io/FileDescriptor;)Z", + (void*)nativeIsSeekable + }, +}; + +int register_android_graphics_BitmapFactory(JNIEnv* env) { + jclass options_class = FindClassOrDie(env, "android/graphics/BitmapFactory$Options"); + gOptions_bitmapFieldID = GetFieldIDOrDie(env, options_class, "inBitmap", + "Landroid/graphics/Bitmap;"); + gOptions_justBoundsFieldID = GetFieldIDOrDie(env, options_class, "inJustDecodeBounds", "Z"); + gOptions_sampleSizeFieldID = GetFieldIDOrDie(env, options_class, "inSampleSize", "I"); + gOptions_configFieldID = GetFieldIDOrDie(env, options_class, "inPreferredConfig", + "Landroid/graphics/Bitmap$Config;"); + gOptions_colorSpaceFieldID = GetFieldIDOrDie(env, options_class, "inPreferredColorSpace", + "Landroid/graphics/ColorSpace;"); + gOptions_premultipliedFieldID = GetFieldIDOrDie(env, options_class, "inPremultiplied", "Z"); + gOptions_mutableFieldID = GetFieldIDOrDie(env, options_class, "inMutable", "Z"); + gOptions_ditherFieldID = GetFieldIDOrDie(env, options_class, "inDither", "Z"); + gOptions_preferQualityOverSpeedFieldID = GetFieldIDOrDie(env, options_class, + "inPreferQualityOverSpeed", "Z"); + gOptions_scaledFieldID = GetFieldIDOrDie(env, options_class, "inScaled", "Z"); + gOptions_densityFieldID = GetFieldIDOrDie(env, options_class, "inDensity", "I"); + gOptions_screenDensityFieldID = GetFieldIDOrDie(env, options_class, "inScreenDensity", "I"); + gOptions_targetDensityFieldID = GetFieldIDOrDie(env, options_class, "inTargetDensity", "I"); + gOptions_widthFieldID = GetFieldIDOrDie(env, options_class, "outWidth", "I"); + gOptions_heightFieldID = GetFieldIDOrDie(env, options_class, "outHeight", "I"); + gOptions_mimeFieldID = GetFieldIDOrDie(env, options_class, "outMimeType", "Ljava/lang/String;"); + gOptions_outConfigFieldID = GetFieldIDOrDie(env, options_class, "outConfig", + "Landroid/graphics/Bitmap$Config;"); + gOptions_outColorSpaceFieldID = GetFieldIDOrDie(env, options_class, "outColorSpace", + "Landroid/graphics/ColorSpace;"); + gOptions_mCancelID = GetFieldIDOrDie(env, options_class, "mCancel", "Z"); + + jclass bitmap_class = FindClassOrDie(env, "android/graphics/Bitmap"); + gBitmap_ninePatchInsetsFieldID = GetFieldIDOrDie(env, bitmap_class, "mNinePatchInsets", + "Landroid/graphics/NinePatch$InsetStruct;"); + + gBitmapConfig_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, + "android/graphics/Bitmap$Config")); + gBitmapConfig_nativeToConfigMethodID = GetStaticMethodIDOrDie(env, gBitmapConfig_class, + "nativeToConfig", "(I)Landroid/graphics/Bitmap$Config;"); + + return android::RegisterMethodsOrDie(env, "android/graphics/BitmapFactory", + gMethods, NELEM(gMethods)); +} diff --git a/libs/hwui/jni/BitmapFactory.h b/libs/hwui/jni/BitmapFactory.h new file mode 100644 index 000000000000..45bffc44967d --- /dev/null +++ b/libs/hwui/jni/BitmapFactory.h @@ -0,0 +1,31 @@ +#ifndef _ANDROID_GRAPHICS_BITMAP_FACTORY_H_ +#define _ANDROID_GRAPHICS_BITMAP_FACTORY_H_ + +#include "GraphicsJNI.h" +#include "SkEncodedImageFormat.h" + +extern jclass gOptions_class; +extern jfieldID gOptions_justBoundsFieldID; +extern jfieldID gOptions_sampleSizeFieldID; +extern jfieldID gOptions_configFieldID; +extern jfieldID gOptions_colorSpaceFieldID; +extern jfieldID gOptions_premultipliedFieldID; +extern jfieldID gOptions_ditherFieldID; +extern jfieldID gOptions_purgeableFieldID; +extern jfieldID gOptions_shareableFieldID; +extern jfieldID gOptions_nativeAllocFieldID; +extern jfieldID gOptions_preferQualityOverSpeedFieldID; +extern jfieldID gOptions_widthFieldID; +extern jfieldID gOptions_heightFieldID; +extern jfieldID gOptions_mimeFieldID; +extern jfieldID gOptions_outConfigFieldID; +extern jfieldID gOptions_outColorSpaceFieldID; +extern jfieldID gOptions_mCancelID; +extern jfieldID gOptions_bitmapFieldID; + +extern jclass gBitmapConfig_class; +extern jmethodID gBitmapConfig_nativeToConfigMethodID; + +jstring getMimeTypeAsJavaString(JNIEnv*, SkEncodedImageFormat); + +#endif // _ANDROID_GRAPHICS_BITMAP_FACTORY_H_ diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp new file mode 100644 index 000000000000..712351382d97 --- /dev/null +++ b/libs/hwui/jni/BitmapRegionDecoder.cpp @@ -0,0 +1,288 @@ +/* + * 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. + */ + +#undef LOG_TAG +#define LOG_TAG "BitmapRegionDecoder" + +#include "BitmapFactory.h" +#include "CreateJavaOutputStreamAdaptor.h" +#include "GraphicsJNI.h" +#include "Utils.h" + +#include "SkBitmap.h" +#include "SkBitmapRegionDecoder.h" +#include "SkCodec.h" +#include "SkData.h" +#include "SkStream.h" + +#include <HardwareBitmapUploader.h> +#include <androidfw/Asset.h> +#include <sys/stat.h> + +#include <memory> + +using namespace android; + +static jobject createBitmapRegionDecoder(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream) { + std::unique_ptr<SkBitmapRegionDecoder> brd( + SkBitmapRegionDecoder::Create(stream.release(), + SkBitmapRegionDecoder::kAndroidCodec_Strategy)); + if (!brd) { + doThrowIOE(env, "Image format not supported"); + return nullObjectReturn("CreateBitmapRegionDecoder returned null"); + } + + return GraphicsJNI::createBitmapRegionDecoder(env, brd.release()); +} + +static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray, + jint offset, jint length, jboolean isShareable) { + /* If isShareable we could decide to just wrap the java array and + share it, but that means adding a globalref to the java array object + For now we just always copy the array's data if isShareable. + */ + AutoJavaByteArray ar(env, byteArray); + std::unique_ptr<SkMemoryStream> stream(new SkMemoryStream(ar.ptr() + offset, length, true)); + + // the decoder owns the stream. + jobject brd = createBitmapRegionDecoder(env, std::move(stream)); + return brd; +} + +static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz, + jobject fileDescriptor, jboolean isShareable) { + NPE_CHECK_RETURN_ZERO(env, fileDescriptor); + + jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); + + struct stat fdStat; + if (fstat(descriptor, &fdStat) == -1) { + doThrowIOE(env, "broken file descriptor"); + return nullObjectReturn("fstat return -1"); + } + + sk_sp<SkData> data(SkData::MakeFromFD(descriptor)); + std::unique_ptr<SkMemoryStream> stream(new SkMemoryStream(std::move(data))); + + // the decoder owns the stream. + jobject brd = createBitmapRegionDecoder(env, std::move(stream)); + return brd; +} + +static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz, + jobject is, // InputStream + jbyteArray storage, // byte[] + jboolean isShareable) { + jobject brd = NULL; + // for now we don't allow shareable with java inputstreams + std::unique_ptr<SkStreamRewindable> stream(CopyJavaInputStream(env, is, storage)); + + if (stream) { + // the decoder owns the stream. + brd = createBitmapRegionDecoder(env, std::move(stream)); + } + return brd; +} + +static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz, + jlong native_asset, // Asset + jboolean isShareable) { + Asset* asset = reinterpret_cast<Asset*>(native_asset); + std::unique_ptr<SkMemoryStream> stream(CopyAssetToStream(asset)); + if (NULL == stream) { + return NULL; + } + + // the decoder owns the stream. + jobject brd = createBitmapRegionDecoder(env, std::move(stream)); + return brd; +} + +/* + * nine patch not supported + * purgeable not supported + * reportSizeToVM not supported + */ +static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint inputX, + jint inputY, jint inputWidth, jint inputHeight, jobject options, jlong inBitmapHandle, + jlong colorSpaceHandle) { + + // Set default options. + int sampleSize = 1; + SkColorType colorType = kN32_SkColorType; + bool requireUnpremul = false; + jobject javaBitmap = nullptr; + bool isHardware = false; + sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); + // Update the default options with any options supplied by the client. + if (NULL != options) { + sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); + jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); + colorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig); + isHardware = GraphicsJNI::isHardwareConfig(env, jconfig); + requireUnpremul = !env->GetBooleanField(options, gOptions_premultipliedFieldID); + javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); + // The Java options of ditherMode and preferQualityOverSpeed are deprecated. We will + // ignore the values of these fields. + + // Initialize these fields to indicate a failure. If the decode succeeds, we + // will update them later on. + env->SetIntField(options, gOptions_widthFieldID, -1); + env->SetIntField(options, gOptions_heightFieldID, -1); + env->SetObjectField(options, gOptions_mimeFieldID, 0); + env->SetObjectField(options, gOptions_outConfigFieldID, 0); + env->SetObjectField(options, gOptions_outColorSpaceFieldID, 0); + } + + // Recycle a bitmap if possible. + android::Bitmap* recycledBitmap = nullptr; + size_t recycledBytes = 0; + if (javaBitmap) { + recycledBitmap = &bitmap::toBitmap(inBitmapHandle); + if (recycledBitmap->isImmutable()) { + ALOGW("Warning: Reusing an immutable bitmap as an image decoder target."); + } + recycledBytes = recycledBitmap->getAllocationByteCount(); + } + + SkBitmapRegionDecoder* brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); + SkColorType decodeColorType = brd->computeOutputColorType(colorType); + if (decodeColorType == kRGBA_F16_SkColorType && isHardware && + !uirenderer::HardwareBitmapUploader::hasFP16Support()) { + decodeColorType = kN32_SkColorType; + } + + // Set up the pixel allocator + SkBRDAllocator* allocator = nullptr; + RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap, recycledBytes); + HeapAllocator heapAlloc; + if (javaBitmap) { + allocator = &recycleAlloc; + // We are required to match the color type of the recycled bitmap. + decodeColorType = recycledBitmap->info().colorType(); + } else { + allocator = &heapAlloc; + } + + sk_sp<SkColorSpace> decodeColorSpace = brd->computeOutputColorSpace( + decodeColorType, colorSpace); + + // Decode the region. + SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight); + SkBitmap bitmap; + if (!brd->decodeRegion(&bitmap, allocator, subset, sampleSize, + decodeColorType, requireUnpremul, decodeColorSpace)) { + return nullObjectReturn("Failed to decode region."); + } + + // If the client provided options, indicate that the decode was successful. + if (NULL != options) { + env->SetIntField(options, gOptions_widthFieldID, bitmap.width()); + env->SetIntField(options, gOptions_heightFieldID, bitmap.height()); + + env->SetObjectField(options, gOptions_mimeFieldID, + getMimeTypeAsJavaString(env, brd->getEncodedFormat())); + if (env->ExceptionCheck()) { + return nullObjectReturn("OOM in encodedFormatToString()"); + } + + jint configID = GraphicsJNI::colorTypeToLegacyBitmapConfig(decodeColorType); + if (isHardware) { + configID = GraphicsJNI::kHardware_LegacyBitmapConfig; + } + jobject config = env->CallStaticObjectMethod(gBitmapConfig_class, + gBitmapConfig_nativeToConfigMethodID, configID); + env->SetObjectField(options, gOptions_outConfigFieldID, config); + + env->SetObjectField(options, gOptions_outColorSpaceFieldID, + GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType)); + } + + // If we may have reused a bitmap, we need to indicate that the pixels have changed. + if (javaBitmap) { + recycleAlloc.copyIfNecessary(); + bitmap::reinitBitmap(env, javaBitmap, recycledBitmap->info(), !requireUnpremul); + return javaBitmap; + } + + int bitmapCreateFlags = 0; + if (!requireUnpremul) { + bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Premultiplied; + } + if (isHardware) { + sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(bitmap); + return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags); + } + return android::bitmap::createBitmap(env, heapAlloc.getStorageObjAndReset(), bitmapCreateFlags); +} + +static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) { + SkBitmapRegionDecoder* brd = + reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); + return static_cast<jint>(brd->height()); +} + +static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) { + SkBitmapRegionDecoder* brd = + reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); + return static_cast<jint>(brd->width()); +} + +static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) { + SkBitmapRegionDecoder* brd = + reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); + delete brd; +} + +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gBitmapRegionDecoderMethods[] = { + { "nativeDecodeRegion", + "(JIIIILandroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;", + (void*)nativeDecodeRegion}, + + { "nativeGetHeight", "(J)I", (void*)nativeGetHeight}, + + { "nativeGetWidth", "(J)I", (void*)nativeGetWidth}, + + { "nativeClean", "(J)V", (void*)nativeClean}, + + { "nativeNewInstance", + "([BIIZ)Landroid/graphics/BitmapRegionDecoder;", + (void*)nativeNewInstanceFromByteArray + }, + + { "nativeNewInstance", + "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;", + (void*)nativeNewInstanceFromStream + }, + + { "nativeNewInstance", + "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;", + (void*)nativeNewInstanceFromFileDescriptor + }, + + { "nativeNewInstance", + "(JZ)Landroid/graphics/BitmapRegionDecoder;", + (void*)nativeNewInstanceFromAsset + }, +}; + +int register_android_graphics_BitmapRegionDecoder(JNIEnv* env) +{ + return android::RegisterMethodsOrDie(env, "android/graphics/BitmapRegionDecoder", + gBitmapRegionDecoderMethods, NELEM(gBitmapRegionDecoderMethods)); +} diff --git a/libs/hwui/jni/ByteBufferStreamAdaptor.cpp b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp new file mode 100644 index 000000000000..b10540cb3fbd --- /dev/null +++ b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp @@ -0,0 +1,316 @@ +#include "ByteBufferStreamAdaptor.h" +#include "GraphicsJNI.h" +#include "Utils.h" + +#include <SkStream.h> + +using namespace android; + +static jmethodID gByteBuffer_getMethodID; +static jmethodID gByteBuffer_setPositionMethodID; + +class ByteBufferStream : public SkStreamAsset { +private: + ByteBufferStream(JavaVM* jvm, jobject jbyteBuffer, size_t initialPosition, size_t length, + jbyteArray storage) + : mJvm(jvm) + , mByteBuffer(jbyteBuffer) + , mPosition(0) + , mInitialPosition(initialPosition) + , mLength(length) + , mStorage(storage) {} + +public: + static ByteBufferStream* Create(JavaVM* jvm, JNIEnv* env, jobject jbyteBuffer, + size_t position, size_t length) { + // This object outlives its native method call. + jbyteBuffer = env->NewGlobalRef(jbyteBuffer); + if (!jbyteBuffer) { + return nullptr; + } + + jbyteArray storage = env->NewByteArray(kStorageSize); + if (!storage) { + env->DeleteGlobalRef(jbyteBuffer); + return nullptr; + } + + // This object outlives its native method call. + storage = static_cast<jbyteArray>(env->NewGlobalRef(storage)); + if (!storage) { + env->DeleteGlobalRef(jbyteBuffer); + return nullptr; + } + + return new ByteBufferStream(jvm, jbyteBuffer, position, length, storage); + } + + ~ByteBufferStream() override { + auto* env = requireEnv(mJvm); + env->DeleteGlobalRef(mByteBuffer); + env->DeleteGlobalRef(mStorage); + } + + size_t read(void* buffer, size_t size) override { + if (size > mLength - mPosition) { + size = mLength - mPosition; + } + if (!size) { + return 0; + } + + if (!buffer) { + return this->setPosition(mPosition + size) ? size : 0; + } + + auto* env = requireEnv(mJvm); + size_t bytesRead = 0; + do { + const size_t requested = (size > kStorageSize) ? kStorageSize : size; + const jint jrequested = static_cast<jint>(requested); + env->CallObjectMethod(mByteBuffer, gByteBuffer_getMethodID, mStorage, 0, jrequested); + if (env->ExceptionCheck()) { + ALOGE("Error in ByteBufferStream::read - was the ByteBuffer modified externally?"); + env->ExceptionDescribe(); + env->ExceptionClear(); + mPosition = mLength; + return bytesRead; + } + + env->GetByteArrayRegion(mStorage, 0, requested, reinterpret_cast<jbyte*>(buffer)); + if (env->ExceptionCheck()) { + ALOGE("Internal error in ByteBufferStream::read"); + env->ExceptionDescribe(); + env->ExceptionClear(); + mPosition = mLength; + return bytesRead; + } + + mPosition += requested; + buffer = reinterpret_cast<void*>(reinterpret_cast<char*>(buffer) + requested); + bytesRead += requested; + size -= requested; + } while (size); + return bytesRead; + } + + bool isAtEnd() const override { return mLength == mPosition; } + + // SkStreamRewindable overrides + bool rewind() override { return this->setPosition(0); } + + SkStreamAsset* onDuplicate() const override { + // SkStreamRewindable requires overriding this, but it is not called by + // decoders, so does not need a true implementation. A proper + // implementation would require duplicating the ByteBuffer, which has + // its own internal position state. + return nullptr; + } + + // SkStreamSeekable overrides + size_t getPosition() const override { return mPosition; } + + bool seek(size_t position) override { + return this->setPosition(position > mLength ? mLength : position); + } + + bool move(long offset) override { + long newPosition = mPosition + offset; + if (newPosition < 0) { + return this->setPosition(0); + } + return this->seek(static_cast<size_t>(newPosition)); + } + + SkStreamAsset* onFork() const override { + // SkStreamSeekable requires overriding this, but it is not called by + // decoders, so does not need a true implementation. A proper + // implementation would require duplicating the ByteBuffer, which has + // its own internal position state. + return nullptr; + } + + // SkStreamAsset overrides + size_t getLength() const override { return mLength; } + +private: + JavaVM* mJvm; + jobject mByteBuffer; + // Logical position of the SkStream, between 0 and mLength. + size_t mPosition; + // Initial position of mByteBuffer, treated as mPosition 0. + const size_t mInitialPosition; + // Logical length of the SkStream, from mInitialPosition to + // mByteBuffer.limit(). + const size_t mLength; + + // Range has already been checked by the caller. + bool setPosition(size_t newPosition) { + auto* env = requireEnv(mJvm); + env->CallObjectMethod(mByteBuffer, gByteBuffer_setPositionMethodID, + newPosition + mInitialPosition); + if (env->ExceptionCheck()) { + ALOGE("Internal error in ByteBufferStream::setPosition"); + env->ExceptionDescribe(); + env->ExceptionClear(); + mPosition = mLength; + return false; + } + mPosition = newPosition; + return true; + } + + // FIXME: This is an arbitrary storage size, which should be plenty for + // some formats (png, gif, many bmps). But for jpeg, the more we can supply + // in one call the better, and webp really wants all of the data. How to + // best choose the amount of storage used? + static constexpr size_t kStorageSize = 4096; + jbyteArray mStorage; +}; + +class ByteArrayStream : public SkStreamAsset { +private: + ByteArrayStream(JavaVM* jvm, jbyteArray jarray, size_t offset, size_t length) + : mJvm(jvm), mByteArray(jarray), mOffset(offset), mPosition(0), mLength(length) {} + +public: + static ByteArrayStream* Create(JavaVM* jvm, JNIEnv* env, jbyteArray jarray, size_t offset, + size_t length) { + // This object outlives its native method call. + jarray = static_cast<jbyteArray>(env->NewGlobalRef(jarray)); + if (!jarray) { + return nullptr; + } + return new ByteArrayStream(jvm, jarray, offset, length); + } + + ~ByteArrayStream() override { + auto* env = requireEnv(mJvm); + env->DeleteGlobalRef(mByteArray); + } + + size_t read(void* buffer, size_t size) override { + if (size > mLength - mPosition) { + size = mLength - mPosition; + } + if (!size) { + return 0; + } + + auto* env = requireEnv(mJvm); + if (buffer) { + env->GetByteArrayRegion(mByteArray, mPosition + mOffset, size, + reinterpret_cast<jbyte*>(buffer)); + if (env->ExceptionCheck()) { + ALOGE("Internal error in ByteArrayStream::read"); + env->ExceptionDescribe(); + env->ExceptionClear(); + mPosition = mLength; + return 0; + } + } + + mPosition += size; + return size; + } + + bool isAtEnd() const override { return mLength == mPosition; } + + // SkStreamRewindable overrides + bool rewind() override { + mPosition = 0; + return true; + } + SkStreamAsset* onDuplicate() const override { + // SkStreamRewindable requires overriding this, but it is not called by + // decoders, so does not need a true implementation. Note that a proper + // implementation is fairly straightforward + return nullptr; + } + + // SkStreamSeekable overrides + size_t getPosition() const override { return mPosition; } + + bool seek(size_t position) override { + mPosition = (position > mLength) ? mLength : position; + return true; + } + + bool move(long offset) override { + long newPosition = mPosition + offset; + if (newPosition < 0) { + return this->seek(0); + } + return this->seek(static_cast<size_t>(newPosition)); + } + + SkStreamAsset* onFork() const override { + // SkStreamSeekable requires overriding this, but it is not called by + // decoders, so does not need a true implementation. Note that a proper + // implementation is fairly straightforward + return nullptr; + } + + // SkStreamAsset overrides + size_t getLength() const override { return mLength; } + +private: + JavaVM* mJvm; + jbyteArray mByteArray; + // Offset in mByteArray. Only used when communicating with Java. + const size_t mOffset; + // Logical position of the SkStream, between 0 and mLength. + size_t mPosition; + const size_t mLength; +}; + +struct release_proc_context { + JavaVM* jvm; + jobject jbyteBuffer; +}; + +std::unique_ptr<SkStream> CreateByteBufferStreamAdaptor(JNIEnv* env, jobject jbyteBuffer, + size_t position, size_t limit) { + JavaVM* jvm; + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK); + + const size_t length = limit - position; + void* addr = env->GetDirectBufferAddress(jbyteBuffer); + if (addr) { + addr = reinterpret_cast<void*>(reinterpret_cast<char*>(addr) + position); + jbyteBuffer = env->NewGlobalRef(jbyteBuffer); + if (!jbyteBuffer) { + return nullptr; + } + + auto* context = new release_proc_context{jvm, jbyteBuffer}; + auto releaseProc = [](const void*, void* context) { + auto* c = reinterpret_cast<release_proc_context*>(context); + JNIEnv* env = requireEnv(c->jvm); + env->DeleteGlobalRef(c->jbyteBuffer); + delete c; + }; + auto data = SkData::MakeWithProc(addr, length, releaseProc, context); + // The new SkMemoryStream will read directly from addr. + return std::unique_ptr<SkStream>(new SkMemoryStream(std::move(data))); + } + + // Non-direct, or direct access is not supported. + return std::unique_ptr<SkStream>(ByteBufferStream::Create(jvm, env, jbyteBuffer, position, + length)); +} + +std::unique_ptr<SkStream> CreateByteArrayStreamAdaptor(JNIEnv* env, jbyteArray array, size_t offset, + size_t length) { + JavaVM* jvm; + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK); + + return std::unique_ptr<SkStream>(ByteArrayStream::Create(jvm, env, array, offset, length)); +} + +int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env) { + jclass byteBuffer_class = FindClassOrDie(env, "java/nio/ByteBuffer"); + gByteBuffer_getMethodID = GetMethodIDOrDie(env, byteBuffer_class, "get", "([BII)Ljava/nio/ByteBuffer;"); + gByteBuffer_setPositionMethodID = GetMethodIDOrDie(env, byteBuffer_class, "position", "(I)Ljava/nio/Buffer;"); + return true; +} diff --git a/libs/hwui/jni/ByteBufferStreamAdaptor.h b/libs/hwui/jni/ByteBufferStreamAdaptor.h new file mode 100644 index 000000000000..367a48fad9b9 --- /dev/null +++ b/libs/hwui/jni/ByteBufferStreamAdaptor.h @@ -0,0 +1,37 @@ +#ifndef _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_ +#define _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_ + +#include <jni.h> +#include <memory> + +class SkStream; + +/** + * Create an adaptor for treating a java.nio.ByteBuffer as an SkStream. + * + * This will special case direct ByteBuffers, but not the case where a byte[] + * can be used directly. For that, use CreateByteArrayStreamAdaptor. + * + * @param jbyteBuffer corresponding to the java ByteBuffer. This method will + * add a global ref. + * @param initialPosition returned by ByteBuffer.position(). Decoding starts + * from here. + * @param limit returned by ByteBuffer.limit(). + * + * Returns null on failure. + */ +std::unique_ptr<SkStream> CreateByteBufferStreamAdaptor(JNIEnv*, jobject jbyteBuffer, + size_t initialPosition, size_t limit); + +/** + * Create an adaptor for treating a Java byte[] as an SkStream. + * + * @param offset into the byte[] of the beginning of the data to use. + * @param length of data to use, starting from offset. + * + * Returns null on failure. + */ +std::unique_ptr<SkStream> CreateByteArrayStreamAdaptor(JNIEnv*, jbyteArray array, size_t offset, + size_t length); + +#endif // _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_ diff --git a/libs/hwui/jni/Camera.cpp b/libs/hwui/jni/Camera.cpp new file mode 100644 index 000000000000..a5e1adf26861 --- /dev/null +++ b/libs/hwui/jni/Camera.cpp @@ -0,0 +1,143 @@ +#include "SkCamera.h" + +#include "GraphicsJNI.h" +#include <hwui/Canvas.h> + +static jfieldID gNativeInstanceFieldID; + +static void Camera_constructor(JNIEnv* env, jobject obj) { + Sk3DView* view = new Sk3DView; + env->SetLongField(obj, gNativeInstanceFieldID, reinterpret_cast<jlong>(view)); +} + +static void Camera_destructor(JNIEnv* env, jobject obj) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* view = reinterpret_cast<Sk3DView*>(viewHandle); + delete view; +} + +static void Camera_save(JNIEnv* env, jobject obj) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle); + v->save(); +} + +static void Camera_restore(JNIEnv* env, jobject obj) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle); + v->restore(); +} + +static void Camera_translate(JNIEnv* env, jobject obj, + jfloat dx, jfloat dy, jfloat dz) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle); + v->translate(dx, dy, dz); +} + +static void Camera_rotateX(JNIEnv* env, jobject obj, jfloat degrees) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle); + v->rotateX(degrees); +} + +static void Camera_rotateY(JNIEnv* env, jobject obj, jfloat degrees) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle); + v->rotateY(degrees); +} + +static void Camera_rotateZ(JNIEnv* env, jobject obj, jfloat degrees) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle); + v->rotateZ(degrees); +} + +static void Camera_rotate(JNIEnv* env, jobject obj, jfloat x, jfloat y, jfloat z) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle); + v->rotateX(x); + v->rotateY(y); + v->rotateZ(z); +} + +static void Camera_setLocation(JNIEnv* env, jobject obj, jfloat x, jfloat y, jfloat z) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle); + v->setCameraLocation(x, y, z); +} + +static jfloat Camera_getLocationX(JNIEnv* env, jobject obj) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle); + return SkScalarToFloat(v->getCameraLocationX()); +} + +static jfloat Camera_getLocationY(JNIEnv* env, jobject obj) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle); + return SkScalarToFloat(v->getCameraLocationY()); +} + +static jfloat Camera_getLocationZ(JNIEnv* env, jobject obj) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle); + return SkScalarToFloat(v->getCameraLocationZ()); +} + +static void Camera_getMatrix(JNIEnv* env, jobject obj, jlong matrixHandle) { + SkMatrix* native_matrix = reinterpret_cast<SkMatrix*>(matrixHandle); + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle); + v->getMatrix(native_matrix); +} + +static void Camera_applyToCanvas(JNIEnv* env, jobject obj, jlong canvasHandle) { + android::Canvas* canvas = reinterpret_cast<android::Canvas*>(canvasHandle); + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle); + SkMatrix matrix; + v->getMatrix(&matrix); + canvas->concat(matrix); +} + +static jfloat Camera_dotWithNormal(JNIEnv* env, jobject obj, + jfloat x, jfloat y, jfloat z) { + jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); + Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle); + SkScalar dot = v->dotWithNormal(x, y, z); + return SkScalarToFloat(dot); +} + +// ---------------------------------------------------------------------------- + +/* + * JNI registration. + */ +static const JNINativeMethod gCameraMethods[] = { + /* name, signature, funcPtr */ + + { "nativeConstructor", "()V", (void*)Camera_constructor }, + { "nativeDestructor", "()V", (void*)Camera_destructor }, + { "save", "()V", (void*)Camera_save }, + { "restore", "()V", (void*)Camera_restore }, + { "translate", "(FFF)V", (void*)Camera_translate }, + { "rotateX", "(F)V", (void*)Camera_rotateX }, + { "rotateY", "(F)V", (void*)Camera_rotateY }, + { "rotateZ", "(F)V", (void*)Camera_rotateZ }, + { "rotate", "(FFF)V", (void*)Camera_rotate }, + { "setLocation", "(FFF)V", (void*)Camera_setLocation }, + { "getLocationX", "()F", (void*)Camera_getLocationX }, + { "getLocationY", "()F", (void*)Camera_getLocationY }, + { "getLocationZ", "()F", (void*)Camera_getLocationZ }, + { "nativeGetMatrix", "(J)V", (void*)Camera_getMatrix }, + { "nativeApplyToCanvas", "(J)V", (void*)Camera_applyToCanvas }, + { "dotWithNormal", "(FFF)F", (void*)Camera_dotWithNormal } +}; + +int register_android_graphics_Camera(JNIEnv* env) { + jclass clazz = android::FindClassOrDie(env, "android/graphics/Camera"); + gNativeInstanceFieldID = android::GetFieldIDOrDie(env, clazz, "native_instance", "J"); + return android::RegisterMethodsOrDie(env, "android/graphics/Camera", gCameraMethods, + NELEM(gCameraMethods)); +} diff --git a/libs/hwui/jni/CanvasProperty.cpp b/libs/hwui/jni/CanvasProperty.cpp new file mode 100644 index 000000000000..684ee23b9fca --- /dev/null +++ b/libs/hwui/jni/CanvasProperty.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 20014 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. + */ + +#include "GraphicsJNI.h" + +#include <hwui/Paint.h> +#include <utils/RefBase.h> +#include <CanvasProperty.h> + +namespace android { + +using namespace uirenderer; + +static jlong createFloat(JNIEnv* env, jobject clazz, jfloat initialValue) { + return reinterpret_cast<jlong>(new CanvasPropertyPrimitive(initialValue)); +} + +static jlong createPaint(JNIEnv* env, jobject clazz, jlong paintPtr) { + const Paint* paint = reinterpret_cast<const Paint*>(paintPtr); + return reinterpret_cast<jlong>(new CanvasPropertyPaint(*paint)); +} + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +static const JNINativeMethod gMethods[] = { + { "nCreateFloat", "(F)J", (void*) createFloat }, + { "nCreatePaint", "(J)J", (void*) createPaint }, +}; + +int register_android_graphics_CanvasProperty(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/CanvasProperty", gMethods, + NELEM(gMethods)); +} + +}; // namespace android diff --git a/libs/hwui/jni/ColorFilter.cpp b/libs/hwui/jni/ColorFilter.cpp new file mode 100644 index 000000000000..cef21f91f3c1 --- /dev/null +++ b/libs/hwui/jni/ColorFilter.cpp @@ -0,0 +1,89 @@ +/* libs/android_runtime/android/graphics/ColorFilter.cpp +** +** Copyright 2006, 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. +*/ + +#include "GraphicsJNI.h" + +#include "SkColorFilter.h" +#include "SkColorMatrixFilter.h" + +namespace android { + +using namespace uirenderer; + +class SkColorFilterGlue { +public: + static void SafeUnref(SkColorFilter* filter) { + SkSafeUnref(filter); + } + + static jlong GetNativeFinalizer(JNIEnv*, jobject) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&SafeUnref)); + } + + static jlong CreateBlendModeFilter(JNIEnv* env, jobject, jint srcColor, jint modeHandle) { + SkBlendMode mode = static_cast<SkBlendMode>(modeHandle); + return reinterpret_cast<jlong>(SkColorFilters::Blend(srcColor, mode).release()); + } + + static jlong CreateLightingFilter(JNIEnv* env, jobject, jint mul, jint add) { + return reinterpret_cast<jlong>(SkColorMatrixFilter::MakeLightingFilter(mul, add).release()); + } + + static jlong CreateColorMatrixFilter(JNIEnv* env, jobject, jfloatArray jarray) { + float matrix[20]; + env->GetFloatArrayRegion(jarray, 0, 20, matrix); + // java biases the translates by 255, so undo that before calling skia + matrix[ 4] *= (1.0f/255); + matrix[ 9] *= (1.0f/255); + matrix[14] *= (1.0f/255); + matrix[19] *= (1.0f/255); + return reinterpret_cast<jlong>(SkColorFilters::Matrix(matrix).release()); + } +}; + +static const JNINativeMethod colorfilter_methods[] = { + {"nativeGetFinalizer", "()J", (void*) SkColorFilterGlue::GetNativeFinalizer } +}; + +static const JNINativeMethod blendmode_methods[] = { + { "native_CreateBlendModeFilter", "(II)J", (void*) SkColorFilterGlue::CreateBlendModeFilter }, +}; + +static const JNINativeMethod lighting_methods[] = { + { "native_CreateLightingFilter", "(II)J", (void*) SkColorFilterGlue::CreateLightingFilter }, +}; + +static const JNINativeMethod colormatrix_methods[] = { + { "nativeColorMatrixFilter", "([F)J", (void*) SkColorFilterGlue::CreateColorMatrixFilter }, +}; + +int register_android_graphics_ColorFilter(JNIEnv* env) { + android::RegisterMethodsOrDie(env, "android/graphics/ColorFilter", colorfilter_methods, + NELEM(colorfilter_methods)); + android::RegisterMethodsOrDie(env, "android/graphics/PorterDuffColorFilter", blendmode_methods, + NELEM(blendmode_methods)); + android::RegisterMethodsOrDie(env, "android/graphics/BlendModeColorFilter", blendmode_methods, + NELEM(blendmode_methods)); + android::RegisterMethodsOrDie(env, "android/graphics/LightingColorFilter", lighting_methods, + NELEM(lighting_methods)); + android::RegisterMethodsOrDie(env, "android/graphics/ColorMatrixColorFilter", + colormatrix_methods, NELEM(colormatrix_methods)); + + return 0; +} + +} diff --git a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp new file mode 100644 index 000000000000..f1c6b29204b2 --- /dev/null +++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp @@ -0,0 +1,306 @@ +#include "CreateJavaOutputStreamAdaptor.h" +#include "SkData.h" +#include "SkMalloc.h" +#include "SkRefCnt.h" +#include "SkStream.h" +#include "SkTypes.h" +#include "Utils.h" + +#include <nativehelper/JNIHelp.h> +#include <log/log.h> +#include <memory> + +static jmethodID gInputStream_readMethodID; +static jmethodID gInputStream_skipMethodID; + +/** + * Wrapper for a Java InputStream. + */ +class JavaInputStreamAdaptor : public SkStream { + JavaInputStreamAdaptor(JavaVM* jvm, jobject js, jbyteArray ar, jint capacity, + bool swallowExceptions) + : fJvm(jvm) + , fJavaInputStream(js) + , fJavaByteArray(ar) + , fCapacity(capacity) + , fBytesRead(0) + , fIsAtEnd(false) + , fSwallowExceptions(swallowExceptions) {} + +public: + static JavaInputStreamAdaptor* Create(JNIEnv* env, jobject js, jbyteArray ar, + bool swallowExceptions) { + JavaVM* jvm; + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK); + + js = env->NewGlobalRef(js); + if (!js) { + return nullptr; + } + + ar = (jbyteArray) env->NewGlobalRef(ar); + if (!ar) { + env->DeleteGlobalRef(js); + return nullptr; + } + + jint capacity = env->GetArrayLength(ar); + return new JavaInputStreamAdaptor(jvm, js, ar, capacity, swallowExceptions); + } + + ~JavaInputStreamAdaptor() override { + auto* env = android::requireEnv(fJvm); + env->DeleteGlobalRef(fJavaInputStream); + env->DeleteGlobalRef(fJavaByteArray); + } + + size_t read(void* buffer, size_t size) override { + auto* env = android::requireEnv(fJvm); + if (!fSwallowExceptions && checkException(env)) { + // Just in case the caller did not clear from a previous exception. + return 0; + } + if (NULL == buffer) { + if (0 == size) { + return 0; + } else { + /* InputStream.skip(n) can return <=0 but still not be at EOF + If we see that value, we need to call read(), which will + block if waiting for more data, or return -1 at EOF + */ + size_t amountSkipped = 0; + do { + size_t amount = this->doSkip(size - amountSkipped, env); + if (0 == amount) { + char tmp; + amount = this->doRead(&tmp, 1, env); + if (0 == amount) { + // if read returned 0, we're at EOF + fIsAtEnd = true; + break; + } + } + amountSkipped += amount; + } while (amountSkipped < size); + return amountSkipped; + } + } + return this->doRead(buffer, size, env); + } + + bool isAtEnd() const override { return fIsAtEnd; } + +private: + size_t doRead(void* buffer, size_t size, JNIEnv* env) { + size_t bytesRead = 0; + // read the bytes + do { + jint requested = 0; + if (size > static_cast<size_t>(fCapacity)) { + requested = fCapacity; + } else { + // This is safe because requested is clamped to (jint) + // fCapacity. + requested = static_cast<jint>(size); + } + + jint n = env->CallIntMethod(fJavaInputStream, + gInputStream_readMethodID, fJavaByteArray, 0, requested); + if (checkException(env)) { + SkDebugf("---- read threw an exception\n"); + return bytesRead; + } + + if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications. + fIsAtEnd = true; + break; // eof + } + + env->GetByteArrayRegion(fJavaByteArray, 0, n, + reinterpret_cast<jbyte*>(buffer)); + if (checkException(env)) { + SkDebugf("---- read:GetByteArrayRegion threw an exception\n"); + return bytesRead; + } + + buffer = (void*)((char*)buffer + n); + bytesRead += n; + size -= n; + fBytesRead += n; + } while (size != 0); + + return bytesRead; + } + + size_t doSkip(size_t size, JNIEnv* env) { + jlong skipped = env->CallLongMethod(fJavaInputStream, + gInputStream_skipMethodID, (jlong)size); + if (checkException(env)) { + SkDebugf("------- skip threw an exception\n"); + return 0; + } + if (skipped < 0) { + skipped = 0; + } + + return (size_t)skipped; + } + + bool checkException(JNIEnv* env) { + if (!env->ExceptionCheck()) { + return false; + } + + env->ExceptionDescribe(); + if (fSwallowExceptions) { + env->ExceptionClear(); + } + + // There is no way to recover from the error, so consider the stream + // to be at the end. + fIsAtEnd = true; + + return true; + } + + JavaVM* fJvm; + jobject fJavaInputStream; + jbyteArray fJavaByteArray; + const jint fCapacity; + size_t fBytesRead; + bool fIsAtEnd; + const bool fSwallowExceptions; +}; + +SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage, + bool swallowExceptions) { + return JavaInputStreamAdaptor::Create(env, stream, storage, swallowExceptions); +} + +static SkMemoryStream* adaptor_to_mem_stream(SkStream* stream) { + SkASSERT(stream != NULL); + size_t bufferSize = 4096; + size_t streamLen = 0; + size_t len; + char* data = (char*)sk_malloc_throw(bufferSize); + + while ((len = stream->read(data + streamLen, + bufferSize - streamLen)) != 0) { + streamLen += len; + if (streamLen == bufferSize) { + bufferSize *= 2; + data = (char*)sk_realloc_throw(data, bufferSize); + } + } + data = (char*)sk_realloc_throw(data, streamLen); + + SkMemoryStream* streamMem = new SkMemoryStream(); + streamMem->setMemoryOwned(data, streamLen); + return streamMem; +} + +SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream, + jbyteArray storage) { + std::unique_ptr<SkStream> adaptor(CreateJavaInputStreamAdaptor(env, stream, storage)); + if (NULL == adaptor.get()) { + return NULL; + } + return adaptor_to_mem_stream(adaptor.get()); +} + +/////////////////////////////////////////////////////////////////////////////// + +static jmethodID gOutputStream_writeMethodID; +static jmethodID gOutputStream_flushMethodID; + +class SkJavaOutputStream : public SkWStream { +public: + SkJavaOutputStream(JNIEnv* env, jobject stream, jbyteArray storage) + : fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage), fBytesWritten(0) { + fCapacity = env->GetArrayLength(storage); + } + + virtual size_t bytesWritten() const { + return fBytesWritten; + } + + virtual bool write(const void* buffer, size_t size) { + JNIEnv* env = fEnv; + jbyteArray storage = fJavaByteArray; + + while (size > 0) { + jint requested = 0; + if (size > static_cast<size_t>(fCapacity)) { + requested = fCapacity; + } else { + // This is safe because requested is clamped to (jint) + // fCapacity. + requested = static_cast<jint>(size); + } + + env->SetByteArrayRegion(storage, 0, requested, + reinterpret_cast<const jbyte*>(buffer)); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + SkDebugf("--- write:SetByteArrayElements threw an exception\n"); + return false; + } + + fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_writeMethodID, + storage, 0, requested); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + SkDebugf("------- write threw an exception\n"); + return false; + } + + buffer = (void*)((char*)buffer + requested); + size -= requested; + fBytesWritten += requested; + } + return true; + } + + virtual void flush() { + fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_flushMethodID); + } + +private: + JNIEnv* fEnv; + jobject fJavaOutputStream; // the caller owns this object + jbyteArray fJavaByteArray; // the caller owns this object + jint fCapacity; + size_t fBytesWritten; +}; + +SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, + jbyteArray storage) { + return new SkJavaOutputStream(env, stream, storage); +} + +static jclass findClassCheck(JNIEnv* env, const char classname[]) { + jclass clazz = env->FindClass(classname); + SkASSERT(!env->ExceptionCheck()); + return clazz; +} + +static jmethodID getMethodIDCheck(JNIEnv* env, jclass clazz, + const char methodname[], const char type[]) { + jmethodID id = env->GetMethodID(clazz, methodname, type); + SkASSERT(!env->ExceptionCheck()); + return id; +} + +int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env) { + jclass inputStream_Clazz = findClassCheck(env, "java/io/InputStream"); + gInputStream_readMethodID = getMethodIDCheck(env, inputStream_Clazz, "read", "([BII)I"); + gInputStream_skipMethodID = getMethodIDCheck(env, inputStream_Clazz, "skip", "(J)J"); + + jclass outputStream_Clazz = findClassCheck(env, "java/io/OutputStream"); + gOutputStream_writeMethodID = getMethodIDCheck(env, outputStream_Clazz, "write", "([BII)V"); + gOutputStream_flushMethodID = getMethodIDCheck(env, outputStream_Clazz, "flush", "()V"); + + return 0; +} diff --git a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.h b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.h new file mode 100644 index 000000000000..849418da01a1 --- /dev/null +++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.h @@ -0,0 +1,42 @@ +#ifndef _ANDROID_GRAPHICS_CREATE_JAVA_OUTPUT_STREAM_ADAPTOR_H_ +#define _ANDROID_GRAPHICS_CREATE_JAVA_OUTPUT_STREAM_ADAPTOR_H_ + +#include "jni.h" + +class SkMemoryStream; +class SkStream; +class SkStreamRewindable; +class SkWStream; + +/** + * Return an adaptor from a Java InputStream to an SkStream. + * Does not support rewind. + * @param env JNIEnv object. + * @param stream Pointer to Java InputStream. + * @param storage Java byte array for retrieving data from the + * Java InputStream. + * @param swallowExceptions Whether to call ExceptionClear() after + * an Exception is thrown. If false, it is up to the client to + * clear or propagate the exception. + * @return SkStream Simple subclass of SkStream which supports its + * basic methods like reading. Only valid until the calling + * function returns, since the Java InputStream is not managed + * by the SkStream. + */ +SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage, + bool swallowExceptions = true); + +/** + * Copy a Java InputStream. The result will be rewindable. + * @param env JNIEnv object. + * @param stream Pointer to Java InputStream. + * @param storage Java byte array for retrieving data from the + * Java InputStream. + * @return SkStreamRewindable The data in stream will be copied + * to a new SkStreamRewindable. + */ +SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream, jbyteArray storage); + +SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage); + +#endif // _ANDROID_GRAPHICS_CREATE_JAVA_OUTPUT_STREAM_ADAPTOR_H_ diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp new file mode 100644 index 000000000000..a2fef1e19328 --- /dev/null +++ b/libs/hwui/jni/FontFamily.cpp @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2014 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. + */ + +#undef LOG_TAG +#define LOG_TAG "Minikin" + +#include "SkData.h" +#include "SkFontMgr.h" +#include "SkRefCnt.h" +#include "SkTypeface.h" +#include "GraphicsJNI.h" +#include <nativehelper/ScopedPrimitiveArray.h> +#include <nativehelper/ScopedUtfChars.h> +#include "Utils.h" +#include "FontUtils.h" + +#include <hwui/MinikinSkia.h> +#include <hwui/Typeface.h> +#include <minikin/FontFamily.h> +#include <minikin/LocaleList.h> +#include <ui/FatVector.h> + +#include <memory> + +namespace android { + +struct NativeFamilyBuilder { + NativeFamilyBuilder(uint32_t langId, int variant) + : langId(langId), variant(static_cast<minikin::FamilyVariant>(variant)) {} + uint32_t langId; + minikin::FamilyVariant variant; + std::vector<minikin::Font> fonts; + std::vector<minikin::FontVariation> axes; +}; + +static inline NativeFamilyBuilder* toNativeBuilder(jlong ptr) { + return reinterpret_cast<NativeFamilyBuilder*>(ptr); +} + +static inline FontFamilyWrapper* toFamily(jlong ptr) { + return reinterpret_cast<FontFamilyWrapper*>(ptr); +} + +template<typename Ptr> static inline jlong toJLong(Ptr ptr) { + return reinterpret_cast<jlong>(ptr); +} + +static jlong FontFamily_initBuilder(JNIEnv* env, jobject clazz, jstring langs, jint variant) { + NativeFamilyBuilder* builder; + if (langs != nullptr) { + ScopedUtfChars str(env, langs); + builder = new NativeFamilyBuilder(minikin::registerLocaleList(str.c_str()), variant); + } else { + builder = new NativeFamilyBuilder(minikin::registerLocaleList(""), variant); + } + return toJLong(builder); +} + +static jlong FontFamily_create(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr) { + if (builderPtr == 0) { + return 0; + } + NativeFamilyBuilder* builder = toNativeBuilder(builderPtr); + if (builder->fonts.empty()) { + return 0; + } + std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>( + builder->langId, builder->variant, std::move(builder->fonts), + true /* isCustomFallback */); + if (family->getCoverage().length() == 0) { + return 0; + } + return toJLong(new FontFamilyWrapper(std::move(family))); +} + +static void releaseBuilder(jlong builderPtr) { + delete toNativeBuilder(builderPtr); +} + +static jlong FontFamily_getBuilderReleaseFunc(CRITICAL_JNI_PARAMS) { + return toJLong(&releaseBuilder); +} + +static void releaseFamily(jlong familyPtr) { + delete toFamily(familyPtr); +} + +static jlong FontFamily_getFamilyReleaseFunc(CRITICAL_JNI_PARAMS) { + return toJLong(&releaseFamily); +} + +static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, int ttcIndex, + jint weight, jint italic) { + FatVector<SkFontArguments::Axis, 2> skiaAxes; + for (const auto& axis : builder->axes) { + skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value}); + } + + const size_t fontSize = data->size(); + const void* fontPtr = data->data(); + std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data))); + + SkFontArguments params; + params.setCollectionIndex(ttcIndex); + params.setAxes(skiaAxes.data(), skiaAxes.size()); + + sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); + sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), params)); + if (face == NULL) { + ALOGE("addFont failed to create font, invalid request"); + builder->axes.clear(); + return false; + } + std::shared_ptr<minikin::MinikinFont> minikinFont = + std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, "", ttcIndex, + builder->axes); + minikin::Font::Builder fontBuilder(minikinFont); + + if (weight != RESOLVE_BY_FONT_TABLE) { + fontBuilder.setWeight(weight); + } + if (italic != RESOLVE_BY_FONT_TABLE) { + fontBuilder.setSlant(static_cast<minikin::FontStyle::Slant>(italic != 0)); + } + builder->fonts.push_back(fontBuilder.build()); + builder->axes.clear(); + return true; +} + +static void release_global_ref(const void* /*data*/, void* context) { + JNIEnv* env = GraphicsJNI::getJNIEnv(); + bool needToAttach = (env == NULL); + if (needToAttach) { + env = GraphicsJNI::attachJNIEnv("release_font_data"); + if (env == nullptr) { + ALOGE("failed to attach to thread to release global ref."); + return; + } + } + + jobject obj = reinterpret_cast<jobject>(context); + env->DeleteGlobalRef(obj); + + if (needToAttach) { + GraphicsJNI::detachJNIEnv(); + } +} + +static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong builderPtr, jobject bytebuf, + jint ttcIndex, jint weight, jint isItalic) { + NPE_CHECK_RETURN_ZERO(env, bytebuf); + NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr); + const void* fontPtr = env->GetDirectBufferAddress(bytebuf); + if (fontPtr == NULL) { + ALOGE("addFont failed to create font, buffer invalid"); + builder->axes.clear(); + return false; + } + jlong fontSize = env->GetDirectBufferCapacity(bytebuf); + if (fontSize < 0) { + ALOGE("addFont failed to create font, buffer size invalid"); + builder->axes.clear(); + return false; + } + jobject fontRef = MakeGlobalRefOrDie(env, bytebuf); + sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize, + release_global_ref, reinterpret_cast<void*>(fontRef))); + return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); +} + +static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong builderPtr, + jobject font, jint ttcIndex, jint weight, jint isItalic) { + NPE_CHECK_RETURN_ZERO(env, font); + NativeFamilyBuilder* builder = toNativeBuilder(builderPtr); + const void* fontPtr = env->GetDirectBufferAddress(font); + if (fontPtr == NULL) { + ALOGE("addFont failed to create font, buffer invalid"); + builder->axes.clear(); + return false; + } + jlong fontSize = env->GetDirectBufferCapacity(font); + if (fontSize < 0) { + ALOGE("addFont failed to create font, buffer size invalid"); + builder->axes.clear(); + return false; + } + jobject fontRef = MakeGlobalRefOrDie(env, font); + sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize, + release_global_ref, reinterpret_cast<void*>(fontRef))); + return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); +} + +static void FontFamily_addAxisValue(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr, jint tag, jfloat value) { + NativeFamilyBuilder* builder = toNativeBuilder(builderPtr); + builder->axes.push_back({static_cast<minikin::AxisTag>(tag), value}); +} + +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gFontFamilyMethods[] = { + { "nInitBuilder", "(Ljava/lang/String;I)J", (void*)FontFamily_initBuilder }, + { "nCreateFamily", "(J)J", (void*)FontFamily_create }, + { "nGetBuilderReleaseFunc", "()J", (void*)FontFamily_getBuilderReleaseFunc }, + { "nGetFamilyReleaseFunc", "()J", (void*)FontFamily_getFamilyReleaseFunc }, + { "nAddFont", "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont }, + { "nAddFontWeightStyle", "(JLjava/nio/ByteBuffer;III)Z", + (void*)FontFamily_addFontWeightStyle }, + { "nAddAxisValue", "(JIF)V", (void*)FontFamily_addAxisValue }, +}; + +int register_android_graphics_FontFamily(JNIEnv* env) +{ + int err = RegisterMethodsOrDie(env, "android/graphics/FontFamily", gFontFamilyMethods, + NELEM(gFontFamilyMethods)); + + init_FontUtils(env); + return err; +} + +} diff --git a/libs/hwui/jni/FontUtils.cpp b/libs/hwui/jni/FontUtils.cpp new file mode 100644 index 000000000000..654c5fdf6528 --- /dev/null +++ b/libs/hwui/jni/FontUtils.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 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. + */ + +#include "FontUtils.h" + +#include "graphics_jni_helpers.h" + +namespace android { +namespace { + +static struct { + jmethodID mGet; + jmethodID mSize; +} gListClassInfo; + +static struct { + jfieldID mTag; + jfieldID mStyleValue; +} gAxisClassInfo; + +} // namespace + +jint ListHelper::size() const { + return mEnv->CallIntMethod(mList, gListClassInfo.mSize); +} + +jobject ListHelper::get(jint index) const { + return mEnv->CallObjectMethod(mList, gListClassInfo.mGet, index); +} + +jint AxisHelper::getTag() const { + return mEnv->GetIntField(mAxis, gAxisClassInfo.mTag); +} + +jfloat AxisHelper::getStyleValue() const { + return mEnv->GetFloatField(mAxis, gAxisClassInfo.mStyleValue); +} + +void init_FontUtils(JNIEnv* env) { + jclass listClass = FindClassOrDie(env, "java/util/List"); + gListClassInfo.mGet = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;"); + gListClassInfo.mSize = GetMethodIDOrDie(env, listClass, "size", "()I"); + + jclass axisClass = FindClassOrDie(env, "android/graphics/fonts/FontVariationAxis"); + gAxisClassInfo.mTag = GetFieldIDOrDie(env, axisClass, "mTag", "I"); + gAxisClassInfo.mStyleValue = GetFieldIDOrDie(env, axisClass, "mStyleValue", "F"); +} + +} // namespace android diff --git a/libs/hwui/jni/FontUtils.h b/libs/hwui/jni/FontUtils.h new file mode 100644 index 000000000000..b36b4e60e33a --- /dev/null +++ b/libs/hwui/jni/FontUtils.h @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#ifndef _ANDROID_GRAPHICS_FONT_UTILS_H_ +#define _ANDROID_GRAPHICS_FONT_UTILS_H_ + +#include <jni.h> +#include <memory> + +#include <minikin/Font.h> + +namespace minikin { +class FontFamily; +} // namespace minikin + +namespace android { + +struct FontFamilyWrapper { + explicit FontFamilyWrapper(std::shared_ptr<minikin::FontFamily>&& family) : family(family) {} + std::shared_ptr<minikin::FontFamily> family; +}; + +struct FontWrapper { + FontWrapper(minikin::Font&& font) : font(std::move(font)) {} + minikin::Font font; +}; + +// Utility wrapper for java.util.List +class ListHelper { +public: + ListHelper(JNIEnv* env, jobject list) : mEnv(env), mList(list) {} + + jint size() const; + jobject get(jint index) const; + +private: + JNIEnv* mEnv; + jobject mList; +}; + +// Utility wrapper for android.graphics.FontConfig$Axis +class AxisHelper { +public: + AxisHelper(JNIEnv* env, jobject axis) : mEnv(env), mAxis(axis) {} + + jint getTag() const; + jfloat getStyleValue() const; + +private: + JNIEnv* mEnv; + jobject mAxis; +}; + +void init_FontUtils(JNIEnv* env); + +}; // namespace android + +#endif // _ANDROID_GRAPHICS_FONT_UTILS_H_ diff --git a/libs/hwui/jni/GIFMovie.cpp b/libs/hwui/jni/GIFMovie.cpp new file mode 100644 index 000000000000..f84a4bd09073 --- /dev/null +++ b/libs/hwui/jni/GIFMovie.cpp @@ -0,0 +1,447 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#include "Movie.h" +#include "SkColor.h" +#include "SkColorPriv.h" +#include "SkStream.h" +#include "SkTemplates.h" +#include "SkUtils.h" + +#include "gif_lib.h" + +#if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0) +#define DGifCloseFile(a, b) DGifCloseFile(a) +#endif + +class GIFMovie : public Movie { +public: + explicit GIFMovie(SkStream* stream); + virtual ~GIFMovie(); + +protected: + virtual bool onGetInfo(Info*); + virtual bool onSetTime(SkMSec); + virtual bool onGetBitmap(SkBitmap*); + +private: + GifFileType* fGIF; + int fCurrIndex; + int fLastDrawIndex; + SkBitmap fBackup; + SkColor fPaintingColor; +}; + +static int Decode(GifFileType* fileType, GifByteType* out, int size) { + SkStream* stream = (SkStream*) fileType->UserData; + return (int) stream->read(out, size); +} + +GIFMovie::GIFMovie(SkStream* stream) +{ +#if GIFLIB_MAJOR < 5 + fGIF = DGifOpen( stream, Decode ); +#else + fGIF = DGifOpen( stream, Decode, nullptr ); +#endif + if (nullptr == fGIF) + return; + + if (DGifSlurp(fGIF) != GIF_OK) + { + DGifCloseFile(fGIF, nullptr); + fGIF = nullptr; + } + fCurrIndex = -1; + fLastDrawIndex = -1; + fPaintingColor = SkPackARGB32(0, 0, 0, 0); +} + +GIFMovie::~GIFMovie() +{ + if (fGIF) + DGifCloseFile(fGIF, nullptr); +} + +static SkMSec savedimage_duration(const SavedImage* image) +{ + for (int j = 0; j < image->ExtensionBlockCount; j++) + { + if (image->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE) + { + SkASSERT(image->ExtensionBlocks[j].ByteCount >= 4); + const uint8_t* b = (const uint8_t*)image->ExtensionBlocks[j].Bytes; + return ((b[2] << 8) | b[1]) * 10; + } + } + return 0; +} + +bool GIFMovie::onGetInfo(Info* info) +{ + if (nullptr == fGIF) + return false; + + SkMSec dur = 0; + for (int i = 0; i < fGIF->ImageCount; i++) + dur += savedimage_duration(&fGIF->SavedImages[i]); + + info->fDuration = dur; + info->fWidth = fGIF->SWidth; + info->fHeight = fGIF->SHeight; + info->fIsOpaque = false; // how to compute? + return true; +} + +bool GIFMovie::onSetTime(SkMSec time) +{ + if (nullptr == fGIF) + return false; + + SkMSec dur = 0; + for (int i = 0; i < fGIF->ImageCount; i++) + { + dur += savedimage_duration(&fGIF->SavedImages[i]); + if (dur >= time) + { + fCurrIndex = i; + return fLastDrawIndex != fCurrIndex; + } + } + fCurrIndex = fGIF->ImageCount - 1; + return true; +} + +static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObject* cmap, + int transparent, int width) +{ + for (; width > 0; width--, src++, dst++) { + if (*src != transparent && *src < cmap->ColorCount) { + const GifColorType& col = cmap->Colors[*src]; + *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue); + } + } +} + +#if GIFLIB_MAJOR < 5 +static void copyInterlaceGroup(SkBitmap* bm, const unsigned char*& src, + const ColorMapObject* cmap, int transparent, int copyWidth, + int copyHeight, const GifImageDesc& imageDesc, int rowStep, + int startRow) +{ + int row; + // every 'rowStep'th row, starting with row 'startRow' + for (row = startRow; row < copyHeight; row += rowStep) { + uint32_t* dst = bm->getAddr32(imageDesc.Left, imageDesc.Top + row); + copyLine(dst, src, cmap, transparent, copyWidth); + src += imageDesc.Width; + } + + // pad for rest height + src += imageDesc.Width * ((imageDesc.Height - row + rowStep - 1) / rowStep); +} + +static void blitInterlace(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap, + int transparent) +{ + int width = bm->width(); + int height = bm->height(); + GifWord copyWidth = frame->ImageDesc.Width; + if (frame->ImageDesc.Left + copyWidth > width) { + copyWidth = width - frame->ImageDesc.Left; + } + + GifWord copyHeight = frame->ImageDesc.Height; + if (frame->ImageDesc.Top + copyHeight > height) { + copyHeight = height - frame->ImageDesc.Top; + } + + // deinterlace + const unsigned char* src = (unsigned char*)frame->RasterBits; + + // group 1 - every 8th row, starting with row 0 + copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 0); + + // group 2 - every 8th row, starting with row 4 + copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 4); + + // group 3 - every 4th row, starting with row 2 + copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 4, 2); + + copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 2, 1); +} +#endif + +static void blitNormal(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap, + int transparent) +{ + int width = bm->width(); + int height = bm->height(); + const unsigned char* src = (unsigned char*)frame->RasterBits; + uint32_t* dst = bm->getAddr32(frame->ImageDesc.Left, frame->ImageDesc.Top); + GifWord copyWidth = frame->ImageDesc.Width; + if (frame->ImageDesc.Left + copyWidth > width) { + copyWidth = width - frame->ImageDesc.Left; + } + + GifWord copyHeight = frame->ImageDesc.Height; + if (frame->ImageDesc.Top + copyHeight > height) { + copyHeight = height - frame->ImageDesc.Top; + } + + for (; copyHeight > 0; copyHeight--) { + copyLine(dst, src, cmap, transparent, copyWidth); + src += frame->ImageDesc.Width; + dst += width; + } +} + +static void fillRect(SkBitmap* bm, GifWord left, GifWord top, GifWord width, GifWord height, + uint32_t col) +{ + int bmWidth = bm->width(); + int bmHeight = bm->height(); + uint32_t* dst = bm->getAddr32(left, top); + GifWord copyWidth = width; + if (left + copyWidth > bmWidth) { + copyWidth = bmWidth - left; + } + + GifWord copyHeight = height; + if (top + copyHeight > bmHeight) { + copyHeight = bmHeight - top; + } + + for (; copyHeight > 0; copyHeight--) { + sk_memset32(dst, col, copyWidth); + dst += bmWidth; + } +} + +static void drawFrame(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap) +{ + int transparent = -1; + + for (int i = 0; i < frame->ExtensionBlockCount; ++i) { + ExtensionBlock* eb = frame->ExtensionBlocks + i; + if (eb->Function == GRAPHICS_EXT_FUNC_CODE && + eb->ByteCount == 4) { + bool has_transparency = ((eb->Bytes[0] & 1) == 1); + if (has_transparency) { + transparent = (unsigned char)eb->Bytes[3]; + } + } + } + + if (frame->ImageDesc.ColorMap != nullptr) { + // use local color table + cmap = frame->ImageDesc.ColorMap; + } + + if (cmap == nullptr || cmap->ColorCount != (1 << cmap->BitsPerPixel)) { + SkDEBUGFAIL("bad colortable setup"); + return; + } + +#if GIFLIB_MAJOR < 5 + // before GIFLIB 5, de-interlacing wasn't done by library at load time + if (frame->ImageDesc.Interlace) { + blitInterlace(bm, frame, cmap, transparent); + return; + } +#endif + + blitNormal(bm, frame, cmap, transparent); +} + +static bool checkIfWillBeCleared(const SavedImage* frame) +{ + for (int i = 0; i < frame->ExtensionBlockCount; ++i) { + ExtensionBlock* eb = frame->ExtensionBlocks + i; + if (eb->Function == GRAPHICS_EXT_FUNC_CODE && + eb->ByteCount == 4) { + // check disposal method + int disposal = ((eb->Bytes[0] >> 2) & 7); + if (disposal == 2 || disposal == 3) { + return true; + } + } + } + return false; +} + +static void getTransparencyAndDisposalMethod(const SavedImage* frame, bool* trans, int* disposal) +{ + *trans = false; + *disposal = 0; + for (int i = 0; i < frame->ExtensionBlockCount; ++i) { + ExtensionBlock* eb = frame->ExtensionBlocks + i; + if (eb->Function == GRAPHICS_EXT_FUNC_CODE && + eb->ByteCount == 4) { + *trans = ((eb->Bytes[0] & 1) == 1); + *disposal = ((eb->Bytes[0] >> 2) & 7); + } + } +} + +// return true if area of 'target' is completely covers area of 'covered' +static bool checkIfCover(const SavedImage* target, const SavedImage* covered) +{ + if (target->ImageDesc.Left <= covered->ImageDesc.Left + && covered->ImageDesc.Left + covered->ImageDesc.Width <= + target->ImageDesc.Left + target->ImageDesc.Width + && target->ImageDesc.Top <= covered->ImageDesc.Top + && covered->ImageDesc.Top + covered->ImageDesc.Height <= + target->ImageDesc.Top + target->ImageDesc.Height) { + return true; + } + return false; +} + +static void disposeFrameIfNeeded(SkBitmap* bm, const SavedImage* cur, const SavedImage* next, + SkBitmap* backup, SkColor color) +{ + // We can skip disposal process if next frame is not transparent + // and completely covers current area + bool curTrans; + int curDisposal; + getTransparencyAndDisposalMethod(cur, &curTrans, &curDisposal); + bool nextTrans; + int nextDisposal; + getTransparencyAndDisposalMethod(next, &nextTrans, &nextDisposal); + if ((curDisposal == 2 || curDisposal == 3) + && (nextTrans || !checkIfCover(next, cur))) { + switch (curDisposal) { + // restore to background color + // -> 'background' means background under this image. + case 2: + fillRect(bm, cur->ImageDesc.Left, cur->ImageDesc.Top, + cur->ImageDesc.Width, cur->ImageDesc.Height, + color); + break; + + // restore to previous + case 3: + bm->swap(*backup); + break; + } + } + + // Save current image if next frame's disposal method == 3 + if (nextDisposal == 3) { + const uint32_t* src = bm->getAddr32(0, 0); + uint32_t* dst = backup->getAddr32(0, 0); + int cnt = bm->width() * bm->height(); + memcpy(dst, src, cnt*sizeof(uint32_t)); + } +} + +bool GIFMovie::onGetBitmap(SkBitmap* bm) +{ + const GifFileType* gif = fGIF; + if (nullptr == gif) + return false; + + if (gif->ImageCount < 1) { + return false; + } + + const int width = gif->SWidth; + const int height = gif->SHeight; + if (width <= 0 || height <= 0) { + return false; + } + + // no need to draw + if (fLastDrawIndex >= 0 && fLastDrawIndex == fCurrIndex) { + return true; + } + + int startIndex = fLastDrawIndex + 1; + if (fLastDrawIndex < 0 || !bm->readyToDraw()) { + // first time + + startIndex = 0; + + // create bitmap + if (!bm->tryAllocN32Pixels(width, height)) { + return false; + } + // create bitmap for backup + if (!fBackup.tryAllocN32Pixels(width, height)) { + return false; + } + } else if (startIndex > fCurrIndex) { + // rewind to 1st frame for repeat + startIndex = 0; + } + + int lastIndex = fCurrIndex; + if (lastIndex < 0) { + // first time + lastIndex = 0; + } else if (lastIndex > fGIF->ImageCount - 1) { + // this block must not be reached. + lastIndex = fGIF->ImageCount - 1; + } + + SkColor bgColor = SkPackARGB32(0, 0, 0, 0); + if (gif->SColorMap != nullptr && gif->SBackGroundColor < gif->SColorMap->ColorCount) { + const GifColorType& col = gif->SColorMap->Colors[gif->SBackGroundColor]; + bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue); + } + + // draw each frames - not intelligent way + for (int i = startIndex; i <= lastIndex; i++) { + const SavedImage* cur = &fGIF->SavedImages[i]; + if (i == 0) { + bool trans; + int disposal; + getTransparencyAndDisposalMethod(cur, &trans, &disposal); + if (!trans && gif->SColorMap != nullptr) { + fPaintingColor = bgColor; + } else { + fPaintingColor = SkColorSetARGB(0, 0, 0, 0); + } + + bm->eraseColor(fPaintingColor); + fBackup.eraseColor(fPaintingColor); + } else { + // Dispose previous frame before move to next frame. + const SavedImage* prev = &fGIF->SavedImages[i-1]; + disposeFrameIfNeeded(bm, prev, cur, &fBackup, fPaintingColor); + } + + // Draw frame + // We can skip this process if this index is not last and disposal + // method == 2 or method == 3 + if (i == lastIndex || !checkIfWillBeCleared(cur)) { + drawFrame(bm, cur, gif->SColorMap); + } + } + + // save index + fLastDrawIndex = lastIndex; + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +Movie* Movie::DecodeStream(SkStreamRewindable* stream) { + char buf[GIF_STAMP_LEN]; + if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { + if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 || + memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 || + memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) { + // must rewind here, since our construct wants to re-read the data + stream->rewind(); + return new GIFMovie(stream); + } + } + return nullptr; +} diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp new file mode 100644 index 000000000000..f76ecb4c9c8a --- /dev/null +++ b/libs/hwui/jni/Graphics.cpp @@ -0,0 +1,768 @@ +#undef LOG_TAG +#define LOG_TAG "GraphicsJNI" + +#include <assert.h> +#include <unistd.h> + +#include "jni.h" +#include <nativehelper/JNIHelp.h> +#include "GraphicsJNI.h" + +#include "SkCanvas.h" +#include "SkMath.h" +#include "SkRegion.h" +#include <cutils/ashmem.h> +#include <hwui/Canvas.h> + +using namespace android; + +/*static*/ JavaVM* GraphicsJNI::mJavaVM = nullptr; + +void GraphicsJNI::setJavaVM(JavaVM* javaVM) { + mJavaVM = javaVM; +} + +/** return a pointer to the JNIEnv for this thread */ +JNIEnv* GraphicsJNI::getJNIEnv() { + assert(mJavaVM != nullptr); + JNIEnv* env; + if (mJavaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + return nullptr; + } + return env; +} + +/** create a JNIEnv* for this thread or assert if one already exists */ +JNIEnv* GraphicsJNI::attachJNIEnv(const char* envName) { + assert(getJNIEnv() == nullptr); + JNIEnv* env = nullptr; + JavaVMAttachArgs args = { JNI_VERSION_1_4, envName, NULL }; + int result = mJavaVM->AttachCurrentThread(&env, (void*) &args); + if (result != JNI_OK) { + ALOGE("thread attach failed: %#x", result); + } + return env; +} + +/** detach the current thread from the JavaVM */ +void GraphicsJNI::detachJNIEnv() { + assert(mJavaVM != nullptr); + mJavaVM->DetachCurrentThread(); +} + +void doThrowNPE(JNIEnv* env) { + jniThrowNullPointerException(env, NULL); +} + +void doThrowAIOOBE(JNIEnv* env) { + jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL); +} + +void doThrowRE(JNIEnv* env, const char* msg) { + jniThrowRuntimeException(env, msg); +} + +void doThrowIAE(JNIEnv* env, const char* msg) { + jniThrowException(env, "java/lang/IllegalArgumentException", msg); +} + +void doThrowISE(JNIEnv* env, const char* msg) { + jniThrowException(env, "java/lang/IllegalStateException", msg); +} + +void doThrowOOME(JNIEnv* env, const char* msg) { + jniThrowException(env, "java/lang/OutOfMemoryError", msg); +} + +void doThrowIOE(JNIEnv* env, const char* msg) { + jniThrowException(env, "java/io/IOException", msg); +} + +bool GraphicsJNI::hasException(JNIEnv *env) { + if (env->ExceptionCheck() != 0) { + ALOGE("*** Uncaught exception returned from Java call!\n"); + env->ExceptionDescribe(); + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +AutoJavaFloatArray::AutoJavaFloatArray(JNIEnv* env, jfloatArray array, + int minLength, JNIAccess access) +: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { + ALOG_ASSERT(env); + if (array) { + fLen = env->GetArrayLength(array); + if (fLen < minLength) { + LOG_ALWAYS_FATAL("bad length"); + } + fPtr = env->GetFloatArrayElements(array, NULL); + } + fReleaseMode = (access == kRO_JNIAccess) ? JNI_ABORT : 0; +} + +AutoJavaFloatArray::~AutoJavaFloatArray() { + if (fPtr) { + fEnv->ReleaseFloatArrayElements(fArray, fPtr, fReleaseMode); + } +} + +AutoJavaIntArray::AutoJavaIntArray(JNIEnv* env, jintArray array, + int minLength) +: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { + ALOG_ASSERT(env); + if (array) { + fLen = env->GetArrayLength(array); + if (fLen < minLength) { + LOG_ALWAYS_FATAL("bad length"); + } + fPtr = env->GetIntArrayElements(array, NULL); + } +} + +AutoJavaIntArray::~AutoJavaIntArray() { + if (fPtr) { + fEnv->ReleaseIntArrayElements(fArray, fPtr, 0); + } +} + +AutoJavaShortArray::AutoJavaShortArray(JNIEnv* env, jshortArray array, + int minLength, JNIAccess access) +: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { + ALOG_ASSERT(env); + if (array) { + fLen = env->GetArrayLength(array); + if (fLen < minLength) { + LOG_ALWAYS_FATAL("bad length"); + } + fPtr = env->GetShortArrayElements(array, NULL); + } + fReleaseMode = (access == kRO_JNIAccess) ? JNI_ABORT : 0; +} + +AutoJavaShortArray::~AutoJavaShortArray() { + if (fPtr) { + fEnv->ReleaseShortArrayElements(fArray, fPtr, fReleaseMode); + } +} + +AutoJavaByteArray::AutoJavaByteArray(JNIEnv* env, jbyteArray array, + int minLength) +: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { + ALOG_ASSERT(env); + if (array) { + fLen = env->GetArrayLength(array); + if (fLen < minLength) { + LOG_ALWAYS_FATAL("bad length"); + } + fPtr = env->GetByteArrayElements(array, NULL); + } +} + +AutoJavaByteArray::~AutoJavaByteArray() { + if (fPtr) { + fEnv->ReleaseByteArrayElements(fArray, fPtr, 0); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static jclass gRect_class; +static jfieldID gRect_leftFieldID; +static jfieldID gRect_topFieldID; +static jfieldID gRect_rightFieldID; +static jfieldID gRect_bottomFieldID; + +static jclass gRectF_class; +static jfieldID gRectF_leftFieldID; +static jfieldID gRectF_topFieldID; +static jfieldID gRectF_rightFieldID; +static jfieldID gRectF_bottomFieldID; + +static jclass gPoint_class; +static jfieldID gPoint_xFieldID; +static jfieldID gPoint_yFieldID; + +static jclass gPointF_class; +static jfieldID gPointF_xFieldID; +static jfieldID gPointF_yFieldID; + +static jclass gBitmapConfig_class; +static jfieldID gBitmapConfig_nativeInstanceID; +static jmethodID gBitmapConfig_nativeToConfigMethodID; + +static jclass gBitmapRegionDecoder_class; +static jmethodID gBitmapRegionDecoder_constructorMethodID; + +static jclass gCanvas_class; +static jfieldID gCanvas_nativeInstanceID; + +static jclass gPicture_class; +static jfieldID gPicture_nativeInstanceID; + +static jclass gRegion_class; +static jfieldID gRegion_nativeInstanceID; +static jmethodID gRegion_constructorMethodID; + +static jclass gByte_class; +static jobject gVMRuntime; +static jclass gVMRuntime_class; +static jmethodID gVMRuntime_newNonMovableArray; +static jmethodID gVMRuntime_addressOf; + +static jclass gColorSpace_class; +static jmethodID gColorSpace_getMethodID; +static jmethodID gColorSpace_matchMethodID; + +static jclass gColorSpaceRGB_class; +static jmethodID gColorSpaceRGB_constructorMethodID; + +static jclass gColorSpace_Named_class; +static jfieldID gColorSpace_Named_sRGBFieldID; +static jfieldID gColorSpace_Named_ExtendedSRGBFieldID; +static jfieldID gColorSpace_Named_LinearSRGBFieldID; +static jfieldID gColorSpace_Named_LinearExtendedSRGBFieldID; + +static jclass gTransferParameters_class; +static jmethodID gTransferParameters_constructorMethodID; + +/////////////////////////////////////////////////////////////////////////////// + +void GraphicsJNI::get_jrect(JNIEnv* env, jobject obj, int* L, int* T, int* R, int* B) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gRect_class)); + + *L = env->GetIntField(obj, gRect_leftFieldID); + *T = env->GetIntField(obj, gRect_topFieldID); + *R = env->GetIntField(obj, gRect_rightFieldID); + *B = env->GetIntField(obj, gRect_bottomFieldID); +} + +void GraphicsJNI::set_jrect(JNIEnv* env, jobject obj, int L, int T, int R, int B) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gRect_class)); + + env->SetIntField(obj, gRect_leftFieldID, L); + env->SetIntField(obj, gRect_topFieldID, T); + env->SetIntField(obj, gRect_rightFieldID, R); + env->SetIntField(obj, gRect_bottomFieldID, B); +} + +SkIRect* GraphicsJNI::jrect_to_irect(JNIEnv* env, jobject obj, SkIRect* ir) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gRect_class)); + + ir->setLTRB(env->GetIntField(obj, gRect_leftFieldID), + env->GetIntField(obj, gRect_topFieldID), + env->GetIntField(obj, gRect_rightFieldID), + env->GetIntField(obj, gRect_bottomFieldID)); + return ir; +} + +void GraphicsJNI::irect_to_jrect(const SkIRect& ir, JNIEnv* env, jobject obj) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gRect_class)); + + env->SetIntField(obj, gRect_leftFieldID, ir.fLeft); + env->SetIntField(obj, gRect_topFieldID, ir.fTop); + env->SetIntField(obj, gRect_rightFieldID, ir.fRight); + env->SetIntField(obj, gRect_bottomFieldID, ir.fBottom); +} + +SkRect* GraphicsJNI::jrectf_to_rect(JNIEnv* env, jobject obj, SkRect* r) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gRectF_class)); + + r->setLTRB(env->GetFloatField(obj, gRectF_leftFieldID), + env->GetFloatField(obj, gRectF_topFieldID), + env->GetFloatField(obj, gRectF_rightFieldID), + env->GetFloatField(obj, gRectF_bottomFieldID)); + return r; +} + +SkRect* GraphicsJNI::jrect_to_rect(JNIEnv* env, jobject obj, SkRect* r) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gRect_class)); + + r->setLTRB(SkIntToScalar(env->GetIntField(obj, gRect_leftFieldID)), + SkIntToScalar(env->GetIntField(obj, gRect_topFieldID)), + SkIntToScalar(env->GetIntField(obj, gRect_rightFieldID)), + SkIntToScalar(env->GetIntField(obj, gRect_bottomFieldID))); + return r; +} + +void GraphicsJNI::rect_to_jrectf(const SkRect& r, JNIEnv* env, jobject obj) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gRectF_class)); + + env->SetFloatField(obj, gRectF_leftFieldID, SkScalarToFloat(r.fLeft)); + env->SetFloatField(obj, gRectF_topFieldID, SkScalarToFloat(r.fTop)); + env->SetFloatField(obj, gRectF_rightFieldID, SkScalarToFloat(r.fRight)); + env->SetFloatField(obj, gRectF_bottomFieldID, SkScalarToFloat(r.fBottom)); +} + +SkIPoint* GraphicsJNI::jpoint_to_ipoint(JNIEnv* env, jobject obj, SkIPoint* point) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gPoint_class)); + + point->set(env->GetIntField(obj, gPoint_xFieldID), + env->GetIntField(obj, gPoint_yFieldID)); + return point; +} + +void GraphicsJNI::ipoint_to_jpoint(const SkIPoint& ir, JNIEnv* env, jobject obj) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gPoint_class)); + + env->SetIntField(obj, gPoint_xFieldID, ir.fX); + env->SetIntField(obj, gPoint_yFieldID, ir.fY); +} + +SkPoint* GraphicsJNI::jpointf_to_point(JNIEnv* env, jobject obj, SkPoint* point) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gPointF_class)); + + point->set(env->GetIntField(obj, gPointF_xFieldID), + env->GetIntField(obj, gPointF_yFieldID)); + return point; +} + +void GraphicsJNI::point_to_jpointf(const SkPoint& r, JNIEnv* env, jobject obj) +{ + ALOG_ASSERT(env->IsInstanceOf(obj, gPointF_class)); + + env->SetFloatField(obj, gPointF_xFieldID, SkScalarToFloat(r.fX)); + env->SetFloatField(obj, gPointF_yFieldID, SkScalarToFloat(r.fY)); +} + +// See enum values in GraphicsJNI.h +jint GraphicsJNI::colorTypeToLegacyBitmapConfig(SkColorType colorType) { + switch (colorType) { + case kRGBA_F16_SkColorType: + return kRGBA_16F_LegacyBitmapConfig; + case kN32_SkColorType: + return kARGB_8888_LegacyBitmapConfig; + case kARGB_4444_SkColorType: + return kARGB_4444_LegacyBitmapConfig; + case kRGB_565_SkColorType: + return kRGB_565_LegacyBitmapConfig; + case kAlpha_8_SkColorType: + return kA8_LegacyBitmapConfig; + case kUnknown_SkColorType: + default: + break; + } + return kNo_LegacyBitmapConfig; +} + +SkColorType GraphicsJNI::legacyBitmapConfigToColorType(jint legacyConfig) { + const uint8_t gConfig2ColorType[] = { + kUnknown_SkColorType, + kAlpha_8_SkColorType, + kUnknown_SkColorType, // Previously kIndex_8_SkColorType, + kRGB_565_SkColorType, + kARGB_4444_SkColorType, + kN32_SkColorType, + kRGBA_F16_SkColorType, + kN32_SkColorType + }; + + if (legacyConfig < 0 || legacyConfig > kLastEnum_LegacyBitmapConfig) { + legacyConfig = kNo_LegacyBitmapConfig; + } + return static_cast<SkColorType>(gConfig2ColorType[legacyConfig]); +} + +AndroidBitmapFormat GraphicsJNI::getFormatFromConfig(JNIEnv* env, jobject jconfig) { + ALOG_ASSERT(env); + if (NULL == jconfig) { + return ANDROID_BITMAP_FORMAT_NONE; + } + ALOG_ASSERT(env->IsInstanceOf(jconfig, gBitmapConfig_class)); + jint javaConfigId = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID); + + const AndroidBitmapFormat config2BitmapFormat[] = { + ANDROID_BITMAP_FORMAT_NONE, + ANDROID_BITMAP_FORMAT_A_8, + ANDROID_BITMAP_FORMAT_NONE, // Previously Config.Index_8 + ANDROID_BITMAP_FORMAT_RGB_565, + ANDROID_BITMAP_FORMAT_RGBA_4444, + ANDROID_BITMAP_FORMAT_RGBA_8888, + ANDROID_BITMAP_FORMAT_RGBA_F16, + ANDROID_BITMAP_FORMAT_NONE // Congfig.HARDWARE + }; + return config2BitmapFormat[javaConfigId]; +} + +jobject GraphicsJNI::getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format) { + ALOG_ASSERT(env); + jint configId = kNo_LegacyBitmapConfig; + switch (format) { + case ANDROID_BITMAP_FORMAT_A_8: + configId = kA8_LegacyBitmapConfig; + break; + case ANDROID_BITMAP_FORMAT_RGB_565: + configId = kRGB_565_LegacyBitmapConfig; + break; + case ANDROID_BITMAP_FORMAT_RGBA_4444: + configId = kARGB_4444_LegacyBitmapConfig; + break; + case ANDROID_BITMAP_FORMAT_RGBA_8888: + configId = kARGB_8888_LegacyBitmapConfig; + break; + case ANDROID_BITMAP_FORMAT_RGBA_F16: + configId = kRGBA_16F_LegacyBitmapConfig; + break; + default: + break; + } + + return env->CallStaticObjectMethod(gBitmapConfig_class, + gBitmapConfig_nativeToConfigMethodID, configId); +} + +SkColorType GraphicsJNI::getNativeBitmapColorType(JNIEnv* env, jobject jconfig) { + ALOG_ASSERT(env); + if (NULL == jconfig) { + return kUnknown_SkColorType; + } + ALOG_ASSERT(env->IsInstanceOf(jconfig, gBitmapConfig_class)); + int c = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID); + return legacyBitmapConfigToColorType(c); +} + +bool GraphicsJNI::isHardwareConfig(JNIEnv* env, jobject jconfig) { + ALOG_ASSERT(env); + if (NULL == jconfig) { + return false; + } + int c = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID); + return c == kHardware_LegacyBitmapConfig; +} + +jint GraphicsJNI::hardwareLegacyBitmapConfig() { + return kHardware_LegacyBitmapConfig; +} + +android::Canvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) { + ALOG_ASSERT(env); + ALOG_ASSERT(canvas); + ALOG_ASSERT(env->IsInstanceOf(canvas, gCanvas_class)); + jlong canvasHandle = env->GetLongField(canvas, gCanvas_nativeInstanceID); + if (!canvasHandle) { + return NULL; + } + return reinterpret_cast<android::Canvas*>(canvasHandle); +} + +SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region) +{ + ALOG_ASSERT(env); + ALOG_ASSERT(region); + ALOG_ASSERT(env->IsInstanceOf(region, gRegion_class)); + jlong regionHandle = env->GetLongField(region, gRegion_nativeInstanceID); + SkRegion* r = reinterpret_cast<SkRegion*>(regionHandle); + ALOG_ASSERT(r); + return r; +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap) +{ + ALOG_ASSERT(bitmap != NULL); + + jobject obj = env->NewObject(gBitmapRegionDecoder_class, + gBitmapRegionDecoder_constructorMethodID, + reinterpret_cast<jlong>(bitmap)); + hasException(env); // For the side effect of logging. + return obj; +} + +jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region) +{ + ALOG_ASSERT(region != NULL); + jobject obj = env->NewObject(gRegion_class, gRegion_constructorMethodID, + reinterpret_cast<jlong>(region), 0); + hasException(env); // For the side effect of logging. + return obj; +} + +/////////////////////////////////////////////////////////////////////////////// + +jobject GraphicsJNI::getColorSpace(JNIEnv* env, SkColorSpace* decodeColorSpace, + SkColorType decodeColorType) { + if (!decodeColorSpace || decodeColorType == kAlpha_8_SkColorType) { + return nullptr; + } + + // Special checks for the common sRGB cases and their extended variants. + jobject namedCS = nullptr; + sk_sp<SkColorSpace> srgbLinear = SkColorSpace::MakeSRGBLinear(); + if (decodeColorType == kRGBA_F16_SkColorType) { + // An F16 Bitmap will always report that it is EXTENDED if + // it matches a ColorSpace that has an EXTENDED variant. + if (decodeColorSpace->isSRGB()) { + namedCS = env->GetStaticObjectField(gColorSpace_Named_class, + gColorSpace_Named_ExtendedSRGBFieldID); + } else if (decodeColorSpace == srgbLinear.get()) { + namedCS = env->GetStaticObjectField(gColorSpace_Named_class, + gColorSpace_Named_LinearExtendedSRGBFieldID); + } + } else if (decodeColorSpace->isSRGB()) { + namedCS = env->GetStaticObjectField(gColorSpace_Named_class, + gColorSpace_Named_sRGBFieldID); + } else if (decodeColorSpace == srgbLinear.get()) { + namedCS = env->GetStaticObjectField(gColorSpace_Named_class, + gColorSpace_Named_LinearSRGBFieldID); + } + + if (namedCS) { + return env->CallStaticObjectMethod(gColorSpace_class, gColorSpace_getMethodID, namedCS); + } + + // Try to match against known RGB color spaces using the CIE XYZ D50 + // conversion matrix and numerical transfer function parameters + skcms_Matrix3x3 xyzMatrix; + LOG_ALWAYS_FATAL_IF(!decodeColorSpace->toXYZD50(&xyzMatrix)); + + skcms_TransferFunction transferParams; + // We can only handle numerical transfer functions at the moment + LOG_ALWAYS_FATAL_IF(!decodeColorSpace->isNumericalTransferFn(&transferParams)); + + jobject params = env->NewObject(gTransferParameters_class, + gTransferParameters_constructorMethodID, + transferParams.a, transferParams.b, transferParams.c, + transferParams.d, transferParams.e, transferParams.f, + transferParams.g); + + jfloatArray xyzArray = env->NewFloatArray(9); + jfloat xyz[9] = { + xyzMatrix.vals[0][0], + xyzMatrix.vals[1][0], + xyzMatrix.vals[2][0], + xyzMatrix.vals[0][1], + xyzMatrix.vals[1][1], + xyzMatrix.vals[2][1], + xyzMatrix.vals[0][2], + xyzMatrix.vals[1][2], + xyzMatrix.vals[2][2] + }; + env->SetFloatArrayRegion(xyzArray, 0, 9, xyz); + + jobject colorSpace = env->CallStaticObjectMethod(gColorSpace_class, + gColorSpace_matchMethodID, xyzArray, params); + + if (colorSpace == nullptr) { + // We couldn't find an exact match, let's create a new color space + // instance with the 3x3 conversion matrix and transfer function + colorSpace = env->NewObject(gColorSpaceRGB_class, + gColorSpaceRGB_constructorMethodID, + env->NewStringUTF("Unknown"), xyzArray, params); + } + + env->DeleteLocalRef(xyzArray); + return colorSpace; +} + +/////////////////////////////////////////////////////////////////////////////// +bool HeapAllocator::allocPixelRef(SkBitmap* bitmap) { + mStorage = android::Bitmap::allocateHeapBitmap(bitmap); + return !!mStorage; +} + +//////////////////////////////////////////////////////////////////////////////// + +RecyclingClippingPixelAllocator::RecyclingClippingPixelAllocator( + android::Bitmap* recycledBitmap, size_t recycledBytes) + : mRecycledBitmap(recycledBitmap) + , mRecycledBytes(recycledBytes) + , mSkiaBitmap(nullptr) + , mNeedsCopy(false) +{} + +RecyclingClippingPixelAllocator::~RecyclingClippingPixelAllocator() {} + +bool RecyclingClippingPixelAllocator::allocPixelRef(SkBitmap* bitmap) { + // Ensure that the caller did not pass in a NULL bitmap to the constructor or this + // function. + LOG_ALWAYS_FATAL_IF(!mRecycledBitmap); + LOG_ALWAYS_FATAL_IF(!bitmap); + mSkiaBitmap = bitmap; + + // This behaves differently than the RecyclingPixelAllocator. For backwards + // compatibility, the original color type of the recycled bitmap must be maintained. + if (mRecycledBitmap->info().colorType() != bitmap->colorType()) { + return false; + } + + // The Skia bitmap specifies the width and height needed by the decoder. + // mRecycledBitmap specifies the width and height of the bitmap that we + // want to reuse. Neither can be changed. We will try to find a way + // to reuse the memory. + const int maxWidth = std::max(bitmap->width(), mRecycledBitmap->info().width()); + const int maxHeight = std::max(bitmap->height(), mRecycledBitmap->info().height()); + const SkImageInfo maxInfo = bitmap->info().makeWH(maxWidth, maxHeight); + const size_t rowBytes = maxInfo.minRowBytes(); + const size_t bytesNeeded = maxInfo.computeByteSize(rowBytes); + if (bytesNeeded <= mRecycledBytes) { + // Here we take advantage of reconfigure() to reset the rowBytes + // of mRecycledBitmap. It is very important that we pass in + // mRecycledBitmap->info() for the SkImageInfo. According to the + // specification for BitmapRegionDecoder, we are not allowed to change + // the SkImageInfo. + // We can (must) preserve the color space since it doesn't affect the + // storage needs + mRecycledBitmap->reconfigure( + mRecycledBitmap->info().makeColorSpace(bitmap->refColorSpace()), + rowBytes); + + // Give the bitmap the same pixelRef as mRecycledBitmap. + // skbug.com/4538: We also need to make sure that the rowBytes on the pixel ref + // match the rowBytes on the bitmap. + bitmap->setInfo(bitmap->info(), rowBytes); + bitmap->setPixelRef(sk_ref_sp(mRecycledBitmap), 0, 0); + + // Make sure that the recycled bitmap has the correct alpha type. + mRecycledBitmap->setAlphaType(bitmap->alphaType()); + + bitmap->notifyPixelsChanged(); + mNeedsCopy = false; + + // TODO: If the dimensions of the SkBitmap are smaller than those of + // mRecycledBitmap, should we zero the memory in mRecycledBitmap? + return true; + } + + // In the event that mRecycledBitmap is not large enough, allocate new memory + // on the heap. + SkBitmap::HeapAllocator heapAllocator; + + // We will need to copy from heap memory to mRecycledBitmap's memory after the + // decode is complete. + mNeedsCopy = true; + + return heapAllocator.allocPixelRef(bitmap); +} + +void RecyclingClippingPixelAllocator::copyIfNecessary() { + if (mNeedsCopy) { + mRecycledBitmap->ref(); + SkPixelRef* recycledPixels = mRecycledBitmap; + void* dst = recycledPixels->pixels(); + const size_t dstRowBytes = mRecycledBitmap->rowBytes(); + const size_t bytesToCopy = std::min(mRecycledBitmap->info().minRowBytes(), + mSkiaBitmap->info().minRowBytes()); + const int rowsToCopy = std::min(mRecycledBitmap->info().height(), + mSkiaBitmap->info().height()); + for (int y = 0; y < rowsToCopy; y++) { + memcpy(dst, mSkiaBitmap->getAddr(0, y), bytesToCopy); + dst = SkTAddOffset<void>(dst, dstRowBytes); + } + recycledPixels->notifyPixelsChanged(); + recycledPixels->unref(); + } + mRecycledBitmap = nullptr; + mSkiaBitmap = nullptr; +} + +//////////////////////////////////////////////////////////////////////////////// + +AshmemPixelAllocator::AshmemPixelAllocator(JNIEnv *env) { + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJavaVM) != JNI_OK, + "env->GetJavaVM failed"); +} + +bool AshmemPixelAllocator::allocPixelRef(SkBitmap* bitmap) { + mStorage = android::Bitmap::allocateAshmemBitmap(bitmap); + return !!mStorage; +} + +//////////////////////////////////////////////////////////////////////////////// + +int register_android_graphics_Graphics(JNIEnv* env) +{ + jmethodID m; + jclass c; + + gRect_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Rect")); + gRect_leftFieldID = GetFieldIDOrDie(env, gRect_class, "left", "I"); + gRect_topFieldID = GetFieldIDOrDie(env, gRect_class, "top", "I"); + gRect_rightFieldID = GetFieldIDOrDie(env, gRect_class, "right", "I"); + gRect_bottomFieldID = GetFieldIDOrDie(env, gRect_class, "bottom", "I"); + + gRectF_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/RectF")); + gRectF_leftFieldID = GetFieldIDOrDie(env, gRectF_class, "left", "F"); + gRectF_topFieldID = GetFieldIDOrDie(env, gRectF_class, "top", "F"); + gRectF_rightFieldID = GetFieldIDOrDie(env, gRectF_class, "right", "F"); + gRectF_bottomFieldID = GetFieldIDOrDie(env, gRectF_class, "bottom", "F"); + + gPoint_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Point")); + gPoint_xFieldID = GetFieldIDOrDie(env, gPoint_class, "x", "I"); + gPoint_yFieldID = GetFieldIDOrDie(env, gPoint_class, "y", "I"); + + gPointF_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/PointF")); + gPointF_xFieldID = GetFieldIDOrDie(env, gPointF_class, "x", "F"); + gPointF_yFieldID = GetFieldIDOrDie(env, gPointF_class, "y", "F"); + + gBitmapRegionDecoder_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/BitmapRegionDecoder")); + gBitmapRegionDecoder_constructorMethodID = GetMethodIDOrDie(env, gBitmapRegionDecoder_class, "<init>", "(J)V"); + + gBitmapConfig_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Bitmap$Config")); + gBitmapConfig_nativeInstanceID = GetFieldIDOrDie(env, gBitmapConfig_class, "nativeInt", "I"); + gBitmapConfig_nativeToConfigMethodID = GetStaticMethodIDOrDie(env, gBitmapConfig_class, + "nativeToConfig", + "(I)Landroid/graphics/Bitmap$Config;"); + + gCanvas_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Canvas")); + gCanvas_nativeInstanceID = GetFieldIDOrDie(env, gCanvas_class, "mNativeCanvasWrapper", "J"); + + gPicture_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Picture")); + gPicture_nativeInstanceID = GetFieldIDOrDie(env, gPicture_class, "mNativePicture", "J"); + + gRegion_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Region")); + gRegion_nativeInstanceID = GetFieldIDOrDie(env, gRegion_class, "mNativeRegion", "J"); + gRegion_constructorMethodID = GetMethodIDOrDie(env, gRegion_class, "<init>", "(JI)V"); + + c = env->FindClass("java/lang/Byte"); + gByte_class = (jclass) env->NewGlobalRef( + env->GetStaticObjectField(c, env->GetStaticFieldID(c, "TYPE", "Ljava/lang/Class;"))); + + gVMRuntime_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "dalvik/system/VMRuntime")); + m = env->GetStaticMethodID(gVMRuntime_class, "getRuntime", "()Ldalvik/system/VMRuntime;"); + gVMRuntime = env->NewGlobalRef(env->CallStaticObjectMethod(gVMRuntime_class, m)); + gVMRuntime_newNonMovableArray = GetMethodIDOrDie(env, gVMRuntime_class, "newNonMovableArray", + "(Ljava/lang/Class;I)Ljava/lang/Object;"); + gVMRuntime_addressOf = GetMethodIDOrDie(env, gVMRuntime_class, "addressOf", "(Ljava/lang/Object;)J"); + + gColorSpace_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ColorSpace")); + gColorSpace_getMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class, + "get", "(Landroid/graphics/ColorSpace$Named;)Landroid/graphics/ColorSpace;"); + gColorSpace_matchMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class, "match", + "([FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)Landroid/graphics/ColorSpace;"); + + gColorSpaceRGB_class = MakeGlobalRefOrDie(env, + FindClassOrDie(env, "android/graphics/ColorSpace$Rgb")); + gColorSpaceRGB_constructorMethodID = GetMethodIDOrDie(env, gColorSpaceRGB_class, + "<init>", "(Ljava/lang/String;[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)V"); + + gColorSpace_Named_class = MakeGlobalRefOrDie(env, + FindClassOrDie(env, "android/graphics/ColorSpace$Named")); + gColorSpace_Named_sRGBFieldID = GetStaticFieldIDOrDie(env, + gColorSpace_Named_class, "SRGB", "Landroid/graphics/ColorSpace$Named;"); + gColorSpace_Named_ExtendedSRGBFieldID = GetStaticFieldIDOrDie(env, + gColorSpace_Named_class, "EXTENDED_SRGB", "Landroid/graphics/ColorSpace$Named;"); + gColorSpace_Named_LinearSRGBFieldID = GetStaticFieldIDOrDie(env, + gColorSpace_Named_class, "LINEAR_SRGB", "Landroid/graphics/ColorSpace$Named;"); + gColorSpace_Named_LinearExtendedSRGBFieldID = GetStaticFieldIDOrDie(env, + gColorSpace_Named_class, "LINEAR_EXTENDED_SRGB", "Landroid/graphics/ColorSpace$Named;"); + + gTransferParameters_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, + "android/graphics/ColorSpace$Rgb$TransferParameters")); + gTransferParameters_constructorMethodID = GetMethodIDOrDie(env, gTransferParameters_class, + "<init>", "(DDDDDDD)V"); + + return 0; +} diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h new file mode 100644 index 000000000000..b58a740a4c27 --- /dev/null +++ b/libs/hwui/jni/GraphicsJNI.h @@ -0,0 +1,335 @@ +#ifndef _ANDROID_GRAPHICS_GRAPHICS_JNI_H_ +#define _ANDROID_GRAPHICS_GRAPHICS_JNI_H_ + +#include <cutils/compiler.h> + +#include "Bitmap.h" +#include "SkBitmap.h" +#include "SkBRDAllocator.h" +#include "SkCodec.h" +#include "SkPixelRef.h" +#include "SkMallocPixelRef.h" +#include "SkPoint.h" +#include "SkRect.h" +#include "SkColorSpace.h" +#include <hwui/Canvas.h> +#include <hwui/Bitmap.h> + +#include "graphics_jni_helpers.h" + +class SkBitmapRegionDecoder; +class SkCanvas; + +namespace android { +class Paint; +struct Typeface; +} + +class GraphicsJNI { +public: + // This enum must keep these int values, to match the int values + // in the java Bitmap.Config enum. + enum LegacyBitmapConfig { + kNo_LegacyBitmapConfig = 0, + kA8_LegacyBitmapConfig = 1, + kIndex8_LegacyBitmapConfig = 2, + kRGB_565_LegacyBitmapConfig = 3, + kARGB_4444_LegacyBitmapConfig = 4, + kARGB_8888_LegacyBitmapConfig = 5, + kRGBA_16F_LegacyBitmapConfig = 6, + kHardware_LegacyBitmapConfig = 7, + + kLastEnum_LegacyBitmapConfig = kHardware_LegacyBitmapConfig + }; + + static void setJavaVM(JavaVM* javaVM); + + /** returns a pointer to the JavaVM provided when we initialized the module */ + static JavaVM* getJavaVM() { return mJavaVM; } + + /** return a pointer to the JNIEnv for this thread */ + static JNIEnv* getJNIEnv(); + + /** create a JNIEnv* for this thread or assert if one already exists */ + static JNIEnv* attachJNIEnv(const char* envName); + + /** detach the current thread from the JavaVM */ + static void detachJNIEnv(); + + // returns true if an exception is set (and dumps it out to the Log) + static bool hasException(JNIEnv*); + + static void get_jrect(JNIEnv*, jobject jrect, int* L, int* T, int* R, int* B); + static void set_jrect(JNIEnv*, jobject jrect, int L, int T, int R, int B); + + static SkIRect* jrect_to_irect(JNIEnv*, jobject jrect, SkIRect*); + static void irect_to_jrect(const SkIRect&, JNIEnv*, jobject jrect); + + static SkRect* jrectf_to_rect(JNIEnv*, jobject jrectf, SkRect*); + static SkRect* jrect_to_rect(JNIEnv*, jobject jrect, SkRect*); + static void rect_to_jrectf(const SkRect&, JNIEnv*, jobject jrectf); + + static void set_jpoint(JNIEnv*, jobject jrect, int x, int y); + + static SkIPoint* jpoint_to_ipoint(JNIEnv*, jobject jpoint, SkIPoint* point); + static void ipoint_to_jpoint(const SkIPoint& point, JNIEnv*, jobject jpoint); + + static SkPoint* jpointf_to_point(JNIEnv*, jobject jpointf, SkPoint* point); + static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf); + + ANDROID_API static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas); + static android::Bitmap* getNativeBitmap(JNIEnv*, jobject bitmap); + static SkImageInfo getBitmapInfo(JNIEnv*, jobject bitmap, uint32_t* outRowBytes, + bool* isHardware); + static SkRegion* getNativeRegion(JNIEnv*, jobject region); + + /* + * LegacyBitmapConfig is the old enum in Skia that matched the enum int values + * in Bitmap.Config. Skia no longer supports this config, but has replaced it + * with SkColorType. These routines convert between the two. + */ + static SkColorType legacyBitmapConfigToColorType(jint legacyConfig); + static jint colorTypeToLegacyBitmapConfig(SkColorType colorType); + + /** Return the corresponding native colorType from the java Config enum, + or kUnknown_SkColorType if the java object is null. + */ + static SkColorType getNativeBitmapColorType(JNIEnv*, jobject jconfig); + static AndroidBitmapFormat getFormatFromConfig(JNIEnv* env, jobject jconfig); + static jobject getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format); + + static bool isHardwareConfig(JNIEnv* env, jobject jconfig); + static jint hardwareLegacyBitmapConfig(); + + static jobject createRegion(JNIEnv* env, SkRegion* region); + + static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap); + + /** + * Given a bitmap we natively allocate a memory block to store the contents + * of that bitmap. The memory is then attached to the bitmap via an + * SkPixelRef, which ensures that upon deletion the appropriate caches + * are notified. + */ + static bool allocatePixels(JNIEnv* env, SkBitmap* bitmap); + + /** Copy the colors in colors[] to the bitmap, convert to the correct + format along the way. + Whether to use premultiplied pixels is determined by dstBitmap's alphaType. + */ + static bool SetPixels(JNIEnv* env, jintArray colors, int srcOffset, + int srcStride, int x, int y, int width, int height, + SkBitmap* dstBitmap); + + /** + * Convert the native SkColorSpace retrieved from ColorSpace.Rgb.getNativeInstance(). + * + * This will never throw an Exception. If the ColorSpace is one that Skia cannot + * use, ColorSpace.Rgb.getNativeInstance() would have thrown an Exception. It may, + * however, be nullptr, which may be acceptable. + */ + static sk_sp<SkColorSpace> getNativeColorSpace(jlong colorSpaceHandle); + + /** + * Return the android.graphics.ColorSpace Java object that corresponds to decodeColorSpace + * and decodeColorType. + * + * This may create a new object if none of the Named ColorSpaces match. + */ + static jobject getColorSpace(JNIEnv* env, SkColorSpace* decodeColorSpace, + SkColorType decodeColorType); + + /** + * Convert from a Java @ColorLong to an SkColor4f that Skia can use directly. + * + * This ignores the encoded ColorSpace, besides checking to see if it is sRGB, + * which is encoded differently. The color space should be passed down separately + * via ColorSpace#getNativeInstance(), and converted with getNativeColorSpace(), + * above. + */ + static SkColor4f convertColorLong(jlong color); + +private: + /* JNI JavaVM pointer */ + static JavaVM* mJavaVM; +}; + +class HeapAllocator : public SkBRDAllocator { +public: + HeapAllocator() { }; + ~HeapAllocator() { }; + + virtual bool allocPixelRef(SkBitmap* bitmap) override; + + /** + * Fetches the backing allocation object. Must be called! + */ + android::Bitmap* getStorageObjAndReset() { + return mStorage.release(); + }; + + SkCodec::ZeroInitialized zeroInit() const override { return SkCodec::kYes_ZeroInitialized; } +private: + sk_sp<android::Bitmap> mStorage; +}; + +/** + * Allocator to handle reusing bitmaps for BitmapRegionDecoder. + * + * The BitmapRegionDecoder documentation states that, if it is + * provided, the recycled bitmap will always be reused, clipping + * the decoded output to fit in the recycled bitmap if necessary. + * This allocator implements that behavior. + * + * Skia's SkBitmapRegionDecoder expects the memory that + * is allocated to be large enough to decode the entire region + * that is requested. It will decode directly into the memory + * that is provided. + * + * FIXME: BUG:25465958 + * If the recycled bitmap is not large enough for the decode + * requested, meaning that a clip is required, we will allocate + * enough memory for Skia to perform the decode, and then copy + * from the decoded output into the recycled bitmap. + * + * If the recycled bitmap is large enough for the decode requested, + * we will provide that memory for Skia to decode directly into. + * + * This allocator should only be used for a single allocation. + * After we reuse the recycledBitmap once, it is dangerous to + * reuse it again, given that it still may be in use from our + * first allocation. + */ +class RecyclingClippingPixelAllocator : public SkBRDAllocator { +public: + + RecyclingClippingPixelAllocator(android::Bitmap* recycledBitmap, + size_t recycledBytes); + + ~RecyclingClippingPixelAllocator(); + + virtual bool allocPixelRef(SkBitmap* bitmap) override; + + /** + * Must be called! + * + * In the event that the recycled bitmap is not large enough for + * the allocation requested, we will allocate memory on the heap + * instead. As a final step, once we are done using this memory, + * we will copy the contents of the heap memory into the recycled + * bitmap's memory, clipping as necessary. + */ + void copyIfNecessary(); + + /** + * Indicates that this allocator does not allocate zero initialized + * memory. + */ + SkCodec::ZeroInitialized zeroInit() const override { return SkCodec::kNo_ZeroInitialized; } + +private: + android::Bitmap* mRecycledBitmap; + const size_t mRecycledBytes; + SkBitmap* mSkiaBitmap; + bool mNeedsCopy; +}; + +class AshmemPixelAllocator : public SkBitmap::Allocator { +public: + explicit AshmemPixelAllocator(JNIEnv* env); + ~AshmemPixelAllocator() { }; + virtual bool allocPixelRef(SkBitmap* bitmap); + android::Bitmap* getStorageObjAndReset() { + return mStorage.release(); + }; + +private: + JavaVM* mJavaVM; + sk_sp<android::Bitmap> mStorage; +}; + + +enum JNIAccess { + kRO_JNIAccess, + kRW_JNIAccess +}; + +class AutoJavaFloatArray { +public: + AutoJavaFloatArray(JNIEnv* env, jfloatArray array, + int minLength = 0, JNIAccess = kRW_JNIAccess); + ~AutoJavaFloatArray(); + + float* ptr() const { return fPtr; } + int length() const { return fLen; } + +private: + JNIEnv* fEnv; + jfloatArray fArray; + float* fPtr; + int fLen; + int fReleaseMode; +}; + +class AutoJavaIntArray { +public: + AutoJavaIntArray(JNIEnv* env, jintArray array, int minLength = 0); + ~AutoJavaIntArray(); + + jint* ptr() const { return fPtr; } + int length() const { return fLen; } + +private: + JNIEnv* fEnv; + jintArray fArray; + jint* fPtr; + int fLen; +}; + +class AutoJavaShortArray { +public: + AutoJavaShortArray(JNIEnv* env, jshortArray array, + int minLength = 0, JNIAccess = kRW_JNIAccess); + ~AutoJavaShortArray(); + + jshort* ptr() const { return fPtr; } + int length() const { return fLen; } + +private: + JNIEnv* fEnv; + jshortArray fArray; + jshort* fPtr; + int fLen; + int fReleaseMode; +}; + +class AutoJavaByteArray { +public: + AutoJavaByteArray(JNIEnv* env, jbyteArray array, int minLength = 0); + ~AutoJavaByteArray(); + + jbyte* ptr() const { return fPtr; } + int length() const { return fLen; } + +private: + JNIEnv* fEnv; + jbyteArray fArray; + jbyte* fPtr; + int fLen; +}; + +void doThrowNPE(JNIEnv* env); +void doThrowAIOOBE(JNIEnv* env); // Array Index Out Of Bounds Exception +void doThrowIAE(JNIEnv* env, const char* msg = NULL); // Illegal Argument +void doThrowRE(JNIEnv* env, const char* msg = NULL); // Runtime +void doThrowISE(JNIEnv* env, const char* msg = NULL); // Illegal State +void doThrowOOME(JNIEnv* env, const char* msg = NULL); // Out of memory +void doThrowIOE(JNIEnv* env, const char* msg = NULL); // IO Exception + +#define NPE_CHECK_RETURN_ZERO(env, object) \ + do { if (NULL == (object)) { doThrowNPE(env); return 0; } } while (0) + +#define NPE_CHECK_RETURN_VOID(env, object) \ + do { if (NULL == (object)) { doThrowNPE(env); return; } } while (0) + +#endif // _ANDROID_GRAPHICS_GRAPHICS_JNI_H_ diff --git a/libs/hwui/jni/GraphicsStatsService.cpp b/libs/hwui/jni/GraphicsStatsService.cpp new file mode 100644 index 000000000000..1591ffabd26a --- /dev/null +++ b/libs/hwui/jni/GraphicsStatsService.cpp @@ -0,0 +1,197 @@ +/* + * 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. + */ + +#undef LOG_TAG +#define LOG_TAG "GraphicsStatsService" + +#include <JankTracker.h> +#include <log/log.h> +#include <nativehelper/ScopedPrimitiveArray.h> +#include <nativehelper/ScopedUtfChars.h> +#include <service/GraphicsStatsService.h> +#include <stats_event.h> +#include <stats_pull_atom_callback.h> +#include <statslog.h> + +#include "android/graphics/jni_runtime.h" +#include "GraphicsJNI.h" + +namespace android { + +using namespace android::uirenderer; + +static jint getAshmemSize(JNIEnv*, jobject) { + return sizeof(ProfileData); +} + +static jlong createDump(JNIEnv*, jobject, jint fd, jboolean isProto) { + GraphicsStatsService::Dump* dump = + GraphicsStatsService::createDump(fd, + isProto ? GraphicsStatsService::DumpType::Protobuf + : GraphicsStatsService::DumpType::Text); + return reinterpret_cast<jlong>(dump); +} + +static void addToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath, jstring jpackage, + jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) { + std::string path; + const ProfileData* data = nullptr; + LOG_ALWAYS_FATAL_IF(jdata == nullptr && jpath == nullptr, "Path and data can't both be null"); + ScopedByteArrayRO buffer{env}; + if (jdata != nullptr) { + buffer.reset(jdata); + LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData), + "Buffer size %zu doesn't match expected %zu!", buffer.size(), + sizeof(ProfileData)); + data = reinterpret_cast<const ProfileData*>(buffer.get()); + } + if (jpath != nullptr) { + ScopedUtfChars pathChars(env, jpath); + LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), + "Failed to get path chars"); + path.assign(pathChars.c_str(), pathChars.size()); + } + ScopedUtfChars packageChars(env, jpackage); + LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(), + "Failed to get path chars"); + GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr); + LOG_ALWAYS_FATAL_IF(!dump, "null passed for dump pointer"); + + const std::string package(packageChars.c_str(), packageChars.size()); + GraphicsStatsService::addToDump(dump, path, package, versionCode, startTime, endTime, data); +} + +static void addFileToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath) { + ScopedUtfChars pathChars(env, jpath); + LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars"); + const std::string path(pathChars.c_str(), pathChars.size()); + GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr); + GraphicsStatsService::addToDump(dump, path); +} + +static void finishDump(JNIEnv*, jobject, jlong dumpPtr) { + GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr); + GraphicsStatsService::finishDump(dump); +} + +static void finishDumpInMemory(JNIEnv* env, jobject, jlong dumpPtr, jlong pulledData, + jboolean lastFullDay) { + GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr); + AStatsEventList* data = reinterpret_cast<AStatsEventList*>(pulledData); + GraphicsStatsService::finishDumpInMemory(dump, data, lastFullDay == JNI_TRUE); +} + +static void saveBuffer(JNIEnv* env, jobject clazz, jstring jpath, jstring jpackage, + jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) { + ScopedByteArrayRO buffer(env, jdata); + LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData), + "Buffer size %zu doesn't match expected %zu!", buffer.size(), + sizeof(ProfileData)); + ScopedUtfChars pathChars(env, jpath); + LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars"); + ScopedUtfChars packageChars(env, jpackage); + LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(), + "Failed to get path chars"); + + const std::string path(pathChars.c_str(), pathChars.size()); + const std::string package(packageChars.c_str(), packageChars.size()); + const ProfileData* data = reinterpret_cast<const ProfileData*>(buffer.get()); + GraphicsStatsService::saveBuffer(path, package, versionCode, startTime, endTime, data); +} + +static jobject gGraphicsStatsServiceObject = nullptr; +static jmethodID gGraphicsStatsService_pullGraphicsStatsMethodID; + +static JNIEnv* getJNIEnv() { + JavaVM* vm = GraphicsJNI::getJavaVM(); + JNIEnv* env = nullptr; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + if (vm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!"); + } + } + return env; +} + +// graphicsStatsPullCallback is invoked by statsd service to pull GRAPHICS_STATS atom. +static AStatsManager_PullAtomCallbackReturn graphicsStatsPullCallback(int32_t atom_tag, + AStatsEventList* data, + void* cookie) { + JNIEnv* env = getJNIEnv(); + if (!env) { + return false; + } + if (gGraphicsStatsServiceObject == nullptr) { + ALOGE("Failed to get graphicsstats service"); + return AStatsManager_PULL_SKIP; + } + + for (bool lastFullDay : {true, false}) { + env->CallVoidMethod(gGraphicsStatsServiceObject, + gGraphicsStatsService_pullGraphicsStatsMethodID, + (jboolean)(lastFullDay ? JNI_TRUE : JNI_FALSE), + reinterpret_cast<jlong>(data)); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + ALOGE("Failed to invoke graphicsstats service"); + return AStatsManager_PULL_SKIP; + } + } + return AStatsManager_PULL_SUCCESS; +} + +// Register a puller for GRAPHICS_STATS atom with the statsd service. +static void nativeInit(JNIEnv* env, jobject javaObject) { + gGraphicsStatsServiceObject = env->NewGlobalRef(javaObject); + AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain(); + AStatsManager_PullAtomMetadata_setCoolDownMillis(metadata, 10); // 10 milliseconds + AStatsManager_PullAtomMetadata_setTimeoutMillis(metadata, 2 * MS_PER_SEC); // 2 seconds + + AStatsManager_setPullAtomCallback(android::util::GRAPHICS_STATS, metadata, + &graphicsStatsPullCallback, nullptr); + + AStatsManager_PullAtomMetadata_release(metadata); +} + +static void nativeDestructor(JNIEnv* env, jobject javaObject) { + AStatsManager_clearPullAtomCallback(android::util::GRAPHICS_STATS); + env->DeleteGlobalRef(gGraphicsStatsServiceObject); + gGraphicsStatsServiceObject = nullptr; +} + +} // namespace android +using namespace android; + +static const JNINativeMethod sMethods[] = + {{"nGetAshmemSize", "()I", (void*)getAshmemSize}, + {"nCreateDump", "(IZ)J", (void*)createDump}, + {"nAddToDump", "(JLjava/lang/String;Ljava/lang/String;JJJ[B)V", (void*)addToDump}, + {"nAddToDump", "(JLjava/lang/String;)V", (void*)addFileToDump}, + {"nFinishDump", "(J)V", (void*)finishDump}, + {"nFinishDumpInMemory", "(JJZ)V", (void*)finishDumpInMemory}, + {"nSaveBuffer", "(Ljava/lang/String;Ljava/lang/String;JJJ[B)V", (void*)saveBuffer}, + {"nativeInit", "()V", (void*)nativeInit}, + {"nativeDestructor", "()V", (void*)nativeDestructor}}; + +int register_android_graphics_GraphicsStatsService(JNIEnv* env) { + jclass graphicsStatsService_class = + FindClassOrDie(env, "android/graphics/GraphicsStatsService"); + gGraphicsStatsService_pullGraphicsStatsMethodID = + GetMethodIDOrDie(env, graphicsStatsService_class, "pullGraphicsStats", "(ZJ)V"); + return jniRegisterNativeMethods(env, "android/graphics/GraphicsStatsService", sMethods, + NELEM(sMethods)); +} diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp new file mode 100644 index 000000000000..41d939bd6373 --- /dev/null +++ b/libs/hwui/jni/ImageDecoder.cpp @@ -0,0 +1,526 @@ +/* + * 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. + */ + +#include "Bitmap.h" +#include "BitmapFactory.h" +#include "ByteBufferStreamAdaptor.h" +#include "CreateJavaOutputStreamAdaptor.h" +#include "GraphicsJNI.h" +#include "ImageDecoder.h" +#include "NinePatchPeeker.h" +#include "Utils.h" + +#include <hwui/Bitmap.h> +#include <hwui/ImageDecoder.h> +#include <HardwareBitmapUploader.h> + +#include <SkAndroidCodec.h> +#include <SkEncodedImageFormat.h> +#include <SkFrontBufferedStream.h> +#include <SkStream.h> + +#include <androidfw/Asset.h> +#include <fcntl.h> +#include <sys/stat.h> + +using namespace android; + +static jclass gImageDecoder_class; +static jclass gSize_class; +static jclass gDecodeException_class; +static jclass gCanvas_class; +static jmethodID gImageDecoder_constructorMethodID; +static jmethodID gImageDecoder_postProcessMethodID; +static jmethodID gSize_constructorMethodID; +static jmethodID gDecodeException_constructorMethodID; +static jmethodID gCallback_onPartialImageMethodID; +static jmethodID gCanvas_constructorMethodID; +static jmethodID gCanvas_releaseMethodID; + +// These need to stay in sync with ImageDecoder.java's Allocator constants. +enum Allocator { + kDefault_Allocator = 0, + kSoftware_Allocator = 1, + kSharedMemory_Allocator = 2, + kHardware_Allocator = 3, +}; + +// These need to stay in sync with ImageDecoder.java's Error constants. +enum Error { + kSourceException = 1, + kSourceIncomplete = 2, + kSourceMalformedData = 3, +}; + +// These need to stay in sync with PixelFormat.java's Format constants. +enum PixelFormat { + kUnknown = 0, + kTranslucent = -3, + kOpaque = -1, +}; + +// Clear and return any pending exception for handling other than throwing directly. +static jthrowable get_and_clear_exception(JNIEnv* env) { + jthrowable jexception = env->ExceptionOccurred(); + if (jexception) { + env->ExceptionClear(); + } + return jexception; +} + +// Throw a new ImageDecoder.DecodeException. Returns null for convenience. +static jobject throw_exception(JNIEnv* env, Error error, const char* msg, + jthrowable cause, jobject source) { + jstring jstr = nullptr; + if (msg) { + jstr = env->NewStringUTF(msg); + if (!jstr) { + // Out of memory. + return nullptr; + } + } + jthrowable exception = (jthrowable) env->NewObject(gDecodeException_class, + gDecodeException_constructorMethodID, error, jstr, cause, source); + // Only throw if not out of memory. + if (exception) { + env->Throw(exception); + } + return nullptr; +} + +static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream, + jobject source, jboolean preferAnimation) { + if (!stream.get()) { + return throw_exception(env, kSourceMalformedData, "Failed to create a stream", + nullptr, source); + } + sk_sp<NinePatchPeeker> peeker(new NinePatchPeeker); + SkCodec::Result result; + auto codec = SkCodec::MakeFromStream( + std::move(stream), &result, peeker.get(), + preferAnimation ? SkCodec::SelectionPolicy::kPreferAnimation + : SkCodec::SelectionPolicy::kPreferStillImage); + if (jthrowable jexception = get_and_clear_exception(env)) { + return throw_exception(env, kSourceException, "", jexception, source); + } + if (!codec) { + switch (result) { + case SkCodec::kIncompleteInput: + return throw_exception(env, kSourceIncomplete, "", nullptr, source); + default: + SkString msg; + msg.printf("Failed to create image decoder with message '%s'", + SkCodec::ResultToString(result)); + return throw_exception(env, kSourceMalformedData, msg.c_str(), + nullptr, source); + + } + } + + const bool animated = codec->getFrameCount() > 1; + if (jthrowable jexception = get_and_clear_exception(env)) { + return throw_exception(env, kSourceException, "", jexception, source); + } + + auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec), + SkAndroidCodec::ExifOrientationBehavior::kRespect); + if (!androidCodec.get()) { + return throw_exception(env, kSourceMalformedData, "", nullptr, source); + } + + const auto& info = androidCodec->getInfo(); + const int width = info.width(); + const int height = info.height(); + const bool isNinePatch = peeker->mPatch != nullptr; + ImageDecoder* decoder = new ImageDecoder(std::move(androidCodec), std::move(peeker)); + return env->NewObject(gImageDecoder_class, gImageDecoder_constructorMethodID, + reinterpret_cast<jlong>(decoder), width, height, + animated, isNinePatch); +} + +static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, + jobject fileDescriptor, jboolean preferAnimation, jobject source) { +#ifndef __ANDROID__ // LayoutLib for Windows does not support F_DUPFD_CLOEXEC + return throw_exception(env, kSourceException, "Only supported on Android", nullptr, source); +#else + int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); + + struct stat fdStat; + if (fstat(descriptor, &fdStat) == -1) { + return throw_exception(env, kSourceMalformedData, + "broken file descriptor; fstat returned -1", nullptr, source); + } + + int dupDescriptor = fcntl(descriptor, F_DUPFD_CLOEXEC, 0); + FILE* file = fdopen(dupDescriptor, "r"); + if (file == NULL) { + close(dupDescriptor); + return throw_exception(env, kSourceMalformedData, "Could not open file", + nullptr, source); + } + + std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file)); + return native_create(env, std::move(fileStream), source, preferAnimation); +#endif +} + +static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/, + jobject is, jbyteArray storage, jboolean preferAnimation, jobject source) { + std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage, false)); + + if (!stream.get()) { + return throw_exception(env, kSourceMalformedData, "Failed to create a stream", + nullptr, source); + } + + std::unique_ptr<SkStream> bufferedStream( + SkFrontBufferedStream::Make(std::move(stream), + SkCodec::MinBufferedBytesNeeded())); + return native_create(env, std::move(bufferedStream), source, preferAnimation); +} + +static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/, + jlong assetPtr, jboolean preferAnimation, jobject source) { + Asset* asset = reinterpret_cast<Asset*>(assetPtr); + std::unique_ptr<SkStream> stream(new AssetStreamAdaptor(asset)); + return native_create(env, std::move(stream), source, preferAnimation); +} + +static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/, + jobject jbyteBuffer, jint initialPosition, jint limit, + jboolean preferAnimation, jobject source) { + std::unique_ptr<SkStream> stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer, + initialPosition, limit); + if (!stream) { + return throw_exception(env, kSourceMalformedData, "Failed to read ByteBuffer", + nullptr, source); + } + return native_create(env, std::move(stream), source, preferAnimation); +} + +static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/, + jbyteArray byteArray, jint offset, jint length, + jboolean preferAnimation, jobject source) { + std::unique_ptr<SkStream> stream(CreateByteArrayStreamAdaptor(env, byteArray, offset, length)); + return native_create(env, std::move(stream), source, preferAnimation); +} + +jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas) { + jobject jcanvas = env->NewObject(gCanvas_class, gCanvas_constructorMethodID, + reinterpret_cast<jlong>(canvas.get())); + if (!jcanvas) { + doThrowOOME(env, "Failed to create Java Canvas for PostProcess!"); + return kUnknown; + } + + // jcanvas now owns canvas. + canvas.release(); + + return env->CallIntMethod(jimageDecoder, gImageDecoder_postProcessMethodID, jcanvas); +} + +static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jobject jdecoder, jboolean jpostProcess, + jint targetWidth, jint targetHeight, jobject jsubset, + jboolean requireMutable, jint allocator, + jboolean requireUnpremul, jboolean preferRamOverQuality, + jboolean asAlphaMask, jlong colorSpaceHandle, + jboolean extended) { + auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr); + if (!decoder->setTargetSize(targetWidth, targetHeight)) { + doThrowISE(env, "Could not scale to target size!"); + return nullptr; + } + if (requireUnpremul && !decoder->setUnpremultipliedRequired(true)) { + doThrowISE(env, "Cannot scale unpremultiplied pixels!"); + return nullptr; + } + + SkColorType colorType = kN32_SkColorType; + if (asAlphaMask && decoder->gray()) { + // We have to trick Skia to decode this to a single channel. + colorType = kGray_8_SkColorType; + } else if (preferRamOverQuality) { + // FIXME: The post-process might add alpha, which would make a 565 + // result incorrect. If we call the postProcess before now and record + // to a picture, we can know whether alpha was added, and if not, we + // can still use 565. + if (decoder->opaque() && !jpostProcess) { + // If the final result will be hardware, decoding to 565 and then + // uploading to the gpu as 8888 will not save memory. This still + // may save us from using F16, but do not go down to 565. + if (allocator != kHardware_Allocator && + (allocator != kDefault_Allocator || requireMutable)) { + colorType = kRGB_565_SkColorType; + } + } + // Otherwise, stick with N32 + } else if (extended) { + colorType = kRGBA_F16_SkColorType; + } else { + colorType = decoder->mCodec->computeOutputColorType(colorType); + } + + const bool isHardware = !requireMutable + && (allocator == kDefault_Allocator || + allocator == kHardware_Allocator) + && colorType != kGray_8_SkColorType; + + if (colorType == kRGBA_F16_SkColorType && isHardware && + !uirenderer::HardwareBitmapUploader::hasFP16Support()) { + colorType = kN32_SkColorType; + } + + if (!decoder->setOutColorType(colorType)) { + doThrowISE(env, "Failed to set out color type!"); + return nullptr; + } + + { + sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); + colorSpace = decoder->mCodec->computeOutputColorSpace(colorType, colorSpace); + decoder->setOutColorSpace(std::move(colorSpace)); + } + + if (jsubset) { + SkIRect subset; + GraphicsJNI::jrect_to_irect(env, jsubset, &subset); + if (!decoder->setCropRect(&subset)) { + doThrowISE(env, "Invalid crop rect!"); + return nullptr; + } + } + + SkImageInfo bitmapInfo = decoder->getOutputInfo(); + if (asAlphaMask && colorType == kGray_8_SkColorType) { + bitmapInfo = bitmapInfo.makeColorType(kAlpha_8_SkColorType); + } + + SkBitmap bm; + if (!bm.setInfo(bitmapInfo)) { + doThrowIOE(env, "Failed to setInfo properly"); + return nullptr; + } + + sk_sp<Bitmap> nativeBitmap; + if (allocator == kSharedMemory_Allocator) { + nativeBitmap = Bitmap::allocateAshmemBitmap(&bm); + } else { + nativeBitmap = Bitmap::allocateHeapBitmap(&bm); + } + if (!nativeBitmap) { + SkString msg; + msg.printf("OOM allocating Bitmap with dimensions %i x %i", + bitmapInfo.width(), bitmapInfo.height()); + doThrowOOME(env, msg.c_str()); + return nullptr; + } + + SkCodec::Result result = decoder->decode(bm.getPixels(), bm.rowBytes()); + jthrowable jexception = get_and_clear_exception(env); + int onPartialImageError = jexception ? kSourceException + : 0; // No error. + switch (result) { + case SkCodec::kSuccess: + // Ignore the exception, since the decode was successful anyway. + jexception = nullptr; + onPartialImageError = 0; + break; + case SkCodec::kIncompleteInput: + if (!jexception) { + onPartialImageError = kSourceIncomplete; + } + break; + case SkCodec::kErrorInInput: + if (!jexception) { + onPartialImageError = kSourceMalformedData; + } + break; + default: + SkString msg; + msg.printf("getPixels failed with error %s", SkCodec::ResultToString(result)); + doThrowIOE(env, msg.c_str()); + return nullptr; + } + + if (onPartialImageError) { + env->CallVoidMethod(jdecoder, gCallback_onPartialImageMethodID, onPartialImageError, + jexception); + if (env->ExceptionCheck()) { + return nullptr; + } + } + + jbyteArray ninePatchChunk = nullptr; + jobject ninePatchInsets = nullptr; + + // Ignore ninepatch when post-processing. + if (!jpostProcess) { + // FIXME: Share more code with BitmapFactory.cpp. + auto* peeker = reinterpret_cast<NinePatchPeeker*>(decoder->mPeeker.get()); + if (peeker->mPatch != nullptr) { + size_t ninePatchArraySize = peeker->mPatch->serializedSize(); + ninePatchChunk = env->NewByteArray(ninePatchArraySize); + if (ninePatchChunk == nullptr) { + doThrowOOME(env, "Failed to allocate nine patch chunk."); + return nullptr; + } + + env->SetByteArrayRegion(ninePatchChunk, 0, peeker->mPatchSize, + reinterpret_cast<jbyte*>(peeker->mPatch)); + } + + if (peeker->mHasInsets) { + ninePatchInsets = peeker->createNinePatchInsets(env, 1.0f); + if (ninePatchInsets == nullptr) { + doThrowOOME(env, "Failed to allocate nine patch insets."); + return nullptr; + } + } + } + + if (jpostProcess) { + std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm)); + + jint pixelFormat = postProcessAndRelease(env, jdecoder, std::move(canvas)); + if (env->ExceptionCheck()) { + return nullptr; + } + + SkAlphaType newAlphaType = bm.alphaType(); + switch (pixelFormat) { + case kUnknown: + break; + case kTranslucent: + newAlphaType = kPremul_SkAlphaType; + break; + case kOpaque: + newAlphaType = kOpaque_SkAlphaType; + break; + default: + SkString msg; + msg.printf("invalid return from postProcess: %i", pixelFormat); + doThrowIAE(env, msg.c_str()); + return nullptr; + } + + if (newAlphaType != bm.alphaType()) { + if (!bm.setAlphaType(newAlphaType)) { + SkString msg; + msg.printf("incompatible return from postProcess: %i", pixelFormat); + doThrowIAE(env, msg.c_str()); + return nullptr; + } + nativeBitmap->setAlphaType(newAlphaType); + } + } + + int bitmapCreateFlags = 0x0; + if (!requireUnpremul) { + // Even if the image is opaque, setting this flag means that + // if alpha is added (e.g. by PostProcess), it will be marked as + // premultiplied. + bitmapCreateFlags |= bitmap::kBitmapCreateFlag_Premultiplied; + } + + if (requireMutable) { + bitmapCreateFlags |= bitmap::kBitmapCreateFlag_Mutable; + } else { + if (isHardware) { + sk_sp<Bitmap> hwBitmap = Bitmap::allocateHardwareBitmap(bm); + if (hwBitmap) { + hwBitmap->setImmutable(); + return bitmap::createBitmap(env, hwBitmap.release(), bitmapCreateFlags, + ninePatchChunk, ninePatchInsets); + } + if (allocator == kHardware_Allocator) { + doThrowOOME(env, "failed to allocate hardware Bitmap!"); + return nullptr; + } + // If we failed to create a hardware bitmap, go ahead and create a + // software one. + } + + nativeBitmap->setImmutable(); + } + return bitmap::createBitmap(env, nativeBitmap.release(), bitmapCreateFlags, ninePatchChunk, + ninePatchInsets); +} + +static jobject ImageDecoder_nGetSampledSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jint sampleSize) { + auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr); + SkISize size = decoder->mCodec->getSampledDimensions(sampleSize); + return env->NewObject(gSize_class, gSize_constructorMethodID, size.width(), size.height()); +} + +static void ImageDecoder_nGetPadding(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jobject outPadding) { + auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr); + reinterpret_cast<NinePatchPeeker*>(decoder->mPeeker.get())->getPadding(env, outPadding); +} + +static void ImageDecoder_nClose(JNIEnv* /*env*/, jobject /*clazz*/, jlong nativePtr) { + delete reinterpret_cast<ImageDecoder*>(nativePtr); +} + +static jstring ImageDecoder_nGetMimeType(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr); + return getMimeTypeAsJavaString(env, decoder->mCodec->getEncodedFormat()); +} + +static jobject ImageDecoder_nGetColorSpace(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* codec = reinterpret_cast<ImageDecoder*>(nativePtr)->mCodec.get(); + auto colorType = codec->computeOutputColorType(kN32_SkColorType); + sk_sp<SkColorSpace> colorSpace = codec->computeOutputColorSpace(colorType); + return GraphicsJNI::getColorSpace(env, colorSpace.get(), colorType); +} + +static const JNINativeMethod gImageDecoderMethods[] = { + { "nCreate", "(JZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateAsset }, + { "nCreate", "(Ljava/nio/ByteBuffer;IIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer }, + { "nCreate", "([BIIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray }, + { "nCreate", "(Ljava/io/InputStream;[BZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream }, + { "nCreate", "(Ljava/io/FileDescriptor;ZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd }, + { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZJZ)Landroid/graphics/Bitmap;", + (void*) ImageDecoder_nDecodeBitmap }, + { "nGetSampledSize","(JI)Landroid/util/Size;", (void*) ImageDecoder_nGetSampledSize }, + { "nGetPadding", "(JLandroid/graphics/Rect;)V", (void*) ImageDecoder_nGetPadding }, + { "nClose", "(J)V", (void*) ImageDecoder_nClose}, + { "nGetMimeType", "(J)Ljava/lang/String;", (void*) ImageDecoder_nGetMimeType }, + { "nGetColorSpace", "(J)Landroid/graphics/ColorSpace;", (void*) ImageDecoder_nGetColorSpace }, +}; + +int register_android_graphics_ImageDecoder(JNIEnv* env) { + gImageDecoder_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder")); + gImageDecoder_constructorMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "<init>", "(JIIZZ)V"); + gImageDecoder_postProcessMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "postProcessAndRelease", "(Landroid/graphics/Canvas;)I"); + + gSize_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/util/Size")); + gSize_constructorMethodID = GetMethodIDOrDie(env, gSize_class, "<init>", "(II)V"); + + gDecodeException_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$DecodeException")); + gDecodeException_constructorMethodID = GetMethodIDOrDie(env, gDecodeException_class, "<init>", "(ILjava/lang/String;Ljava/lang/Throwable;Landroid/graphics/ImageDecoder$Source;)V"); + + gCallback_onPartialImageMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "onPartialImage", "(ILjava/lang/Throwable;)V"); + + gCanvas_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Canvas")); + gCanvas_constructorMethodID = GetMethodIDOrDie(env, gCanvas_class, "<init>", "(J)V"); + gCanvas_releaseMethodID = GetMethodIDOrDie(env, gCanvas_class, "release", "()V"); + + return android::RegisterMethodsOrDie(env, "android/graphics/ImageDecoder", gImageDecoderMethods, + NELEM(gImageDecoderMethods)); +} diff --git a/libs/hwui/jni/ImageDecoder.h b/libs/hwui/jni/ImageDecoder.h new file mode 100644 index 000000000000..8a7fa79503ba --- /dev/null +++ b/libs/hwui/jni/ImageDecoder.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2018 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. + */ + +#include <hwui/Canvas.h> + +#include <jni.h> + +// Creates a Java Canvas object from canvas, calls jimageDecoder's PostProcess on it, and then +// releases the Canvas. +// Caller needs to check for exceptions. +jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, + std::unique_ptr<android::Canvas> canvas); diff --git a/libs/hwui/jni/Interpolator.cpp b/libs/hwui/jni/Interpolator.cpp new file mode 100644 index 000000000000..146d634a297c --- /dev/null +++ b/libs/hwui/jni/Interpolator.cpp @@ -0,0 +1,84 @@ +#include "GraphicsJNI.h" +#include "SkInterpolator.h" + +static jlong Interpolator_constructor(JNIEnv* env, jobject clazz, jint valueCount, jint frameCount) +{ + return reinterpret_cast<jlong>(new SkInterpolator(valueCount, frameCount)); +} + +static void Interpolator_destructor(JNIEnv* env, jobject clazz, jlong interpHandle) +{ + SkInterpolator* interp = reinterpret_cast<SkInterpolator*>(interpHandle); + delete interp; +} + +static void Interpolator_reset(JNIEnv* env, jobject clazz, jlong interpHandle, jint valueCount, jint frameCount) +{ + SkInterpolator* interp = reinterpret_cast<SkInterpolator*>(interpHandle); + interp->reset(valueCount, frameCount); +} + +static void Interpolator_setKeyFrame(JNIEnv* env, jobject clazz, jlong interpHandle, jint index, jint msec, jfloatArray valueArray, jfloatArray blendArray) +{ + SkInterpolator* interp = reinterpret_cast<SkInterpolator*>(interpHandle); + + AutoJavaFloatArray autoValues(env, valueArray); + AutoJavaFloatArray autoBlend(env, blendArray, 4); +#ifdef SK_SCALAR_IS_FLOAT + SkScalar* scalars = autoValues.ptr(); + SkScalar* blend = autoBlend.ptr(); +#else + #error Need to convert float array to SkScalar array before calling the following function. +#endif + + interp->setKeyFrame(index, msec, scalars, blend); +} + +static void Interpolator_setRepeatMirror(JNIEnv* env, jobject clazz, jlong interpHandle, jfloat repeatCount, jboolean mirror) +{ + SkInterpolator* interp = reinterpret_cast<SkInterpolator*>(interpHandle); + if (repeatCount > 32000) + repeatCount = 32000; + + interp->setRepeatCount(repeatCount); + interp->setMirror(mirror != 0); +} + +static jint Interpolator_timeToValues(JNIEnv* env, jobject clazz, jlong interpHandle, jint msec, jfloatArray valueArray) +{ + SkInterpolator* interp = reinterpret_cast<SkInterpolator*>(interpHandle); + SkInterpolatorBase::Result result; + + float* values = valueArray ? env->GetFloatArrayElements(valueArray, NULL) : NULL; + result = interp->timeToValues(msec, (SkScalar*)values); + + if (valueArray) { + int n = env->GetArrayLength(valueArray); + for (int i = 0; i < n; i++) { + values[i] = SkScalarToFloat(*(SkScalar*)&values[i]); + } + env->ReleaseFloatArrayElements(valueArray, values, 0); + } + + return static_cast<jint>(result); +} + +// ---------------------------------------------------------------------------- + +/* + * JNI registration. + */ +static const JNINativeMethod gInterpolatorMethods[] = { + { "nativeConstructor", "(II)J", (void*)Interpolator_constructor }, + { "nativeDestructor", "(J)V", (void*)Interpolator_destructor }, + { "nativeReset", "(JII)V", (void*)Interpolator_reset }, + { "nativeSetKeyFrame", "(JII[F[F)V", (void*)Interpolator_setKeyFrame }, + { "nativeSetRepeatMirror", "(JFZ)V", (void*)Interpolator_setRepeatMirror }, + { "nativeTimeToValues", "(JI[F)I", (void*)Interpolator_timeToValues } +}; + +int register_android_graphics_Interpolator(JNIEnv* env) +{ + return android::RegisterMethodsOrDie(env, "android/graphics/Interpolator", + gInterpolatorMethods, NELEM(gInterpolatorMethods)); +} diff --git a/libs/hwui/jni/MaskFilter.cpp b/libs/hwui/jni/MaskFilter.cpp new file mode 100644 index 000000000000..5383032e0f77 --- /dev/null +++ b/libs/hwui/jni/MaskFilter.cpp @@ -0,0 +1,90 @@ +#include "GraphicsJNI.h" +#include "SkMaskFilter.h" +#include "SkBlurMask.h" +#include "SkBlurMaskFilter.h" +#include "SkTableMaskFilter.h" + +static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) { + if (NULL == ptr) { + doThrowIAE(env); + } +} + +class SkMaskFilterGlue { +public: + static void destructor(JNIEnv* env, jobject, jlong filterHandle) { + SkMaskFilter* filter = reinterpret_cast<SkMaskFilter *>(filterHandle); + SkSafeUnref(filter); + } + + static jlong createBlur(JNIEnv* env, jobject, jfloat radius, jint blurStyle) { + SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius); + SkMaskFilter* filter = SkMaskFilter::MakeBlur((SkBlurStyle)blurStyle, sigma).release(); + ThrowIAE_IfNull(env, filter); + return reinterpret_cast<jlong>(filter); + } + + static jlong createEmboss(JNIEnv* env, jobject, jfloatArray dirArray, jfloat ambient, jfloat specular, jfloat radius) { + SkScalar direction[3]; + + AutoJavaFloatArray autoDir(env, dirArray, 3); + float* values = autoDir.ptr(); + for (int i = 0; i < 3; i++) { + direction[i] = values[i]; + } + + SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius); + SkMaskFilter* filter = SkBlurMaskFilter::MakeEmboss(sigma, + direction, ambient, specular).release(); + ThrowIAE_IfNull(env, filter); + return reinterpret_cast<jlong>(filter); + } + + static jlong createTable(JNIEnv* env, jobject, jbyteArray jtable) { + AutoJavaByteArray autoTable(env, jtable, 256); + SkMaskFilter* filter = SkTableMaskFilter::Create((const uint8_t*)autoTable.ptr()); + return reinterpret_cast<jlong>(filter); + } + + static jlong createClipTable(JNIEnv* env, jobject, jint min, jint max) { + SkMaskFilter* filter = SkTableMaskFilter::CreateClip(min, max); + return reinterpret_cast<jlong>(filter); + } + + static jlong createGammaTable(JNIEnv* env, jobject, jfloat gamma) { + SkMaskFilter* filter = SkTableMaskFilter::CreateGamma(gamma); + return reinterpret_cast<jlong>(filter); + } +}; + +static const JNINativeMethod gMaskFilterMethods[] = { + { "nativeDestructor", "(J)V", (void*)SkMaskFilterGlue::destructor } +}; + +static const JNINativeMethod gBlurMaskFilterMethods[] = { + { "nativeConstructor", "(FI)J", (void*)SkMaskFilterGlue::createBlur } +}; + +static const JNINativeMethod gEmbossMaskFilterMethods[] = { + { "nativeConstructor", "([FFFF)J", (void*)SkMaskFilterGlue::createEmboss } +}; + +static const JNINativeMethod gTableMaskFilterMethods[] = { + { "nativeNewTable", "([B)J", (void*)SkMaskFilterGlue::createTable }, + { "nativeNewClip", "(II)J", (void*)SkMaskFilterGlue::createClipTable }, + { "nativeNewGamma", "(F)J", (void*)SkMaskFilterGlue::createGammaTable } +}; + +int register_android_graphics_MaskFilter(JNIEnv* env) +{ + android::RegisterMethodsOrDie(env, "android/graphics/MaskFilter", gMaskFilterMethods, + NELEM(gMaskFilterMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/BlurMaskFilter", gBlurMaskFilterMethods, + NELEM(gBlurMaskFilterMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/EmbossMaskFilter", + gEmbossMaskFilterMethods, NELEM(gEmbossMaskFilterMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/TableMaskFilter", gTableMaskFilterMethods, + NELEM(gTableMaskFilterMethods)); + + return 0; +} diff --git a/libs/hwui/jni/MimeType.h b/libs/hwui/jni/MimeType.h new file mode 100644 index 000000000000..fdd510cfeb79 --- /dev/null +++ b/libs/hwui/jni/MimeType.h @@ -0,0 +1,22 @@ +/* + * Copyright 2019 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. + */ + +#pragma once + +#include <cutils/compiler.h> +#include "SkEncodedImageFormat.h" + +ANDROID_API const char* getMimeType(SkEncodedImageFormat); diff --git a/libs/hwui/jni/Movie.cpp b/libs/hwui/jni/Movie.cpp new file mode 100644 index 000000000000..ede0ca8cda5b --- /dev/null +++ b/libs/hwui/jni/Movie.cpp @@ -0,0 +1,164 @@ +#include "CreateJavaOutputStreamAdaptor.h" +#include "GraphicsJNI.h" +#include <nativehelper/ScopedLocalRef.h> +#include "SkFrontBufferedStream.h" +#include "Movie.h" +#include "SkStream.h" +#include "SkUtils.h" +#include "Utils.h" + +#include <androidfw/Asset.h> +#include <androidfw/ResourceTypes.h> +#include <hwui/Canvas.h> +#include <hwui/Paint.h> +#include <netinet/in.h> + +static jclass gMovie_class; +static jmethodID gMovie_constructorMethodID; +static jfieldID gMovie_nativeInstanceID; + +jobject create_jmovie(JNIEnv* env, Movie* moov) { + if (NULL == moov) { + return NULL; + } + return env->NewObject(gMovie_class, gMovie_constructorMethodID, + static_cast<jlong>(reinterpret_cast<uintptr_t>(moov))); +} + +static Movie* J2Movie(JNIEnv* env, jobject movie) { + SkASSERT(env); + SkASSERT(movie); + SkASSERT(env->IsInstanceOf(movie, gMovie_class)); + Movie* m = (Movie*)env->GetLongField(movie, gMovie_nativeInstanceID); + SkASSERT(m); + return m; +} + +/////////////////////////////////////////////////////////////////////////////// + +static jint movie_width(JNIEnv* env, jobject movie) { + NPE_CHECK_RETURN_ZERO(env, movie); + return static_cast<jint>(J2Movie(env, movie)->width()); +} + +static jint movie_height(JNIEnv* env, jobject movie) { + NPE_CHECK_RETURN_ZERO(env, movie); + return static_cast<jint>(J2Movie(env, movie)->height()); +} + +static jboolean movie_isOpaque(JNIEnv* env, jobject movie) { + NPE_CHECK_RETURN_ZERO(env, movie); + return J2Movie(env, movie)->isOpaque() ? JNI_TRUE : JNI_FALSE; +} + +static jint movie_duration(JNIEnv* env, jobject movie) { + NPE_CHECK_RETURN_ZERO(env, movie); + return static_cast<jint>(J2Movie(env, movie)->duration()); +} + +static jboolean movie_setTime(JNIEnv* env, jobject movie, jint ms) { + NPE_CHECK_RETURN_ZERO(env, movie); + return J2Movie(env, movie)->setTime(ms) ? JNI_TRUE : JNI_FALSE; +} + +static void movie_draw(JNIEnv* env, jobject movie, jlong canvasHandle, + jfloat fx, jfloat fy, jlong paintHandle) { + NPE_CHECK_RETURN_VOID(env, movie); + + android::Canvas* c = reinterpret_cast<android::Canvas*>(canvasHandle); + const android::Paint* p = reinterpret_cast<android::Paint*>(paintHandle); + + // Canvas should never be NULL. However paint is an optional parameter and + // therefore may be NULL. + SkASSERT(c != NULL); + + Movie* m = J2Movie(env, movie); + const SkBitmap& b = m->bitmap(); + sk_sp<android::Bitmap> wrapper = android::Bitmap::createFrom(b.info(), *b.pixelRef()); + c->drawBitmap(*wrapper, fx, fy, p); +} + +static jobject movie_decodeAsset(JNIEnv* env, jobject clazz, jlong native_asset) { + android::Asset* asset = reinterpret_cast<android::Asset*>(native_asset); + if (asset == NULL) return NULL; + android::AssetStreamAdaptor stream(asset); + Movie* moov = Movie::DecodeStream(&stream); + return create_jmovie(env, moov); +} + +static jobject movie_decodeStream(JNIEnv* env, jobject clazz, jobject istream) { + + NPE_CHECK_RETURN_ZERO(env, istream); + + jbyteArray byteArray = env->NewByteArray(16*1024); + ScopedLocalRef<jbyteArray> scoper(env, byteArray); + SkStream* strm = CreateJavaInputStreamAdaptor(env, istream, byteArray); + if (NULL == strm) { + return 0; + } + + // Need to buffer enough input to be able to rewind as much as might be read by a decoder + // trying to determine the stream's format. The only decoder for movies is GIF, which + // will only read 6. + // FIXME: Get this number from SkImageDecoder + // bufferedStream takes ownership of strm + std::unique_ptr<SkStreamRewindable> bufferedStream(SkFrontBufferedStream::Make( + std::unique_ptr<SkStream>(strm), 6)); + SkASSERT(bufferedStream.get() != NULL); + + Movie* moov = Movie::DecodeStream(bufferedStream.get()); + return create_jmovie(env, moov); +} + +static jobject movie_decodeByteArray(JNIEnv* env, jobject clazz, + jbyteArray byteArray, + jint offset, jint length) { + + NPE_CHECK_RETURN_ZERO(env, byteArray); + + int totalLength = env->GetArrayLength(byteArray); + if ((offset | length) < 0 || offset + length > totalLength) { + doThrowAIOOBE(env); + return 0; + } + + AutoJavaByteArray ar(env, byteArray); + Movie* moov = Movie::DecodeMemory(ar.ptr() + offset, length); + return create_jmovie(env, moov); +} + +static void movie_destructor(JNIEnv* env, jobject, jlong movieHandle) { + Movie* movie = (Movie*) movieHandle; + delete movie; +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gMethods[] = { + { "width", "()I", (void*)movie_width }, + { "height", "()I", (void*)movie_height }, + { "isOpaque", "()Z", (void*)movie_isOpaque }, + { "duration", "()I", (void*)movie_duration }, + { "setTime", "(I)Z", (void*)movie_setTime }, + { "nDraw", "(JFFJ)V", + (void*)movie_draw }, + { "nativeDecodeAsset", "(J)Landroid/graphics/Movie;", + (void*)movie_decodeAsset }, + { "nativeDecodeStream", "(Ljava/io/InputStream;)Landroid/graphics/Movie;", + (void*)movie_decodeStream }, + { "nativeDestructor","(J)V", (void*)movie_destructor }, + { "decodeByteArray", "([BII)Landroid/graphics/Movie;", + (void*)movie_decodeByteArray }, +}; + +int register_android_graphics_Movie(JNIEnv* env) +{ + gMovie_class = android::FindClassOrDie(env, "android/graphics/Movie"); + gMovie_class = android::MakeGlobalRefOrDie(env, gMovie_class); + + gMovie_constructorMethodID = android::GetMethodIDOrDie(env, gMovie_class, "<init>", "(J)V"); + + gMovie_nativeInstanceID = android::GetFieldIDOrDie(env, gMovie_class, "mNativeMovie", "J"); + + return android::RegisterMethodsOrDie(env, "android/graphics/Movie", gMethods, NELEM(gMethods)); +} diff --git a/libs/hwui/jni/Movie.h b/libs/hwui/jni/Movie.h new file mode 100644 index 000000000000..736890d5215e --- /dev/null +++ b/libs/hwui/jni/Movie.h @@ -0,0 +1,79 @@ + +/* + * Copyright 2008 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#ifndef Movie_DEFINED +#define Movie_DEFINED + +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkRefCnt.h" + +class SkStreamRewindable; + +class Movie : public SkRefCnt { +public: + /** Try to create a movie from the stream. If the stream format is not + supported, return NULL. + */ + static Movie* DecodeStream(SkStreamRewindable*); + /** Try to create a movie from the specified file path. If the file is not + found, or the format is not supported, return NULL. If a movie is + returned, the stream may be retained by the movie (via ref()) until + the movie is finished with it (by calling unref()). + */ + static Movie* DecodeFile(const char path[]); + /** Try to create a movie from the specified memory. + If the format is not supported, return NULL. If a movie is returned, + the data will have been read or copied, and so the caller may free + it. + */ + static Movie* DecodeMemory(const void* data, size_t length); + + SkMSec duration(); + int width(); + int height(); + int isOpaque(); + + /** Specify the time code (between 0...duration) to sample a bitmap + from the movie. Returns true if this time code generated a different + bitmap/frame from the previous state (i.e. true means you need to + redraw). + */ + bool setTime(SkMSec); + + // return the right bitmap for the current time code + const SkBitmap& bitmap(); + +protected: + struct Info { + SkMSec fDuration; + int fWidth; + int fHeight; + bool fIsOpaque; + }; + + virtual bool onGetInfo(Info*) = 0; + virtual bool onSetTime(SkMSec) = 0; + virtual bool onGetBitmap(SkBitmap*) = 0; + + // visible for subclasses + Movie(); + +private: + Info fInfo; + SkMSec fCurrTime; + SkBitmap fBitmap; + bool fNeedBitmap; + + void ensureInfo(); + + typedef SkRefCnt INHERITED; +}; + +#endif diff --git a/libs/hwui/jni/MovieImpl.cpp b/libs/hwui/jni/MovieImpl.cpp new file mode 100644 index 000000000000..ae9e04e617b0 --- /dev/null +++ b/libs/hwui/jni/MovieImpl.cpp @@ -0,0 +1,94 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "Movie.h" +#include "SkCanvas.h" +#include "SkPaint.h" + +// We should never see this in normal operation since our time values are +// 0-based. So we use it as a sentinal. +#define UNINITIALIZED_MSEC ((SkMSec)-1) + +Movie::Movie() +{ + fInfo.fDuration = UNINITIALIZED_MSEC; // uninitialized + fCurrTime = UNINITIALIZED_MSEC; // uninitialized + fNeedBitmap = true; +} + +void Movie::ensureInfo() +{ + if (fInfo.fDuration == UNINITIALIZED_MSEC && !this->onGetInfo(&fInfo)) + memset(&fInfo, 0, sizeof(fInfo)); // failure +} + +SkMSec Movie::duration() +{ + this->ensureInfo(); + return fInfo.fDuration; +} + +int Movie::width() +{ + this->ensureInfo(); + return fInfo.fWidth; +} + +int Movie::height() +{ + this->ensureInfo(); + return fInfo.fHeight; +} + +int Movie::isOpaque() +{ + this->ensureInfo(); + return fInfo.fIsOpaque; +} + +bool Movie::setTime(SkMSec time) +{ + SkMSec dur = this->duration(); + if (time > dur) + time = dur; + + bool changed = false; + if (time != fCurrTime) + { + fCurrTime = time; + changed = this->onSetTime(time); + fNeedBitmap |= changed; + } + return changed; +} + +const SkBitmap& Movie::bitmap() +{ + if (fCurrTime == UNINITIALIZED_MSEC) // uninitialized + this->setTime(0); + + if (fNeedBitmap) + { + if (!this->onGetBitmap(&fBitmap)) // failure + fBitmap.reset(); + fNeedBitmap = false; + } + return fBitmap; +} + +//////////////////////////////////////////////////////////////////// + +#include "SkStream.h" + +Movie* Movie::DecodeMemory(const void* data, size_t length) { + SkMemoryStream stream(data, length, false); + return Movie::DecodeStream(&stream); +} + +Movie* Movie::DecodeFile(const char path[]) { + std::unique_ptr<SkStreamRewindable> stream = SkStream::MakeFromFile(path); + return stream ? Movie::DecodeStream(stream.get()) : nullptr; +} diff --git a/libs/hwui/jni/NinePatch.cpp b/libs/hwui/jni/NinePatch.cpp new file mode 100644 index 000000000000..6942017d5f27 --- /dev/null +++ b/libs/hwui/jni/NinePatch.cpp @@ -0,0 +1,168 @@ +/* +** +** Copyright 2006, 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. +*/ + +#undef LOG_TAG +#define LOG_TAG "9patch" +#define LOG_NDEBUG 1 + +#include <androidfw/ResourceTypes.h> +#include <hwui/Canvas.h> +#include <hwui/Paint.h> +#include <utils/Log.h> + +#include "SkCanvas.h" +#include "SkLatticeIter.h" +#include "SkRegion.h" +#include "GraphicsJNI.h" +#include "NinePatchPeeker.h" +#include "NinePatchUtils.h" + +jclass gInsetStruct_class; +jmethodID gInsetStruct_constructorMethodID; + +using namespace android; + +/** + * IMPORTANT NOTE: 9patch chunks can be manipuated either as an array of bytes + * or as a Res_png_9patch instance. It is important to note that the size of the + * array required to hold a 9patch chunk is greater than sizeof(Res_png_9patch). + * The code below manipulates chunks as Res_png_9patch* types to draw and as + * int8_t* to allocate and free the backing storage. + */ + +class SkNinePatchGlue { +public: + static jboolean isNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj) { + if (NULL == obj) { + return JNI_FALSE; + } + if (env->GetArrayLength(obj) < (int)sizeof(Res_png_9patch)) { + return JNI_FALSE; + } + const jbyte* array = env->GetByteArrayElements(obj, 0); + if (array != NULL) { + const Res_png_9patch* chunk = reinterpret_cast<const Res_png_9patch*>(array); + int8_t wasDeserialized = chunk->wasDeserialized; + env->ReleaseByteArrayElements(obj, const_cast<jbyte*>(array), JNI_ABORT); + return (wasDeserialized != -1) ? JNI_TRUE : JNI_FALSE; + } + return JNI_FALSE; + } + + static jlong validateNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj) { + size_t chunkSize = env->GetArrayLength(obj); + if (chunkSize < (int) (sizeof(Res_png_9patch))) { + jniThrowRuntimeException(env, "Array too small for chunk."); + return NULL; + } + + int8_t* storage = new int8_t[chunkSize]; + // This call copies the content of the jbyteArray + env->GetByteArrayRegion(obj, 0, chunkSize, reinterpret_cast<jbyte*>(storage)); + // Deserialize in place, return the array we just allocated + return reinterpret_cast<jlong>(Res_png_9patch::deserialize(storage)); + } + + static void finalize(JNIEnv* env, jobject, jlong patchHandle) { + int8_t* patch = reinterpret_cast<int8_t*>(patchHandle); + delete[] patch; + } + + static jlong getTransparentRegion(JNIEnv* env, jobject, jlong bitmapPtr, + jlong chunkHandle, jobject dstRect) { + Res_png_9patch* chunk = reinterpret_cast<Res_png_9patch*>(chunkHandle); + SkASSERT(chunk); + + SkBitmap bitmap; + bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); + SkRect dst; + GraphicsJNI::jrect_to_rect(env, dstRect, &dst); + + SkCanvas::Lattice lattice; + SkIRect src = SkIRect::MakeWH(bitmap.width(), bitmap.height()); + lattice.fBounds = &src; + NinePatchUtils::SetLatticeDivs(&lattice, *chunk, bitmap.width(), bitmap.height()); + lattice.fRectTypes = nullptr; + lattice.fColors = nullptr; + + SkRegion* region = nullptr; + if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), lattice)) { + SkLatticeIter iter(lattice, dst); + if (iter.numRectsToDraw() == chunk->numColors) { + SkRect dummy; + SkRect iterDst; + int index = 0; + while (iter.next(&dummy, &iterDst)) { + if (0 == chunk->getColors()[index++] && !iterDst.isEmpty()) { + if (!region) { + region = new SkRegion(); + } + + region->op(iterDst.round(), SkRegion::kUnion_Op); + } + } + } + } + + return reinterpret_cast<jlong>(region); + } + +}; + +jobject NinePatchPeeker::createNinePatchInsets(JNIEnv* env, float scale) const { + if (!mHasInsets) { + return nullptr; + } + + return env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID, + mOpticalInsets[0], mOpticalInsets[1], + mOpticalInsets[2], mOpticalInsets[3], + mOutlineInsets[0], mOutlineInsets[1], + mOutlineInsets[2], mOutlineInsets[3], + mOutlineRadius, mOutlineAlpha, scale); +} + +void NinePatchPeeker::getPadding(JNIEnv* env, jobject outPadding) const { + if (mPatch) { + GraphicsJNI::set_jrect(env, outPadding, + mPatch->paddingLeft, mPatch->paddingTop, + mPatch->paddingRight, mPatch->paddingBottom); + + } else { + GraphicsJNI::set_jrect(env, outPadding, -1, -1, -1, -1); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gNinePatchMethods[] = { + { "isNinePatchChunk", "([B)Z", (void*) SkNinePatchGlue::isNinePatchChunk }, + { "validateNinePatchChunk", "([B)J", + (void*) SkNinePatchGlue::validateNinePatchChunk }, + { "nativeFinalize", "(J)V", (void*) SkNinePatchGlue::finalize }, + { "nativeGetTransparentRegion", "(JJLandroid/graphics/Rect;)J", + (void*) SkNinePatchGlue::getTransparentRegion } +}; + +int register_android_graphics_NinePatch(JNIEnv* env) { + gInsetStruct_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, + "android/graphics/NinePatch$InsetStruct")); + gInsetStruct_constructorMethodID = GetMethodIDOrDie(env, gInsetStruct_class, "<init>", + "(IIIIIIIIFIF)V"); + return android::RegisterMethodsOrDie(env, + "android/graphics/NinePatch", gNinePatchMethods, NELEM(gNinePatchMethods)); +} diff --git a/libs/hwui/jni/NinePatchPeeker.cpp b/libs/hwui/jni/NinePatchPeeker.cpp new file mode 100644 index 000000000000..9171fc687276 --- /dev/null +++ b/libs/hwui/jni/NinePatchPeeker.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2011 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. + */ + +#include "NinePatchPeeker.h" + +#include <SkBitmap.h> +#include <cutils/compiler.h> + +using namespace android; + +bool NinePatchPeeker::readChunk(const char tag[], const void* data, size_t length) { + if (!strcmp("npTc", tag) && length >= sizeof(Res_png_9patch)) { + Res_png_9patch* patch = (Res_png_9patch*) data; + size_t patchSize = patch->serializedSize(); + if (length != patchSize) { + return false; + } + // You have to copy the data because it is owned by the png reader + Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize); + memcpy(patchNew, patch, patchSize); + Res_png_9patch::deserialize(patchNew); + patchNew->fileToDevice(); + free(mPatch); + mPatch = patchNew; + mPatchSize = patchSize; + } else if (!strcmp("npLb", tag) && length == sizeof(int32_t) * 4) { + mHasInsets = true; + memcpy(&mOpticalInsets, data, sizeof(int32_t) * 4); + } else if (!strcmp("npOl", tag) && length == 24) { // 4 int32_ts, 1 float, 1 int32_t sized byte + mHasInsets = true; + memcpy(&mOutlineInsets, data, sizeof(int32_t) * 4); + mOutlineRadius = ((const float*)data)[4]; + mOutlineAlpha = ((const int32_t*)data)[5] & 0xff; + } + return true; // keep on decoding +} + +static void scaleDivRange(int32_t* divs, int count, float scale, int maxValue) { + for (int i = 0; i < count; i++) { + divs[i] = int32_t(divs[i] * scale + 0.5f); + if (i > 0 && divs[i] == divs[i - 1]) { + divs[i]++; // avoid collisions + } + } + + if (CC_UNLIKELY(divs[count - 1] > maxValue)) { + // if the collision avoidance above put some divs outside the bounds of the bitmap, + // slide outer stretchable divs inward to stay within bounds + int highestAvailable = maxValue; + for (int i = count - 1; i >= 0; i--) { + divs[i] = highestAvailable; + if (i > 0 && divs[i] <= divs[i-1]) { + // keep shifting + highestAvailable = divs[i] - 1; + } else { + break; + } + } + } +} + +void NinePatchPeeker::scale(float scaleX, float scaleY, int scaledWidth, int scaledHeight) { + if (!mPatch) { + return; + } + + // The max value for the divRange is one pixel less than the actual max to ensure that the size + // of the last div is not zero. A div of size 0 is considered invalid input and will not render. + if (!SkScalarNearlyEqual(scaleX, 1.0f)) { + mPatch->paddingLeft = int(mPatch->paddingLeft * scaleX + 0.5f); + mPatch->paddingRight = int(mPatch->paddingRight * scaleX + 0.5f); + scaleDivRange(mPatch->getXDivs(), mPatch->numXDivs, scaleX, scaledWidth - 1); + } + + if (!SkScalarNearlyEqual(scaleY, 1.0f)) { + mPatch->paddingTop = int(mPatch->paddingTop * scaleY + 0.5f); + mPatch->paddingBottom = int(mPatch->paddingBottom * scaleY + 0.5f); + scaleDivRange(mPatch->getYDivs(), mPatch->numYDivs, scaleY, scaledHeight - 1); + } +} diff --git a/libs/hwui/jni/NinePatchPeeker.h b/libs/hwui/jni/NinePatchPeeker.h new file mode 100644 index 000000000000..e4e58dda4783 --- /dev/null +++ b/libs/hwui/jni/NinePatchPeeker.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011 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. + */ + +#ifndef _ANDROID_GRAPHICS_NINE_PATCH_PEEKER_H_ +#define _ANDROID_GRAPHICS_NINE_PATCH_PEEKER_H_ + +#include "SkPngChunkReader.h" +#include <androidfw/ResourceTypes.h> + +#include <jni.h> + +using namespace android; + +class NinePatchPeeker : public SkPngChunkReader { +public: + NinePatchPeeker() + : mPatch(NULL) + , mPatchSize(0) + , mHasInsets(false) + , mOutlineRadius(0) + , mOutlineAlpha(0) { + memset(mOpticalInsets, 0, 4 * sizeof(int32_t)); + memset(mOutlineInsets, 0, 4 * sizeof(int32_t)); + } + + ~NinePatchPeeker() { + free(mPatch); + } + + bool readChunk(const char tag[], const void* data, size_t length) override; + + jobject createNinePatchInsets(JNIEnv*, float scale) const; + void getPadding(JNIEnv*, jobject outPadding) const; + void scale(float scaleX, float scaleY, int scaledWidth, int scaledHeight); + + Res_png_9patch* mPatch; + size_t mPatchSize; + bool mHasInsets; +private: + int32_t mOpticalInsets[4]; + int32_t mOutlineInsets[4]; + float mOutlineRadius; + uint8_t mOutlineAlpha; +}; + +#endif // _ANDROID_GRAPHICS_NINE_PATCH_PEEKER_H_ diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp new file mode 100644 index 000000000000..df8635a8fe5a --- /dev/null +++ b/libs/hwui/jni/Paint.cpp @@ -0,0 +1,1159 @@ +/* libs/android_runtime/android/graphics/Paint.cpp +** +** Copyright 2006, 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. +*/ + +#undef LOG_TAG +#define LOG_TAG "Paint" + +#include <utils/Log.h> + +#include "GraphicsJNI.h" +#include <nativehelper/ScopedStringChars.h> +#include <nativehelper/ScopedUtfChars.h> +#include <nativehelper/ScopedPrimitiveArray.h> + +#include "SkBlurDrawLooper.h" +#include "SkColorFilter.h" +#include "SkFont.h" +#include "SkFontMetrics.h" +#include "SkFontTypes.h" +#include "SkMaskFilter.h" +#include "SkPath.h" +#include "SkPathEffect.h" +#include "SkShader.h" +#include "SkBlendMode.h" +#include "unicode/uloc.h" +#include "unicode/ushape.h" +#include "utils/Blur.h" + +#include <hwui/MinikinSkia.h> +#include <hwui/MinikinUtils.h> +#include <hwui/Paint.h> +#include <hwui/Typeface.h> +#include <minikin/GraphemeBreak.h> +#include <minikin/LocaleList.h> +#include <minikin/Measurement.h> +#include <minikin/MinikinPaint.h> +#include <unicode/utf16.h> + +#include <cassert> +#include <cstring> +#include <memory> +#include <vector> + +namespace android { + +struct JMetricsID { + jfieldID top; + jfieldID ascent; + jfieldID descent; + jfieldID bottom; + jfieldID leading; +}; + +static jclass gFontMetrics_class; +static JMetricsID gFontMetrics_fieldID; + +static jclass gFontMetricsInt_class; +static JMetricsID gFontMetricsInt_fieldID; + +static void getPosTextPath(const SkFont& font, const uint16_t glyphs[], int count, + const SkPoint pos[], SkPath* dst) { + dst->reset(); + struct Rec { + SkPath* fDst; + const SkPoint* fPos; + } rec = { dst, pos }; + font.getPaths(glyphs, count, [](const SkPath* src, const SkMatrix& mx, void* ctx) { + Rec* rec = (Rec*)ctx; + if (src) { + SkMatrix tmp(mx); + tmp.postTranslate(rec->fPos->fX, rec->fPos->fY); + rec->fDst->addPath(*src, tmp); + } + rec->fPos += 1; + }, &rec); +} + +namespace PaintGlue { + enum MoveOpt { + AFTER, AT_OR_AFTER, BEFORE, AT_OR_BEFORE, AT + }; + + static void deletePaint(Paint* paint) { + delete paint; + } + + static jlong getNativeFinalizer(JNIEnv*, jobject) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&deletePaint)); + } + + static jlong init(JNIEnv* env, jobject) { + return reinterpret_cast<jlong>(new Paint); + } + + static jlong initWithPaint(JNIEnv* env, jobject clazz, jlong paintHandle) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + Paint* obj = new Paint(*paint); + return reinterpret_cast<jlong>(obj); + } + + static int breakText(JNIEnv* env, const Paint& paint, const Typeface* typeface, + const jchar text[], int count, float maxWidth, jint bidiFlags, jfloatArray jmeasured, + const bool forwardScan) { + size_t measuredCount = 0; + float measured = 0; + + std::unique_ptr<float[]> advancesArray(new float[count]); + MinikinUtils::measureText(&paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text, + 0, count, count, advancesArray.get()); + + for (int i = 0; i < count; i++) { + // traverse in the given direction + int index = forwardScan ? i : (count - i - 1); + float width = advancesArray[index]; + if (measured + width > maxWidth) { + break; + } + // properly handle clusters when scanning backwards + if (forwardScan || width != 0.0f) { + measuredCount = i + 1; + } + measured += width; + } + + if (jmeasured && env->GetArrayLength(jmeasured) > 0) { + AutoJavaFloatArray autoMeasured(env, jmeasured, 1); + jfloat* array = autoMeasured.ptr(); + array[0] = measured; + } + return measuredCount; + } + + static jint breakTextC(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray jtext, + jint index, jint count, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) { + NPE_CHECK_RETURN_ZERO(env, jtext); + + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + + bool forwardTextDirection; + if (count < 0) { + forwardTextDirection = false; + count = -count; + } + else { + forwardTextDirection = true; + } + + if ((index < 0) || (index + count > env->GetArrayLength(jtext))) { + doThrowAIOOBE(env); + return 0; + } + + const jchar* text = env->GetCharArrayElements(jtext, nullptr); + count = breakText(env, *paint, typeface, text + index, count, maxWidth, + bidiFlags, jmeasuredWidth, forwardTextDirection); + env->ReleaseCharArrayElements(jtext, const_cast<jchar*>(text), + JNI_ABORT); + return count; + } + + static jint breakTextS(JNIEnv* env, jobject clazz, jlong paintHandle, jstring jtext, + jboolean forwards, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) { + NPE_CHECK_RETURN_ZERO(env, jtext); + + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + + int count = env->GetStringLength(jtext); + const jchar* text = env->GetStringChars(jtext, nullptr); + count = breakText(env, *paint, typeface, text, count, maxWidth, bidiFlags, jmeasuredWidth, forwards); + env->ReleaseStringChars(jtext, text); + return count; + } + + static jfloat doTextAdvances(JNIEnv *env, Paint *paint, const Typeface* typeface, + const jchar *text, jint start, jint count, jint contextCount, jint bidiFlags, + jfloatArray advances, jint advancesIndex) { + NPE_CHECK_RETURN_ZERO(env, text); + + if ((start | count | contextCount | advancesIndex) < 0 || contextCount < count) { + doThrowAIOOBE(env); + return 0; + } + if (count == 0) { + return 0; + } + if (advances) { + size_t advancesLength = env->GetArrayLength(advances); + if ((size_t)(count + advancesIndex) > advancesLength) { + doThrowAIOOBE(env); + return 0; + } + } + std::unique_ptr<jfloat[]> advancesArray; + if (advances) { + advancesArray.reset(new jfloat[count]); + } + const float advance = MinikinUtils::measureText(paint, + static_cast<minikin::Bidi>(bidiFlags), typeface, text, start, count, contextCount, + advancesArray.get()); + if (advances) { + env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray.get()); + } + return advance; + } + + static jfloat getTextAdvances___CIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle, + jcharArray text, jint index, jint count, jint contextIndex, jint contextCount, + jint bidiFlags, jfloatArray advances, jint advancesIndex) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + jchar* textArray = env->GetCharArrayElements(text, nullptr); + jfloat result = doTextAdvances(env, paint, typeface, textArray + contextIndex, + index - contextIndex, count, contextCount, bidiFlags, advances, advancesIndex); + env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); + return result; + } + + static jfloat getTextAdvances__StringIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle, + jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint bidiFlags, + jfloatArray advances, jint advancesIndex) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + const jchar* textArray = env->GetStringChars(text, nullptr); + jfloat result = doTextAdvances(env, paint, typeface, textArray + contextStart, + start - contextStart, end - start, contextEnd - contextStart, bidiFlags, + advances, advancesIndex); + env->ReleaseStringChars(text, textArray); + return result; + } + + static jint doTextRunCursor(JNIEnv *env, Paint* paint, const Typeface* typeface, + const jchar *text, jint start, jint count, jint dir, jint offset, jint opt) { + minikin::GraphemeBreak::MoveOpt moveOpt = minikin::GraphemeBreak::MoveOpt(opt); + minikin::Bidi bidiFlags = dir == 1 ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR; + std::unique_ptr<float[]> advancesArray(new float[count]); + MinikinUtils::measureText(paint, bidiFlags, typeface, text, start, count, start + count, + advancesArray.get()); + size_t result = minikin::GraphemeBreak::getTextRunCursor(advancesArray.get(), text, + start, count, offset, moveOpt); + return static_cast<jint>(result); + } + + static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray text, + jint contextStart, jint contextCount, jint dir, jint offset, jint cursorOpt) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + jchar* textArray = env->GetCharArrayElements(text, nullptr); + jint result = doTextRunCursor(env, paint, typeface, textArray, + contextStart, contextCount, dir, offset, cursorOpt); + env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); + return result; + } + + static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, jlong paintHandle, + jstring text, jint contextStart, jint contextEnd, jint dir, jint offset, + jint cursorOpt) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + const jchar* textArray = env->GetStringChars(text, nullptr); + jint result = doTextRunCursor(env, paint, typeface, textArray, + contextStart, contextEnd - contextStart, dir, offset, cursorOpt); + env->ReleaseStringChars(text, textArray); + return result; + } + + class GetTextFunctor { + public: + GetTextFunctor(const minikin::Layout& layout, SkPath* path, jfloat x, jfloat y, + Paint* paint, uint16_t* glyphs, SkPoint* pos) + : layout(layout), path(path), x(x), y(y), paint(paint), glyphs(glyphs), pos(pos) { + } + + void operator()(size_t start, size_t end) { + for (size_t i = start; i < end; i++) { + glyphs[i] = layout.getGlyphId(i); + pos[i].fX = x + layout.getX(i); + pos[i].fY = y + layout.getY(i); + } + const SkFont& font = paint->getSkFont(); + if (start == 0) { + getPosTextPath(font, glyphs, end, pos, path); + } else { + getPosTextPath(font, glyphs + start, end - start, pos + start, &tmpPath); + path->addPath(tmpPath); + } + } + private: + const minikin::Layout& layout; + SkPath* path; + jfloat x; + jfloat y; + Paint* paint; + uint16_t* glyphs; + SkPoint* pos; + SkPath tmpPath; + }; + + static void getTextPath(JNIEnv* env, Paint* paint, const Typeface* typeface, const jchar* text, + jint count, jint bidiFlags, jfloat x, jfloat y, SkPath* path) { + minikin::Layout layout = MinikinUtils::doLayout( + paint, static_cast<minikin::Bidi>(bidiFlags), typeface, + text, count, // text buffer + 0, count, // draw range + 0, count, // context range + nullptr); + size_t nGlyphs = layout.nGlyphs(); + uint16_t* glyphs = new uint16_t[nGlyphs]; + SkPoint* pos = new SkPoint[nGlyphs]; + + x += MinikinUtils::xOffsetForTextAlign(paint, layout); + Paint::Align align = paint->getTextAlign(); + paint->setTextAlign(Paint::kLeft_Align); + GetTextFunctor f(layout, path, x, y, paint, glyphs, pos); + MinikinUtils::forFontRun(layout, paint, f); + paint->setTextAlign(align); + delete[] glyphs; + delete[] pos; + } + + static void getTextPath___C(JNIEnv* env, jobject clazz, jlong paintHandle, jint bidiFlags, + jcharArray text, jint index, jint count, jfloat x, jfloat y, jlong pathHandle) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + SkPath* path = reinterpret_cast<SkPath*>(pathHandle); + const jchar* textArray = env->GetCharArrayElements(text, nullptr); + getTextPath(env, paint, typeface, textArray + index, count, bidiFlags, x, y, path); + env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT); + } + + static void getTextPath__String(JNIEnv* env, jobject clazz, jlong paintHandle, jint bidiFlags, + jstring text, jint start, jint end, jfloat x, jfloat y, jlong pathHandle) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + SkPath* path = reinterpret_cast<SkPath*>(pathHandle); + const jchar* textArray = env->GetStringChars(text, nullptr); + getTextPath(env, paint, typeface, textArray + start, end - start, bidiFlags, x, y, path); + env->ReleaseStringChars(text, textArray); + } + + static void doTextBounds(JNIEnv* env, const jchar* text, int count, jobject bounds, + const Paint& paint, const Typeface* typeface, jint bidiFlags) { + SkRect r; + SkIRect ir; + + minikin::Layout layout = MinikinUtils::doLayout(&paint, + static_cast<minikin::Bidi>(bidiFlags), typeface, + text, count, // text buffer + 0, count, // draw range + 0, count, // context range + nullptr); + minikin::MinikinRect rect; + layout.getBounds(&rect); + r.fLeft = rect.mLeft; + r.fTop = rect.mTop; + r.fRight = rect.mRight; + r.fBottom = rect.mBottom; + r.roundOut(&ir); + GraphicsJNI::irect_to_jrect(ir, env, bounds); + } + + static void getStringBounds(JNIEnv* env, jobject, jlong paintHandle, jstring text, jint start, + jint end, jint bidiFlags, jobject bounds) { + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + const jchar* textArray = env->GetStringChars(text, nullptr); + doTextBounds(env, textArray + start, end - start, bounds, *paint, typeface, bidiFlags); + env->ReleaseStringChars(text, textArray); + } + + static void getCharArrayBounds(JNIEnv* env, jobject, jlong paintHandle, jcharArray text, + jint index, jint count, jint bidiFlags, jobject bounds) { + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + const jchar* textArray = env->GetCharArrayElements(text, nullptr); + doTextBounds(env, textArray + index, count, bounds, *paint, typeface, bidiFlags); + env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), + JNI_ABORT); + } + + // Returns true if the given string is exact one pair of regional indicators. + static bool isFlag(const jchar* str, size_t length) { + const jchar RI_LEAD_SURROGATE = 0xD83C; + const jchar RI_TRAIL_SURROGATE_MIN = 0xDDE6; + const jchar RI_TRAIL_SURROGATE_MAX = 0xDDFF; + + if (length != 4) { + return false; + } + if (str[0] != RI_LEAD_SURROGATE || str[2] != RI_LEAD_SURROGATE) { + return false; + } + return RI_TRAIL_SURROGATE_MIN <= str[1] && str[1] <= RI_TRAIL_SURROGATE_MAX && + RI_TRAIL_SURROGATE_MIN <= str[3] && str[3] <= RI_TRAIL_SURROGATE_MAX; + } + + static jboolean layoutContainsNotdef(const minikin::Layout& layout) { + for (size_t i = 0; i < layout.nGlyphs(); i++) { + if (layout.getGlyphId(i) == 0) { + return true; + } + } + return false; + } + + // Don't count glyphs that are the recommended "space" glyph and are zero width. + // This logic makes assumptions about HarfBuzz layout, but does correctly handle + // cases where ligatures form and zero width space glyphs are left in as + // placeholders. + static size_t countNonSpaceGlyphs(const minikin::Layout& layout) { + size_t count = 0; + static unsigned int kSpaceGlyphId = 3; + for (size_t i = 0; i < layout.nGlyphs(); i++) { + if (layout.getGlyphId(i) != kSpaceGlyphId || layout.getCharAdvance(i) != 0.0) { + count++; + } + } + return count; + } + + static jboolean hasGlyph(JNIEnv *env, jclass, jlong paintHandle, jint bidiFlags, + jstring string) { + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + ScopedStringChars str(env, string); + + /* Start by rejecting unsupported base code point and variation selector pairs. */ + size_t nChars = 0; + const uint32_t kStartOfString = 0xFFFFFFFF; + uint32_t prevCp = kStartOfString; + for (size_t i = 0; i < str.size(); i++) { + jchar cu = str[i]; + uint32_t cp = cu; + if (U16_IS_TRAIL(cu)) { + // invalid UTF-16, unpaired trailing surrogate + return false; + } else if (U16_IS_LEAD(cu)) { + if (i + 1 == str.size()) { + // invalid UTF-16, unpaired leading surrogate at end of string + return false; + } + i++; + jchar cu2 = str[i]; + if (!U16_IS_TRAIL(cu2)) { + // invalid UTF-16, unpaired leading surrogate + return false; + } + cp = U16_GET_SUPPLEMENTARY(cu, cu2); + } + + if (prevCp != kStartOfString && + ((0xFE00 <= cp && cp <= 0xFE0F) || (0xE0100 <= cp && cp <= 0xE01EF))) { + bool hasVS = MinikinUtils::hasVariationSelector(typeface, prevCp, cp); + if (!hasVS) { + // No font has a glyph for the code point and variation selector pair. + return false; + } else if (nChars == 1 && i + 1 == str.size()) { + // The string is just a codepoint and a VS, we have an authoritative answer + return true; + } + } + nChars++; + prevCp = cp; + } + minikin::Layout layout = MinikinUtils::doLayout(paint, + static_cast<minikin::Bidi>(bidiFlags), typeface, + str.get(), str.size(), // text buffer + 0, str.size(), // draw range + 0, str.size(), // context range + nullptr); + size_t nGlyphs = countNonSpaceGlyphs(layout); + if (nGlyphs != 1 && nChars > 1) { + // multiple-character input, and was not a ligature + // TODO: handle ZWJ/ZWNJ characters specially so we can detect certain ligatures + // in joining scripts, such as Arabic and Mongolian. + return false; + } + + if (nGlyphs == 0 || layoutContainsNotdef(layout)) { + return false; // The collection doesn't have a glyph. + } + + if (nChars == 2 && isFlag(str.get(), str.size())) { + // Some font may have a special glyph for unsupported regional indicator pairs. + // To return false for this case, need to compare the glyph id with the one of ZZ + // since ZZ is reserved for unknown or invalid territory. + // U+1F1FF (REGIONAL INDICATOR SYMBOL LETTER Z) is \uD83C\uDDFF in UTF16. + static const jchar ZZ_FLAG_STR[] = { 0xD83C, 0xDDFF, 0xD83C, 0xDDFF }; + minikin::Layout zzLayout = MinikinUtils::doLayout(paint, + static_cast<minikin::Bidi>(bidiFlags), typeface, + ZZ_FLAG_STR, 4, // text buffer + 0, 4, // draw range + 0, 4, // context range + nullptr); + if (zzLayout.nGlyphs() != 1 || layoutContainsNotdef(zzLayout)) { + // The font collection doesn't have a glyph for unknown flag. Just return true. + return true; + } + return zzLayout.getGlyphId(0) != layout.getGlyphId(0); + } + return true; + } + + static jfloat doRunAdvance(const Paint* paint, const Typeface* typeface, const jchar buf[], + jint start, jint count, jint bufSize, jboolean isRtl, jint offset) { + minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR; + if (offset == start + count) { + return MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, + bufSize, nullptr); + } + std::unique_ptr<float[]> advancesArray(new float[count]); + MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize, + advancesArray.get()); + return minikin::getRunAdvance(advancesArray.get(), buf, start, count, offset); + } + + static jfloat getRunAdvance___CIIIIZI_F(JNIEnv *env, jclass, jlong paintHandle, jcharArray text, + jint start, jint end, jint contextStart, jint contextEnd, jboolean isRtl, jint offset) { + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + ScopedCharArrayRO textArray(env, text); + jfloat result = doRunAdvance(paint, typeface, textArray.get() + contextStart, + start - contextStart, end - start, contextEnd - contextStart, isRtl, + offset - contextStart); + return result; + } + + static jint doOffsetForAdvance(const Paint* paint, const Typeface* typeface, const jchar buf[], + jint start, jint count, jint bufSize, jboolean isRtl, jfloat advance) { + minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR; + std::unique_ptr<float[]> advancesArray(new float[count]); + MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize, + advancesArray.get()); + return minikin::getOffsetForAdvance(advancesArray.get(), buf, start, count, advance); + } + + static jint getOffsetForAdvance___CIIIIZF_I(JNIEnv *env, jclass, jlong paintHandle, + jcharArray text, jint start, jint end, jint contextStart, jint contextEnd, + jboolean isRtl, jfloat advance) { + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + ScopedCharArrayRO textArray(env, text); + jint result = doOffsetForAdvance(paint, typeface, textArray.get() + contextStart, + start - contextStart, end - start, contextEnd - contextStart, isRtl, advance); + result += contextStart; + return result; + } + + // ------------------ @FastNative --------------------------- + + static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) { + Paint* obj = reinterpret_cast<Paint*>(objHandle); + ScopedUtfChars localesChars(env, locales); + jint minikinLocaleListId = minikin::registerLocaleList(localesChars.c_str()); + obj->setMinikinLocaleListId(minikinLocaleListId); + return minikinLocaleListId; + } + + static void setFontFeatureSettings(JNIEnv* env, jobject clazz, jlong paintHandle, jstring settings) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + if (!settings) { + paint->setFontFeatureSettings(std::string()); + } else { + ScopedUtfChars settingsChars(env, settings); + paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size())); + } + } + + static SkScalar getMetricsInternal(jlong paintHandle, SkFontMetrics *metrics) { + const int kElegantTop = 2500; + const int kElegantBottom = -1000; + const int kElegantAscent = 1900; + const int kElegantDescent = -500; + const int kElegantLeading = 0; + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + SkFont* font = &paint->getSkFont(); + const Typeface* typeface = paint->getAndroidTypeface(); + typeface = Typeface::resolveDefault(typeface); + minikin::FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle); + float saveSkewX = font->getSkewX(); + bool savefakeBold = font->isEmbolden(); + MinikinFontSkia::populateSkFont(font, baseFont.font->typeface().get(), baseFont.fakery); + SkScalar spacing = font->getMetrics(metrics); + // The populateSkPaint call may have changed fake bold / text skew + // because we want to measure with those effects applied, so now + // restore the original settings. + font->setSkewX(saveSkewX); + font->setEmbolden(savefakeBold); + if (paint->getFamilyVariant() == minikin::FamilyVariant::ELEGANT) { + SkScalar size = font->getSize(); + metrics->fTop = -size * kElegantTop / 2048; + metrics->fBottom = -size * kElegantBottom / 2048; + metrics->fAscent = -size * kElegantAscent / 2048; + metrics->fDescent = -size * kElegantDescent / 2048; + metrics->fLeading = size * kElegantLeading / 2048; + spacing = metrics->fDescent - metrics->fAscent + metrics->fLeading; + } + return spacing; + } + + static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) { + SkFontMetrics metrics; + SkScalar spacing = getMetricsInternal(paintHandle, &metrics); + + if (metricsObj) { + SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class)); + env->SetFloatField(metricsObj, gFontMetrics_fieldID.top, SkScalarToFloat(metrics.fTop)); + env->SetFloatField(metricsObj, gFontMetrics_fieldID.ascent, SkScalarToFloat(metrics.fAscent)); + env->SetFloatField(metricsObj, gFontMetrics_fieldID.descent, SkScalarToFloat(metrics.fDescent)); + env->SetFloatField(metricsObj, gFontMetrics_fieldID.bottom, SkScalarToFloat(metrics.fBottom)); + env->SetFloatField(metricsObj, gFontMetrics_fieldID.leading, SkScalarToFloat(metrics.fLeading)); + } + return SkScalarToFloat(spacing); + } + + static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) { + SkFontMetrics metrics; + + getMetricsInternal(paintHandle, &metrics); + int ascent = SkScalarRoundToInt(metrics.fAscent); + int descent = SkScalarRoundToInt(metrics.fDescent); + int leading = SkScalarRoundToInt(metrics.fLeading); + + if (metricsObj) { + SkASSERT(env->IsInstanceOf(metricsObj, gFontMetricsInt_class)); + env->SetIntField(metricsObj, gFontMetricsInt_fieldID.top, SkScalarFloorToInt(metrics.fTop)); + env->SetIntField(metricsObj, gFontMetricsInt_fieldID.ascent, ascent); + env->SetIntField(metricsObj, gFontMetricsInt_fieldID.descent, descent); + env->SetIntField(metricsObj, gFontMetricsInt_fieldID.bottom, SkScalarCeilToInt(metrics.fBottom)); + env->SetIntField(metricsObj, gFontMetricsInt_fieldID.leading, leading); + } + return descent - ascent + leading; + } + + + // ------------------ @CriticalNative --------------------------- + + static void reset(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + reinterpret_cast<Paint*>(objHandle)->reset(); + } + + static void assign(CRITICAL_JNI_PARAMS_COMMA jlong dstPaintHandle, jlong srcPaintHandle) { + Paint* dst = reinterpret_cast<Paint*>(dstPaintHandle); + const Paint* src = reinterpret_cast<Paint*>(srcPaintHandle); + *dst = *src; + } + + static jint getFlags(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + uint32_t flags = reinterpret_cast<Paint*>(paintHandle)->getJavaFlags(); + return static_cast<jint>(flags); + } + + static void setFlags(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint flags) { + reinterpret_cast<Paint*>(paintHandle)->setJavaFlags(flags); + } + + static jint getHinting(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + return (SkFontHinting)reinterpret_cast<Paint*>(paintHandle)->getSkFont().getHinting() + == SkFontHinting::kNone ? 0 : 1; + } + + static void setHinting(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint mode) { + reinterpret_cast<Paint*>(paintHandle)->getSkFont().setHinting( + mode == 0 ? SkFontHinting::kNone : SkFontHinting::kNormal); + } + + static void setAntiAlias(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean aa) { + reinterpret_cast<Paint*>(paintHandle)->setAntiAlias(aa); + } + + static void setLinearText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean linearText) { + reinterpret_cast<Paint*>(paintHandle)->getSkFont().setLinearMetrics(linearText); + } + + static void setSubpixelText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean subpixelText) { + reinterpret_cast<Paint*>(paintHandle)->getSkFont().setSubpixel(subpixelText); + } + + static void setUnderlineText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean underlineText) { + reinterpret_cast<Paint*>(paintHandle)->setUnderline(underlineText); + } + + static void setStrikeThruText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean strikeThruText) { + reinterpret_cast<Paint*>(paintHandle)->setStrikeThru(strikeThruText); + } + + static void setFakeBoldText(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean fakeBoldText) { + reinterpret_cast<Paint*>(paintHandle)->getSkFont().setEmbolden(fakeBoldText); + } + + static void setFilterBitmap(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean filterBitmap) { + reinterpret_cast<Paint*>(paintHandle)->setFilterQuality( + filterBitmap ? kLow_SkFilterQuality : kNone_SkFilterQuality); + } + + static void setDither(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean dither) { + reinterpret_cast<Paint*>(paintHandle)->setDither(dither); + } + + static jint getStyle(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + Paint* obj = reinterpret_cast<Paint*>(objHandle); + return static_cast<jint>(obj->getStyle()); + } + + static void setStyle(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jint styleHandle) { + Paint* obj = reinterpret_cast<Paint*>(objHandle); + Paint::Style style = static_cast<Paint::Style>(styleHandle); + obj->setStyle(style); + } + + static void setColorLong(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jlong colorSpaceHandle, + jlong colorLong) { + SkColor4f color = GraphicsJNI::convertColorLong(colorLong); + sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); + reinterpret_cast<Paint*>(paintHandle)->setColor4f(color, cs.get()); + } + + static void setColor(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint color) { + reinterpret_cast<Paint*>(paintHandle)->setColor(color); + } + + static void setAlpha(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint a) { + reinterpret_cast<Paint*>(paintHandle)->setAlpha(a); + } + + static jfloat getStrokeWidth(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeWidth()); + } + + static void setStrokeWidth(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat width) { + reinterpret_cast<Paint*>(paintHandle)->setStrokeWidth(width); + } + + static jfloat getStrokeMiter(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeMiter()); + } + + static void setStrokeMiter(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat miter) { + reinterpret_cast<Paint*>(paintHandle)->setStrokeMiter(miter); + } + + static jint getStrokeCap(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + Paint* obj = reinterpret_cast<Paint*>(objHandle); + return static_cast<jint>(obj->getStrokeCap()); + } + + static void setStrokeCap(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jint capHandle) { + Paint* obj = reinterpret_cast<Paint*>(objHandle); + Paint::Cap cap = static_cast<Paint::Cap>(capHandle); + obj->setStrokeCap(cap); + } + + static jint getStrokeJoin(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + Paint* obj = reinterpret_cast<Paint*>(objHandle); + return static_cast<jint>(obj->getStrokeJoin()); + } + + static void setStrokeJoin(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jint joinHandle) { + Paint* obj = reinterpret_cast<Paint*>(objHandle); + Paint::Join join = (Paint::Join) joinHandle; + obj->setStrokeJoin(join); + } + + static jboolean getFillPath(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong srcHandle, jlong dstHandle) { + Paint* obj = reinterpret_cast<Paint*>(objHandle); + SkPath* src = reinterpret_cast<SkPath*>(srcHandle); + SkPath* dst = reinterpret_cast<SkPath*>(dstHandle); + return obj->getFillPath(*src, dst) ? JNI_TRUE : JNI_FALSE; + } + + static jlong setShader(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong shaderHandle) { + Paint* obj = reinterpret_cast<Paint*>(objHandle); + SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle); + obj->setShader(sk_ref_sp(shader)); + return reinterpret_cast<jlong>(obj->getShader()); + } + + static jlong setColorFilter(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong filterHandle) { + Paint* obj = reinterpret_cast<Paint *>(objHandle); + SkColorFilter* filter = reinterpret_cast<SkColorFilter *>(filterHandle); + obj->setColorFilter(sk_ref_sp(filter)); + return reinterpret_cast<jlong>(obj->getColorFilter()); + } + + static void setXfermode(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint xfermodeHandle) { + // validate that the Java enum values match our expectations + static_assert(0 == static_cast<int>(SkBlendMode::kClear), "xfermode_mismatch"); + static_assert(1 == static_cast<int>(SkBlendMode::kSrc), "xfermode_mismatch"); + static_assert(2 == static_cast<int>(SkBlendMode::kDst), "xfermode_mismatch"); + static_assert(3 == static_cast<int>(SkBlendMode::kSrcOver), "xfermode_mismatch"); + static_assert(4 == static_cast<int>(SkBlendMode::kDstOver), "xfermode_mismatch"); + static_assert(5 == static_cast<int>(SkBlendMode::kSrcIn), "xfermode_mismatch"); + static_assert(6 == static_cast<int>(SkBlendMode::kDstIn), "xfermode_mismatch"); + static_assert(7 == static_cast<int>(SkBlendMode::kSrcOut), "xfermode_mismatch"); + static_assert(8 == static_cast<int>(SkBlendMode::kDstOut), "xfermode_mismatch"); + static_assert(9 == static_cast<int>(SkBlendMode::kSrcATop), "xfermode_mismatch"); + static_assert(10 == static_cast<int>(SkBlendMode::kDstATop), "xfermode_mismatch"); + static_assert(11 == static_cast<int>(SkBlendMode::kXor), "xfermode_mismatch"); + static_assert(12 == static_cast<int>(SkBlendMode::kPlus), "xfermode_mismatch"); + static_assert(13 == static_cast<int>(SkBlendMode::kModulate), "xfermode_mismatch"); + static_assert(14 == static_cast<int>(SkBlendMode::kScreen), "xfermode_mismatch"); + static_assert(15 == static_cast<int>(SkBlendMode::kOverlay), "xfermode_mismatch"); + static_assert(16 == static_cast<int>(SkBlendMode::kDarken), "xfermode_mismatch"); + static_assert(17 == static_cast<int>(SkBlendMode::kLighten), "xfermode_mismatch"); + static_assert(18 == static_cast<int>(SkBlendMode::kColorDodge), "xfermode mismatch"); + static_assert(19 == static_cast<int>(SkBlendMode::kColorBurn), "xfermode mismatch"); + static_assert(20 == static_cast<int>(SkBlendMode::kHardLight), "xfermode mismatch"); + static_assert(21 == static_cast<int>(SkBlendMode::kSoftLight), "xfermode mismatch"); + static_assert(22 == static_cast<int>(SkBlendMode::kDifference), "xfermode mismatch"); + static_assert(23 == static_cast<int>(SkBlendMode::kExclusion), "xfermode mismatch"); + static_assert(24 == static_cast<int>(SkBlendMode::kMultiply), "xfermode mismatch"); + static_assert(25 == static_cast<int>(SkBlendMode::kHue), "xfermode mismatch"); + static_assert(26 == static_cast<int>(SkBlendMode::kSaturation), "xfermode mismatch"); + static_assert(27 == static_cast<int>(SkBlendMode::kColor), "xfermode mismatch"); + static_assert(28 == static_cast<int>(SkBlendMode::kLuminosity), "xfermode mismatch"); + + SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle); + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + paint->setBlendMode(mode); + } + + static jlong setPathEffect(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong effectHandle) { + Paint* obj = reinterpret_cast<Paint*>(objHandle); + SkPathEffect* effect = reinterpret_cast<SkPathEffect*>(effectHandle); + obj->setPathEffect(sk_ref_sp(effect)); + return reinterpret_cast<jlong>(obj->getPathEffect()); + } + + static jlong setMaskFilter(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong maskfilterHandle) { + Paint* obj = reinterpret_cast<Paint*>(objHandle); + SkMaskFilter* maskfilter = reinterpret_cast<SkMaskFilter*>(maskfilterHandle); + obj->setMaskFilter(sk_ref_sp(maskfilter)); + return reinterpret_cast<jlong>(obj->getMaskFilter()); + } + + static void setTypeface(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong typefaceHandle) { + Paint* paint = reinterpret_cast<Paint*>(objHandle); + paint->setAndroidTypeface(reinterpret_cast<Typeface*>(typefaceHandle)); + } + + static jint getTextAlign(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + Paint* obj = reinterpret_cast<Paint*>(objHandle); + return static_cast<jint>(obj->getTextAlign()); + } + + static void setTextAlign(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jint alignHandle) { + Paint* obj = reinterpret_cast<Paint*>(objHandle); + Paint::Align align = static_cast<Paint::Align>(alignHandle); + obj->setTextAlign(align); + } + + static void setTextLocalesByMinikinLocaleListId(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, + jint minikinLocaleListId) { + Paint* obj = reinterpret_cast<Paint*>(objHandle); + obj->setMinikinLocaleListId(minikinLocaleListId); + } + + static jboolean isElegantTextHeight(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + Paint* obj = reinterpret_cast<Paint*>(paintHandle); + return obj->getFamilyVariant() == minikin::FamilyVariant::ELEGANT; + } + + static void setElegantTextHeight(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean aa) { + Paint* obj = reinterpret_cast<Paint*>(paintHandle); + obj->setFamilyVariant( + aa ? minikin::FamilyVariant::ELEGANT : minikin::FamilyVariant::DEFAULT); + } + + static jfloat getTextSize(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize()); + } + + static void setTextSize(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat textSize) { + if (textSize >= 0) { + reinterpret_cast<Paint*>(paintHandle)->getSkFont().setSize(textSize); + } + } + + static jfloat getTextScaleX(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getSkFont().getScaleX()); + } + + static void setTextScaleX(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat scaleX) { + reinterpret_cast<Paint*>(paintHandle)->getSkFont().setScaleX(scaleX); + } + + static jfloat getTextSkewX(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSkewX()); + } + + static void setTextSkewX(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat skewX) { + reinterpret_cast<Paint*>(paintHandle)->getSkFont().setSkewX(skewX); + } + + static jfloat getLetterSpacing(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + return paint->getLetterSpacing(); + } + + static void setLetterSpacing(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat letterSpacing) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + paint->setLetterSpacing(letterSpacing); + } + + static jfloat getWordSpacing(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + return paint->getWordSpacing(); + } + + static void setWordSpacing(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat wordSpacing) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + paint->setWordSpacing(wordSpacing); + } + + static jint getStartHyphenEdit(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint hyphen) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + return static_cast<jint>(paint->getStartHyphenEdit()); + } + + static jint getEndHyphenEdit(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint hyphen) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + return static_cast<jint>(paint->getEndHyphenEdit()); + } + + static void setStartHyphenEdit(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint hyphen) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + paint->setStartHyphenEdit((uint32_t)hyphen); + } + + static void setEndHyphenEdit(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jint hyphen) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + paint->setEndHyphenEdit((uint32_t)hyphen); + } + + static jfloat ascent(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + SkFontMetrics metrics; + getMetricsInternal(paintHandle, &metrics); + return SkScalarToFloat(metrics.fAscent); + } + + static jfloat descent(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + SkFontMetrics metrics; + getMetricsInternal(paintHandle, &metrics); + return SkScalarToFloat(metrics.fDescent); + } + + static jfloat getUnderlinePosition(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + SkFontMetrics metrics; + getMetricsInternal(paintHandle, &metrics); + SkScalar position; + if (metrics.hasUnderlinePosition(&position)) { + return SkScalarToFloat(position); + } else { + const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize(); + return SkScalarToFloat(Paint::kStdUnderline_Top * textSize); + } + } + + static jfloat getUnderlineThickness(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + SkFontMetrics metrics; + getMetricsInternal(paintHandle, &metrics); + SkScalar thickness; + if (metrics.hasUnderlineThickness(&thickness)) { + return SkScalarToFloat(thickness); + } else { + const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize(); + return SkScalarToFloat(Paint::kStdUnderline_Thickness * textSize); + } + } + + static jfloat getStrikeThruPosition(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize(); + return SkScalarToFloat(Paint::kStdStrikeThru_Top * textSize); + } + + static jfloat getStrikeThruThickness(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize(); + return SkScalarToFloat(Paint::kStdStrikeThru_Thickness * textSize); + } + + static void setShadowLayer(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat radius, + jfloat dx, jfloat dy, jlong colorSpaceHandle, + jlong colorLong) { + SkColor4f color = GraphicsJNI::convertColorLong(colorLong); + sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); + + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + if (radius <= 0) { + paint->setLooper(nullptr); + } + else { + SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius); + paint->setLooper(SkBlurDrawLooper::Make(color, cs.get(), sigma, dx, dy)); + } + } + + static jboolean hasShadowLayer(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr); + } + + static jboolean equalsForTextMeasurement(CRITICAL_JNI_PARAMS_COMMA jlong lPaint, jlong rPaint) { + if (lPaint == rPaint) { + return true; + } + Paint* leftPaint = reinterpret_cast<Paint*>(lPaint); + Paint* rightPaint = reinterpret_cast<Paint*>(rPaint); + + const Typeface* leftTypeface = Typeface::resolveDefault(leftPaint->getAndroidTypeface()); + const Typeface* rightTypeface = Typeface::resolveDefault(rightPaint->getAndroidTypeface()); + minikin::MinikinPaint leftMinikinPaint + = MinikinUtils::prepareMinikinPaint(leftPaint, leftTypeface); + minikin::MinikinPaint rightMinikinPaint + = MinikinUtils::prepareMinikinPaint(rightPaint, rightTypeface); + + return leftMinikinPaint == rightMinikinPaint; + } + +}; // namespace PaintGlue + +static const JNINativeMethod methods[] = { + {"nGetNativeFinalizer", "()J", (void*) PaintGlue::getNativeFinalizer}, + {"nInit","()J", (void*) PaintGlue::init}, + {"nInitWithPaint","(J)J", (void*) PaintGlue::initWithPaint}, + {"nBreakText","(J[CIIFI[F)I", (void*) PaintGlue::breakTextC}, + {"nBreakText","(JLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS}, + {"nGetTextAdvances","(J[CIIIII[FI)F", + (void*) PaintGlue::getTextAdvances___CIIIII_FI}, + {"nGetTextAdvances","(JLjava/lang/String;IIIII[FI)F", + (void*) PaintGlue::getTextAdvances__StringIIIII_FI}, + + {"nGetTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C}, + {"nGetTextRunCursor", "(JLjava/lang/String;IIIII)I", + (void*) PaintGlue::getTextRunCursor__String}, + {"nGetTextPath", "(JI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C}, + {"nGetTextPath", "(JILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String}, + {"nGetStringBounds", "(JLjava/lang/String;IIILandroid/graphics/Rect;)V", + (void*) PaintGlue::getStringBounds }, + {"nGetCharArrayBounds", "(J[CIIILandroid/graphics/Rect;)V", + (void*) PaintGlue::getCharArrayBounds }, + {"nHasGlyph", "(JILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph }, + {"nGetRunAdvance", "(J[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F}, + {"nGetOffsetForAdvance", "(J[CIIIIZF)I", + (void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I}, + + // --------------- @FastNative ---------------------- + + {"nSetTextLocales","(JLjava/lang/String;)I", (void*) PaintGlue::setTextLocales}, + {"nSetFontFeatureSettings","(JLjava/lang/String;)V", + (void*) PaintGlue::setFontFeatureSettings}, + {"nGetFontMetrics", "(JLandroid/graphics/Paint$FontMetrics;)F", + (void*)PaintGlue::getFontMetrics}, + {"nGetFontMetricsInt", "(JLandroid/graphics/Paint$FontMetricsInt;)I", + (void*)PaintGlue::getFontMetricsInt}, + + // --------------- @CriticalNative ------------------ + + {"nReset","(J)V", (void*) PaintGlue::reset}, + {"nSet","(JJ)V", (void*) PaintGlue::assign}, + {"nGetFlags","(J)I", (void*) PaintGlue::getFlags}, + {"nSetFlags","(JI)V", (void*) PaintGlue::setFlags}, + {"nGetHinting","(J)I", (void*) PaintGlue::getHinting}, + {"nSetHinting","(JI)V", (void*) PaintGlue::setHinting}, + {"nSetAntiAlias","(JZ)V", (void*) PaintGlue::setAntiAlias}, + {"nSetSubpixelText","(JZ)V", (void*) PaintGlue::setSubpixelText}, + {"nSetLinearText","(JZ)V", (void*) PaintGlue::setLinearText}, + {"nSetUnderlineText","(JZ)V", (void*) PaintGlue::setUnderlineText}, + {"nSetStrikeThruText","(JZ)V", (void*) PaintGlue::setStrikeThruText}, + {"nSetFakeBoldText","(JZ)V", (void*) PaintGlue::setFakeBoldText}, + {"nSetFilterBitmap","(JZ)V", (void*) PaintGlue::setFilterBitmap}, + {"nSetDither","(JZ)V", (void*) PaintGlue::setDither}, + {"nGetStyle","(J)I", (void*) PaintGlue::getStyle}, + {"nSetStyle","(JI)V", (void*) PaintGlue::setStyle}, + {"nSetColor","(JI)V", (void*) PaintGlue::setColor}, + {"nSetColor","(JJJ)V", (void*) PaintGlue::setColorLong}, + {"nSetAlpha","(JI)V", (void*) PaintGlue::setAlpha}, + {"nGetStrokeWidth","(J)F", (void*) PaintGlue::getStrokeWidth}, + {"nSetStrokeWidth","(JF)V", (void*) PaintGlue::setStrokeWidth}, + {"nGetStrokeMiter","(J)F", (void*) PaintGlue::getStrokeMiter}, + {"nSetStrokeMiter","(JF)V", (void*) PaintGlue::setStrokeMiter}, + {"nGetStrokeCap","(J)I", (void*) PaintGlue::getStrokeCap}, + {"nSetStrokeCap","(JI)V", (void*) PaintGlue::setStrokeCap}, + {"nGetStrokeJoin","(J)I", (void*) PaintGlue::getStrokeJoin}, + {"nSetStrokeJoin","(JI)V", (void*) PaintGlue::setStrokeJoin}, + {"nGetFillPath","(JJJ)Z", (void*) PaintGlue::getFillPath}, + {"nSetShader","(JJ)J", (void*) PaintGlue::setShader}, + {"nSetColorFilter","(JJ)J", (void*) PaintGlue::setColorFilter}, + {"nSetXfermode","(JI)V", (void*) PaintGlue::setXfermode}, + {"nSetPathEffect","(JJ)J", (void*) PaintGlue::setPathEffect}, + {"nSetMaskFilter","(JJ)J", (void*) PaintGlue::setMaskFilter}, + {"nSetTypeface","(JJ)V", (void*) PaintGlue::setTypeface}, + {"nGetTextAlign","(J)I", (void*) PaintGlue::getTextAlign}, + {"nSetTextAlign","(JI)V", (void*) PaintGlue::setTextAlign}, + {"nSetTextLocalesByMinikinLocaleListId","(JI)V", + (void*) PaintGlue::setTextLocalesByMinikinLocaleListId}, + {"nIsElegantTextHeight","(J)Z", (void*) PaintGlue::isElegantTextHeight}, + {"nSetElegantTextHeight","(JZ)V", (void*) PaintGlue::setElegantTextHeight}, + {"nGetTextSize","(J)F", (void*) PaintGlue::getTextSize}, + {"nSetTextSize","(JF)V", (void*) PaintGlue::setTextSize}, + {"nGetTextScaleX","(J)F", (void*) PaintGlue::getTextScaleX}, + {"nSetTextScaleX","(JF)V", (void*) PaintGlue::setTextScaleX}, + {"nGetTextSkewX","(J)F", (void*) PaintGlue::getTextSkewX}, + {"nSetTextSkewX","(JF)V", (void*) PaintGlue::setTextSkewX}, + {"nGetLetterSpacing","(J)F", (void*) PaintGlue::getLetterSpacing}, + {"nSetLetterSpacing","(JF)V", (void*) PaintGlue::setLetterSpacing}, + {"nGetWordSpacing","(J)F", (void*) PaintGlue::getWordSpacing}, + {"nSetWordSpacing","(JF)V", (void*) PaintGlue::setWordSpacing}, + {"nGetStartHyphenEdit", "(J)I", (void*) PaintGlue::getStartHyphenEdit}, + {"nGetEndHyphenEdit", "(J)I", (void*) PaintGlue::getEndHyphenEdit}, + {"nSetStartHyphenEdit", "(JI)V", (void*) PaintGlue::setStartHyphenEdit}, + {"nSetEndHyphenEdit", "(JI)V", (void*) PaintGlue::setEndHyphenEdit}, + {"nAscent","(J)F", (void*) PaintGlue::ascent}, + {"nDescent","(J)F", (void*) PaintGlue::descent}, + {"nGetUnderlinePosition","(J)F", (void*) PaintGlue::getUnderlinePosition}, + {"nGetUnderlineThickness","(J)F", (void*) PaintGlue::getUnderlineThickness}, + {"nGetStrikeThruPosition","(J)F", (void*) PaintGlue::getStrikeThruPosition}, + {"nGetStrikeThruThickness","(J)F", (void*) PaintGlue::getStrikeThruThickness}, + {"nSetShadowLayer", "(JFFFJJ)V", (void*)PaintGlue::setShadowLayer}, + {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer}, + {"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement}, +}; + +int register_android_graphics_Paint(JNIEnv* env) { + gFontMetrics_class = FindClassOrDie(env, "android/graphics/Paint$FontMetrics"); + gFontMetrics_class = MakeGlobalRefOrDie(env, gFontMetrics_class); + + gFontMetrics_fieldID.top = GetFieldIDOrDie(env, gFontMetrics_class, "top", "F"); + gFontMetrics_fieldID.ascent = GetFieldIDOrDie(env, gFontMetrics_class, "ascent", "F"); + gFontMetrics_fieldID.descent = GetFieldIDOrDie(env, gFontMetrics_class, "descent", "F"); + gFontMetrics_fieldID.bottom = GetFieldIDOrDie(env, gFontMetrics_class, "bottom", "F"); + gFontMetrics_fieldID.leading = GetFieldIDOrDie(env, gFontMetrics_class, "leading", "F"); + + gFontMetricsInt_class = FindClassOrDie(env, "android/graphics/Paint$FontMetricsInt"); + gFontMetricsInt_class = MakeGlobalRefOrDie(env, gFontMetricsInt_class); + + gFontMetricsInt_fieldID.top = GetFieldIDOrDie(env, gFontMetricsInt_class, "top", "I"); + gFontMetricsInt_fieldID.ascent = GetFieldIDOrDie(env, gFontMetricsInt_class, "ascent", "I"); + gFontMetricsInt_fieldID.descent = GetFieldIDOrDie(env, gFontMetricsInt_class, "descent", "I"); + gFontMetricsInt_fieldID.bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I"); + gFontMetricsInt_fieldID.leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I"); + + return RegisterMethodsOrDie(env, "android/graphics/Paint", methods, NELEM(methods)); +} + +} diff --git a/libs/hwui/jni/PaintFilter.cpp b/libs/hwui/jni/PaintFilter.cpp new file mode 100644 index 000000000000..ec115b4e141c --- /dev/null +++ b/libs/hwui/jni/PaintFilter.cpp @@ -0,0 +1,80 @@ +/* libs/android_runtime/android/graphics/ColorFilter.cpp +** +** Copyright 2006, 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. +*/ + +#include "GraphicsJNI.h" + +#include "hwui/Paint.h" +#include "hwui/PaintFilter.h" +#include "SkPaint.h" + +namespace android { + +class PaintFlagsFilter : public PaintFilter { +public: + PaintFlagsFilter(uint32_t clearFlags, uint32_t setFlags) { + fClearFlags = static_cast<uint16_t>(clearFlags); + fSetFlags = static_cast<uint16_t>(setFlags); + } + void filter(SkPaint* paint) override { + uint32_t flags = Paint::GetSkPaintJavaFlags(*paint); + Paint::SetSkPaintJavaFlags(paint, (flags & ~fClearFlags) | fSetFlags); + } + void filterFullPaint(Paint* paint) override { + paint->setJavaFlags((paint->getJavaFlags() & ~fClearFlags) | fSetFlags); + } + +private: + uint16_t fClearFlags; + uint16_t fSetFlags; +}; + +class PaintFilterGlue { +public: + + static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) { + PaintFilter* obj = reinterpret_cast<PaintFilter*>(objHandle); + SkSafeUnref(obj); + } + + static jlong CreatePaintFlagsFilter(JNIEnv* env, jobject clazz, + jint clearFlags, jint setFlags) { + PaintFilter* filter = nullptr; + if (clearFlags | setFlags) { + filter = new PaintFlagsFilter(clearFlags, setFlags); + } + return reinterpret_cast<jlong>(filter); + } +}; + +static const JNINativeMethod drawfilter_methods[] = { + {"nativeDestructor", "(J)V", (void*) PaintFilterGlue::finalizer} +}; + +static const JNINativeMethod paintflags_methods[] = { + {"nativeConstructor","(II)J", (void*) PaintFilterGlue::CreatePaintFlagsFilter} +}; + +int register_android_graphics_DrawFilter(JNIEnv* env) { + int result = RegisterMethodsOrDie(env, "android/graphics/DrawFilter", drawfilter_methods, + NELEM(drawfilter_methods)); + result |= RegisterMethodsOrDie(env, "android/graphics/PaintFlagsDrawFilter", paintflags_methods, + NELEM(paintflags_methods)); + + return 0; +} + +} diff --git a/libs/hwui/jni/Path.cpp b/libs/hwui/jni/Path.cpp new file mode 100644 index 000000000000..d67bcf221681 --- /dev/null +++ b/libs/hwui/jni/Path.cpp @@ -0,0 +1,560 @@ +/* libs/android_runtime/android/graphics/Path.cpp +** +** Copyright 2006, 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. +*/ + +// This file was generated from the C++ include file: SkPath.h +// Any changes made to this file will be discarded by the build. +// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, +// or one of the auxilary file specifications in device/tools/gluemaker. + +#include "GraphicsJNI.h" + +#include "SkPath.h" +#include "SkPathOps.h" +#include "SkGeometry.h" // WARNING: Internal Skia Header + +#include <vector> +#include <map> + +namespace android { + +class SkPathGlue { +public: + + static void finalizer(SkPath* obj) { + delete obj; + } + + // ---------------- Regular JNI ----------------------------- + + static jlong init(JNIEnv* env, jclass clazz) { + return reinterpret_cast<jlong>(new SkPath()); + } + + static jlong init_Path(JNIEnv* env, jclass clazz, jlong valHandle) { + SkPath* val = reinterpret_cast<SkPath*>(valHandle); + return reinterpret_cast<jlong>(new SkPath(*val)); + } + + static jlong getFinalizer(JNIEnv* env, jclass clazz) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&finalizer)); + } + + static void set(JNIEnv* env, jclass clazz, jlong dstHandle, jlong srcHandle) { + SkPath* dst = reinterpret_cast<SkPath*>(dstHandle); + const SkPath* src = reinterpret_cast<SkPath*>(srcHandle); + *dst = *src; + } + + static void computeBounds(JNIEnv* env, jclass clazz, jlong objHandle, jobject jbounds) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + const SkRect& bounds = obj->getBounds(); + GraphicsJNI::rect_to_jrectf(bounds, env, jbounds); + } + + static void incReserve(JNIEnv* env, jclass clazz, jlong objHandle, jint extraPtCount) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + obj->incReserve(extraPtCount); + } + + static void moveTo__FF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x, jfloat y) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + obj->moveTo(x, y); + } + + static void rMoveTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx, jfloat dy) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + obj->rMoveTo(dx, dy); + } + + static void lineTo__FF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x, jfloat y) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + obj->lineTo(x, y); + } + + static void rLineTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx, jfloat dy) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + obj->rLineTo(dx, dy); + } + + static void quadTo__FFFF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x1, jfloat y1, + jfloat x2, jfloat y2) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + obj->quadTo(x1, y1, x2, y2); + } + + static void rQuadTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx1, jfloat dy1, + jfloat dx2, jfloat dy2) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + obj->rQuadTo(dx1, dy1, dx2, dy2); + } + + static void cubicTo__FFFFFF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x1, jfloat y1, + jfloat x2, jfloat y2, jfloat x3, jfloat y3) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + obj->cubicTo(x1, y1, x2, y2, x3, y3); + } + + static void rCubicTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x1, jfloat y1, + jfloat x2, jfloat y2, jfloat x3, jfloat y3) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + obj->rCubicTo(x1, y1, x2, y2, x3, y3); + } + + static void arcTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat left, jfloat top, + jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle, + jboolean forceMoveTo) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + SkRect oval = SkRect::MakeLTRB(left, top, right, bottom); + obj->arcTo(oval, startAngle, sweepAngle, forceMoveTo); + } + + static void close(JNIEnv* env, jclass clazz, jlong objHandle) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + obj->close(); + } + + static void addRect(JNIEnv* env, jclass clazz, jlong objHandle, + jfloat left, jfloat top, jfloat right, jfloat bottom, jint dirHandle) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + SkPathDirection dir = static_cast<SkPathDirection>(dirHandle); + obj->addRect(left, top, right, bottom, dir); + } + + static void addOval(JNIEnv* env, jclass clazz, jlong objHandle, + jfloat left, jfloat top, jfloat right, jfloat bottom, jint dirHandle) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + SkPathDirection dir = static_cast<SkPathDirection>(dirHandle); + SkRect oval = SkRect::MakeLTRB(left, top, right, bottom); + obj->addOval(oval, dir); + } + + static void addCircle(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x, jfloat y, + jfloat radius, jint dirHandle) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + SkPathDirection dir = static_cast<SkPathDirection>(dirHandle); + obj->addCircle(x, y, radius, dir); + } + + static void addArc(JNIEnv* env, jclass clazz, jlong objHandle, jfloat left, jfloat top, + jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle) { + SkRect oval = SkRect::MakeLTRB(left, top, right, bottom); + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + obj->addArc(oval, startAngle, sweepAngle); + } + + static void addRoundRectXY(JNIEnv* env, jclass clazz, jlong objHandle, jfloat left, jfloat top, + jfloat right, jfloat bottom, jfloat rx, jfloat ry, jint dirHandle) { + SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + SkPathDirection dir = static_cast<SkPathDirection>(dirHandle); + obj->addRoundRect(rect, rx, ry, dir); + } + + static void addRoundRect8(JNIEnv* env, jclass clazz, jlong objHandle, jfloat left, jfloat top, + jfloat right, jfloat bottom, jfloatArray array, jint dirHandle) { + SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + SkPathDirection dir = static_cast<SkPathDirection>(dirHandle); + AutoJavaFloatArray afa(env, array, 8); +#ifdef SK_SCALAR_IS_FLOAT + const float* src = afa.ptr(); +#else + #error Need to convert float array to SkScalar array before calling the following function. +#endif + obj->addRoundRect(rect, src, dir); + } + + static void addPath__PathFF(JNIEnv* env, jclass clazz, jlong objHandle, jlong srcHandle, + jfloat dx, jfloat dy) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + SkPath* src = reinterpret_cast<SkPath*>(srcHandle); + obj->addPath(*src, dx, dy); + } + + static void addPath__Path(JNIEnv* env, jclass clazz, jlong objHandle, jlong srcHandle) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + SkPath* src = reinterpret_cast<SkPath*>(srcHandle); + obj->addPath(*src); + } + + static void addPath__PathMatrix(JNIEnv* env, jclass clazz, jlong objHandle, jlong srcHandle, + jlong matrixHandle) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + SkPath* src = reinterpret_cast<SkPath*>(srcHandle); + SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); + obj->addPath(*src, *matrix); + } + + static void offset__FF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx, jfloat dy) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + obj->offset(dx, dy); + } + + static void setLastPoint(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx, jfloat dy) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + obj->setLastPt(dx, dy); + } + + static void transform__MatrixPath(JNIEnv* env, jclass clazz, jlong objHandle, jlong matrixHandle, + jlong dstHandle) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); + SkPath* dst = reinterpret_cast<SkPath*>(dstHandle); + obj->transform(*matrix, dst); + } + + static void transform__Matrix(JNIEnv* env, jclass clazz, jlong objHandle, jlong matrixHandle) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); + obj->transform(*matrix); + } + + static jboolean op(JNIEnv* env, jclass clazz, jlong p1Handle, jlong p2Handle, jint opHandle, + jlong rHandle) { + SkPath* p1 = reinterpret_cast<SkPath*>(p1Handle); + SkPath* p2 = reinterpret_cast<SkPath*>(p2Handle); + SkPathOp op = static_cast<SkPathOp>(opHandle); + SkPath* r = reinterpret_cast<SkPath*>(rHandle); + return Op(*p1, *p2, op, r); + } + + typedef SkPoint (*bezierCalculation)(float t, const SkPoint* points); + + static void addMove(std::vector<SkPoint>& segmentPoints, std::vector<float>& lengths, + const SkPoint& point) { + float length = 0; + if (!lengths.empty()) { + length = lengths.back(); + } + segmentPoints.push_back(point); + lengths.push_back(length); + } + + static void addLine(std::vector<SkPoint>& segmentPoints, std::vector<float>& lengths, + const SkPoint& toPoint) { + if (segmentPoints.empty()) { + segmentPoints.push_back(SkPoint::Make(0, 0)); + lengths.push_back(0); + } else if (segmentPoints.back() == toPoint) { + return; // Empty line + } + float length = lengths.back() + SkPoint::Distance(segmentPoints.back(), toPoint); + segmentPoints.push_back(toPoint); + lengths.push_back(length); + } + + static float cubicCoordinateCalculation(float t, float p0, float p1, float p2, float p3) { + float oneMinusT = 1 - t; + float oneMinusTSquared = oneMinusT * oneMinusT; + float oneMinusTCubed = oneMinusTSquared * oneMinusT; + float tSquared = t * t; + float tCubed = tSquared * t; + return (oneMinusTCubed * p0) + (3 * oneMinusTSquared * t * p1) + + (3 * oneMinusT * tSquared * p2) + (tCubed * p3); + } + + static SkPoint cubicBezierCalculation(float t, const SkPoint* points) { + float x = cubicCoordinateCalculation(t, points[0].x(), points[1].x(), + points[2].x(), points[3].x()); + float y = cubicCoordinateCalculation(t, points[0].y(), points[1].y(), + points[2].y(), points[3].y()); + return SkPoint::Make(x, y); + } + + static float quadraticCoordinateCalculation(float t, float p0, float p1, float p2) { + float oneMinusT = 1 - t; + return oneMinusT * ((oneMinusT * p0) + (t * p1)) + t * ((oneMinusT * p1) + (t * p2)); + } + + static SkPoint quadraticBezierCalculation(float t, const SkPoint* points) { + float x = quadraticCoordinateCalculation(t, points[0].x(), points[1].x(), points[2].x()); + float y = quadraticCoordinateCalculation(t, points[0].y(), points[1].y(), points[2].y()); + return SkPoint::Make(x, y); + } + + // Subdivide a section of the Bezier curve, set the mid-point and the mid-t value. + // Returns true if further subdivision is necessary as defined by errorSquared. + static bool subdividePoints(const SkPoint* points, bezierCalculation bezierFunction, + float t0, const SkPoint &p0, float t1, const SkPoint &p1, + float& midT, SkPoint &midPoint, float errorSquared) { + midT = (t1 + t0) / 2; + float midX = (p1.x() + p0.x()) / 2; + float midY = (p1.y() + p0.y()) / 2; + + midPoint = (*bezierFunction)(midT, points); + float xError = midPoint.x() - midX; + float yError = midPoint.y() - midY; + float midErrorSquared = (xError * xError) + (yError * yError); + return midErrorSquared > errorSquared; + } + + // Divides Bezier curves until linear interpolation is very close to accurate, using + // errorSquared as a metric. Cubic Bezier curves can have an inflection point that improperly + // short-circuit subdivision. If you imagine an S shape, the top and bottom points being the + // starting and end points, linear interpolation would mark the center where the curve places + // the point. It is clearly not the case that we can linearly interpolate at that point. + // doubleCheckDivision forces a second examination between subdivisions to ensure that linear + // interpolation works. + static void addBezier(const SkPoint* points, + bezierCalculation bezierFunction, std::vector<SkPoint>& segmentPoints, + std::vector<float>& lengths, float errorSquared, bool doubleCheckDivision) { + typedef std::map<float, SkPoint> PointMap; + PointMap tToPoint; + + tToPoint[0] = (*bezierFunction)(0, points); + tToPoint[1] = (*bezierFunction)(1, points); + + PointMap::iterator iter = tToPoint.begin(); + PointMap::iterator next = iter; + ++next; + while (next != tToPoint.end()) { + bool needsSubdivision = true; + SkPoint midPoint; + do { + float midT; + needsSubdivision = subdividePoints(points, bezierFunction, iter->first, + iter->second, next->first, next->second, midT, midPoint, errorSquared); + if (!needsSubdivision && doubleCheckDivision) { + SkPoint quarterPoint; + float quarterT; + needsSubdivision = subdividePoints(points, bezierFunction, iter->first, + iter->second, midT, midPoint, quarterT, quarterPoint, errorSquared); + if (needsSubdivision) { + // Found an inflection point. No need to double-check. + doubleCheckDivision = false; + } + } + if (needsSubdivision) { + next = tToPoint.insert(iter, PointMap::value_type(midT, midPoint)); + } + } while (needsSubdivision); + iter = next; + next++; + } + + // Now that each division can use linear interpolation with less than the allowed error + for (iter = tToPoint.begin(); iter != tToPoint.end(); ++iter) { + addLine(segmentPoints, lengths, iter->second); + } + } + + static void createVerbSegments(const SkPath::Iter& pathIter, SkPath::Verb verb, + const SkPoint* points, std::vector<SkPoint>& segmentPoints, + std::vector<float>& lengths, float errorSquared, float errorConic) { + switch (verb) { + case SkPath::kMove_Verb: + addMove(segmentPoints, lengths, points[0]); + break; + case SkPath::kClose_Verb: + addLine(segmentPoints, lengths, points[0]); + break; + case SkPath::kLine_Verb: + addLine(segmentPoints, lengths, points[1]); + break; + case SkPath::kQuad_Verb: + addBezier(points, quadraticBezierCalculation, segmentPoints, lengths, + errorSquared, false); + break; + case SkPath::kCubic_Verb: + addBezier(points, cubicBezierCalculation, segmentPoints, lengths, + errorSquared, true); + break; + case SkPath::kConic_Verb: { + SkAutoConicToQuads converter; + const SkPoint* quads = converter.computeQuads( + points, pathIter.conicWeight(), errorConic); + for (int i = 0; i < converter.countQuads(); i++) { + // Note: offset each subsequent quad by 2, since end points are shared + const SkPoint* quad = quads + i * 2; + addBezier(quad, quadraticBezierCalculation, segmentPoints, lengths, + errorConic, false); + } + break; + } + default: + static_assert(SkPath::kMove_Verb == 0 + && SkPath::kLine_Verb == 1 + && SkPath::kQuad_Verb == 2 + && SkPath::kConic_Verb == 3 + && SkPath::kCubic_Verb == 4 + && SkPath::kClose_Verb == 5 + && SkPath::kDone_Verb == 6, + "Path enum changed, new types may have been added."); + break; + } + } + + // Returns a float[] with each point along the path represented by 3 floats + // * fractional length along the path that the point resides + // * x coordinate + // * y coordinate + // Note that more than one point may have the same length along the path in + // the case of a move. + // NULL can be returned if the Path is empty. + static jfloatArray approximate(JNIEnv* env, jclass clazz, jlong pathHandle, + float acceptableError) { + SkPath* path = reinterpret_cast<SkPath*>(pathHandle); + SkASSERT(path); + SkPath::Iter pathIter(*path, false); + SkPath::Verb verb; + SkPoint points[4]; + std::vector<SkPoint> segmentPoints; + std::vector<float> lengths; + float errorSquared = acceptableError * acceptableError; + float errorConic = acceptableError / 2; // somewhat arbitrary + + while ((verb = pathIter.next(points)) != SkPath::kDone_Verb) { + createVerbSegments(pathIter, verb, points, segmentPoints, lengths, + errorSquared, errorConic); + } + + if (segmentPoints.empty()) { + int numVerbs = path->countVerbs(); + if (numVerbs == 1) { + addMove(segmentPoints, lengths, path->getPoint(0)); + } else { + // Invalid or empty path. Fall back to point(0,0) + addMove(segmentPoints, lengths, SkPoint()); + } + } + + float totalLength = lengths.back(); + if (totalLength == 0) { + // Lone Move instructions should still be able to animate at the same value. + segmentPoints.push_back(segmentPoints.back()); + lengths.push_back(1); + totalLength = 1; + } + + size_t numPoints = segmentPoints.size(); + size_t approximationArraySize = numPoints * 3; + + float* approximation = new float[approximationArraySize]; + + int approximationIndex = 0; + for (size_t i = 0; i < numPoints; i++) { + const SkPoint& point = segmentPoints[i]; + approximation[approximationIndex++] = lengths[i] / totalLength; + approximation[approximationIndex++] = point.x(); + approximation[approximationIndex++] = point.y(); + } + + jfloatArray result = env->NewFloatArray(approximationArraySize); + env->SetFloatArrayRegion(result, 0, approximationArraySize, approximation); + delete[] approximation; + return result; + } + + // ---------------- @FastNative ----------------------------- + + static jboolean isRect(JNIEnv* env, jclass clazz, jlong objHandle, jobject jrect) { + SkRect rect; + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + jboolean result = obj->isRect(&rect); + if (jrect) { + GraphicsJNI::rect_to_jrectf(rect, env, jrect); + } + return result; + } + + // ---------------- @CriticalNative ------------------------- + + static void reset(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + obj->reset(); + } + + static void rewind(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + obj->rewind(); + } + + static jboolean isEmpty(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + return obj->isEmpty(); + } + + static jboolean isConvex(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + return obj->isConvex(); + } + + static jint getFillType(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + SkPath* obj = reinterpret_cast<SkPath*>(objHandle); + return static_cast<int>(obj->getFillType()); + } + + static void setFillType(CRITICAL_JNI_PARAMS_COMMA jlong pathHandle, jint ftHandle) {; + SkPath* path = reinterpret_cast<SkPath*>(pathHandle); + SkPathFillType ft = static_cast<SkPathFillType>(ftHandle); + path->setFillType(ft); + } +}; + +static const JNINativeMethod methods[] = { + {"nInit","()J", (void*) SkPathGlue::init}, + {"nInit","(J)J", (void*) SkPathGlue::init_Path}, + {"nGetFinalizer", "()J", (void*) SkPathGlue::getFinalizer}, + {"nSet","(JJ)V", (void*) SkPathGlue::set}, + {"nComputeBounds","(JLandroid/graphics/RectF;)V", (void*) SkPathGlue::computeBounds}, + {"nIncReserve","(JI)V", (void*) SkPathGlue::incReserve}, + {"nMoveTo","(JFF)V", (void*) SkPathGlue::moveTo__FF}, + {"nRMoveTo","(JFF)V", (void*) SkPathGlue::rMoveTo}, + {"nLineTo","(JFF)V", (void*) SkPathGlue::lineTo__FF}, + {"nRLineTo","(JFF)V", (void*) SkPathGlue::rLineTo}, + {"nQuadTo","(JFFFF)V", (void*) SkPathGlue::quadTo__FFFF}, + {"nRQuadTo","(JFFFF)V", (void*) SkPathGlue::rQuadTo}, + {"nCubicTo","(JFFFFFF)V", (void*) SkPathGlue::cubicTo__FFFFFF}, + {"nRCubicTo","(JFFFFFF)V", (void*) SkPathGlue::rCubicTo}, + {"nArcTo","(JFFFFFFZ)V", (void*) SkPathGlue::arcTo}, + {"nClose","(J)V", (void*) SkPathGlue::close}, + {"nAddRect","(JFFFFI)V", (void*) SkPathGlue::addRect}, + {"nAddOval","(JFFFFI)V", (void*) SkPathGlue::addOval}, + {"nAddCircle","(JFFFI)V", (void*) SkPathGlue::addCircle}, + {"nAddArc","(JFFFFFF)V", (void*) SkPathGlue::addArc}, + {"nAddRoundRect","(JFFFFFFI)V", (void*) SkPathGlue::addRoundRectXY}, + {"nAddRoundRect","(JFFFF[FI)V", (void*) SkPathGlue::addRoundRect8}, + {"nAddPath","(JJFF)V", (void*) SkPathGlue::addPath__PathFF}, + {"nAddPath","(JJ)V", (void*) SkPathGlue::addPath__Path}, + {"nAddPath","(JJJ)V", (void*) SkPathGlue::addPath__PathMatrix}, + {"nOffset","(JFF)V", (void*) SkPathGlue::offset__FF}, + {"nSetLastPoint","(JFF)V", (void*) SkPathGlue::setLastPoint}, + {"nTransform","(JJJ)V", (void*) SkPathGlue::transform__MatrixPath}, + {"nTransform","(JJ)V", (void*) SkPathGlue::transform__Matrix}, + {"nOp","(JJIJ)Z", (void*) SkPathGlue::op}, + {"nApproximate", "(JF)[F", (void*) SkPathGlue::approximate}, + + // ------- @FastNative below here ---------------------- + {"nIsRect","(JLandroid/graphics/RectF;)Z", (void*) SkPathGlue::isRect}, + + // ------- @CriticalNative below here ------------------ + {"nReset","(J)V", (void*) SkPathGlue::reset}, + {"nRewind","(J)V", (void*) SkPathGlue::rewind}, + {"nIsEmpty","(J)Z", (void*) SkPathGlue::isEmpty}, + {"nIsConvex","(J)Z", (void*) SkPathGlue::isConvex}, + {"nGetFillType","(J)I", (void*) SkPathGlue::getFillType}, + {"nSetFillType","(JI)V", (void*) SkPathGlue::setFillType}, +}; + +int register_android_graphics_Path(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/Path", methods, NELEM(methods)); + + static_assert(0 == (int)SkPathDirection::kCW, "direction_mismatch"); + static_assert(1 == (int)SkPathDirection::kCCW, "direction_mismatch"); +} + +} diff --git a/libs/hwui/jni/PathEffect.cpp b/libs/hwui/jni/PathEffect.cpp new file mode 100644 index 000000000000..f99bef7b7d58 --- /dev/null +++ b/libs/hwui/jni/PathEffect.cpp @@ -0,0 +1,117 @@ +#include "GraphicsJNI.h" +#include "Sk1DPathEffect.h" +#include "SkCornerPathEffect.h" +#include "SkDashPathEffect.h" +#include "SkDiscretePathEffect.h" +#include "SkPathEffect.h" + +class SkPathEffectGlue { +public: + + static void destructor(JNIEnv* env, jobject, jlong effectHandle) { + SkPathEffect* effect = reinterpret_cast<SkPathEffect*>(effectHandle); + SkSafeUnref(effect); + } + + static jlong Compose_constructor(JNIEnv* env, jobject, + jlong outerHandle, jlong innerHandle) { + SkPathEffect* outer = reinterpret_cast<SkPathEffect*>(outerHandle); + SkPathEffect* inner = reinterpret_cast<SkPathEffect*>(innerHandle); + SkPathEffect* effect = SkPathEffect::MakeCompose(sk_ref_sp(outer), + sk_ref_sp(inner)).release(); + return reinterpret_cast<jlong>(effect); + } + + static jlong Sum_constructor(JNIEnv* env, jobject, + jlong firstHandle, jlong secondHandle) { + SkPathEffect* first = reinterpret_cast<SkPathEffect*>(firstHandle); + SkPathEffect* second = reinterpret_cast<SkPathEffect*>(secondHandle); + SkPathEffect* effect = SkPathEffect::MakeSum(sk_ref_sp(first), + sk_ref_sp(second)).release(); + return reinterpret_cast<jlong>(effect); + } + + static jlong Dash_constructor(JNIEnv* env, jobject, + jfloatArray intervalArray, jfloat phase) { + AutoJavaFloatArray autoInterval(env, intervalArray); + int count = autoInterval.length() & ~1; // even number +#ifdef SK_SCALAR_IS_FLOAT + SkScalar* intervals = autoInterval.ptr(); +#else + #error Need to convert float array to SkScalar array before calling the following function. +#endif + SkPathEffect* effect = SkDashPathEffect::Make(intervals, count, phase).release(); + return reinterpret_cast<jlong>(effect); + } + + static jlong OneD_constructor(JNIEnv* env, jobject, + jlong shapeHandle, jfloat advance, jfloat phase, jint style) { + const SkPath* shape = reinterpret_cast<SkPath*>(shapeHandle); + SkASSERT(shape != NULL); + SkPathEffect* effect = SkPath1DPathEffect::Make(*shape, advance, phase, + (SkPath1DPathEffect::Style)style).release(); + return reinterpret_cast<jlong>(effect); + } + + static jlong Corner_constructor(JNIEnv* env, jobject, jfloat radius){ + SkPathEffect* effect = SkCornerPathEffect::Make(radius).release(); + return reinterpret_cast<jlong>(effect); + } + + static jlong Discrete_constructor(JNIEnv* env, jobject, + jfloat length, jfloat deviation) { + SkPathEffect* effect = SkDiscretePathEffect::Make(length, deviation).release(); + return reinterpret_cast<jlong>(effect); + } + +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gPathEffectMethods[] = { + { "nativeDestructor", "(J)V", (void*)SkPathEffectGlue::destructor } +}; + +static const JNINativeMethod gComposePathEffectMethods[] = { + { "nativeCreate", "(JJ)J", (void*)SkPathEffectGlue::Compose_constructor } +}; + +static const JNINativeMethod gSumPathEffectMethods[] = { + { "nativeCreate", "(JJ)J", (void*)SkPathEffectGlue::Sum_constructor } +}; + +static const JNINativeMethod gDashPathEffectMethods[] = { + { "nativeCreate", "([FF)J", (void*)SkPathEffectGlue::Dash_constructor } +}; + +static const JNINativeMethod gPathDashPathEffectMethods[] = { + { "nativeCreate", "(JFFI)J", (void*)SkPathEffectGlue::OneD_constructor } +}; + +static const JNINativeMethod gCornerPathEffectMethods[] = { + { "nativeCreate", "(F)J", (void*)SkPathEffectGlue::Corner_constructor } +}; + +static const JNINativeMethod gDiscretePathEffectMethods[] = { + { "nativeCreate", "(FF)J", (void*)SkPathEffectGlue::Discrete_constructor } +}; + +int register_android_graphics_PathEffect(JNIEnv* env) +{ + android::RegisterMethodsOrDie(env, "android/graphics/PathEffect", gPathEffectMethods, + NELEM(gPathEffectMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/ComposePathEffect", + gComposePathEffectMethods, NELEM(gComposePathEffectMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/SumPathEffect", gSumPathEffectMethods, + NELEM(gSumPathEffectMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/DashPathEffect", gDashPathEffectMethods, + NELEM(gDashPathEffectMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/PathDashPathEffect", + gPathDashPathEffectMethods, NELEM(gPathDashPathEffectMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/CornerPathEffect", + gCornerPathEffectMethods, NELEM(gCornerPathEffectMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/DiscretePathEffect", + gDiscretePathEffectMethods, NELEM(gDiscretePathEffectMethods)); + + return 0; +} diff --git a/libs/hwui/jni/PathMeasure.cpp b/libs/hwui/jni/PathMeasure.cpp new file mode 100644 index 000000000000..acf893e9544c --- /dev/null +++ b/libs/hwui/jni/PathMeasure.cpp @@ -0,0 +1,160 @@ +/* libs/android_runtime/android/graphics/PathMeasure.cpp +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "GraphicsJNI.h" + +#include "SkPathMeasure.h" + +/* We declare an explicit pair, so that we don't have to rely on the java + client to be sure not to edit the path while we have an active measure + object associated with it. + + This costs us the copy of the path, for the sake of not allowing a bad + java client to randomly crash (since we can't detect the case where the + native path has been modified). + + The C side does have this risk, but it chooses for speed over safety. If it + later changes this, and is internally safe from changes to the path, then + we can remove this explicit copy from our JNI code. + + Note that we do not have a reference on the java side to the java path. + Were we to not need the native copy here, we would want to add a java + reference, so that the java path would not get GD'd while the measure object + was still alive. +*/ +struct PathMeasurePair { + PathMeasurePair() {} + PathMeasurePair(const SkPath& path, bool forceClosed) + : fPath(path), fMeasure(fPath, forceClosed) {} + + SkPath fPath; // copy of the user's path + SkPathMeasure fMeasure; // this guy points to fPath +}; + +namespace android { + +class SkPathMeasureGlue { +public: + + static jlong create(JNIEnv* env, jobject clazz, jlong pathHandle, + jboolean forceClosedHandle) { + const SkPath* path = reinterpret_cast<SkPath*>(pathHandle); + bool forceClosed = (forceClosedHandle == JNI_TRUE); + PathMeasurePair* pair; + if(path) + pair = new PathMeasurePair(*path, forceClosed); + else + pair = new PathMeasurePair; + return reinterpret_cast<jlong>(pair); + } + + static void setPath(JNIEnv* env, jobject clazz, jlong pairHandle, + jlong pathHandle, jboolean forceClosedHandle) { + PathMeasurePair* pair = reinterpret_cast<PathMeasurePair*>(pairHandle); + const SkPath* path = reinterpret_cast<SkPath*>(pathHandle); + bool forceClosed = (forceClosedHandle == JNI_TRUE); + + if (NULL == path) { + pair->fPath.reset(); + } else { + pair->fPath = *path; + } + pair->fMeasure.setPath(&pair->fPath, forceClosed); + } + + static jfloat getLength(JNIEnv* env, jobject clazz, jlong pairHandle) { + PathMeasurePair* pair = reinterpret_cast<PathMeasurePair*>(pairHandle); + return static_cast<jfloat>(SkScalarToFloat(pair->fMeasure.getLength())); + } + + static void convertTwoElemFloatArray(JNIEnv* env, jfloatArray array, const SkScalar src[2]) { + AutoJavaFloatArray autoArray(env, array, 2); + jfloat* ptr = autoArray.ptr(); + ptr[0] = SkScalarToFloat(src[0]); + ptr[1] = SkScalarToFloat(src[1]); + } + + static jboolean getPosTan(JNIEnv* env, jobject clazz, jlong pairHandle, jfloat dist, jfloatArray pos, jfloatArray tan) { + PathMeasurePair* pair = reinterpret_cast<PathMeasurePair*>(pairHandle); + SkScalar tmpPos[2], tmpTan[2]; + SkScalar* posPtr = pos ? tmpPos : NULL; + SkScalar* tanPtr = tan ? tmpTan : NULL; + + if (!pair->fMeasure.getPosTan(dist, (SkPoint*)posPtr, (SkVector*)tanPtr)) { + return JNI_FALSE; + } + + if (pos) { + convertTwoElemFloatArray(env, pos, tmpPos); + } + if (tan) { + convertTwoElemFloatArray(env, tan, tmpTan); + } + return JNI_TRUE; + } + + static jboolean getMatrix(JNIEnv* env, jobject clazz, jlong pairHandle, jfloat dist, + jlong matrixHandle, jint flags) { + PathMeasurePair* pair = reinterpret_cast<PathMeasurePair*>(pairHandle); + SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); + bool result = pair->fMeasure.getMatrix(dist, matrix, (SkPathMeasure::MatrixFlags)flags); + return result ? JNI_TRUE : JNI_FALSE; + } + + static jboolean getSegment(JNIEnv* env, jobject clazz, jlong pairHandle, jfloat startF, + jfloat stopF, jlong dstHandle, jboolean startWithMoveTo) { + PathMeasurePair* pair = reinterpret_cast<PathMeasurePair*>(pairHandle); + SkPath* dst = reinterpret_cast<SkPath*>(dstHandle); + bool result = pair->fMeasure.getSegment(startF, stopF, dst, startWithMoveTo); + return result ? JNI_TRUE : JNI_FALSE; + } + + static jboolean isClosed(JNIEnv* env, jobject clazz, jlong pairHandle) { + PathMeasurePair* pair = reinterpret_cast<PathMeasurePair*>(pairHandle); + bool result = pair->fMeasure.isClosed(); + return result ? JNI_TRUE : JNI_FALSE; + } + + static jboolean nextContour(JNIEnv* env, jobject clazz, jlong pairHandle) { + PathMeasurePair* pair = reinterpret_cast<PathMeasurePair*>(pairHandle); + bool result = pair->fMeasure.nextContour(); + return result ? JNI_TRUE : JNI_FALSE; + } + + static void destroy(JNIEnv* env, jobject clazz, jlong pairHandle) { + PathMeasurePair* pair = reinterpret_cast<PathMeasurePair*>(pairHandle); + delete pair; + } +}; + +static const JNINativeMethod methods[] = { + {"native_create", "(JZ)J", (void*) SkPathMeasureGlue::create }, + {"native_setPath", "(JJZ)V", (void*) SkPathMeasureGlue::setPath }, + {"native_getLength", "(J)F", (void*) SkPathMeasureGlue::getLength }, + {"native_getPosTan", "(JF[F[F)Z", (void*) SkPathMeasureGlue::getPosTan }, + {"native_getMatrix", "(JFJI)Z", (void*) SkPathMeasureGlue::getMatrix }, + {"native_getSegment", "(JFFJZ)Z", (void*) SkPathMeasureGlue::getSegment }, + {"native_isClosed", "(J)Z", (void*) SkPathMeasureGlue::isClosed }, + {"native_nextContour", "(J)Z", (void*) SkPathMeasureGlue::nextContour }, + {"native_destroy", "(J)V", (void*) SkPathMeasureGlue::destroy } +}; + +int register_android_graphics_PathMeasure(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/PathMeasure", methods, NELEM(methods)); +} + +} diff --git a/libs/hwui/jni/Picture.cpp b/libs/hwui/jni/Picture.cpp new file mode 100644 index 000000000000..d1b952130e88 --- /dev/null +++ b/libs/hwui/jni/Picture.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2014 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. + */ + +#include "Picture.h" +#include "SkStream.h" + +#include <memory> +#include <hwui/Canvas.h> + +namespace android { + +Picture::Picture(const Picture* src) { + if (NULL != src) { + mWidth = src->width(); + mHeight = src->height(); + if (NULL != src->mPicture.get()) { + mPicture = src->mPicture; + } else if (NULL != src->mRecorder.get()) { + mPicture = src->makePartialCopy(); + } + } else { + mWidth = 0; + mHeight = 0; + } +} + +Picture::Picture(sk_sp<SkPicture>&& src) { + mPicture = std::move(src); + mWidth = 0; + mHeight = 0; +} + +Canvas* Picture::beginRecording(int width, int height) { + mPicture.reset(NULL); + mRecorder.reset(new SkPictureRecorder); + mWidth = width; + mHeight = height; + SkCanvas* canvas = mRecorder->beginRecording(SkIntToScalar(width), SkIntToScalar(height)); + return Canvas::create_canvas(canvas); +} + +void Picture::endRecording() { + if (NULL != mRecorder.get()) { + mPicture = mRecorder->finishRecordingAsPicture(); + mRecorder.reset(NULL); + } +} + +int Picture::width() const { + return mWidth; +} + +int Picture::height() const { + return mHeight; +} + +Picture* Picture::CreateFromStream(SkStream* stream) { + Picture* newPict = new Picture; + + sk_sp<SkPicture> skPicture = SkPicture::MakeFromStream(stream); + if (NULL != skPicture) { + newPict->mPicture = skPicture; + + const SkIRect cullRect = skPicture->cullRect().roundOut(); + newPict->mWidth = cullRect.width(); + newPict->mHeight = cullRect.height(); + } + + return newPict; +} + +void Picture::serialize(SkWStream* stream) const { + if (NULL != mRecorder.get()) { + this->makePartialCopy()->serialize(stream); + } else if (NULL != mPicture.get()) { + mPicture->serialize(stream); + } else { + // serialize "empty" picture + SkPictureRecorder recorder; + recorder.beginRecording(0, 0); + recorder.finishRecordingAsPicture()->serialize(stream); + } +} + +void Picture::draw(Canvas* canvas) { + if (NULL != mRecorder.get()) { + this->endRecording(); + SkASSERT(NULL != mPicture.get()); + } + + if (mPicture) { + canvas->drawPicture(*mPicture); + } +} + +sk_sp<SkPicture> Picture::makePartialCopy() const { + SkASSERT(NULL != mRecorder.get()); + + SkPictureRecorder reRecorder; + + SkCanvas* canvas = reRecorder.beginRecording(mWidth, mHeight, NULL, 0); + mRecorder->partialReplay(canvas); + return reRecorder.finishRecordingAsPicture(); +} + +}; // namespace android diff --git a/libs/hwui/jni/Picture.h b/libs/hwui/jni/Picture.h new file mode 100644 index 000000000000..536f651473a9 --- /dev/null +++ b/libs/hwui/jni/Picture.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef ANDROID_GRAPHICS_PICTURE_H_ +#define ANDROID_GRAPHICS_PICTURE_H_ + +#include "SkPicture.h" +#include "SkPictureRecorder.h" +#include "SkRefCnt.h" + +#include <memory> + +class SkStream; +class SkWStream; + +namespace android { + +class Canvas; + +// Skia's SkPicture class has been split into an SkPictureRecorder +// and an SkPicture. AndroidPicture recreates the functionality +// of the old SkPicture interface by flip-flopping between the two +// new classes. +class Picture { +public: + explicit Picture(const Picture* src = NULL); + explicit Picture(sk_sp<SkPicture>&& src); + + Canvas* beginRecording(int width, int height); + + void endRecording(); + + int width() const; + + int height() const; + + static Picture* CreateFromStream(SkStream* stream); + + void serialize(SkWStream* stream) const; + + void draw(Canvas* canvas); + +private: + int mWidth; + int mHeight; + sk_sp<SkPicture> mPicture; + std::unique_ptr<SkPictureRecorder> mRecorder; + + // Make a copy of a picture that is in the midst of being recorded. The + // resulting picture will have balanced saves and restores. + sk_sp<SkPicture> makePartialCopy() const; +}; + +}; // namespace android +#endif // ANDROID_GRAPHICS_PICTURE_H_ diff --git a/libs/hwui/jni/Region.cpp b/libs/hwui/jni/Region.cpp new file mode 100644 index 000000000000..1e064b820591 --- /dev/null +++ b/libs/hwui/jni/Region.cpp @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2011 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. + */ + +#include "SkRegion.h" +#include "SkPath.h" +#include "GraphicsJNI.h" + +#ifdef __ANDROID__ // Layoutlib does not support parcel +#include <android/binder_parcel.h> +#include <android/binder_parcel_jni.h> +#include <android/binder_parcel_utils.h> +#endif + +namespace android { + +static jfieldID gRegion_nativeInstanceFieldID; + +static inline jboolean boolTojboolean(bool value) { + return value ? JNI_TRUE : JNI_FALSE; +} + +static inline SkRegion* GetSkRegion(JNIEnv* env, jobject regionObject) { + jlong regionHandle = env->GetLongField(regionObject, gRegion_nativeInstanceFieldID); + SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle); + SkASSERT(region != NULL); + return region; +} + +static jlong Region_constructor(JNIEnv* env, jobject) { + return reinterpret_cast<jlong>(new SkRegion); +} + +static void Region_destructor(JNIEnv* env, jobject, jlong regionHandle) { + SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle); + SkASSERT(region); + delete region; +} + +static void Region_setRegion(JNIEnv* env, jobject, jlong dstHandle, jlong srcHandle) { + SkRegion* dst = reinterpret_cast<SkRegion*>(dstHandle); + const SkRegion* src = reinterpret_cast<SkRegion*>(srcHandle); + SkASSERT(dst && src); + *dst = *src; +} + +static jboolean Region_setRect(JNIEnv* env, jobject, jlong dstHandle, jint left, jint top, jint right, jint bottom) { + SkRegion* dst = reinterpret_cast<SkRegion*>(dstHandle); + bool result = dst->setRect({left, top, right, bottom}); + return boolTojboolean(result); +} + +static jboolean Region_setPath(JNIEnv* env, jobject, jlong dstHandle, + jlong pathHandle, jlong clipHandle) { + SkRegion* dst = reinterpret_cast<SkRegion*>(dstHandle); + const SkPath* path = reinterpret_cast<SkPath*>(pathHandle); + const SkRegion* clip = reinterpret_cast<SkRegion*>(clipHandle); + SkASSERT(dst && path && clip); + bool result = dst->setPath(*path, *clip); + return boolTojboolean(result); + +} + +static jboolean Region_getBounds(JNIEnv* env, jobject, jlong regionHandle, jobject rectBounds) { + SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle); + GraphicsJNI::irect_to_jrect(region->getBounds(), env, rectBounds); + bool result = !region->isEmpty(); + return boolTojboolean(result); +} + +static jboolean Region_getBoundaryPath(JNIEnv* env, jobject, jlong regionHandle, jlong pathHandle) { + const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle); + SkPath* path = reinterpret_cast<SkPath*>(pathHandle); + bool result = region->getBoundaryPath(path); + return boolTojboolean(result); +} + +static jboolean Region_op0(JNIEnv* env, jobject, jlong dstHandle, jint left, jint top, jint right, jint bottom, jint op) { + SkRegion* dst = reinterpret_cast<SkRegion*>(dstHandle); + bool result = dst->op({left, top, right, bottom}, (SkRegion::Op)op); + return boolTojboolean(result); +} + +static jboolean Region_op1(JNIEnv* env, jobject, jlong dstHandle, jobject rectObject, jlong regionHandle, jint op) { + SkRegion* dst = reinterpret_cast<SkRegion*>(dstHandle); + const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle); + SkIRect ir; + GraphicsJNI::jrect_to_irect(env, rectObject, &ir); + bool result = dst->op(ir, *region, (SkRegion::Op)op); + return boolTojboolean(result); +} + +static jboolean Region_op2(JNIEnv* env, jobject, jlong dstHandle, jlong region1Handle, jlong region2Handle, jint op) { + SkRegion* dst = reinterpret_cast<SkRegion*>(dstHandle); + const SkRegion* region1 = reinterpret_cast<SkRegion*>(region1Handle); + const SkRegion* region2 = reinterpret_cast<SkRegion*>(region2Handle); + bool result = dst->op(*region1, *region2, (SkRegion::Op)op); + return boolTojboolean(result); +} + +//////////////////////////////////// These are methods, not static + +static jboolean Region_isEmpty(JNIEnv* env, jobject region) { + bool result = GetSkRegion(env, region)->isEmpty(); + return boolTojboolean(result); +} + +static jboolean Region_isRect(JNIEnv* env, jobject region) { + bool result = GetSkRegion(env, region)->isRect(); + return boolTojboolean(result); +} + +static jboolean Region_isComplex(JNIEnv* env, jobject region) { + bool result = GetSkRegion(env, region)->isComplex(); + return boolTojboolean(result); +} + +static jboolean Region_contains(JNIEnv* env, jobject region, jint x, jint y) { + bool result = GetSkRegion(env, region)->contains(x, y); + return boolTojboolean(result); +} + +static jboolean Region_quickContains(JNIEnv* env, jobject region, jint left, jint top, jint right, jint bottom) { + bool result = GetSkRegion(env, region)->quickContains({left, top, right, bottom}); + return boolTojboolean(result); +} + +static jboolean Region_quickRejectIIII(JNIEnv* env, jobject region, jint left, jint top, jint right, jint bottom) { + SkIRect ir; + ir.setLTRB(left, top, right, bottom); + bool result = GetSkRegion(env, region)->quickReject(ir); + return boolTojboolean(result); +} + +static jboolean Region_quickRejectRgn(JNIEnv* env, jobject region, jobject other) { + bool result = GetSkRegion(env, region)->quickReject(*GetSkRegion(env, other)); + return boolTojboolean(result); +} + +static void Region_translate(JNIEnv* env, jobject region, jint x, jint y, jobject dst) { + SkRegion* rgn = GetSkRegion(env, region); + if (dst) + rgn->translate(x, y, GetSkRegion(env, dst)); + else + rgn->translate(x, y); +} + +// Scale the rectangle by given scale and set the reuslt to the dst. +static void scale_rect(SkIRect* dst, const SkIRect& src, float scale) { + dst->fLeft = (int)::roundf(src.fLeft * scale); + dst->fTop = (int)::roundf(src.fTop * scale); + dst->fRight = (int)::roundf(src.fRight * scale); + dst->fBottom = (int)::roundf(src.fBottom * scale); +} + +// Scale the region by given scale and set the reuslt to the dst. +// dest and src can be the same region instance. +static void scale_rgn(SkRegion* dst, const SkRegion& src, float scale) { + SkRegion tmp; + SkRegion::Iterator iter(src); + + for (; !iter.done(); iter.next()) { + SkIRect r; + scale_rect(&r, iter.rect(), scale); + tmp.op(r, SkRegion::kUnion_Op); + } + dst->swap(tmp); +} + +static void Region_scale(JNIEnv* env, jobject region, jfloat scale, jobject dst) { + SkRegion* rgn = GetSkRegion(env, region); + if (dst) + scale_rgn(GetSkRegion(env, dst), *rgn, scale); + else + scale_rgn(rgn, *rgn, scale); +} + +static jstring Region_toString(JNIEnv* env, jobject clazz, jlong regionHandle) { + SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle); + char* str = region->toString(); + if (str == NULL) { + return NULL; + } + jstring result = env->NewStringUTF(str); + free(str); + return result; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static jlong Region_createFromParcel(JNIEnv* env, jobject clazz, jobject parcel) +{ +#ifdef __ANDROID__ // Layoutlib does not support parcel + if (parcel == nullptr) { + return 0; + } + + std::vector<int32_t> rects; + + AParcel* p = AParcel_fromJavaParcel(env, parcel); + ndk::AParcel_readVector(p, &rects); + AParcel_delete(p); + + if ((rects.size() % 4) != 0) { + return 0; + } + + SkRegion* region = new SkRegion; + for (size_t x = 0; x + 4 <= rects.size(); x += 4) { + region->op({rects[x], rects[x+1], rects[x+2], rects[x+3]}, SkRegion::kUnion_Op); + } + + return reinterpret_cast<jlong>(region); +#else + return 0; +#endif +} + +static jboolean Region_writeToParcel(JNIEnv* env, jobject clazz, jlong regionHandle, jobject parcel) +{ +#ifdef __ANDROID__ // Layoutlib does not support parcel + const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle); + if (parcel == nullptr) { + return JNI_FALSE; + } + + std::vector<int32_t> rects; + SkRegion::Iterator it(*region); + while (!it.done()) { + const SkIRect& r = it.rect(); + rects.push_back(r.fLeft); + rects.push_back(r.fTop); + rects.push_back(r.fRight); + rects.push_back(r.fBottom); + it.next(); + } + + AParcel* p = AParcel_fromJavaParcel(env, parcel); + ndk::AParcel_writeVector(p, rects); + AParcel_delete(p); + + return JNI_TRUE; +#else + return JNI_FALSE; +#endif +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static jboolean Region_equals(JNIEnv* env, jobject clazz, jlong r1Handle, jlong r2Handle) +{ + const SkRegion *r1 = reinterpret_cast<SkRegion*>(r1Handle); + const SkRegion *r2 = reinterpret_cast<SkRegion*>(r2Handle); + return boolTojboolean(*r1 == *r2); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct RgnIterPair { + SkRegion fRgn; // a copy of the caller's region + SkRegion::Iterator fIter; // an iterator acting upon the copy (fRgn) + + explicit RgnIterPair(const SkRegion& rgn) : fRgn(rgn) { + // have our iterator reference our copy (fRgn), so we know it will be + // unchanged for the lifetime of the iterator + fIter.reset(fRgn); + } +}; + +static jlong RegionIter_constructor(JNIEnv* env, jobject, jlong regionHandle) +{ + const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle); + SkASSERT(region); + return reinterpret_cast<jlong>(new RgnIterPair(*region)); +} + +static void RegionIter_destructor(JNIEnv* env, jobject, jlong pairHandle) +{ + RgnIterPair* pair = reinterpret_cast<RgnIterPair*>(pairHandle); + SkASSERT(pair); + delete pair; +} + +static jboolean RegionIter_next(JNIEnv* env, jobject, jlong pairHandle, jobject rectObject) +{ + RgnIterPair* pair = reinterpret_cast<RgnIterPair*>(pairHandle); + // the caller has checked that rectObject is not nul + SkASSERT(pair); + SkASSERT(rectObject); + + if (!pair->fIter.done()) { + GraphicsJNI::irect_to_jrect(pair->fIter.rect(), env, rectObject); + pair->fIter.next(); + return JNI_TRUE; + } + return JNI_FALSE; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gRegionIterMethods[] = { + { "nativeConstructor", "(J)J", (void*)RegionIter_constructor }, + { "nativeDestructor", "(J)V", (void*)RegionIter_destructor }, + { "nativeNext", "(JLandroid/graphics/Rect;)Z", (void*)RegionIter_next } +}; + +static const JNINativeMethod gRegionMethods[] = { + // these are static methods + { "nativeConstructor", "()J", (void*)Region_constructor }, + { "nativeDestructor", "(J)V", (void*)Region_destructor }, + { "nativeSetRegion", "(JJ)V", (void*)Region_setRegion }, + { "nativeSetRect", "(JIIII)Z", (void*)Region_setRect }, + { "nativeSetPath", "(JJJ)Z", (void*)Region_setPath }, + { "nativeGetBounds", "(JLandroid/graphics/Rect;)Z", (void*)Region_getBounds }, + { "nativeGetBoundaryPath", "(JJ)Z", (void*)Region_getBoundaryPath }, + { "nativeOp", "(JIIIII)Z", (void*)Region_op0 }, + { "nativeOp", "(JLandroid/graphics/Rect;JI)Z", (void*)Region_op1 }, + { "nativeOp", "(JJJI)Z", (void*)Region_op2 }, + // these are methods that take the java region object + { "isEmpty", "()Z", (void*)Region_isEmpty }, + { "isRect", "()Z", (void*)Region_isRect }, + { "isComplex", "()Z", (void*)Region_isComplex }, + { "contains", "(II)Z", (void*)Region_contains }, + { "quickContains", "(IIII)Z", (void*)Region_quickContains }, + { "quickReject", "(IIII)Z", (void*)Region_quickRejectIIII }, + { "quickReject", "(Landroid/graphics/Region;)Z", (void*)Region_quickRejectRgn }, + { "scale", "(FLandroid/graphics/Region;)V", (void*)Region_scale }, + { "translate", "(IILandroid/graphics/Region;)V", (void*)Region_translate }, + { "nativeToString", "(J)Ljava/lang/String;", (void*)Region_toString }, + // parceling methods + { "nativeCreateFromParcel", "(Landroid/os/Parcel;)J", (void*)Region_createFromParcel }, + { "nativeWriteToParcel", "(JLandroid/os/Parcel;)Z", (void*)Region_writeToParcel }, + { "nativeEquals", "(JJ)Z", (void*)Region_equals }, +}; + +int register_android_graphics_Region(JNIEnv* env) +{ + jclass clazz = FindClassOrDie(env, "android/graphics/Region"); + + gRegion_nativeInstanceFieldID = GetFieldIDOrDie(env, clazz, "mNativeRegion", "J"); + + RegisterMethodsOrDie(env, "android/graphics/Region", gRegionMethods, NELEM(gRegionMethods)); + return RegisterMethodsOrDie(env, "android/graphics/RegionIterator", gRegionIterMethods, + NELEM(gRegionIterMethods)); +} + +} // namespace android diff --git a/libs/hwui/jni/RtlProperties.h b/libs/hwui/jni/RtlProperties.h new file mode 100644 index 000000000000..907dd59b6e68 --- /dev/null +++ b/libs/hwui/jni/RtlProperties.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2011 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. + */ + +#ifndef _ANDROID_GRAPHICS_RTL_PROPERTIES_H_ +#define _ANDROID_GRAPHICS_RTL_PROPERTIES_H_ + +#include <cutils/properties.h> +#include <stdlib.h> + +namespace android { + +/** + * Debug level for app developers. + */ +#define RTL_PROPERTY_DEBUG "rtl.debug_level" + +/** + * Debug levels. Debug levels are used as flags. + */ +enum RtlDebugLevel { + kRtlDebugDisabled = 0, + kRtlDebugMemory = 1, + kRtlDebugCaches = 2, + kRtlDebugAllocations = 3 +}; + +static RtlDebugLevel readRtlDebugLevel() { + char property[PROPERTY_VALUE_MAX]; + if (property_get(RTL_PROPERTY_DEBUG, property, NULL) > 0) { + return (RtlDebugLevel) atoi(property); + } + return kRtlDebugDisabled; +} + +} // namespace android +#endif // _ANDROID_GRAPHICS_RTL_PROPERTIES_H_ diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp new file mode 100644 index 000000000000..0f6837640524 --- /dev/null +++ b/libs/hwui/jni/Shader.cpp @@ -0,0 +1,305 @@ +#include "GraphicsJNI.h" +#include "SkColorFilter.h" +#include "SkGradientShader.h" +#include "SkImagePriv.h" +#include "SkShader.h" +#include "SkBlendMode.h" +#include "include/effects/SkRuntimeEffect.h" + +#include <vector> + +using namespace android::uirenderer; + +/** + * By default Skia gradients will interpolate their colors in unpremul space + * and then premultiply each of the results. We must set this flag to preserve + * backwards compatiblity by premultiplying the colors of the gradient first, + * and then interpolating between them. + */ +static const uint32_t sGradientShaderFlags = SkGradientShader::kInterpolateColorsInPremul_Flag; + +#define ThrowIAE_IfNull(env, ptr) \ + if (nullptr == ptr) { \ + doThrowIAE(env); \ + return 0; \ + } + +static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue, jfloatArray hsvArray) +{ + SkScalar hsv[3]; + SkRGBToHSV(red, green, blue, hsv); + + AutoJavaFloatArray autoHSV(env, hsvArray, 3); + float* values = autoHSV.ptr(); + for (int i = 0; i < 3; i++) { + values[i] = SkScalarToFloat(hsv[i]); + } +} + +static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvArray) +{ + AutoJavaFloatArray autoHSV(env, hsvArray, 3); +#ifdef SK_SCALAR_IS_FLOAT + SkScalar* hsv = autoHSV.ptr(); +#else + #error Need to convert float array to SkScalar array before calling the following function. +#endif + + return static_cast<jint>(SkHSVToColor(alpha, hsv)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static void Shader_safeUnref(SkShader* shader) { + SkSafeUnref(shader); +} + +static jlong Shader_getNativeFinalizer(JNIEnv*, jobject) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Shader_safeUnref)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle, + jint tileModeX, jint tileModeY) { + const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); + sk_sp<SkImage> image; + if (bitmapHandle) { + // Only pass a valid SkBitmap object to the constructor if the Bitmap exists. Otherwise, + // we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility. + image = android::bitmap::toBitmap(bitmapHandle).makeImage(); + } + + if (!image.get()) { + SkBitmap bitmap; + image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); + } + sk_sp<SkShader> shader = image->makeShader( + (SkTileMode)tileModeX, (SkTileMode)tileModeY); + ThrowIAE_IfNull(env, shader.get()); + + if (matrix) { + shader = shader->makeWithLocalMatrix(*matrix); + } + + return reinterpret_cast<jlong>(shader.release()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static std::vector<SkColor4f> convertColorLongs(JNIEnv* env, jlongArray colorArray) { + const size_t count = env->GetArrayLength(colorArray); + const jlong* colorValues = env->GetLongArrayElements(colorArray, nullptr); + + std::vector<SkColor4f> colors(count); + for (size_t i = 0; i < count; ++i) { + colors[i] = GraphicsJNI::convertColorLong(colorValues[i]); + } + + env->ReleaseLongArrayElements(colorArray, const_cast<jlong*>(colorValues), JNI_ABORT); + return colors; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static jlong LinearGradient_create(JNIEnv* env, jobject, jlong matrixPtr, + jfloat x0, jfloat y0, jfloat x1, jfloat y1, jlongArray colorArray, + jfloatArray posArray, jint tileMode, jlong colorSpaceHandle) { + SkPoint pts[2]; + pts[0].set(x0, y0); + pts[1].set(x1, y1); + + std::vector<SkColor4f> colors = convertColorLongs(env, colorArray); + + AutoJavaFloatArray autoPos(env, posArray, colors.size()); +#ifdef SK_SCALAR_IS_FLOAT + SkScalar* pos = autoPos.ptr(); +#else + #error Need to convert float array to SkScalar array before calling the following function. +#endif + + sk_sp<SkShader> shader(SkGradientShader::MakeLinear(pts, &colors[0], + GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(), + static_cast<SkTileMode>(tileMode), sGradientShaderFlags, nullptr)); + ThrowIAE_IfNull(env, shader); + + const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); + if (matrix) { + shader = shader->makeWithLocalMatrix(*matrix); + } + + return reinterpret_cast<jlong>(shader.release()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static jlong RadialGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y, + jfloat radius, jlongArray colorArray, jfloatArray posArray, jint tileMode, + jlong colorSpaceHandle) { + SkPoint center; + center.set(x, y); + + std::vector<SkColor4f> colors = convertColorLongs(env, colorArray); + + AutoJavaFloatArray autoPos(env, posArray, colors.size()); +#ifdef SK_SCALAR_IS_FLOAT + SkScalar* pos = autoPos.ptr(); +#else + #error Need to convert float array to SkScalar array before calling the following function. +#endif + + sk_sp<SkShader> shader = SkGradientShader::MakeRadial(center, radius, &colors[0], + GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(), + static_cast<SkTileMode>(tileMode), sGradientShaderFlags, nullptr); + ThrowIAE_IfNull(env, shader); + + const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); + if (matrix) { + shader = shader->makeWithLocalMatrix(*matrix); + } + + return reinterpret_cast<jlong>(shader.release()); +} + +/////////////////////////////////////////////////////////////////////////////// + +static jlong SweepGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y, + jlongArray colorArray, jfloatArray jpositions, jlong colorSpaceHandle) { + std::vector<SkColor4f> colors = convertColorLongs(env, colorArray); + + AutoJavaFloatArray autoPos(env, jpositions, colors.size()); +#ifdef SK_SCALAR_IS_FLOAT + SkScalar* pos = autoPos.ptr(); +#else + #error Need to convert float array to SkScalar array before calling the following function. +#endif + + sk_sp<SkShader> shader = SkGradientShader::MakeSweep(x, y, &colors[0], + GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(), + sGradientShaderFlags, nullptr); + ThrowIAE_IfNull(env, shader); + + const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); + if (matrix) { + shader = shader->makeWithLocalMatrix(*matrix); + } + + return reinterpret_cast<jlong>(shader.release()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr, + jlong shaderAHandle, jlong shaderBHandle, jint xfermodeHandle) { + const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); + SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle); + SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle); + SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle); + sk_sp<SkShader> baseShader(SkShaders::Blend(mode, + sk_ref_sp(shaderA), sk_ref_sp(shaderB))); + + SkShader* shader; + + if (matrix) { + shader = baseShader->makeWithLocalMatrix(*matrix).release(); + } else { + shader = baseShader.release(); + } + return reinterpret_cast<jlong>(shader); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlong matrixPtr, + jbyteArray inputs, jlong colorSpaceHandle, jboolean isOpaque) { + SkRuntimeEffect* effect = reinterpret_cast<SkRuntimeEffect*>(shaderFactory); + AutoJavaByteArray arInputs(env, inputs); + + sk_sp<SkData> fData; + fData = SkData::MakeWithCopy(arInputs.ptr(), arInputs.length()); + const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); + sk_sp<SkShader> shader = effect->makeShader(fData, nullptr, 0, matrix, isOpaque == JNI_TRUE); + ThrowIAE_IfNull(env, shader); + + return reinterpret_cast<jlong>(shader.release()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static jlong RuntimeShader_createShaderFactory(JNIEnv* env, jobject, jstring sksl) { + ScopedUtfChars strSksl(env, sksl); + sk_sp<SkRuntimeEffect> effect = std::get<0>(SkRuntimeEffect::Make(SkString(strSksl.c_str()))); + ThrowIAE_IfNull(env, effect); + + return reinterpret_cast<jlong>(effect.release()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static void Effect_safeUnref(SkRuntimeEffect* effect) { + SkSafeUnref(effect); +} + +static jlong RuntimeShader_getNativeFinalizer(JNIEnv*, jobject) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Effect_safeUnref)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gColorMethods[] = { + { "nativeRGBToHSV", "(III[F)V", (void*)Color_RGBToHSV }, + { "nativeHSVToColor", "(I[F)I", (void*)Color_HSVToColor } +}; + +static const JNINativeMethod gShaderMethods[] = { + { "nativeGetFinalizer", "()J", (void*)Shader_getNativeFinalizer }, +}; + +static const JNINativeMethod gBitmapShaderMethods[] = { + { "nativeCreate", "(JJII)J", (void*)BitmapShader_constructor }, +}; + +static const JNINativeMethod gLinearGradientMethods[] = { + { "nativeCreate", "(JFFFF[J[FIJ)J", (void*)LinearGradient_create }, +}; + +static const JNINativeMethod gRadialGradientMethods[] = { + { "nativeCreate", "(JFFF[J[FIJ)J", (void*)RadialGradient_create }, +}; + +static const JNINativeMethod gSweepGradientMethods[] = { + { "nativeCreate", "(JFF[J[FJ)J", (void*)SweepGradient_create }, +}; + +static const JNINativeMethod gComposeShaderMethods[] = { + { "nativeCreate", "(JJJI)J", (void*)ComposeShader_create }, +}; + +static const JNINativeMethod gRuntimeShaderMethods[] = { + { "nativeGetFinalizer", "()J", (void*)RuntimeShader_getNativeFinalizer }, + { "nativeCreate", "(JJ[BJZ)J", (void*)RuntimeShader_create }, + { "nativeCreateShaderFactory", "(Ljava/lang/String;)J", + (void*)RuntimeShader_createShaderFactory }, +}; + +int register_android_graphics_Shader(JNIEnv* env) +{ + android::RegisterMethodsOrDie(env, "android/graphics/Color", gColorMethods, + NELEM(gColorMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/Shader", gShaderMethods, + NELEM(gShaderMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/BitmapShader", gBitmapShaderMethods, + NELEM(gBitmapShaderMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/LinearGradient", gLinearGradientMethods, + NELEM(gLinearGradientMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/RadialGradient", gRadialGradientMethods, + NELEM(gRadialGradientMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/SweepGradient", gSweepGradientMethods, + NELEM(gSweepGradientMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/ComposeShader", gComposeShaderMethods, + NELEM(gComposeShaderMethods)); + android::RegisterMethodsOrDie(env, "android/graphics/RuntimeShader", gRuntimeShaderMethods, + NELEM(gRuntimeShaderMethods)); + + return 0; +} diff --git a/libs/hwui/jni/TEST_MAPPING b/libs/hwui/jni/TEST_MAPPING new file mode 100644 index 000000000000..10bd0ee906fd --- /dev/null +++ b/libs/hwui/jni/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "CtsGraphicsTestCases" + } + ] +} diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp new file mode 100644 index 000000000000..2a5f402a4fa6 --- /dev/null +++ b/libs/hwui/jni/Typeface.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2013 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. + */ + +#include "FontUtils.h" +#include "GraphicsJNI.h" +#include <nativehelper/ScopedPrimitiveArray.h> +#include <nativehelper/ScopedUtfChars.h> +#include "SkTypeface.h" +#include <hwui/Typeface.h> +#include <minikin/FontFamily.h> +#include <minikin/SystemFonts.h> + +using namespace android; + +static inline Typeface* toTypeface(jlong ptr) { + return reinterpret_cast<Typeface*>(ptr); +} + +template<typename Ptr> static inline jlong toJLong(Ptr ptr) { + return reinterpret_cast<jlong>(ptr); +} + +static jlong Typeface_createFromTypeface(JNIEnv* env, jobject, jlong familyHandle, jint style) { + Typeface* family = toTypeface(familyHandle); + Typeface* face = Typeface::createRelative(family, (Typeface::Style)style); + // TODO: the following logic shouldn't be necessary, the above should always succeed. + // Try to find the closest matching font, using the standard heuristic + if (NULL == face) { + face = Typeface::createRelative(family, (Typeface::Style)(style ^ Typeface::kItalic)); + } + for (int i = 0; NULL == face && i < 4; i++) { + face = Typeface::createRelative(family, (Typeface::Style)i); + } + return toJLong(face); +} + +static jlong Typeface_createFromTypefaceWithExactStyle(JNIEnv* env, jobject, jlong nativeInstance, + jint weight, jboolean italic) { + return toJLong(Typeface::createAbsolute(toTypeface(nativeInstance), weight, italic)); +} + +static jlong Typeface_createFromTypefaceWithVariation(JNIEnv* env, jobject, jlong familyHandle, + jobject listOfAxis) { + std::vector<minikin::FontVariation> variations; + ListHelper list(env, listOfAxis); + for (jint i = 0; i < list.size(); i++) { + jobject axisObject = list.get(i); + if (axisObject == nullptr) { + continue; + } + AxisHelper axis(env, axisObject); + variations.push_back(minikin::FontVariation(axis.getTag(), axis.getStyleValue())); + } + return toJLong(Typeface::createFromTypefaceWithVariation(toTypeface(familyHandle), variations)); +} + +static jlong Typeface_createWeightAlias(JNIEnv* env, jobject, jlong familyHandle, jint weight) { + return toJLong(Typeface::createWithDifferentBaseWeight(toTypeface(familyHandle), weight)); +} + +static void releaseFunc(jlong ptr) { + delete toTypeface(ptr); +} + +// CriticalNative +static jlong Typeface_getReleaseFunc(CRITICAL_JNI_PARAMS) { + return toJLong(&releaseFunc); +} + +// CriticalNative +static jint Typeface_getStyle(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) { + return toTypeface(faceHandle)->fAPIStyle; +} + +// CriticalNative +static jint Typeface_getWeight(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) { + return toTypeface(faceHandle)->fStyle.weight(); +} + +static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArray, + int weight, int italic) { + ScopedLongArrayRO families(env, familyArray); + std::vector<std::shared_ptr<minikin::FontFamily>> familyVec; + familyVec.reserve(families.size()); + for (size_t i = 0; i < families.size(); i++) { + FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(families[i]); + familyVec.emplace_back(family->family); + } + return toJLong(Typeface::createFromFamilies(std::move(familyVec), weight, italic)); +} + +// CriticalNative +static void Typeface_setDefault(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) { + Typeface::setDefault(toTypeface(faceHandle)); + minikin::SystemFonts::registerDefault(toTypeface(faceHandle)->fFontCollection); +} + +static jobject Typeface_getSupportedAxes(JNIEnv *env, jobject, jlong faceHandle) { + Typeface* face = toTypeface(faceHandle); + const std::unordered_set<minikin::AxisTag>& tagSet = face->fFontCollection->getSupportedTags(); + const size_t length = tagSet.size(); + if (length == 0) { + return nullptr; + } + std::vector<jint> tagVec(length); + int index = 0; + for (const auto& tag : tagSet) { + tagVec[index++] = tag; + } + std::sort(tagVec.begin(), tagVec.end()); + const jintArray result = env->NewIntArray(length); + env->SetIntArrayRegion(result, 0, length, tagVec.data()); + return result; +} + +static void Typeface_registerGenericFamily(JNIEnv *env, jobject, jstring familyName, jlong ptr) { + ScopedUtfChars familyNameChars(env, familyName); + minikin::SystemFonts::registerFallback(familyNameChars.c_str(), + toTypeface(ptr)->fFontCollection); +} + +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gTypefaceMethods[] = { + { "nativeCreateFromTypeface", "(JI)J", (void*)Typeface_createFromTypeface }, + { "nativeCreateFromTypefaceWithExactStyle", "(JIZ)J", + (void*)Typeface_createFromTypefaceWithExactStyle }, + { "nativeCreateFromTypefaceWithVariation", "(JLjava/util/List;)J", + (void*)Typeface_createFromTypefaceWithVariation }, + { "nativeCreateWeightAlias", "(JI)J", (void*)Typeface_createWeightAlias }, + { "nativeGetReleaseFunc", "()J", (void*)Typeface_getReleaseFunc }, + { "nativeGetStyle", "(J)I", (void*)Typeface_getStyle }, + { "nativeGetWeight", "(J)I", (void*)Typeface_getWeight }, + { "nativeCreateFromArray", "([JII)J", + (void*)Typeface_createFromArray }, + { "nativeSetDefault", "(J)V", (void*)Typeface_setDefault }, + { "nativeGetSupportedAxes", "(J)[I", (void*)Typeface_getSupportedAxes }, + { "nativeRegisterGenericFamily", "(Ljava/lang/String;J)V", + (void*)Typeface_registerGenericFamily }, +}; + +int register_android_graphics_Typeface(JNIEnv* env) +{ + return RegisterMethodsOrDie(env, "android/graphics/Typeface", gTypefaceMethods, + NELEM(gTypefaceMethods)); +} diff --git a/libs/hwui/jni/Utils.cpp b/libs/hwui/jni/Utils.cpp new file mode 100644 index 000000000000..34fd6687d52c --- /dev/null +++ b/libs/hwui/jni/Utils.cpp @@ -0,0 +1,171 @@ +/* + * 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. + */ + +#include "Utils.h" +#include "SkUtils.h" +#include "SkData.h" + +#include <log/log.h> + +using namespace android; + +AssetStreamAdaptor::AssetStreamAdaptor(Asset* asset) + : fAsset(asset) +{ +} + +bool AssetStreamAdaptor::rewind() { + off64_t pos = fAsset->seek(0, SEEK_SET); + if (pos == (off64_t)-1) { + SkDebugf("----- fAsset->seek(rewind) failed\n"); + return false; + } + return true; +} + +size_t AssetStreamAdaptor::getLength() const { + return fAsset->getLength(); +} + +bool AssetStreamAdaptor::isAtEnd() const { + return fAsset->getRemainingLength() == 0; +} + +SkStreamRewindable* AssetStreamAdaptor::onDuplicate() const { + // Cannot create a duplicate, since each AssetStreamAdaptor + // would be modifying the Asset. + //return new AssetStreamAdaptor(fAsset); + return NULL; +} + +bool AssetStreamAdaptor::hasPosition() const { + return fAsset->seek(0, SEEK_CUR) != -1; +} + +size_t AssetStreamAdaptor::getPosition() const { + const off64_t offset = fAsset->seek(0, SEEK_CUR); + if (offset == -1) { + SkDebugf("---- fAsset->seek(0, SEEK_CUR) failed\n"); + return 0; + } + + return offset; +} + +bool AssetStreamAdaptor::seek(size_t position) { + if (fAsset->seek(position, SEEK_SET) == -1) { + SkDebugf("---- fAsset->seek(0, SEEK_SET) failed\n"); + return false; + } + + return true; +} + +bool AssetStreamAdaptor::move(long offset) { + if (fAsset->seek(offset, SEEK_CUR) == -1) { + SkDebugf("---- fAsset->seek(%i, SEEK_CUR) failed\n", offset); + return false; + } + + return true; +} + +size_t AssetStreamAdaptor::read(void* buffer, size_t size) { + ssize_t amount; + + if (NULL == buffer) { + if (0 == size) { + return 0; + } + // asset->seek returns new total offset + // we want to return amount that was skipped + + off64_t oldOffset = fAsset->seek(0, SEEK_CUR); + if (-1 == oldOffset) { + SkDebugf("---- fAsset->seek(oldOffset) failed\n"); + return 0; + } + off64_t newOffset = fAsset->seek(size, SEEK_CUR); + if (-1 == newOffset) { + SkDebugf("---- fAsset->seek(%d) failed\n", size); + return 0; + } + amount = newOffset - oldOffset; + } else { + amount = fAsset->read(buffer, size); + } + + if (amount < 0) { + amount = 0; + } + return amount; +} + +SkMemoryStream* android::CopyAssetToStream(Asset* asset) { + if (NULL == asset) { + return NULL; + } + + const off64_t seekReturnVal = asset->seek(0, SEEK_SET); + if ((off64_t)-1 == seekReturnVal) { + SkDebugf("---- copyAsset: asset rewind failed\n"); + return NULL; + } + + const off64_t size = asset->getLength(); + if (size <= 0) { + SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size); + return NULL; + } + + sk_sp<SkData> data(SkData::MakeUninitialized(size)); + const off64_t len = asset->read(data->writable_data(), size); + if (len != size) { + SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len); + return NULL; + } + + return new SkMemoryStream(std::move(data)); +} + +jobject android::nullObjectReturn(const char msg[]) { + if (msg) { + SkDebugf("--- %s\n", msg); + } + return NULL; +} + +bool android::isSeekable(int descriptor) { + return ::lseek64(descriptor, 0, SEEK_CUR) != -1; +} + +JNIEnv* android::get_env_or_die(JavaVM* jvm) { + JNIEnv* env; + if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", jvm); + } + return env; +} + +JNIEnv* android::requireEnv(JavaVM* jvm) { + JNIEnv* env; + if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + if (jvm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!"); + } + } + return env; +} diff --git a/libs/hwui/jni/Utils.h b/libs/hwui/jni/Utils.h new file mode 100644 index 000000000000..f628cc3c85ed --- /dev/null +++ b/libs/hwui/jni/Utils.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2006 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. + */ + +#ifndef _ANDROID_GRAPHICS_UTILS_H_ +#define _ANDROID_GRAPHICS_UTILS_H_ + +#include "SkStream.h" + +#include <jni.h> +#include <androidfw/Asset.h> + +namespace android { + +class AssetStreamAdaptor : public SkStreamRewindable { +public: + explicit AssetStreamAdaptor(Asset*); + + virtual bool rewind(); + virtual size_t read(void* buffer, size_t size); + virtual bool hasLength() const { return true; } + virtual size_t getLength() const; + virtual bool hasPosition() const; + virtual size_t getPosition() const; + virtual bool seek(size_t position); + virtual bool move(long offset); + virtual bool isAtEnd() const; + +protected: + SkStreamRewindable* onDuplicate() const override; + +private: + Asset* fAsset; +}; + +/** + * Make a deep copy of the asset, and return it as a stream, or NULL if there + * was an error. + * FIXME: If we could "ref/reopen" the asset, we may not need to copy it here. + */ + +SkMemoryStream* CopyAssetToStream(Asset*); + +/** Restore the file descriptor's offset in our destructor + */ +class AutoFDSeek { +public: + explicit AutoFDSeek(int fd) : fFD(fd) { + fCurr = ::lseek(fd, 0, SEEK_CUR); + } + ~AutoFDSeek() { + if (fCurr >= 0) { + ::lseek(fFD, fCurr, SEEK_SET); + } + } +private: + int fFD; + off64_t fCurr; +}; + +jobject nullObjectReturn(const char msg[]); + +/** Check if the file descriptor is seekable. + */ +bool isSeekable(int descriptor); + +JNIEnv* get_env_or_die(JavaVM* jvm); + +/** + * Helper method for accessing the JNI interface pointer. + * + * Image decoding (which this supports) is started on a thread that is already + * attached to the Java VM. But an AnimatedImageDrawable continues decoding on + * the AnimatedImageThread, which is not attached. This will attach if + * necessary. + */ +JNIEnv* requireEnv(JavaVM* jvm); + +}; // namespace android + +#endif // _ANDROID_GRAPHICS_UTILS_H_ diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp new file mode 100644 index 000000000000..689cf0bea741 --- /dev/null +++ b/libs/hwui/jni/YuvToJpegEncoder.cpp @@ -0,0 +1,268 @@ +#include "CreateJavaOutputStreamAdaptor.h" +#include "SkJPEGWriteUtility.h" +#include "YuvToJpegEncoder.h" +#include <ui/PixelFormat.h> +#include <hardware/hardware.h> + +#include "graphics_jni_helpers.h" + +YuvToJpegEncoder* YuvToJpegEncoder::create(int format, int* strides) { + // Only ImageFormat.NV21 and ImageFormat.YUY2 are supported + // for now. + if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) { + return new Yuv420SpToJpegEncoder(strides); + } else if (format == HAL_PIXEL_FORMAT_YCbCr_422_I) { + return new Yuv422IToJpegEncoder(strides); + } else { + return NULL; + } +} + +YuvToJpegEncoder::YuvToJpegEncoder(int* strides) : fStrides(strides) { +} + +struct ErrorMgr { + struct jpeg_error_mgr pub; + jmp_buf jmp; +}; + +void error_exit(j_common_ptr cinfo) { + ErrorMgr* err = (ErrorMgr*) cinfo->err; + (*cinfo->err->output_message) (cinfo); + longjmp(err->jmp, 1); +} + +bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width, + int height, int* offsets, int jpegQuality) { + jpeg_compress_struct cinfo; + ErrorMgr err; + skjpeg_destination_mgr sk_wstream(stream); + + cinfo.err = jpeg_std_error(&err.pub); + err.pub.error_exit = error_exit; + + if (setjmp(err.jmp)) { + jpeg_destroy_compress(&cinfo); + return false; + } + jpeg_create_compress(&cinfo); + + cinfo.dest = &sk_wstream; + + setJpegCompressStruct(&cinfo, width, height, jpegQuality); + + jpeg_start_compress(&cinfo, TRUE); + + compress(&cinfo, (uint8_t*) inYuv, offsets); + + jpeg_finish_compress(&cinfo); + + jpeg_destroy_compress(&cinfo); + + return true; +} + +void YuvToJpegEncoder::setJpegCompressStruct(jpeg_compress_struct* cinfo, + int width, int height, int quality) { + cinfo->image_width = width; + cinfo->image_height = height; + cinfo->input_components = 3; + cinfo->in_color_space = JCS_YCbCr; + jpeg_set_defaults(cinfo); + + jpeg_set_quality(cinfo, quality, TRUE); + jpeg_set_colorspace(cinfo, JCS_YCbCr); + cinfo->raw_data_in = TRUE; + cinfo->dct_method = JDCT_IFAST; + configSamplingFactors(cinfo); +} + +/////////////////////////////////////////////////////////////////// +Yuv420SpToJpegEncoder::Yuv420SpToJpegEncoder(int* strides) : + YuvToJpegEncoder(strides) { + fNumPlanes = 2; +} + +void Yuv420SpToJpegEncoder::compress(jpeg_compress_struct* cinfo, + uint8_t* yuv, int* offsets) { + SkDebugf("onFlyCompress"); + JSAMPROW y[16]; + JSAMPROW cb[8]; + JSAMPROW cr[8]; + JSAMPARRAY planes[3]; + planes[0] = y; + planes[1] = cb; + planes[2] = cr; + + int width = cinfo->image_width; + int height = cinfo->image_height; + uint8_t* yPlanar = yuv + offsets[0]; + uint8_t* vuPlanar = yuv + offsets[1]; //width * height; + uint8_t* uRows = new uint8_t [8 * (width >> 1)]; + uint8_t* vRows = new uint8_t [8 * (width >> 1)]; + + + // process 16 lines of Y and 8 lines of U/V each time. + while (cinfo->next_scanline < cinfo->image_height) { + //deitnerleave u and v + deinterleave(vuPlanar, uRows, vRows, cinfo->next_scanline, width, height); + + // Jpeg library ignores the rows whose indices are greater than height. + for (int i = 0; i < 16; i++) { + // y row + y[i] = yPlanar + (cinfo->next_scanline + i) * fStrides[0]; + + // construct u row and v row + if ((i & 1) == 0) { + // height and width are both halved because of downsampling + int offset = (i >> 1) * (width >> 1); + cb[i/2] = uRows + offset; + cr[i/2] = vRows + offset; + } + } + jpeg_write_raw_data(cinfo, planes, 16); + } + delete [] uRows; + delete [] vRows; + +} + +void Yuv420SpToJpegEncoder::deinterleave(uint8_t* vuPlanar, uint8_t* uRows, + uint8_t* vRows, int rowIndex, int width, int height) { + int numRows = (height - rowIndex) / 2; + if (numRows > 8) numRows = 8; + for (int row = 0; row < numRows; ++row) { + int offset = ((rowIndex >> 1) + row) * fStrides[1]; + uint8_t* vu = vuPlanar + offset; + for (int i = 0; i < (width >> 1); ++i) { + int index = row * (width >> 1) + i; + uRows[index] = vu[1]; + vRows[index] = vu[0]; + vu += 2; + } + } +} + +void Yuv420SpToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) { + // cb and cr are horizontally downsampled and vertically downsampled as well. + cinfo->comp_info[0].h_samp_factor = 2; + cinfo->comp_info[0].v_samp_factor = 2; + cinfo->comp_info[1].h_samp_factor = 1; + cinfo->comp_info[1].v_samp_factor = 1; + cinfo->comp_info[2].h_samp_factor = 1; + cinfo->comp_info[2].v_samp_factor = 1; +} + +/////////////////////////////////////////////////////////////////////////////// +Yuv422IToJpegEncoder::Yuv422IToJpegEncoder(int* strides) : + YuvToJpegEncoder(strides) { + fNumPlanes = 1; +} + +void Yuv422IToJpegEncoder::compress(jpeg_compress_struct* cinfo, + uint8_t* yuv, int* offsets) { + SkDebugf("onFlyCompress_422"); + JSAMPROW y[16]; + JSAMPROW cb[16]; + JSAMPROW cr[16]; + JSAMPARRAY planes[3]; + planes[0] = y; + planes[1] = cb; + planes[2] = cr; + + int width = cinfo->image_width; + int height = cinfo->image_height; + uint8_t* yRows = new uint8_t [16 * width]; + uint8_t* uRows = new uint8_t [16 * (width >> 1)]; + uint8_t* vRows = new uint8_t [16 * (width >> 1)]; + + uint8_t* yuvOffset = yuv + offsets[0]; + + // process 16 lines of Y and 16 lines of U/V each time. + while (cinfo->next_scanline < cinfo->image_height) { + deinterleave(yuvOffset, yRows, uRows, vRows, cinfo->next_scanline, width, height); + + // Jpeg library ignores the rows whose indices are greater than height. + for (int i = 0; i < 16; i++) { + // y row + y[i] = yRows + i * width; + + // construct u row and v row + // width is halved because of downsampling + int offset = i * (width >> 1); + cb[i] = uRows + offset; + cr[i] = vRows + offset; + } + + jpeg_write_raw_data(cinfo, planes, 16); + } + delete [] yRows; + delete [] uRows; + delete [] vRows; +} + + +void Yuv422IToJpegEncoder::deinterleave(uint8_t* yuv, uint8_t* yRows, uint8_t* uRows, + uint8_t* vRows, int rowIndex, int width, int height) { + int numRows = height - rowIndex; + if (numRows > 16) numRows = 16; + for (int row = 0; row < numRows; ++row) { + uint8_t* yuvSeg = yuv + (rowIndex + row) * fStrides[0]; + for (int i = 0; i < (width >> 1); ++i) { + int indexY = row * width + (i << 1); + int indexU = row * (width >> 1) + i; + yRows[indexY] = yuvSeg[0]; + yRows[indexY + 1] = yuvSeg[2]; + uRows[indexU] = yuvSeg[1]; + vRows[indexU] = yuvSeg[3]; + yuvSeg += 4; + } + } +} + +void Yuv422IToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) { + // cb and cr are horizontally downsampled and vertically downsampled as well. + cinfo->comp_info[0].h_samp_factor = 2; + cinfo->comp_info[0].v_samp_factor = 2; + cinfo->comp_info[1].h_samp_factor = 1; + cinfo->comp_info[1].v_samp_factor = 2; + cinfo->comp_info[2].h_samp_factor = 1; + cinfo->comp_info[2].v_samp_factor = 2; +} +/////////////////////////////////////////////////////////////////////////////// + +static jboolean YuvImage_compressToJpeg(JNIEnv* env, jobject, jbyteArray inYuv, + jint format, jint width, jint height, jintArray offsets, + jintArray strides, jint jpegQuality, jobject jstream, + jbyteArray jstorage) { + jbyte* yuv = env->GetByteArrayElements(inYuv, NULL); + SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); + + jint* imgOffsets = env->GetIntArrayElements(offsets, NULL); + jint* imgStrides = env->GetIntArrayElements(strides, NULL); + YuvToJpegEncoder* encoder = YuvToJpegEncoder::create(format, imgStrides); + jboolean result = JNI_FALSE; + if (encoder != NULL) { + encoder->encode(strm, yuv, width, height, imgOffsets, jpegQuality); + delete encoder; + result = JNI_TRUE; + } + + env->ReleaseByteArrayElements(inYuv, yuv, 0); + env->ReleaseIntArrayElements(offsets, imgOffsets, 0); + env->ReleaseIntArrayElements(strides, imgStrides, 0); + delete strm; + return result; +} +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gYuvImageMethods[] = { + { "nativeCompressToJpeg", "([BIII[I[IILjava/io/OutputStream;[B)Z", + (void*)YuvImage_compressToJpeg } +}; + +int register_android_graphics_YuvImage(JNIEnv* env) +{ + return android::RegisterMethodsOrDie(env, "android/graphics/YuvImage", gYuvImageMethods, + NELEM(gYuvImageMethods)); +} diff --git a/libs/hwui/jni/YuvToJpegEncoder.h b/libs/hwui/jni/YuvToJpegEncoder.h new file mode 100644 index 000000000000..7e7b935df276 --- /dev/null +++ b/libs/hwui/jni/YuvToJpegEncoder.h @@ -0,0 +1,74 @@ +#ifndef _ANDROID_GRAPHICS_YUV_TO_JPEG_ENCODER_H_ +#define _ANDROID_GRAPHICS_YUV_TO_JPEG_ENCODER_H_ + +#include "SkTypes.h" +#include "SkStream.h" +extern "C" { + #include "jpeglib.h" + #include "jerror.h" +} + +class YuvToJpegEncoder { +public: + /** Create an encoder based on the YUV format. + * + * @param pixelFormat The yuv pixel format as defined in ui/PixelFormat.h. + * @param strides The number of row bytes in each image plane. + * @return an encoder based on the pixelFormat. + */ + static YuvToJpegEncoder* create(int pixelFormat, int* strides); + + explicit YuvToJpegEncoder(int* strides); + + /** Encode YUV data to jpeg, which is output to a stream. + * + * @param stream The jpeg output stream. + * @param inYuv The input yuv data. + * @param width Width of the the Yuv data in terms of pixels. + * @param height Height of the Yuv data in terms of pixels. + * @param offsets The offsets in each image plane with respect to inYuv. + * @param jpegQuality Picture quality in [0, 100]. + * @return true if successfully compressed the stream. + */ + bool encode(SkWStream* stream, void* inYuv, int width, + int height, int* offsets, int jpegQuality); + + virtual ~YuvToJpegEncoder() {} + +protected: + int fNumPlanes; + int* fStrides; + void setJpegCompressStruct(jpeg_compress_struct* cinfo, int width, + int height, int quality); + virtual void configSamplingFactors(jpeg_compress_struct* cinfo) = 0; + virtual void compress(jpeg_compress_struct* cinfo, + uint8_t* yuv, int* offsets) = 0; +}; + +class Yuv420SpToJpegEncoder : public YuvToJpegEncoder { +public: + explicit Yuv420SpToJpegEncoder(int* strides); + virtual ~Yuv420SpToJpegEncoder() {} + +private: + void configSamplingFactors(jpeg_compress_struct* cinfo); + void deinterleaveYuv(uint8_t* yuv, int width, int height, + uint8_t*& yPlanar, uint8_t*& uPlanar, uint8_t*& vPlanar); + void deinterleave(uint8_t* vuPlanar, uint8_t* uRows, uint8_t* vRows, + int rowIndex, int width, int height); + void compress(jpeg_compress_struct* cinfo, uint8_t* yuv, int* offsets); +}; + +class Yuv422IToJpegEncoder : public YuvToJpegEncoder { +public: + explicit Yuv422IToJpegEncoder(int* strides); + virtual ~Yuv422IToJpegEncoder() {} + +private: + void configSamplingFactors(jpeg_compress_struct* cinfo); + void compress(jpeg_compress_struct* cinfo, uint8_t* yuv, int* offsets); + void deinterleave(uint8_t* yuv, uint8_t* yRows, uint8_t* uRows, + uint8_t* vRows, int rowIndex, int width, int height); +}; + +#endif // _ANDROID_GRAPHICS_YUV_TO_JPEG_ENCODER_H_ diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp new file mode 100644 index 000000000000..b6c6cd0b5c1c --- /dev/null +++ b/libs/hwui/jni/android_graphics_Canvas.cpp @@ -0,0 +1,739 @@ +/* + * Copyright (C) 2014 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. + */ + +#include "GraphicsJNI.h" + +#ifdef __ANDROID_ +#include <android/api-level.h> +#else +#define __ANDROID_API_P__ 28 +#endif +#include <androidfw/ResourceTypes.h> +#include <hwui/Canvas.h> +#include <hwui/Paint.h> +#include <hwui/PaintFilter.h> +#include <hwui/Typeface.h> +#include <minikin/Layout.h> +#include <nativehelper/ScopedPrimitiveArray.h> +#include <nativehelper/ScopedStringChars.h> + +#include "Bitmap.h" +#include "SkGraphics.h" +#include "SkRegion.h" +#include "SkVertices.h" + +namespace minikin { +class MeasuredText; +} // namespace minikin + +namespace android { + +namespace CanvasJNI { + +static Canvas* get_canvas(jlong canvasHandle) { + return reinterpret_cast<Canvas*>(canvasHandle); +} + +static void delete_canvas(Canvas* canvas) { + delete canvas; +} + +static jlong getNativeFinalizer(JNIEnv* env, jobject clazz) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&delete_canvas)); +} + +// Native wrapper constructor used by Canvas(Bitmap) +static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) { + SkBitmap bitmap; + if (bitmapHandle != 0) { + bitmap::toBitmap(bitmapHandle).getSkBitmap(&bitmap); + } + return reinterpret_cast<jlong>(Canvas::create_canvas(bitmap)); +} + +// Set the given bitmap as the new draw target (wrapped in a new SkCanvas), +// optionally copying canvas matrix & clip state. +static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle) { + SkBitmap bitmap; + if (bitmapHandle != 0) { + bitmap::toBitmap(bitmapHandle).getSkBitmap(&bitmap); + } + get_canvas(canvasHandle)->setBitmap(bitmap); +} + +static jboolean isOpaque(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) { + return get_canvas(canvasHandle)->isOpaque() ? JNI_TRUE : JNI_FALSE; +} + +static jint getWidth(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) { + return static_cast<jint>(get_canvas(canvasHandle)->width()); +} + +static jint getHeight(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) { + return static_cast<jint>(get_canvas(canvasHandle)->height()); +} + +static jint save(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint flagsHandle) { + SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle); + return static_cast<jint>(get_canvas(canvasHandle)->save(flags)); +} + +static jint saveLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t, + jfloat r, jfloat b, jlong paintHandle, jint flagsHandle) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle); + return static_cast<jint>(get_canvas(canvasHandle)->saveLayer(l, t, r, b, paint, flags)); +} + +static jint saveLayerAlpha(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t, + jfloat r, jfloat b, jint alpha, jint flagsHandle) { + SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle); + return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags)); +} + +static jint saveUnclippedLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint l, jint t, jint r, jint b) { + return reinterpret_cast<jint>(get_canvas(canvasHandle)->saveUnclippedLayer(l, t, r, b)); +} + +static void restoreUnclippedLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint saveCount, jlong paintHandle) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + get_canvas(canvasHandle)->restoreUnclippedLayer(saveCount, *paint); +} + +static jboolean restore(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) { + Canvas* canvas = get_canvas(canvasHandle); + if (canvas->getSaveCount() <= 1) { + return false; // cannot restore anymore + } + canvas->restore(); + return true; // success +} + +static void restoreToCount(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint saveCount) { + Canvas* canvas = get_canvas(canvasHandle); + canvas->restoreToCount(saveCount); +} + +static jint getSaveCount(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) { + return static_cast<jint>(get_canvas(canvasHandle)->getSaveCount()); +} + +static void getMatrix(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong matrixHandle) { + SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); + get_canvas(canvasHandle)->getMatrix(matrix); +} + +static void setMatrix(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong matrixHandle) { + const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); + get_canvas(canvasHandle)->setMatrix(matrix ? *matrix : SkMatrix::I()); +} + +static void concat(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong matrixHandle) { + const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); + get_canvas(canvasHandle)->concat(*matrix); +} + +static void rotate(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat degrees) { + get_canvas(canvasHandle)->rotate(degrees); +} + +static void scale(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat sx, jfloat sy) { + get_canvas(canvasHandle)->scale(sx, sy); +} + +static void skew(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat sx, jfloat sy) { + get_canvas(canvasHandle)->skew(sx, sy); +} + +static void translate(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat dx, jfloat dy) { + get_canvas(canvasHandle)->translate(dx, dy); +} + +static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds) { + SkRect r; + SkIRect ir; + bool result = get_canvas(canvasHandle)->getClipBounds(&r); + + if (!result) { + r.setEmpty(); + } + r.round(&ir); + + (void)GraphicsJNI::irect_to_jrect(ir, env, bounds); + return result ? JNI_TRUE : JNI_FALSE; +} + +static jboolean quickRejectRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, + jfloat left, jfloat top, jfloat right, jfloat bottom) { + bool result = get_canvas(canvasHandle)->quickRejectRect(left, top, right, bottom); + return result ? JNI_TRUE : JNI_FALSE; +} + +static jboolean quickRejectPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong pathHandle) { + SkPath* path = reinterpret_cast<SkPath*>(pathHandle); + bool result = get_canvas(canvasHandle)->quickRejectPath(*path); + return result ? JNI_TRUE : JNI_FALSE; +} + +// SkRegion::Op and SkClipOp are numerically identical, so we can freely cast +// from one to the other (though SkClipOp is destined to become a strict subset) +static_assert(SkRegion::kDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kDifference), ""); +static_assert(SkRegion::kIntersect_Op == static_cast<SkRegion::Op>(SkClipOp::kIntersect), ""); +static_assert(SkRegion::kUnion_Op == static_cast<SkRegion::Op>(SkClipOp::kUnion_deprecated), ""); +static_assert(SkRegion::kXOR_Op == static_cast<SkRegion::Op>(SkClipOp::kXOR_deprecated), ""); +static_assert(SkRegion::kReverseDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kReverseDifference_deprecated), ""); +static_assert(SkRegion::kReplace_Op == static_cast<SkRegion::Op>(SkClipOp::kReplace_deprecated), ""); + +static SkClipOp opHandleToClipOp(jint opHandle) { + // The opHandle is defined in Canvas.java to be Region::Op + SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle); + + // In the future, when we no longer support the wide range of ops (e.g. Union, Xor) + // this function can perform a range check and throw an unsupported-exception. + // e.g. if (rgnOp != kIntersect && rgnOp != kDifference) throw... + + // Skia now takes a different type, SkClipOp, as the parameter to clipping calls + // This type is binary compatible with SkRegion::Op, so a static_cast<> is safe. + return static_cast<SkClipOp>(rgnOp); +} + +static jboolean clipRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t, + jfloat r, jfloat b, jint opHandle) { + bool nonEmptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b, + opHandleToClipOp(opHandle)); + return nonEmptyClip ? JNI_TRUE : JNI_FALSE; +} + +static jboolean clipPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong pathHandle, + jint opHandle) { + SkPath* path = reinterpret_cast<SkPath*>(pathHandle); + bool nonEmptyClip = get_canvas(canvasHandle)->clipPath(path, opHandleToClipOp(opHandle)); + return nonEmptyClip ? JNI_TRUE : JNI_FALSE; +} + +static void drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) { + SkBlendMode mode = static_cast<SkBlendMode>(modeHandle); + get_canvas(canvasHandle)->drawColor(color, mode); +} + +static void drawColorLong(JNIEnv* env, jobject, jlong canvasHandle, jlong colorSpaceHandle, + jlong colorLong, jint modeHandle) { + SkColor4f color = GraphicsJNI::convertColorLong(colorLong); + sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); + SkPaint p; + p.setColor4f(color, cs.get()); + + SkBlendMode mode = static_cast<SkBlendMode>(modeHandle); + p.setBlendMode(mode); + get_canvas(canvasHandle)->drawPaint(p); +} + +static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle, jlong paintHandle) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + get_canvas(canvasHandle)->drawPaint(*paint); +} + +static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y, + jlong paintHandle) { + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + get_canvas(canvasHandle)->drawPoint(x, y, *paint); +} + +static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray, + jint offset, jint count, jlong paintHandle) { + NPE_CHECK_RETURN_VOID(env, jptsArray); + AutoJavaFloatArray autoPts(env, jptsArray); + float* floats = autoPts.ptr(); + const int length = autoPts.length(); + + if ((offset | count) < 0 || offset + count > length) { + doThrowAIOOBE(env); + return; + } + + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + get_canvas(canvasHandle)->drawPoints(floats + offset, count, *paint); +} + +static void drawLine(JNIEnv* env, jobject, jlong canvasHandle, jfloat startX, jfloat startY, + jfloat stopX, jfloat stopY, jlong paintHandle) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + get_canvas(canvasHandle)->drawLine(startX, startY, stopX, stopY, *paint); +} + +static void drawLines(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray, + jint offset, jint count, jlong paintHandle) { + NPE_CHECK_RETURN_VOID(env, jptsArray); + AutoJavaFloatArray autoPts(env, jptsArray); + float* floats = autoPts.ptr(); + const int length = autoPts.length(); + + if ((offset | count) < 0 || offset + count > length) { + doThrowAIOOBE(env); + return; + } + + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + get_canvas(canvasHandle)->drawLines(floats + offset, count, *paint); +} + +static void drawRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top, + jfloat right, jfloat bottom, jlong paintHandle) { + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + get_canvas(canvasHandle)->drawRect(left, top, right, bottom, *paint); +} + +static void drawDoubleRoundRectXY(JNIEnv* env, jobject, jlong canvasHandle, jfloat outerLeft, + jfloat outerTop, jfloat outerRight, jfloat outerBottom, jfloat outerRx, + jfloat outerRy, jfloat innerLeft, jfloat innerTop, jfloat innerRight, + jfloat innerBottom, jfloat innerRx, jfloat innerRy, jlong paintHandle) { + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + get_canvas(canvasHandle)->drawDoubleRoundRectXY( + outerLeft, outerTop, outerRight, outerBottom, outerRx, outerRy, + innerLeft, innerTop, innerRight, innerBottom, innerRx, innerRy, *paint); +} + +static void drawDoubleRoundRectRadii(JNIEnv* env, jobject, jlong canvasHandle, jfloat outerLeft, + jfloat outerTop, jfloat outerRight, jfloat outerBottom, jfloatArray jouterRadii, + jfloat innerLeft, jfloat innerTop, jfloat innerRight, + jfloat innerBottom, jfloatArray jinnerRadii, jlong paintHandle) { + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + + float outerRadii[8]; + float innerRadii[8]; + env->GetFloatArrayRegion(jouterRadii, 0, 8, outerRadii); + env->GetFloatArrayRegion(jinnerRadii, 0, 8, innerRadii); + get_canvas(canvasHandle)->drawDoubleRoundRectRadii( + outerLeft, outerTop, outerRight, outerBottom, outerRadii, + innerLeft, innerTop, innerRight, innerBottom, innerRadii, *paint); + +} + +static void drawRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong regionHandle, + jlong paintHandle) { + const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle); + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + get_canvas(canvasHandle)->drawRegion(*region, *paint); +} + +static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top, + jfloat right, jfloat bottom, jfloat rx, jfloat ry, jlong paintHandle) { + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + get_canvas(canvasHandle)->drawRoundRect(left, top, right, bottom, rx, ry, *paint); +} + +static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx, jfloat cy, + jfloat radius, jlong paintHandle) { + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + get_canvas(canvasHandle)->drawCircle(cx, cy, radius, *paint); +} + +static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top, + jfloat right, jfloat bottom, jlong paintHandle) { + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + get_canvas(canvasHandle)->drawOval(left, top, right, bottom, *paint); +} + +static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top, + jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle, + jboolean useCenter, jlong paintHandle) { + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + get_canvas(canvasHandle)->drawArc(left, top, right, bottom, startAngle, sweepAngle, + useCenter, *paint); +} + +static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle, + jlong paintHandle) { + const SkPath* path = reinterpret_cast<SkPath*>(pathHandle); + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + get_canvas(canvasHandle)->drawPath(*path, *paint); +} + +static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle, + jint modeHandle, jint floatCount, + jfloatArray jverts, jint vertIndex, + jfloatArray jtexs, jint texIndex, + jintArray jcolors, jint colorIndex, + jshortArray jindices, jint indexIndex, + jint indexCount, jlong paintHandle) { + + const int vertexCount = floatCount >> 1; // 2 floats per SkPoint + + AutoJavaFloatArray vertA(env, jverts, vertIndex + floatCount); + AutoJavaFloatArray texA(env, jtexs, texIndex + floatCount); + AutoJavaIntArray colorA(env, jcolors, colorIndex + vertexCount); + AutoJavaShortArray indexA(env, jindices, indexIndex + indexCount); + + const float* verts = vertA.ptr() + vertIndex; + const float* texs = texA.ptr() + vertIndex; + const int* colors = NULL; + const uint16_t* indices = NULL; + + if (jcolors != NULL) { + colors = colorA.ptr() + colorIndex; + } + if (jindices != NULL) { + indices = (const uint16_t*)(indexA.ptr() + indexIndex); + } + + SkVertices::VertexMode mode = static_cast<SkVertices::VertexMode>(modeHandle); + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + get_canvas(canvasHandle)->drawVertices(SkVertices::MakeCopy(mode, vertexCount, + reinterpret_cast<const SkPoint*>(verts), + reinterpret_cast<const SkPoint*>(texs), + reinterpret_cast<const SkColor*>(colors), + indexCount, indices).get(), + SkBlendMode::kModulate, *paint); +} + +static void drawNinePatch(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle, + jlong chunkHandle, jfloat left, jfloat top, jfloat right, jfloat bottom, + jlong paintHandle, jint dstDensity, jint srcDensity) { + + Canvas* canvas = get_canvas(canvasHandle); + Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle); + const android::Res_png_9patch* chunk = reinterpret_cast<android::Res_png_9patch*>(chunkHandle); + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + + if (CC_LIKELY(dstDensity == srcDensity || dstDensity == 0 || srcDensity == 0)) { + canvas->drawNinePatch(bitmap, *chunk, left, top, right, bottom, paint); + } else { + canvas->save(SaveFlags::MatrixClip); + + SkScalar scale = dstDensity / (float)srcDensity; + canvas->translate(left, top); + canvas->scale(scale, scale); + + Paint filteredPaint; + if (paint) { + filteredPaint = *paint; + } + filteredPaint.setFilterQuality(kLow_SkFilterQuality); + + canvas->drawNinePatch(bitmap, *chunk, 0, 0, (right-left)/scale, (bottom-top)/scale, + &filteredPaint); + + canvas->restore(); + } +} + +static void drawBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle, + jfloat left, jfloat top, jlong paintHandle, jint canvasDensity, + jint screenDensity, jint bitmapDensity) { + Canvas* canvas = get_canvas(canvasHandle); + Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle); + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + + if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) { + if (screenDensity != 0 && screenDensity != bitmapDensity) { + Paint filteredPaint; + if (paint) { + filteredPaint = *paint; + } + filteredPaint.setFilterQuality(kLow_SkFilterQuality); + canvas->drawBitmap(bitmap, left, top, &filteredPaint); + } else { + canvas->drawBitmap(bitmap, left, top, paint); + } + } else { + canvas->save(SaveFlags::MatrixClip); + SkScalar scale = canvasDensity / (float)bitmapDensity; + canvas->translate(left, top); + canvas->scale(scale, scale); + + Paint filteredPaint; + if (paint) { + filteredPaint = *paint; + } + filteredPaint.setFilterQuality(kLow_SkFilterQuality); + + canvas->drawBitmap(bitmap, 0, 0, &filteredPaint); + canvas->restore(); + } +} + +static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle, + jlong matrixHandle, jlong paintHandle) { + const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle); + get_canvas(canvasHandle)->drawBitmap(bitmap, *matrix, paint); +} + +static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle, + float srcLeft, float srcTop, float srcRight, float srcBottom, + float dstLeft, float dstTop, float dstRight, float dstBottom, + jlong paintHandle, jint screenDensity, jint bitmapDensity) { + Canvas* canvas = get_canvas(canvasHandle); + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + + Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle); + if (screenDensity != 0 && screenDensity != bitmapDensity) { + Paint filteredPaint; + if (paint) { + filteredPaint = *paint; + } + filteredPaint.setFilterQuality(kLow_SkFilterQuality); + canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom, + dstLeft, dstTop, dstRight, dstBottom, &filteredPaint); + } else { + canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom, + dstLeft, dstTop, dstRight, dstBottom, paint); + } +} + +static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle, + jintArray jcolors, jint offset, jint stride, + jfloat x, jfloat y, jint width, jint height, + jboolean hasAlpha, jlong paintHandle) { + // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will + // correct the alphaType to kOpaque_SkAlphaType. + SkImageInfo info = SkImageInfo::Make(width, height, + hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType, + kPremul_SkAlphaType); + SkBitmap bitmap; + bitmap.setInfo(info); + sk_sp<Bitmap> androidBitmap = Bitmap::allocateHeapBitmap(&bitmap); + if (!androidBitmap) { + return; + } + + if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride, 0, 0, width, height, &bitmap)) { + return; + } + + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + get_canvas(canvasHandle)->drawBitmap(*androidBitmap, x, y, paint); +} + +static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle, + jint meshWidth, jint meshHeight, jfloatArray jverts, + jint vertIndex, jintArray jcolors, jint colorIndex, jlong paintHandle) { + if (Canvas::GetApiLevel() < __ANDROID_API_P__) { + // Before P we forgot to respect these. Now that we do respect them, explicitly + // zero them for backward compatibility. + vertIndex = 0; + colorIndex = 0; + } + + const int ptCount = (meshWidth + 1) * (meshHeight + 1); + AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1)); + AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount); + + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle); + get_canvas(canvasHandle)->drawBitmapMesh(bitmap, meshWidth, meshHeight, + vertA.ptr() + vertIndex*2, + colorA.ptr() + colorIndex, paint); +} + +static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray charArray, + jint index, jint count, jfloat x, jfloat y, jint bidiFlags, + jlong paintHandle) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + ScopedCharArrayRO text(env, charArray); + // drawTextString and drawTextChars doesn't use context info + get_canvas(canvasHandle)->drawText( + text.get() + index, count, // text buffer + 0, count, // draw range + 0, count, // context range + x, y, // draw position + static_cast<minikin::Bidi>(bidiFlags), *paint, typeface, nullptr /* measured text */); +} + +static void drawTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring strObj, + jint start, jint end, jfloat x, jfloat y, jint bidiFlags, + jlong paintHandle) { + ScopedStringChars text(env, strObj); + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + const int count = end - start; + // drawTextString and drawTextChars doesn't use context info + get_canvas(canvasHandle)->drawText( + text.get() + start, count, // text buffer + 0, count, // draw range + 0, count, // context range + x, y, // draw position + static_cast<minikin::Bidi>(bidiFlags), *paint, typeface, nullptr /* measured text */); +} + +static void drawTextRunChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray charArray, + jint index, jint count, jint contextIndex, jint contextCount, + jfloat x, jfloat y, jboolean isRtl, jlong paintHandle, + jlong mtHandle) { + minikin::MeasuredText* mt = reinterpret_cast<minikin::MeasuredText*>(mtHandle); + const minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR; + + ScopedCharArrayRO text(env, charArray); + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + get_canvas(canvasHandle)->drawText( + text.get(), text.size(), // text buffer + index, count, // draw range + contextIndex, contextCount, // context range, + x, y, // draw position + bidiFlags, *paint, typeface, mt); +} + +static void drawTextRunString(JNIEnv* env, jobject obj, jlong canvasHandle, jstring strObj, + jint start, jint end, jint contextStart, jint contextEnd, + jfloat x, jfloat y, jboolean isRtl, jlong paintHandle) { + const minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR; + + ScopedStringChars text(env, strObj); + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + get_canvas(canvasHandle)->drawText( + text.get(), text.size(), // text buffer + start, end - start, // draw range + contextStart, contextEnd - contextStart, // context range + x, y, // draw position + bidiFlags, *paint, typeface, nullptr /* measured text */); +} + +static void drawTextOnPathChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, + jint index, jint count, jlong pathHandle, jfloat hOffset, + jfloat vOffset, jint bidiFlags, jlong paintHandle) { + SkPath* path = reinterpret_cast<SkPath*>(pathHandle); + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + + jchar* jchars = env->GetCharArrayElements(text, NULL); + + get_canvas(canvasHandle)->drawTextOnPath(jchars + index, count, + static_cast<minikin::Bidi>(bidiFlags), *path, hOffset, vOffset, *paint, typeface); + + env->ReleaseCharArrayElements(text, jchars, 0); +} + +static void drawTextOnPathString(JNIEnv* env, jobject, jlong canvasHandle, jstring text, + jlong pathHandle, jfloat hOffset, jfloat vOffset, + jint bidiFlags, jlong paintHandle) { + SkPath* path = reinterpret_cast<SkPath*>(pathHandle); + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + const Typeface* typeface = paint->getAndroidTypeface(); + + const jchar* jchars = env->GetStringChars(text, NULL); + int count = env->GetStringLength(text); + + get_canvas(canvasHandle)->drawTextOnPath(jchars, count, static_cast<minikin::Bidi>(bidiFlags), + *path, hOffset, vOffset, *paint, typeface); + + env->ReleaseStringChars(text, jchars); +} + +static void setPaintFilter(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong filterHandle) { + PaintFilter* paintFilter = reinterpret_cast<PaintFilter*>(filterHandle); + get_canvas(canvasHandle)->setPaintFilter(sk_ref_sp(paintFilter)); +} + +static void freeCaches(JNIEnv* env, jobject) { + SkGraphics::PurgeFontCache(); +} + +static void freeTextLayoutCaches(JNIEnv* env, jobject) { + minikin::Layout::purgeCaches(); +} + +static void setCompatibilityVersion(JNIEnv* env, jobject, jint apiLevel) { + Canvas::setCompatibilityVersion(apiLevel); +} + + +}; // namespace CanvasJNI + +static const JNINativeMethod gMethods[] = { + {"nGetNativeFinalizer", "()J", (void*) CanvasJNI::getNativeFinalizer}, + {"nFreeCaches", "()V", (void*) CanvasJNI::freeCaches}, + {"nFreeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches}, + {"nSetCompatibilityVersion", "(I)V", (void*) CanvasJNI::setCompatibilityVersion}, + + // ------------ @FastNative ---------------- + {"nInitRaster", "(J)J", (void*) CanvasJNI::initRaster}, + {"nSetBitmap", "(JJ)V", (void*) CanvasJNI::setBitmap}, + {"nGetClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds}, + + // ------------ @CriticalNative ---------------- + {"nIsOpaque","(J)Z", (void*) CanvasJNI::isOpaque}, + {"nGetWidth","(J)I", (void*) CanvasJNI::getWidth}, + {"nGetHeight","(J)I", (void*) CanvasJNI::getHeight}, + {"nSave","(JI)I", (void*) CanvasJNI::save}, + {"nSaveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer}, + {"nSaveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha}, + {"nSaveUnclippedLayer","(JIIII)I", (void*) CanvasJNI::saveUnclippedLayer}, + {"nRestoreUnclippedLayer","(JIJ)V", (void*) CanvasJNI::restoreUnclippedLayer}, + {"nGetSaveCount","(J)I", (void*) CanvasJNI::getSaveCount}, + {"nRestore","(J)Z", (void*) CanvasJNI::restore}, + {"nRestoreToCount","(JI)V", (void*) CanvasJNI::restoreToCount}, + {"nGetMatrix", "(JJ)V", (void*)CanvasJNI::getMatrix}, + {"nSetMatrix","(JJ)V", (void*) CanvasJNI::setMatrix}, + {"nConcat","(JJ)V", (void*) CanvasJNI::concat}, + {"nRotate","(JF)V", (void*) CanvasJNI::rotate}, + {"nScale","(JFF)V", (void*) CanvasJNI::scale}, + {"nSkew","(JFF)V", (void*) CanvasJNI::skew}, + {"nTranslate","(JFF)V", (void*) CanvasJNI::translate}, + {"nQuickReject","(JJ)Z", (void*) CanvasJNI::quickRejectPath}, + {"nQuickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect}, + {"nClipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect}, + {"nClipPath","(JJI)Z", (void*) CanvasJNI::clipPath}, + {"nSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setPaintFilter}, +}; + +// If called from Canvas these are regular JNI +// If called from DisplayListCanvas they are @FastNative +static const JNINativeMethod gDrawMethods[] = { + {"nDrawColor","(JII)V", (void*) CanvasJNI::drawColor}, + {"nDrawColor","(JJJI)V", (void*) CanvasJNI::drawColorLong}, + {"nDrawPaint","(JJ)V", (void*) CanvasJNI::drawPaint}, + {"nDrawPoint", "(JFFJ)V", (void*) CanvasJNI::drawPoint}, + {"nDrawPoints", "(J[FIIJ)V", (void*) CanvasJNI::drawPoints}, + {"nDrawLine", "(JFFFFJ)V", (void*) CanvasJNI::drawLine}, + {"nDrawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines}, + {"nDrawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect}, + {"nDrawRegion", "(JJJ)V", (void*) CanvasJNI::drawRegion }, + {"nDrawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect}, + {"nDrawDoubleRoundRect", "(JFFFFFFFFFFFFJ)V", (void*) CanvasJNI::drawDoubleRoundRectXY}, + {"nDrawDoubleRoundRect", "(JFFFF[FFFFF[FJ)V", (void*) CanvasJNI::drawDoubleRoundRectRadii}, + {"nDrawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle}, + {"nDrawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval}, + {"nDrawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc}, + {"nDrawPath","(JJJ)V", (void*) CanvasJNI::drawPath}, + {"nDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices}, + {"nDrawNinePatch", "(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch}, + {"nDrawBitmapMatrix", "(JJJJ)V", (void*)CanvasJNI::drawBitmapMatrix}, + {"nDrawBitmapMesh", "(JJII[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh}, + {"nDrawBitmap","(JJFFJIII)V", (void*) CanvasJNI::drawBitmap}, + {"nDrawBitmap","(JJFFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect}, + {"nDrawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray}, + {"nDrawText","(J[CIIFFIJ)V", (void*) CanvasJNI::drawTextChars}, + {"nDrawText","(JLjava/lang/String;IIFFIJ)V", (void*) CanvasJNI::drawTextString}, + {"nDrawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars}, + {"nDrawTextRun","(JLjava/lang/String;IIIIFFZJ)V", (void*) CanvasJNI::drawTextRunString}, + {"nDrawTextOnPath","(J[CIIJFFIJ)V", (void*) CanvasJNI::drawTextOnPathChars}, + {"nDrawTextOnPath","(JLjava/lang/String;JFFIJ)V", (void*) CanvasJNI::drawTextOnPathString}, +}; + +int register_android_graphics_Canvas(JNIEnv* env) { + int ret = 0; + ret |= RegisterMethodsOrDie(env, "android/graphics/Canvas", gMethods, NELEM(gMethods)); + ret |= RegisterMethodsOrDie(env, "android/graphics/BaseCanvas", gDrawMethods, NELEM(gDrawMethods)); + ret |= RegisterMethodsOrDie(env, "android/graphics/BaseRecordingCanvas", gDrawMethods, NELEM(gDrawMethods)); + return ret; + +} + +}; // namespace android diff --git a/libs/hwui/jni/android_graphics_ColorSpace.cpp b/libs/hwui/jni/android_graphics_ColorSpace.cpp new file mode 100644 index 000000000000..232fd71a12b4 --- /dev/null +++ b/libs/hwui/jni/android_graphics_ColorSpace.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2019 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. + */ + +#include "GraphicsJNI.h" + +#include "SkColor.h" +#include "SkColorSpace.h" +#include "SkHalf.h" + +using namespace android; + +static skcms_Matrix3x3 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50) { + skcms_Matrix3x3 xyzMatrix; + jfloat* array = env->GetFloatArrayElements(xyzD50, NULL); + xyzMatrix.vals[0][0] = array[0]; + xyzMatrix.vals[1][0] = array[1]; + xyzMatrix.vals[2][0] = array[2]; + xyzMatrix.vals[0][1] = array[3]; + xyzMatrix.vals[1][1] = array[4]; + xyzMatrix.vals[2][1] = array[5]; + xyzMatrix.vals[0][2] = array[6]; + xyzMatrix.vals[1][2] = array[7]; + xyzMatrix.vals[2][2] = array[8]; + env->ReleaseFloatArrayElements(xyzD50, array, 0); + return xyzMatrix; +} + +/////////////////////////////////////////////////////////////////////////////// + +static float halfToFloat(uint16_t bits) { +#ifdef __ANDROID__ // __fp16 is not defined on non-Android builds + __fp16 h; + memcpy(&h, &bits, 2); + return (float)h; +#else + return SkHalfToFloat(bits); +#endif +} + +SkColor4f GraphicsJNI::convertColorLong(jlong color) { + if ((color & 0x3f) == 0) { + // This corresponds to sRGB, which is treated differently than the rest. + uint8_t a = color >> 56 & 0xff; + uint8_t r = color >> 48 & 0xff; + uint8_t g = color >> 40 & 0xff; + uint8_t b = color >> 32 & 0xff; + SkColor c = SkColorSetARGB(a, r, g, b); + return SkColor4f::FromColor(c); + } + + // These match the implementation of android.graphics.Color#red(long) etc. + float r = halfToFloat((uint16_t)(color >> 48 & 0xffff)); + float g = halfToFloat((uint16_t)(color >> 32 & 0xffff)); + float b = halfToFloat((uint16_t)(color >> 16 & 0xffff)); + float a = (color >> 6 & 0x3ff) / 1023.0f; + + return SkColor4f{r, g, b, a}; +} + +sk_sp<SkColorSpace> GraphicsJNI::getNativeColorSpace(jlong colorSpaceHandle) { + if (colorSpaceHandle == 0) return nullptr; + return sk_ref_sp(reinterpret_cast<SkColorSpace*>(colorSpaceHandle)); +} + +static void unref_colorSpace(SkColorSpace* cs) { + SkSafeUnref(cs); +} + +static jlong ColorSpace_getNativeFinalizer(JNIEnv*, jobject) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&unref_colorSpace)); +} + +static jlong ColorSpace_creator(JNIEnv* env, jobject, jfloat a, jfloat b, jfloat c, + jfloat d, jfloat e, jfloat f, jfloat g, jfloatArray xyzD50) { + skcms_TransferFunction p; + p.a = a; + p.b = b; + p.c = c; + p.d = d; + p.e = e; + p.f = f; + p.g = g; + skcms_Matrix3x3 xyzMatrix = getNativeXYZMatrix(env, xyzD50); + + return reinterpret_cast<jlong>(SkColorSpace::MakeRGB(p, xyzMatrix).release()); +} + +static const JNINativeMethod gColorSpaceRgbMethods[] = { + { "nativeGetNativeFinalizer", "()J", (void*)ColorSpace_getNativeFinalizer }, + { "nativeCreate", "(FFFFFFF[F)J", (void*)ColorSpace_creator } +}; + +namespace android { + +int register_android_graphics_ColorSpace(JNIEnv* env) { + return android::RegisterMethodsOrDie(env, "android/graphics/ColorSpace$Rgb", + gColorSpaceRgbMethods, NELEM(gColorSpaceRgbMethods)); +} + +}; // namespace android diff --git a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp new file mode 100644 index 000000000000..54822f1f07e2 --- /dev/null +++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp @@ -0,0 +1,213 @@ +/* + * 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. + */ + +#include "GraphicsJNI.h" + +#ifdef __ANDROID__ // Layoutlib does not support Looper and device properties +#include <utils/Looper.h> +#endif + +#include <SkBitmap.h> +#include <SkRegion.h> + +#include <Rect.h> +#include <RenderNode.h> +#include <CanvasProperty.h> +#include <hwui/Canvas.h> +#include <hwui/Paint.h> +#include <minikin/Layout.h> +#ifdef __ANDROID__ // Layoutlib does not support RenderThread +#include <renderthread/RenderProxy.h> +#endif + +namespace android { + +using namespace uirenderer; + +jmethodID gRunnableMethodId; + +static JNIEnv* jnienv(JavaVM* vm) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm); + } + return env; +} + +#ifdef __ANDROID__ // Layoutlib does not support GL, Looper +class InvokeRunnableMessage : public MessageHandler { +public: + InvokeRunnableMessage(JNIEnv* env, jobject runnable) { + mRunnable = env->NewGlobalRef(runnable); + env->GetJavaVM(&mVm); + } + + virtual ~InvokeRunnableMessage() { + jnienv(mVm)->DeleteGlobalRef(mRunnable); + } + + virtual void handleMessage(const Message&) { + jnienv(mVm)->CallVoidMethod(mRunnable, gRunnableMethodId); + } + +private: + JavaVM* mVm; + jobject mRunnable; +}; + +class GlFunctorReleasedCallbackBridge : public GlFunctorLifecycleListener { +public: + GlFunctorReleasedCallbackBridge(JNIEnv* env, jobject javaCallback) { + mLooper = Looper::getForThread(); + mMessage = new InvokeRunnableMessage(env, javaCallback); + } + + virtual void onGlFunctorReleased(Functor* functor) override { + mLooper->sendMessage(mMessage, 0); + } + +private: + sp<Looper> mLooper; + sp<InvokeRunnableMessage> mMessage; +}; +#endif + +// ---------------- @FastNative ----------------------------- + +static void android_view_DisplayListCanvas_callDrawGLFunction(JNIEnv* env, jobject clazz, + jlong canvasPtr, jlong functorPtr, jobject releasedCallback) { +#ifdef __ANDROID__ // Layoutlib does not support GL + Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr); + Functor* functor = reinterpret_cast<Functor*>(functorPtr); + sp<GlFunctorReleasedCallbackBridge> bridge; + if (releasedCallback) { + bridge = new GlFunctorReleasedCallbackBridge(env, releasedCallback); + } + canvas->callDrawGLFunction(functor, bridge.get()); +#endif +} + + +// ---------------- @CriticalNative ------------------------- + +static jlong android_view_DisplayListCanvas_createDisplayListCanvas(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jint width, jint height) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return reinterpret_cast<jlong>(Canvas::create_recording_canvas(width, height, renderNode)); +} + +static void android_view_DisplayListCanvas_resetDisplayListCanvas(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, + jlong renderNodePtr, jint width, jint height) { + Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr); + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + canvas->resetRecording(width, height, renderNode); +} + +static jint android_view_DisplayListCanvas_getMaxTextureSize(CRITICAL_JNI_PARAMS) { +#ifdef __ANDROID__ // Layoutlib does not support RenderProxy (RenderThread) + return android::uirenderer::renderthread::RenderProxy::maxTextureSize(); +#else + return 4096; +#endif +} + +static void android_view_DisplayListCanvas_insertReorderBarrier(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, + jboolean reorderEnable) { + Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr); + canvas->insertReorderBarrier(reorderEnable); +} + +static jlong android_view_DisplayListCanvas_finishRecording(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr) { + Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr); + return reinterpret_cast<jlong>(canvas->finishRecording()); +} + +static void android_view_DisplayListCanvas_drawRenderNode(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jlong renderNodePtr) { + Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr); + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + canvas->drawRenderNode(renderNode); +} + +static void android_view_DisplayListCanvas_drawTextureLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jlong layerPtr) { + Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr); + DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr); + canvas->drawLayer(layer); +} + +static void android_view_DisplayListCanvas_drawRoundRectProps(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, + jlong leftPropPtr, jlong topPropPtr, jlong rightPropPtr, jlong bottomPropPtr, + jlong rxPropPtr, jlong ryPropPtr, jlong paintPropPtr) { + Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr); + CanvasPropertyPrimitive* leftProp = reinterpret_cast<CanvasPropertyPrimitive*>(leftPropPtr); + CanvasPropertyPrimitive* topProp = reinterpret_cast<CanvasPropertyPrimitive*>(topPropPtr); + CanvasPropertyPrimitive* rightProp = reinterpret_cast<CanvasPropertyPrimitive*>(rightPropPtr); + CanvasPropertyPrimitive* bottomProp = reinterpret_cast<CanvasPropertyPrimitive*>(bottomPropPtr); + CanvasPropertyPrimitive* rxProp = reinterpret_cast<CanvasPropertyPrimitive*>(rxPropPtr); + CanvasPropertyPrimitive* ryProp = reinterpret_cast<CanvasPropertyPrimitive*>(ryPropPtr); + CanvasPropertyPaint* paintProp = reinterpret_cast<CanvasPropertyPaint*>(paintPropPtr); + canvas->drawRoundRect(leftProp, topProp, rightProp, bottomProp, rxProp, ryProp, paintProp); +} + +static void android_view_DisplayListCanvas_drawCircleProps(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, + jlong xPropPtr, jlong yPropPtr, jlong radiusPropPtr, jlong paintPropPtr) { + Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr); + CanvasPropertyPrimitive* xProp = reinterpret_cast<CanvasPropertyPrimitive*>(xPropPtr); + CanvasPropertyPrimitive* yProp = reinterpret_cast<CanvasPropertyPrimitive*>(yPropPtr); + CanvasPropertyPrimitive* radiusProp = reinterpret_cast<CanvasPropertyPrimitive*>(radiusPropPtr); + CanvasPropertyPaint* paintProp = reinterpret_cast<CanvasPropertyPaint*>(paintPropPtr); + canvas->drawCircle(xProp, yProp, radiusProp, paintProp); +} + +static void android_view_DisplayListCanvas_drawWebViewFunctor(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jint functor) { + Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr); + canvas->drawWebViewFunctor(functor); +} + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "android/graphics/RecordingCanvas"; + +static JNINativeMethod gMethods[] = { + + // ------------ @FastNative ------------------ + + { "nCallDrawGLFunction", "(JJLjava/lang/Runnable;)V", + (void*) android_view_DisplayListCanvas_callDrawGLFunction }, + + // ------------ @CriticalNative -------------- + { "nCreateDisplayListCanvas", "(JII)J", (void*) android_view_DisplayListCanvas_createDisplayListCanvas }, + { "nResetDisplayListCanvas", "(JJII)V", (void*) android_view_DisplayListCanvas_resetDisplayListCanvas }, + { "nGetMaximumTextureWidth", "()I", (void*) android_view_DisplayListCanvas_getMaxTextureSize }, + { "nGetMaximumTextureHeight", "()I", (void*) android_view_DisplayListCanvas_getMaxTextureSize }, + { "nInsertReorderBarrier", "(JZ)V", (void*) android_view_DisplayListCanvas_insertReorderBarrier }, + { "nFinishRecording", "(J)J", (void*) android_view_DisplayListCanvas_finishRecording }, + { "nDrawRenderNode", "(JJ)V", (void*) android_view_DisplayListCanvas_drawRenderNode }, + { "nDrawTextureLayer", "(JJ)V", (void*) android_view_DisplayListCanvas_drawTextureLayer }, + { "nDrawCircle", "(JJJJJ)V", (void*) android_view_DisplayListCanvas_drawCircleProps }, + { "nDrawRoundRect", "(JJJJJJJJ)V",(void*) android_view_DisplayListCanvas_drawRoundRectProps }, + { "nDrawWebViewFunctor", "(JI)V", (void*) android_view_DisplayListCanvas_drawWebViewFunctor }, +}; + +int register_android_view_DisplayListCanvas(JNIEnv* env) { + jclass runnableClass = FindClassOrDie(env, "java/lang/Runnable"); + gRunnableMethodId = GetMethodIDOrDie(env, runnableClass, "run", "()V"); + + return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); +} + +}; diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp new file mode 100644 index 000000000000..9815e85db880 --- /dev/null +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -0,0 +1,745 @@ +/* + * 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. + */ + +#undef LOG_TAG +#define LOG_TAG "ThreadedRenderer" +#define ATRACE_TAG ATRACE_TAG_VIEW + +#include <FrameInfo.h> +#include <GraphicsJNI.h> +#include <Picture.h> +#include <Properties.h> +#include <RootRenderNode.h> +#include <dlfcn.h> +#include <inttypes.h> +#include <media/NdkImage.h> +#include <media/NdkImageReader.h> +#include <nativehelper/JNIPlatformHelp.h> +#include <pipeline/skia/ShaderCache.h> +#include <private/EGL/cache.h> +#include <renderthread/CanvasContext.h> +#include <renderthread/RenderProxy.h> +#include <renderthread/RenderTask.h> +#include <renderthread/RenderThread.h> +#include <utils/Color.h> +#include <utils/RefBase.h> +#include <utils/StrongPointer.h> +#include <utils/Timers.h> +#include <utils/TraceUtils.h> + +#include <algorithm> +#include <atomic> + +#include "android_graphics_HardwareRendererObserver.h" + +namespace android { + +using namespace android::uirenderer; +using namespace android::uirenderer::renderthread; + +struct { + jclass clazz; + jmethodID invokePictureCapturedCallback; +} gHardwareRenderer; + +struct { + jmethodID onFrameDraw; +} gFrameDrawingCallback; + +struct { + jmethodID onFrameComplete; +} gFrameCompleteCallback; + +static JNIEnv* getenv(JavaVM* vm) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm); + } + return env; +} + +typedef ANativeWindow* (*ANW_fromSurface)(JNIEnv* env, jobject surface); +ANW_fromSurface fromSurface; + +class JvmErrorReporter : public ErrorHandler { +public: + JvmErrorReporter(JNIEnv* env) { + env->GetJavaVM(&mVm); + } + + virtual void onError(const std::string& message) override { + JNIEnv* env = getenv(mVm); + jniThrowException(env, "java/lang/IllegalStateException", message.c_str()); + } +private: + JavaVM* mVm; +}; + +class FrameCompleteWrapper : public LightRefBase<FrameCompleteWrapper> { +public: + explicit FrameCompleteWrapper(JNIEnv* env, jobject jobject) { + env->GetJavaVM(&mVm); + mObject = env->NewGlobalRef(jobject); + LOG_ALWAYS_FATAL_IF(!mObject, "Failed to make global ref"); + } + + ~FrameCompleteWrapper() { + releaseObject(); + } + + void onFrameComplete(int64_t frameNr) { + if (mObject) { + ATRACE_FORMAT("frameComplete %" PRId64, frameNr); + getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, frameNr); + releaseObject(); + } + } + +private: + JavaVM* mVm; + jobject mObject; + + void releaseObject() { + if (mObject) { + getenv(mVm)->DeleteGlobalRef(mObject); + mObject = nullptr; + } + } +}; + +static void android_view_ThreadedRenderer_rotateProcessStatsBuffer(JNIEnv* env, jobject clazz) { + RenderProxy::rotateProcessStatsBuffer(); +} + +static void android_view_ThreadedRenderer_setProcessStatsBuffer(JNIEnv* env, jobject clazz, + jint fd) { + RenderProxy::setProcessStatsBuffer(fd); +} + +static jint android_view_ThreadedRenderer_getRenderThreadTid(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + return proxy->getRenderThreadTid(); +} + +static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) { + RootRenderNode* node = new RootRenderNode(std::make_unique<JvmErrorReporter>(env)); + node->incStrong(0); + node->setName("RootRenderNode"); + return reinterpret_cast<jlong>(node); +} + +static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz, + jboolean translucent, jboolean isWideGamut, jlong rootRenderNodePtr) { + RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr); + ContextFactoryImpl factory(rootRenderNode); + RenderProxy* proxy = new RenderProxy(translucent, rootRenderNode, &factory); + proxy->setWideGamut(isWideGamut); + return (jlong) proxy; +} + +static void android_view_ThreadedRenderer_deleteProxy(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + delete proxy; +} + +static jboolean android_view_ThreadedRenderer_loadSystemProperties(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + return proxy->loadSystemProperties(); +} + +static void android_view_ThreadedRenderer_setName(JNIEnv* env, jobject clazz, + jlong proxyPtr, jstring jname) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + const char* name = env->GetStringUTFChars(jname, NULL); + proxy->setName(name); + env->ReleaseStringUTFChars(jname, name); +} + +static void android_view_ThreadedRenderer_setSurface(JNIEnv* env, jobject clazz, + jlong proxyPtr, jobject jsurface, jboolean discardBuffer) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + ANativeWindow* window = nullptr; + if (jsurface) { + window = fromSurface(env, jsurface); + } + bool enableTimeout = true; + if (discardBuffer) { + // Currently only Surface#lockHardwareCanvas takes this path + enableTimeout = false; + proxy->setSwapBehavior(SwapBehavior::kSwap_discardBuffer); + } + proxy->setSurface(window, enableTimeout); + ANativeWindow_release(window); +} + +static jboolean android_view_ThreadedRenderer_pause(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + return proxy->pause(); +} + +static void android_view_ThreadedRenderer_setStopped(JNIEnv* env, jobject clazz, + jlong proxyPtr, jboolean stopped) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + proxy->setStopped(stopped); +} + +static void android_view_ThreadedRenderer_setLightAlpha(JNIEnv* env, jobject clazz, jlong proxyPtr, + jfloat ambientShadowAlpha, jfloat spotShadowAlpha) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + proxy->setLightAlpha((uint8_t) (255 * ambientShadowAlpha), (uint8_t) (255 * spotShadowAlpha)); +} + +static void android_view_ThreadedRenderer_setLightGeometry(JNIEnv* env, jobject clazz, + jlong proxyPtr, jfloat lightX, jfloat lightY, jfloat lightZ, jfloat lightRadius) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + proxy->setLightGeometry((Vector3){lightX, lightY, lightZ}, lightRadius); +} + +static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz, + jlong proxyPtr, jboolean opaque) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + proxy->setOpaque(opaque); +} + +static void android_view_ThreadedRenderer_setWideGamut(JNIEnv* env, jobject clazz, + jlong proxyPtr, jboolean wideGamut) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + proxy->setWideGamut(wideGamut); +} + +static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) { + LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE, + "Mismatched size expectations, given %d expected %d", + frameInfoSize, UI_THREAD_FRAME_INFO_SIZE); + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo()); + return proxy->syncAndDrawFrame(); +} + +static void android_view_ThreadedRenderer_destroy(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlong rootNodePtr) { + RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr); + rootRenderNode->destroy(); + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + proxy->destroy(); +} + +static void android_view_ThreadedRenderer_registerAnimatingRenderNode(JNIEnv* env, jobject clazz, + jlong rootNodePtr, jlong animatingNodePtr) { + RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr); + RenderNode* animatingNode = reinterpret_cast<RenderNode*>(animatingNodePtr); + rootRenderNode->attachAnimatingNode(animatingNode); +} + +static void android_view_ThreadedRenderer_registerVectorDrawableAnimator(JNIEnv* env, jobject clazz, + jlong rootNodePtr, jlong animatorPtr) { + RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr); + PropertyValuesAnimatorSet* animator = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorPtr); + rootRenderNode->addVectorDrawableAnimator(animator); +} + +static void android_view_ThreadedRenderer_invokeFunctor(JNIEnv* env, jobject clazz, + jlong functorPtr, jboolean waitForCompletion) { + Functor* functor = reinterpret_cast<Functor*>(functorPtr); + RenderProxy::invokeFunctor(functor, waitForCompletion); +} + +static jlong android_view_ThreadedRenderer_createTextureLayer(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + DeferredLayerUpdater* layer = proxy->createTextureLayer(); + return reinterpret_cast<jlong>(layer); +} + +static void android_view_ThreadedRenderer_buildLayer(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlong nodePtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + RenderNode* node = reinterpret_cast<RenderNode*>(nodePtr); + proxy->buildLayer(node); +} + +static jboolean android_view_ThreadedRenderer_copyLayerInto(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlong layerPtr, jlong bitmapPtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr); + SkBitmap bitmap; + bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); + return proxy->copyLayerInto(layer, bitmap); +} + +static void android_view_ThreadedRenderer_pushLayerUpdate(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlong layerPtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr); + proxy->pushLayerUpdate(layer); +} + +static void android_view_ThreadedRenderer_cancelLayerUpdate(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlong layerPtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr); + proxy->cancelLayerUpdate(layer); +} + +static void android_view_ThreadedRenderer_detachSurfaceTexture(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlong layerPtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr); + proxy->detachSurfaceTexture(layer); +} + +static void android_view_ThreadedRenderer_destroyHardwareResources(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + proxy->destroyHardwareResources(); +} + +static void android_view_ThreadedRenderer_trimMemory(JNIEnv* env, jobject clazz, + jint level) { + RenderProxy::trimMemory(level); +} + +static void android_view_ThreadedRenderer_overrideProperty(JNIEnv* env, jobject clazz, + jstring name, jstring value) { + const char* nameCharArray = env->GetStringUTFChars(name, NULL); + const char* valueCharArray = env->GetStringUTFChars(value, NULL); + RenderProxy::overrideProperty(nameCharArray, valueCharArray); + env->ReleaseStringUTFChars(name, nameCharArray); + env->ReleaseStringUTFChars(name, valueCharArray); +} + +static void android_view_ThreadedRenderer_fence(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + proxy->fence(); +} + +static void android_view_ThreadedRenderer_stopDrawing(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + proxy->stopDrawing(); +} + +static void android_view_ThreadedRenderer_notifyFramePending(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + proxy->notifyFramePending(); +} + +static void android_view_ThreadedRenderer_dumpProfileInfo(JNIEnv* env, jobject clazz, + jlong proxyPtr, jobject javaFileDescriptor, jint dumpFlags) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor); + proxy->dumpProfileInfo(fd, dumpFlags); +} + +static void android_view_ThreadedRenderer_addRenderNode(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlong renderNodePtr, jboolean placeFront) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + proxy->addRenderNode(renderNode, placeFront); +} + +static void android_view_ThreadedRenderer_removeRenderNode(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlong renderNodePtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + proxy->removeRenderNode(renderNode); +} + +static void android_view_ThreadedRendererd_drawRenderNode(JNIEnv* env, jobject clazz, + jlong proxyPtr, jlong renderNodePtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + proxy->drawRenderNode(renderNode); +} + +static void android_view_ThreadedRenderer_setContentDrawBounds(JNIEnv* env, + jobject clazz, jlong proxyPtr, jint left, jint top, jint right, jint bottom) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + proxy->setContentDrawBounds(left, top, right, bottom); +} + +class JGlobalRefHolder { +public: + JGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm), mObject(object) {} + + virtual ~JGlobalRefHolder() { + getenv(mVm)->DeleteGlobalRef(mObject); + mObject = nullptr; + } + + jobject object() { return mObject; } + JavaVM* vm() { return mVm; } + +private: + JGlobalRefHolder(const JGlobalRefHolder&) = delete; + void operator=(const JGlobalRefHolder&) = delete; + + JavaVM* mVm; + jobject mObject; +}; + +static void android_view_ThreadedRenderer_setPictureCapturedCallbackJNI(JNIEnv* env, + jobject clazz, jlong proxyPtr, jobject pictureCallback) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + if (!pictureCallback) { + proxy->setPictureCapturedCallback(nullptr); + } else { + JavaVM* vm = nullptr; + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM"); + auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(vm, + env->NewGlobalRef(pictureCallback)); + proxy->setPictureCapturedCallback([globalCallbackRef](sk_sp<SkPicture>&& picture) { + JNIEnv* env = getenv(globalCallbackRef->vm()); + Picture* wrapper = new Picture{std::move(picture)}; + env->CallStaticVoidMethod(gHardwareRenderer.clazz, + gHardwareRenderer.invokePictureCapturedCallback, + static_cast<jlong>(reinterpret_cast<intptr_t>(wrapper)), + globalCallbackRef->object()); + }); + } +} + +static void android_view_ThreadedRenderer_setFrameCallback(JNIEnv* env, + jobject clazz, jlong proxyPtr, jobject frameCallback) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + if (!frameCallback) { + proxy->setFrameCallback(nullptr); + } else { + JavaVM* vm = nullptr; + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM"); + auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(vm, + env->NewGlobalRef(frameCallback)); + proxy->setFrameCallback([globalCallbackRef](int64_t frameNr) { + JNIEnv* env = getenv(globalCallbackRef->vm()); + env->CallVoidMethod(globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw, + static_cast<jlong>(frameNr)); + }); + } +} + +static void android_view_ThreadedRenderer_setFrameCompleteCallback(JNIEnv* env, + jobject clazz, jlong proxyPtr, jobject callback) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + if (!callback) { + proxy->setFrameCompleteCallback(nullptr); + } else { + sp<FrameCompleteWrapper> wrapper = new FrameCompleteWrapper{env, callback}; + proxy->setFrameCompleteCallback([wrapper](int64_t frameNr) { + wrapper->onFrameComplete(frameNr); + }); + } +} + +static jint android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env, + jobject clazz, jobject jsurface, jint left, jint top, + jint right, jint bottom, jlong bitmapPtr) { + SkBitmap bitmap; + bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); + ANativeWindow* window = fromSurface(env, jsurface); + jint result = RenderProxy::copySurfaceInto(window, left, top, right, bottom, &bitmap); + ANativeWindow_release(window); + return result; +} + +class ContextFactory : public IContextFactory { +public: + virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) { + return new AnimationContext(clock); + } +}; + +static jobject android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode(JNIEnv* env, + jobject clazz, jlong renderNodePtr, jint jwidth, jint jheight) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + if (jwidth <= 0 || jheight <= 0) { + ALOGW("Invalid width %d or height %d", jwidth, jheight); + return nullptr; + } + + uint32_t width = jwidth; + uint32_t height = jheight; + + // Create an ImageReader wired up to a BufferItemConsumer + AImageReader* rawReader; + media_status_t result = + AImageReader_newWithUsage(width, height, AIMAGE_FORMAT_RGBA_8888, + AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE, 2, &rawReader); + std::unique_ptr<AImageReader, decltype(&AImageReader_delete)> reader(rawReader, + AImageReader_delete); + + if (result != AMEDIA_OK) { + ALOGW("Error creating image reader!"); + return nullptr; + } + + // Note that ownership of this window is maintained by AImageReader, so we + // shouldn't need to wrap around a smart pointer. + ANativeWindow* window; + result = AImageReader_getWindow(rawReader, &window); + + if (result != AMEDIA_OK) { + ALOGW("Error retrieving the native window!"); + return nullptr; + } + + // Render into the surface + { + ContextFactory factory; + RenderProxy proxy{true, renderNode, &factory}; + proxy.setSwapBehavior(SwapBehavior::kSwap_discardBuffer); + proxy.setSurface(window); + // Shadows can't be used via this interface, so just set the light source + // to all 0s. + proxy.setLightAlpha(0, 0); + proxy.setLightGeometry((Vector3){0, 0, 0}, 0); + nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC); + UiFrameInfoBuilder(proxy.frameInfo()) + .setVsync(vsync, vsync) + .addFlag(FrameInfoFlags::SurfaceCanvas); + proxy.syncAndDrawFrame(); + } + + AImage* rawImage; + result = AImageReader_acquireNextImage(rawReader, &rawImage); + std::unique_ptr<AImage, decltype(&AImage_delete)> image(rawImage, AImage_delete); + if (result != AMEDIA_OK) { + ALOGW("Error reading image: %d!", result); + return nullptr; + } + + AHardwareBuffer* buffer; + result = AImage_getHardwareBuffer(rawImage, &buffer); + + AHardwareBuffer_Desc desc; + AHardwareBuffer_describe(buffer, &desc); + + if (desc.width != width || desc.height != height) { + ALOGW("AHardwareBuffer size mismatch, got %dx%d expected %dx%d", desc.width, desc.height, + width, height); + // Continue I guess? + } + + sk_sp<SkColorSpace> cs = uirenderer::DataSpaceToColorSpace( + static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window))); + if (cs == nullptr) { + // nullptr is treated as SRGB in Skia, thus explicitly use SRGB in order to make sure + // the returned bitmap has a color space. + cs = SkColorSpace::MakeSRGB(); + } + sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, cs); + return bitmap::createBitmap(env, bitmap.release(), + android::bitmap::kBitmapCreateFlag_Premultiplied); +} + +static void android_view_ThreadedRenderer_disableVsync(JNIEnv*, jclass) { + RenderProxy::disableVsync(); +} + +static void android_view_ThreadedRenderer_setHighContrastText(JNIEnv*, jclass, jboolean enable) { + Properties::enableHighContrastText = enable; +} + +static void android_view_ThreadedRenderer_hackySetRTAnimationsEnabled(JNIEnv*, jclass, + jboolean enable) { + Properties::enableRTAnimations = enable; +} + +static void android_view_ThreadedRenderer_setDebuggingEnabled(JNIEnv*, jclass, jboolean enable) { + Properties::debuggingEnabled = enable; +} + +static void android_view_ThreadedRenderer_setIsolatedProcess(JNIEnv*, jclass, jboolean isolated) { + Properties::isolatedProcess = isolated; +} + +static void android_view_ThreadedRenderer_setContextPriority(JNIEnv*, jclass, + jint contextPriority) { + Properties::contextPriority = contextPriority; +} + +static void android_view_ThreadedRenderer_allocateBuffers(JNIEnv* env, jobject clazz, + jlong proxyPtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + proxy->allocateBuffers(); +} + +static void android_view_ThreadedRenderer_setForceDark(JNIEnv* env, jobject clazz, + jlong proxyPtr, jboolean enable) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + proxy->setForceDark(enable); +} + +static void android_view_ThreadedRenderer_preload(JNIEnv*, jclass) { + RenderProxy::preload(); +} + +// ---------------------------------------------------------------------------- +// HardwareRendererObserver +// ---------------------------------------------------------------------------- + +static void android_view_ThreadedRenderer_addObserver(JNIEnv* env, jclass clazz, + jlong proxyPtr, jlong observerPtr) { + HardwareRendererObserver* observer = reinterpret_cast<HardwareRendererObserver*>(observerPtr); + renderthread::RenderProxy* renderProxy = + reinterpret_cast<renderthread::RenderProxy*>(proxyPtr); + + renderProxy->addFrameMetricsObserver(observer); +} + +static void android_view_ThreadedRenderer_removeObserver(JNIEnv* env, jclass clazz, + jlong proxyPtr, jlong observerPtr) { + HardwareRendererObserver* observer = reinterpret_cast<HardwareRendererObserver*>(observerPtr); + renderthread::RenderProxy* renderProxy = + reinterpret_cast<renderthread::RenderProxy*>(proxyPtr); + + renderProxy->removeFrameMetricsObserver(observer); +} + +// ---------------------------------------------------------------------------- +// Shaders +// ---------------------------------------------------------------------------- + +static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz, + jstring diskCachePath, jstring skiaDiskCachePath) { + const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL); + android::egl_set_cache_filename(cacheArray); + env->ReleaseStringUTFChars(diskCachePath, cacheArray); + + const char* skiaCacheArray = env->GetStringUTFChars(skiaDiskCachePath, NULL); + uirenderer::skiapipeline::ShaderCache::get().setFilename(skiaCacheArray); + env->ReleaseStringUTFChars(skiaDiskCachePath, skiaCacheArray); +} + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "android/graphics/HardwareRenderer"; + +static const JNINativeMethod gMethods[] = { + { "nRotateProcessStatsBuffer", "()V", (void*) android_view_ThreadedRenderer_rotateProcessStatsBuffer }, + { "nSetProcessStatsBuffer", "(I)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer }, + { "nGetRenderThreadTid", "(J)I", (void*) android_view_ThreadedRenderer_getRenderThreadTid }, + { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode }, + { "nCreateProxy", "(ZZJ)J", (void*) android_view_ThreadedRenderer_createProxy }, + { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy }, + { "nLoadSystemProperties", "(J)Z", (void*) android_view_ThreadedRenderer_loadSystemProperties }, + { "nSetName", "(JLjava/lang/String;)V", (void*) android_view_ThreadedRenderer_setName }, + { "nSetSurface", "(JLandroid/view/Surface;Z)V", (void*) android_view_ThreadedRenderer_setSurface }, + { "nPause", "(J)Z", (void*) android_view_ThreadedRenderer_pause }, + { "nSetStopped", "(JZ)V", (void*) android_view_ThreadedRenderer_setStopped }, + { "nSetLightAlpha", "(JFF)V", (void*) android_view_ThreadedRenderer_setLightAlpha }, + { "nSetLightGeometry", "(JFFFF)V", (void*) android_view_ThreadedRenderer_setLightGeometry }, + { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque }, + { "nSetWideGamut", "(JZ)V", (void*) android_view_ThreadedRenderer_setWideGamut }, + { "nSyncAndDrawFrame", "(J[JI)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame }, + { "nDestroy", "(JJ)V", (void*) android_view_ThreadedRenderer_destroy }, + { "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode }, + { "nRegisterVectorDrawableAnimator", "(JJ)V", (void*) android_view_ThreadedRenderer_registerVectorDrawableAnimator }, + { "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor }, + { "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer }, + { "nBuildLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_buildLayer }, + { "nCopyLayerInto", "(JJJ)Z", (void*) android_view_ThreadedRenderer_copyLayerInto }, + { "nPushLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_pushLayerUpdate }, + { "nCancelLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_cancelLayerUpdate }, + { "nDetachSurfaceTexture", "(JJ)V", (void*) android_view_ThreadedRenderer_detachSurfaceTexture }, + { "nDestroyHardwareResources", "(J)V", (void*) android_view_ThreadedRenderer_destroyHardwareResources }, + { "nTrimMemory", "(I)V", (void*) android_view_ThreadedRenderer_trimMemory }, + { "nOverrideProperty", "(Ljava/lang/String;Ljava/lang/String;)V", (void*) android_view_ThreadedRenderer_overrideProperty }, + { "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence }, + { "nStopDrawing", "(J)V", (void*) android_view_ThreadedRenderer_stopDrawing }, + { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending }, + { "nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo }, + { "setupShadersDiskCache", "(Ljava/lang/String;Ljava/lang/String;)V", + (void*) android_view_ThreadedRenderer_setupShadersDiskCache }, + { "nAddRenderNode", "(JJZ)V", (void*) android_view_ThreadedRenderer_addRenderNode}, + { "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode}, + { "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode}, + { "nSetContentDrawBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentDrawBounds}, + { "nSetPictureCaptureCallback", "(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V", + (void*) android_view_ThreadedRenderer_setPictureCapturedCallbackJNI }, + { "nSetFrameCallback", "(JLandroid/graphics/HardwareRenderer$FrameDrawingCallback;)V", + (void*)android_view_ThreadedRenderer_setFrameCallback}, + { "nSetFrameCompleteCallback", "(JLandroid/graphics/HardwareRenderer$FrameCompleteCallback;)V", + (void*)android_view_ThreadedRenderer_setFrameCompleteCallback }, + { "nAddObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_addObserver }, + { "nRemoveObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_removeObserver }, + { "nCopySurfaceInto", "(Landroid/view/Surface;IIIIJ)I", + (void*)android_view_ThreadedRenderer_copySurfaceInto }, + { "nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;", + (void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode }, + { "disableVsync", "()V", (void*)android_view_ThreadedRenderer_disableVsync }, + { "nSetHighContrastText", "(Z)V", (void*)android_view_ThreadedRenderer_setHighContrastText }, + { "nHackySetRTAnimationsEnabled", "(Z)V", + (void*)android_view_ThreadedRenderer_hackySetRTAnimationsEnabled }, + { "nSetDebuggingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDebuggingEnabled }, + { "nSetIsolatedProcess", "(Z)V", (void*)android_view_ThreadedRenderer_setIsolatedProcess }, + { "nSetContextPriority", "(I)V", (void*)android_view_ThreadedRenderer_setContextPriority }, + { "nAllocateBuffers", "(J)V", (void*)android_view_ThreadedRenderer_allocateBuffers }, + { "nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark }, + { "preload", "()V", (void*)android_view_ThreadedRenderer_preload }, +}; + +static JavaVM* mJvm = nullptr; + +static void attachRenderThreadToJvm(const char* name) { + LOG_ALWAYS_FATAL_IF(!mJvm, "No jvm but we set the hook??"); + + JavaVMAttachArgs args; + args.version = JNI_VERSION_1_4; + args.name = name; + args.group = NULL; + JNIEnv* env; + mJvm->AttachCurrentThreadAsDaemon(&env, (void*) &args); +} + +int register_android_view_ThreadedRenderer(JNIEnv* env) { + env->GetJavaVM(&mJvm); + RenderThread::setOnStartHook(&attachRenderThreadToJvm); + + jclass hardwareRenderer = FindClassOrDie(env, + "android/graphics/HardwareRenderer"); + gHardwareRenderer.clazz = reinterpret_cast<jclass>(env->NewGlobalRef(hardwareRenderer)); + gHardwareRenderer.invokePictureCapturedCallback = GetStaticMethodIDOrDie(env, hardwareRenderer, + "invokePictureCapturedCallback", + "(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V"); + + jclass frameCallbackClass = FindClassOrDie(env, + "android/graphics/HardwareRenderer$FrameDrawingCallback"); + gFrameDrawingCallback.onFrameDraw = GetMethodIDOrDie(env, frameCallbackClass, + "onFrameDraw", "(J)V"); + + jclass frameCompleteClass = FindClassOrDie(env, + "android/graphics/HardwareRenderer$FrameCompleteCallback"); + gFrameCompleteCallback.onFrameComplete = GetMethodIDOrDie(env, frameCompleteClass, + "onFrameComplete", "(J)V"); + + void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE); + fromSurface = (ANW_fromSurface)dlsym(handle_, "ANativeWindow_fromSurface"); + LOG_ALWAYS_FATAL_IF(fromSurface == nullptr, + "Failed to find required symbol ANativeWindow_fromSurface!"); + + return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); +} + +}; // namespace android diff --git a/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp b/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp new file mode 100644 index 000000000000..5b3e65648981 --- /dev/null +++ b/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2019 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. + */ + +#include "android_graphics_HardwareRendererObserver.h" + +#include "graphics_jni_helpers.h" +#include "nativehelper/jni_macros.h" + +#include <array> + +namespace android { + +struct { + jmethodID callback; +} gHardwareRendererObserverClassInfo; + +static JNIEnv* getenv(JavaVM* vm) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm); + } + return env; +} + +HardwareRendererObserver::HardwareRendererObserver(JavaVM *vm, jobject observer) : mVm(vm) { + mObserverWeak = getenv(mVm)->NewWeakGlobalRef(observer); + LOG_ALWAYS_FATAL_IF(mObserverWeak == nullptr, + "unable to create frame stats observer reference"); +} + +HardwareRendererObserver::~HardwareRendererObserver() { + JNIEnv* env = getenv(mVm); + env->DeleteWeakGlobalRef(mObserverWeak); +} + +bool HardwareRendererObserver::getNextBuffer(JNIEnv* env, jlongArray metrics, int* dropCount) { + jsize bufferSize = env->GetArrayLength(reinterpret_cast<jarray>(metrics)); + LOG_ALWAYS_FATAL_IF(bufferSize != HardwareRendererObserver::kBufferSize, + "Mismatched Java/Native FrameMetrics data format."); + + FrameMetricsNotification& elem = mRingBuffer[mNextInQueue]; + if (elem.hasData.load()) { + env->SetLongArrayRegion(metrics, 0, kBufferSize, elem.buffer); + *dropCount = elem.dropCount; + mNextInQueue = (mNextInQueue + 1) % kRingSize; + elem.hasData = false; + return true; + } + + return false; +} + +void HardwareRendererObserver::notify(const int64_t* stats) { + FrameMetricsNotification& elem = mRingBuffer[mNextFree]; + + if (!elem.hasData.load()) { + memcpy(elem.buffer, stats, kBufferSize * sizeof(stats[0])); + + elem.dropCount = mDroppedReports; + mDroppedReports = 0; + mNextFree = (mNextFree + 1) % kRingSize; + elem.hasData = true; + + JNIEnv* env = getenv(mVm); + jobject target = env->NewLocalRef(mObserverWeak); + if (target != nullptr) { + env->CallVoidMethod(target, gHardwareRendererObserverClassInfo.callback); + env->DeleteLocalRef(target); + } + } else { + mDroppedReports++; + } +} + +static jlong android_graphics_HardwareRendererObserver_createObserver(JNIEnv* env, + jobject observerObj) { + JavaVM* vm = nullptr; + if (env->GetJavaVM(&vm) != JNI_OK) { + LOG_ALWAYS_FATAL("Unable to get Java VM"); + return 0; + } + + HardwareRendererObserver* observer = new HardwareRendererObserver(vm, observerObj); + return reinterpret_cast<jlong>(observer); +} + +static jint android_graphics_HardwareRendererObserver_getNextBuffer(JNIEnv* env, jobject, + jlong observerPtr, + jlongArray metrics) { + HardwareRendererObserver* observer = reinterpret_cast<HardwareRendererObserver*>(observerPtr); + int dropCount = 0; + if (observer->getNextBuffer(env, metrics, &dropCount)) { + return dropCount; + } else { + return -1; + } +} + +static const std::array gMethods = { + MAKE_JNI_NATIVE_METHOD("nCreateObserver", "()J", + android_graphics_HardwareRendererObserver_createObserver), + MAKE_JNI_NATIVE_METHOD("nGetNextBuffer", "(J[J)I", + android_graphics_HardwareRendererObserver_getNextBuffer), +}; + +int register_android_graphics_HardwareRendererObserver(JNIEnv* env) { + + jclass observerClass = FindClassOrDie(env, "android/graphics/HardwareRendererObserver"); + gHardwareRendererObserverClassInfo.callback = GetMethodIDOrDie(env, observerClass, + "notifyDataAvailable", "()V"); + + return RegisterMethodsOrDie(env, "android/graphics/HardwareRendererObserver", + gMethods.data(), gMethods.size()); + +} + +} // namespace android
\ No newline at end of file diff --git a/libs/hwui/jni/android_graphics_HardwareRendererObserver.h b/libs/hwui/jni/android_graphics_HardwareRendererObserver.h new file mode 100644 index 000000000000..62111fd7d7a1 --- /dev/null +++ b/libs/hwui/jni/android_graphics_HardwareRendererObserver.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 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. + */ + +#include "jni.h" + +#include <FrameInfo.h> +#include <FrameMetricsObserver.h> + +namespace android { + +/* + * Implements JNI layer for hwui frame metrics reporting. + */ +class HardwareRendererObserver : public uirenderer::FrameMetricsObserver { +public: + HardwareRendererObserver(JavaVM *vm, jobject observer); + ~HardwareRendererObserver(); + + /** + * Retrieves frame metrics for the oldest frame that the renderer has retained. The renderer + * will retain a buffer until it has been retrieved, via this method, or its internal storage + * is exhausted at which point it informs the caller of how many frames it has failed to store + * since the last time this method was invoked. + * @param env java env required to populate the provided buffer array + * @param metrics output parameter that represents the buffer of metrics that is to be filled + * @param dropCount output parameter that is updated to reflect the number of buffers that were + discarded since the last successful invocation of this method. + * @return true if there was data to populate the array and false otherwise. If false then + * neither the metrics buffer or dropCount will be modified. + */ + bool getNextBuffer(JNIEnv* env, jlongArray metrics, int* dropCount); + + void notify(const int64_t* stats) override; + +private: + static constexpr int kBufferSize = static_cast<int>(uirenderer::FrameInfoIndex::NumIndexes); + static constexpr int kRingSize = 3; + + class FrameMetricsNotification { + public: + FrameMetricsNotification() {} + + std::atomic_bool hasData = false; + int64_t buffer[kBufferSize]; + int dropCount = 0; + private: + // non-copyable + FrameMetricsNotification(const FrameMetricsNotification&) = delete; + FrameMetricsNotification& operator=(const FrameMetricsNotification& ) = delete; + }; + + JavaVM* const mVm; + jweak mObserverWeak; + + int mNextFree = 0; + int mNextInQueue = 0; + FrameMetricsNotification mRingBuffer[kRingSize]; + + int mDroppedReports = 0; +}; + +} // namespace android diff --git a/libs/hwui/jni/android_graphics_Matrix.cpp b/libs/hwui/jni/android_graphics_Matrix.cpp new file mode 100644 index 000000000000..7338ef24cb58 --- /dev/null +++ b/libs/hwui/jni/android_graphics_Matrix.cpp @@ -0,0 +1,396 @@ +/* libs/android_runtime/android/graphics/Matrix.cpp +** +** Copyright 2006, 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. +*/ + +#include "GraphicsJNI.h" +#include "Matrix.h" +#include "SkMatrix.h" + +namespace android { + +static_assert(sizeof(SkMatrix) == 40, "Unexpected sizeof(SkMatrix), " + "update size in Matrix.java#NATIVE_ALLOCATION_SIZE and here"); +static_assert(SK_SCALAR_IS_FLOAT, "SK_SCALAR_IS_FLOAT is false, " + "only float scalar is supported"); + +class SkMatrixGlue { +public: + + // ---------------- Regular JNI ----------------------------- + + static void finalizer(jlong objHandle) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + delete obj; + } + + static jlong getNativeFinalizer(JNIEnv* env, jobject clazz) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&finalizer)); + } + + static jlong create(JNIEnv* env, jobject clazz, jlong srcHandle) { + const SkMatrix* src = reinterpret_cast<SkMatrix*>(srcHandle); + SkMatrix* obj = new SkMatrix(); + if (src) + *obj = *src; + else + obj->reset(); + return reinterpret_cast<jlong>(obj); + } + + // ---------------- @FastNative ----------------------------- + + static void mapPoints(JNIEnv* env, jobject clazz, jlong matrixHandle, + jfloatArray dst, jint dstIndex, jfloatArray src, jint srcIndex, + jint ptCount, jboolean isPts) { + SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); + SkASSERT(ptCount >= 0); + AutoJavaFloatArray autoSrc(env, src, srcIndex + (ptCount << 1), + kRO_JNIAccess); + AutoJavaFloatArray autoDst(env, dst, dstIndex + (ptCount << 1), + kRW_JNIAccess); + float* srcArray = autoSrc.ptr() + srcIndex; + float* dstArray = autoDst.ptr() + dstIndex; + if (isPts) + matrix->mapPoints((SkPoint*) dstArray, (const SkPoint*) srcArray, + ptCount); + else + matrix->mapVectors((SkVector*) dstArray, (const SkVector*) srcArray, + ptCount); + } + + static jboolean mapRect__RectFRectF(JNIEnv* env, jobject clazz, + jlong matrixHandle, jobjectArray dst, jobject src) { + SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); + SkRect dst_, src_; + GraphicsJNI::jrectf_to_rect(env, src, &src_); + jboolean rectStaysRect = matrix->mapRect(&dst_, src_); + GraphicsJNI::rect_to_jrectf(dst_, env, dst); + return rectStaysRect ? JNI_TRUE : JNI_FALSE; + } + + static jboolean setRectToRect(JNIEnv* env, jobject clazz, + jlong matrixHandle, jobject src, jobject dst, jint stfHandle) { + SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); + SkMatrix::ScaleToFit stf = static_cast<SkMatrix::ScaleToFit>(stfHandle); + SkRect src_; + GraphicsJNI::jrectf_to_rect(env, src, &src_); + SkRect dst_; + GraphicsJNI::jrectf_to_rect(env, dst, &dst_); + return matrix->setRectToRect(src_, dst_, stf) ? JNI_TRUE : JNI_FALSE; + } + + static jboolean setPolyToPoly(JNIEnv* env, jobject clazz, + jlong matrixHandle, jfloatArray jsrc, jint srcIndex, + jfloatArray jdst, jint dstIndex, jint ptCount) { + SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); + SkASSERT(srcIndex >= 0); + SkASSERT(dstIndex >= 0); + SkASSERT((unsigned )ptCount <= 4); + + AutoJavaFloatArray autoSrc(env, jsrc, srcIndex + (ptCount << 1), + kRO_JNIAccess); + AutoJavaFloatArray autoDst(env, jdst, dstIndex + (ptCount << 1), + kRW_JNIAccess); + float* src = autoSrc.ptr() + srcIndex; + float* dst = autoDst.ptr() + dstIndex; + bool result; + + result = matrix->setPolyToPoly((const SkPoint*) src, + (const SkPoint*) dst, ptCount); + return result ? JNI_TRUE : JNI_FALSE; + } + + static void getValues(JNIEnv* env, jobject clazz, jlong matrixHandle, + jfloatArray values) { + SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); + AutoJavaFloatArray autoValues(env, values, 9, kRW_JNIAccess); + float* dst = autoValues.ptr(); + for (int i = 0; i < 9; i++) { + dst[i] = matrix->get(i); + } + } + + static void setValues(JNIEnv* env, jobject clazz, jlong matrixHandle, + jfloatArray values) { + SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); + AutoJavaFloatArray autoValues(env, values, 9, kRO_JNIAccess); + const float* src = autoValues.ptr(); + + for (int i = 0; i < 9; i++) { + matrix->set(i, src[i]); + } + } + + // ---------------- @CriticalNative ----------------------------- + + static jboolean isIdentity(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + return obj->isIdentity() ? JNI_TRUE : JNI_FALSE; + } + + static jboolean isAffine(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + return obj->asAffine(NULL) ? JNI_TRUE : JNI_FALSE; + } + + static jboolean rectStaysRect(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + return obj->rectStaysRect() ? JNI_TRUE : JNI_FALSE; + } + + static void reset(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->reset(); + } + + static void set(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong otherHandle) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle); + *obj = *other; + } + + static void setTranslate(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat dx, jfloat dy) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->setTranslate(dx, dy); + } + + static void setScale__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sx, jfloat sy, jfloat px, + jfloat py) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->setScale(sx, sy, px, py); + } + + static void setScale__FF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sx, jfloat sy) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->setScale(sx, sy); + } + + static void setRotate__FFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat degrees, jfloat px, + jfloat py) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->setRotate(degrees, px, py); + } + + static void setRotate__F(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat degrees) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->setRotate(degrees); + } + + static void setSinCos__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sinValue, + jfloat cosValue, jfloat px, jfloat py) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->setSinCos(sinValue, cosValue, px, py); + } + + static void setSinCos__FF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sinValue, + jfloat cosValue) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->setSinCos(sinValue, cosValue); + } + + static void setSkew__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat kx, jfloat ky, jfloat px, + jfloat py) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->setSkew(kx, ky, px, py); + } + + static void setSkew__FF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat kx, jfloat ky) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->setSkew(kx, ky); + } + + static void setConcat(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong aHandle, jlong bHandle) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + SkMatrix* a = reinterpret_cast<SkMatrix*>(aHandle); + SkMatrix* b = reinterpret_cast<SkMatrix*>(bHandle); + obj->setConcat(*a, *b); + } + + static void preTranslate(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat dx, jfloat dy) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->preTranslate(dx, dy); + } + + static void preScale__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sx, jfloat sy, jfloat px, + jfloat py) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->preScale(sx, sy, px, py); + } + + static void preScale__FF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sx, jfloat sy) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->preScale(sx, sy); + } + + static void preRotate__FFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat degrees, jfloat px, + jfloat py) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->preRotate(degrees, px, py); + } + + static void preRotate__F(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat degrees) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->preRotate(degrees); + } + + static void preSkew__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat kx, jfloat ky, jfloat px, + jfloat py) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->preSkew(kx, ky, px, py); + } + + static void preSkew__FF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat kx, jfloat ky) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->preSkew(kx, ky); + } + + static void preConcat(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong otherHandle) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle); + obj->preConcat(*other); + } + + static void postTranslate(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat dx, jfloat dy) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->postTranslate(dx, dy); + } + + static void postScale__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sx, jfloat sy, + jfloat px, jfloat py) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->postScale(sx, sy, px, py); + } + + static void postScale__FF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat sx, jfloat sy) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->postScale(sx, sy); + } + + static void postRotate__FFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat degrees, jfloat px, + jfloat py) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->postRotate(degrees, px, py); + } + + static void postRotate__F(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat degrees) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->postRotate(degrees); + } + + static void postSkew__FFFF(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jfloat kx, jfloat ky, jfloat px, + jfloat py) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + obj->postSkew(kx, ky, px, py); + } + + static void postSkew__FF(CRITICAL_JNI_PARAMS_COMMA jlong matrixHandle, jfloat kx, jfloat ky) { + SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); + matrix->postSkew(kx, ky); + } + + static void postConcat(CRITICAL_JNI_PARAMS_COMMA jlong matrixHandle, jlong otherHandle) { + SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); + SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle); + matrix->postConcat(*other); + } + + static jboolean invert(CRITICAL_JNI_PARAMS_COMMA jlong matrixHandle, jlong inverseHandle) { + SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); + SkMatrix* inverse = reinterpret_cast<SkMatrix*>(inverseHandle); + return matrix->invert(inverse); + } + + static jfloat mapRadius(CRITICAL_JNI_PARAMS_COMMA jlong matrixHandle, jfloat radius) { + SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); + float result; + result = SkScalarToFloat(matrix->mapRadius(radius)); + return static_cast<jfloat>(result); + } + + static jboolean equals(CRITICAL_JNI_PARAMS_COMMA jlong aHandle, jlong bHandle) { + const SkMatrix* a = reinterpret_cast<SkMatrix*>(aHandle); + const SkMatrix* b = reinterpret_cast<SkMatrix*>(bHandle); + return *a == *b; + } +}; + +static const JNINativeMethod methods[] = { + {"nGetNativeFinalizer", "()J", (void*) SkMatrixGlue::getNativeFinalizer}, + {"nCreate","(J)J", (void*) SkMatrixGlue::create}, + + // ------- @FastNative below here --------------- + {"nMapPoints","(J[FI[FIIZ)V", (void*) SkMatrixGlue::mapPoints}, + {"nMapRect","(JLandroid/graphics/RectF;Landroid/graphics/RectF;)Z", + (void*) SkMatrixGlue::mapRect__RectFRectF}, + {"nSetRectToRect","(JLandroid/graphics/RectF;Landroid/graphics/RectF;I)Z", + (void*) SkMatrixGlue::setRectToRect}, + {"nSetPolyToPoly","(J[FI[FII)Z", (void*) SkMatrixGlue::setPolyToPoly}, + {"nGetValues","(J[F)V", (void*) SkMatrixGlue::getValues}, + {"nSetValues","(J[F)V", (void*) SkMatrixGlue::setValues}, + + // ------- @CriticalNative below here --------------- + {"nIsIdentity","(J)Z", (void*) SkMatrixGlue::isIdentity}, + {"nIsAffine","(J)Z", (void*) SkMatrixGlue::isAffine}, + {"nRectStaysRect","(J)Z", (void*) SkMatrixGlue::rectStaysRect}, + {"nReset","(J)V", (void*) SkMatrixGlue::reset}, + {"nSet","(JJ)V", (void*) SkMatrixGlue::set}, + {"nSetTranslate","(JFF)V", (void*) SkMatrixGlue::setTranslate}, + {"nSetScale","(JFFFF)V", (void*) SkMatrixGlue::setScale__FFFF}, + {"nSetScale","(JFF)V", (void*) SkMatrixGlue::setScale__FF}, + {"nSetRotate","(JFFF)V", (void*) SkMatrixGlue::setRotate__FFF}, + {"nSetRotate","(JF)V", (void*) SkMatrixGlue::setRotate__F}, + {"nSetSinCos","(JFFFF)V", (void*) SkMatrixGlue::setSinCos__FFFF}, + {"nSetSinCos","(JFF)V", (void*) SkMatrixGlue::setSinCos__FF}, + {"nSetSkew","(JFFFF)V", (void*) SkMatrixGlue::setSkew__FFFF}, + {"nSetSkew","(JFF)V", (void*) SkMatrixGlue::setSkew__FF}, + {"nSetConcat","(JJJ)V", (void*) SkMatrixGlue::setConcat}, + {"nPreTranslate","(JFF)V", (void*) SkMatrixGlue::preTranslate}, + {"nPreScale","(JFFFF)V", (void*) SkMatrixGlue::preScale__FFFF}, + {"nPreScale","(JFF)V", (void*) SkMatrixGlue::preScale__FF}, + {"nPreRotate","(JFFF)V", (void*) SkMatrixGlue::preRotate__FFF}, + {"nPreRotate","(JF)V", (void*) SkMatrixGlue::preRotate__F}, + {"nPreSkew","(JFFFF)V", (void*) SkMatrixGlue::preSkew__FFFF}, + {"nPreSkew","(JFF)V", (void*) SkMatrixGlue::preSkew__FF}, + {"nPreConcat","(JJ)V", (void*) SkMatrixGlue::preConcat}, + {"nPostTranslate","(JFF)V", (void*) SkMatrixGlue::postTranslate}, + {"nPostScale","(JFFFF)V", (void*) SkMatrixGlue::postScale__FFFF}, + {"nPostScale","(JFF)V", (void*) SkMatrixGlue::postScale__FF}, + {"nPostRotate","(JFFF)V", (void*) SkMatrixGlue::postRotate__FFF}, + {"nPostRotate","(JF)V", (void*) SkMatrixGlue::postRotate__F}, + {"nPostSkew","(JFFFF)V", (void*) SkMatrixGlue::postSkew__FFFF}, + {"nPostSkew","(JFF)V", (void*) SkMatrixGlue::postSkew__FF}, + {"nPostConcat","(JJ)V", (void*) SkMatrixGlue::postConcat}, + {"nInvert","(JJ)Z", (void*) SkMatrixGlue::invert}, + {"nMapRadius","(JF)F", (void*) SkMatrixGlue::mapRadius}, + {"nEquals", "(JJ)Z", (void*) SkMatrixGlue::equals} +}; + +static jfieldID sNativeInstanceField; + +int register_android_graphics_Matrix(JNIEnv* env) { + int result = RegisterMethodsOrDie(env, "android/graphics/Matrix", methods, NELEM(methods)); + + jclass clazz = FindClassOrDie(env, "android/graphics/Matrix"); + sNativeInstanceField = GetFieldIDOrDie(env, clazz, "native_instance", "J"); + + return result; +} + +SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj) { + return reinterpret_cast<SkMatrix*>(env->GetLongField(matrixObj, sNativeInstanceField)); +} + +} diff --git a/libs/hwui/jni/android_graphics_Matrix.h b/libs/hwui/jni/android_graphics_Matrix.h new file mode 100644 index 000000000000..fe90d2ef945d --- /dev/null +++ b/libs/hwui/jni/android_graphics_Matrix.h @@ -0,0 +1,30 @@ +/* + * 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. + */ + +#ifndef _ANDROID_GRAPHICS_MATRIX_H_ +#define _ANDROID_GRAPHICS_MATRIX_H_ + +#include "jni.h" +#include "SkMatrix.h" + +namespace android { + +/* Gets the underlying SkMatrix from a Matrix object. */ +SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj); + +} // namespace android + +#endif // _ANDROID_GRAPHICS_MATRIX_H_ diff --git a/libs/hwui/jni/android_graphics_Picture.cpp b/libs/hwui/jni/android_graphics_Picture.cpp new file mode 100644 index 000000000000..403efb2ab9c9 --- /dev/null +++ b/libs/hwui/jni/android_graphics_Picture.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2008 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. + */ + +#include "CreateJavaOutputStreamAdaptor.h" +#include "GraphicsJNI.h" +#include "Picture.h" +#include "SkCanvas.h" +#include "SkStream.h" + +#include <array> +#include "nativehelper/jni_macros.h" + +namespace android { + +static jlong android_graphics_Picture_newPicture(JNIEnv* env, jobject, jlong srcHandle) { + const Picture* src = reinterpret_cast<Picture*>(srcHandle); + return reinterpret_cast<jlong>(new Picture(src)); +} + +static jlong android_graphics_Picture_deserialize(JNIEnv* env, jobject, jobject jstream, + jbyteArray jstorage) { + Picture* picture = NULL; + SkStream* strm = CreateJavaInputStreamAdaptor(env, jstream, jstorage); + if (strm) { + picture = Picture::CreateFromStream(strm); + delete strm; + } + return reinterpret_cast<jlong>(picture); +} + +static void android_graphics_Picture_killPicture(JNIEnv* env, jobject, jlong pictureHandle) { + Picture* picture = reinterpret_cast<Picture*>(pictureHandle); + SkASSERT(picture); + delete picture; +} + +static void android_graphics_Picture_draw(JNIEnv* env, jobject, jlong canvasHandle, + jlong pictureHandle) { + Canvas* canvas = reinterpret_cast<Canvas*>(canvasHandle); + Picture* picture = reinterpret_cast<Picture*>(pictureHandle); + SkASSERT(canvas); + SkASSERT(picture); + picture->draw(canvas); +} + +static jboolean android_graphics_Picture_serialize(JNIEnv* env, jobject, jlong pictureHandle, + jobject jstream, jbyteArray jstorage) { + Picture* picture = reinterpret_cast<Picture*>(pictureHandle); + SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); + + if (NULL != strm) { + picture->serialize(strm); + delete strm; + return JNI_TRUE; + } + return JNI_FALSE; +} + +static jint android_graphics_Picture_getWidth(JNIEnv* env, jobject, jlong pictureHandle) { + Picture* pict = reinterpret_cast<Picture*>(pictureHandle); + return static_cast<jint>(pict->width()); +} + +static jint android_graphics_Picture_getHeight(JNIEnv* env, jobject, jlong pictureHandle) { + Picture* pict = reinterpret_cast<Picture*>(pictureHandle); + return static_cast<jint>(pict->height()); +} + +static jlong android_graphics_Picture_beginRecording(JNIEnv* env, jobject, jlong pictHandle, + jint w, jint h) { + Picture* pict = reinterpret_cast<Picture*>(pictHandle); + Canvas* canvas = pict->beginRecording(w, h); + return reinterpret_cast<jlong>(canvas); +} + +static void android_graphics_Picture_endRecording(JNIEnv* env, jobject, jlong pictHandle) { + Picture* pict = reinterpret_cast<Picture*>(pictHandle); + pict->endRecording(); +} + +static const std::array gMethods = { + MAKE_JNI_NATIVE_METHOD("nativeGetWidth", "(J)I", android_graphics_Picture_getWidth), + MAKE_JNI_NATIVE_METHOD("nativeGetHeight", "(J)I", android_graphics_Picture_getHeight), + MAKE_JNI_NATIVE_METHOD("nativeConstructor", "(J)J", android_graphics_Picture_newPicture), + MAKE_JNI_NATIVE_METHOD("nativeCreateFromStream", "(Ljava/io/InputStream;[B)J", android_graphics_Picture_deserialize), + MAKE_JNI_NATIVE_METHOD("nativeBeginRecording", "(JII)J", android_graphics_Picture_beginRecording), + MAKE_JNI_NATIVE_METHOD("nativeEndRecording", "(J)V", android_graphics_Picture_endRecording), + MAKE_JNI_NATIVE_METHOD("nativeDraw", "(JJ)V", android_graphics_Picture_draw), + MAKE_JNI_NATIVE_METHOD("nativeWriteToStream", "(JLjava/io/OutputStream;[B)Z", android_graphics_Picture_serialize), + MAKE_JNI_NATIVE_METHOD("nativeDestructor","(J)V", android_graphics_Picture_killPicture) +}; + +int register_android_graphics_Picture(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/Picture", gMethods.data(), gMethods.size()); +} + +}; // namespace android diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp new file mode 100644 index 000000000000..85c802b40459 --- /dev/null +++ b/libs/hwui/jni/android_graphics_RenderNode.cpp @@ -0,0 +1,759 @@ +/* + * Copyright (C) 2012 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. + */ + +#define ATRACE_TAG ATRACE_TAG_VIEW +#include "GraphicsJNI.h" + +#include <Animator.h> +#include <DamageAccumulator.h> +#include <Matrix.h> +#include <RenderNode.h> +#ifdef __ANDROID__ // Layoutlib does not support CanvasContext +#include <renderthread/CanvasContext.h> +#endif +#include <TreeInfo.h> +#include <hwui/Paint.h> +#include <utils/TraceUtils.h> + +namespace android { + +using namespace uirenderer; + +#define SET_AND_DIRTY(prop, val, dirtyFlag) \ + (reinterpret_cast<RenderNode*>(renderNodePtr)->mutateStagingProperties().prop(val) \ + ? (reinterpret_cast<RenderNode*>(renderNodePtr)->setPropertyFieldsDirty(dirtyFlag), true) \ + : false) + +// ---------------------------------------------------------------------------- +// DisplayList view properties +// ---------------------------------------------------------------------------- + +static void android_view_RenderNode_output(JNIEnv* env, jobject clazz, jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + renderNode->output(); +} + +static jint android_view_RenderNode_getUsageSize(JNIEnv* env, jobject clazz, jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->getUsageSize(); +} + +static jint android_view_RenderNode_getAllocatedSize(JNIEnv* env, jobject clazz, jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->getAllocatedSize(); +} + +static jlong android_view_RenderNode_create(JNIEnv* env, jobject, jstring name) { + RenderNode* renderNode = new RenderNode(); + renderNode->incStrong(0); + if (name != NULL) { + const char* textArray = env->GetStringUTFChars(name, NULL); + renderNode->setName(textArray); + env->ReleaseStringUTFChars(name, textArray); + } + return reinterpret_cast<jlong>(renderNode); +} + +static void releaseRenderNode(RenderNode* renderNode) { + renderNode->decStrong(0); +} + +static jlong android_view_RenderNode_getNativeFinalizer(JNIEnv* env, + jobject clazz) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&releaseRenderNode)); +} + +static void android_view_RenderNode_setDisplayList(JNIEnv* env, + jobject clazz, jlong renderNodePtr, jlong displayListPtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + DisplayList* newData = reinterpret_cast<DisplayList*>(displayListPtr); + renderNode->setStagingDisplayList(newData); +} + +static jboolean android_view_RenderNode_isValid(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return reinterpret_cast<RenderNode*>(renderNodePtr)->isValid(); +} + +// ---------------------------------------------------------------------------- +// RenderProperties - setters +// ---------------------------------------------------------------------------- + +static jboolean android_view_RenderNode_setLayerType(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint jlayerType) { + LayerType layerType = static_cast<LayerType>(jlayerType); + return SET_AND_DIRTY(mutateLayerProperties().setType, layerType, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setLayerPaint(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong paintPtr) { + Paint* paint = reinterpret_cast<Paint*>(paintPtr); + return SET_AND_DIRTY(mutateLayerProperties().setFromPaint, paint, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setStaticMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong matrixPtr) { + SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr); + return SET_AND_DIRTY(setStaticMatrix, matrix, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setAnimationMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong matrixPtr) { + SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr); + return SET_AND_DIRTY(setAnimationMatrix, matrix, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setClipToBounds(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jboolean clipToBounds) { + return SET_AND_DIRTY(setClipToBounds, clipToBounds, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setClipBounds(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jint left, jint top, jint right, jint bottom) { + android::uirenderer::Rect clipBounds(left, top, right, bottom); + return SET_AND_DIRTY(setClipBounds, clipBounds, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setClipBoundsEmpty(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return SET_AND_DIRTY(setClipBoundsEmpty,, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setProjectBackwards(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jboolean shouldProject) { + return SET_AND_DIRTY(setProjectBackwards, shouldProject, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setProjectionReceiver(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jboolean shouldRecieve) { + return SET_AND_DIRTY(setProjectionReceiver, shouldRecieve, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setOutlineRoundRect(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jint left, jint top, jint right, jint bottom, jfloat radius, jfloat alpha) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + renderNode->mutateStagingProperties().mutableOutline().setRoundRect(left, top, right, bottom, + radius, alpha); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); + return true; +} + +static jboolean android_view_RenderNode_setOutlinePath(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jlong outlinePathPtr, jfloat alpha) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + SkPath* outlinePath = reinterpret_cast<SkPath*>(outlinePathPtr); + renderNode->mutateStagingProperties().mutableOutline().setPath(outlinePath, alpha); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); + return true; +} + +static jboolean android_view_RenderNode_setOutlineEmpty(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + renderNode->mutateStagingProperties().mutableOutline().setEmpty(); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); + return true; +} + +static jboolean android_view_RenderNode_setOutlineNone(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + renderNode->mutateStagingProperties().mutableOutline().setNone(); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); + return true; +} + +static jboolean android_view_RenderNode_hasShadow(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().hasShadow(); +} + +static jboolean android_view_RenderNode_setSpotShadowColor(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint shadowColor) { + return SET_AND_DIRTY(setSpotShadowColor, + static_cast<SkColor>(shadowColor), RenderNode::GENERIC); +} + +static jint android_view_RenderNode_getSpotShadowColor(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().getSpotShadowColor(); +} + +static jboolean android_view_RenderNode_setAmbientShadowColor(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jint shadowColor) { + return SET_AND_DIRTY(setAmbientShadowColor, + static_cast<SkColor>(shadowColor), RenderNode::GENERIC); +} + +static jint android_view_RenderNode_getAmbientShadowColor(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().getAmbientShadowColor(); +} + +static jboolean android_view_RenderNode_setClipToOutline(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jboolean clipToOutline) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + renderNode->mutateStagingProperties().mutableOutline().setShouldClip(clipToOutline); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); + return true; +} + +static jboolean android_view_RenderNode_setRevealClip(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jboolean shouldClip, + jfloat x, jfloat y, jfloat radius) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + renderNode->mutateStagingProperties().mutableRevealClip().set( + shouldClip, x, y, radius); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); + return true; +} + +static jboolean android_view_RenderNode_setAlpha(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float alpha) { + return SET_AND_DIRTY(setAlpha, alpha, RenderNode::ALPHA); +} + +static jboolean android_view_RenderNode_setHasOverlappingRendering(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + bool hasOverlappingRendering) { + return SET_AND_DIRTY(setHasOverlappingRendering, hasOverlappingRendering, + RenderNode::GENERIC); +} + +static void android_view_RenderNode_setUsageHint(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint usageHint) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + renderNode->setUsageHint(static_cast<UsageHint>(usageHint)); +} + +static jboolean android_view_RenderNode_setElevation(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float elevation) { + return SET_AND_DIRTY(setElevation, elevation, RenderNode::Z); +} + +static jboolean android_view_RenderNode_setTranslationX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float tx) { + return SET_AND_DIRTY(setTranslationX, tx, RenderNode::TRANSLATION_X | RenderNode::X); +} + +static jboolean android_view_RenderNode_setTranslationY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float ty) { + return SET_AND_DIRTY(setTranslationY, ty, RenderNode::TRANSLATION_Y | RenderNode::Y); +} + +static jboolean android_view_RenderNode_setTranslationZ(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float tz) { + return SET_AND_DIRTY(setTranslationZ, tz, RenderNode::TRANSLATION_Z | RenderNode::Z); +} + +static jboolean android_view_RenderNode_setRotation(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float rotation) { + return SET_AND_DIRTY(setRotation, rotation, RenderNode::ROTATION); +} + +static jboolean android_view_RenderNode_setRotationX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float rx) { + return SET_AND_DIRTY(setRotationX, rx, RenderNode::ROTATION_X); +} + +static jboolean android_view_RenderNode_setRotationY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float ry) { + return SET_AND_DIRTY(setRotationY, ry, RenderNode::ROTATION_Y); +} + +static jboolean android_view_RenderNode_setScaleX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float sx) { + return SET_AND_DIRTY(setScaleX, sx, RenderNode::SCALE_X); +} + +static jboolean android_view_RenderNode_setScaleY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float sy) { + return SET_AND_DIRTY(setScaleY, sy, RenderNode::SCALE_Y); +} + +static jboolean android_view_RenderNode_setPivotX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float px) { + return SET_AND_DIRTY(setPivotX, px, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setPivotY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float py) { + return SET_AND_DIRTY(setPivotY, py, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_resetPivot(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return SET_AND_DIRTY(resetPivot, /* void */, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setCameraDistance(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, float distance) { + return SET_AND_DIRTY(setCameraDistance, distance, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_setLeft(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, int left) { + return SET_AND_DIRTY(setLeft, left, RenderNode::X); +} + +static jboolean android_view_RenderNode_setTop(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, int top) { + return SET_AND_DIRTY(setTop, top, RenderNode::Y); +} + +static jboolean android_view_RenderNode_setRight(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, int right) { + return SET_AND_DIRTY(setRight, right, RenderNode::X); +} + +static jboolean android_view_RenderNode_setBottom(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, int bottom) { + return SET_AND_DIRTY(setBottom, bottom, RenderNode::Y); +} + +static jint android_view_RenderNode_getLeft(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getLeft(); +} + +static jint android_view_RenderNode_getTop(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getTop(); +} + +static jint android_view_RenderNode_getRight(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getRight(); +} + +static jint android_view_RenderNode_getBottom(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getBottom(); +} + +static jboolean android_view_RenderNode_setLeftTopRightBottom(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + int left, int top, int right, int bottom) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + if (renderNode->mutateStagingProperties().setLeftTopRightBottom(left, top, right, bottom)) { + renderNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + return true; + } + return false; +} + +static jboolean android_view_RenderNode_offsetLeftAndRight(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint offset) { + return SET_AND_DIRTY(offsetLeftRight, offset, RenderNode::X); +} + +static jboolean android_view_RenderNode_offsetTopAndBottom(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jint offset) { + return SET_AND_DIRTY(offsetTopBottom, offset, RenderNode::Y); +} + +// ---------------------------------------------------------------------------- +// RenderProperties - getters +// ---------------------------------------------------------------------------- + +static jboolean android_view_RenderNode_hasOverlappingRendering(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().hasOverlappingRendering(); +} + +static jboolean android_view_RenderNode_getAnimationMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong outMatrixPtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + SkMatrix* outMatrix = reinterpret_cast<SkMatrix*>(outMatrixPtr); + + const SkMatrix* animationMatrix = renderNode->stagingProperties().getAnimationMatrix(); + + if (animationMatrix) { + *outMatrix = *animationMatrix; + return JNI_TRUE; + } + return JNI_FALSE; +} + +static jboolean android_view_RenderNode_getClipToBounds(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().getClipToBounds(); +} + +static jboolean android_view_RenderNode_getClipToOutline(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().getOutline().getShouldClip(); +} + +static jfloat android_view_RenderNode_getAlpha(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().getAlpha(); +} + +static jfloat android_view_RenderNode_getCameraDistance(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().getCameraDistance(); +} + +static jfloat android_view_RenderNode_getScaleX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().getScaleX(); +} + +static jfloat android_view_RenderNode_getScaleY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().getScaleY(); +} + +static jfloat android_view_RenderNode_getElevation(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().getElevation(); +} + +static jfloat android_view_RenderNode_getTranslationX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().getTranslationX(); +} + +static jfloat android_view_RenderNode_getTranslationY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().getTranslationY(); +} + +static jfloat android_view_RenderNode_getTranslationZ(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().getTranslationZ(); +} + +static jfloat android_view_RenderNode_getRotation(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().getRotation(); +} + +static jfloat android_view_RenderNode_getRotationX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().getRotationX(); +} + +static jfloat android_view_RenderNode_getRotationY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().getRotationY(); +} + +static jboolean android_view_RenderNode_isPivotExplicitlySet(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().isPivotExplicitlySet(); +} + +static jboolean android_view_RenderNode_hasIdentityMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + renderNode->mutateStagingProperties().updateMatrix(); + return !renderNode->stagingProperties().hasTransformMatrix(); +} + +static jint android_view_RenderNode_getLayerType(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return static_cast<int>(renderNode->stagingProperties().layerProperties().type()); +} + +// ---------------------------------------------------------------------------- +// RenderProperties - computed getters +// ---------------------------------------------------------------------------- + +static void getTransformMatrix(jlong renderNodePtr, jlong outMatrixPtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + SkMatrix* outMatrix = reinterpret_cast<SkMatrix*>(outMatrixPtr); + + renderNode->mutateStagingProperties().updateMatrix(); + const SkMatrix* transformMatrix = renderNode->stagingProperties().getTransformMatrix(); + + if (transformMatrix) { + *outMatrix = *transformMatrix; + } else { + outMatrix->setIdentity(); + } +} + +static void android_view_RenderNode_getTransformMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong outMatrixPtr) { + getTransformMatrix(renderNodePtr, outMatrixPtr); +} + +static void android_view_RenderNode_getInverseTransformMatrix(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, + jlong outMatrixPtr) { + // load transform matrix + getTransformMatrix(renderNodePtr, outMatrixPtr); + SkMatrix* outMatrix = reinterpret_cast<SkMatrix*>(outMatrixPtr); + + // return it inverted + if (!outMatrix->invert(outMatrix)) { + // failed to load inverse, pass back identity + outMatrix->setIdentity(); + } +} + +static jfloat android_view_RenderNode_getPivotX(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + renderNode->mutateStagingProperties().updateMatrix(); + return renderNode->stagingProperties().getPivotX(); +} + +static jfloat android_view_RenderNode_getPivotY(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + renderNode->mutateStagingProperties().updateMatrix(); + return renderNode->stagingProperties().getPivotY(); +} + +static jint android_view_RenderNode_getWidth(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getWidth(); +} + +static jint android_view_RenderNode_getHeight(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getHeight(); +} + +static jboolean android_view_RenderNode_setAllowForceDark(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jboolean allow) { + return SET_AND_DIRTY(setAllowForceDark, allow, RenderNode::GENERIC); +} + +static jboolean android_view_RenderNode_getAllowForceDark(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getAllowForceDark(); +} + +static jlong android_view_RenderNode_getUniqueId(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) { + return reinterpret_cast<RenderNode*>(renderNodePtr)->uniqueId(); +} + +// ---------------------------------------------------------------------------- +// RenderProperties - Animations +// ---------------------------------------------------------------------------- + +static void android_view_RenderNode_addAnimator(JNIEnv* env, jobject clazz, jlong renderNodePtr, + jlong animatorPtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + RenderPropertyAnimator* animator = reinterpret_cast<RenderPropertyAnimator*>(animatorPtr); + renderNode->addAnimator(animator); +} + +static void android_view_RenderNode_endAllAnimators(JNIEnv* env, jobject clazz, + jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + renderNode->animators().endAllStagingAnimators(); +} + +// ---------------------------------------------------------------------------- +// SurfaceView position callback +// ---------------------------------------------------------------------------- + +jmethodID gPositionListener_PositionChangedMethod; +jmethodID gPositionListener_PositionLostMethod; + +static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, + jlong renderNodePtr, jobject listener) { + class PositionListenerTrampoline : public RenderNode::PositionListener { + public: + PositionListenerTrampoline(JNIEnv* env, jobject listener) { + env->GetJavaVM(&mVm); + mWeakRef = env->NewWeakGlobalRef(listener); + } + + virtual ~PositionListenerTrampoline() { + jnienv()->DeleteWeakGlobalRef(mWeakRef); + mWeakRef = nullptr; + } + + virtual void onPositionUpdated(RenderNode& node, const TreeInfo& info) override { + if (CC_UNLIKELY(!mWeakRef || !info.updateWindowPositions)) return; + + Matrix4 transform; + info.damageAccumulator->computeCurrentTransform(&transform); + const RenderProperties& props = node.properties(); + uirenderer::Rect bounds(props.getWidth(), props.getHeight()); + transform.mapRect(bounds); + + if (CC_LIKELY(transform.isPureTranslate())) { + // snap/round the computed bounds, so they match the rounding behavior + // of the clear done in SurfaceView#draw(). + bounds.snapGeometryToPixelBoundaries(false); + } else { + // Conservatively round out so the punched hole (in the ZOrderOnTop = true case) + // doesn't extend beyond the other window + bounds.roundOut(); + } + + if (mPreviousPosition == bounds) { + return; + } + mPreviousPosition = bounds; + +#ifdef __ANDROID__ // Layoutlib does not support CanvasContext + incStrong(0); + auto functor = std::bind( + std::mem_fn(&PositionListenerTrampoline::doUpdatePositionAsync), this, + (jlong) info.canvasContext.getFrameNumber(), + (jint) bounds.left, (jint) bounds.top, + (jint) bounds.right, (jint) bounds.bottom); + + info.canvasContext.enqueueFrameWork(std::move(functor)); +#endif + } + + virtual void onPositionLost(RenderNode& node, const TreeInfo* info) override { + if (CC_UNLIKELY(!mWeakRef || (info && !info->updateWindowPositions))) return; + + if (mPreviousPosition.isEmpty()) { + return; + } + mPreviousPosition.setEmpty(); + + ATRACE_NAME("SurfaceView position lost"); + JNIEnv* env = jnienv(); + jobject localref = env->NewLocalRef(mWeakRef); + if (CC_UNLIKELY(!localref)) { + jnienv()->DeleteWeakGlobalRef(mWeakRef); + mWeakRef = nullptr; + return; + } +#ifdef __ANDROID__ // Layoutlib does not support CanvasContext + // TODO: Remember why this is synchronous and then make a comment + env->CallVoidMethod(localref, gPositionListener_PositionLostMethod, + info ? info->canvasContext.getFrameNumber() : 0); +#endif + env->DeleteLocalRef(localref); + } + + private: + JNIEnv* jnienv() { + JNIEnv* env; + if (mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", mVm); + } + return env; + } + + void doUpdatePositionAsync(jlong frameNumber, jint left, jint top, + jint right, jint bottom) { + ATRACE_NAME("Update SurfaceView position"); + + JNIEnv* env = jnienv(); + jobject localref = env->NewLocalRef(mWeakRef); + if (CC_UNLIKELY(!localref)) { + env->DeleteWeakGlobalRef(mWeakRef); + mWeakRef = nullptr; + } else { + env->CallVoidMethod(localref, gPositionListener_PositionChangedMethod, + frameNumber, left, top, right, bottom); + env->DeleteLocalRef(localref); + } + + // We need to release ourselves here + decStrong(0); + } + + JavaVM* mVm; + jobject mWeakRef; + uirenderer::Rect mPreviousPosition; + }; + + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + renderNode->setPositionListener(new PositionListenerTrampoline(env, listener)); +} + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "android/graphics/RenderNode"; + +static const JNINativeMethod gMethods[] = { +// ---------------------------------------------------------------------------- +// Regular JNI +// ---------------------------------------------------------------------------- + { "nCreate", "(Ljava/lang/String;)J", (void*) android_view_RenderNode_create }, + { "nGetNativeFinalizer", "()J", (void*) android_view_RenderNode_getNativeFinalizer }, + { "nOutput", "(J)V", (void*) android_view_RenderNode_output }, + { "nGetUsageSize", "(J)I", (void*) android_view_RenderNode_getUsageSize }, + { "nGetAllocatedSize", "(J)I", (void*) android_view_RenderNode_getAllocatedSize }, + { "nAddAnimator", "(JJ)V", (void*) android_view_RenderNode_addAnimator }, + { "nEndAllAnimators", "(J)V", (void*) android_view_RenderNode_endAllAnimators }, + { "nRequestPositionUpdates", "(JLandroid/graphics/RenderNode$PositionUpdateListener;)V", (void*) android_view_RenderNode_requestPositionUpdates }, + { "nSetDisplayList", "(JJ)V", (void*) android_view_RenderNode_setDisplayList }, + + +// ---------------------------------------------------------------------------- +// Fast JNI via @CriticalNative annotation in RenderNode.java +// ---------------------------------------------------------------------------- + { "nSetDisplayList", "(JJ)V", (void*) android_view_RenderNode_setDisplayList }, + + +// ---------------------------------------------------------------------------- +// Critical JNI via @CriticalNative annotation in RenderNode.java +// ---------------------------------------------------------------------------- + { "nIsValid", "(J)Z", (void*) android_view_RenderNode_isValid }, + { "nSetLayerType", "(JI)Z", (void*) android_view_RenderNode_setLayerType }, + { "nGetLayerType", "(J)I", (void*) android_view_RenderNode_getLayerType }, + { "nSetLayerPaint", "(JJ)Z", (void*) android_view_RenderNode_setLayerPaint }, + { "nSetStaticMatrix", "(JJ)Z", (void*) android_view_RenderNode_setStaticMatrix }, + { "nSetAnimationMatrix", "(JJ)Z", (void*) android_view_RenderNode_setAnimationMatrix }, + { "nGetAnimationMatrix", "(JJ)Z", (void*) android_view_RenderNode_getAnimationMatrix }, + { "nSetClipToBounds", "(JZ)Z", (void*) android_view_RenderNode_setClipToBounds }, + { "nGetClipToBounds", "(J)Z", (void*) android_view_RenderNode_getClipToBounds }, + { "nSetClipBounds", "(JIIII)Z", (void*) android_view_RenderNode_setClipBounds }, + { "nSetClipBoundsEmpty", "(J)Z", (void*) android_view_RenderNode_setClipBoundsEmpty }, + { "nSetProjectBackwards", "(JZ)Z", (void*) android_view_RenderNode_setProjectBackwards }, + { "nSetProjectionReceiver","(JZ)Z", (void*) android_view_RenderNode_setProjectionReceiver }, + + { "nSetOutlineRoundRect", "(JIIIIFF)Z", (void*) android_view_RenderNode_setOutlineRoundRect }, + { "nSetOutlinePath", "(JJF)Z", (void*) android_view_RenderNode_setOutlinePath }, + { "nSetOutlineEmpty", "(J)Z", (void*) android_view_RenderNode_setOutlineEmpty }, + { "nSetOutlineNone", "(J)Z", (void*) android_view_RenderNode_setOutlineNone }, + { "nHasShadow", "(J)Z", (void*) android_view_RenderNode_hasShadow }, + { "nSetSpotShadowColor", "(JI)Z", (void*) android_view_RenderNode_setSpotShadowColor }, + { "nGetSpotShadowColor", "(J)I", (void*) android_view_RenderNode_getSpotShadowColor }, + { "nSetAmbientShadowColor","(JI)Z", (void*) android_view_RenderNode_setAmbientShadowColor }, + { "nGetAmbientShadowColor","(J)I", (void*) android_view_RenderNode_getAmbientShadowColor }, + { "nSetClipToOutline", "(JZ)Z", (void*) android_view_RenderNode_setClipToOutline }, + { "nSetRevealClip", "(JZFFF)Z", (void*) android_view_RenderNode_setRevealClip }, + + { "nSetAlpha", "(JF)Z", (void*) android_view_RenderNode_setAlpha }, + { "nSetHasOverlappingRendering", "(JZ)Z", + (void*) android_view_RenderNode_setHasOverlappingRendering }, + { "nSetUsageHint", "(JI)V", (void*) android_view_RenderNode_setUsageHint }, + { "nSetElevation", "(JF)Z", (void*) android_view_RenderNode_setElevation }, + { "nSetTranslationX", "(JF)Z", (void*) android_view_RenderNode_setTranslationX }, + { "nSetTranslationY", "(JF)Z", (void*) android_view_RenderNode_setTranslationY }, + { "nSetTranslationZ", "(JF)Z", (void*) android_view_RenderNode_setTranslationZ }, + { "nSetRotation", "(JF)Z", (void*) android_view_RenderNode_setRotation }, + { "nSetRotationX", "(JF)Z", (void*) android_view_RenderNode_setRotationX }, + { "nSetRotationY", "(JF)Z", (void*) android_view_RenderNode_setRotationY }, + { "nSetScaleX", "(JF)Z", (void*) android_view_RenderNode_setScaleX }, + { "nSetScaleY", "(JF)Z", (void*) android_view_RenderNode_setScaleY }, + { "nSetPivotX", "(JF)Z", (void*) android_view_RenderNode_setPivotX }, + { "nSetPivotY", "(JF)Z", (void*) android_view_RenderNode_setPivotY }, + { "nResetPivot", "(J)Z", (void*) android_view_RenderNode_resetPivot }, + { "nSetCameraDistance", "(JF)Z", (void*) android_view_RenderNode_setCameraDistance }, + { "nSetLeft", "(JI)Z", (void*) android_view_RenderNode_setLeft }, + { "nSetTop", "(JI)Z", (void*) android_view_RenderNode_setTop }, + { "nSetRight", "(JI)Z", (void*) android_view_RenderNode_setRight }, + { "nSetBottom", "(JI)Z", (void*) android_view_RenderNode_setBottom }, + { "nGetLeft", "(J)I", (void*) android_view_RenderNode_getLeft }, + { "nGetTop", "(J)I", (void*) android_view_RenderNode_getTop }, + { "nGetRight", "(J)I", (void*) android_view_RenderNode_getRight }, + { "nGetBottom", "(J)I", (void*) android_view_RenderNode_getBottom }, + { "nSetLeftTopRightBottom","(JIIII)Z", (void*) android_view_RenderNode_setLeftTopRightBottom }, + { "nOffsetLeftAndRight", "(JI)Z", (void*) android_view_RenderNode_offsetLeftAndRight }, + { "nOffsetTopAndBottom", "(JI)Z", (void*) android_view_RenderNode_offsetTopAndBottom }, + + { "nHasOverlappingRendering", "(J)Z", (void*) android_view_RenderNode_hasOverlappingRendering }, + { "nGetClipToOutline", "(J)Z", (void*) android_view_RenderNode_getClipToOutline }, + { "nGetAlpha", "(J)F", (void*) android_view_RenderNode_getAlpha }, + { "nGetCameraDistance", "(J)F", (void*) android_view_RenderNode_getCameraDistance }, + { "nGetScaleX", "(J)F", (void*) android_view_RenderNode_getScaleX }, + { "nGetScaleY", "(J)F", (void*) android_view_RenderNode_getScaleY }, + { "nGetElevation", "(J)F", (void*) android_view_RenderNode_getElevation }, + { "nGetTranslationX", "(J)F", (void*) android_view_RenderNode_getTranslationX }, + { "nGetTranslationY", "(J)F", (void*) android_view_RenderNode_getTranslationY }, + { "nGetTranslationZ", "(J)F", (void*) android_view_RenderNode_getTranslationZ }, + { "nGetRotation", "(J)F", (void*) android_view_RenderNode_getRotation }, + { "nGetRotationX", "(J)F", (void*) android_view_RenderNode_getRotationX }, + { "nGetRotationY", "(J)F", (void*) android_view_RenderNode_getRotationY }, + { "nIsPivotExplicitlySet", "(J)Z", (void*) android_view_RenderNode_isPivotExplicitlySet }, + { "nHasIdentityMatrix", "(J)Z", (void*) android_view_RenderNode_hasIdentityMatrix }, + + { "nGetTransformMatrix", "(JJ)V", (void*) android_view_RenderNode_getTransformMatrix }, + { "nGetInverseTransformMatrix","(JJ)V", (void*) android_view_RenderNode_getInverseTransformMatrix }, + + { "nGetPivotX", "(J)F", (void*) android_view_RenderNode_getPivotX }, + { "nGetPivotY", "(J)F", (void*) android_view_RenderNode_getPivotY }, + { "nGetWidth", "(J)I", (void*) android_view_RenderNode_getWidth }, + { "nGetHeight", "(J)I", (void*) android_view_RenderNode_getHeight }, + { "nSetAllowForceDark", "(JZ)Z", (void*) android_view_RenderNode_setAllowForceDark }, + { "nGetAllowForceDark", "(J)Z", (void*) android_view_RenderNode_getAllowForceDark }, + { "nGetUniqueId", "(J)J", (void*) android_view_RenderNode_getUniqueId }, +}; + +int register_android_view_RenderNode(JNIEnv* env) { + jclass clazz = FindClassOrDie(env, "android/graphics/RenderNode$PositionUpdateListener"); + gPositionListener_PositionChangedMethod = GetMethodIDOrDie(env, clazz, + "positionChanged", "(JIIII)V"); + gPositionListener_PositionLostMethod = GetMethodIDOrDie(env, clazz, + "positionLost", "(J)V"); + return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); +} + +}; + diff --git a/libs/hwui/jni/android_graphics_TextureLayer.cpp b/libs/hwui/jni/android_graphics_TextureLayer.cpp new file mode 100644 index 000000000000..bd20269d3751 --- /dev/null +++ b/libs/hwui/jni/android_graphics_TextureLayer.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2014 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. + */ + +#include <android/surface_texture_jni.h> +#include "graphics_jni_helpers.h" + +#include <hwui/Paint.h> +#include <SkMatrix.h> +#include <DeferredLayerUpdater.h> + +namespace android { + +using namespace uirenderer; + +static jboolean TextureLayer_prepare(JNIEnv* env, jobject clazz, + jlong layerUpdaterPtr, jint width, jint height, jboolean isOpaque) { + DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr); + bool changed = false; + changed |= layer->setSize(width, height); + changed |= layer->setBlend(!isOpaque); + return changed; +} + +static void TextureLayer_setLayerPaint(JNIEnv* env, jobject clazz, + jlong layerUpdaterPtr, jlong paintPtr) { + DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr); + if (layer) { + Paint* paint = reinterpret_cast<Paint*>(paintPtr); + layer->setPaint(paint); + } +} + +static void TextureLayer_setTransform(JNIEnv* env, jobject clazz, + jlong layerUpdaterPtr, jlong matrixPtr) { + DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr); + SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr); + layer->setTransform(matrix); +} + +static void TextureLayer_setSurfaceTexture(JNIEnv* env, jobject clazz, + jlong layerUpdaterPtr, jobject surface) { + DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr); + ASurfaceTexture* surfaceTexture = ASurfaceTexture_fromSurfaceTexture(env, surface); + layer->setSurfaceTexture(AutoTextureRelease(surfaceTexture, &ASurfaceTexture_release)); +} + +static void TextureLayer_updateSurfaceTexture(JNIEnv* env, jobject clazz, + jlong layerUpdaterPtr) { + DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr); + layer->updateTexImage(); +} + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "android/view/TextureLayer"; + +static const JNINativeMethod gMethods[] = { + { "nPrepare", "(JIIZ)Z", (void*) TextureLayer_prepare }, + { "nSetLayerPaint", "(JJ)V", (void*) TextureLayer_setLayerPaint }, + { "nSetTransform", "(JJ)V", (void*) TextureLayer_setTransform }, + { "nSetSurfaceTexture", "(JLandroid/graphics/SurfaceTexture;)V", + (void*) TextureLayer_setSurfaceTexture }, + { "nUpdateSurfaceTexture", "(J)V", (void*) TextureLayer_updateSurfaceTexture }, +}; + +int register_android_view_TextureLayer(JNIEnv* env) { + return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); +} + +}; diff --git a/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp b/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp new file mode 100644 index 000000000000..764eff9a04be --- /dev/null +++ b/libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2014 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include <Interpolator.h> +#include <cutils/log.h> + +#include "graphics_jni_helpers.h" + +namespace android { + +using namespace uirenderer; + +static jlong createAccelerateDecelerateInterpolator(JNIEnv* env, jobject clazz) { + return reinterpret_cast<jlong>(new AccelerateDecelerateInterpolator()); +} + +static jlong createAccelerateInterpolator(JNIEnv* env, jobject clazz, jfloat factor) { + return reinterpret_cast<jlong>(new AccelerateInterpolator(factor)); +} + +static jlong createAnticipateInterpolator(JNIEnv* env, jobject clazz, jfloat tension) { + return reinterpret_cast<jlong>(new AnticipateInterpolator(tension)); +} + +static jlong createAnticipateOvershootInterpolator(JNIEnv* env, jobject clazz, jfloat tension) { + return reinterpret_cast<jlong>(new AnticipateOvershootInterpolator(tension)); +} + +static jlong createBounceInterpolator(JNIEnv* env, jobject clazz) { + return reinterpret_cast<jlong>(new BounceInterpolator()); +} + +static jlong createCycleInterpolator(JNIEnv* env, jobject clazz, jfloat cycles) { + return reinterpret_cast<jlong>(new CycleInterpolator(cycles)); +} + +static jlong createDecelerateInterpolator(JNIEnv* env, jobject clazz, jfloat factor) { + return reinterpret_cast<jlong>(new DecelerateInterpolator(factor)); +} + +static jlong createLinearInterpolator(JNIEnv* env, jobject clazz) { + return reinterpret_cast<jlong>(new LinearInterpolator()); +} + +static jlong createOvershootInterpolator(JNIEnv* env, jobject clazz, jfloat tension) { + return reinterpret_cast<jlong>(new OvershootInterpolator(tension)); +} + +static jlong createPathInterpolator(JNIEnv* env, jobject clazz, jfloatArray jX, jfloatArray jY) { + jsize lenX = env->GetArrayLength(jX); + jsize lenY = env->GetArrayLength(jY); + LOG_ALWAYS_FATAL_IF(lenX != lenY || lenX <= 0, "Invalid path interpolator, x size: %d," + " y size: %d", lenX, lenY); + std::vector<float> x(lenX); + std::vector<float> y(lenY); + env->GetFloatArrayRegion(jX, 0, lenX, x.data()); + env->GetFloatArrayRegion(jY, 0, lenX, y.data()); + + return reinterpret_cast<jlong>(new PathInterpolator(std::move(x), std::move(y))); +} + +static jlong createLutInterpolator(JNIEnv* env, jobject clazz, jfloatArray jlut) { + jsize len = env->GetArrayLength(jlut); + if (len <= 0) { + return 0; + } + float* lut = new float[len]; + env->GetFloatArrayRegion(jlut, 0, len, lut); + return reinterpret_cast<jlong>(new LUTInterpolator(lut, len)); +} + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "android/graphics/animation/NativeInterpolatorFactory"; + +static const JNINativeMethod gMethods[] = { + { "createAccelerateDecelerateInterpolator", "()J", (void*) createAccelerateDecelerateInterpolator }, + { "createAccelerateInterpolator", "(F)J", (void*) createAccelerateInterpolator }, + { "createAnticipateInterpolator", "(F)J", (void*) createAnticipateInterpolator }, + { "createAnticipateOvershootInterpolator", "(F)J", (void*) createAnticipateOvershootInterpolator }, + { "createBounceInterpolator", "()J", (void*) createBounceInterpolator }, + { "createCycleInterpolator", "(F)J", (void*) createCycleInterpolator }, + { "createDecelerateInterpolator", "(F)J", (void*) createDecelerateInterpolator }, + { "createLinearInterpolator", "()J", (void*) createLinearInterpolator }, + { "createOvershootInterpolator", "(F)J", (void*) createOvershootInterpolator }, + { "createPathInterpolator", "([F[F)J", (void*) createPathInterpolator }, + { "createLutInterpolator", "([F)J", (void*) createLutInterpolator }, +}; + +int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env) { + return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); +} + + +} // namespace android diff --git a/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp b/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp new file mode 100644 index 000000000000..c6d26f853c1d --- /dev/null +++ b/libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2013 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include <Animator.h> +#include <Interpolator.h> +#include <RenderProperties.h> + +#include "graphics_jni_helpers.h" + +namespace android { + +using namespace uirenderer; + +static struct { + jclass clazz; + + jmethodID callOnFinished; +} gRenderNodeAnimatorClassInfo; + +static JNIEnv* getEnv(JavaVM* vm) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + return 0; + } + return env; +} + +class AnimationListenerLifecycleChecker : public AnimationListener { +public: + virtual void onAnimationFinished(BaseRenderNodeAnimator* animator) { + LOG_ALWAYS_FATAL("Lifecycle failure, nStart(%p) wasn't called", animator); + } +}; + +static AnimationListenerLifecycleChecker sLifecycleChecker; + +class AnimationListenerBridge : public AnimationListener { +public: + // This holds a strong reference to a Java WeakReference<T> object. This avoids + // cyclic-references-of-doom. If you think "I know, just use NewWeakGlobalRef!" + // then you end up with basically a PhantomReference, which is totally not + // what we want. + AnimationListenerBridge(JNIEnv* env, jobject finishListener) { + mFinishListener = env->NewGlobalRef(finishListener); + env->GetJavaVM(&mJvm); + } + + virtual ~AnimationListenerBridge() { + if (mFinishListener) { + onAnimationFinished(NULL); + } + } + + virtual void onAnimationFinished(BaseRenderNodeAnimator*) { + LOG_ALWAYS_FATAL_IF(!mFinishListener, "Finished listener twice?"); + JNIEnv* env = getEnv(mJvm); + env->CallStaticVoidMethod( + gRenderNodeAnimatorClassInfo.clazz, + gRenderNodeAnimatorClassInfo.callOnFinished, + mFinishListener); + releaseJavaObject(); + } + +private: + void releaseJavaObject() { + JNIEnv* env = getEnv(mJvm); + env->DeleteGlobalRef(mFinishListener); + mFinishListener = NULL; + } + + JavaVM* mJvm; + jobject mFinishListener; +}; + +static inline RenderPropertyAnimator::RenderProperty toRenderProperty(jint property) { + LOG_ALWAYS_FATAL_IF(property < 0 || property > RenderPropertyAnimator::ALPHA, + "Invalid property %d", property); + return static_cast<RenderPropertyAnimator::RenderProperty>(property); +} + +static inline CanvasPropertyPaintAnimator::PaintField toPaintField(jint field) { + LOG_ALWAYS_FATAL_IF(field < 0 + || field > CanvasPropertyPaintAnimator::ALPHA, + "Invalid paint field %d", field); + return static_cast<CanvasPropertyPaintAnimator::PaintField>(field); +} + +static jlong createAnimator(JNIEnv* env, jobject clazz, + jint propertyRaw, jfloat finalValue) { + RenderPropertyAnimator::RenderProperty property = toRenderProperty(propertyRaw); + BaseRenderNodeAnimator* animator = new RenderPropertyAnimator(property, finalValue); + animator->setListener(&sLifecycleChecker); + return reinterpret_cast<jlong>( animator ); +} + +static jlong createCanvasPropertyFloatAnimator(JNIEnv* env, jobject clazz, + jlong canvasPropertyPtr, jfloat finalValue) { + CanvasPropertyPrimitive* canvasProperty = reinterpret_cast<CanvasPropertyPrimitive*>(canvasPropertyPtr); + BaseRenderNodeAnimator* animator = new CanvasPropertyPrimitiveAnimator(canvasProperty, finalValue); + animator->setListener(&sLifecycleChecker); + return reinterpret_cast<jlong>( animator ); +} + +static jlong createCanvasPropertyPaintAnimator(JNIEnv* env, jobject clazz, + jlong canvasPropertyPtr, jint paintFieldRaw, + jfloat finalValue) { + CanvasPropertyPaint* canvasProperty = reinterpret_cast<CanvasPropertyPaint*>(canvasPropertyPtr); + CanvasPropertyPaintAnimator::PaintField paintField = toPaintField(paintFieldRaw); + BaseRenderNodeAnimator* animator = new CanvasPropertyPaintAnimator( + canvasProperty, paintField, finalValue); + animator->setListener(&sLifecycleChecker); + return reinterpret_cast<jlong>( animator ); +} + +static jlong createRevealAnimator(JNIEnv* env, jobject clazz, + jint centerX, jint centerY, jfloat startRadius, jfloat endRadius) { + BaseRenderNodeAnimator* animator = new RevealAnimator(centerX, centerY, startRadius, endRadius); + animator->setListener(&sLifecycleChecker); + return reinterpret_cast<jlong>( animator ); +} + +static void setStartValue(JNIEnv* env, jobject clazz, jlong animatorPtr, jfloat startValue) { + BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr); + animator->setStartValue(startValue); +} + +static void setDuration(JNIEnv* env, jobject clazz, jlong animatorPtr, jlong duration) { + LOG_ALWAYS_FATAL_IF(duration < 0, "Duration cannot be negative"); + BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr); + animator->setDuration(duration); +} + +static jlong getDuration(JNIEnv* env, jobject clazz, jlong animatorPtr) { + BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr); + return static_cast<jlong>(animator->duration()); +} + +static void setStartDelay(JNIEnv* env, jobject clazz, jlong animatorPtr, jlong startDelay) { + LOG_ALWAYS_FATAL_IF(startDelay < 0, "Start delay cannot be negative"); + BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr); + animator->setStartDelay(startDelay); +} + +static void setInterpolator(JNIEnv* env, jobject clazz, jlong animatorPtr, jlong interpolatorPtr) { + BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr); + Interpolator* interpolator = reinterpret_cast<Interpolator*>(interpolatorPtr); + animator->setInterpolator(interpolator); +} + +static void setAllowRunningAsync(JNIEnv* env, jobject clazz, jlong animatorPtr, jboolean mayRunAsync) { + BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr); + animator->setAllowRunningAsync(mayRunAsync); +} + +static void setListener(JNIEnv* env, jobject clazz, jlong animatorPtr, jobject finishListener) { + BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr); + animator->setListener(new AnimationListenerBridge(env, finishListener)); +} + +static void start(JNIEnv* env, jobject clazz, jlong animatorPtr) { + BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr); + animator->start(); +} + +static void end(JNIEnv* env, jobject clazz, jlong animatorPtr) { + BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr); + animator->cancel(); +} + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "android/graphics/animation/RenderNodeAnimator"; + +static const JNINativeMethod gMethods[] = { + { "nCreateAnimator", "(IF)J", (void*) createAnimator }, + { "nCreateCanvasPropertyFloatAnimator", "(JF)J", (void*) createCanvasPropertyFloatAnimator }, + { "nCreateCanvasPropertyPaintAnimator", "(JIF)J", (void*) createCanvasPropertyPaintAnimator }, + { "nCreateRevealAnimator", "(IIFF)J", (void*) createRevealAnimator }, + { "nSetStartValue", "(JF)V", (void*) setStartValue }, + { "nSetDuration", "(JJ)V", (void*) setDuration }, + { "nGetDuration", "(J)J", (void*) getDuration }, + { "nSetStartDelay", "(JJ)V", (void*) setStartDelay }, + { "nSetInterpolator", "(JJ)V", (void*) setInterpolator }, + { "nSetAllowRunningAsync", "(JZ)V", (void*) setAllowRunningAsync }, + { "nSetListener", "(JLandroid/graphics/animation/RenderNodeAnimator;)V", (void*) setListener}, + { "nStart", "(J)V", (void*) start}, + { "nEnd", "(J)V", (void*) end }, +}; + +int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env) { + sLifecycleChecker.incStrong(0); + gRenderNodeAnimatorClassInfo.clazz = FindClassOrDie(env, kClassPathName); + gRenderNodeAnimatorClassInfo.clazz = MakeGlobalRefOrDie(env, + gRenderNodeAnimatorClassInfo.clazz); + + gRenderNodeAnimatorClassInfo.callOnFinished = GetStaticMethodIDOrDie( + env, gRenderNodeAnimatorClassInfo.clazz, "callOnFinished", + "(Landroid/graphics/animation/RenderNodeAnimator;)V"); + + return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); +} + + +} // namespace android diff --git a/libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp b/libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp new file mode 100644 index 000000000000..b3121e7b0373 --- /dev/null +++ b/libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2016 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. + */ + +#include "android/log.h" + +#include "GraphicsJNI.h" + +#include "Animator.h" +#include "Interpolator.h" +#include "PropertyValuesAnimatorSet.h" +#include "PropertyValuesHolder.h" +#include "VectorDrawable.h" + +namespace android { +using namespace uirenderer; +using namespace VectorDrawable; + +static struct { + jclass clazz; + jmethodID callOnFinished; +} gVectorDrawableAnimatorClassInfo; + +static JNIEnv* getEnv(JavaVM* vm) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + return 0; + } + return env; +} + +static AnimationListener* createAnimationListener(JNIEnv* env, jobject finishListener, jint id) { + class AnimationListenerBridge : public AnimationListener { + public: + AnimationListenerBridge(JNIEnv* env, jobject finishListener, jint id) { + mFinishListener = env->NewGlobalRef(finishListener); + env->GetJavaVM(&mJvm); + mId = id; + } + + virtual ~AnimationListenerBridge() { + if (mFinishListener) { + onAnimationFinished(NULL); + } + } + + virtual void onAnimationFinished(BaseRenderNodeAnimator*) { + LOG_ALWAYS_FATAL_IF(!mFinishListener, "Finished listener twice?"); + JNIEnv* env = getEnv(mJvm); + env->CallStaticVoidMethod( + gVectorDrawableAnimatorClassInfo.clazz, + gVectorDrawableAnimatorClassInfo.callOnFinished, + mFinishListener, mId); + releaseJavaObject(); + } + + private: + void releaseJavaObject() { + JNIEnv* env = getEnv(mJvm); + env->DeleteGlobalRef(mFinishListener); + mFinishListener = NULL; + } + + JavaVM* mJvm; + jobject mFinishListener; + jint mId; + }; + return new AnimationListenerBridge(env, finishListener, id); +} + +static void addAnimator(JNIEnv*, jobject, jlong animatorSetPtr, jlong propertyHolderPtr, + jlong interpolatorPtr, jlong startDelay, jlong duration, jint repeatCount, + jint repeatMode) { + PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr); + PropertyValuesHolder* holder = reinterpret_cast<PropertyValuesHolder*>(propertyHolderPtr); + Interpolator* interpolator = reinterpret_cast<Interpolator*>(interpolatorPtr); + RepeatMode mode = static_cast<RepeatMode>(repeatMode); + set->addPropertyAnimator(holder, interpolator, startDelay, duration, repeatCount, mode); +} + +static jlong createAnimatorSet(JNIEnv*, jobject) { + PropertyValuesAnimatorSet* animatorSet = new PropertyValuesAnimatorSet(); + return reinterpret_cast<jlong>(animatorSet); +} + +static void setVectorDrawableTarget(JNIEnv*, jobject,jlong animatorPtr, jlong vectorDrawablePtr) { + VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(vectorDrawablePtr); + PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorPtr); + set->setVectorDrawable(tree); +} + +static jlong createGroupPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jint propertyId, + jfloat startValue, jfloat endValue) { + VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(nativePtr); + GroupPropertyValuesHolder* newHolder = new GroupPropertyValuesHolder(group, propertyId, + startValue, endValue); + return reinterpret_cast<jlong>(newHolder); +} + +static jlong createPathDataPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jlong startValuePtr, + jlong endValuePtr) { + VectorDrawable::Path* path = reinterpret_cast<VectorDrawable::Path*>(nativePtr); + PathData* startData = reinterpret_cast<PathData*>(startValuePtr); + PathData* endData = reinterpret_cast<PathData*>(endValuePtr); + PathDataPropertyValuesHolder* newHolder = new PathDataPropertyValuesHolder(path, + startData, endData); + return reinterpret_cast<jlong>(newHolder); +} + +static jlong createPathColorPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jint propertyId, + int startValue, jint endValue) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(nativePtr); + FullPathColorPropertyValuesHolder* newHolder = new FullPathColorPropertyValuesHolder(fullPath, + propertyId, startValue, endValue); + return reinterpret_cast<jlong>(newHolder); +} + +static jlong createPathPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jint propertyId, + float startValue, jfloat endValue) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(nativePtr); + FullPathPropertyValuesHolder* newHolder = new FullPathPropertyValuesHolder(fullPath, + propertyId, startValue, endValue); + return reinterpret_cast<jlong>(newHolder); +} + +static jlong createRootAlphaPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jfloat startValue, + float endValue) { + VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(nativePtr); + RootAlphaPropertyValuesHolder* newHolder = new RootAlphaPropertyValuesHolder(tree, + startValue, endValue); + return reinterpret_cast<jlong>(newHolder); +} +static void setFloatPropertyHolderData(JNIEnv* env, jobject, jlong propertyHolderPtr, + jfloatArray srcData, jint length) { + jfloat* propertyData = env->GetFloatArrayElements(srcData, nullptr); + PropertyValuesHolderImpl<float>* holder = + reinterpret_cast<PropertyValuesHolderImpl<float>*>(propertyHolderPtr); + holder->setPropertyDataSource(propertyData, length); + env->ReleaseFloatArrayElements(srcData, propertyData, JNI_ABORT); +} + +static void setIntPropertyHolderData(JNIEnv* env, jobject, jlong propertyHolderPtr, + jintArray srcData, jint length) { + jint* propertyData = env->GetIntArrayElements(srcData, nullptr); + PropertyValuesHolderImpl<int>* holder = + reinterpret_cast<PropertyValuesHolderImpl<int>*>(propertyHolderPtr); + holder->setPropertyDataSource(propertyData, length); + env->ReleaseIntArrayElements(srcData, propertyData, JNI_ABORT); +} + +static void start(JNIEnv* env, jobject, jlong animatorSetPtr, jobject finishListener, jint id) { + PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr); + AnimationListener* listener = createAnimationListener(env, finishListener, id); + set->start(listener); +} + +static void reverse(JNIEnv* env, jobject, jlong animatorSetPtr, jobject finishListener, jint id) { + PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr); + AnimationListener* listener = createAnimationListener(env, finishListener, id); + set->reverse(listener); +} + +static void end(JNIEnv*, jobject, jlong animatorSetPtr) { + PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr); + set->end(); +} + +static void reset(JNIEnv*, jobject, jlong animatorSetPtr) { + PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr); + set->reset(); +} + +static const JNINativeMethod gMethods[] = { + {"nCreateAnimatorSet", "()J", (void*)createAnimatorSet}, + {"nSetVectorDrawableTarget", "(JJ)V", (void*)setVectorDrawableTarget}, + {"nAddAnimator", "(JJJJJII)V", (void*)addAnimator}, + {"nSetPropertyHolderData", "(J[FI)V", (void*)setFloatPropertyHolderData}, + {"nSetPropertyHolderData", "(J[II)V", (void*)setIntPropertyHolderData}, + {"nStart", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V", (void*)start}, + {"nReverse", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V", (void*)reverse}, + + // ------------- @FastNative ------------------- + + {"nCreateGroupPropertyHolder", "(JIFF)J", (void*)createGroupPropertyHolder}, + {"nCreatePathDataPropertyHolder", "(JJJ)J", (void*)createPathDataPropertyHolder}, + {"nCreatePathColorPropertyHolder", "(JIII)J", (void*)createPathColorPropertyHolder}, + {"nCreatePathPropertyHolder", "(JIFF)J", (void*)createPathPropertyHolder}, + {"nCreateRootAlphaPropertyHolder", "(JFF)J", (void*)createRootAlphaPropertyHolder}, + {"nEnd", "(J)V", (void*)end}, + {"nReset", "(J)V", (void*)reset}, +}; + +const char* const kClassPathName = "android/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT"; +int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env) { + gVectorDrawableAnimatorClassInfo.clazz = FindClassOrDie(env, kClassPathName); + gVectorDrawableAnimatorClassInfo.clazz = MakeGlobalRefOrDie(env, + gVectorDrawableAnimatorClassInfo.clazz); + + gVectorDrawableAnimatorClassInfo.callOnFinished = GetStaticMethodIDOrDie( + env, gVectorDrawableAnimatorClassInfo.clazz, "callOnFinished", + "(Landroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V"); + return RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedVectorDrawable", + gMethods, NELEM(gMethods)); +} + +}; // namespace android diff --git a/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp new file mode 100644 index 000000000000..9cffceb308c8 --- /dev/null +++ b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp @@ -0,0 +1,423 @@ +/* + * Copyright (C) 2015 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. + */ + +#include "GraphicsJNI.h" + +#include "PathParser.h" +#include "VectorDrawable.h" + +#include <hwui/Paint.h> + +namespace android { +using namespace uirenderer; +using namespace uirenderer::VectorDrawable; + +/** + * VectorDrawable's pre-draw construction. + */ +static jlong createTree(JNIEnv*, jobject, jlong groupPtr) { + VectorDrawable::Group* rootGroup = reinterpret_cast<VectorDrawable::Group*>(groupPtr); + VectorDrawable::Tree* tree = new VectorDrawable::Tree(rootGroup); + return reinterpret_cast<jlong>(tree); +} + +static jlong createTreeFromCopy(JNIEnv*, jobject, jlong treePtr, jlong groupPtr) { + VectorDrawable::Group* rootGroup = reinterpret_cast<VectorDrawable::Group*>(groupPtr); + VectorDrawable::Tree* treeToCopy = reinterpret_cast<VectorDrawable::Tree*>(treePtr); + VectorDrawable::Tree* tree = new VectorDrawable::Tree(treeToCopy, rootGroup); + return reinterpret_cast<jlong>(tree); +} + +static jlong createEmptyFullPath(JNIEnv*, jobject) { + VectorDrawable::FullPath* newPath = new VectorDrawable::FullPath(); + return reinterpret_cast<jlong>(newPath); +} + +static jlong createFullPath(JNIEnv*, jobject, jlong srcFullPathPtr) { + VectorDrawable::FullPath* srcFullPath = + reinterpret_cast<VectorDrawable::FullPath*>(srcFullPathPtr); + VectorDrawable::FullPath* newPath = new VectorDrawable::FullPath(*srcFullPath); + return reinterpret_cast<jlong>(newPath); +} + +static jlong createEmptyClipPath(JNIEnv*, jobject) { + VectorDrawable::ClipPath* newPath = new VectorDrawable::ClipPath(); + return reinterpret_cast<jlong>(newPath); +} + +static jlong createClipPath(JNIEnv*, jobject, jlong srcClipPathPtr) { + VectorDrawable::ClipPath* srcClipPath = + reinterpret_cast<VectorDrawable::ClipPath*>(srcClipPathPtr); + VectorDrawable::ClipPath* newPath = new VectorDrawable::ClipPath(*srcClipPath); + return reinterpret_cast<jlong>(newPath); +} + +static jlong createEmptyGroup(JNIEnv*, jobject) { + VectorDrawable::Group* newGroup = new VectorDrawable::Group(); + return reinterpret_cast<jlong>(newGroup); +} + +static jlong createGroup(JNIEnv*, jobject, jlong srcGroupPtr) { + VectorDrawable::Group* srcGroup = reinterpret_cast<VectorDrawable::Group*>(srcGroupPtr); + VectorDrawable::Group* newGroup = new VectorDrawable::Group(*srcGroup); + return reinterpret_cast<jlong>(newGroup); +} + +static void setNodeName(JNIEnv* env, jobject, jlong nodePtr, jstring nameStr) { + VectorDrawable::Node* node = reinterpret_cast<VectorDrawable::Node*>(nodePtr); + const char* nodeName = env->GetStringUTFChars(nameStr, NULL); + node->setName(nodeName); + env->ReleaseStringUTFChars(nameStr, nodeName); +} + +static void addChild(JNIEnv*, jobject, jlong groupPtr, jlong childPtr) { + VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr); + VectorDrawable::Node* child = reinterpret_cast<VectorDrawable::Node*>(childPtr); + group->addChild(child); +} + +static void setAllowCaching(JNIEnv*, jobject, jlong treePtr, jboolean allowCaching) { + VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr); + tree->setAllowCaching(allowCaching); +} + +static void setAntiAlias(JNIEnv*, jobject, jlong treePtr, jboolean aa) { + VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr); + tree->setAntiAlias(aa); +} + +/** + * Draw + */ +static jint draw(JNIEnv* env, jobject, jlong treePtr, jlong canvasPtr, + jlong colorFilterPtr, jobject jrect, jboolean needsMirroring, jboolean canReuseCache) { + VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr); + Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr); + SkRect rect; + GraphicsJNI::jrect_to_rect(env, jrect, &rect); + SkColorFilter* colorFilter = reinterpret_cast<SkColorFilter*>(colorFilterPtr); + return tree->draw(canvas, colorFilter, rect, needsMirroring, canReuseCache); +} + +/** + * Setters and getters for updating staging properties that can happen both pre-draw and post draw. + */ +static void setTreeViewportSize(JNIEnv*, jobject, jlong treePtr, + jfloat viewportWidth, jfloat viewportHeight) { + VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr); + tree->mutateStagingProperties()->setViewportSize(viewportWidth, viewportHeight); +} + +static jboolean setRootAlpha(JNIEnv*, jobject, jlong treePtr, jfloat alpha) { + VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr); + return tree->mutateStagingProperties()->setRootAlpha(alpha); +} + +static jfloat getRootAlpha(JNIEnv*, jobject, jlong treePtr) { + VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr); + return tree->stagingProperties().getRootAlpha(); +} + +static void updateFullPathPropertiesAndStrokeStyles(JNIEnv*, jobject, jlong fullPathPtr, + jfloat strokeWidth, jint strokeColor, jfloat strokeAlpha, jint fillColor, jfloat fillAlpha, + jfloat trimPathStart, jfloat trimPathEnd, jfloat trimPathOffset, jfloat strokeMiterLimit, + jint strokeLineCap, jint strokeLineJoin, jint fillType) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr); + fullPath->mutateStagingProperties()->updateProperties(strokeWidth, strokeColor, strokeAlpha, + fillColor, fillAlpha, trimPathStart, trimPathEnd, trimPathOffset, strokeMiterLimit, + strokeLineCap, strokeLineJoin, fillType); +} + +static void updateFullPathFillGradient(JNIEnv*, jobject, jlong pathPtr, jlong fillGradientPtr) { + VectorDrawable::FullPath* path = reinterpret_cast<VectorDrawable::FullPath*>(pathPtr); + SkShader* fillShader = reinterpret_cast<SkShader*>(fillGradientPtr); + path->mutateStagingProperties()->setFillGradient(fillShader); +} + +static void updateFullPathStrokeGradient(JNIEnv*, jobject, jlong pathPtr, jlong strokeGradientPtr) { + VectorDrawable::FullPath* path = reinterpret_cast<VectorDrawable::FullPath*>(pathPtr); + SkShader* strokeShader = reinterpret_cast<SkShader*>(strokeGradientPtr); + path->mutateStagingProperties()->setStrokeGradient(strokeShader); +} + +static jboolean getFullPathProperties(JNIEnv* env, jobject, jlong fullPathPtr, + jbyteArray outProperties, jint length) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr); + int8_t pathProperties[length]; + bool success = fullPath->stagingProperties()->copyProperties(pathProperties, length); + env->SetByteArrayRegion(outProperties, 0, length, reinterpret_cast<int8_t*>(&pathProperties)); + return success; +} + +static jboolean getGroupProperties(JNIEnv* env, jobject, jlong groupPtr, + jfloatArray outProperties, jint length) { + VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr); + float groupProperties[length]; + bool success = group->stagingProperties()->copyProperties(groupProperties, length); + env->SetFloatArrayRegion(outProperties, 0, length, reinterpret_cast<float*>(&groupProperties)); + return success; +} + +static void updateGroupProperties(JNIEnv*, jobject, jlong groupPtr, jfloat rotate, jfloat pivotX, + jfloat pivotY, jfloat scaleX, jfloat scaleY, jfloat translateX, jfloat translateY) { + VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr); + group->mutateStagingProperties()->updateProperties(rotate, pivotX, pivotY, scaleX, scaleY, + translateX, translateY); +} + +static void setPathString(JNIEnv* env, jobject, jlong pathPtr, jstring inputStr, + jint stringLength) { + VectorDrawable::Path* path = reinterpret_cast<VectorDrawable::Path*>(pathPtr); + const char* pathString = env->GetStringUTFChars(inputStr, NULL); + + PathParser::ParseResult result; + PathData data; + PathParser::getPathDataFromAsciiString(&data, &result, pathString, stringLength); + if (result.failureOccurred) { + doThrowIAE(env, result.failureMessage.c_str()); + } + path->mutateStagingProperties()->setData(data); + env->ReleaseStringUTFChars(inputStr, pathString); +} + +/** + * Setters and getters that should only be called from animation thread for animation purpose. + */ +static jfloat getRotation(JNIEnv*, jobject, jlong groupPtr) { + VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr); + return group->stagingProperties()->getRotation(); +} + +static void setRotation(JNIEnv*, jobject, jlong groupPtr, jfloat rotation) { + VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr); + group->mutateStagingProperties()->setRotation(rotation); +} + +static jfloat getPivotX(JNIEnv*, jobject, jlong groupPtr) { + VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr); + return group->stagingProperties()->getPivotX(); +} + +static void setPivotX(JNIEnv*, jobject, jlong groupPtr, jfloat pivotX) { + VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr); + group->mutateStagingProperties()->setPivotX(pivotX); +} + +static jfloat getPivotY(JNIEnv*, jobject, jlong groupPtr) { + VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr); + return group->stagingProperties()->getPivotY(); +} + +static void setPivotY(JNIEnv*, jobject, jlong groupPtr, jfloat pivotY) { + VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr); + group->mutateStagingProperties()->setPivotY(pivotY); +} + +static jfloat getScaleX(JNIEnv*, jobject, jlong groupPtr) { + VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr); + return group->stagingProperties()->getScaleX(); +} + +static void setScaleX(JNIEnv*, jobject, jlong groupPtr, jfloat scaleX) { + VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr); + group->mutateStagingProperties()->setScaleX(scaleX); +} + +static jfloat getScaleY(JNIEnv*, jobject, jlong groupPtr) { + VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr); + return group->stagingProperties()->getScaleY(); +} + +static void setScaleY(JNIEnv*, jobject, jlong groupPtr, jfloat scaleY) { + VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr); + group->mutateStagingProperties()->setScaleY(scaleY); +} + +static jfloat getTranslateX(JNIEnv*, jobject, jlong groupPtr) { + VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr); + return group->stagingProperties()->getTranslateX(); +} + +static void setTranslateX(JNIEnv*, jobject, jlong groupPtr, jfloat translateX) { + VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr); + group->mutateStagingProperties()->setTranslateX(translateX); +} + +static jfloat getTranslateY(JNIEnv*, jobject, jlong groupPtr) { + VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr); + return group->stagingProperties()->getTranslateY(); +} + +static void setTranslateY(JNIEnv*, jobject, jlong groupPtr, jfloat translateY) { + VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr); + group->mutateStagingProperties()->setTranslateY(translateY); +} + +static void setPathData(JNIEnv*, jobject, jlong pathPtr, jlong pathDataPtr) { + VectorDrawable::Path* path = reinterpret_cast<VectorDrawable::Path*>(pathPtr); + PathData* pathData = reinterpret_cast<PathData*>(pathDataPtr); + path->mutateStagingProperties()->setData(*pathData); +} + +static jfloat getStrokeWidth(JNIEnv*, jobject, jlong fullPathPtr) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr); + return fullPath->stagingProperties()->getStrokeWidth(); +} + +static void setStrokeWidth(JNIEnv*, jobject, jlong fullPathPtr, jfloat strokeWidth) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr); + fullPath->mutateStagingProperties()->setStrokeWidth(strokeWidth); +} + +static jint getStrokeColor(JNIEnv*, jobject, jlong fullPathPtr) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr); + return fullPath->stagingProperties()->getStrokeColor(); +} + +static void setStrokeColor(JNIEnv*, jobject, jlong fullPathPtr, jint strokeColor) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr); + fullPath->mutateStagingProperties()->setStrokeColor(strokeColor); +} + +static jfloat getStrokeAlpha(JNIEnv*, jobject, jlong fullPathPtr) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr); + return fullPath->stagingProperties()->getStrokeAlpha(); +} + +static void setStrokeAlpha(JNIEnv*, jobject, jlong fullPathPtr, jfloat strokeAlpha) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr); + fullPath->mutateStagingProperties()->setStrokeAlpha(strokeAlpha); +} + +static jint getFillColor(JNIEnv*, jobject, jlong fullPathPtr) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr); + return fullPath->stagingProperties()->getFillColor(); +} + +static void setFillColor(JNIEnv*, jobject, jlong fullPathPtr, jint fillColor) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr); + fullPath->mutateStagingProperties()->setFillColor(fillColor); +} + +static jfloat getFillAlpha(JNIEnv*, jobject, jlong fullPathPtr) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr); + return fullPath->stagingProperties()->getFillAlpha(); +} + +static void setFillAlpha(JNIEnv*, jobject, jlong fullPathPtr, jfloat fillAlpha) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr); + fullPath->mutateStagingProperties()->setFillAlpha(fillAlpha); +} + +static jfloat getTrimPathStart(JNIEnv*, jobject, jlong fullPathPtr) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr); + return fullPath->stagingProperties()->getTrimPathStart(); +} + +static void setTrimPathStart(JNIEnv*, jobject, jlong fullPathPtr, jfloat trimPathStart) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr); + fullPath->mutateStagingProperties()->setTrimPathStart(trimPathStart); +} + +static jfloat getTrimPathEnd(JNIEnv*, jobject, jlong fullPathPtr) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr); + return fullPath->stagingProperties()->getTrimPathEnd(); +} + +static void setTrimPathEnd(JNIEnv*, jobject, jlong fullPathPtr, jfloat trimPathEnd) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr); + fullPath->mutateStagingProperties()->setTrimPathEnd(trimPathEnd); +} + +static jfloat getTrimPathOffset(JNIEnv*, jobject, jlong fullPathPtr) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr); + return fullPath->stagingProperties()->getTrimPathOffset(); +} + +static void setTrimPathOffset(JNIEnv*, jobject, jlong fullPathPtr, jfloat trimPathOffset) { + VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr); + fullPath->mutateStagingProperties()->setTrimPathOffset(trimPathOffset); +} + +static const JNINativeMethod gMethods[] = { + {"nDraw", "(JJJLandroid/graphics/Rect;ZZ)I", (void*)draw}, + {"nGetFullPathProperties", "(J[BI)Z", (void*)getFullPathProperties}, + {"nGetGroupProperties", "(J[FI)Z", (void*)getGroupProperties}, + {"nSetPathString", "(JLjava/lang/String;I)V", (void*)setPathString}, + {"nSetName", "(JLjava/lang/String;)V", (void*)setNodeName}, + + // ------------- @FastNative ---------------- + + {"nCreateTree", "(J)J", (void*)createTree}, + {"nCreateTreeFromCopy", "(JJ)J", (void*)createTreeFromCopy}, + {"nSetRendererViewportSize", "(JFF)V", (void*)setTreeViewportSize}, + {"nSetRootAlpha", "(JF)Z", (void*)setRootAlpha}, + {"nGetRootAlpha", "(J)F", (void*)getRootAlpha}, + {"nSetAntiAlias", "(JZ)V", (void*)setAntiAlias}, + {"nSetAllowCaching", "(JZ)V", (void*)setAllowCaching}, + + {"nCreateFullPath", "()J", (void*)createEmptyFullPath}, + {"nCreateFullPath", "(J)J", (void*)createFullPath}, + {"nUpdateFullPathProperties", "(JFIFIFFFFFIII)V", (void*)updateFullPathPropertiesAndStrokeStyles}, + {"nUpdateFullPathFillGradient", "(JJ)V", (void*)updateFullPathFillGradient}, + {"nUpdateFullPathStrokeGradient", "(JJ)V", (void*)updateFullPathStrokeGradient}, + + {"nCreateClipPath", "()J", (void*)createEmptyClipPath}, + {"nCreateClipPath", "(J)J", (void*)createClipPath}, + {"nCreateGroup", "()J", (void*)createEmptyGroup}, + {"nCreateGroup", "(J)J", (void*)createGroup}, + {"nUpdateGroupProperties", "(JFFFFFFF)V", (void*)updateGroupProperties}, + + {"nAddChild", "(JJ)V", (void*)addChild}, + {"nGetRotation", "(J)F", (void*)getRotation}, + {"nSetRotation", "(JF)V", (void*)setRotation}, + {"nGetPivotX", "(J)F", (void*)getPivotX}, + {"nSetPivotX", "(JF)V", (void*)setPivotX}, + {"nGetPivotY", "(J)F", (void*)getPivotY}, + {"nSetPivotY", "(JF)V", (void*)setPivotY}, + {"nGetScaleX", "(J)F", (void*)getScaleX}, + {"nSetScaleX", "(JF)V", (void*)setScaleX}, + {"nGetScaleY", "(J)F", (void*)getScaleY}, + {"nSetScaleY", "(JF)V", (void*)setScaleY}, + {"nGetTranslateX", "(J)F", (void*)getTranslateX}, + {"nSetTranslateX", "(JF)V", (void*)setTranslateX}, + {"nGetTranslateY", "(J)F", (void*)getTranslateY}, + {"nSetTranslateY", "(JF)V", (void*)setTranslateY}, + + {"nSetPathData", "(JJ)V", (void*)setPathData}, + {"nGetStrokeWidth", "(J)F", (void*)getStrokeWidth}, + {"nSetStrokeWidth", "(JF)V", (void*)setStrokeWidth}, + {"nGetStrokeColor", "(J)I", (void*)getStrokeColor}, + {"nSetStrokeColor", "(JI)V", (void*)setStrokeColor}, + {"nGetStrokeAlpha", "(J)F", (void*)getStrokeAlpha}, + {"nSetStrokeAlpha", "(JF)V", (void*)setStrokeAlpha}, + {"nGetFillColor", "(J)I", (void*)getFillColor}, + {"nSetFillColor", "(JI)V", (void*)setFillColor}, + {"nGetFillAlpha", "(J)F", (void*)getFillAlpha}, + {"nSetFillAlpha", "(JF)V", (void*)setFillAlpha}, + {"nGetTrimPathStart", "(J)F", (void*)getTrimPathStart}, + {"nSetTrimPathStart", "(JF)V", (void*)setTrimPathStart}, + {"nGetTrimPathEnd", "(J)F", (void*)getTrimPathEnd}, + {"nSetTrimPathEnd", "(JF)V", (void*)setTrimPathEnd}, + {"nGetTrimPathOffset", "(J)F", (void*)getTrimPathOffset}, + {"nSetTrimPathOffset", "(JF)V", (void*)setTrimPathOffset}, +}; + +int register_android_graphics_drawable_VectorDrawable(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/drawable/VectorDrawable", gMethods, NELEM(gMethods)); +} + +}; // namespace android diff --git a/libs/hwui/jni/android_nio_utils.cpp b/libs/hwui/jni/android_nio_utils.cpp new file mode 100644 index 000000000000..0663821a5d89 --- /dev/null +++ b/libs/hwui/jni/android_nio_utils.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 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. + */ + +#include "android_nio_utils.h" + +#include <nativehelper/JNIPlatformHelp.h> + +namespace android { + +AutoBufferPointer::AutoBufferPointer(JNIEnv* env, jobject nioBuffer, jboolean commit) + : fEnv(env), fCommit(commit) { + jlong pointer = jniGetNioBufferPointer(fEnv, nioBuffer); + if (pointer != 0L) { + // Buffer is backed by a direct buffer. + fArray = nullptr; + fElements = nullptr; + fPointer = reinterpret_cast<void*>(pointer); + } else { + // Buffer is backed by a managed array. + jint byteOffset = jniGetNioBufferBaseArrayOffset(fEnv, nioBuffer); + fArray = jniGetNioBufferBaseArray(fEnv, nioBuffer); + fElements = fEnv->GetPrimitiveArrayCritical(fArray, /* isCopy= */ nullptr); + fPointer = reinterpret_cast<void*>(reinterpret_cast<char*>(fElements) + byteOffset); + } +} + +AutoBufferPointer::~AutoBufferPointer() { + if (nullptr != fArray) { + fEnv->ReleasePrimitiveArrayCritical(fArray, fElements, fCommit ? 0 : JNI_ABORT); + } +} + +} // namespace android diff --git a/libs/hwui/jni/android_nio_utils.h b/libs/hwui/jni/android_nio_utils.h new file mode 100644 index 000000000000..4760d9ca8107 --- /dev/null +++ b/libs/hwui/jni/android_nio_utils.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef _ANDROID_NIO_UTILS_H_ +#define _ANDROID_NIO_UTILS_H_ + +#include <jni.h> + +#include <cstddef> + +namespace android { + +/** + * Class providing scoped access to the memory backing a java.nio.Buffer instance. + * + * Instances of this class should only be allocated on the stack as heap allocation is not + * supported. + * + * Instances of this class do not create any global references for performance reasons. + */ +class AutoBufferPointer final { +public: + /** Constructor for an AutoBufferPointer instance. + * + * @param env The current JNI env + * @param nioBuffer Instance of a java.nio.Buffer whose memory will be accessed. + * @param commit JNI_TRUE if the underlying memory will be updated and should be + * copied back to the managed heap. JNI_FALSE if the data will + * not be modified or the modifications may be discarded. + * + * The commit parameter is only applicable if the buffer is backed by a managed heap + * array and the runtime had to provide a copy of the data rather than the original data. + */ + AutoBufferPointer(JNIEnv* env, jobject nioBuffer, jboolean commit); + + /** Destructor for an AutoBufferPointer instance. + * + * Releases critical managed heap array pointer if acquired. + */ + ~AutoBufferPointer(); + + /** + * Returns a pointer to the current position of the buffer provided to the constructor. This + * pointer is only valid whilst the AutoBufferPointer instance remains in scope. + */ + void* pointer() const { return fPointer; } + +private: + JNIEnv* const fEnv; + void* fPointer; // Pointer to current buffer position when constructed. + void* fElements; // Pointer to array element 0 (null if buffer is direct, may be + // within fArray or point to a copy of the array). + jarray fArray; // Pointer to array on managed heap. + const jboolean fCommit; // Flag to commit data to source (when fElements is a copy of fArray). + + // Unsupported constructors and operators. + AutoBufferPointer() = delete; + AutoBufferPointer(AutoBufferPointer&) = delete; + AutoBufferPointer& operator=(AutoBufferPointer&) = delete; + static void* operator new(size_t); + static void* operator new[](size_t); + static void* operator new(size_t, void*); + static void* operator new[](size_t, void*); + static void operator delete(void*, size_t); + static void operator delete[](void*, size_t); +}; + +} /* namespace android */ + +#endif // _ANDROID_NIO_UTILS_H_ diff --git a/libs/hwui/jni/android_util_PathParser.cpp b/libs/hwui/jni/android_util_PathParser.cpp new file mode 100644 index 000000000000..72995efb1c21 --- /dev/null +++ b/libs/hwui/jni/android_util_PathParser.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2015 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. + */ + +#include "GraphicsJNI.h" + +#include <PathParser.h> +#include <SkPath.h> +#include <utils/VectorDrawableUtils.h> + +#include <android/log.h> + +namespace android { + +using namespace uirenderer; + +static void parseStringForPath(JNIEnv* env, jobject, jlong skPathHandle, jstring inputPathStr, + jint strLength) { + const char* pathString = env->GetStringUTFChars(inputPathStr, NULL); + SkPath* skPath = reinterpret_cast<SkPath*>(skPathHandle); + + PathParser::ParseResult result; + PathParser::parseAsciiStringForSkPath(skPath, &result, pathString, strLength); + env->ReleaseStringUTFChars(inputPathStr, pathString); + if (result.failureOccurred) { + doThrowIAE(env, result.failureMessage.c_str()); + } +} + +static jlong createEmptyPathData(JNIEnv*, jobject) { + PathData* pathData = new PathData(); + return reinterpret_cast<jlong>(pathData); +} + +static jlong createPathData(JNIEnv*, jobject, jlong pathDataPtr) { + PathData* pathData = reinterpret_cast<PathData*>(pathDataPtr); + PathData* newPathData = new PathData(*pathData); + return reinterpret_cast<jlong>(newPathData); +} + +static jlong createPathDataFromStringPath(JNIEnv* env, jobject, jstring inputStr, jint strLength) { + const char* pathString = env->GetStringUTFChars(inputStr, NULL); + PathData* pathData = new PathData(); + PathParser::ParseResult result; + PathParser::getPathDataFromAsciiString(pathData, &result, pathString, strLength); + env->ReleaseStringUTFChars(inputStr, pathString); + if (!result.failureOccurred) { + return reinterpret_cast<jlong>(pathData); + } else { + delete pathData; + doThrowIAE(env, result.failureMessage.c_str()); + return NULL; + } +} + +static jboolean interpolatePathData(JNIEnv*, jobject, jlong outPathDataPtr, jlong fromPathDataPtr, + jlong toPathDataPtr, jfloat fraction) { + PathData* outPathData = reinterpret_cast<PathData*>(outPathDataPtr); + PathData* fromPathData = reinterpret_cast<PathData*>(fromPathDataPtr); + PathData* toPathData = reinterpret_cast<PathData*>(toPathDataPtr); + return VectorDrawableUtils::interpolatePathData(outPathData, *fromPathData, + *toPathData, fraction); +} + +static void deletePathData(JNIEnv*, jobject, jlong pathDataHandle) { + PathData* pathData = reinterpret_cast<PathData*>(pathDataHandle); + delete pathData; +} + +static jboolean canMorphPathData(JNIEnv*, jobject, jlong fromPathDataPtr, jlong toPathDataPtr) { + PathData* fromPathData = reinterpret_cast<PathData*>(fromPathDataPtr); + PathData* toPathData = reinterpret_cast<PathData*>(toPathDataPtr); + return VectorDrawableUtils::canMorph(*fromPathData, *toPathData); +} + +static void setPathData(JNIEnv*, jobject, jlong outPathDataPtr, jlong fromPathDataPtr) { + PathData* fromPathData = reinterpret_cast<PathData*>(fromPathDataPtr); + PathData* outPathData = reinterpret_cast<PathData*>(outPathDataPtr); + *outPathData = *fromPathData; +} + +static void setSkPathFromPathData(JNIEnv*, jobject, jlong outPathPtr, jlong pathDataPtr) { + PathData* pathData = reinterpret_cast<PathData*>(pathDataPtr); + SkPath* skPath = reinterpret_cast<SkPath*>(outPathPtr); + VectorDrawableUtils::verbsToPath(skPath, *pathData); +} + +static const JNINativeMethod gMethods[] = { + {"nParseStringForPath", "(JLjava/lang/String;I)V", (void*)parseStringForPath}, + {"nCreatePathDataFromString", "(Ljava/lang/String;I)J", (void*)createPathDataFromStringPath}, + + // ---------------- @FastNative ----------------- + + {"nCreateEmptyPathData", "()J", (void*)createEmptyPathData}, + {"nCreatePathData", "(J)J", (void*)createPathData}, + {"nInterpolatePathData", "(JJJF)Z", (void*)interpolatePathData}, + {"nFinalize", "(J)V", (void*)deletePathData}, + {"nCanMorph", "(JJ)Z", (void*)canMorphPathData}, + {"nSetPathData", "(JJ)V", (void*)setPathData}, + {"nCreatePathFromPathData", "(JJ)V", (void*)setSkPathFromPathData}, +}; + +int register_android_util_PathParser(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/util/PathParser", gMethods, NELEM(gMethods)); +} +}; diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp new file mode 100644 index 000000000000..5714cd1d0390 --- /dev/null +++ b/libs/hwui/jni/fonts/Font.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2018 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. + */ + +#undef LOG_TAG +#define LOG_TAG "Minikin" + +#include "SkData.h" +#include "SkFontMgr.h" +#include "SkRefCnt.h" +#include "SkTypeface.h" +#include "GraphicsJNI.h" +#include <nativehelper/ScopedUtfChars.h> +#include "Utils.h" +#include "FontUtils.h" + +#include <hwui/MinikinSkia.h> +#include <hwui/Typeface.h> +#include <minikin/FontFamily.h> +#include <ui/FatVector.h> + +#include <memory> + +namespace android { + +struct NativeFontBuilder { + std::vector<minikin::FontVariation> axes; +}; + +static inline NativeFontBuilder* toBuilder(jlong ptr) { + return reinterpret_cast<NativeFontBuilder*>(ptr); +} + +static void releaseFont(jlong font) { + delete reinterpret_cast<FontWrapper*>(font); +} + +static void release_global_ref(const void* /*data*/, void* context) { + JNIEnv* env = GraphicsJNI::getJNIEnv(); + bool needToAttach = (env == nullptr); + if (needToAttach) { + env = GraphicsJNI::attachJNIEnv("release_font_data"); + if (env == nullptr) { + ALOGE("failed to attach to thread to release global ref."); + return; + } + } + + jobject obj = reinterpret_cast<jobject>(context); + env->DeleteGlobalRef(obj); +} + +// Regular JNI +static jlong Font_Builder_initBuilder(JNIEnv*, jobject) { + return reinterpret_cast<jlong>(new NativeFontBuilder()); +} + +// Critical Native +static void Font_Builder_addAxis(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr, jint tag, jfloat value) { + toBuilder(builderPtr)->axes.emplace_back(static_cast<minikin::AxisTag>(tag), value); +} + +// Regular JNI +static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jobject buffer, + jstring filePath, jint weight, jboolean italic, jint ttcIndex) { + NPE_CHECK_RETURN_ZERO(env, buffer); + std::unique_ptr<NativeFontBuilder> builder(toBuilder(builderPtr)); + const void* fontPtr = env->GetDirectBufferAddress(buffer); + if (fontPtr == nullptr) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Not a direct buffer"); + return 0; + } + jlong fontSize = env->GetDirectBufferCapacity(buffer); + if (fontSize <= 0) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "buffer size must not be zero or negative"); + return 0; + } + ScopedUtfChars fontPath(env, filePath); + jobject fontRef = MakeGlobalRefOrDie(env, buffer); + sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize, + release_global_ref, reinterpret_cast<void*>(fontRef))); + + FatVector<SkFontArguments::Axis, 2> skiaAxes; + for (const auto& axis : builder->axes) { + skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value}); + } + + std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data))); + + SkFontArguments params; + params.setCollectionIndex(ttcIndex); + params.setAxes(skiaAxes.data(), skiaAxes.size()); + + sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); + sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), params)); + if (face == nullptr) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "Failed to create internal object. maybe invalid font data."); + return 0; + } + std::shared_ptr<minikin::MinikinFont> minikinFont = + std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, + std::string_view(fontPath.c_str(), fontPath.size()), + ttcIndex, builder->axes); + minikin::Font font = minikin::Font::Builder(minikinFont).setWeight(weight) + .setSlant(static_cast<minikin::FontStyle::Slant>(italic)).build(); + return reinterpret_cast<jlong>(new FontWrapper(std::move(font))); +} + +// Critical Native +static jlong Font_Builder_getReleaseNativeFont(CRITICAL_JNI_PARAMS) { + return reinterpret_cast<jlong>(releaseFont); +} + +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gFontBuilderMethods[] = { + { "nInitBuilder", "()J", (void*) Font_Builder_initBuilder }, + { "nAddAxis", "(JIF)V", (void*) Font_Builder_addAxis }, + { "nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;IZI)J", (void*) Font_Builder_build }, + { "nGetReleaseNativeFont", "()J", (void*) Font_Builder_getReleaseNativeFont }, +}; + +int register_android_graphics_fonts_Font(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/fonts/Font$Builder", gFontBuilderMethods, + NELEM(gFontBuilderMethods)); +} + +} diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp new file mode 100644 index 000000000000..df619d9f1406 --- /dev/null +++ b/libs/hwui/jni/fonts/FontFamily.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2018 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. + */ + +#undef LOG_TAG +#define LOG_TAG "Minikin" + +#include "graphics_jni_helpers.h" +#include <nativehelper/ScopedUtfChars.h> + +#include "FontUtils.h" + +#include <minikin/FontFamily.h> +#include <minikin/LocaleList.h> + +#include <memory> + +namespace android { + +struct NativeFamilyBuilder { + std::vector<minikin::Font> fonts; +}; + +static inline NativeFamilyBuilder* toBuilder(jlong ptr) { + return reinterpret_cast<NativeFamilyBuilder*>(ptr); +} + +static inline FontWrapper* toFontWrapper(jlong ptr) { + return reinterpret_cast<FontWrapper*>(ptr); +} + +static void releaseFontFamily(jlong family) { + delete reinterpret_cast<FontFamilyWrapper*>(family); +} + +// Regular JNI +static jlong FontFamily_Builder_initBuilder(JNIEnv*, jobject) { + return reinterpret_cast<jlong>(new NativeFamilyBuilder()); +} + +// Critical Native +static void FontFamily_Builder_addFont(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr, jlong fontPtr) { + toBuilder(builderPtr)->fonts.push_back(toFontWrapper(fontPtr)->font); +} + +// Regular JNI +static jlong FontFamily_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, + jstring langTags, jint variant, jboolean isCustomFallback) { + std::unique_ptr<NativeFamilyBuilder> builder(toBuilder(builderPtr)); + uint32_t localeId; + if (langTags == nullptr) { + localeId = minikin::registerLocaleList(""); + } else { + ScopedUtfChars str(env, langTags); + localeId = minikin::registerLocaleList(str.c_str()); + } + std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>( + localeId, static_cast<minikin::FamilyVariant>(variant), std::move(builder->fonts), + isCustomFallback); + if (family->getCoverage().length() == 0) { + // No coverage means minikin rejected given font for some reasons. + jniThrowException(env, "java/lang/IllegalArgumentException", + "Failed to create internal object. maybe invalid font data"); + return 0; + } + return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family))); +} + +// CriticalNative +static jlong FontFamily_Builder_GetReleaseFunc(CRITICAL_JNI_PARAMS) { + return reinterpret_cast<jlong>(releaseFontFamily); +} + +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gFontFamilyBuilderMethods[] = { + { "nInitBuilder", "()J", (void*) FontFamily_Builder_initBuilder }, + { "nAddFont", "(JJ)V", (void*) FontFamily_Builder_addFont }, + { "nBuild", "(JLjava/lang/String;IZ)J", (void*) FontFamily_Builder_build }, + + { "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc }, +}; + +int register_android_graphics_fonts_FontFamily(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily$Builder", + gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods)); +} + +} diff --git a/libs/hwui/jni/graphics_jni_helpers.h b/libs/hwui/jni/graphics_jni_helpers.h new file mode 100644 index 000000000000..78db54acc9e5 --- /dev/null +++ b/libs/hwui/jni/graphics_jni_helpers.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef GRAPHICS_JNI_HELPERS +#define GRAPHICS_JNI_HELPERS + +#include <log/log.h> +#include <nativehelper/JNIPlatformHelp.h> +#include <nativehelper/scoped_local_ref.h> +#include <nativehelper/scoped_utf_chars.h> +#include <string> + +// Host targets (layoutlib) do not differentiate between regular and critical native methods, +// and they need all the JNI methods to have JNIEnv* and jclass/jobject as their first two arguments. +// The following macro allows to have those arguments when compiling for host while omitting them when +// compiling for Android. +#ifdef __ANDROID__ +#define CRITICAL_JNI_PARAMS +#define CRITICAL_JNI_PARAMS_COMMA +#else +#define CRITICAL_JNI_PARAMS JNIEnv*, jclass +#define CRITICAL_JNI_PARAMS_COMMA JNIEnv*, jclass, +#endif + +namespace android { + +// Defines some helpful functions. + +static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) { + jclass clazz = env->FindClass(class_name); + LOG_ALWAYS_FATAL_IF(clazz == NULL, "Unable to find class %s", class_name); + return clazz; +} + +static inline jfieldID GetFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name, + const char* field_signature) { + jfieldID res = env->GetFieldID(clazz, field_name, field_signature); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s", field_name); + return res; +} + +static inline jmethodID GetMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name, + const char* method_signature) { + jmethodID res = env->GetMethodID(clazz, method_name, method_signature); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find method %s", method_name); + return res; +} + +static inline jfieldID GetStaticFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name, + const char* field_signature) { + jfieldID res = env->GetStaticFieldID(clazz, field_name, field_signature); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s", field_name); + return res; +} + +static inline jmethodID GetStaticMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name, + const char* method_signature) { + jmethodID res = env->GetStaticMethodID(clazz, method_name, method_signature); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static method %s", method_name); + return res; +} + +template <typename T> +static inline T MakeGlobalRefOrDie(JNIEnv* env, T in) { + jobject res = env->NewGlobalRef(in); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to create global reference."); + return static_cast<T>(res); +} + +static inline int RegisterMethodsOrDie(JNIEnv* env, const char* className, + const JNINativeMethod* gMethods, int numMethods) { + int res = jniRegisterNativeMethods(env, className, gMethods, numMethods); + LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods."); + return res; +} + +/** + * Read the specified field from jobject, and convert to std::string. + * If the field cannot be obtained, return defaultValue. + */ +static inline std::string getStringField(JNIEnv* env, jobject obj, jfieldID fieldId, + const char* defaultValue) { + ScopedLocalRef<jstring> strObj(env, jstring(env->GetObjectField(obj, fieldId))); + if (strObj != nullptr) { + ScopedUtfChars chars(env, strObj.get()); + return std::string(chars.c_str()); + } + return std::string(defaultValue); +} + +} // namespace android + +#endif // GRAPHICS_JNI_HELPERS diff --git a/libs/hwui/jni/pdf/PdfDocument.cpp b/libs/hwui/jni/pdf/PdfDocument.cpp new file mode 100644 index 000000000000..d21eb3f6a208 --- /dev/null +++ b/libs/hwui/jni/pdf/PdfDocument.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2013 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. + */ + +#include "GraphicsJNI.h" +#include <vector> + +#include "CreateJavaOutputStreamAdaptor.h" + +#include "SkPDFDocument.h" +#include "SkPicture.h" +#include "SkPictureRecorder.h" +#include "SkRect.h" +#include "SkStream.h" + +#include <hwui/Canvas.h> + +namespace android { + +struct PageRecord { + + PageRecord(int width, int height, const SkRect& contentRect) + : mPictureRecorder(new SkPictureRecorder()) + , mPicture(NULL) + , mWidth(width) + , mHeight(height) { + mContentRect = contentRect; + } + + ~PageRecord() { + delete mPictureRecorder; + if (NULL != mPicture) { + mPicture->unref(); + } + } + + SkPictureRecorder* mPictureRecorder; + SkPicture* mPicture; + const int mWidth; + const int mHeight; + SkRect mContentRect; +}; + +class PdfDocument { +public: + PdfDocument() { + mCurrentPage = NULL; + } + + SkCanvas* startPage(int width, int height, + int contentLeft, int contentTop, int contentRight, int contentBottom) { + assert(mCurrentPage == NULL); + + SkRect contentRect = SkRect::MakeLTRB( + contentLeft, contentTop, contentRight, contentBottom); + PageRecord* page = new PageRecord(width, height, contentRect); + mPages.push_back(page); + mCurrentPage = page; + + SkCanvas* canvas = page->mPictureRecorder->beginRecording( + SkRect::MakeWH(contentRect.width(), contentRect.height())); + + return canvas; + } + + void finishPage() { + assert(mCurrentPage != NULL); + assert(mCurrentPage->mPictureRecorder != NULL); + assert(mCurrentPage->mPicture == NULL); + mCurrentPage->mPicture = mCurrentPage->mPictureRecorder->finishRecordingAsPicture().release(); + delete mCurrentPage->mPictureRecorder; + mCurrentPage->mPictureRecorder = NULL; + mCurrentPage = NULL; + } + + void write(SkWStream* stream) { + sk_sp<SkDocument> document = SkPDF::MakeDocument(stream); + for (unsigned i = 0; i < mPages.size(); i++) { + PageRecord* page = mPages[i]; + + SkCanvas* canvas = document->beginPage(page->mWidth, page->mHeight, + &(page->mContentRect)); + canvas->drawPicture(page->mPicture); + + document->endPage(); + } + document->close(); + } + + void close() { + assert(NULL == mCurrentPage); + for (unsigned i = 0; i < mPages.size(); i++) { + delete mPages[i]; + } + } + +private: + ~PdfDocument() { + close(); + } + + std::vector<PageRecord*> mPages; + PageRecord* mCurrentPage; +}; + +static jlong nativeCreateDocument(JNIEnv* env, jobject thiz) { + return reinterpret_cast<jlong>(new PdfDocument()); +} + +static jlong nativeStartPage(JNIEnv* env, jobject thiz, jlong documentPtr, + jint pageWidth, jint pageHeight, + jint contentLeft, jint contentTop, jint contentRight, jint contentBottom) { + PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr); + SkCanvas* canvas = document->startPage(pageWidth, pageHeight, + contentLeft, contentTop, contentRight, contentBottom); + return reinterpret_cast<jlong>(Canvas::create_canvas(canvas)); +} + +static void nativeFinishPage(JNIEnv* env, jobject thiz, jlong documentPtr) { + PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr); + document->finishPage(); +} + +static void nativeWriteTo(JNIEnv* env, jobject thiz, jlong documentPtr, jobject out, + jbyteArray chunk) { + PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr); + SkWStream* skWStream = CreateJavaOutputStreamAdaptor(env, out, chunk); + document->write(skWStream); + delete skWStream; +} + +static void nativeClose(JNIEnv* env, jobject thiz, jlong documentPtr) { + PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr); + document->close(); +} + +static const JNINativeMethod gPdfDocument_Methods[] = { + {"nativeCreateDocument", "()J", (void*) nativeCreateDocument}, + {"nativeStartPage", "(JIIIIII)J", (void*) nativeStartPage}, + {"nativeFinishPage", "(J)V", (void*) nativeFinishPage}, + {"nativeWriteTo", "(JLjava/io/OutputStream;[B)V", (void*) nativeWriteTo}, + {"nativeClose", "(J)V", (void*) nativeClose} +}; + +int register_android_graphics_pdf_PdfDocument(JNIEnv* env) { + return RegisterMethodsOrDie( + env, "android/graphics/pdf/PdfDocument", gPdfDocument_Methods, + NELEM(gPdfDocument_Methods)); +} + +}; diff --git a/libs/hwui/jni/pdf/PdfEditor.cpp b/libs/hwui/jni/pdf/PdfEditor.cpp new file mode 100644 index 000000000000..e65921ac8e0a --- /dev/null +++ b/libs/hwui/jni/pdf/PdfEditor.cpp @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2014 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. + */ + +#undef LOG_TAG +#define LOG_TAG "PdfEditor" + +#include <sys/types.h> +#include <unistd.h> + +#include <vector> + +#include <log/log.h> +#include <utils/Log.h> + +#include "PdfUtils.h" + +#include "graphics_jni_helpers.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" +#include "fpdfview.h" +#include "fpdf_edit.h" +#include "fpdf_save.h" +#include "fpdf_transformpage.h" +#pragma GCC diagnostic pop + +#include "SkMatrix.h" + +namespace android { + +enum PageBox {PAGE_BOX_MEDIA, PAGE_BOX_CROP}; + +static struct { + jfieldID x; + jfieldID y; +} gPointClassInfo; + +static struct { + jfieldID left; + jfieldID top; + jfieldID right; + jfieldID bottom; +} gRectClassInfo; + +static jint nativeRemovePage(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + + FPDFPage_Delete(document, pageIndex); + return FPDF_GetPageCount(document); +} + +struct PdfToFdWriter : FPDF_FILEWRITE { + int dstFd; +}; + +static bool writeAllBytes(const int fd, const void* buffer, const size_t byteCount) { + char* writeBuffer = static_cast<char*>(const_cast<void*>(buffer)); + size_t remainingBytes = byteCount; + while (remainingBytes > 0) { + ssize_t writtenByteCount = write(fd, writeBuffer, remainingBytes); + if (writtenByteCount == -1) { + if (errno == EINTR) { + continue; + } + ALOGE("Error writing to buffer: %d", errno); + return false; + } + remainingBytes -= writtenByteCount; + writeBuffer += writtenByteCount; + } + return true; +} + +static int writeBlock(FPDF_FILEWRITE* owner, const void* buffer, unsigned long size) { + const PdfToFdWriter* writer = reinterpret_cast<PdfToFdWriter*>(owner); + const bool success = writeAllBytes(writer->dstFd, buffer, size); + if (!success) { + ALOGE("Cannot write to file descriptor. Error:%d", errno); + return 0; + } + return 1; +} + +static void nativeWrite(JNIEnv* env, jclass thiz, jlong documentPtr, jint fd) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + PdfToFdWriter writer; + writer.dstFd = fd; + writer.WriteBlock = &writeBlock; + const bool success = FPDF_SaveAsCopy(document, &writer, FPDF_NO_INCREMENTAL); + if (!success) { + jniThrowExceptionFmt(env, "java/io/IOException", + "cannot write to fd. Error: %d", errno); + } +} + +static void nativeSetTransformAndClip(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jlong transformPtr, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + + FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot open page"); + return; + } + + double width = 0; + double height = 0; + + const int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height); + if (!result) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot get page size"); + return; + } + + // PDF's coordinate system origin is left-bottom while in graphics it + // is the top-left. So, translate the PDF coordinates to ours. + SkMatrix reflectOnX = SkMatrix::MakeScale(1, -1); + SkMatrix moveUp = SkMatrix::MakeTrans(0, FPDF_GetPageHeight(page)); + SkMatrix coordinateChange = SkMatrix::Concat(moveUp, reflectOnX); + + // Apply the transformation what was created in our coordinates. + SkMatrix matrix = SkMatrix::Concat(*reinterpret_cast<SkMatrix*>(transformPtr), + coordinateChange); + + // Translate the result back to PDF coordinates. + matrix.setConcat(coordinateChange, matrix); + + SkScalar transformValues[6]; + if (!matrix.asAffine(transformValues)) { + FPDF_ClosePage(page); + + jniThrowException(env, "java/lang/IllegalArgumentException", + "transform matrix has perspective. Only affine matrices are allowed."); + return; + } + + FS_MATRIX transform = {transformValues[SkMatrix::kAScaleX], transformValues[SkMatrix::kASkewY], + transformValues[SkMatrix::kASkewX], transformValues[SkMatrix::kAScaleY], + transformValues[SkMatrix::kATransX], + transformValues[SkMatrix::kATransY]}; + + FS_RECTF clip = {(float) clipLeft, (float) clipTop, (float) clipRight, (float) clipBottom}; + + FPDFPage_TransFormWithClip(page, &transform, &clip); + + FPDF_ClosePage(page); +} + +static void nativeGetPageSize(JNIEnv* env, jclass thiz, jlong documentPtr, + jint pageIndex, jobject outSize) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + + FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot open page"); + return; + } + + double width = 0; + double height = 0; + + const int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height); + if (!result) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot get page size"); + return; + } + + env->SetIntField(outSize, gPointClassInfo.x, width); + env->SetIntField(outSize, gPointClassInfo.y, height); + + FPDF_ClosePage(page); +} + +static bool nativeGetPageBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + PageBox pageBox, jobject outBox) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + + FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot open page"); + return false; + } + + float left; + float top; + float right; + float bottom; + + const FPDF_BOOL success = (pageBox == PAGE_BOX_MEDIA) + ? FPDFPage_GetMediaBox(page, &left, &top, &right, &bottom) + : FPDFPage_GetCropBox(page, &left, &top, &right, &bottom); + + FPDF_ClosePage(page); + + if (!success) { + return false; + } + + env->SetIntField(outBox, gRectClassInfo.left, (int) left); + env->SetIntField(outBox, gRectClassInfo.top, (int) top); + env->SetIntField(outBox, gRectClassInfo.right, (int) right); + env->SetIntField(outBox, gRectClassInfo.bottom, (int) bottom); + + return true; +} + +static jboolean nativeGetPageMediaBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jobject outMediaBox) { + const bool success = nativeGetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_MEDIA, + outMediaBox); + return success ? JNI_TRUE : JNI_FALSE; +} + +static jboolean nativeGetPageCropBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jobject outMediaBox) { + const bool success = nativeGetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_CROP, + outMediaBox); + return success ? JNI_TRUE : JNI_FALSE; +} + +static void nativeSetPageBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + PageBox pageBox, jobject box) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + + FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot open page"); + return; + } + + const int left = env->GetIntField(box, gRectClassInfo.left); + const int top = env->GetIntField(box, gRectClassInfo.top); + const int right = env->GetIntField(box, gRectClassInfo.right); + const int bottom = env->GetIntField(box, gRectClassInfo.bottom); + + if (pageBox == PAGE_BOX_MEDIA) { + FPDFPage_SetMediaBox(page, left, top, right, bottom); + } else { + FPDFPage_SetCropBox(page, left, top, right, bottom); + } + + FPDF_ClosePage(page); +} + +static void nativeSetPageMediaBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jobject mediaBox) { + nativeSetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_MEDIA, mediaBox); +} + +static void nativeSetPageCropBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jobject mediaBox) { + nativeSetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_CROP, mediaBox); +} + +static const JNINativeMethod gPdfEditor_Methods[] = { + {"nativeOpen", "(IJ)J", (void*) nativeOpen}, + {"nativeClose", "(J)V", (void*) nativeClose}, + {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount}, + {"nativeRemovePage", "(JI)I", (void*) nativeRemovePage}, + {"nativeWrite", "(JI)V", (void*) nativeWrite}, + {"nativeSetTransformAndClip", "(JIJIIII)V", (void*) nativeSetTransformAndClip}, + {"nativeGetPageSize", "(JILandroid/graphics/Point;)V", (void*) nativeGetPageSize}, + {"nativeScaleForPrinting", "(J)Z", (void*) nativeScaleForPrinting}, + {"nativeGetPageMediaBox", "(JILandroid/graphics/Rect;)Z", (void*) nativeGetPageMediaBox}, + {"nativeSetPageMediaBox", "(JILandroid/graphics/Rect;)V", (void*) nativeSetPageMediaBox}, + {"nativeGetPageCropBox", "(JILandroid/graphics/Rect;)Z", (void*) nativeGetPageCropBox}, + {"nativeSetPageCropBox", "(JILandroid/graphics/Rect;)V", (void*) nativeSetPageCropBox} +}; + +int register_android_graphics_pdf_PdfEditor(JNIEnv* env) { + const int result = RegisterMethodsOrDie( + env, "android/graphics/pdf/PdfEditor", gPdfEditor_Methods, + NELEM(gPdfEditor_Methods)); + + jclass pointClass = FindClassOrDie(env, "android/graphics/Point"); + gPointClassInfo.x = GetFieldIDOrDie(env, pointClass, "x", "I"); + gPointClassInfo.y = GetFieldIDOrDie(env, pointClass, "y", "I"); + + jclass rectClass = FindClassOrDie(env, "android/graphics/Rect"); + gRectClassInfo.left = GetFieldIDOrDie(env, rectClass, "left", "I"); + gRectClassInfo.top = GetFieldIDOrDie(env, rectClass, "top", "I"); + gRectClassInfo.right = GetFieldIDOrDie(env, rectClass, "right", "I"); + gRectClassInfo.bottom = GetFieldIDOrDie(env, rectClass, "bottom", "I"); + + return result; +}; + +}; diff --git a/libs/hwui/jni/pdf/PdfRenderer.cpp b/libs/hwui/jni/pdf/PdfRenderer.cpp new file mode 100644 index 000000000000..cc1f96197c74 --- /dev/null +++ b/libs/hwui/jni/pdf/PdfRenderer.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2014 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. + */ + +#include "PdfUtils.h" + +#include "GraphicsJNI.h" +#include "SkBitmap.h" +#include "SkMatrix.h" +#include "fpdfview.h" + +#include <vector> +#include <utils/Log.h> +#include <unistd.h> +#include <sys/types.h> +#include <unistd.h> + +namespace android { + +static const int RENDER_MODE_FOR_DISPLAY = 1; +static const int RENDER_MODE_FOR_PRINT = 2; + +static struct { + jfieldID x; + jfieldID y; +} gPointClassInfo; + +static jlong nativeOpenPageAndGetSize(JNIEnv* env, jclass thiz, jlong documentPtr, + jint pageIndex, jobject outSize) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + + FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot load page"); + return -1; + } + + double width = 0; + double height = 0; + + int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height); + if (!result) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot get page size"); + return -1; + } + + env->SetIntField(outSize, gPointClassInfo.x, width); + env->SetIntField(outSize, gPointClassInfo.y, height); + + return reinterpret_cast<jlong>(page); +} + +static void nativeClosePage(JNIEnv* env, jclass thiz, jlong pagePtr) { + FPDF_PAGE page = reinterpret_cast<FPDF_PAGE>(pagePtr); + FPDF_ClosePage(page); +} + +static void nativeRenderPage(JNIEnv* env, jclass thiz, jlong documentPtr, jlong pagePtr, + jlong bitmapPtr, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom, + jlong transformPtr, jint renderMode) { + FPDF_PAGE page = reinterpret_cast<FPDF_PAGE>(pagePtr); + + SkBitmap skBitmap; + bitmap::toBitmap(bitmapPtr).getSkBitmap(&skBitmap); + + const int stride = skBitmap.width() * 4; + + FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(skBitmap.width(), skBitmap.height(), + FPDFBitmap_BGRA, skBitmap.getPixels(), stride); + + int renderFlags = FPDF_REVERSE_BYTE_ORDER; + if (renderMode == RENDER_MODE_FOR_DISPLAY) { + renderFlags |= FPDF_LCD_TEXT; + } else if (renderMode == RENDER_MODE_FOR_PRINT) { + renderFlags |= FPDF_PRINTING; + } + + SkMatrix matrix = *reinterpret_cast<SkMatrix*>(transformPtr); + SkScalar transformValues[6]; + if (!matrix.asAffine(transformValues)) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "transform matrix has perspective. Only affine matrices are allowed."); + return; + } + + FS_MATRIX transform = {transformValues[SkMatrix::kAScaleX], transformValues[SkMatrix::kASkewY], + transformValues[SkMatrix::kASkewX], transformValues[SkMatrix::kAScaleY], + transformValues[SkMatrix::kATransX], + transformValues[SkMatrix::kATransY]}; + + FS_RECTF clip = {(float) clipLeft, (float) clipTop, (float) clipRight, (float) clipBottom}; + + FPDF_RenderPageBitmapWithMatrix(bitmap, page, &transform, &clip, renderFlags); + + skBitmap.notifyPixelsChanged(); +} + +static const JNINativeMethod gPdfRenderer_Methods[] = { + {"nativeCreate", "(IJ)J", (void*) nativeOpen}, + {"nativeClose", "(J)V", (void*) nativeClose}, + {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount}, + {"nativeScaleForPrinting", "(J)Z", (void*) nativeScaleForPrinting}, + {"nativeRenderPage", "(JJJIIIIJI)V", (void*) nativeRenderPage}, + {"nativeOpenPageAndGetSize", "(JILandroid/graphics/Point;)J", (void*) nativeOpenPageAndGetSize}, + {"nativeClosePage", "(J)V", (void*) nativeClosePage} +}; + +int register_android_graphics_pdf_PdfRenderer(JNIEnv* env) { + int result = RegisterMethodsOrDie( + env, "android/graphics/pdf/PdfRenderer", gPdfRenderer_Methods, + NELEM(gPdfRenderer_Methods)); + + jclass clazz = FindClassOrDie(env, "android/graphics/Point"); + gPointClassInfo.x = GetFieldIDOrDie(env, clazz, "x", "I"); + gPointClassInfo.y = GetFieldIDOrDie(env, clazz, "y", "I"); + + return result; +}; + +}; diff --git a/libs/hwui/jni/pdf/PdfUtils.cpp b/libs/hwui/jni/pdf/PdfUtils.cpp new file mode 100644 index 000000000000..06d202828b85 --- /dev/null +++ b/libs/hwui/jni/pdf/PdfUtils.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2016 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. + */ + +#include "PdfUtils.h" + +#include "jni.h" +#include <nativehelper/JNIHelp.h> + +#include "fpdfview.h" + +#undef LOG_TAG +#define LOG_TAG "PdfUtils" +#include <utils/Log.h> + +namespace android { + +static int sUnmatchedPdfiumInitRequestCount = 0; + +int getBlock(void* param, unsigned long position, unsigned char* outBuffer, + unsigned long size) { + const int fd = reinterpret_cast<intptr_t>(param); + const int readCount = pread(fd, outBuffer, size, position); + if (readCount < 0) { + ALOGE("Cannot read from file descriptor. Error:%d", errno); + return 0; + } + return 1; +} + +// Check if the last pdfium command failed and if so, forward the error to java via an exception. If +// this function returns true an exception is pending. +bool forwardPdfiumError(JNIEnv* env) { + long error = FPDF_GetLastError(); + switch (error) { + case FPDF_ERR_SUCCESS: + return false; + case FPDF_ERR_FILE: + jniThrowException(env, "java/io/IOException", "file not found or cannot be opened"); + break; + case FPDF_ERR_FORMAT: + jniThrowException(env, "java/io/IOException", "file not in PDF format or corrupted"); + break; + case FPDF_ERR_PASSWORD: + jniThrowException(env, "java/lang/SecurityException", + "password required or incorrect password"); + break; + case FPDF_ERR_SECURITY: + jniThrowException(env, "java/lang/SecurityException", "unsupported security scheme"); + break; + case FPDF_ERR_PAGE: + jniThrowException(env, "java/io/IOException", "page not found or content error"); + break; +#ifdef PDF_ENABLE_XFA + case FPDF_ERR_XFALOAD: + jniThrowException(env, "java/lang/Exception", "load XFA error"); + break; + case FPDF_ERR_XFALAYOUT: + jniThrowException(env, "java/lang/Exception", "layout XFA error"); + break; +#endif // PDF_ENABLE_XFA + case FPDF_ERR_UNKNOWN: + default: + jniThrowExceptionFmt(env, "java/lang/Exception", "unknown error %d", error); + } + + return true; +} + +static void initializeLibraryIfNeeded(JNIEnv* env) { + if (sUnmatchedPdfiumInitRequestCount == 0) { + FPDF_InitLibrary(); + } + + sUnmatchedPdfiumInitRequestCount++; +} + +static void destroyLibraryIfNeeded(JNIEnv* env, bool handleError) { + if (sUnmatchedPdfiumInitRequestCount == 1) { + FPDF_DestroyLibrary(); + } + + sUnmatchedPdfiumInitRequestCount--; +} + +jlong nativeOpen(JNIEnv* env, jclass thiz, jint fd, jlong size) { + initializeLibraryIfNeeded(env); + + FPDF_FILEACCESS loader; + loader.m_FileLen = size; + loader.m_Param = reinterpret_cast<void*>(intptr_t(fd)); + loader.m_GetBlock = &getBlock; + + FPDF_DOCUMENT document = FPDF_LoadCustomDocument(&loader, NULL); + if (!document) { + forwardPdfiumError(env); + destroyLibraryIfNeeded(env, false); + return -1; + } + + return reinterpret_cast<jlong>(document); +} + +void nativeClose(JNIEnv* env, jclass thiz, jlong documentPtr) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + FPDF_CloseDocument(document); + + destroyLibraryIfNeeded(env, true); +} + +jint nativeGetPageCount(JNIEnv* env, jclass thiz, jlong documentPtr) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + + return FPDF_GetPageCount(document); +} + +jboolean nativeScaleForPrinting(JNIEnv* env, jclass thiz, jlong documentPtr) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + FPDF_BOOL printScaling = FPDF_VIEWERREF_GetPrintScaling(document); + + return printScaling ? JNI_TRUE : JNI_FALSE; +} + +}; diff --git a/libs/hwui/debug/DefaultGlesDriver.cpp b/libs/hwui/jni/pdf/PdfUtils.h index 46ab20081d17..65327382e899 100644 --- a/libs/hwui/debug/DefaultGlesDriver.cpp +++ b/libs/hwui/jni/pdf/PdfUtils.h @@ -14,27 +14,22 @@ * limitations under the License. */ -#include "DefaultGlesDriver.h" +#ifndef PDF_UTILS_H_ +#define PDF_UTILS_H_ -#include "gles_undefine.h" - -#include <EGL/egl.h> +#include "jni.h" namespace android { -namespace uirenderer { -namespace debug { -// Generate the proxy -#define API_ENTRY(x) DefaultGlesDriver::x##_ -#define CALL_GL_API(x, ...) x(__VA_ARGS__); -#define CALL_GL_API_RETURN(x, ...) return x(__VA_ARGS__); +int getBlock(void* param, unsigned long position, unsigned char* outBuffer, + unsigned long size); + +jlong nativeOpen(JNIEnv* env, jclass thiz, jint fd, jlong size); +void nativeClose(JNIEnv* env, jclass thiz, jlong documentPtr); -#include "gles_stubs.in" +jint nativeGetPageCount(JNIEnv* env, jclass thiz, jlong documentPtr); +jboolean nativeScaleForPrinting(JNIEnv* env, jclass thiz, jlong documentPtr); -#undef API_ENTRY -#undef CALL_GL_API -#undef CALL_GL_API_RETURN +}; -} // namespace debug -} // namespace uirenderer -} // namespace android +#endif /* PDF_UTILS_H_ */ diff --git a/libs/hwui/jni/scoped_nullable_primitive_array.h b/libs/hwui/jni/scoped_nullable_primitive_array.h new file mode 100644 index 000000000000..77f4c9d14f07 --- /dev/null +++ b/libs/hwui/jni/scoped_nullable_primitive_array.h @@ -0,0 +1,103 @@ +/* + * 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. + */ + +#ifndef SCOPED_NULLABLE_PRIMITIVE_ARRAY_H +#define SCOPED_NULLABLE_PRIMITIVE_ARRAY_H + +#include <jni.h> + +namespace android { + +#define ARRAY_TRAITS(ARRAY_TYPE, POINTER_TYPE, NAME) \ +class NAME ## ArrayTraits { \ +public: \ + static constexpr void getArrayRegion(JNIEnv* env, ARRAY_TYPE array, size_t start, \ + size_t len, POINTER_TYPE out) { \ + env->Get ## NAME ## ArrayRegion(array, start, len, out); \ + } \ + \ + static constexpr POINTER_TYPE getArrayElements(JNIEnv* env, ARRAY_TYPE array) { \ + return env->Get ## NAME ## ArrayElements(array, nullptr); \ + } \ + \ + static constexpr void releaseArrayElements(JNIEnv* env, ARRAY_TYPE array, \ + POINTER_TYPE buffer, jint mode) { \ + env->Release ## NAME ## ArrayElements(array, buffer, mode); \ + } \ +}; \ + +ARRAY_TRAITS(jbooleanArray, jboolean*, Boolean) +ARRAY_TRAITS(jbyteArray, jbyte*, Byte) +ARRAY_TRAITS(jcharArray, jchar*, Char) +ARRAY_TRAITS(jdoubleArray, jdouble*, Double) +ARRAY_TRAITS(jfloatArray, jfloat*, Float) +ARRAY_TRAITS(jintArray, jint*, Int) +ARRAY_TRAITS(jlongArray, jlong*, Long) +ARRAY_TRAITS(jshortArray, jshort*, Short) + +#undef ARRAY_TRAITS + +template<typename JavaArrayType, typename PrimitiveType, class Traits, size_t preallocSize = 10> +class ScopedArrayRO { +public: + ScopedArrayRO(JNIEnv* env, JavaArrayType javaArray) : mEnv(env), mJavaArray(javaArray) { + if (mJavaArray == nullptr) { + mSize = 0; + mRawArray = nullptr; + } else { + mSize = mEnv->GetArrayLength(mJavaArray); + if (mSize <= preallocSize) { + Traits::getArrayRegion(mEnv, mJavaArray, 0, mSize, mBuffer); + mRawArray = mBuffer; + } else { + mRawArray = Traits::getArrayElements(mEnv, mJavaArray); + } + } + } + + ~ScopedArrayRO() { + if (mRawArray != nullptr && mRawArray != mBuffer) { + Traits::releaseArrayElements(mEnv, mJavaArray, mRawArray, JNI_ABORT); + } + } + + const PrimitiveType* get() const { return mRawArray; } + const PrimitiveType& operator[](size_t n) const { return mRawArray[n]; } + size_t size() const { return mSize; } + +private: + JNIEnv* const mEnv; + JavaArrayType mJavaArray; + PrimitiveType* mRawArray; + size_t mSize; + PrimitiveType mBuffer[preallocSize]; + DISALLOW_COPY_AND_ASSIGN(ScopedArrayRO); +}; + +// ScopedNullable***ArrayRO provide convenient read-only access to Java array from JNI code. +// These accept nullptr. In that case, get() returns nullptr and size() returns 0. +using ScopedNullableBooleanArrayRO = ScopedArrayRO<jbooleanArray, jboolean, BooleanArrayTraits>; +using ScopedNullableByteArrayRO = ScopedArrayRO<jbyteArray, jbyte, ByteArrayTraits>; +using ScopedNullableCharArrayRO = ScopedArrayRO<jcharArray, jchar, CharArrayTraits>; +using ScopedNullableDoubleArrayRO = ScopedArrayRO<jdoubleArray, jdouble, DoubleArrayTraits>; +using ScopedNullableFloatArrayRO = ScopedArrayRO<jfloatArray, jfloat, FloatArrayTraits>; +using ScopedNullableIntArrayRO = ScopedArrayRO<jintArray, jint, IntArrayTraits>; +using ScopedNullableLongArrayRO = ScopedArrayRO<jlongArray, jlong, LongArrayTraits>; +using ScopedNullableShortArrayRO = ScopedArrayRO<jshortArray, jshort, ShortArrayTraits>; + +} // namespace android + +#endif // SCOPED_NULLABLE_PRIMITIVE_ARRAY_H diff --git a/libs/hwui/jni/text/LineBreaker.cpp b/libs/hwui/jni/text/LineBreaker.cpp new file mode 100644 index 000000000000..69865171a09d --- /dev/null +++ b/libs/hwui/jni/text/LineBreaker.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2014 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. + */ + +#undef LOG_TAG +#define LOG_TAG "LineBreaker" + +#include "utils/misc.h" +#include "utils/Log.h" +#include "graphics_jni_helpers.h" +#include <nativehelper/ScopedStringChars.h> +#include <nativehelper/ScopedPrimitiveArray.h> +#include "scoped_nullable_primitive_array.h" +#include <cstdint> +#include <vector> +#include <list> +#include <algorithm> + +#include "SkPaint.h" +#include "SkTypeface.h" +#include <hwui/MinikinSkia.h> +#include <hwui/MinikinUtils.h> +#include <hwui/Paint.h> +#include <minikin/FontCollection.h> +#include <minikin/AndroidLineBreakerHelper.h> +#include <minikin/MinikinFont.h> + +namespace android { + +static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray javaArray) { + if (javaArray == nullptr) { + return std::vector<float>(); + } else { + ScopedIntArrayRO intArr(env, javaArray); + return std::vector<float>(intArr.get(), intArr.get() + intArr.size()); + } +} + +static inline minikin::android::StaticLayoutNative* toNative(jlong ptr) { + return reinterpret_cast<minikin::android::StaticLayoutNative*>(ptr); +} + +// set text and set a number of parameters for creating a layout (width, tabstops, strategy, +// hyphenFrequency) +static jlong nInit(JNIEnv* env, jclass /* unused */, + jint breakStrategy, jint hyphenationFrequency, jboolean isJustified, jintArray indents) { + return reinterpret_cast<jlong>(new minikin::android::StaticLayoutNative( + static_cast<minikin::BreakStrategy>(breakStrategy), + static_cast<minikin::HyphenationFrequency>(hyphenationFrequency), + isJustified, + jintArrayToFloatVector(env, indents))); +} + +static void nFinish(jlong nativePtr) { + delete toNative(nativePtr); +} + +// CriticalNative +static jlong nGetReleaseFunc(CRITICAL_JNI_PARAMS) { + return reinterpret_cast<jlong>(nFinish); +} + +static jlong nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, + // Inputs + jcharArray javaText, + jlong measuredTextPtr, + jint length, + jfloat firstWidth, + jint firstWidthLineCount, + jfloat restWidth, + jfloatArray variableTabStops, + jfloat defaultTabStop, + jint indentsOffset) { + minikin::android::StaticLayoutNative* builder = toNative(nativePtr); + + ScopedCharArrayRO text(env, javaText); + ScopedNullableFloatArrayRO tabStops(env, variableTabStops); + + minikin::U16StringPiece u16Text(text.get(), length); + minikin::MeasuredText* measuredText = reinterpret_cast<minikin::MeasuredText*>(measuredTextPtr); + + std::unique_ptr<minikin::LineBreakResult> result = + std::make_unique<minikin::LineBreakResult>(builder->computeBreaks( + u16Text, *measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset, + tabStops.get(), tabStops.size(), defaultTabStop)); + return reinterpret_cast<jlong>(result.release()); +} + +static jint nGetLineCount(CRITICAL_JNI_PARAMS_COMMA jlong ptr) { + return reinterpret_cast<minikin::LineBreakResult*>(ptr)->breakPoints.size(); +} + +static jint nGetLineBreakOffset(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) { + return reinterpret_cast<minikin::LineBreakResult*>(ptr)->breakPoints[i]; +} + +static jfloat nGetLineWidth(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) { + return reinterpret_cast<minikin::LineBreakResult*>(ptr)->widths[i]; +} + +static jfloat nGetLineAscent(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) { + return reinterpret_cast<minikin::LineBreakResult*>(ptr)->ascents[i]; +} + +static jfloat nGetLineDescent(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) { + return reinterpret_cast<minikin::LineBreakResult*>(ptr)->descents[i]; +} + +static jint nGetLineFlag(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) { + return reinterpret_cast<minikin::LineBreakResult*>(ptr)->flags[i]; +} + +static void nReleaseResult(jlong ptr) { + delete reinterpret_cast<minikin::LineBreakResult*>(ptr); +} + +static jlong nGetReleaseResultFunc(CRITICAL_JNI_PARAMS) { + return reinterpret_cast<jlong>(nReleaseResult); +} + +static const JNINativeMethod gMethods[] = { + // Fast Natives + {"nInit", "(" + "I" // breakStrategy + "I" // hyphenationFrequency + "Z" // isJustified + "[I" // indents + ")J", (void*) nInit}, + + // Critical Natives + {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc}, + + // Regular JNI + {"nComputeLineBreaks", "(" + "J" // nativePtr + "[C" // text + "J" // MeasuredParagraph ptr. + "I" // length + "F" // firstWidth + "I" // firstWidthLineCount + "F" // restWidth + "[F" // variableTabStops + "F" // defaultTabStop + "I" // indentsOffset + ")J", (void*) nComputeLineBreaks}, + + // Result accessors, CriticalNatives + {"nGetLineCount", "(J)I", (void*)nGetLineCount}, + {"nGetLineBreakOffset", "(JI)I", (void*)nGetLineBreakOffset}, + {"nGetLineWidth", "(JI)F", (void*)nGetLineWidth}, + {"nGetLineAscent", "(JI)F", (void*)nGetLineAscent}, + {"nGetLineDescent", "(JI)F", (void*)nGetLineDescent}, + {"nGetLineFlag", "(JI)I", (void*)nGetLineFlag}, + {"nGetReleaseResultFunc", "()J", (void*)nGetReleaseResultFunc}, +}; + +int register_android_graphics_text_LineBreaker(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/text/LineBreaker", gMethods, + NELEM(gMethods)); +} + +} diff --git a/libs/hwui/jni/text/MeasuredText.cpp b/libs/hwui/jni/text/MeasuredText.cpp new file mode 100644 index 000000000000..7793746ee285 --- /dev/null +++ b/libs/hwui/jni/text/MeasuredText.cpp @@ -0,0 +1,167 @@ +/* + * 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. + */ + +#undef LOG_TAG +#define LOG_TAG "MeasuredText" + +#include "GraphicsJNI.h" +#include "utils/misc.h" +#include "utils/Log.h" +#include <nativehelper/ScopedStringChars.h> +#include <nativehelper/ScopedPrimitiveArray.h> +#include <cstdint> +#include <vector> +#include <list> +#include <algorithm> + +#include "SkPaint.h" +#include "SkTypeface.h" +#include <hwui/MinikinSkia.h> +#include <hwui/MinikinUtils.h> +#include <hwui/Paint.h> +#include <minikin/FontCollection.h> +#include <minikin/AndroidLineBreakerHelper.h> +#include <minikin/MinikinFont.h> + +namespace android { + +static inline minikin::MeasuredTextBuilder* toBuilder(jlong ptr) { + return reinterpret_cast<minikin::MeasuredTextBuilder*>(ptr); +} + +static inline Paint* toPaint(jlong ptr) { + return reinterpret_cast<Paint*>(ptr); +} + +static inline minikin::MeasuredText* toMeasuredParagraph(jlong ptr) { + return reinterpret_cast<minikin::MeasuredText*>(ptr); +} + +template<typename Ptr> static inline jlong toJLong(Ptr ptr) { + return reinterpret_cast<jlong>(ptr); +} + +static void releaseMeasuredParagraph(jlong measuredTextPtr) { + delete toMeasuredParagraph(measuredTextPtr); +} + +// Regular JNI +static jlong nInitBuilder(CRITICAL_JNI_PARAMS) { + return toJLong(new minikin::MeasuredTextBuilder()); +} + +// Regular JNI +static void nAddStyleRun(JNIEnv* /* unused */, jclass /* unused */, jlong builderPtr, + jlong paintPtr, jint start, jint end, jboolean isRtl) { + Paint* paint = toPaint(paintPtr); + const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface()); + minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface); + toBuilder(builderPtr)->addStyleRun(start, end, std::move(minikinPaint), isRtl); +} + +// Regular JNI +static void nAddReplacementRun(JNIEnv* /* unused */, jclass /* unused */, jlong builderPtr, + jlong paintPtr, jint start, jint end, jfloat width) { + toBuilder(builderPtr)->addReplacementRun(start, end, width, + toPaint(paintPtr)->getMinikinLocaleListId()); +} + +// Regular JNI +static jlong nBuildMeasuredText(JNIEnv* env, jclass /* unused */, jlong builderPtr, + jlong hintPtr, jcharArray javaText, jboolean computeHyphenation, + jboolean computeLayout) { + ScopedCharArrayRO text(env, javaText); + const minikin::U16StringPiece textBuffer(text.get(), text.size()); + + // Pass the ownership to Java. + return toJLong(toBuilder(builderPtr)->build(textBuffer, computeHyphenation, computeLayout, + toMeasuredParagraph(hintPtr)).release()); +} + +// Regular JNI +static void nFreeBuilder(JNIEnv* env, jclass /* unused */, jlong builderPtr) { + delete toBuilder(builderPtr); +} + +// CriticalNative +static jfloat nGetWidth(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint start, jint end) { + minikin::MeasuredText* mt = toMeasuredParagraph(ptr); + float r = 0.0f; + for (int i = start; i < end; ++i) { + r += mt->widths[i]; + } + return r; +} + +static jfloat nGetCharWidthAt(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint offset) { + return toMeasuredParagraph(ptr)->widths[offset]; +} + +// Regular JNI +static void nGetBounds(JNIEnv* env, jobject, jlong ptr, jcharArray javaText, jint start, jint end, + jobject bounds) { + ScopedCharArrayRO text(env, javaText); + const minikin::U16StringPiece textBuffer(text.get(), text.size()); + const minikin::Range range(start, end); + + minikin::MinikinRect rect = toMeasuredParagraph(ptr)->getBounds(textBuffer, range); + + SkRect r; + r.fLeft = rect.mLeft; + r.fTop = rect.mTop; + r.fRight = rect.mRight; + r.fBottom = rect.mBottom; + + SkIRect ir; + r.roundOut(&ir); + GraphicsJNI::irect_to_jrect(ir, env, bounds); +} + +// CriticalNative +static jlong nGetReleaseFunc(CRITICAL_JNI_PARAMS) { + return toJLong(&releaseMeasuredParagraph); +} + +static jint nGetMemoryUsage(CRITICAL_JNI_PARAMS_COMMA jlong ptr) { + return static_cast<jint>(toMeasuredParagraph(ptr)->getMemoryUsage()); +} + +static const JNINativeMethod gMTBuilderMethods[] = { + // MeasuredParagraphBuilder native functions. + {"nInitBuilder", "()J", (void*) nInitBuilder}, + {"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun}, + {"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun}, + {"nBuildMeasuredText", "(JJ[CZZ)J", (void*) nBuildMeasuredText}, + {"nFreeBuilder", "(J)V", (void*) nFreeBuilder}, +}; + +static const JNINativeMethod gMTMethods[] = { + // MeasuredParagraph native functions. + {"nGetWidth", "(JII)F", (void*) nGetWidth}, // Critical Natives + {"nGetBounds", "(J[CIILandroid/graphics/Rect;)V", (void*) nGetBounds}, // Regular JNI + {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc}, // Critical Natives + {"nGetMemoryUsage", "(J)I", (void*) nGetMemoryUsage}, // Critical Native + {"nGetCharWidthAt", "(JI)F", (void*) nGetCharWidthAt}, // Critical Native +}; + +int register_android_graphics_text_MeasuredText(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/text/MeasuredText", + gMTMethods, NELEM(gMTMethods)) + + RegisterMethodsOrDie(env, "android/graphics/text/MeasuredText$Builder", + gMTBuilderMethods, NELEM(gMTBuilderMethods)); +} + +} diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp new file mode 100644 index 000000000000..234f42d79cb7 --- /dev/null +++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2020 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. + */ + +#include "ATraceMemoryDump.h" + +#include <utils/Trace.h> + +#include <cstring> + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +// When purgeable is INVALID_MEMORY_SIZE it won't be logged at all. +#define INVALID_MEMORY_SIZE -1 + +/** + * Skia invokes the following SkTraceMemoryDump functions: + * 1. dumpNumericValue (dumpName, units="bytes", valueName="size") + * 2. dumpStringValue (dumpName, valueName="type") [optional -> for example CPU memory does not + * invoke dumpStringValue] + * 3. dumpNumericValue (dumpName, units="bytes", valueName="purgeable_size") [optional] + * 4. setMemoryBacking(dumpName, backingType) [optional -> for example Vulkan GPU resources do not + * invoke setMemoryBacking] + * + * ATraceMemoryDump calculates memory category first by looking at the "type" string passed to + * dumpStringValue and then by looking at "backingType" passed to setMemoryBacking. + * Only GPU Texture memory is tracked separately and everything else is grouped as one + * "Misc Memory" category. + */ +static std::unordered_map<const char*, const char*> sResourceMap = { + {"malloc", "HWUI CPU Memory"}, // taken from setMemoryBacking(backingType) + {"gl_texture", "HWUI Texture Memory"}, // taken from setMemoryBacking(backingType) + {"Texture", "HWUI Texture Memory"}, // taken from dumpStringValue(value, valueName="type") + // Uncomment categories below to split "Misc Memory" into more brackets for debugging. + /*{"vk_buffer", "vk_buffer"}, + {"gl_renderbuffer", "gl_renderbuffer"}, + {"gl_buffer", "gl_buffer"}, + {"RenderTarget", "RenderTarget"}, + {"Stencil", "Stencil"}, + {"Path Data", "Path Data"}, + {"Buffer Object", "Buffer Object"}, + {"Surface", "Surface"},*/ +}; + +ATraceMemoryDump::ATraceMemoryDump() { + mLastDumpName.reserve(100); + mCategory.reserve(100); +} + +void ATraceMemoryDump::dumpNumericValue(const char* dumpName, const char* valueName, + const char* units, uint64_t value) { + if (!strcmp(units, "bytes")) { + recordAndResetCountersIfNeeded(dumpName); + if (!strcmp(valueName, "size")) { + mLastDumpValue = value; + } else if (!strcmp(valueName, "purgeable_size")) { + mLastPurgeableDumpValue = value; + } + } +} + +void ATraceMemoryDump::dumpStringValue(const char* dumpName, const char* valueName, + const char* value) { + if (!strcmp(valueName, "type")) { + recordAndResetCountersIfNeeded(dumpName); + auto categoryIt = sResourceMap.find(value); + if (categoryIt != sResourceMap.end()) { + mCategory = categoryIt->second; + } + } +} + +void ATraceMemoryDump::setMemoryBacking(const char* dumpName, const char* backingType, + const char* backingObjectId) { + recordAndResetCountersIfNeeded(dumpName); + auto categoryIt = sResourceMap.find(backingType); + if (categoryIt != sResourceMap.end()) { + mCategory = categoryIt->second; + } +} + +/** + * startFrame is invoked before dumping anything. It resets counters from the previous frame. + * This is important, because if there is no new data for a given category trace would assume + * usage has not changed (instead of reporting 0). + */ +void ATraceMemoryDump::startFrame() { + resetCurrentCounter(""); + for (auto& it : mCurrentValues) { + // Once a category is observed in at least one frame, it is always reported in subsequent + // frames (even if it is 0). Not logging a category to ATRACE would mean its value has not + // changed since the previous frame, which is not what we want. + it.second.memory = 0; + // If purgeableMemory is INVALID_MEMORY_SIZE, then logTraces won't log it at all. + if (it.second.purgeableMemory != INVALID_MEMORY_SIZE) { + it.second.purgeableMemory = 0; + } + } +} + +/** + * logTraces reads from mCurrentValues and logs the counters with ATRACE. + */ +void ATraceMemoryDump::logTraces() { + // Accumulate data from last dumpName + recordAndResetCountersIfNeeded(""); + uint64_t hwui_all_frame_memory = 0; + for (auto& it : mCurrentValues) { + hwui_all_frame_memory += it.second.memory; + ATRACE_INT64(it.first.c_str(), it.second.memory); + if (it.second.purgeableMemory != INVALID_MEMORY_SIZE) { + ATRACE_INT64((std::string("Purgeable ") + it.first).c_str(), it.second.purgeableMemory); + } + } + ATRACE_INT64("HWUI All Memory", hwui_all_frame_memory); +} + +/** + * recordAndResetCountersIfNeeded reads memory usage from mLastDumpValue/mLastPurgeableDumpValue and + * accumulates in mCurrentValues[category]. It makes provision to create a new category and track + * purgeable memory only if there is at least one observation. + * recordAndResetCountersIfNeeded won't do anything until all the information for a given dumpName + * is received. + */ +void ATraceMemoryDump::recordAndResetCountersIfNeeded(const char* dumpName) { + if (!mLastDumpName.compare(dumpName)) { + // Still waiting for more data for current dumpName. + return; + } + + // First invocation will have an empty mLastDumpName. + if (!mLastDumpName.empty()) { + // A new dumpName observed -> store the data already collected. + auto memoryCounter = mCurrentValues.find(mCategory); + if (memoryCounter != mCurrentValues.end()) { + memoryCounter->second.memory += mLastDumpValue; + if (mLastPurgeableDumpValue != INVALID_MEMORY_SIZE) { + if (memoryCounter->second.purgeableMemory == INVALID_MEMORY_SIZE) { + memoryCounter->second.purgeableMemory = mLastPurgeableDumpValue; + } else { + memoryCounter->second.purgeableMemory += mLastPurgeableDumpValue; + } + } + } else { + mCurrentValues[mCategory] = {mLastDumpValue, mLastPurgeableDumpValue}; + } + } + + // Reset counters and default category for the newly observed "dumpName". + resetCurrentCounter(dumpName); +} + +void ATraceMemoryDump::resetCurrentCounter(const char* dumpName) { + mLastDumpValue = 0; + mLastPurgeableDumpValue = INVALID_MEMORY_SIZE; + mLastDumpName = dumpName; + // Categories not listed in sResourceMap are reported as "Misc Memory" + mCategory = "HWUI Misc Memory"; +} + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.h b/libs/hwui/pipeline/skia/ATraceMemoryDump.h new file mode 100644 index 000000000000..4592711dd5b5 --- /dev/null +++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include <SkString.h> +#include <SkTraceMemoryDump.h> + +#include <string> +#include <unordered_map> +#include <utility> + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +class ATraceMemoryDump : public SkTraceMemoryDump { +public: + ATraceMemoryDump(); + ~ATraceMemoryDump() override {} + + void dumpNumericValue(const char* dumpName, const char* valueName, const char* units, + uint64_t value) override; + + void dumpStringValue(const char* dumpName, const char* valueName, const char* value) override; + + LevelOfDetail getRequestedDetails() const override { + return SkTraceMemoryDump::kLight_LevelOfDetail; + } + + bool shouldDumpWrappedObjects() const override { return false; } + + void setMemoryBacking(const char* dumpName, const char* backingType, + const char* backingObjectId) override; + + void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override {} + + void startFrame(); + + void logTraces(); + +private: + std::string mLastDumpName; + + uint64_t mLastDumpValue; + + uint64_t mLastPurgeableDumpValue; + + std::string mCategory; + + struct TraceValue { + uint64_t memory; + uint64_t purgeableMemory; + }; + + // keys are define in sResourceMap + std::unordered_map<std::string, TraceValue> mCurrentValues; + + void recordAndResetCountersIfNeeded(const char* dumpName); + + void resetCurrentCounter(const char* dumpName); +}; + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */
\ No newline at end of file diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp index a1b2b18195bc..8f67f97fb4bc 100644 --- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp @@ -26,6 +26,7 @@ #include "SkAndroidFrameworkUtils.h" #include "SkClipStack.h" #include "SkRect.h" +#include "include/private/SkM44.h" namespace android { namespace uirenderer { @@ -47,29 +48,20 @@ static void setScissor(int viewportHeight, const SkIRect& clip) { glScissor(clip.fLeft, y, clip.width(), height); } -static bool GetFboDetails(SkCanvas* canvas, GLuint* outFboID, SkISize* outFboSize) { +static void GetFboDetails(SkCanvas* canvas, GLuint* outFboID, SkISize* outFboSize) { GrRenderTargetContext* renderTargetContext = canvas->internal_private_accessTopLayerRenderTargetContext(); - if (!renderTargetContext) { - ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw"); - return false; - } + LOG_ALWAYS_FATAL_IF(!renderTargetContext, "Failed to retrieve GrRenderTargetContext"); GrRenderTarget* renderTarget = renderTargetContext->accessRenderTarget(); - if (!renderTarget) { - ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw"); - return false; - } + LOG_ALWAYS_FATAL_IF(!renderTarget, "accessRenderTarget failed"); GrGLFramebufferInfo fboInfo; - if (!renderTarget->getBackendRenderTarget().getGLFramebufferInfo(&fboInfo)) { - ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw"); - return false; - } + LOG_ALWAYS_FATAL_IF(!renderTarget->getBackendRenderTarget().getGLFramebufferInfo(&fboInfo), + "getGLFrameBufferInfo failed"); *outFboID = fboInfo.fFBOID; *outFboSize = SkISize::Make(renderTargetContext->width(), renderTargetContext->height()); - return true; } void GLFunctorDrawable::onDraw(SkCanvas* canvas) { @@ -84,15 +76,16 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { return; } + // flush will create a GrRenderTarget if not already present. + canvas->flush(); + GLuint fboID = 0; SkISize fboSize; - if (!GetFboDetails(canvas, &fboID, &fboSize)) { - return; - } + GetFboDetails(canvas, &fboID, &fboSize); SkIRect surfaceBounds = canvas->internal_private_getTopLayerBounds(); SkIRect clipBounds = canvas->getDeviceClipBounds(); - SkMatrix44 mat4(canvas->getTotalMatrix()); + SkM44 mat4(canvas->experimental_getLocalToDevice()); SkRegion clipRegion; canvas->temporary_internal_getRgnClip(&clipRegion); @@ -118,7 +111,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { // update the matrix and clip that we pass to the WebView to match the coordinates of // the offscreen layer - mat4.preTranslate(-clipBounds.fLeft, -clipBounds.fTop, 0); + mat4.preTranslate(-clipBounds.fLeft, -clipBounds.fTop); clipBounds.offsetTo(0, 0); clipRegion.translate(-surfaceBounds.fLeft, -surfaceBounds.fTop); @@ -126,7 +119,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { // we are drawing into a (clipped) offscreen layer so we must update the clip and matrix // from device coordinates to the layer's coordinates clipBounds.offset(-surfaceBounds.fLeft, -surfaceBounds.fTop); - mat4.preTranslate(-surfaceBounds.fLeft, -surfaceBounds.fTop, 0); + mat4.preTranslate(-surfaceBounds.fLeft, -surfaceBounds.fTop); } DrawGlInfo info; @@ -137,12 +130,11 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { info.isLayer = fboID != 0; info.width = fboSize.width(); info.height = fboSize.height(); - mat4.asColMajorf(&info.transform[0]); + mat4.getColMajor(&info.transform[0]); info.color_space_ptr = canvas->imageInfo().colorSpace(); // ensure that the framebuffer that the webview will render into is bound before we clear // the stencil and/or draw the functor. - canvas->flush(); glViewport(0, 0, info.width, info.height); glBindFramebuffer(GL_FRAMEBUFFER, fboID); diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index 1bd30eb5371b..00ceb2d84f9e 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -18,7 +18,6 @@ #include <SkPaintFilterCanvas.h> #include "RenderNode.h" #include "SkiaDisplayList.h" -#include "SkiaPipeline.h" #include "utils/TraceUtils.h" #include <optional> @@ -42,7 +41,7 @@ RenderNodeDrawable::~RenderNodeDrawable() { void RenderNodeDrawable::drawBackwardsProjectedNodes(SkCanvas* canvas, const SkiaDisplayList& displayList, - int nestLevel) { + int nestLevel) const { LOG_ALWAYS_FATAL_IF(0 == nestLevel && !displayList.mProjectionReceiver); for (auto& child : displayList.mChildNodes) { const RenderProperties& childProperties = child.getNodeProperties(); @@ -133,7 +132,7 @@ private: RenderNode& mNode; }; -void RenderNodeDrawable::forceDraw(SkCanvas* canvas) { +void RenderNodeDrawable::forceDraw(SkCanvas* canvas) const { RenderNode* renderNode = mRenderNode.get(); MarkDraw _marker{*canvas, *renderNode}; @@ -187,17 +186,13 @@ public: AlphaFilterCanvas(SkCanvas* canvas, float alpha) : SkPaintFilterCanvas(canvas), mAlpha(alpha) {} protected: - bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type t) const override { - std::optional<SkPaint> defaultPaint; - if (!*paint) { - paint->init(defaultPaint.emplace()); - } - paint->writable()->setAlpha((uint8_t)(*paint)->getAlpha() * mAlpha); + bool onFilter(SkPaint& paint) const override { + paint.setAlpha((uint8_t)paint.getAlpha() * mAlpha); return true; } void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override { // We unroll the drawable using "this" canvas, so that draw calls contained inside will - // get their alpha applied. THe default SkPaintFilterCanvas::onDrawDrawable does not unroll. + // get their alpha applied. The default SkPaintFilterCanvas::onDrawDrawable does not unroll. drawable->draw(this, matrix); } @@ -235,7 +230,14 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { // we need to restrict the portion of the surface drawn to the size of the renderNode. SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width()); SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height()); - canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(), bounds, + + // If SKP recording is active save an annotation that indicates this drawImageRect + // could also be rendered with the commands saved at ID associated with this node. + if (CC_UNLIKELY(Properties::skpCaptureEnabled)) { + canvas->drawAnnotation(bounds, String8::format( + "SurfaceID|%" PRId64, renderNode->uniqueId()).c_str(), nullptr); + } + canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot(), bounds, bounds, &paint); if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) { diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h index 6ba8e599818c..6c390c3fce24 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.h +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h @@ -58,7 +58,7 @@ public: * projection receiver then all projected children (excluding direct children) will be drawn * last. Any projected node not matching those requirements will not be drawn by this function. */ - void forceDraw(SkCanvas* canvas); + void forceDraw(SkCanvas* canvas) const; /** * Returns readonly render properties for this render node. @@ -113,7 +113,7 @@ private: * @param nestLevel should be always 0. Used to track how far we are from the receiver. */ void drawBackwardsProjectedNodes(SkCanvas* canvas, const SkiaDisplayList& displayList, - int nestLevel = 0); + int nestLevel = 0) const; /** * Applies the rendering properties of a view onto a SkCanvas. diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp index 0a3c8f4347eb..3b8caeb3aab1 100644 --- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp +++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp @@ -17,7 +17,7 @@ #include "ReorderBarrierDrawables.h" #include "RenderNode.h" #include "SkiaDisplayList.h" -#include "SkiaPipeline.h" +#include "LightingInfo.h" #include <SkPathOps.h> #include <SkShadowUtils.h> @@ -27,7 +27,7 @@ namespace uirenderer { namespace skiapipeline { StartReorderBarrierDrawable::StartReorderBarrierDrawable(SkiaDisplayList* data) - : mEndChildIndex(0), mBeginChildIndex(data->mChildNodes.size()), mDisplayList(data) {} + : mEndChildIndex(-1), mBeginChildIndex(data->mChildNodes.size()), mDisplayList(data) {} void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) { if (mChildren.empty()) { @@ -139,8 +139,8 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* return; } - float ambientAlpha = (SkiaPipeline::getAmbientShadowAlpha() / 255.f) * casterAlpha; - float spotAlpha = (SkiaPipeline::getSpotShadowAlpha() / 255.f) * casterAlpha; + float ambientAlpha = (LightingInfo::getAmbientShadowAlpha() / 255.f) * casterAlpha; + float spotAlpha = (LightingInfo::getSpotShadowAlpha() / 255.f) * casterAlpha; const RevealClip& revealClip = casterProperties.getRevealClip(); const SkPath* revealClipPath = revealClip.getPath(); @@ -192,7 +192,7 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* casterPath = &tmpPath; } - const Vector3 lightPos = SkiaPipeline::getLightCenter(); + const Vector3 lightPos = LightingInfo::getLightCenter(); SkPoint3 skiaLightPos = SkPoint3::Make(lightPos.x, lightPos.y, lightPos.z); SkPoint3 zParams; if (shadowMatrix.hasPerspective()) { @@ -206,7 +206,7 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* SkColor ambientColor = multiplyAlpha(casterProperties.getAmbientShadowColor(), ambientAlpha); SkColor spotColor = multiplyAlpha(casterProperties.getSpotShadowColor(), spotAlpha); SkShadowUtils::DrawShadow( - canvas, *casterPath, zParams, skiaLightPos, SkiaPipeline::getLightRadius(), + canvas, *casterPath, zParams, skiaLightPos, LightingInfo::getLightRadius(), ambientColor, spotColor, casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0); } diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h index cfc0f9b258da..d669f84c5ee2 100644 --- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h +++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h @@ -21,7 +21,7 @@ #include <SkCanvas.h> #include <SkDrawable.h> -#include <utils/FatVector.h> +#include <ui/FatVector.h> namespace android { namespace uirenderer { diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp index 41bcfc25f5c1..158c3493a90c 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -15,11 +15,18 @@ */ #include "SkiaDisplayList.h" +#include "FunctorDrawable.h" #include "DumpOpsCanvas.h" +#ifdef __ANDROID__ // Layoutlib does not support SkiaPipeline #include "SkiaPipeline.h" +#else +#include "DamageAccumulator.h" +#endif #include "VectorDrawable.h" +#ifdef __ANDROID__ #include "renderthread/CanvasContext.h" +#endif #include <SkImagePriv.h> #include <SkPathOps.h> @@ -81,6 +88,7 @@ bool SkiaDisplayList::prepareListAndChildren( // If the prepare tree is triggered by the UI thread and no previous call to // pinImages has failed then we must pin all mutable images in the GPU cache // until the next UI thread draw. +#ifdef __ANDROID__ // Layoutlib does not support CanvasContext if (info.prepareTextures && !info.canvasContext.pinImages(mMutableImages)) { // In the event that pinning failed we prevent future pinImage calls for the // remainder of this tree traversal and also unpin any currently pinned images @@ -88,6 +96,7 @@ bool SkiaDisplayList::prepareListAndChildren( info.prepareTextures = false; info.canvasContext.unpinImages(); } +#endif bool hasBackwardProjectedNodesHere = false; bool hasBackwardProjectedNodesSubtree = false; @@ -131,20 +140,16 @@ bool SkiaDisplayList::prepareListAndChildren( } } - for (auto& vectorDrawablePair : mVectorDrawables) { + for (auto& [vectorDrawable, cachedMatrix] : mVectorDrawables) { // If any vector drawable in the display list needs update, damage the node. - auto& vectorDrawable = vectorDrawablePair.first; if (vectorDrawable->isDirty()) { Matrix4 totalMatrix; info.damageAccumulator->computeCurrentTransform(&totalMatrix); - Matrix4 canvasMatrix(vectorDrawablePair.second); + Matrix4 canvasMatrix(cachedMatrix); totalMatrix.multiply(canvasMatrix); const SkRect& bounds = vectorDrawable->properties().getBounds(); if (intersects(info.screenSize, totalMatrix, bounds)) { isDirty = true; - static_cast<SkiaPipeline*>(info.canvasContext.getRenderPipeline()) - ->getVectorDrawables() - ->push_back(vectorDrawable); vectorDrawable->setPropertyChangeWillBeConsumed(true); } } diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h index b79103787023..cdd00db9afdc 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.h +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h @@ -16,7 +16,6 @@ #pragma once -#include "FunctorDrawable.h" #include "RecordingCanvas.h" #include "RenderNodeDrawable.h" #include "TreeInfo.h" @@ -34,6 +33,7 @@ class CanvasContext; } class Outline; +struct WebViewSyncData; namespace VectorDrawable { class Tree; @@ -42,9 +42,12 @@ typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot; namespace skiapipeline { +class FunctorDrawable; + class SkiaDisplayList { public: size_t getUsedSize() { return allocator.usedSize() + mDisplayList.usedSize(); } + size_t getAllocatedSize() { return allocator.allocatedSize() + mDisplayList.allocatedSize(); } ~SkiaDisplayList() { /* Given that we are using a LinearStdAllocator to store some of the diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 8092b1d10659..24a6228242a5 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -18,6 +18,7 @@ #include "DeferredLayerUpdater.h" #include "LayerDrawable.h" +#include "LightingInfo.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" #include "hwui/Bitmap.h" @@ -99,7 +100,7 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, con mRenderThread.getGrContext(), backendRT, this->getSurfaceOrigin(), colorType, mSurfaceColorSpace, &props)); - SkiaPipeline::updateLighting(lightGeometry, lightInfo); + LightingInfo::updateLighting(lightGeometry, lightInfo); renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, SkMatrix::I()); layerUpdateQueue->clear(); @@ -156,31 +157,15 @@ void SkiaOpenGLPipeline::onStop() { } } -static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) { - int query_value; - int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value); - if (err != 0 || query_value < 0) { - ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value); - return; - } - auto min_undequeued_buffers = static_cast<uint32_t>(query_value); - - int bufferCount = min_undequeued_buffers + 2 + extraBuffers; - native_window_set_buffer_count(window, bufferCount); -} - -bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior, - ColorMode colorMode, uint32_t extraBuffers) { +bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior) { if (mEglSurface != EGL_NO_SURFACE) { mEglManager.destroySurface(mEglSurface); mEglSurface = EGL_NO_SURFACE; } - setSurfaceColorProperties(colorMode); - if (surface) { mRenderThread.requireGlContext(); - auto newSurface = mEglManager.createSurface(surface, colorMode, mSurfaceColorSpace); + auto newSurface = mEglManager.createSurface(surface, mColorMode, mSurfaceColorSpace); if (!newSurface) { return false; } @@ -190,7 +175,6 @@ bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBeh if (mEglSurface != EGL_NO_SURFACE) { const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer); mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer); - setBufferCount(surface, extraBuffers); return true; } diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index 3fe0f92b1924..fddd97f1c5b3 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -16,8 +16,10 @@ #pragma once -#include "SkiaPipeline.h" +#include <EGL/egl.h> +#include <system/window.h> +#include "SkiaPipeline.h" #include "renderstate/RenderState.h" namespace android { @@ -43,8 +45,7 @@ public: bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) override; DeferredLayerUpdater* createTextureLayer() override; - bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior, - renderthread::ColorMode colorMode, uint32_t extraBuffers) override; + bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior) override; void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 1f9ab5a242b4..5088494d6a07 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -19,31 +19,33 @@ #include <SkImageEncoder.h> #include <SkImageInfo.h> #include <SkImagePriv.h> +#include <SkMultiPictureDocument.h> #include <SkOverdrawCanvas.h> #include <SkOverdrawColorFilter.h> #include <SkPicture.h> #include <SkPictureRecorder.h> -#include "TreeInfo.h" +#include <SkSerialProcs.h> +#include <SkTypeface.h> +#include <android-base/properties.h> +#include <unistd.h> + +#include <sstream> + +#include "LightingInfo.h" #include "VectorDrawable.h" #include "thread/CommonPool.h" +#include "tools/SkSharingProc.h" +#include "utils/String8.h" #include "utils/TraceUtils.h" -#include <unistd.h> - using namespace android::uirenderer::renderthread; namespace android { namespace uirenderer { namespace skiapipeline { -float SkiaPipeline::mLightRadius = 0; -uint8_t SkiaPipeline::mAmbientShadowAlpha = 0; -uint8_t SkiaPipeline::mSpotShadowAlpha = 0; - -Vector3 SkiaPipeline::mLightCenter = {FLT_MIN, FLT_MIN, FLT_MIN}; - SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) { - mVectorDrawables.reserve(30); + setSurfaceColorProperties(mColorMode); } SkiaPipeline::~SkiaPipeline() { @@ -73,18 +75,11 @@ void SkiaPipeline::unpinImages() { mPinnedImages.clear(); } -void SkiaPipeline::onPrepareTree() { - // The only time mVectorDrawables is not empty is if prepare tree was called 2 times without - // a renderFrame in the middle. - mVectorDrawables.clear(); -} - void SkiaPipeline::renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, bool opaque, const LightInfo& lightInfo) { - updateLighting(lightGeometry, lightInfo); + LightingInfo::updateLighting(lightGeometry, lightInfo); ATRACE_NAME("draw layers"); - renderVectorDrawableCache(); renderLayersImpl(*layerUpdateQueue, opaque); layerUpdateQueue->clear(); } @@ -98,54 +93,61 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) // only schedule repaint if node still on layer - possible it may have been // removed during a dropped frame, but layers may still remain scheduled so // as not to lose info on what portion is damaged - if (CC_LIKELY(layerNode->getLayerSurface() != nullptr)) { - SkASSERT(layerNode->getLayerSurface()); - SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList(); - if (!displayList || displayList->isEmpty()) { - SkDEBUGF(("%p drawLayers(%s) : missing drawable", layerNode, layerNode->getName())); - return; - } + if (CC_UNLIKELY(layerNode->getLayerSurface() == nullptr)) { + continue; + } + SkASSERT(layerNode->getLayerSurface()); + SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList(); + if (!displayList || displayList->isEmpty()) { + ALOGE("%p drawLayers(%s) : missing drawable", layerNode, layerNode->getName()); + return; + } - const Rect& layerDamage = layers.entries()[i].damage; + const Rect& layerDamage = layers.entries()[i].damage; - SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas(); + SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas(); - int saveCount = layerCanvas->save(); - SkASSERT(saveCount == 1); + int saveCount = layerCanvas->save(); + SkASSERT(saveCount == 1); - layerCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect()); + layerCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect()); - auto savedLightCenter = mLightCenter; - // map current light center into RenderNode's coordinate space - layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(mLightCenter); + // TODO: put localized light center calculation and storage to a drawable related code. + // It does not seem right to store something localized in a global state + // fix here and in recordLayers + const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw()); + Vector3 transformedLightCenter(savedLightCenter); + // map current light center into RenderNode's coordinate space + layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(transformedLightCenter); + LightingInfo::setLightCenterRaw(transformedLightCenter); - const RenderProperties& properties = layerNode->properties(); - const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight()); - if (properties.getClipToBounds() && layerCanvas->quickReject(bounds)) { - return; - } + const RenderProperties& properties = layerNode->properties(); + const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight()); + if (properties.getClipToBounds() && layerCanvas->quickReject(bounds)) { + return; + } + + ATRACE_FORMAT("drawLayer [%s] %.1f x %.1f", layerNode->getName(), bounds.width(), + bounds.height()); - ATRACE_FORMAT("drawLayer [%s] %.1f x %.1f", layerNode->getName(), bounds.width(), - bounds.height()); - - layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false; - layerCanvas->clear(SK_ColorTRANSPARENT); - - RenderNodeDrawable root(layerNode, layerCanvas, false); - root.forceDraw(layerCanvas); - layerCanvas->restoreToCount(saveCount); - mLightCenter = savedLightCenter; - - // cache the current context so that we can defer flushing it until - // either all the layers have been rendered or the context changes - GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext(); - if (cachedContext.get() != currentContext) { - if (cachedContext.get()) { - ATRACE_NAME("flush layers (context changed)"); - cachedContext->flush(); - } - cachedContext.reset(SkSafeRef(currentContext)); + layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false; + layerCanvas->clear(SK_ColorTRANSPARENT); + + RenderNodeDrawable root(layerNode, layerCanvas, false); + root.forceDraw(layerCanvas); + layerCanvas->restoreToCount(saveCount); + + LightingInfo::setLightCenterRaw(savedLightCenter); + + // cache the current context so that we can defer flushing it until + // either all the layers have been rendered or the context changes + GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext(); + if (cachedContext.get() != currentContext) { + if (cachedContext.get()) { + ATRACE_NAME("flush layers (context changed)"); + cachedContext->flush(); } + cachedContext.reset(SkSafeRef(currentContext)); } } @@ -209,19 +211,6 @@ void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) { } } -void SkiaPipeline::renderVectorDrawableCache() { - if (!mVectorDrawables.empty()) { - sp<VectorDrawableAtlas> atlas = mRenderThread.cacheManager().acquireVectorDrawableAtlas(); - auto grContext = mRenderThread.getGrContext(); - atlas->prepareForDraw(grContext); - ATRACE_NAME("Update VectorDrawables"); - for (auto vd : mVectorDrawables) { - vd->updateCache(atlas, grContext); - } - mVectorDrawables.clear(); - } -} - static void savePictureAsync(const sk_sp<SkData>& data, const std::string& filename) { CommonPool::post([data, filename] { if (0 == access(filename.c_str(), F_OK)) { @@ -232,57 +221,204 @@ static void savePictureAsync(const sk_sp<SkData>& data, const std::string& filen if (stream.isValid()) { stream.write(data->data(), data->size()); stream.flush(); - SkDebugf("SKP Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(), + ALOGD("SKP Captured Drawing Output (%zu bytes) for frame. %s", stream.bytesWritten(), filename.c_str()); } }); } -SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) { - if (CC_UNLIKELY(Properties::skpCaptureEnabled)) { - char prop[PROPERTY_VALUE_MAX] = {'\0'}; +// Note multiple SkiaPipeline instances may be loaded if more than one app is visible. +// Each instance may observe the filename changing and try to record to a file of the same name. +// Only the first one will succeed. There is no scope available here where we could coordinate +// to cause this function to return true for only one of the instances. +bool SkiaPipeline::shouldStartNewFileCapture() { + // Don't start a new file based capture if one is currently ongoing. + if (mCaptureMode != CaptureMode::None) { return false; } + + // A new capture is started when the filename property changes. + // Read the filename property. + std::string prop = base::GetProperty(PROPERTY_CAPTURE_SKP_FILENAME, "0"); + // if the filename property changed to a valid value + if (prop[0] != '0' && mCapturedFile != prop) { + // remember this new filename + mCapturedFile = prop; + // and get a property indicating how many frames to capture. + mCaptureSequence = base::GetIntProperty(PROPERTY_CAPTURE_SKP_FRAMES, 1); if (mCaptureSequence <= 0) { - property_get(PROPERTY_CAPTURE_SKP_FILENAME, prop, "0"); - if (prop[0] != '0' && mCapturedFile != prop) { - mCapturedFile = prop; - mCaptureSequence = property_get_int32(PROPERTY_CAPTURE_SKP_FRAMES, 1); - } + return false; + } else if (mCaptureSequence == 1) { + mCaptureMode = CaptureMode::SingleFrameSKP; + } else { + mCaptureMode = CaptureMode::MultiFrameSKP; } - if (mCaptureSequence > 0 || mPictureCapturedCallback) { - mRecorder.reset(new SkPictureRecorder()); - SkCanvas* pictureCanvas = - mRecorder->beginRecording(surface->width(), surface->height(), nullptr, - SkPictureRecorder::kPlaybackDrawPicture_RecordFlag); - mNwayCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height()); - mNwayCanvas->addCanvas(surface->getCanvas()); - mNwayCanvas->addCanvas(pictureCanvas); - return mNwayCanvas.get(); + return true; + } + return false; +} + +// performs the first-frame work of a multi frame SKP capture. Returns true if successful. +bool SkiaPipeline::setupMultiFrameCapture() { + ALOGD("Set up multi-frame capture, frames = %d", mCaptureSequence); + // We own this stream and need to hold it until close() finishes. + auto stream = std::make_unique<SkFILEWStream>(mCapturedFile.c_str()); + if (stream->isValid()) { + mOpenMultiPicStream = std::move(stream); + mSerialContext.reset(new SkSharingSerialContext()); + SkSerialProcs procs; + procs.fImageProc = SkSharingSerialContext::serializeImage; + procs.fImageCtx = mSerialContext.get(); + procs.fTypefaceProc = [](SkTypeface* tf, void* ctx){ + return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData); + }; + // SkDocuments don't take owership of the streams they write. + // we need to keep it until after mMultiPic.close() + // procs is passed as a pointer, but just as a method of having an optional default. + // procs doesn't need to outlive this Make call. + mMultiPic = SkMakeMultiPictureDocument(mOpenMultiPicStream.get(), &procs); + return true; + } else { + ALOGE("Could not open \"%s\" for writing.", mCapturedFile.c_str()); + mCaptureSequence = 0; + mCaptureMode = CaptureMode::None; + return false; + } +} + +// recurse through the rendernode's children, add any nodes which are layers to the queue. +static void collectLayers(RenderNode* node, LayerUpdateQueue* layers) { + SkiaDisplayList* dl = (SkiaDisplayList*)node->getDisplayList(); + if (dl) { + const auto& prop = node->properties(); + if (node->hasLayer()) { + layers->enqueueLayerWithDamage(node, Rect(prop.getWidth(), prop.getHeight())); + } + // The way to recurse through rendernodes is to call this with a lambda. + dl->updateChildren([&](RenderNode* child) { collectLayers(child, layers); }); + } +} + +// record the provided layers to the provided canvas as self-contained skpictures. +static void recordLayers(const LayerUpdateQueue& layers, + SkCanvas* mskpCanvas) { + const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw()); + // Record the commands to re-draw each dirty layer into an SkPicture + for (size_t i = 0; i < layers.entries().size(); i++) { + RenderNode* layerNode = layers.entries()[i].renderNode.get(); + const Rect& layerDamage = layers.entries()[i].damage; + const RenderProperties& properties = layerNode->properties(); + + // Temporarily map current light center into RenderNode's coordinate space + Vector3 transformedLightCenter(savedLightCenter); + layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(transformedLightCenter); + LightingInfo::setLightCenterRaw(transformedLightCenter); + + SkPictureRecorder layerRec; + auto* recCanvas = layerRec.beginRecording(properties.getWidth(), + properties.getHeight()); + // This is not recorded but still causes clipping. + recCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect()); + RenderNodeDrawable root(layerNode, recCanvas, false); + root.forceDraw(recCanvas); + // Now write this picture into the SKP canvas with an annotation indicating what it is + mskpCanvas->drawAnnotation(layerDamage.toSkRect(), String8::format( + "OffscreenLayerDraw|%" PRId64, layerNode->uniqueId()).c_str(), nullptr); + mskpCanvas->drawPicture(layerRec.finishRecordingAsPicture()); + } + LightingInfo::setLightCenterRaw(savedLightCenter); +} + +SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface, RenderNode* root, + const LayerUpdateQueue& dirtyLayers) { + if (CC_LIKELY(!Properties::skpCaptureEnabled)) { + return surface->getCanvas(); // Bail out early when capture is not turned on. + } + // Note that shouldStartNewFileCapture tells us if this is the *first* frame of a capture. + bool firstFrameOfAnim = false; + if (shouldStartNewFileCapture() && mCaptureMode == CaptureMode::MultiFrameSKP) { + // set a reminder to record every layer near the end of this method, after we have set up + // the nway canvas. + firstFrameOfAnim = true; + if (!setupMultiFrameCapture()) { + return surface->getCanvas(); } } - return surface->getCanvas(); + + // Create a canvas pointer, fill it depending on what kind of capture is requested (if any) + SkCanvas* pictureCanvas = nullptr; + switch (mCaptureMode) { + case CaptureMode::CallbackAPI: + case CaptureMode::SingleFrameSKP: + mRecorder.reset(new SkPictureRecorder()); + pictureCanvas = mRecorder->beginRecording(surface->width(), surface->height()); + break; + case CaptureMode::MultiFrameSKP: + // If a multi frame recording is active, initialize recording for a single frame of a + // multi frame file. + pictureCanvas = mMultiPic->beginPage(surface->width(), surface->height()); + break; + case CaptureMode::None: + // Returning here in the non-capture case means we can count on pictureCanvas being + // non-null below. + return surface->getCanvas(); + } + + // Setting up an nway canvas is common to any kind of capture. + mNwayCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height()); + mNwayCanvas->addCanvas(surface->getCanvas()); + mNwayCanvas->addCanvas(pictureCanvas); + + if (firstFrameOfAnim) { + // On the first frame of any mskp capture we want to record any layers that are needed in + // frame but may have been rendered offscreen before recording began. + // We do not maintain a list of all layers, since it isn't needed outside this rare, + // recording use case. Traverse the tree to find them and put them in this LayerUpdateQueue. + LayerUpdateQueue luq; + collectLayers(root, &luq); + recordLayers(luq, mNwayCanvas.get()); + } else { + // on non-first frames, we record any normal layer draws (dirty regions) + recordLayers(dirtyLayers, mNwayCanvas.get()); + } + + return mNwayCanvas.get(); } void SkiaPipeline::endCapture(SkSurface* surface) { + if (CC_LIKELY(mCaptureMode == CaptureMode::None)) { return; } mNwayCanvas.reset(); - if (CC_UNLIKELY(mRecorder.get())) { - ATRACE_CALL(); + ATRACE_CALL(); + if (mCaptureSequence > 0 && mCaptureMode == CaptureMode::MultiFrameSKP) { + mMultiPic->endPage(); + mCaptureSequence--; + if (mCaptureSequence == 0) { + mCaptureMode = CaptureMode::None; + // Pass mMultiPic and mOpenMultiPicStream to a background thread, which will handle + // the heavyweight serialization work and destroy them. mOpenMultiPicStream is released + // to a bare pointer because keeping it in a smart pointer makes the lambda + // non-copyable. The lambda is only called once, so this is safe. + SkFILEWStream* stream = mOpenMultiPicStream.release(); + CommonPool::post([doc = std::move(mMultiPic), stream]{ + ALOGD("Finalizing multi frame SKP"); + doc->close(); + delete stream; + ALOGD("Multi frame SKP complete."); + }); + } + } else { sk_sp<SkPicture> picture = mRecorder->finishRecordingAsPicture(); if (picture->approximateOpCount() > 0) { - if (mCaptureSequence > 0) { - ATRACE_BEGIN("picture->serialize"); - auto data = picture->serialize(); - ATRACE_END(); - - // offload saving to file in a different thread - if (1 == mCaptureSequence) { - savePictureAsync(data, mCapturedFile); - } else { - savePictureAsync(data, mCapturedFile + "_" + std::to_string(mCaptureSequence)); - } - mCaptureSequence--; - } if (mPictureCapturedCallback) { std::invoke(mPictureCapturedCallback, std::move(picture)); + } else { + // single frame skp to file + SkSerialProcs procs; + procs.fTypefaceProc = [](SkTypeface* tf, void* ctx){ + return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData); + }; + auto data = picture->serialize(); + savePictureAsync(data, mCapturedFile); + mCaptureSequence = 0; + mCaptureMode = CaptureMode::None; } } mRecorder.reset(); @@ -298,22 +434,19 @@ void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& cli Properties::skpCaptureEnabled = true; } - renderVectorDrawableCache(); + // Initialize the canvas for the current frame, that might be a recording canvas if SKP + // capture is enabled. + SkCanvas* canvas = tryCapture(surface.get(), nodes[0].get(), layers); // draw all layers up front renderLayersImpl(layers, opaque); - // initialize the canvas for the current frame, that might be a recording canvas if SKP - // capture is enabled. - std::unique_ptr<SkPictureRecorder> recorder; - SkCanvas* canvas = tryCapture(surface.get()); - - renderFrameImpl(layers, clip, nodes, opaque, contentDrawBounds, canvas, preTransform); + renderFrameImpl(clip, nodes, opaque, contentDrawBounds, canvas, preTransform); endCapture(surface.get()); if (CC_UNLIKELY(Properties::debugOverdraw)) { - renderOverdraw(layers, clip, nodes, contentDrawBounds, surface, preTransform); + renderOverdraw(clip, nodes, contentDrawBounds, surface, preTransform); } ATRACE_NAME("flush commands"); @@ -329,12 +462,21 @@ static Rect nodeBounds(RenderNode& node) { } } // namespace -void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip, +void SkiaPipeline::renderFrameImpl(const SkRect& clip, const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect& contentDrawBounds, SkCanvas* canvas, const SkMatrix& preTransform) { SkAutoCanvasRestore saver(canvas, true); - canvas->androidFramework_setDeviceClipRestriction(preTransform.mapRect(clip).roundOut()); + auto clipRestriction = preTransform.mapRect(clip).roundOut(); + if (CC_UNLIKELY(mCaptureMode == CaptureMode::SingleFrameSKP + || mCaptureMode == CaptureMode::MultiFrameSKP)) { + canvas->drawAnnotation(SkRect::Make(clipRestriction), "AndroidDeviceClipRestriction", + nullptr); + } else { + // clip drawing to dirty region only when not recording SKP files (which should contain all + // draw ops on every frame) + canvas->androidFramework_setDeviceClipRestriction(clipRestriction); + } canvas->concat(preTransform); // STOPSHIP: Revert, temporary workaround to clear always F16 frame buffer for b/74976293 @@ -430,13 +572,13 @@ void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& } void SkiaPipeline::dumpResourceCacheUsage() const { - int resources, maxResources; - size_t bytes, maxBytes; + int resources; + size_t bytes; mRenderThread.getGrContext()->getResourceCacheUsage(&resources, &bytes); - mRenderThread.getGrContext()->getResourceCacheLimits(&maxResources, &maxBytes); + size_t maxBytes = mRenderThread.getGrContext()->getResourceCacheLimit(); SkString log("Resource Cache Usage:\n"); - log.appendf("%8d items out of %d maximum items\n", resources, maxResources); + log.appendf("%8d items\n", resources); log.appendf("%8zu bytes (%.2f MB) out of %.2f MB maximum\n", bytes, bytes * (1.0f / (1024.0f * 1024.0f)), maxBytes * (1.0f / (1024.0f * 1024.0f))); @@ -444,6 +586,7 @@ void SkiaPipeline::dumpResourceCacheUsage() const { } void SkiaPipeline::setSurfaceColorProperties(ColorMode colorMode) { + mColorMode = colorMode; if (colorMode == ColorMode::SRGB) { mSurfaceColorType = SkColorType::kN32_SkColorType; mSurfaceColorSpace = SkColorSpace::MakeSRGB(); @@ -458,49 +601,47 @@ void SkiaPipeline::setSurfaceColorProperties(ColorMode colorMode) { // Overdraw debugging // These colors should be kept in sync with Caches::getOverdrawColor() with a few differences. -// This implementation: -// (1) Requires transparent entries for "no overdraw" and "single draws". -// (2) Requires premul colors (instead of unpremul). -// (3) Requires RGBA colors (instead of BGRA). -static const uint32_t kOverdrawColors[2][6] = { - { - 0x00000000, - 0x00000000, - 0x2f2f0000, - 0x2f002f00, - 0x3f00003f, - 0x7f00007f, - }, - { - 0x00000000, - 0x00000000, - 0x2f2f0000, - 0x4f004f4f, - 0x5f50335f, - 0x7f00007f, - }, +// This implementation requires transparent entries for "no overdraw" and "single draws". +static const SkColor kOverdrawColors[2][6] = { + { + 0x00000000, + 0x00000000, + 0x2f0000ff, + 0x2f00ff00, + 0x3fff0000, + 0x7fff0000, + }, + { + 0x00000000, + 0x00000000, + 0x2f0000ff, + 0x4fffff00, + 0x5fff89d7, + 0x7fff0000, + }, }; -void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip, +void SkiaPipeline::renderOverdraw(const SkRect& clip, const std::vector<sp<RenderNode>>& nodes, const Rect& contentDrawBounds, sk_sp<SkSurface> surface, const SkMatrix& preTransform) { // Set up the overdraw canvas. SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height()); sk_sp<SkSurface> offscreen = surface->makeSurface(offscreenInfo); + LOG_ALWAYS_FATAL_IF(!offscreen, "Failed to create offscreen SkSurface for overdraw viz."); SkOverdrawCanvas overdrawCanvas(offscreen->getCanvas()); // Fake a redraw to replay the draw commands. This will increment the alpha channel // each time a pixel would have been drawn. // Pass true for opaque so we skip the clear - the overdrawCanvas is already zero // initialized. - renderFrameImpl(layers, clip, nodes, true, contentDrawBounds, &overdrawCanvas, preTransform); + renderFrameImpl(clip, nodes, true, contentDrawBounds, &overdrawCanvas, preTransform); sk_sp<SkImage> counts = offscreen->makeImageSnapshot(); // Draw overdraw colors to the canvas. The color filter will convert counts to colors. SkPaint paint; - const SkPMColor* colors = kOverdrawColors[static_cast<int>(Properties::overdrawColorSet)]; - paint.setColorFilter(SkOverdrawColorFilter::Make(colors)); + const SkColor* colors = kOverdrawColors[static_cast<int>(Properties::overdrawColorSet)]; + paint.setColorFilter(SkOverdrawColorFilter::MakeWithSkColors(colors)); surface->getCanvas()->drawImage(counts.get(), 0.0f, 0.0f, &paint); } diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index 41d864653b67..8341164edc19 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -17,12 +17,15 @@ #pragma once #include <SkSurface.h> +#include <SkDocument.h> +#include <SkMultiPictureDocument.h> #include "Lighting.h" #include "hwui/AnimatedImageDrawable.h" #include "renderthread/CanvasContext.h" #include "renderthread/IRenderPipeline.h" class SkPictureRecorder; +struct SkSharingSerialContext; namespace android { namespace uirenderer { @@ -38,14 +41,16 @@ public: bool pinImages(std::vector<SkImage*>& mutableImages) override; bool pinImages(LsaVector<sk_sp<Bitmap>>& images) override { return false; } void unpinImages() override; - void onPrepareTree() override; void renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, bool opaque, const LightInfo& lightInfo) override; + // If the given node didn't have a layer surface, or had one of the wrong size, this method + // creates a new one and returns true. Otherwise does nothing and returns false. bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, ErrorHandler* errorHandler) override; + void setSurfaceColorProperties(renderthread::ColorMode colorMode) override; SkColorType getSurfaceColorType() const override { return mSurfaceColorType; } sk_sp<SkColorSpace> getSurfaceColorSpace() override { return mSurfaceColorSpace; } @@ -54,70 +59,29 @@ public: const Rect& contentDrawBounds, sk_sp<SkSurface> surface, const SkMatrix& preTransform); - std::vector<VectorDrawableRoot*>* getVectorDrawables() { return &mVectorDrawables; } - static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap); void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque); - static float getLightRadius() { - if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) { - return Properties::overrideLightRadius; - } - return mLightRadius; - } - - static uint8_t getAmbientShadowAlpha() { - if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) { - return Properties::overrideAmbientShadowStrength; - } - return mAmbientShadowAlpha; - } - - static uint8_t getSpotShadowAlpha() { - if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) { - return Properties::overrideSpotShadowStrength; - } - return mSpotShadowAlpha; - } - - static Vector3 getLightCenter() { - if (CC_UNLIKELY(Properties::overrideLightPosY > 0 || Properties::overrideLightPosZ > 0)) { - Vector3 adjustedLightCenter = mLightCenter; - if (CC_UNLIKELY(Properties::overrideLightPosY > 0)) { - // negated since this shifts up - adjustedLightCenter.y = -Properties::overrideLightPosY; - } - if (CC_UNLIKELY(Properties::overrideLightPosZ > 0)) { - adjustedLightCenter.z = Properties::overrideLightPosZ; - } - return adjustedLightCenter; - } - return mLightCenter; - } - - static void updateLighting(const LightGeometry& lightGeometry, const LightInfo& lightInfo) { - mLightRadius = lightGeometry.radius; - mAmbientShadowAlpha = lightInfo.ambientShadowAlpha; - mSpotShadowAlpha = lightInfo.spotShadowAlpha; - mLightCenter = lightGeometry.center; - } - + // Sets the recording callback to the provided function and the recording mode + // to CallbackAPI void setPictureCapturedCallback( const std::function<void(sk_sp<SkPicture>&&)>& callback) override { mPictureCapturedCallback = callback; + mCaptureMode = callback ? CaptureMode::CallbackAPI : CaptureMode::None; } protected: void dumpResourceCacheUsage() const; - void setSurfaceColorProperties(renderthread::ColorMode colorMode); renderthread::RenderThread& mRenderThread; + + renderthread::ColorMode mColorMode = renderthread::ColorMode::SRGB; SkColorType mSurfaceColorType; sk_sp<SkColorSpace> mSurfaceColorSpace; private: - void renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip, + void renderFrameImpl(const SkRect& clip, const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect& contentDrawBounds, SkCanvas* canvas, const SkMatrix& preTransform); @@ -126,48 +90,67 @@ private: * Debugging feature. Draws a semi-transparent overlay on each pixel, indicating * how many times it has been drawn. */ - void renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip, + void renderOverdraw(const SkRect& clip, const std::vector<sp<RenderNode>>& nodes, const Rect& contentDrawBounds, sk_sp<SkSurface> surface, const SkMatrix& preTransform); - /** - * Render mVectorDrawables into offscreen buffers. - */ - void renderVectorDrawableCache(); - - SkCanvas* tryCapture(SkSurface* surface); + // Called every frame. Normally returns early with screen canvas. + // But when capture is enabled, returns an nwaycanvas where commands are also recorded. + SkCanvas* tryCapture(SkSurface* surface, RenderNode* root, const LayerUpdateQueue& dirtyLayers); + // Called at the end of every frame, closes the recording if necessary. void endCapture(SkSurface* surface); + // Determine if a new file-based capture should be started. + // If so, sets mCapturedFile and mCaptureSequence and returns true. + // Should be called every frame when capture is enabled. + // sets mCaptureMode. + bool shouldStartNewFileCapture(); + // Set up a multi frame capture. + bool setupMultiFrameCapture(); std::vector<sk_sp<SkImage>> mPinnedImages; - /** - * populated by prepareTree with dirty VDs - */ - std::vector<VectorDrawableRoot*> mVectorDrawables; - // Block of properties used only for debugging to record a SkPicture and save it in a file. + // There are three possible ways of recording drawing commands. + enum class CaptureMode { + // return to this mode when capture stops. + None, + // A mode where every frame is recorded into an SkPicture and sent to a provided callback, + // until that callback is cleared + CallbackAPI, + // A mode where a finite number of frames are recorded to a file with + // SkMultiPictureDocument + MultiFrameSKP, + // A mode which records a single frame to a normal SKP file. + SingleFrameSKP, + }; + CaptureMode mCaptureMode = CaptureMode::None; + /** - * mCapturedFile is used to enforce we don't capture more than once for a given name (cause - * permissions don't allow to reset a property from render thread). + * mCapturedFile - the filename to write a recorded SKP to in either MultiFrameSKP or + * SingleFrameSKP mode. */ std::string mCapturedFile; /** - * mCaptureSequence counts how many frames are left to take in the sequence. + * mCaptureSequence counts down how many frames are left to take in the sequence. Applicable + * only to MultiFrameSKP or SingleFrameSKP mode. */ int mCaptureSequence = 0; + // Multi frame serialization stream and writer used when serializing more than one frame. + std::unique_ptr<SkFILEWStream> mOpenMultiPicStream; + sk_sp<SkDocument> mMultiPic; + std::unique_ptr<SkSharingSerialContext> mSerialContext; + /** - * mRecorder holds the current picture recorder. We could store it on the stack to support - * parallel tryCapture calls (not really needed). + * mRecorder holds the current picture recorder when serializing in either SingleFrameSKP or + * CallbackAPI modes. */ std::unique_ptr<SkPictureRecorder> mRecorder; std::unique_ptr<SkNWayCanvas> mNwayCanvas; - std::function<void(sk_sp<SkPicture>&&)> mPictureCapturedCallback; - static float mLightRadius; - static uint8_t mAmbientShadowAlpha; - static uint8_t mSpotShadowAlpha; - static Vector3 mLightCenter; + // Set by setPictureCapturedCallback and when set, CallbackAPI mode recording is ongoing. + // Not used in other recording modes. + std::function<void(sk_sp<SkPicture>&&)> mPictureCapturedCallback; }; } /* namespace skiapipeline */ diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 16c8b8923074..d67cf8c9c73f 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -15,17 +15,21 @@ */ #include "SkiaRecordingCanvas.h" - +#include "hwui/Paint.h" #include <SkImagePriv.h> #include "CanvasTransform.h" +#ifdef __ANDROID__ // Layoutlib does not support Layers #include "Layer.h" #include "LayerDrawable.h" +#endif #include "NinePatchUtils.h" #include "RenderNode.h" #include "pipeline/skia/AnimatedDrawables.h" +#ifdef __ANDROID__ // Layoutlib does not support GL, Vulcan etc. #include "pipeline/skia/GLFunctorDrawable.h" #include "pipeline/skia/VkFunctorDrawable.h" #include "pipeline/skia/VkInteropFunctorDrawable.h" +#endif namespace android { namespace uirenderer { @@ -102,13 +106,16 @@ void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) { } void SkiaRecordingCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layerUpdater) { +#ifdef __ANDROID__ // Layoutlib does not support Layers if (layerUpdater != nullptr) { // Create a ref-counted drawable, which is kept alive by sk_sp in SkLiteDL. sk_sp<SkDrawable> drawable(new LayerDrawable(layerUpdater)); drawDrawable(drawable.get()); } +#endif } + void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) { // Record the child node. Drawable dtor will be invoked when mChildNodes deque is cleared. mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas(), true, mCurrentBarrier); @@ -125,8 +132,10 @@ void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) { } } + void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor, uirenderer::GlFunctorLifecycleListener* listener) { +#ifdef __ANDROID__ // Layoutlib does not support GL, Vulcan etc. FunctorDrawable* functorDrawable; if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>( @@ -137,9 +146,11 @@ void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor, } mDisplayList->mChildFunctors.push_back(functorDrawable); drawDrawable(functorDrawable); +#endif } void SkiaRecordingCanvas::drawWebViewFunctor(int functor) { +#ifdef __ANDROID__ // Layoutlib does not support GL, Vulcan etc. FunctorDrawable* functorDrawable; if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { functorDrawable = mDisplayList->allocateDrawable<VkFunctorDrawable>(functor, asSkCanvas()); @@ -147,7 +158,8 @@ void SkiaRecordingCanvas::drawWebViewFunctor(int functor) { functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, asSkCanvas()); } mDisplayList->mChildFunctors.push_back(functorDrawable); - drawDrawable(functorDrawable); + mRecorder.drawWebView(functorDrawable); +#endif } void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { @@ -185,9 +197,37 @@ SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint) { return filterPaint(std::move(paint)); } -void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) { +static SkDrawLooper* get_looper(const Paint* paint) { + return paint ? paint->getLooper() : nullptr; +} + +template <typename Proc> +void applyLooper(SkDrawLooper* looper, const SkPaint& paint, Proc proc) { + if (looper) { + SkSTArenaAlloc<256> alloc; + SkDrawLooper::Context* ctx = looper->makeContext(&alloc); + if (ctx) { + SkDrawLooper::Context::Info info; + for (;;) { + SkPaint p = paint; + if (!ctx->next(&info, &p)) { + break; + } + proc(info.fTranslate.fX, info.fTranslate.fY, p); + } + } + } else { + proc(0, 0, paint); + } +} + +void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) { sk_sp<SkImage> image = bitmap.makeImage(); - mRecorder.drawImage(image, left, top, filterBitmap(paint), bitmap.palette()); + + applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) { + mRecorder.drawImage(image, left + x, top + y, &p, bitmap.palette()); + }); + // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means // it is not safe to store a raw SkImage pointer, because the image object will be destroyed // when this function ends. @@ -196,12 +236,16 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, cons } } -void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) { +void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) { SkAutoCanvasRestore acr(&mRecorder, true); concat(matrix); sk_sp<SkImage> image = bitmap.makeImage(); - mRecorder.drawImage(image, 0, 0, filterBitmap(paint), bitmap.palette()); + + applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) { + mRecorder.drawImage(image, x, y, &p, bitmap.palette()); + }); + if (!bitmap.isImmutable() && image.get() && !image->unique()) { mDisplayList->mMutableImages.push_back(image.get()); } @@ -209,13 +253,17 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, con void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, - float dstBottom, const SkPaint* paint) { + float dstBottom, const Paint* paint) { SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom); SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); sk_sp<SkImage> image = bitmap.makeImage(); - mRecorder.drawImageRect(image, srcRect, dstRect, filterBitmap(paint), - SkCanvas::kFast_SrcRectConstraint, bitmap.palette()); + + applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) { + mRecorder.drawImageRect(image, srcRect, dstRect.makeOffset(x, y), &p, + SkCanvas::kFast_SrcRectConstraint, bitmap.palette()); + }); + if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() && !dstRect.isEmpty()) { mDisplayList->mMutableImages.push_back(image.get()); @@ -224,7 +272,7 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) { + const Paint* paint) { SkCanvas::Lattice lattice; NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height()); @@ -252,8 +300,11 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& ch filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality); } sk_sp<SkImage> image = bitmap.makeImage(); - mRecorder.drawImageLattice(image, lattice, dst, filterBitmap(std::move(filteredPaint)), - bitmap.palette()); + + applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) { + mRecorder.drawImageLattice(image, lattice, dst.makeOffset(x, y), &p, bitmap.palette()); + }); + if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) { mDisplayList->mMutableImages.push_back(image.get()); } diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h index c42cea33211e..bd5274c94e75 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -45,14 +45,14 @@ public: virtual uirenderer::DisplayList* finishRecording() override; - virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override; - virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override; + virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) override; + virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) override; virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, - float dstBottom, const SkPaint* paint) override; + float dstBottom, const Paint* paint) override; virtual void drawNinePatch(Bitmap& hwuiBitmap, const android::Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) override; + const Paint* paint) override; virtual double drawAnimatedImage(AnimatedImageDrawable* animatedImage) override; virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left, diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index e8cb219db320..212a4284a824 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -19,6 +19,7 @@ #include "DeferredLayerUpdater.h" #include "Readback.h" #include "ShaderCache.h" +#include "LightingInfo.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" #include "VkInteropFunctorDrawable.h" @@ -69,7 +70,7 @@ bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, con if (backBuffer.get() == nullptr) { return false; } - SkiaPipeline::updateLighting(lightGeometry, lightInfo); + LightingInfo::updateLighting(lightGeometry, lightInfo); renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer, mVkSurface->getCurrentPreTransform()); ShaderCache::get().onVkFrameFlushed(mRenderThread.getGrContext()); @@ -115,19 +116,17 @@ DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() { void SkiaVulkanPipeline::onStop() {} -bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior, - ColorMode colorMode, uint32_t extraBuffers) { +bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior) { if (mVkSurface) { mVkManager.destroySurface(mVkSurface); mVkSurface = nullptr; } - setSurfaceColorProperties(colorMode); if (surface) { mRenderThread.requireVkContext(); mVkSurface = - mVkManager.createSurface(surface, colorMode, mSurfaceColorSpace, mSurfaceColorType, - mRenderThread.getGrContext(), extraBuffers); + mVkManager.createSurface(surface, mColorMode, mSurfaceColorSpace, mSurfaceColorType, + mRenderThread.getGrContext(), 0); } return mVkSurface != nullptr; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index 31734783de7f..6268daa6213f 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -42,8 +42,7 @@ public: bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) override; DeferredLayerUpdater* createTextureLayer() override; - bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior, - renderthread::ColorMode colorMode, uint32_t extraBuffers) override; + bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior) override; void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp b/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp deleted file mode 100644 index e783f389feb8..000000000000 --- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp +++ /dev/null @@ -1,283 +0,0 @@ -/* - * 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. - */ - -#include "VectorDrawableAtlas.h" - -#include <GrRectanizer_pow2.h> -#include <SkCanvas.h> -#include <cmath> -#include "renderthread/RenderProxy.h" -#include "renderthread/RenderThread.h" -#include "utils/TraceUtils.h" - -namespace android { -namespace uirenderer { -namespace skiapipeline { - -VectorDrawableAtlas::VectorDrawableAtlas(size_t surfaceArea, StorageMode storageMode) - : mWidth((int)std::sqrt(surfaceArea)) - , mHeight((int)std::sqrt(surfaceArea)) - , mStorageMode(storageMode) {} - -void VectorDrawableAtlas::prepareForDraw(GrContext* context) { - if (StorageMode::allowSharedSurface == mStorageMode) { - if (!mSurface) { - mSurface = createSurface(mWidth, mHeight, context); - mRectanizer = std::make_unique<GrRectanizerPow2>(mWidth, mHeight); - mPixelUsedByVDs = 0; - mPixelAllocated = 0; - mConsecutiveFailures = 0; - mFreeRects.clear(); - } else { - if (isFragmented()) { - // Invoke repack outside renderFrame to avoid jank. - renderthread::RenderProxy::repackVectorDrawableAtlas(); - } - } - } -} - -#define MAX_CONSECUTIVE_FAILURES 5 -#define MAX_UNUSED_RATIO 2.0f - -bool VectorDrawableAtlas::isFragmented() { - return mConsecutiveFailures > MAX_CONSECUTIVE_FAILURES && - mPixelUsedByVDs * MAX_UNUSED_RATIO < mPixelAllocated; -} - -void VectorDrawableAtlas::repackIfNeeded(GrContext* context) { - // We repackage when atlas failed to allocate space MAX_CONSECUTIVE_FAILURES consecutive - // times and the atlas allocated pixels are at least MAX_UNUSED_RATIO times higher than pixels - // used by atlas VDs. - if (isFragmented() && mSurface) { - repack(context); - } -} - -// compare to CacheEntry objects based on VD area. -bool VectorDrawableAtlas::compareCacheEntry(const CacheEntry& first, const CacheEntry& second) { - return first.VDrect.width() * first.VDrect.height() < - second.VDrect.width() * second.VDrect.height(); -} - -void VectorDrawableAtlas::repack(GrContext* context) { - ATRACE_CALL(); - sk_sp<SkSurface> newSurface; - SkCanvas* canvas = nullptr; - if (StorageMode::allowSharedSurface == mStorageMode) { - newSurface = createSurface(mWidth, mHeight, context); - if (!newSurface) { - return; - } - canvas = newSurface->getCanvas(); - canvas->clear(SK_ColorTRANSPARENT); - mRectanizer = std::make_unique<GrRectanizerPow2>(mWidth, mHeight); - } else { - if (!mSurface) { - return; // nothing to repack - } - mRectanizer.reset(); - } - mFreeRects.clear(); - SkImage* sourceImageAtlas = nullptr; - if (mSurface) { - sourceImageAtlas = mSurface->makeImageSnapshot().get(); - } - - // Sort the list by VD size, which allows for the smallest VDs to get first in the atlas. - // Sorting is safe, because it does not affect iterator validity. - if (mRects.size() <= 100) { - mRects.sort(compareCacheEntry); - } - - for (CacheEntry& entry : mRects) { - SkRect currentVDRect = entry.VDrect; - SkImage* sourceImage; // copy either from the atlas or from a standalone surface - if (entry.surface) { - if (!fitInAtlas(currentVDRect.width(), currentVDRect.height())) { - continue; // don't even try to repack huge VD - } - sourceImage = entry.surface->makeImageSnapshot().get(); - } else { - sourceImage = sourceImageAtlas; - } - size_t VDRectArea = currentVDRect.width() * currentVDRect.height(); - SkIPoint16 pos; - if (canvas && mRectanizer->addRect(currentVDRect.width(), currentVDRect.height(), &pos)) { - SkRect newRect = - SkRect::MakeXYWH(pos.fX, pos.fY, currentVDRect.width(), currentVDRect.height()); - canvas->drawImageRect(sourceImage, currentVDRect, newRect, nullptr); - entry.VDrect = newRect; - entry.rect = newRect; - if (entry.surface) { - // A rectangle moved from a standalone surface to the atlas. - entry.surface = nullptr; - mPixelUsedByVDs += VDRectArea; - } - } else { - // Repack failed for this item. If it is not already, store it in a standalone - // surface. - if (!entry.surface) { - // A rectangle moved from an atlas to a standalone surface. - mPixelUsedByVDs -= VDRectArea; - SkRect newRect = SkRect::MakeWH(currentVDRect.width(), currentVDRect.height()); - entry.surface = createSurface(newRect.width(), newRect.height(), context); - auto tempCanvas = entry.surface->getCanvas(); - tempCanvas->clear(SK_ColorTRANSPARENT); - tempCanvas->drawImageRect(sourceImageAtlas, currentVDRect, newRect, nullptr); - entry.VDrect = newRect; - entry.rect = newRect; - } - } - } - mPixelAllocated = mPixelUsedByVDs; - context->flush(); - mSurface = newSurface; - mConsecutiveFailures = 0; -} - -AtlasEntry VectorDrawableAtlas::requestNewEntry(int width, int height, GrContext* context) { - AtlasEntry result; - if (width <= 0 || height <= 0) { - return result; - } - - if (mSurface) { - const size_t area = width * height; - - // Use a rectanizer to allocate unused space from the atlas surface. - bool notTooBig = fitInAtlas(width, height); - SkIPoint16 pos; - if (notTooBig && mRectanizer->addRect(width, height, &pos)) { - mPixelUsedByVDs += area; - mPixelAllocated += area; - result.rect = SkRect::MakeXYWH(pos.fX, pos.fY, width, height); - result.surface = mSurface; - auto eraseIt = mRects.emplace(mRects.end(), result.rect, result.rect, nullptr); - CacheEntry* entry = &(*eraseIt); - entry->eraseIt = eraseIt; - result.key = reinterpret_cast<AtlasKey>(entry); - mConsecutiveFailures = 0; - return result; - } - - // Try to reuse atlas memory from rectangles freed by "releaseEntry". - auto freeRectIt = mFreeRects.lower_bound(area); - while (freeRectIt != mFreeRects.end()) { - SkRect& freeRect = freeRectIt->second; - if (freeRect.width() >= width && freeRect.height() >= height) { - result.rect = SkRect::MakeXYWH(freeRect.fLeft, freeRect.fTop, width, height); - result.surface = mSurface; - auto eraseIt = mRects.emplace(mRects.end(), result.rect, freeRect, nullptr); - CacheEntry* entry = &(*eraseIt); - entry->eraseIt = eraseIt; - result.key = reinterpret_cast<AtlasKey>(entry); - mPixelUsedByVDs += area; - mFreeRects.erase(freeRectIt); - mConsecutiveFailures = 0; - return result; - } - freeRectIt++; - } - - if (notTooBig && mConsecutiveFailures <= MAX_CONSECUTIVE_FAILURES) { - mConsecutiveFailures++; - } - } - - // Allocate a surface for a rectangle that is too big or if atlas is full. - if (nullptr != context) { - result.rect = SkRect::MakeWH(width, height); - result.surface = createSurface(width, height, context); - auto eraseIt = mRects.emplace(mRects.end(), result.rect, result.rect, result.surface); - CacheEntry* entry = &(*eraseIt); - entry->eraseIt = eraseIt; - result.key = reinterpret_cast<AtlasKey>(entry); - } - - return result; -} - -AtlasEntry VectorDrawableAtlas::getEntry(AtlasKey atlasKey) { - AtlasEntry result; - if (INVALID_ATLAS_KEY != atlasKey) { - CacheEntry* entry = reinterpret_cast<CacheEntry*>(atlasKey); - result.rect = entry->VDrect; - result.surface = entry->surface; - if (!result.surface) { - result.surface = mSurface; - } - result.key = atlasKey; - } - return result; -} - -void VectorDrawableAtlas::releaseEntry(AtlasKey atlasKey) { - if (INVALID_ATLAS_KEY != atlasKey) { - if (!renderthread::RenderThread::isCurrent()) { - { - AutoMutex _lock(mReleaseKeyLock); - mKeysForRelease.push_back(atlasKey); - } - // invoke releaseEntry on the renderthread - renderthread::RenderProxy::releaseVDAtlasEntries(); - return; - } - CacheEntry* entry = reinterpret_cast<CacheEntry*>(atlasKey); - if (!entry->surface) { - // Store freed atlas rectangles in "mFreeRects" and try to reuse them later, when atlas - // is full. - SkRect& removedRect = entry->rect; - size_t rectArea = removedRect.width() * removedRect.height(); - mFreeRects.emplace(rectArea, removedRect); - SkRect& removedVDRect = entry->VDrect; - size_t VDRectArea = removedVDRect.width() * removedVDRect.height(); - mPixelUsedByVDs -= VDRectArea; - mConsecutiveFailures = 0; - } - auto eraseIt = entry->eraseIt; - mRects.erase(eraseIt); - } -} - -void VectorDrawableAtlas::delayedReleaseEntries() { - AutoMutex _lock(mReleaseKeyLock); - for (auto key : mKeysForRelease) { - releaseEntry(key); - } - mKeysForRelease.clear(); -} - -sk_sp<SkSurface> VectorDrawableAtlas::createSurface(int width, int height, GrContext* context) { - SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType); - // This must have a top-left origin so that calls to surface->canvas->writePixels - // performs a basic texture upload instead of a more complex drawing operation - return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info, 0, kTopLeft_GrSurfaceOrigin, - nullptr); -} - -void VectorDrawableAtlas::setStorageMode(StorageMode mode) { - mStorageMode = mode; - if (StorageMode::disallowSharedSurface == mStorageMode && mSurface) { - mSurface.reset(); - mRectanizer.reset(); - mFreeRects.clear(); - } -} - -} /* namespace skiapipeline */ -} /* namespace uirenderer */ -} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h deleted file mode 100644 index 5e892aa7e92c..000000000000 --- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h +++ /dev/null @@ -1,212 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include <SkSurface.h> -#include <utils/FatVector.h> -#include <utils/RefBase.h> -#include <utils/Thread.h> -#include <list> -#include <map> - -class GrRectanizer; - -namespace android { -namespace uirenderer { -namespace skiapipeline { - -typedef uintptr_t AtlasKey; - -#define INVALID_ATLAS_KEY 0 - -struct AtlasEntry { - sk_sp<SkSurface> surface; - SkRect rect; - AtlasKey key = INVALID_ATLAS_KEY; -}; - -/** - * VectorDrawableAtlas provides offscreen buffers used to draw VD and AnimatedVD. - * VectorDrawableAtlas can allocate a standalone surface or provide a subrect from a shared surface. - * VectorDrawableAtlas is owned by the CacheManager and weak pointers are kept by each - * VectorDrawable that is using it. VectorDrawableAtlas and its surface can be deleted at any time, - * except during a renderFrame call. VectorDrawable does not contain a pointer to atlas SkSurface - * nor any coordinates into the atlas, but instead holds a rectangle "id", which is resolved only - * when drawing. This design makes VectorDrawableAtlas free to move the data internally. - * At draw time a VectorDrawable may find, that its atlas has been deleted, which will make it - * draw in a standalone cache surface not part of an atlas. In this case VD won't use - * VectorDrawableAtlas until the next frame. - * VectorDrawableAtlas tries to fit VDs in the atlas SkSurface. If there is not enough space in - * the atlas, VectorDrawableAtlas creates a standalone surface for each VD. - * When a VectorDrawable is deleted, it invokes VectorDrawableAtlas::releaseEntry, which is keeping - * track of free spaces and allow to reuse the surface for another VD. - */ -// TODO: Check if not using atlas for AnimatedVD is more efficient. -// TODO: For low memory situations, when there are no paint effects in VD, we may render without an -// TODO: offscreen surface. -class VectorDrawableAtlas : public virtual RefBase { -public: - enum class StorageMode { allowSharedSurface, disallowSharedSurface }; - - explicit VectorDrawableAtlas(size_t surfaceArea, - StorageMode storageMode = StorageMode::allowSharedSurface); - - /** - * "prepareForDraw" may allocate a new surface if needed. It may schedule to repack the - * atlas at a later time. - */ - void prepareForDraw(GrContext* context); - - /** - * Repack the atlas if needed, by moving used rectangles into a new atlas surface. - * The goal of repacking is to fix a fragmented atlas. - */ - void repackIfNeeded(GrContext* context); - - /** - * Returns true if atlas is fragmented and repack is needed. - */ - bool isFragmented(); - - /** - * "requestNewEntry" is called by VectorDrawable to allocate a new rectangle area from the atlas - * or create a standalone surface if atlas is full. - * On success it returns a non-negative unique id, which can be used later with "getEntry" and - * "releaseEntry". - */ - AtlasEntry requestNewEntry(int width, int height, GrContext* context); - - /** - * "getEntry" extracts coordinates and surface of a previously created rectangle. - * "atlasKey" is an unique id created by "requestNewEntry". Passing a non-existing "atlasKey" is - * causing an undefined behaviour. - * On success it returns a rectangle Id -> may be same or different from "atlasKey" if - * implementation decides to move the record internally. - */ - AtlasEntry getEntry(AtlasKey atlasKey); - - /** - * "releaseEntry" is invoked when a VectorDrawable is deleted. Passing a non-existing "atlasKey" - * is causing an undefined behaviour. This is the only function in the class that can be - * invoked from any thread. It will marshal internally to render thread if needed. - */ - void releaseEntry(AtlasKey atlasKey); - - void setStorageMode(StorageMode mode); - - /** - * "delayedReleaseEntries" is indirectly invoked by "releaseEntry", when "releaseEntry" is - * invoked from a non render thread. - */ - void delayedReleaseEntries(); - -private: - struct CacheEntry { - CacheEntry(const SkRect& newVDrect, const SkRect& newRect, - const sk_sp<SkSurface>& newSurface) - : VDrect(newVDrect), rect(newRect), surface(newSurface) {} - - /** - * size and position of VectorDrawable into the atlas or in "this.surface" - */ - SkRect VDrect; - - /** - * rect allocated in atlas surface or "this.surface". It may be bigger than "VDrect" - */ - SkRect rect; - - /** - * this surface is used if atlas is full or VD is too big - */ - sk_sp<SkSurface> surface; - - /** - * iterator is used to delete self with a constant complexity (without traversing the list) - */ - std::list<CacheEntry>::iterator eraseIt; - }; - - /** - * atlas surface shared by all VDs - */ - sk_sp<SkSurface> mSurface; - - std::unique_ptr<GrRectanizer> mRectanizer; - const int mWidth; - const int mHeight; - - /** - * "mRects" keeps records only for rectangles used by VDs. List has nice properties: constant - * complexity to insert and erase and references are not invalidated by insert/erase. - */ - std::list<CacheEntry> mRects; - - /** - * Rectangles freed by "releaseEntry" are removed from "mRects" and added to "mFreeRects". - * "mFreeRects" is using for an index the rectangle area. There could be more than one free - * rectangle with the same area, which is the reason to use "multimap" instead of "map". - */ - std::multimap<size_t, SkRect> mFreeRects; - - /** - * area in atlas used by VectorDrawables (area in standalone surface not counted) - */ - int mPixelUsedByVDs = 0; - - /** - * area allocated in mRectanizer - */ - int mPixelAllocated = 0; - - /** - * Consecutive times we had to allocate standalone surfaces, because atlas was full. - */ - int mConsecutiveFailures = 0; - - /** - * mStorageMode allows using a shared surface to store small vector drawables. - * Using a shared surface can boost the performance by allowing GL ops to be batched, but may - * consume more memory. - */ - StorageMode mStorageMode; - - /** - * mKeysForRelease is used by releaseEntry implementation to pass atlas keys from an arbitrary - * calling thread to the render thread. - */ - std::vector<AtlasKey> mKeysForRelease; - - /** - * A lock used to protect access to mKeysForRelease. - */ - Mutex mReleaseKeyLock; - - sk_sp<SkSurface> createSurface(int width, int height, GrContext* context); - - inline bool fitInAtlas(int width, int height) { - return 2 * width < mWidth && 2 * height < mHeight; - } - - void repack(GrContext* context); - - static bool compareCacheEntry(const CacheEntry& first, const CacheEntry& second); -}; - -} /* namespace skiapipeline */ -} /* namespace uirenderer */ -} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp index 112792611fc3..68f111752a4c 100644 --- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp @@ -20,6 +20,7 @@ #include <GrBackendDrawableInfo.h> #include <SkAndroidFrameworkUtils.h> #include <SkImage.h> +#include "include/private/SkM44.h" #include <utils/Color.h> #include <utils/Trace.h> #include <utils/TraceUtils.h> @@ -62,7 +63,7 @@ void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) { renderthread::RenderThread::getInstance().vulkanManager(); mFunctorHandle->initVk(vk_manager.getVkFunctorInitParams()); - SkMatrix44 mat4(mMatrix); + SkM44 mat4(mMatrix); VkFunctorDrawParams params{ .width = mImageInfo.width(), .height = mImageInfo.height(), @@ -72,7 +73,7 @@ void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) { .clip_right = mClip.fRight, .clip_bottom = mClip.fBottom, }; - mat4.asColMajorf(¶ms.transform[0]); + mat4.getColMajor(¶ms.transform[0]); params.secondary_command_buffer = vulkan_info.fSecondaryCommandBuffer; params.color_attachment_index = vulkan_info.fColorAttachmentIndex; params.compatible_render_pass = vulkan_info.fCompatibleRenderPass; diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp index 706325f00bd2..241d3708def5 100644 --- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp @@ -121,7 +121,7 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { glBindTexture(GL_TEXTURE_2D, 0); DrawGlInfo info; - SkMatrix44 mat4(canvas->getTotalMatrix()); + SkM44 mat4(canvas->experimental_getLocalToDevice()); SkIRect clipBounds = canvas->getDeviceClipBounds(); info.clipLeft = clipBounds.fLeft; @@ -131,7 +131,7 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { info.isLayer = true; info.width = mFBInfo.width(); info.height = mFBInfo.height(); - mat4.asColMajorf(&info.transform[0]); + mat4.getColMajor(&info.transform[0]); info.color_space_ptr = canvas->imageInfo().colorSpace(); glViewport(0, 0, info.width, info.height); diff --git a/libs/hwui/protos/graphicsstats.proto b/libs/hwui/protos/graphicsstats.proto index 1226d44ceb85..745393ce1a3d 100644 --- a/libs/hwui/protos/graphicsstats.proto +++ b/libs/hwui/protos/graphicsstats.proto @@ -29,6 +29,12 @@ message GraphicsStatsServiceDumpProto { } message GraphicsStatsProto { + enum PipelineType { + UNKNOWN = 0; + GL = 1; + VULKAN = 2; + } + // The package name of the app optional string package_name = 1; @@ -46,6 +52,12 @@ message GraphicsStatsProto { // The frame time histogram for the package repeated GraphicsStatsHistogramBucketProto histogram = 6; + + // The gpu frame time histogram for the package + repeated GraphicsStatsHistogramBucketProto gpu_histogram = 7; + + // HWUI renders pipeline type: GL or Vulkan + optional PipelineType pipeline = 8; } message GraphicsStatsJankSummaryProto { diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index fad9440be73f..7e8c96d96860 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -16,7 +16,6 @@ #include "renderstate/RenderState.h" #include "renderthread/RenderThread.h" -#include "GpuMemoryTracker.h" namespace android { namespace uirenderer { @@ -25,15 +24,10 @@ RenderState::RenderState(renderthread::RenderThread& thread) : mRenderThread(thr mThreadId = pthread_self(); } -void RenderState::onContextCreated() { - GpuMemoryTracker::onGpuContextCreated(); -} - void RenderState::onContextDestroyed() { for(auto callback : mContextCallbacks) { callback->onContextDestroyed(); } - GpuMemoryTracker::onGpuContextDestroyed(); } void RenderState::postDecStrong(VirtualLightRefBase* object) { diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h index ff5d02fe359a..e08d32a7735c 100644 --- a/libs/hwui/renderstate/RenderState.h +++ b/libs/hwui/renderstate/RenderState.h @@ -62,7 +62,6 @@ private: ~RenderState() {} // Context notifications are only to be triggered by renderthread::RenderThread - void onContextCreated(); void onContextDestroyed(); std::set<IGpuContextCallback*> mContextCallbacks; diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 1b638c12ac7b..1e5877356e8d 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -16,19 +16,21 @@ #include "CacheManager.h" +#include "DeviceInfo.h" #include "Layer.h" #include "Properties.h" #include "RenderThread.h" +#include "pipeline/skia/ATraceMemoryDump.h" #include "pipeline/skia/ShaderCache.h" #include "pipeline/skia/SkiaMemoryTracer.h" #include "renderstate/RenderState.h" #include "thread/CommonPool.h" +#include <utils/Trace.h> #include <GrContextOptions.h> #include <SkExecutor.h> #include <SkGraphics.h> #include <SkMathPriv.h> -#include <gui/Surface.h> #include <math.h> #include <set> @@ -43,8 +45,8 @@ namespace renderthread { #define SURFACE_SIZE_MULTIPLIER (12.0f * 4.0f) #define BACKGROUND_RETENTION_PERCENTAGE (0.5f) -CacheManager::CacheManager(const DisplayInfo& display) - : mMaxSurfaceArea(display.w * display.h) +CacheManager::CacheManager() + : mMaxSurfaceArea(DeviceInfo::getWidth() * DeviceInfo::getHeight()) , mMaxResourceBytes(mMaxSurfaceArea * SURFACE_SIZE_MULTIPLIER) , mBackgroundResourceBytes(mMaxResourceBytes * BACKGROUND_RETENTION_PERCENTAGE) // This sets the maximum size for a single texture atlas in the GPU font cache. If @@ -53,14 +55,10 @@ CacheManager::CacheManager(const DisplayInfo& display) , mMaxGpuFontAtlasBytes(GrNextSizePow2(mMaxSurfaceArea)) // This sets the maximum size of the CPU font cache to be at least the same size as the // total number of GPU font caches (i.e. 4 separate GPU atlases). - , mMaxCpuFontCacheBytes(std::max(mMaxGpuFontAtlasBytes*4, SkGraphics::GetFontCacheLimit())) + , mMaxCpuFontCacheBytes( + std::max(mMaxGpuFontAtlasBytes * 4, SkGraphics::GetFontCacheLimit())) , mBackgroundCpuFontCacheBytes(mMaxCpuFontCacheBytes * BACKGROUND_RETENTION_PERCENTAGE) { - SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes); - - mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas( - mMaxSurfaceArea / 2, - skiapipeline::VectorDrawableAtlas::StorageMode::disallowSharedSurface); } void CacheManager::reset(sk_sp<GrContext> context) { @@ -70,17 +68,13 @@ void CacheManager::reset(sk_sp<GrContext> context) { if (context) { mGrContext = std::move(context); - mGrContext->getResourceCacheLimits(&mMaxResources, nullptr); - mGrContext->setResourceCacheLimits(mMaxResources, mMaxResourceBytes); + mGrContext->setResourceCacheLimit(mMaxResourceBytes); } } void CacheManager::destroy() { // cleanup any caches here as the GrContext is about to go away... mGrContext.reset(nullptr); - mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas( - mMaxSurfaceArea / 2, - skiapipeline::VectorDrawableAtlas::StorageMode::disallowSharedSurface); } class CommonPoolExecutor : public SkExecutor { @@ -111,7 +105,6 @@ void CacheManager::trimMemory(TrimMemoryMode mode) { switch (mode) { case TrimMemoryMode::Complete: - mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(mMaxSurfaceArea / 2); mGrContext->freeGpuResources(); SkGraphics::PurgeAllCaches(); break; @@ -120,8 +113,8 @@ void CacheManager::trimMemory(TrimMemoryMode mode) { // limits between the background and max amounts. This causes the unlocked resources // that have persistent data to be purged in LRU order. mGrContext->purgeUnlockedResources(true); - mGrContext->setResourceCacheLimits(mMaxResources, mBackgroundResourceBytes); - mGrContext->setResourceCacheLimits(mMaxResources, mMaxResourceBytes); + mGrContext->setResourceCacheLimit(mBackgroundResourceBytes); + mGrContext->setResourceCacheLimit(mMaxResourceBytes); SkGraphics::SetFontCacheLimit(mBackgroundCpuFontCacheBytes); SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes); break; @@ -140,16 +133,6 @@ void CacheManager::trimStaleResources() { mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30)); } -sp<skiapipeline::VectorDrawableAtlas> CacheManager::acquireVectorDrawableAtlas() { - LOG_ALWAYS_FATAL_IF(mVectorDrawableAtlas.get() == nullptr); - LOG_ALWAYS_FATAL_IF(mGrContext == nullptr); - - /** - * TODO: define memory conditions where we clear the cache (e.g. surface->reset()) - */ - return mVectorDrawableAtlas; -} - void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) { if (!mGrContext) { log.appendFormat("No valid cache instance.\n"); @@ -178,8 +161,6 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) log.appendFormat("Other Caches:\n"); log.appendFormat(" Current / Maximum\n"); - log.appendFormat(" VectorDrawableAtlas %6.2f kB / %6.2f KB (entries = %zu)\n", 0.0f, 0.0f, - (size_t)0); if (renderState) { if (renderState->mActiveLayers.size() > 0) { @@ -205,6 +186,18 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) gpuTracer.logTotals(log); } +void CacheManager::onFrameCompleted() { + if (ATRACE_ENABLED()) { + static skiapipeline::ATraceMemoryDump tracer; + tracer.startFrame(); + SkGraphics::DumpMemoryStatistics(&tracer); + if (mGrContext) { + mGrContext->dumpMemoryStatistics(&tracer); + } + tracer.logTraces(); + } +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h index 9a5a00fcf762..b009cc4f48f2 100644 --- a/libs/hwui/renderthread/CacheManager.h +++ b/libs/hwui/renderthread/CacheManager.h @@ -17,14 +17,13 @@ #ifndef CACHEMANAGER_H #define CACHEMANAGER_H +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration #include <GrContext.h> +#endif #include <SkSurface.h> -#include <ui/DisplayInfo.h> #include <utils/String8.h> #include <vector> -#include "pipeline/skia/VectorDrawableAtlas.h" - namespace android { class Surface; @@ -42,41 +41,38 @@ class CacheManager { public: enum class TrimMemoryMode { Complete, UiHidden }; +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration void configureContext(GrContextOptions* context, const void* identity, ssize_t size); +#endif void trimMemory(TrimMemoryMode mode); void trimStaleResources(); void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr); - sp<skiapipeline::VectorDrawableAtlas> acquireVectorDrawableAtlas(); - size_t getCacheSize() const { return mMaxResourceBytes; } size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; } + void onFrameCompleted(); private: friend class RenderThread; - explicit CacheManager(const DisplayInfo& display); + explicit CacheManager(); +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration void reset(sk_sp<GrContext> grContext); +#endif void destroy(); const size_t mMaxSurfaceArea; +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration sk_sp<GrContext> mGrContext; +#endif - int mMaxResources = 0; const size_t mMaxResourceBytes; const size_t mBackgroundResourceBytes; const size_t mMaxGpuFontAtlasBytes; const size_t mMaxCpuFontCacheBytes; const size_t mBackgroundCpuFontCacheBytes; - - struct PipelineProps { - const void* pipelineKey = nullptr; - size_t surfaceArea = 0; - }; - - sp<skiapipeline::VectorDrawableAtlas> mVectorDrawableAtlas; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 827cced2883b..a362bd220936 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -15,11 +15,19 @@ */ #include "CanvasContext.h" -#include <GpuMemoryTracker.h> + +#include <apex/window.h> +#include <fcntl.h> +#include <strings.h> +#include <sys/stat.h> + +#include <algorithm> +#include <cstdint> +#include <cstdlib> +#include <functional> #include "../Properties.h" #include "AnimationContext.h" -#include "EglManager.h" #include "Frame.h" #include "LayerUpdateQueue.h" #include "Properties.h" @@ -33,18 +41,6 @@ #include "utils/TimeUtils.h" #include "utils/TraceUtils.h" -#include <cutils/properties.h> -#include <private/hwui/DrawGlInfo.h> -#include <strings.h> - -#include <fcntl.h> -#include <sys/stat.h> -#include <algorithm> - -#include <cstdint> -#include <cstdlib> -#include <functional> - #define TRIM_MEMORY_COMPLETE 80 #define TRIM_MEMORY_UI_HIDDEN 20 @@ -105,13 +101,13 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* , mGenerationID(0) , mOpaque(!translucent) , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord())) - , mJankTracker(&thread.globalProfileData(), DeviceInfo::get()->displayInfo()) + , mJankTracker(&thread.globalProfileData()) , mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos()) , mContentDrawBounds(0, 0, 0, 0) , mRenderPipeline(std::move(renderPipeline)) { rootRenderNode->makeRoot(); mRenderNodes.emplace_back(rootRenderNode); - mProfiler.setDensity(DeviceInfo::get()->displayInfo().density); + mProfiler.setDensity(DeviceInfo::getDensity()); setRenderAheadDepth(Properties::defaultRenderAhead); } @@ -143,18 +139,21 @@ void CanvasContext::destroy() { mAnimationContext->destroy(); } -void CanvasContext::setSurface(sp<Surface>&& surface, bool enableTimeout) { - ATRACE_CALL(); - - if (surface) { - mNativeSurface = new ReliableSurface{std::move(surface)}; - if (enableTimeout) { - // TODO: Fix error handling & re-shorten timeout - mNativeSurface->setDequeueTimeout(4000_ms); - } - } else { - mNativeSurface = nullptr; +static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) { + int query_value; + int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value); + if (err != 0 || query_value < 0) { + ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value); + return; } + auto min_undequeued_buffers = static_cast<uint32_t>(query_value); + + int bufferCount = min_undequeued_buffers + 2 + extraBuffers; + native_window_set_buffer_count(window, bufferCount); +} + +void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) { + ATRACE_CALL(); if (mRenderAheadDepth == 0 && DeviceInfo::get()->getMaxRefreshRate() > 66.6f) { mFixedRenderAhead = false; @@ -164,15 +163,34 @@ void CanvasContext::setSurface(sp<Surface>&& surface, bool enableTimeout) { mRenderAheadCapacity = mRenderAheadDepth; } - ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::SRGB; - bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode, - mRenderAheadCapacity); + if (window) { + mNativeSurface = std::make_unique<ReliableSurface>(window); + mNativeSurface->init(); + if (enableTimeout) { + // TODO: Fix error handling & re-shorten timeout + ANativeWindow_setDequeueTimeout(window, 4000_ms); + } + mNativeSurface->setExtraBufferCount(mRenderAheadCapacity); + } else { + mNativeSurface = nullptr; + } + + bool hasSurface = mRenderPipeline->setSurface( + mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior); + + if (mNativeSurface && !mNativeSurface->didSetExtraBuffers()) { + setBufferCount(mNativeSurface->getNativeWindow(), mRenderAheadCapacity); + } mFrameNumber = -1; - if (hasSurface) { + if (window != nullptr && hasSurface) { mHaveNewSurface = true; mSwapHistory.clear(); + // Enable frame stats after the surface has been bound to the appropriate graphics API. + // Order is important when new and old surfaces are the same, because old surface has + // its frame stats disabled automatically. + native_window_enable_frame_timestamps(mNativeSurface->getNativeWindow(), true); } else { mRenderThread.removeFrameCallback(this); mGenerationID++; @@ -203,7 +221,7 @@ void CanvasContext::setStopped(bool stopped) { void CanvasContext::allocateBuffers() { if (mNativeSurface) { - mNativeSurface->allocateBuffers(); + ANativeWindow_tryAllocateBuffers(mNativeSurface->getNativeWindow()); } } @@ -222,7 +240,8 @@ void CanvasContext::setOpaque(bool opaque) { } void CanvasContext::setWideGamut(bool wideGamut) { - mWideColorGamut = wideGamut; + ColorMode colorMode = wideGamut ? ColorMode::WideColorGamut : ColorMode::SRGB; + mRenderPipeline->setSurfaceColorProperties(colorMode); } bool CanvasContext::makeCurrent() { @@ -299,6 +318,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy // just keep using the previous frame's structure instead if (!wasSkipped(mCurrentFrameInfo)) { mCurrentFrameInfo = mJankTracker.startFrame(); + mLast4FrameInfos.next().first = mCurrentFrameInfo; } mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo); mCurrentFrameInfo->set(FrameInfoIndex::SyncQueued) = syncQueued; @@ -306,10 +326,10 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy info.damageAccumulator = &mDamageAccumulator; info.layerUpdateQueue = &mLayerUpdateQueue; + info.damageGenerationId = mDamageId++; info.out.canDrawThisFrame = true; mAnimationContext->startFrame(info.mode); - mRenderPipeline->onPrepareTree(); for (const sp<RenderNode>& node : mRenderNodes) { // Only the primary target node will be drawn full - all other nodes would get drawn in // real time mode. In case of a window, the primary node is the window content and the other @@ -425,9 +445,10 @@ void CanvasContext::setPresentTime() { if (renderAhead) { presentTime = mCurrentFrameInfo->get(FrameInfoIndex::Vsync) + - (frameIntervalNanos * (renderAhead + 1)); + (frameIntervalNanos * (renderAhead + 1)) - DeviceInfo::get()->getAppOffset() + + (frameIntervalNanos / 2); } - native_window_set_buffers_timestamp(mNativeSurface.get(), presentTime); + native_window_set_buffers_timestamp(mNativeSurface->getNativeWindow(), presentTime); } void CanvasContext::draw() { @@ -436,6 +457,12 @@ void CanvasContext::draw() { if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) { mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); + // Notify the callbacks, even if there's nothing to draw so they aren't waiting + // indefinitely + for (auto& func : mFrameCompleteCallbacks) { + std::invoke(func, mFrameNumber); + } + mFrameCompleteCallbacks.clear(); return; } @@ -450,48 +477,69 @@ void CanvasContext::draw() { mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes, &(profiler())); - int64_t frameCompleteNr = mFrameCompleteCallbacks.size() ? getFrameNumber() : -1; + int64_t frameCompleteNr = getFrameNumber(); waitOnFences(); bool requireSwap = false; + int error = OK; bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap); mIsDirty = false; if (requireSwap) { - if (!didSwap) { // some error happened + bool didDraw = true; + // Handle any swapchain errors + error = mNativeSurface->getAndClearError(); + if (error == TIMED_OUT) { + // Try again + mRenderThread.postFrameCallback(this); + // But since this frame didn't happen, we need to mark full damage in the swap + // history + didDraw = false; + + } else if (error != OK || !didSwap) { + // Unknown error, abandon the surface setSurface(nullptr); + didDraw = false; } + SwapHistory& swap = mSwapHistory.next(); - swap.damage = windowDirty; - swap.swapCompletedTime = systemTime(CLOCK_MONOTONIC); + if (didDraw) { + swap.damage = windowDirty; + } else { + float max = static_cast<float>(INT_MAX); + swap.damage = SkRect::MakeWH(max, max); + } + swap.swapCompletedTime = systemTime(SYSTEM_TIME_MONOTONIC); swap.vsyncTime = mRenderThread.timeLord().latestVsync(); - if (mNativeSurface.get()) { - int durationUs; - nsecs_t dequeueStart = mNativeSurface->getLastDequeueStartTime(); + if (didDraw) { + nsecs_t dequeueStart = + ANativeWindow_getLastDequeueStartTime(mNativeSurface->getNativeWindow()); if (dequeueStart < mCurrentFrameInfo->get(FrameInfoIndex::SyncStart)) { // Ignoring dequeue duration as it happened prior to frame render start // and thus is not part of the frame. swap.dequeueDuration = 0; } else { - mNativeSurface->query(NATIVE_WINDOW_LAST_DEQUEUE_DURATION, &durationUs); - swap.dequeueDuration = us2ns(durationUs); + swap.dequeueDuration = + ANativeWindow_getLastDequeueDuration(mNativeSurface->getNativeWindow()); } - mNativeSurface->query(NATIVE_WINDOW_LAST_QUEUE_DURATION, &durationUs); - swap.queueDuration = us2ns(durationUs); + swap.queueDuration = + ANativeWindow_getLastQueueDuration(mNativeSurface->getNativeWindow()); } else { swap.dequeueDuration = 0; swap.queueDuration = 0; } mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = swap.dequeueDuration; mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = swap.queueDuration; + mLast4FrameInfos[-1].second = frameCompleteNr; mHaveNewSurface = false; mFrameNumber = -1; } else { mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = 0; mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = 0; + mLast4FrameInfos[-1].second = -1; } // TODO: Use a fence for real completion? @@ -524,7 +572,23 @@ void CanvasContext::draw() { mFrameMetricsReporter->reportFrameMetrics(mCurrentFrameInfo->data()); } - GpuMemoryTracker::onFrameCompleted(); + if (mLast4FrameInfos.size() == mLast4FrameInfos.capacity()) { + // By looking 4 frames back, we guarantee all SF stats are available. There are at + // most 3 buffers in BufferQueue. Surface object keeps stats for the last 8 frames. + FrameInfo* forthBehind = mLast4FrameInfos.front().first; + int64_t composedFrameId = mLast4FrameInfos.front().second; + nsecs_t acquireTime = -1; + if (mNativeSurface) { + native_window_get_frame_timestamps(mNativeSurface->getNativeWindow(), composedFrameId, + nullptr, &acquireTime, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr); + } + // Ignore default -1, NATIVE_WINDOW_TIMESTAMP_INVALID and NATIVE_WINDOW_TIMESTAMP_PENDING + forthBehind->set(FrameInfoIndex::GpuCompleted) = acquireTime > 0 ? acquireTime : -1; + mJankTracker.finishGpuDraw(*forthBehind); + } + + mRenderThread.cacheManager().onFrameCompleted(); } // Called by choreographer to do an RT-driven animation @@ -534,14 +598,16 @@ void CanvasContext::doFrame() { } SkISize CanvasContext::getNextFrameSize() const { - ReliableSurface* surface = mNativeSurface.get(); - if (surface) { - SkISize size; - surface->query(NATIVE_WINDOW_WIDTH, &size.fWidth); - surface->query(NATIVE_WINDOW_HEIGHT, &size.fHeight); - return size; + static constexpr SkISize defaultFrameSize = {INT32_MAX, INT32_MAX}; + if (mNativeSurface == nullptr) { + return defaultFrameSize; } - return {INT32_MAX, INT32_MAX}; + ANativeWindow* anw = mNativeSurface->getNativeWindow(); + + SkISize size; + size.fWidth = ANativeWindow_getWidth(anw); + size.fHeight = ANativeWindow_getHeight(anw); + return size; } void CanvasContext::prepareAndDraw(RenderNode* node) { @@ -552,7 +618,7 @@ void CanvasContext::prepareAndDraw(RenderNode* node) { UiFrameInfoBuilder(frameInfo).addFlag(FrameInfoFlags::RTAnimation).setVsync(vsync, vsync); TreeInfo info(TreeInfo::MODE_RT_ONLY, *this); - prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC), node); + prepareTree(info, frameInfo, systemTime(SYSTEM_TIME_MONOTONIC), node); if (info.out.canDrawThisFrame) { draw(); } else { @@ -660,7 +726,7 @@ void CanvasContext::enqueueFrameWork(std::function<void()>&& func) { int64_t CanvasContext::getFrameNumber() { // mFrameNumber is reset to -1 when the surface changes or we swap buffers if (mFrameNumber == -1 && mNativeSurface.get()) { - mFrameNumber = static_cast<int64_t>(mNativeSurface->getNextFrameNumber()); + mFrameNumber = ANativeWindow_getNextFrameId(mNativeSurface->getNativeWindow()); } return mFrameNumber; } @@ -669,13 +735,11 @@ bool CanvasContext::surfaceRequiresRedraw() { if (!mNativeSurface) return false; if (mHaveNewSurface) return true; - int width = -1; - int height = -1; - ReliableSurface* surface = mNativeSurface.get(); - surface->query(NATIVE_WINDOW_WIDTH, &width); - surface->query(NATIVE_WINDOW_HEIGHT, &height); + ANativeWindow* anw = mNativeSurface->getNativeWindow(); + const int width = ANativeWindow_getWidth(anw); + const int height = ANativeWindow_getHeight(anw); - return width == mLastFrameWidth && height == mLastFrameHeight; + return width != mLastFrameWidth || height != mLastFrameHeight; } void CanvasContext::setRenderAheadDepth(int renderAhead) { @@ -696,7 +760,7 @@ SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) { // New surface needs a full draw dirty->setEmpty(); } else { - if (!dirty->isEmpty() && !dirty->intersect(0, 0, frame.width(), frame.height())) { + if (!dirty->isEmpty() && !dirty->intersect(SkRect::MakeIWH(frame.width(), frame.height()))) { ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", SK_RECT_ARGS(*dirty), frame.width(), frame.height()); dirty->setEmpty(); @@ -705,7 +769,7 @@ SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) { } if (dirty->isEmpty()) { - dirty->set(0, 0, frame.width(), frame.height()); + dirty->setIWH(frame.width(), frame.height()); } // At this point dirty is the area of the window to update. However, @@ -721,7 +785,7 @@ SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) { if (frame.bufferAge() > (int)mSwapHistory.size()) { // We don't have enough history to handle this old of a buffer // Just do a full-draw - dirty->set(0, 0, frame.width(), frame.height()); + dirty->setIWH(frame.width(), frame.height()); } else { // At this point we haven't yet added the latest frame // to the damage history (happens below) diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index a0233ca357aa..0f1b8aebf56c 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -22,25 +22,26 @@ #include "FrameMetricsReporter.h" #include "IContextFactory.h" #include "IRenderPipeline.h" +#include "JankTracker.h" #include "LayerUpdateQueue.h" #include "Lighting.h" #include "ReliableSurface.h" #include "RenderNode.h" #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" +#include "utils/RingBuffer.h" -#include <EGL/egl.h> #include <SkBitmap.h> #include <SkRect.h> #include <SkSize.h> #include <cutils/compiler.h> -#include <gui/Surface.h> #include <utils/Functor.h> #include <functional> #include <future> #include <set> #include <string> +#include <utility> #include <vector> namespace android { @@ -55,7 +56,6 @@ class RenderState; namespace renderthread { -class EglManager; class Frame; // This per-renderer class manages the bridge between the global EGL context @@ -110,7 +110,7 @@ public: // Won't take effect until next EGLSurface creation void setSwapBehavior(SwapBehavior swapBehavior); - void setSurface(sp<Surface>&& surface, bool enableTimeout = true); + void setSurface(ANativeWindow* window, bool enableTimeout = true); bool pauseSurface(); void setStopped(bool stopped); bool hasSurface() const { return mNativeSurface.get(); } @@ -152,8 +152,6 @@ public: void setContentDrawBounds(const Rect& bounds) { mContentDrawBounds = bounds; } - RenderState& getRenderState() { return mRenderThread.renderState(); } - void addFrameMetricsObserver(FrameMetricsObserver* observer) { if (mFrameMetricsReporter.get() == nullptr) { mFrameMetricsReporter.reset(new FrameMetricsReporter()); @@ -216,11 +214,12 @@ private: SkRect computeDirtyRect(const Frame& frame, SkRect* dirty); - EGLint mLastFrameWidth = 0; - EGLint mLastFrameHeight = 0; + // The same type as Frame.mWidth and Frame.mHeight + int32_t mLastFrameWidth = 0; + int32_t mLastFrameHeight = 0; RenderThread& mRenderThread; - sp<ReliableSurface> mNativeSurface; + std::unique_ptr<ReliableSurface> mNativeSurface; // stopped indicates the CanvasContext will reject actual redraw operations, // and defer repaint until it is un-stopped bool mStopped = false; @@ -242,14 +241,15 @@ private: nsecs_t queueDuration; }; - RingBuffer<SwapHistory, 3> mSwapHistory; + // Need at least 4 because we do quad buffer. Add a 5th for good measure. + RingBuffer<SwapHistory, 5> mSwapHistory; int64_t mFrameNumber = -1; + int64_t mDamageId = 0; // last vsync for a dropped frame due to stuffed queue nsecs_t mLastDropVsync = 0; bool mOpaque; - bool mWideColorGamut = false; bool mUseForceDark = false; LightInfo mLightInfo; LightGeometry mLightGeometry = {{0, 0, 0}, 0}; @@ -262,6 +262,7 @@ private: std::vector<sp<RenderNode>> mRenderNodes; FrameInfo* mCurrentFrameInfo = nullptr; + RingBuffer<std::pair<FrameInfo*, int64_t>, 4> mLast4FrameInfos; std::string mName; JankTracker mJankTracker; FrameInfoVisualizer mProfiler; diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index 91dc3bc6e603..1e593388d063 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -69,7 +69,7 @@ int DrawFrameTask::drawFrame() { LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!"); mSyncResult = SyncResult::OK; - mSyncQueued = systemTime(CLOCK_MONOTONIC); + mSyncQueued = systemTime(SYSTEM_TIME_MONOTONIC); postAndWait(); return mSyncResult; diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 159cf497384a..5e0471c08d67 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -16,24 +16,21 @@ #include "EglManager.h" +#include <EGL/eglext.h> +#include <GLES/gl.h> #include <cutils/properties.h> #include <log/log.h> -#include <private/gui/SyncFeatures.h> +#include <sync/sync.h> #include <utils/Trace.h> -#include "utils/Color.h" -#include "utils/StringUtils.h" - -#include "Frame.h" -#include "Properties.h" - -#include <EGL/eglext.h> -#include <GLES/gl.h> -#include <gui/Surface.h> -#include <system/window.h> #include <string> #include <vector> +#include "Frame.h" +#include "Properties.h" +#include "utils/Color.h" +#include "utils/StringUtils.h" + #define GLES_VERSION 2 // Android-specific addition that is used to show when frames began in systrace @@ -81,6 +78,9 @@ static struct { bool displayP3 = false; bool contextPriority = false; bool surfacelessContext = false; + bool nativeFenceSync = false; + bool fenceSync = false; + bool waitSync = false; } EglExtensions; EglManager::EglManager() @@ -228,6 +228,9 @@ void EglManager::initExtensions() { EglExtensions.displayP3 = extensions.has("EGL_EXT_gl_colorspace_display_p3_passthrough"); EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority"); EglExtensions.surfacelessContext = extensions.has("EGL_KHR_surfaceless_context"); + EglExtensions.nativeFenceSync = extensions.has("EGL_ANDROID_native_fence_sync"); + EglExtensions.fenceSync = extensions.has("EGL_KHR_fence_sync"); + EglExtensions.waitSync = extensions.has("EGL_KHR_wait_sync"); } bool EglManager::hasEglContext() { @@ -289,6 +292,10 @@ void EglManager::createPBufferSurface() { if (mPBufferSurface == EGL_NO_SURFACE && !EglExtensions.surfacelessContext) { EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE}; mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs); + LOG_ALWAYS_FATAL_IF(mPBufferSurface == EGL_NO_SURFACE, + "Failed to create a pixel buffer display=%p, " + "mEglConfig=%p, error=%s", + mEglDisplay, mEglConfig, eglErrorString()); } } @@ -505,17 +512,30 @@ bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) { return preserved; } -status_t EglManager::fenceWait(sp<Fence>& fence) { +static status_t waitForeverOnFence(int fence, const char* logname) { + ATRACE_CALL(); + if (fence == -1) { + return NO_ERROR; + } + constexpr int warningTimeout = 3000; + int err = sync_wait(fence, warningTimeout); + if (err < 0 && errno == ETIME) { + ALOGE("%s: fence %d didn't signal in %d ms", logname, fence, warningTimeout); + err = sync_wait(fence, -1); + } + return err < 0 ? -errno : status_t(NO_ERROR); +} + +status_t EglManager::fenceWait(int fence) { if (!hasEglContext()) { ALOGE("EglManager::fenceWait: EGLDisplay not initialized"); return INVALID_OPERATION; } - if (SyncFeatures::getInstance().useWaitSync() && - SyncFeatures::getInstance().useNativeFenceSync()) { + if (EglExtensions.waitSync && EglExtensions.nativeFenceSync) { // Block GPU on the fence. // Create an EGLSyncKHR from the current fence. - int fenceFd = fence->dup(); + int fenceFd = ::dup(fence); if (fenceFd == -1) { ALOGE("EglManager::fenceWait: error dup'ing fence fd: %d", errno); return -errno; @@ -540,7 +560,7 @@ status_t EglManager::fenceWait(sp<Fence>& fence) { } } else { // Block CPU on the fence. - status_t err = fence->waitForever("EglManager::fenceWait"); + status_t err = waitForeverOnFence(fence, "EglManager::fenceWait"); if (err != NO_ERROR) { ALOGE("EglManager::fenceWait: error waiting for fence: %d", err); return err; @@ -549,14 +569,14 @@ status_t EglManager::fenceWait(sp<Fence>& fence) { return OK; } -status_t EglManager::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, - sp<Fence>& nativeFence) { +status_t EglManager::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, int* nativeFence) { + *nativeFence = -1; if (!hasEglContext()) { ALOGE("EglManager::createReleaseFence: EGLDisplay not initialized"); return INVALID_OPERATION; } - if (SyncFeatures::getInstance().useNativeFenceSync()) { + if (EglExtensions.nativeFenceSync) { EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); if (sync == EGL_NO_SYNC_KHR) { ALOGE("EglManager::createReleaseFence: error creating EGL fence: %#x", eglGetError()); @@ -571,9 +591,9 @@ status_t EglManager::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, eglGetError()); return UNKNOWN_ERROR; } - nativeFence = new Fence(fenceFd); + *nativeFence = fenceFd; *eglFence = EGL_NO_SYNC_KHR; - } else if (useFenceSync && SyncFeatures::getInstance().useFenceSync()) { + } else if (useFenceSync && EglExtensions.fenceSync) { if (*eglFence != EGL_NO_SYNC_KHR) { // There is already a fence for the current slot. We need to // wait on that before replacing it with another fence to diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index 27d41d26a73a..a893e245b214 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -21,9 +21,9 @@ #include <SkImageInfo.h> #include <SkRect.h> #include <cutils/compiler.h> -#include <ui/Fence.h> #include <ui/GraphicBuffer.h> #include <utils/StrongPointer.h> + #include "IRenderPipeline.h" #include "utils/Result.h" @@ -74,11 +74,11 @@ public: // Inserts a wait on fence command into the OpenGL ES command stream. If EGL extension // support is missing, block the CPU on the fence. - status_t fenceWait(sp<Fence>& fence); + status_t fenceWait(int fence); // Creates a fence that is signaled, when all the pending GL commands are flushed. // Depending on installed extensions, the result is either Android native fence or EGL fence. - status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, sp<Fence>& nativeFence); + status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, int* nativeFence); private: enum class SwapBehavior { diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index 3b81014c05e2..c3c22869a42f 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -66,8 +66,7 @@ public: virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) = 0; virtual DeferredLayerUpdater* createTextureLayer() = 0; - virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior, ColorMode colorMode, - uint32_t extraBuffers) = 0; + virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior) = 0; virtual void onStop() = 0; virtual bool isSurfaceReady() = 0; virtual bool isContextReady() = 0; @@ -80,7 +79,8 @@ public: virtual bool pinImages(std::vector<SkImage*>& mutableImages) = 0; virtual bool pinImages(LsaVector<sk_sp<Bitmap>>& images) = 0; virtual void unpinImages() = 0; - virtual void onPrepareTree() = 0; + + virtual void setSurfaceColorProperties(ColorMode colorMode) = 0; virtual SkColorType getSurfaceColorType() const = 0; virtual sk_sp<SkColorSpace> getSurfaceColorSpace() = 0; virtual GrSurfaceOrigin getSurfaceOrigin() = 0; diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp index ad1fc4921781..dcf1fc189588 100644 --- a/libs/hwui/renderthread/ReliableSurface.cpp +++ b/libs/hwui/renderthread/ReliableSurface.cpp @@ -16,7 +16,11 @@ #include "ReliableSurface.h" +#include <log/log_main.h> #include <private/android/AHardwareBufferHelpers.h> +// TODO: this should be including apex instead. +#include <system/window.h> +#include <vndk/window.h> namespace android::uirenderer::renderthread { @@ -26,82 +30,63 @@ namespace android::uirenderer::renderthread { // to propagate this error back to the caller constexpr bool DISABLE_BUFFER_PREFETCH = true; -// TODO: Make surface less protected -// This exists because perform is a varargs, and ANativeWindow has no va_list perform. -// So wrapping/chaining that is hard. Telling the compiler to ignore protected is easy, so we do -// that instead -struct SurfaceExposer : Surface { - // Make warnings happy - SurfaceExposer() = delete; - - using Surface::cancelBuffer; - using Surface::dequeueBuffer; - using Surface::lockBuffer_DEPRECATED; - using Surface::perform; - using Surface::queueBuffer; - using Surface::setBufferCount; - using Surface::setSwapInterval; -}; - -#define callProtected(surface, func, ...) ((*surface).*&SurfaceExposer::func)(__VA_ARGS__) - -ReliableSurface::ReliableSurface(sp<Surface>&& surface) : mSurface(std::move(surface)) { - LOG_ALWAYS_FATAL_IF(!mSurface, "Error, unable to wrap a nullptr"); - - ANativeWindow::setSwapInterval = hook_setSwapInterval; - ANativeWindow::dequeueBuffer = hook_dequeueBuffer; - ANativeWindow::cancelBuffer = hook_cancelBuffer; - ANativeWindow::queueBuffer = hook_queueBuffer; - ANativeWindow::query = hook_query; - ANativeWindow::perform = hook_perform; - - ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED; - ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED; - ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED; - ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED; +ReliableSurface::ReliableSurface(ANativeWindow* window) : mWindow(window) { + LOG_ALWAYS_FATAL_IF(!mWindow, "Error, unable to wrap a nullptr"); + ANativeWindow_acquire(mWindow); } ReliableSurface::~ReliableSurface() { clearReservedBuffer(); + // Clear out the interceptors for proper hygiene. + // As a concrete example, if the underlying ANativeWindow is associated with + // an EGLSurface that is still in use, then if we don't clear out the + // interceptors then we walk into undefined behavior. + ANativeWindow_setCancelBufferInterceptor(mWindow, nullptr, nullptr); + ANativeWindow_setDequeueBufferInterceptor(mWindow, nullptr, nullptr); + ANativeWindow_setQueueBufferInterceptor(mWindow, nullptr, nullptr); + ANativeWindow_setPerformInterceptor(mWindow, nullptr, nullptr); + ANativeWindow_setQueryInterceptor(mWindow, nullptr, nullptr); + ANativeWindow_release(mWindow); } -void ReliableSurface::perform(int operation, va_list args) { - std::lock_guard _lock{mMutex}; +void ReliableSurface::init() { + int result = ANativeWindow_setCancelBufferInterceptor(mWindow, hook_cancelBuffer, this); + LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set cancelBuffer interceptor: error = %d", + result); - switch (operation) { - case NATIVE_WINDOW_SET_USAGE: - mUsage = va_arg(args, uint32_t); - break; - case NATIVE_WINDOW_SET_USAGE64: - mUsage = va_arg(args, uint64_t); - break; - case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY: - /* width */ va_arg(args, uint32_t); - /* height */ va_arg(args, uint32_t); - mFormat = va_arg(args, PixelFormat); - break; - case NATIVE_WINDOW_SET_BUFFERS_FORMAT: - mFormat = va_arg(args, PixelFormat); - break; - } + result = ANativeWindow_setDequeueBufferInterceptor(mWindow, hook_dequeueBuffer, this); + LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set dequeueBuffer interceptor: error = %d", + result); + + result = ANativeWindow_setQueueBufferInterceptor(mWindow, hook_queueBuffer, this); + LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set queueBuffer interceptor: error = %d", + result); + + result = ANativeWindow_setPerformInterceptor(mWindow, hook_perform, this); + LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set perform interceptor: error = %d", + result); + + result = ANativeWindow_setQueryInterceptor(mWindow, hook_query, this); + LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set query interceptor: error = %d", + result); } int ReliableSurface::reserveNext() { + if constexpr (DISABLE_BUFFER_PREFETCH) { + return OK; + } { std::lock_guard _lock{mMutex}; if (mReservedBuffer) { ALOGW("reserveNext called but there was already a buffer reserved?"); return OK; } - if (mInErrorState) { + if (mBufferQueueState != OK) { return UNKNOWN_ERROR; } if (mHasDequeuedBuffer) { return OK; } - if constexpr (DISABLE_BUFFER_PREFETCH) { - return OK; - } } // TODO: Update this to better handle when requested dimensions have changed @@ -111,7 +96,9 @@ int ReliableSurface::reserveNext() { int fenceFd = -1; ANativeWindowBuffer* buffer = nullptr; - int result = callProtected(mSurface, dequeueBuffer, &buffer, &fenceFd); + + // Note that this calls back into our own hooked method. + int result = ANativeWindow_dequeueBuffer(mWindow, &buffer, &fenceFd); { std::lock_guard _lock{mMutex}; @@ -138,58 +125,11 @@ void ReliableSurface::clearReservedBuffer() { mHasDequeuedBuffer = false; } if (buffer) { - callProtected(mSurface, cancelBuffer, buffer, releaseFd); - } -} - -int ReliableSurface::cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd) { - clearReservedBuffer(); - if (isFallbackBuffer(buffer)) { - if (fenceFd > 0) { - close(fenceFd); - } - return OK; - } - int result = callProtected(mSurface, cancelBuffer, buffer, fenceFd); - return result; -} - -int ReliableSurface::dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd) { - { - std::lock_guard _lock{mMutex}; - if (mReservedBuffer) { - *buffer = mReservedBuffer; - *fenceFd = mReservedFenceFd.release(); - mReservedBuffer = nullptr; - return OK; - } - } - - int result = callProtected(mSurface, dequeueBuffer, buffer, fenceFd); - if (result != OK) { - ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result); - *buffer = acquireFallbackBuffer(); - *fenceFd = -1; - return *buffer ? OK : INVALID_OPERATION; - } else { - std::lock_guard _lock{mMutex}; - mHasDequeuedBuffer = true; - } - return OK; -} - -int ReliableSurface::queueBuffer(ANativeWindowBuffer* buffer, int fenceFd) { - clearReservedBuffer(); - - if (isFallbackBuffer(buffer)) { - if (fenceFd > 0) { - close(fenceFd); - } - return OK; + // Note that clearReservedBuffer may be reentrant here, so + // mReservedBuffer must be cleared once we reach here to avoid recursing + // forever. + ANativeWindow_cancelBuffer(mWindow, buffer, releaseFd); } - - int result = callProtected(mSurface, queueBuffer, buffer, fenceFd); - return result; } bool ReliableSurface::isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const { @@ -201,9 +141,9 @@ bool ReliableSurface::isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) return windowBuffer == scratchBuffer; } -ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer() { +ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer(int error) { std::lock_guard _lock{mMutex}; - mInErrorState = true; + mBufferQueueState = error; if (mScratchBuffer) { return AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get()); @@ -228,83 +168,116 @@ ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer() { return AHardwareBuffer_to_ANativeWindowBuffer(newBuffer); } -Surface* ReliableSurface::getWrapped(const ANativeWindow* window) { - return getSelf(window)->mSurface.get(); -} - -int ReliableSurface::hook_setSwapInterval(ANativeWindow* window, int interval) { - return callProtected(getWrapped(window), setSwapInterval, interval); -} - -int ReliableSurface::hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, - int* fenceFd) { - return getSelf(window)->dequeueBuffer(buffer, fenceFd); -} - -int ReliableSurface::hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, - int fenceFd) { - return getSelf(window)->cancelBuffer(buffer, fenceFd); -} - -int ReliableSurface::hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, - int fenceFd) { - return getSelf(window)->queueBuffer(buffer, fenceFd); -} +int ReliableSurface::hook_dequeueBuffer(ANativeWindow* window, + ANativeWindow_dequeueBufferFn dequeueBuffer, void* data, + ANativeWindowBuffer** buffer, int* fenceFd) { + ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data); + { + std::lock_guard _lock{rs->mMutex}; + if (rs->mReservedBuffer) { + *buffer = rs->mReservedBuffer; + *fenceFd = rs->mReservedFenceFd.release(); + rs->mReservedBuffer = nullptr; + return OK; + } + } -int ReliableSurface::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, - ANativeWindowBuffer** buffer) { - ANativeWindowBuffer* buf; - int fenceFd = -1; - int result = window->dequeueBuffer(window, &buf, &fenceFd); + int result = dequeueBuffer(window, buffer, fenceFd); if (result != OK) { - return result; - } - sp<Fence> fence(new Fence(fenceFd)); - int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED"); - if (waitResult != OK) { - ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d", waitResult); - window->cancelBuffer(window, buf, -1); - return waitResult; + ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result); + *buffer = rs->acquireFallbackBuffer(result); + *fenceFd = -1; + return *buffer ? OK : INVALID_OPERATION; + } else { + std::lock_guard _lock{rs->mMutex}; + rs->mHasDequeuedBuffer = true; } - *buffer = buf; - return result; + return OK; } -int ReliableSurface::hook_cancelBuffer_DEPRECATED(ANativeWindow* window, - ANativeWindowBuffer* buffer) { - return window->cancelBuffer(window, buffer, -1); +int ReliableSurface::hook_cancelBuffer(ANativeWindow* window, + ANativeWindow_cancelBufferFn cancelBuffer, void* data, + ANativeWindowBuffer* buffer, int fenceFd) { + ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data); + rs->clearReservedBuffer(); + if (rs->isFallbackBuffer(buffer)) { + if (fenceFd > 0) { + close(fenceFd); + } + return OK; + } + return cancelBuffer(window, buffer, fenceFd); } -int ReliableSurface::hook_lockBuffer_DEPRECATED(ANativeWindow* window, - ANativeWindowBuffer* buffer) { - // This method is a no-op in Surface as well - return OK; -} +int ReliableSurface::hook_queueBuffer(ANativeWindow* window, + ANativeWindow_queueBufferFn queueBuffer, void* data, + ANativeWindowBuffer* buffer, int fenceFd) { + ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data); + rs->clearReservedBuffer(); -int ReliableSurface::hook_queueBuffer_DEPRECATED(ANativeWindow* window, - ANativeWindowBuffer* buffer) { - return window->queueBuffer(window, buffer, -1); -} + if (rs->isFallbackBuffer(buffer)) { + if (fenceFd > 0) { + close(fenceFd); + } + return OK; + } -int ReliableSurface::hook_query(const ANativeWindow* window, int what, int* value) { - return getWrapped(window)->query(what, value); + return queueBuffer(window, buffer, fenceFd); } -int ReliableSurface::hook_perform(ANativeWindow* window, int operation, ...) { +int ReliableSurface::hook_perform(ANativeWindow* window, ANativeWindow_performFn perform, + void* data, int operation, va_list args) { // Drop the reserved buffer if there is one since this (probably) mutated buffer dimensions // TODO: Filter to things that only affect the reserved buffer // TODO: Can we mutate the reserved buffer in some cases? - getSelf(window)->clearReservedBuffer(); - va_list args; - va_start(args, operation); - int result = callProtected(getWrapped(window), perform, operation, args); - va_end(args); + ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data); + rs->clearReservedBuffer(); - va_start(args, operation); - getSelf(window)->perform(operation, args); - va_end(args); + va_list argsCopy; + va_copy(argsCopy, args); + int result = perform(window, operation, argsCopy); + { + std::lock_guard _lock{rs->mMutex}; + + switch (operation) { + case ANATIVEWINDOW_PERFORM_SET_USAGE: + rs->mUsage = va_arg(args, uint32_t); + break; + case ANATIVEWINDOW_PERFORM_SET_USAGE64: + rs->mUsage = va_arg(args, uint64_t); + break; + case ANATIVEWINDOW_PERFORM_SET_BUFFERS_GEOMETRY: + /* width */ va_arg(args, uint32_t); + /* height */ va_arg(args, uint32_t); + rs->mFormat = static_cast<AHardwareBuffer_Format>(va_arg(args, int32_t)); + break; + case ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT: + rs->mFormat = static_cast<AHardwareBuffer_Format>(va_arg(args, int32_t)); + break; + case NATIVE_WINDOW_SET_BUFFER_COUNT: + size_t bufferCount = va_arg(args, size_t); + if (bufferCount >= rs->mExpectedBufferCount) { + rs->mDidSetExtraBuffers = true; + } else { + ALOGD("HOOK FAILED! Expected %zd got = %zd", rs->mExpectedBufferCount, bufferCount); + } + break; + } + } + return result; +} + +int ReliableSurface::hook_query(const ANativeWindow *window, ANativeWindow_queryFn query, + void *data, int what, int *value) { + ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data); + int result = query(window, what, value); + if (what == ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS && result == OK) { + std::lock_guard _lock{rs->mMutex}; + *value += rs->mExtraBuffers; + rs->mExpectedBufferCount = *value + 2; + } return result; } -}; // namespace android::uirenderer::renderthread
\ No newline at end of file +}; // namespace android::uirenderer::renderthread diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h index 0bfc72ef61cb..f699eb1fe6b3 100644 --- a/libs/hwui/renderthread/ReliableSurface.h +++ b/libs/hwui/renderthread/ReliableSurface.h @@ -16,72 +16,87 @@ #pragma once -#include <gui/Surface.h> +#include <android-base/unique_fd.h> +#include <system/window.h> +#include <apex/window.h> +#include <utils/Errors.h> #include <utils/Macros.h> #include <utils/StrongPointer.h> #include <memory> +#include <mutex> namespace android::uirenderer::renderthread { -class ReliableSurface : public ANativeObjectBase<ANativeWindow, ReliableSurface, RefBase> { +class ReliableSurface { PREVENT_COPY_AND_ASSIGN(ReliableSurface); public: - ReliableSurface(sp<Surface>&& surface); + ReliableSurface(ANativeWindow* window); ~ReliableSurface(); - void setDequeueTimeout(nsecs_t timeout) { mSurface->setDequeueTimeout(timeout); } + // Performs initialization that is not safe to do in the constructor. + // For instance, registering ANativeWindow interceptors with ReliableSurface + // passed as the data pointer is not safe. + void init(); - int reserveNext(); + ANativeWindow* getNativeWindow() { return mWindow; } - void allocateBuffers() { mSurface->allocateBuffers(); } + int reserveNext(); - int query(int what, int* value) const { return mSurface->query(what, value); } + int getAndClearError() { + int ret = mBufferQueueState; + mBufferQueueState = OK; + return ret; + } - nsecs_t getLastDequeueStartTime() const { return mSurface->getLastDequeueStartTime(); } + void setExtraBufferCount(size_t extraBuffers) { + std::lock_guard _lock{mMutex}; + mExtraBuffers = extraBuffers; + } - uint64_t getNextFrameNumber() const { return mSurface->getNextFrameNumber(); } + bool didSetExtraBuffers() const { + std::lock_guard _lock{mMutex}; + return mDidSetExtraBuffers; + } private: - const sp<Surface> mSurface; + ANativeWindow* mWindow; mutable std::mutex mMutex; uint64_t mUsage = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER; - PixelFormat mFormat = PIXEL_FORMAT_RGBA_8888; + AHardwareBuffer_Format mFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; std::unique_ptr<AHardwareBuffer, void (*)(AHardwareBuffer*)> mScratchBuffer{ nullptr, AHardwareBuffer_release}; ANativeWindowBuffer* mReservedBuffer = nullptr; base::unique_fd mReservedFenceFd; bool mHasDequeuedBuffer = false; - bool mInErrorState = false; + int mBufferQueueState = OK; + size_t mExtraBuffers = 0; + size_t mExpectedBufferCount = 0; + bool mDidSetExtraBuffers = false; bool isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const; - ANativeWindowBuffer* acquireFallbackBuffer(); + ANativeWindowBuffer* acquireFallbackBuffer(int error); void clearReservedBuffer(); - void perform(int operation, va_list args); - int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd); - int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd); - int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd); - - static Surface* getWrapped(const ANativeWindow*); - - // ANativeWindow hooks - static int hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd); - static int hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, - int* fenceFd); - static int hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd); - - static int hook_perform(ANativeWindow* window, int operation, ...); - static int hook_query(const ANativeWindow* window, int what, int* value); - static int hook_setSwapInterval(ANativeWindow* window, int interval); - - static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer); - static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer** buffer); - static int hook_lockBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer); - static int hook_queueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer); + // ANativeWindow hooks. When an ANativeWindow_* method is called on the + // underlying ANativeWindow, these methods will intercept the original call. + // For example, an EGL driver would call into these hooks instead of the + // original methods. + static int hook_cancelBuffer(ANativeWindow* window, ANativeWindow_cancelBufferFn cancelBuffer, + void* data, ANativeWindowBuffer* buffer, int fenceFd); + static int hook_dequeueBuffer(ANativeWindow* window, + ANativeWindow_dequeueBufferFn dequeueBuffer, void* data, + ANativeWindowBuffer** buffer, int* fenceFd); + static int hook_queueBuffer(ANativeWindow* window, ANativeWindow_queueBufferFn queueBuffer, + void* data, ANativeWindowBuffer* buffer, int fenceFd); + + static int hook_perform(ANativeWindow* window, ANativeWindow_performFn perform, void* data, + int operation, va_list args); + static int hook_query(const ANativeWindow* window, ANativeWindow_queryFn query, void* data, + int what, int* value); }; -}; // namespace android::uirenderer::renderthread
\ No newline at end of file +}; // namespace android::uirenderer::renderthread diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index edb82f4db16d..b66a13d1efda 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -22,19 +22,13 @@ #include "Readback.h" #include "Rect.h" #include "WebViewFunctorManager.h" -#include "pipeline/skia/SkiaOpenGLPipeline.h" -#include "pipeline/skia/VectorDrawableAtlas.h" -#include "renderstate/RenderState.h" #include "renderthread/CanvasContext.h" -#include "renderthread/EglManager.h" #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" #include "utils/Macros.h" #include "utils/TimeUtils.h" #include "utils/TraceUtils.h" -#include <ui/GraphicBuffer.h> - namespace android { namespace uirenderer { namespace renderthread { @@ -82,9 +76,11 @@ void RenderProxy::setName(const char* name) { mRenderThread.queue().runSync([this, name]() { mContext->setName(std::string(name)); }); } -void RenderProxy::setSurface(const sp<Surface>& surface, bool enableTimeout) { - mRenderThread.queue().post([this, surf = surface, enableTimeout]() mutable { - mContext->setSurface(std::move(surf), enableTimeout); +void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) { + ANativeWindow_acquire(window); + mRenderThread.queue().post([this, win = window, enableTimeout]() mutable { + mContext->setSurface(win, enableTimeout); + ANativeWindow_release(win); }); } @@ -318,11 +314,11 @@ void RenderProxy::setRenderAheadDepth(int renderAhead) { [context = mContext, renderAhead] { context->setRenderAheadDepth(renderAhead); }); } -int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top, int right, int bottom, +int RenderProxy::copySurfaceInto(ANativeWindow* window, int left, int top, int right, int bottom, SkBitmap* bitmap) { auto& thread = RenderThread::getInstance(); return static_cast<int>(thread.queue().runSync([&]() -> auto { - return thread.readback().copySurfaceInto(*surface, Rect(left, top, right, bottom), bitmap); + return thread.readback().copySurfaceInto(window, Rect(left, top, right, bottom), bitmap); })); } @@ -340,7 +336,7 @@ void RenderProxy::prepareToDraw(Bitmap& bitmap) { }; nsecs_t lastVsync = renderThread->timeLord().latestVsync(); nsecs_t estimatedNextVsync = lastVsync + renderThread->timeLord().frameIntervalNanos(); - nsecs_t timeToNextVsync = estimatedNextVsync - systemTime(CLOCK_MONOTONIC); + nsecs_t timeToNextVsync = estimatedNextVsync - systemTime(SYSTEM_TIME_MONOTONIC); // We expect the UI thread to take 4ms and for RT to be active from VSYNC+4ms to // VSYNC+12ms or so, so aim for the gap during which RT is expected to // be idle @@ -369,27 +365,6 @@ void RenderProxy::disableVsync() { Properties::disableVsync = true; } -void RenderProxy::repackVectorDrawableAtlas() { - RenderThread& thread = RenderThread::getInstance(); - thread.queue().post([&thread]() { - // The context may be null if trimMemory executed, but then the atlas was deleted too. - if (thread.getGrContext() != nullptr) { - thread.cacheManager().acquireVectorDrawableAtlas()->repackIfNeeded( - thread.getGrContext()); - } - }); -} - -void RenderProxy::releaseVDAtlasEntries() { - RenderThread& thread = RenderThread::getInstance(); - thread.queue().post([&thread]() { - // The context may be null if trimMemory executed, but then the atlas was deleted too. - if (thread.getGrContext() != nullptr) { - thread.cacheManager().acquireVectorDrawableAtlas()->delayedReleaseEntries(); - } - }); -} - void RenderProxy::preload() { // Create RenderThread object and start the thread. Then preload Vulkan/EGL driver. auto& thread = RenderThread::getInstance(); diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 76cd0ee2a2ce..3baeb2f7a476 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -18,8 +18,8 @@ #define RENDERPROXY_H_ #include <SkBitmap.h> +#include <android/native_window.h> #include <cutils/compiler.h> -#include <gui/Surface.h> #include <utils/Functor.h> #include "../FrameMetricsObserver.h" @@ -30,6 +30,7 @@ namespace android { class GraphicBuffer; +class Surface; namespace uirenderer { @@ -69,7 +70,7 @@ public: ANDROID_API bool loadSystemProperties(); ANDROID_API void setName(const char* name); - ANDROID_API void setSurface(const sp<Surface>& surface, bool enableTimeout = true); + ANDROID_API void setSurface(ANativeWindow* window, bool enableTimeout = true); ANDROID_API void allocateBuffers(); ANDROID_API bool pause(); ANDROID_API void setStopped(bool stopped); @@ -140,7 +141,7 @@ public: */ ANDROID_API void setRenderAheadDepth(int renderAhead); - ANDROID_API static int copySurfaceInto(sp<Surface>& surface, int left, int top, int right, + ANDROID_API static int copySurfaceInto(ANativeWindow* window, int left, int top, int right, int bottom, SkBitmap* bitmap); ANDROID_API static void prepareToDraw(Bitmap& bitmap); @@ -150,10 +151,6 @@ public: ANDROID_API static void preload(); - static void repackVectorDrawableAtlas(); - - static void releaseVDAtlasEntries(); - private: RenderThread& mRenderThread; CanvasContext* mContext; diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 71c5b53f727a..206b58f62ea7 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -27,75 +27,61 @@ #include "pipeline/skia/SkiaOpenGLPipeline.h" #include "pipeline/skia/SkiaVulkanPipeline.h" #include "renderstate/RenderState.h" -#include "utils/FatVector.h" #include "utils/TimeUtils.h" #include "utils/TraceUtils.h" -#ifdef HWUI_GLES_WRAP_ENABLED -#include "debug/GlesDriver.h" -#endif - #include <GrContextOptions.h> #include <gl/GrGLInterface.h> -#include <gui/DisplayEventReceiver.h> #include <sys/resource.h> #include <utils/Condition.h> #include <utils/Log.h> #include <utils/Mutex.h> #include <thread> +#include <ui/FatVector.h> + namespace android { namespace uirenderer { namespace renderthread { -// Number of events to read at a time from the DisplayEventReceiver pipe. -// The value should be large enough that we can quickly drain the pipe -// using just a few large reads. -static const size_t EVENT_BUFFER_SIZE = 100; - static bool gHasRenderThreadInstance = false; static JVMAttachHook gOnStartHook = nullptr; -class DisplayEventReceiverWrapper : public VsyncSource { +void RenderThread::frameCallback(int64_t frameTimeNanos, void* data) { + RenderThread* rt = reinterpret_cast<RenderThread*>(data); + rt->mVsyncRequested = false; + if (rt->timeLord().vsyncReceived(frameTimeNanos) && !rt->mFrameCallbackTaskPending) { + ATRACE_NAME("queue mFrameCallbackTask"); + rt->mFrameCallbackTaskPending = true; + nsecs_t runAt = (frameTimeNanos + rt->mDispatchFrameDelay); + rt->queue().postAt(runAt, [=]() { rt->dispatchFrameCallbacks(); }); + } +} + +void RenderThread::refreshRateCallback(int64_t vsyncPeriod, void* data) { + ATRACE_NAME("refreshRateCallback"); + RenderThread* rt = reinterpret_cast<RenderThread*>(data); + DeviceInfo::get()->onRefreshRateChanged(vsyncPeriod); + rt->setupFrameInterval(); +} + +class ChoreographerSource : public VsyncSource { public: - DisplayEventReceiverWrapper(std::unique_ptr<DisplayEventReceiver>&& receiver, - const std::function<void()>& onDisplayConfigChanged) - : mDisplayEventReceiver(std::move(receiver)) - , mOnDisplayConfigChanged(onDisplayConfigChanged) {} + ChoreographerSource(RenderThread* renderThread) : mRenderThread(renderThread) {} virtual void requestNextVsync() override { - status_t status = mDisplayEventReceiver->requestNextVsync(); - LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "requestNextVsync failed with status: %d", status); + AChoreographer_postFrameCallback64(mRenderThread->mChoreographer, + RenderThread::frameCallback, mRenderThread); } - virtual nsecs_t latestVsyncEvent() override { - DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; - nsecs_t latest = 0; - ssize_t n; - while ((n = mDisplayEventReceiver->getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { - for (ssize_t i = 0; i < n; i++) { - const DisplayEventReceiver::Event& ev = buf[i]; - switch (ev.header.type) { - case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: - latest = ev.header.timestamp; - break; - case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: - mOnDisplayConfigChanged(); - break; - } - } - } - if (n < 0) { - ALOGW("Failed to get events from display event receiver, status=%d", status_t(n)); - } - return latest; + virtual void drainPendingEvents() override { + AChoreographer_handlePendingEvents(mRenderThread->mChoreographer, mRenderThread); } private: - std::unique_ptr<DisplayEventReceiver> mDisplayEventReceiver; - std::function<void()> mOnDisplayConfigChanged; + RenderThread* mRenderThread; }; class DummyVsyncSource : public VsyncSource { @@ -103,11 +89,14 @@ public: DummyVsyncSource(RenderThread* renderThread) : mRenderThread(renderThread) {} virtual void requestNextVsync() override { - mRenderThread->queue().postDelayed(16_ms, - [this]() { mRenderThread->drainDisplayEventQueue(); }); + mRenderThread->queue().postDelayed(16_ms, [this]() { + RenderThread::frameCallback(systemTime(SYSTEM_TIME_MONOTONIC), mRenderThread); + }); } - virtual nsecs_t latestVsyncEvent() override { return systemTime(CLOCK_MONOTONIC); } + virtual void drainPendingEvents() override { + RenderThread::frameCallback(systemTime(SYSTEM_TIME_MONOTONIC), mRenderThread); + } private: RenderThread* mRenderThread; @@ -149,29 +138,24 @@ RenderThread::RenderThread() } RenderThread::~RenderThread() { + // Note that if this fatal assertion is removed then member variables must + // be properly destroyed. LOG_ALWAYS_FATAL("Can't destroy the render thread"); } -void RenderThread::initializeDisplayEventReceiver() { - LOG_ALWAYS_FATAL_IF(mVsyncSource, "Initializing a second DisplayEventReceiver?"); +void RenderThread::initializeChoreographer() { + LOG_ALWAYS_FATAL_IF(mVsyncSource, "Initializing a second Choreographer?"); if (!Properties::isolatedProcess) { - auto receiver = std::make_unique<DisplayEventReceiver>( - ISurfaceComposer::eVsyncSourceApp, - ISurfaceComposer::eConfigChangedDispatch); - status_t status = receiver->initCheck(); - LOG_ALWAYS_FATAL_IF(status != NO_ERROR, - "Initialization of DisplayEventReceiver " - "failed with status: %d", - status); + mChoreographer = AChoreographer_create(); + LOG_ALWAYS_FATAL_IF(mChoreographer == nullptr, "Initialization of Choreographer failed"); + AChoreographer_registerRefreshRateCallback(mChoreographer, + RenderThread::refreshRateCallback, this); // Register the FD - mLooper->addFd(receiver->getFd(), 0, Looper::EVENT_INPUT, - RenderThread::displayEventReceiverCallback, this); - mVsyncSource = new DisplayEventReceiverWrapper(std::move(receiver), [this] { - DeviceInfo::get()->onDisplayConfigChanged(); - setupFrameInterval(); - }); + mLooper->addFd(AChoreographer_getFd(mChoreographer), 0, Looper::EVENT_INPUT, + RenderThread::choreographerCallback, this); + mVsyncSource = new ChoreographerSource(this); } else { mVsyncSource = new DummyVsyncSource(this); } @@ -179,16 +163,15 @@ void RenderThread::initializeDisplayEventReceiver() { void RenderThread::initThreadLocals() { setupFrameInterval(); - initializeDisplayEventReceiver(); + initializeChoreographer(); mEglManager = new EglManager(); mRenderState = new RenderState(*this); mVkManager = new VulkanManager(); - mCacheManager = new CacheManager(DeviceInfo::get()->displayInfo()); + mCacheManager = new CacheManager(); } void RenderThread::setupFrameInterval() { - auto& displayInfo = DeviceInfo::get()->displayInfo(); - nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / displayInfo.fps); + nsecs_t frameIntervalNanos = DeviceInfo::getVsyncPeriod(); mTimeLord.setFrameInterval(frameIntervalNanos); mDispatchFrameDelay = static_cast<nsecs_t>(frameIntervalNanos * .25f); } @@ -199,12 +182,7 @@ void RenderThread::requireGlContext() { } mEglManager->initialize(); -#ifdef HWUI_GLES_WRAP_ENABLED - debug::GlesDriver* driver = debug::GlesDriver::get(); - sk_sp<const GrGLInterface> glInterface(driver->getSkiaInterface()); -#else sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface()); -#endif LOG_ALWAYS_FATAL_IF(!glInterface.get()); GrContextOptions options; @@ -293,12 +271,11 @@ void RenderThread::setGrContext(sk_sp<GrContext> context) { } mGrContext = std::move(context); if (mGrContext) { - mRenderState->onContextCreated(); DeviceInfo::setMaxTextureSize(mGrContext->maxRenderTargetSize()); } } -int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { +int RenderThread::choreographerCallback(int fd, int events, void* data) { if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { ALOGE("Display event receiver pipe was closed or an error occurred. " "events=0x%x", @@ -312,24 +289,10 @@ int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { events); return 1; // keep the callback } + RenderThread* rt = reinterpret_cast<RenderThread*>(data); + AChoreographer_handlePendingEvents(rt->mChoreographer, data); - reinterpret_cast<RenderThread*>(data)->drainDisplayEventQueue(); - - return 1; // keep the callback -} - -void RenderThread::drainDisplayEventQueue() { - ATRACE_CALL(); - nsecs_t vsyncEvent = mVsyncSource->latestVsyncEvent(); - if (vsyncEvent > 0) { - mVsyncRequested = false; - if (mTimeLord.vsyncReceived(vsyncEvent) && !mFrameCallbackTaskPending) { - ATRACE_NAME("queue mFrameCallbackTask"); - mFrameCallbackTaskPending = true; - nsecs_t runAt = (vsyncEvent + mDispatchFrameDelay); - queue().postAt(runAt, [this]() { dispatchFrameCallbacks(); }); - } - } + return 1; } void RenderThread::dispatchFrameCallbacks() { @@ -370,7 +333,7 @@ bool RenderThread::threadLoop() { processQueue(); if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) { - drainDisplayEventQueue(); + mVsyncSource->drainPendingEvents(); mFrameCallbacks.insert(mPendingRegistrationFrameCallbacks.begin(), mPendingRegistrationFrameCallbacks.end()); mPendingRegistrationFrameCallbacks.clear(); diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index c96e284df6b4..8be46a6d16e1 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -17,34 +17,33 @@ #ifndef RENDERTHREAD_H_ #define RENDERTHREAD_H_ -#include "RenderTask.h" - -#include "../JankTracker.h" -#include "CacheManager.h" -#include "TimeLord.h" -#include "WebViewFunctorManager.h" -#include "thread/ThreadBase.h" -#include "utils/TimeUtils.h" - #include <GrContext.h> #include <SkBitmap.h> +#include <apex/choreographer.h> #include <cutils/compiler.h> -#include <ui/DisplayInfo.h> +#include <thread/ThreadBase.h> #include <utils/Looper.h> #include <utils/Thread.h> -#include <thread/ThreadBase.h> #include <memory> #include <mutex> #include <set> +#include "CacheManager.h" +#include "ProfileDataContainer.h" +#include "RenderTask.h" +#include "TimeLord.h" +#include "WebViewFunctorManager.h" +#include "thread/ThreadBase.h" +#include "utils/TimeUtils.h" + namespace android { class Bitmap; -class AutoBackendTextureRelease; namespace uirenderer { +class AutoBackendTextureRelease; class Readback; class RenderState; class TestUtils; @@ -53,6 +52,10 @@ namespace skiapipeline { class VkFunctorDrawHandler; } +namespace VectorDrawable { +class Tree; +} + namespace renderthread { class CanvasContext; @@ -71,10 +74,11 @@ protected: struct VsyncSource { virtual void requestNextVsync() = 0; - virtual nsecs_t latestVsyncEvent() = 0; + virtual void drainPendingEvents() = 0; virtual ~VsyncSource() {} }; +class ChoreographerSource; class DummyVsyncSource; typedef void (*JVMAttachHook)(const char* name); @@ -134,10 +138,12 @@ private: friend class DispatchFrameCallbacks; friend class RenderProxy; friend class DummyVsyncSource; - friend class android::AutoBackendTextureRelease; + friend class ChoreographerSource; + friend class android::uirenderer::AutoBackendTextureRelease; friend class android::uirenderer::TestUtils; friend class android::uirenderer::WebViewFunctor; friend class android::uirenderer::skiapipeline::VkFunctorDrawHandler; + friend class android::uirenderer::VectorDrawable::Tree; RenderThread(); virtual ~RenderThread(); @@ -146,13 +152,21 @@ private: static RenderThread& getInstance(); void initThreadLocals(); - void initializeDisplayEventReceiver(); + void initializeChoreographer(); void setupFrameInterval(); - static int displayEventReceiverCallback(int fd, int events, void* data); + // Callbacks for choreographer events: + // choreographerCallback will call AChoreograper_handleEvent to call the + // corresponding callbacks for each display event type + static int choreographerCallback(int fd, int events, void* data); + // Callback that will be run on vsync ticks. + static void frameCallback(int64_t frameTimeNanos, void* data); + // Callback that will be run whenver there is a refresh rate change. + static void refreshRateCallback(int64_t vsyncPeriod, void* data); void drainDisplayEventQueue(); void dispatchFrameCallbacks(); void requestVsync(); + AChoreographer* mChoreographer; VsyncSource* mVsyncSource; bool mVsyncRequested; std::set<IFrameCallback*> mFrameCallbacks; diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp index b82c5d159756..784068f1d877 100644 --- a/libs/hwui/renderthread/TimeLord.cpp +++ b/libs/hwui/renderthread/TimeLord.cpp @@ -31,7 +31,7 @@ bool TimeLord::vsyncReceived(nsecs_t vsync) { nsecs_t TimeLord::computeFrameTimeNanos() { // Logic copied from Choreographer.java - nsecs_t now = systemTime(CLOCK_MONOTONIC); + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); nsecs_t jitterNanos = now - mFrameTimeNanos; if (jitterNanos >= mFrameIntervalNanos) { nsecs_t lastFrameOffset = jitterNanos % mFrameIntervalNanos; diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 5173f638068d..ba70afc8b8d2 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -16,22 +16,22 @@ #include "VulkanManager.h" -#include <android/sync.h> -#include <gui/Surface.h> - -#include "Properties.h" -#include "RenderThread.h" -#include "renderstate/RenderState.h" -#include "utils/FatVector.h" -#include "utils/TraceUtils.h" - +#include <EGL/egl.h> +#include <EGL/eglext.h> #include <GrBackendSemaphore.h> #include <GrBackendSurface.h> #include <GrContext.h> #include <GrTypes.h> +#include <android/sync.h> +#include <ui/FatVector.h> #include <vk/GrVkExtensions.h> #include <vk/GrVkTypes.h> +#include "Properties.h" +#include "RenderThread.h" +#include "renderstate/RenderState.h" +#include "utils/TraceUtils.h" + namespace android { namespace uirenderer { namespace renderthread { @@ -137,20 +137,14 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe err = mCreateInstance(&instance_create, nullptr, &mInstance); LOG_ALWAYS_FATAL_IF(err < 0); + GET_INST_PROC(CreateDevice); GET_INST_PROC(DestroyInstance); + GET_INST_PROC(EnumerateDeviceExtensionProperties); GET_INST_PROC(EnumeratePhysicalDevices); - GET_INST_PROC(GetPhysicalDeviceProperties); - GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties); GET_INST_PROC(GetPhysicalDeviceFeatures2); GET_INST_PROC(GetPhysicalDeviceImageFormatProperties2); - GET_INST_PROC(CreateDevice); - GET_INST_PROC(EnumerateDeviceExtensionProperties); - GET_INST_PROC(CreateAndroidSurfaceKHR); - GET_INST_PROC(DestroySurfaceKHR); - GET_INST_PROC(GetPhysicalDeviceSurfaceSupportKHR); - GET_INST_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR); - GET_INST_PROC(GetPhysicalDeviceSurfaceFormatsKHR); - GET_INST_PROC(GetPhysicalDeviceSurfacePresentModesKHR); + GET_INST_PROC(GetPhysicalDeviceProperties); + GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties); uint32_t gpuCount; LOG_ALWAYS_FATAL_IF(mEnumeratePhysicalDevices(mInstance, &gpuCount, nullptr)); @@ -317,29 +311,27 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe LOG_ALWAYS_FATAL_IF(mCreateDevice(mPhysicalDevice, &deviceInfo, nullptr, &mDevice)); - GET_DEV_PROC(GetDeviceQueue); - GET_DEV_PROC(DeviceWaitIdle); - GET_DEV_PROC(DestroyDevice); - GET_DEV_PROC(CreateCommandPool); - GET_DEV_PROC(DestroyCommandPool); GET_DEV_PROC(AllocateCommandBuffers); - GET_DEV_PROC(FreeCommandBuffers); - GET_DEV_PROC(ResetCommandBuffer); GET_DEV_PROC(BeginCommandBuffer); - GET_DEV_PROC(EndCommandBuffer); GET_DEV_PROC(CmdPipelineBarrier); - GET_DEV_PROC(GetDeviceQueue); - GET_DEV_PROC(QueueSubmit); - GET_DEV_PROC(QueueWaitIdle); - GET_DEV_PROC(DeviceWaitIdle); + GET_DEV_PROC(CreateCommandPool); + GET_DEV_PROC(CreateFence); GET_DEV_PROC(CreateSemaphore); + GET_DEV_PROC(DestroyCommandPool); + GET_DEV_PROC(DestroyDevice); + GET_DEV_PROC(DestroyFence); GET_DEV_PROC(DestroySemaphore); - GET_DEV_PROC(ImportSemaphoreFdKHR); + GET_DEV_PROC(DeviceWaitIdle); + GET_DEV_PROC(EndCommandBuffer); + GET_DEV_PROC(FreeCommandBuffers); + GET_DEV_PROC(GetDeviceQueue); GET_DEV_PROC(GetSemaphoreFdKHR); - GET_DEV_PROC(CreateFence); - GET_DEV_PROC(DestroyFence); - GET_DEV_PROC(WaitForFences); + GET_DEV_PROC(ImportSemaphoreFdKHR); + GET_DEV_PROC(QueueSubmit); + GET_DEV_PROC(QueueWaitIdle); + GET_DEV_PROC(ResetCommandBuffer); GET_DEV_PROC(ResetFences); + GET_DEV_PROC(WaitForFences); } void VulkanManager::initialize() { @@ -480,16 +472,26 @@ struct DestroySemaphoreInfo { PFN_vkDestroySemaphore mDestroyFunction; VkDevice mDevice; VkSemaphore mSemaphore; + // We need to make sure we don't delete the VkSemaphore until it is done being used by both Skia + // (including by the GPU) and inside the VulkanManager. So we always start with two refs, one + // owned by Skia and one owned by the VulkanManager. The refs are decremented each time + // destroy_semaphore is called with this object. Skia will call destroy_semaphore once it is + // done with the semaphore and the GPU has finished work on the semaphore. The VulkanManager + // calls destroy_semaphore after sending the semaphore to Skia and exporting it if need be. + int mRefs = 2; DestroySemaphoreInfo(PFN_vkDestroySemaphore destroyFunction, VkDevice device, - VkSemaphore semaphore) + VkSemaphore semaphore) : mDestroyFunction(destroyFunction), mDevice(device), mSemaphore(semaphore) {} }; static void destroy_semaphore(void* context) { DestroySemaphoreInfo* info = reinterpret_cast<DestroySemaphoreInfo*>(context); - info->mDestroyFunction(info->mDevice, info->mSemaphore, nullptr); - delete info; + --info->mRefs; + if (!info->mRefs) { + info->mDestroyFunction(info->mDevice, info->mSemaphore, nullptr); + delete info; + } } void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) { @@ -521,12 +523,11 @@ void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) backendSemaphore.initVulkan(semaphore); int fenceFd = -1; - DestroySemaphoreInfo* destroyInfo = new DestroySemaphoreInfo(mDestroySemaphore, mDevice, - semaphore); - GrSemaphoresSubmitted submitted = - bufferInfo->skSurface->flush(SkSurface::BackendSurfaceAccess::kPresent, - kNone_GrFlushFlags, 1, &backendSemaphore, - destroy_semaphore, destroyInfo); + DestroySemaphoreInfo* destroyInfo = + new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore); + GrSemaphoresSubmitted submitted = bufferInfo->skSurface->flush( + SkSurface::BackendSurfaceAccess::kPresent, kNone_GrFlushFlags, 1, &backendSemaphore, + destroy_semaphore, destroyInfo); if (submitted == GrSemaphoresSubmitted::kYes) { VkSemaphoreGetFdInfoKHR getFdInfo; getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; @@ -540,6 +541,7 @@ void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) ALOGE("VulkanManager::swapBuffers(): Semaphore submission failed"); mQueueWaitIdle(mGraphicsQueue); } + destroy_semaphore(destroyInfo); surface->presentCurrentBuffer(dirtyRect, fenceFd); } @@ -567,14 +569,14 @@ VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode col *this, extraBuffers); } -status_t VulkanManager::fenceWait(sp<Fence>& fence, GrContext* grContext) { +status_t VulkanManager::fenceWait(int fence, GrContext* grContext) { if (!hasVkContext()) { ALOGE("VulkanManager::fenceWait: VkDevice not initialized"); return INVALID_OPERATION; } // Block GPU on the fence. - int fenceFd = fence->dup(); + int fenceFd = ::dup(fence); if (fenceFd == -1) { ALOGE("VulkanManager::fenceWait: error dup'ing fence fd: %d", errno); return -errno; @@ -615,7 +617,8 @@ status_t VulkanManager::fenceWait(sp<Fence>& fence, GrContext* grContext) { return OK; } -status_t VulkanManager::createReleaseFence(sp<Fence>& nativeFence, GrContext* grContext) { +status_t VulkanManager::createReleaseFence(int* nativeFence, GrContext* grContext) { + *nativeFence = -1; if (!hasVkContext()) { ALOGE("VulkanManager::createReleaseFence: VkDevice not initialized"); return INVALID_OPERATION; @@ -640,15 +643,17 @@ status_t VulkanManager::createReleaseFence(sp<Fence>& nativeFence, GrContext* gr GrBackendSemaphore backendSemaphore; backendSemaphore.initVulkan(semaphore); - DestroySemaphoreInfo* destroyInfo = new DestroySemaphoreInfo(mDestroySemaphore, mDevice, - semaphore); - GrSemaphoresSubmitted submitted = - grContext->flush(kNone_GrFlushFlags, 1, &backendSemaphore, - destroy_semaphore, destroyInfo); + DestroySemaphoreInfo* destroyInfo = + new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore); + // Even if Skia fails to submit the semaphore, it will still call the destroy_semaphore callback + // which will remove its ref to the semaphore. The VulkanManager must still release its ref, + // when it is done with the semaphore. + GrSemaphoresSubmitted submitted = grContext->flush(kNone_GrFlushFlags, 1, &backendSemaphore, + destroy_semaphore, destroyInfo); if (submitted == GrSemaphoresSubmitted::kNo) { ALOGE("VulkanManager::createReleaseFence: Failed to submit semaphore"); - mDestroySemaphore(mDevice, semaphore, nullptr); + destroy_semaphore(destroyInfo); return INVALID_OPERATION; } @@ -661,11 +666,12 @@ status_t VulkanManager::createReleaseFence(sp<Fence>& nativeFence, GrContext* gr int fenceFd = 0; err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd); + destroy_semaphore(destroyInfo); if (VK_SUCCESS != err) { ALOGE("VulkanManager::createReleaseFence: Failed to get semaphore Fd"); return INVALID_OPERATION; } - nativeFence = new Fence(fenceFd); + *nativeFence = fenceFd; return OK; } diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index dd3c6d0dba81..8b19f13fdfb9 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -20,17 +20,17 @@ #if !defined(VK_USE_PLATFORM_ANDROID_KHR) #define VK_USE_PLATFORM_ANDROID_KHR #endif -#include <vulkan/vulkan.h> - #include <GrContextOptions.h> #include <SkSurface.h> -#include <ui/Fence.h> #include <utils/StrongPointer.h> #include <vk/GrVkBackendContext.h> #include <vk/GrVkExtensions.h> +#include <vulkan/vulkan.h> + #include "Frame.h" #include "IRenderPipeline.h" #include "VulkanSurface.h" +#include "private/hwui/DrawVkInfo.h" class GrVkExtensions; @@ -70,11 +70,11 @@ public: void destroy(); // Inserts a wait on fence command into the Vulkan command buffer. - status_t fenceWait(sp<Fence>& fence, GrContext* grContext); + status_t fenceWait(int fence, GrContext* grContext); // Creates a fence that is signaled when all the pending Vulkan commands are finished on the // GPU. - status_t createReleaseFence(sp<Fence>& nativeFence, GrContext* grContext); + status_t createReleaseFence(int* nativeFence, GrContext* grContext); // Returned pointers are owned by VulkanManager. // An instance of VkFunctorInitParams returned from getVkFunctorInitParams refers to @@ -107,14 +107,6 @@ private: FNPTR_TYPE fPtr; }; - // WSI interface functions - VkPtr<PFN_vkCreateAndroidSurfaceKHR> mCreateAndroidSurfaceKHR; - VkPtr<PFN_vkDestroySurfaceKHR> mDestroySurfaceKHR; - VkPtr<PFN_vkGetPhysicalDeviceSurfaceSupportKHR> mGetPhysicalDeviceSurfaceSupportKHR; - VkPtr<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR> mGetPhysicalDeviceSurfaceCapabilitiesKHR; - VkPtr<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR> mGetPhysicalDeviceSurfaceFormatsKHR; - VkPtr<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR> mGetPhysicalDeviceSurfacePresentModesKHR; - // Instance Functions VkPtr<PFN_vkEnumerateInstanceVersion> mEnumerateInstanceVersion; VkPtr<PFN_vkEnumerateInstanceExtensionProperties> mEnumerateInstanceExtensionProperties; diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp index b2cc23e76b8a..a7ea21d8c4de 100644 --- a/libs/hwui/renderthread/VulkanSurface.cpp +++ b/libs/hwui/renderthread/VulkanSurface.cpp @@ -27,38 +27,14 @@ namespace android { namespace uirenderer { namespace renderthread { -static bool IsTransformSupported(int transform) { - // For now, only support pure rotations, not flip or flip-and-rotate, until we have - // more time to test them and build sample code. As far as I know we never actually - // use anything besides pure rotations anyway. - return transform == 0 || transform == NATIVE_WINDOW_TRANSFORM_ROT_90 || - transform == NATIVE_WINDOW_TRANSFORM_ROT_180 || - transform == NATIVE_WINDOW_TRANSFORM_ROT_270; -} - static int InvertTransform(int transform) { switch (transform) { - case NATIVE_WINDOW_TRANSFORM_ROT_90: - return NATIVE_WINDOW_TRANSFORM_ROT_270; - case NATIVE_WINDOW_TRANSFORM_ROT_180: - return NATIVE_WINDOW_TRANSFORM_ROT_180; - case NATIVE_WINDOW_TRANSFORM_ROT_270: - return NATIVE_WINDOW_TRANSFORM_ROT_90; - default: - return 0; - } -} - -static int ConvertVkTransformToNative(VkSurfaceTransformFlagsKHR transform) { - switch (transform) { - case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR: - return NATIVE_WINDOW_TRANSFORM_ROT_270; - case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR: - return NATIVE_WINDOW_TRANSFORM_ROT_180; - case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR: - return NATIVE_WINDOW_TRANSFORM_ROT_90; - case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR: - case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR: + case ANATIVEWINDOW_TRANSFORM_ROTATE_90: + return ANATIVEWINDOW_TRANSFORM_ROTATE_270; + case ANATIVEWINDOW_TRANSFORM_ROTATE_180: + return ANATIVEWINDOW_TRANSFORM_ROTATE_180; + case ANATIVEWINDOW_TRANSFORM_ROTATE_270: + return ANATIVEWINDOW_TRANSFORM_ROTATE_90; default: return 0; } @@ -71,11 +47,11 @@ static SkMatrix GetPreTransformMatrix(SkISize windowSize, int transform) { switch (transform) { case 0: return SkMatrix::I(); - case NATIVE_WINDOW_TRANSFORM_ROT_90: + case ANATIVEWINDOW_TRANSFORM_ROTATE_90: return SkMatrix::MakeAll(0, -1, height, 1, 0, 0, 0, 0, 1); - case NATIVE_WINDOW_TRANSFORM_ROT_180: + case ANATIVEWINDOW_TRANSFORM_ROTATE_180: return SkMatrix::MakeAll(-1, 0, width, 0, -1, height, 0, 0, 1); - case NATIVE_WINDOW_TRANSFORM_ROT_270: + case ANATIVEWINDOW_TRANSFORM_ROTATE_270: return SkMatrix::MakeAll(0, 1, 0, -1, 0, width, 0, 0, 1); default: LOG_ALWAYS_FATAL("Unsupported Window Transform (%d)", transform); @@ -83,180 +59,157 @@ static SkMatrix GetPreTransformMatrix(SkISize windowSize, int transform) { return SkMatrix::I(); } -void VulkanSurface::ComputeWindowSizeAndTransform(WindowInfo* windowInfo, const SkISize& minSize, - const SkISize& maxSize) { - SkISize& windowSize = windowInfo->size; - - // clamp width & height to handle currentExtent of -1 and protect us from broken hints - if (windowSize.width() < minSize.width() || windowSize.width() > maxSize.width() || - windowSize.height() < minSize.height() || windowSize.height() > maxSize.height()) { - int width = std::min(maxSize.width(), std::max(minSize.width(), windowSize.width())); - int height = std::min(maxSize.height(), std::max(minSize.height(), windowSize.height())); - ALOGE("Invalid Window Dimensions [%d, %d]; clamping to [%d, %d]", windowSize.width(), - windowSize.height(), width, height); - windowSize.set(width, height); - } - - windowInfo->actualSize = windowSize; - if (windowInfo->transform & HAL_TRANSFORM_ROT_90) { - windowInfo->actualSize.set(windowSize.height(), windowSize.width()); - } - - windowInfo->preTransform = GetPreTransformMatrix(windowInfo->size, windowInfo->transform); -} - -static bool ResetNativeWindow(ANativeWindow* window) { - // -- Reset the native window -- - // The native window might have been used previously, and had its properties - // changed from defaults. That will affect the answer we get for queries - // like MIN_UNDEQUEUED_BUFFERS. Reset to a known/default state before we - // attempt such queries. +static bool ConnectAndSetWindowDefaults(ANativeWindow* window) { + ATRACE_CALL(); int err = native_window_api_connect(window, NATIVE_WINDOW_API_EGL); if (err != 0) { - ALOGW("native_window_api_connect failed: %s (%d)", strerror(-err), err); + ALOGE("native_window_api_connect failed: %s (%d)", strerror(-err), err); return false; } // this will match what we do on GL so pick that here. err = window->setSwapInterval(window, 1); if (err != 0) { - ALOGW("native_window->setSwapInterval(1) failed: %s (%d)", strerror(-err), err); + ALOGE("native_window->setSwapInterval(1) failed: %s (%d)", strerror(-err), err); return false; } err = native_window_set_shared_buffer_mode(window, false); if (err != 0) { - ALOGW("native_window_set_shared_buffer_mode(false) failed: %s (%d)", strerror(-err), err); + ALOGE("native_window_set_shared_buffer_mode(false) failed: %s (%d)", strerror(-err), err); return false; } err = native_window_set_auto_refresh(window, false); if (err != 0) { - ALOGW("native_window_set_auto_refresh(false) failed: %s (%d)", strerror(-err), err); + ALOGE("native_window_set_auto_refresh(false) failed: %s (%d)", strerror(-err), err); return false; } - return true; -} + err = native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_FREEZE); + if (err != 0) { + ALOGE("native_window_set_scaling_mode(NATIVE_WINDOW_SCALING_MODE_FREEZE) failed: %s (%d)", + strerror(-err), err); + return false; + } -class VkSurfaceAutoDeleter { -public: - VkSurfaceAutoDeleter(VkInstance instance, VkSurfaceKHR surface, - PFN_vkDestroySurfaceKHR destroySurfaceKHR) - : mInstance(instance), mSurface(surface), mDestroySurfaceKHR(destroySurfaceKHR) {} - ~VkSurfaceAutoDeleter() { destroy(); } - - void destroy() { - if (mSurface != VK_NULL_HANDLE) { - mDestroySurfaceKHR(mInstance, mSurface, nullptr); - mSurface = VK_NULL_HANDLE; - } + // Let consumer drive the size of the buffers. + err = native_window_set_buffers_dimensions(window, 0, 0); + if (err != 0) { + ALOGE("native_window_set_buffers_dimensions(0,0) failed: %s (%d)", strerror(-err), err); + return false; + } + + // Enable auto prerotation, so when buffer size is driven by the consumer + // and the transform hint specifies a 90 or 270 degree rotation, the width + // and height used for buffer pre-allocation and dequeueBuffer will be + // additionally swapped. + err = native_window_set_auto_prerotation(window, true); + if (err != 0) { + ALOGE("VulkanSurface::UpdateWindow() native_window_set_auto_prerotation failed: %s (%d)", + strerror(-err), err); + return false; } -private: - VkInstance mInstance; - VkSurfaceKHR mSurface; - PFN_vkDestroySurfaceKHR mDestroySurfaceKHR; -}; + return true; +} VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode, SkColorType colorType, sk_sp<SkColorSpace> colorSpace, GrContext* grContext, const VulkanManager& vkManager, uint32_t extraBuffers) { - VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo; - memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR)); - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; - surfaceCreateInfo.pNext = nullptr; - surfaceCreateInfo.flags = 0; - surfaceCreateInfo.window = window; - - VkSurfaceKHR vkSurface = VK_NULL_HANDLE; - VkResult res = vkManager.mCreateAndroidSurfaceKHR(vkManager.mInstance, &surfaceCreateInfo, - nullptr, &vkSurface); - if (VK_SUCCESS != res) { - ALOGE("VulkanSurface::Create() vkCreateAndroidSurfaceKHR failed (%d)", res); + // Connect and set native window to default configurations. + if (!ConnectAndSetWindowDefaults(window)) { return nullptr; } - VkSurfaceAutoDeleter vkSurfaceDeleter(vkManager.mInstance, vkSurface, - vkManager.mDestroySurfaceKHR); - - SkDEBUGCODE(VkBool32 supported; res = vkManager.mGetPhysicalDeviceSurfaceSupportKHR( - vkManager.mPhysicalDevice, vkManager.mPresentQueueIndex, - vkSurface, &supported); - // All physical devices and queue families on Android must be capable of - // presentation with any native window. - SkASSERT(VK_SUCCESS == res && supported);); + // Initialize WindowInfo struct. + WindowInfo windowInfo; + if (!InitializeWindowInfoStruct(window, colorMode, colorType, colorSpace, vkManager, + extraBuffers, &windowInfo)) { + return nullptr; + } - // check for capabilities - VkSurfaceCapabilitiesKHR caps; - res = vkManager.mGetPhysicalDeviceSurfaceCapabilitiesKHR(vkManager.mPhysicalDevice, vkSurface, - &caps); - if (VK_SUCCESS != res) { - ALOGE("VulkanSurface::Create() vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed (%d)", res); + // Now we attempt to modify the window. + if (!UpdateWindow(window, windowInfo)) { return nullptr; } - LOG_ALWAYS_FATAL_IF(0 == (caps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)); + return new VulkanSurface(window, windowInfo, grContext); +} + +bool VulkanSurface::InitializeWindowInfoStruct(ANativeWindow* window, ColorMode colorMode, + SkColorType colorType, + sk_sp<SkColorSpace> colorSpace, + const VulkanManager& vkManager, + uint32_t extraBuffers, WindowInfo* outWindowInfo) { + ATRACE_CALL(); - /* - * We must destroy the VK Surface before attempting to update the window as doing so after - * will cause the native window to be modified in unexpected ways. - */ - vkSurfaceDeleter.destroy(); + int width, height; + int err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width); + if (err != 0 || width < 0) { + ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, width); + return false; + } + err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height); + if (err != 0 || height < 0) { + ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, height); + return false; + } + outWindowInfo->size = SkISize::Make(width, height); - /* - * Populate Window Info struct - */ - WindowInfo windowInfo; + int query_value; + err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &query_value); + if (err != 0 || query_value < 0) { + ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value); + return false; + } + outWindowInfo->transform = query_value; - windowInfo.transform = ConvertVkTransformToNative(caps.supportedTransforms); - windowInfo.size = SkISize::Make(caps.currentExtent.width, caps.currentExtent.height); + outWindowInfo->actualSize = outWindowInfo->size; + if (outWindowInfo->transform & ANATIVEWINDOW_TRANSFORM_ROTATE_90) { + outWindowInfo->actualSize.set(outWindowInfo->size.height(), outWindowInfo->size.width()); + } - const SkISize minSize = SkISize::Make(caps.minImageExtent.width, caps.minImageExtent.height); - const SkISize maxSize = SkISize::Make(caps.maxImageExtent.width, caps.maxImageExtent.height); - ComputeWindowSizeAndTransform(&windowInfo, minSize, maxSize); + outWindowInfo->preTransform = + GetPreTransformMatrix(outWindowInfo->size, outWindowInfo->transform); - int query_value; - int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value); + err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value); if (err != 0 || query_value < 0) { ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value); - return nullptr; + return false; } - auto min_undequeued_buffers = static_cast<uint32_t>(query_value); + outWindowInfo->bufferCount = + static_cast<uint32_t>(query_value) + sTargetBufferCount + extraBuffers; - windowInfo.bufferCount = min_undequeued_buffers + - std::max(sTargetBufferCount + extraBuffers, caps.minImageCount); - if (caps.maxImageCount > 0 && windowInfo.bufferCount > caps.maxImageCount) { + err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value); + if (err != 0 || query_value < 0) { + ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value); + return false; + } + if (outWindowInfo->bufferCount > static_cast<uint32_t>(query_value)) { // Application must settle for fewer images than desired: - windowInfo.bufferCount = caps.maxImageCount; + outWindowInfo->bufferCount = static_cast<uint32_t>(query_value); } - // Currently Skia requires the images to be color attachments and support all transfer - // operations. - VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | - VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | - VK_IMAGE_USAGE_TRANSFER_DST_BIT; - LOG_ALWAYS_FATAL_IF((caps.supportedUsageFlags & usageFlags) != usageFlags); - - windowInfo.dataspace = HAL_DATASPACE_V0_SRGB; + outWindowInfo->dataspace = HAL_DATASPACE_V0_SRGB; if (colorMode == ColorMode::WideColorGamut) { skcms_Matrix3x3 surfaceGamut; LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&surfaceGamut), "Could not get gamut matrix from color space"); if (memcmp(&surfaceGamut, &SkNamedGamut::kSRGB, sizeof(surfaceGamut)) == 0) { - windowInfo.dataspace = HAL_DATASPACE_V0_SCRGB; + outWindowInfo->dataspace = HAL_DATASPACE_V0_SCRGB; } else if (memcmp(&surfaceGamut, &SkNamedGamut::kDCIP3, sizeof(surfaceGamut)) == 0) { - windowInfo.dataspace = HAL_DATASPACE_DISPLAY_P3; + outWindowInfo->dataspace = HAL_DATASPACE_DISPLAY_P3; } else { LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space."); } } - windowInfo.pixelFormat = ColorTypeToPixelFormat(colorType); + outWindowInfo->pixelFormat = ColorTypeToPixelFormat(colorType); VkFormat vkPixelFormat = VK_FORMAT_R8G8B8A8_UNORM; - if (windowInfo.pixelFormat == PIXEL_FORMAT_RGBA_FP16) { + if (outWindowInfo->pixelFormat == PIXEL_FORMAT_RGBA_FP16) { vkPixelFormat = VK_FORMAT_R16G16B16A16_SFLOAT; } @@ -275,7 +228,10 @@ VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode, imageFormatInfo.format = vkPixelFormat; imageFormatInfo.type = VK_IMAGE_TYPE_2D; imageFormatInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageFormatInfo.usage = usageFlags; + // Currently Skia requires the images to be color attachments and support all transfer + // operations. + imageFormatInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; imageFormatInfo.flags = 0; VkAndroidHardwareBufferUsageANDROID hwbUsage; @@ -286,35 +242,27 @@ VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode, imgFormProps.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2; imgFormProps.pNext = &hwbUsage; - res = vkManager.mGetPhysicalDeviceImageFormatProperties2(vkManager.mPhysicalDevice, - &imageFormatInfo, &imgFormProps); + VkResult res = vkManager.mGetPhysicalDeviceImageFormatProperties2( + vkManager.mPhysicalDevice, &imageFormatInfo, &imgFormProps); if (VK_SUCCESS != res) { ALOGE("Failed to query GetPhysicalDeviceImageFormatProperties2"); - return nullptr; + return false; } uint64_t consumerUsage; - native_window_get_consumer_usage(window, &consumerUsage); - windowInfo.windowUsageFlags = consumerUsage | hwbUsage.androidHardwareBufferUsage; - - /* - * Now we attempt to modify the window! - */ - if (!UpdateWindow(window, windowInfo)) { - return nullptr; + err = native_window_get_consumer_usage(window, &consumerUsage); + if (err != 0) { + ALOGE("native_window_get_consumer_usage failed: %s (%d)", strerror(-err), err); + return false; } + outWindowInfo->windowUsageFlags = consumerUsage | hwbUsage.androidHardwareBufferUsage; - return new VulkanSurface(window, windowInfo, minSize, maxSize, grContext); + return true; } bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo) { ATRACE_CALL(); - if (!ResetNativeWindow(window)) { - return false; - } - - // -- Configure the native window -- int err = native_window_set_buffers_format(window, windowInfo.pixelFormat); if (err != 0) { ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_format(%d) failed: %s (%d)", @@ -330,15 +278,6 @@ bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& window return false; } - const SkISize& size = windowInfo.actualSize; - err = native_window_set_buffers_dimensions(window, size.width(), size.height()); - if (err != 0) { - ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_dimensions(%d,%d) " - "failed: %s (%d)", - size.width(), size.height(), strerror(-err), err); - return false; - } - // native_window_set_buffers_transform() expects the transform the app is requesting that // the compositor perform during composition. With native windows, pre-transform works by // rendering with the same transform the compositor is applying (as in Vulkan), but @@ -353,16 +292,6 @@ bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& window return false; } - // Vulkan defaults to NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, but this is different than - // HWUI's expectation - err = native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_FREEZE); - if (err != 0) { - ALOGE("VulkanSurface::UpdateWindow() native_window_set_scaling_mode(SCALE_TO_WINDOW) " - "failed: %s (%d)", - strerror(-err), err); - return false; - } - err = native_window_set_buffer_count(window, windowInfo.bufferCount); if (err != 0) { ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffer_count(%zu) failed: %s (%d)", @@ -377,16 +306,12 @@ bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& window return false; } - return err == 0; + return true; } VulkanSurface::VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo, - SkISize minWindowSize, SkISize maxWindowSize, GrContext* grContext) - : mNativeWindow(window) - , mWindowInfo(windowInfo) - , mGrContext(grContext) - , mMinWindowSize(minWindowSize) - , mMaxWindowSize(maxWindowSize) {} + GrContext* grContext) + : mNativeWindow(window), mWindowInfo(windowInfo), mGrContext(grContext) {} VulkanSurface::~VulkanSurface() { releaseBuffers(); @@ -429,56 +354,49 @@ VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() { // value at the end of the function if everything dequeued correctly. mCurrentBufferInfo = nullptr; - // check if the native window has been resized or rotated and update accordingly - SkISize newSize = SkISize::MakeEmpty(); + // Query the transform hint synced from the initial Surface connect or last queueBuffer. The + // auto prerotation on the buffer is based on the same transform hint in use by the producer. int transformHint = 0; - mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_WIDTH, &newSize.fWidth); - mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_HEIGHT, &newSize.fHeight); - mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_TRANSFORM_HINT, &transformHint); - if (newSize != mWindowInfo.actualSize || transformHint != mWindowInfo.transform) { - WindowInfo newWindowInfo = mWindowInfo; - newWindowInfo.size = newSize; - newWindowInfo.transform = IsTransformSupported(transformHint) ? transformHint : 0; - ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize); - - int err = 0; - if (newWindowInfo.actualSize != mWindowInfo.actualSize) { - // reset the native buffers and update the window - err = native_window_set_buffers_dimensions(mNativeWindow.get(), - newWindowInfo.actualSize.width(), - newWindowInfo.actualSize.height()); - if (err != 0) { - ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)", - newWindowInfo.actualSize.width(), newWindowInfo.actualSize.height(), - strerror(-err), err); - return nullptr; - } + int err = + mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_TRANSFORM_HINT, &transformHint); + + // Since auto pre-rotation is enabled, dequeueBuffer to get the consumer driven buffer size + // from ANativeWindowBuffer. + ANativeWindowBuffer* buffer; + int fence_fd; + err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fence_fd); + if (err != 0) { + ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err); + return nullptr; + } + + SkISize actualSize = SkISize::Make(buffer->width, buffer->height); + if (actualSize != mWindowInfo.actualSize || transformHint != mWindowInfo.transform) { + if (actualSize != mWindowInfo.actualSize) { // reset the NativeBufferInfo (including SkSurface) associated with the old buffers. The // new NativeBufferInfo storage will be populated lazily as we dequeue each new buffer. + mWindowInfo.actualSize = actualSize; releaseBuffers(); - // TODO should we ask the nativewindow to allocate buffers? } - if (newWindowInfo.transform != mWindowInfo.transform) { + if (transformHint != mWindowInfo.transform) { err = native_window_set_buffers_transform(mNativeWindow.get(), - InvertTransform(newWindowInfo.transform)); + InvertTransform(transformHint)); if (err != 0) { - ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)", - newWindowInfo.transform, strerror(-err), err); - newWindowInfo.transform = mWindowInfo.transform; - ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize); + ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)", transformHint, + strerror(-err), err); + mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd); + return nullptr; } + mWindowInfo.transform = transformHint; } - mWindowInfo = newWindowInfo; - } + mWindowInfo.size = actualSize; + if (mWindowInfo.transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { + mWindowInfo.size.set(actualSize.height(), actualSize.width()); + } - ANativeWindowBuffer* buffer; - int fence_fd; - int err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fence_fd); - if (err != 0) { - ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err); - return nullptr; + mWindowInfo.preTransform = GetPreTransformMatrix(mWindowInfo.size, mWindowInfo.transform); } uint32_t idx; diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h index b7af596ae762..bd2362612a13 100644 --- a/libs/hwui/renderthread/VulkanSurface.h +++ b/libs/hwui/renderthread/VulkanSurface.h @@ -17,6 +17,8 @@ #include <system/graphics.h> #include <system/window.h> +#include <ui/BufferQueueDefs.h> +#include <ui/PixelFormat.h> #include <vulkan/vulkan.h> #include <SkRefCnt.h> @@ -101,11 +103,12 @@ private: SkMatrix preTransform; }; - VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo, SkISize minWindowSize, - SkISize maxWindowSize, GrContext* grContext); + VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo, GrContext* grContext); + static bool InitializeWindowInfoStruct(ANativeWindow* window, ColorMode colorMode, + SkColorType colorType, sk_sp<SkColorSpace> colorSpace, + const VulkanManager& vkManager, uint32_t extraBuffers, + WindowInfo* outWindowInfo); static bool UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo); - static void ComputeWindowSizeAndTransform(WindowInfo* windowInfo, const SkISize& minSize, - const SkISize& maxSize); void releaseBuffers(); // TODO: Just use a vector? @@ -117,11 +120,8 @@ private: uint32_t mPresentCount = 0; NativeBufferInfo* mCurrentBufferInfo = nullptr; - - const SkISize mMinWindowSize; - const SkISize mMaxWindowSize; }; } /* namespace renderthread */ } /* namespace uirenderer */ -} /* namespace android */
\ No newline at end of file +} /* namespace android */ diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp index f79c8d3351e0..e4198017aee0 100644 --- a/libs/hwui/service/GraphicsStatsService.cpp +++ b/libs/hwui/service/GraphicsStatsService.cpp @@ -16,34 +16,39 @@ #include "GraphicsStatsService.h" -#include "JankTracker.h" -#include "protos/graphicsstats.pb.h" - -#include <google/protobuf/io/zero_copy_stream_impl_lite.h> -#include <log/log.h> - #include <errno.h> #include <fcntl.h> +#include <google/protobuf/io/zero_copy_stream_impl_lite.h> #include <inttypes.h> +#include <log/log.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> +#include <android/util/ProtoOutputStream.h> +#include <stats_event.h> +#include <statslog.h> + +#include "JankTracker.h" +#include "protos/graphicsstats.pb.h" + namespace android { namespace uirenderer { using namespace google::protobuf; +using namespace uirenderer::protos; constexpr int32_t sCurrentFileVersion = 1; constexpr int32_t sHeaderSize = 4; static_assert(sizeof(sCurrentFileVersion) == sHeaderSize, "Header size is wrong"); constexpr int sHistogramSize = ProfileData::HistogramSize(); +constexpr int sGPUHistogramSize = ProfileData::GPUHistogramSize(); -static bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, - const std::string& package, int64_t versionCode, - int64_t startTime, int64_t endTime, const ProfileData* data); +static bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, const std::string& package, + int64_t versionCode, int64_t startTime, int64_t endTime, + const ProfileData* data); static void dumpAsTextToFd(protos::GraphicsStatsProto* proto, int outFd); class FileDescriptor { @@ -166,6 +171,8 @@ bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, const std::str } proto->set_package_name(package); proto->set_version_code(versionCode); + proto->set_pipeline(data->pipelineType() == RenderPipelineType::SkiaGL ? + GraphicsStatsProto_PipelineType_GL : GraphicsStatsProto_PipelineType_VULKAN); auto summary = proto->mutable_summary(); summary->set_total_frames(summary->total_frames() + data->totalFrameCount()); summary->set_janky_frames(summary->janky_frames() + data->jankFrameCount()); @@ -178,8 +185,8 @@ bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, const std::str summary->set_slow_bitmap_upload_count(summary->slow_bitmap_upload_count() + data->jankTypeCount(kSlowSync)); summary->set_slow_draw_count(summary->slow_draw_count() + data->jankTypeCount(kSlowRT)); - summary->set_missed_deadline_count(summary->missed_deadline_count() - + data->jankTypeCount(kMissedDeadline)); + summary->set_missed_deadline_count(summary->missed_deadline_count() + + data->jankTypeCount(kMissedDeadline)); bool creatingHistogram = false; if (proto->histogram_size() == 0) { @@ -211,6 +218,37 @@ bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, const std::str bucket->set_frame_count(bucket->frame_count() + entry.frameCount); index++; }); + if (hitMergeError) return false; + // fill in GPU frame time histogram + creatingHistogram = false; + if (proto->gpu_histogram_size() == 0) { + proto->mutable_gpu_histogram()->Reserve(sGPUHistogramSize); + creatingHistogram = true; + } else if (proto->gpu_histogram_size() != sGPUHistogramSize) { + ALOGE("GPU histogram size mismatch, proto is %d expected %d", proto->gpu_histogram_size(), + sGPUHistogramSize); + return false; + } + index = 0; + data->histogramGPUForEach([&](ProfileData::HistogramEntry entry) { + if (hitMergeError) return; + + protos::GraphicsStatsHistogramBucketProto* bucket; + if (creatingHistogram) { + bucket = proto->add_gpu_histogram(); + bucket->set_render_millis(entry.renderTimeMs); + } else { + bucket = proto->mutable_gpu_histogram(index); + if (bucket->render_millis() != static_cast<int32_t>(entry.renderTimeMs)) { + ALOGW("GPU frame time mistmatch %d vs. %u", bucket->render_millis(), + entry.renderTimeMs); + hitMergeError = true; + return; + } + } + bucket->set_frame_count(bucket->frame_count() + entry.frameCount); + index++; + }); return !hitMergeError; } @@ -226,6 +264,22 @@ static int32_t findPercentile(protos::GraphicsStatsProto* proto, int percentile) return 0; } +static int32_t findGPUPercentile(protos::GraphicsStatsProto* proto, int percentile) { + uint32_t totalGPUFrameCount = 0; // this is usually proto->summary().total_frames() - 3. + for (auto it = proto->gpu_histogram().rbegin(); it != proto->gpu_histogram().rend(); ++it) { + totalGPUFrameCount += it->frame_count(); + } + int32_t pos = percentile * totalGPUFrameCount / 100; + int32_t remaining = totalGPUFrameCount - pos; + for (auto it = proto->gpu_histogram().rbegin(); it != proto->gpu_histogram().rend(); ++it) { + remaining -= it->frame_count(); + if (remaining <= 0) { + return it->render_millis(); + } + } + return 0; +} + void dumpAsTextToFd(protos::GraphicsStatsProto* proto, int fd) { // This isn't a full validation, just enough that we can deref at will if (proto->package_name().empty() || !proto->has_summary()) { @@ -255,6 +309,14 @@ void dumpAsTextToFd(protos::GraphicsStatsProto* proto, int fd) { for (const auto& it : proto->histogram()) { dprintf(fd, " %dms=%d", it.render_millis(), it.frame_count()); } + dprintf(fd, "\n50th gpu percentile: %dms", findGPUPercentile(proto, 50)); + dprintf(fd, "\n90th gpu percentile: %dms", findGPUPercentile(proto, 90)); + dprintf(fd, "\n95th gpu percentile: %dms", findGPUPercentile(proto, 95)); + dprintf(fd, "\n99th gpu percentile: %dms", findGPUPercentile(proto, 99)); + dprintf(fd, "\nGPU HISTOGRAM:"); + for (const auto& it : proto->gpu_histogram()) { + dprintf(fd, " %dms=%d", it.render_millis(), it.frame_count()); + } dprintf(fd, "\n"); } @@ -309,17 +371,69 @@ void GraphicsStatsService::saveBuffer(const std::string& path, const std::string class GraphicsStatsService::Dump { public: - Dump(int outFd, DumpType type) : mFd(outFd), mType(type) {} + Dump(int outFd, DumpType type) : mFd(outFd), mType(type) { + if (mFd == -1 && mType == DumpType::Protobuf) { + mType = DumpType::ProtobufStatsd; + } + } int fd() { return mFd; } DumpType type() { return mType; } protos::GraphicsStatsServiceDumpProto& proto() { return mProto; } + void mergeStat(const protos::GraphicsStatsProto& stat); + void updateProto(); private: + // use package name and app version for a key + typedef std::pair<std::string, int64_t> DumpKey; + + std::map<DumpKey, protos::GraphicsStatsProto> mStats; int mFd; DumpType mType; protos::GraphicsStatsServiceDumpProto mProto; }; +void GraphicsStatsService::Dump::mergeStat(const protos::GraphicsStatsProto& stat) { + auto dumpKey = std::make_pair(stat.package_name(), stat.version_code()); + auto findIt = mStats.find(dumpKey); + if (findIt == mStats.end()) { + mStats[dumpKey] = stat; + } else { + auto summary = findIt->second.mutable_summary(); + summary->set_total_frames(summary->total_frames() + stat.summary().total_frames()); + summary->set_janky_frames(summary->janky_frames() + stat.summary().janky_frames()); + summary->set_missed_vsync_count(summary->missed_vsync_count() + + stat.summary().missed_vsync_count()); + summary->set_high_input_latency_count(summary->high_input_latency_count() + + stat.summary().high_input_latency_count()); + summary->set_slow_ui_thread_count(summary->slow_ui_thread_count() + + stat.summary().slow_ui_thread_count()); + summary->set_slow_bitmap_upload_count(summary->slow_bitmap_upload_count() + + stat.summary().slow_bitmap_upload_count()); + summary->set_slow_draw_count(summary->slow_draw_count() + stat.summary().slow_draw_count()); + summary->set_missed_deadline_count(summary->missed_deadline_count() + + stat.summary().missed_deadline_count()); + for (int bucketIndex = 0; bucketIndex < findIt->second.histogram_size(); bucketIndex++) { + auto bucket = findIt->second.mutable_histogram(bucketIndex); + bucket->set_frame_count(bucket->frame_count() + + stat.histogram(bucketIndex).frame_count()); + } + for (int bucketIndex = 0; bucketIndex < findIt->second.gpu_histogram_size(); + bucketIndex++) { + auto bucket = findIt->second.mutable_gpu_histogram(bucketIndex); + bucket->set_frame_count(bucket->frame_count() + + stat.gpu_histogram(bucketIndex).frame_count()); + } + findIt->second.set_stats_start(std::min(findIt->second.stats_start(), stat.stats_start())); + findIt->second.set_stats_end(std::max(findIt->second.stats_end(), stat.stats_end())); + } +} + +void GraphicsStatsService::Dump::updateProto() { + for (auto& stat : mStats) { + mProto.add_stats()->CopyFrom(stat.second); + } +} + GraphicsStatsService::Dump* GraphicsStatsService::createDump(int outFd, DumpType type) { return new Dump(outFd, type); } @@ -340,8 +454,9 @@ void GraphicsStatsService::addToDump(Dump* dump, const std::string& path, path.empty() ? "<empty>" : path.c_str(), data); return; } - - if (dump->type() == DumpType::Protobuf) { + if (dump->type() == DumpType::ProtobufStatsd) { + dump->mergeStat(statsProto); + } else if (dump->type() == DumpType::Protobuf) { dump->proto().add_stats()->CopyFrom(statsProto); } else { dumpAsTextToFd(&statsProto, dump->fd()); @@ -353,7 +468,9 @@ void GraphicsStatsService::addToDump(Dump* dump, const std::string& path) { if (!parseFromFile(path, &statsProto)) { return; } - if (dump->type() == DumpType::Protobuf) { + if (dump->type() == DumpType::ProtobufStatsd) { + dump->mergeStat(statsProto); + } else if (dump->type() == DumpType::Protobuf) { dump->proto().add_stats()->CopyFrom(statsProto); } else { dumpAsTextToFd(&statsProto, dump->fd()); @@ -368,5 +485,83 @@ void GraphicsStatsService::finishDump(Dump* dump) { delete dump; } +using namespace google::protobuf; + +// Field ids taken from FrameTimingHistogram message in atoms.proto +#define TIME_MILLIS_BUCKETS_FIELD_NUMBER 1 +#define FRAME_COUNTS_FIELD_NUMBER 2 + +static void writeCpuHistogram(AStatsEvent* event, + const uirenderer::protos::GraphicsStatsProto& stat) { + util::ProtoOutputStream proto; + for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) { + auto& bucket = stat.histogram(bucketIndex); + proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED | + TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */, + (int)bucket.render_millis()); + } + for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) { + auto& bucket = stat.histogram(bucketIndex); + proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED | + FRAME_COUNTS_FIELD_NUMBER /* field id */, + (long long)bucket.frame_count()); + } + std::vector<uint8_t> outVector; + proto.serializeToVector(&outVector); + AStatsEvent_writeByteArray(event, outVector.data(), outVector.size()); +} + +static void writeGpuHistogram(AStatsEvent* event, + const uirenderer::protos::GraphicsStatsProto& stat) { + util::ProtoOutputStream proto; + for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) { + auto& bucket = stat.gpu_histogram(bucketIndex); + proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED | + TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */, + (int)bucket.render_millis()); + } + for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) { + auto& bucket = stat.gpu_histogram(bucketIndex); + proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED | + FRAME_COUNTS_FIELD_NUMBER /* field id */, + (long long)bucket.frame_count()); + } + std::vector<uint8_t> outVector; + proto.serializeToVector(&outVector); + AStatsEvent_writeByteArray(event, outVector.data(), outVector.size()); +} + + +void GraphicsStatsService::finishDumpInMemory(Dump* dump, AStatsEventList* data, + bool lastFullDay) { + dump->updateProto(); + auto& serviceDump = dump->proto(); + for (int stat_index = 0; stat_index < serviceDump.stats_size(); stat_index++) { + auto& stat = serviceDump.stats(stat_index); + AStatsEvent* event = AStatsEventList_addStatsEvent(data); + AStatsEvent_setAtomId(event, android::util::GRAPHICS_STATS); + AStatsEvent_writeString(event, stat.package_name().c_str()); + AStatsEvent_writeInt64(event, (int64_t)stat.version_code()); + AStatsEvent_writeInt64(event, (int64_t)stat.stats_start()); + AStatsEvent_writeInt64(event, (int64_t)stat.stats_end()); + AStatsEvent_writeInt32(event, (int32_t)stat.pipeline()); + AStatsEvent_writeInt32(event, (int32_t)stat.summary().total_frames()); + AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_vsync_count()); + AStatsEvent_writeInt32(event, (int32_t)stat.summary().high_input_latency_count()); + AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_ui_thread_count()); + AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_bitmap_upload_count()); + AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_draw_count()); + AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_deadline_count()); + writeCpuHistogram(event, stat); + writeGpuHistogram(event, stat); + // TODO: fill in UI mainline module version, when the feature is available. + AStatsEvent_writeInt64(event, (int64_t)0); + AStatsEvent_writeBool(event, !lastFullDay); + AStatsEvent_build(event); + } + delete dump; +} + + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/service/GraphicsStatsService.h b/libs/hwui/service/GraphicsStatsService.h index 389f599f439f..59e21d039c9d 100644 --- a/libs/hwui/service/GraphicsStatsService.h +++ b/libs/hwui/service/GraphicsStatsService.h @@ -20,6 +20,7 @@ #include "JankTracker.h" #include "utils/Macros.h" +#include <stats_pull_atom_callback.h> namespace android { namespace uirenderer { @@ -40,6 +41,7 @@ public: enum class DumpType { Text, Protobuf, + ProtobufStatsd, }; ANDROID_API static void saveBuffer(const std::string& path, const std::string& package, @@ -52,6 +54,8 @@ public: int64_t startTime, int64_t endTime, const ProfileData* data); ANDROID_API static void addToDump(Dump* dump, const std::string& path); ANDROID_API static void finishDump(Dump* dump); + ANDROID_API static void finishDumpInMemory(Dump* dump, AStatsEventList* data, + bool lastFullDay); // Visible for testing static bool parseFromFile(const std::string& path, protos::GraphicsStatsProto* output); diff --git a/libs/hwui/surfacetexture/EGLConsumer.cpp b/libs/hwui/surfacetexture/EGLConsumer.cpp deleted file mode 100644 index 85b3917809fa..000000000000 --- a/libs/hwui/surfacetexture/EGLConsumer.cpp +++ /dev/null @@ -1,675 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#include <inttypes.h> - -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> -#include <cutils/compiler.h> -#include <gui/BufferItem.h> -#include <gui/BufferQueue.h> -#include <private/gui/SyncFeatures.h> -#include "EGLConsumer.h" -#include "SurfaceTexture.h" - -#include <utils/Log.h> -#include <utils/String8.h> -#include <utils/Trace.h> - -#define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content" -#define EGL_PROTECTED_CONTENT_EXT 0x32C0 - -namespace android { - -// Macros for including the SurfaceTexture name in log messages -#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.string(), ##__VA_ARGS__) -#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.string(), ##__VA_ARGS__) -#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.string(), ##__VA_ARGS__) -#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) - -static const struct { - uint32_t width, height; - char const* bits; -} kDebugData = {15, 12, - "_______________" - "_______________" - "_____XX_XX_____" - "__X_X_____X_X__" - "__X_XXXXXXX_X__" - "__XXXXXXXXXXX__" - "___XX_XXX_XX___" - "____XXXXXXX____" - "_____X___X_____" - "____X_____X____" - "_______________" - "_______________"}; - -Mutex EGLConsumer::sStaticInitLock; -sp<GraphicBuffer> EGLConsumer::sReleasedTexImageBuffer; - -static bool hasEglProtectedContentImpl() { - EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); - const char* exts = eglQueryString(dpy, EGL_EXTENSIONS); - size_t cropExtLen = strlen(PROT_CONTENT_EXT_STR); - size_t extsLen = strlen(exts); - bool equal = !strcmp(PROT_CONTENT_EXT_STR, exts); - bool atStart = !strncmp(PROT_CONTENT_EXT_STR " ", exts, cropExtLen + 1); - bool atEnd = (cropExtLen + 1) < extsLen && - !strcmp(" " PROT_CONTENT_EXT_STR, exts + extsLen - (cropExtLen + 1)); - bool inMiddle = strstr(exts, " " PROT_CONTENT_EXT_STR " "); - return equal || atStart || atEnd || inMiddle; -} - -static bool hasEglProtectedContent() { - // Only compute whether the extension is present once the first time this - // function is called. - static bool hasIt = hasEglProtectedContentImpl(); - return hasIt; -} - -EGLConsumer::EGLConsumer() : mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT) {} - -status_t EGLConsumer::updateTexImage(SurfaceTexture& st) { - // Make sure the EGL state is the same as in previous calls. - status_t err = checkAndUpdateEglStateLocked(st); - if (err != NO_ERROR) { - return err; - } - - BufferItem item; - - // Acquire the next buffer. - // In asynchronous mode the list is guaranteed to be one buffer - // deep, while in synchronous mode we use the oldest buffer. - err = st.acquireBufferLocked(&item, 0); - if (err != NO_ERROR) { - if (err == BufferQueue::NO_BUFFER_AVAILABLE) { - // We always bind the texture even if we don't update its contents. - EGC_LOGV("updateTexImage: no buffers were available"); - glBindTexture(st.mTexTarget, st.mTexName); - err = NO_ERROR; - } else { - EGC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); - } - return err; - } - - // Release the previous buffer. - err = updateAndReleaseLocked(item, nullptr, st); - if (err != NO_ERROR) { - // We always bind the texture. - glBindTexture(st.mTexTarget, st.mTexName); - return err; - } - - // Bind the new buffer to the GL texture, and wait until it's ready. - return bindTextureImageLocked(st); -} - -status_t EGLConsumer::releaseTexImage(SurfaceTexture& st) { - // Make sure the EGL state is the same as in previous calls. - status_t err = NO_ERROR; - - // if we're detached, no need to validate EGL's state -- we won't use it. - if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { - err = checkAndUpdateEglStateLocked(st, true); - if (err != NO_ERROR) { - return err; - } - } - - // Update the EGLConsumer state. - int buf = st.mCurrentTexture; - if (buf != BufferQueue::INVALID_BUFFER_SLOT) { - EGC_LOGV("releaseTexImage: (slot=%d, mOpMode=%d)", buf, (int)st.mOpMode); - - // if we're detached, we just use the fence that was created in detachFromContext() - // so... basically, nothing more to do here. - if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { - // Do whatever sync ops we need to do before releasing the slot. - err = syncForReleaseLocked(mEglDisplay, st); - if (err != NO_ERROR) { - EGC_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err); - return err; - } - } - - err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer, mEglDisplay, - EGL_NO_SYNC_KHR); - if (err < NO_ERROR) { - EGC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err); - return err; - } - - if (mReleasedTexImage == nullptr) { - mReleasedTexImage = new EglImage(getDebugTexImageBuffer()); - } - - st.mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; - mCurrentTextureImage = mReleasedTexImage; - st.mCurrentCrop.makeInvalid(); - st.mCurrentTransform = 0; - st.mCurrentTimestamp = 0; - st.mCurrentDataSpace = HAL_DATASPACE_UNKNOWN; - st.mCurrentFence = Fence::NO_FENCE; - st.mCurrentFenceTime = FenceTime::NO_FENCE; - - // detached, don't touch the texture (and we may not even have an - // EGLDisplay here. - if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { - // This binds a dummy buffer (mReleasedTexImage). - status_t result = bindTextureImageLocked(st); - if (result != NO_ERROR) { - return result; - } - } - } - - return NO_ERROR; -} - -sp<GraphicBuffer> EGLConsumer::getDebugTexImageBuffer() { - Mutex::Autolock _l(sStaticInitLock); - if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) { - // The first time, create the debug texture in case the application - // continues to use it. - sp<GraphicBuffer> buffer = new GraphicBuffer( - kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, - GraphicBuffer::USAGE_SW_WRITE_RARELY, "[EGLConsumer debug texture]"); - uint32_t* bits; - buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits)); - uint32_t stride = buffer->getStride(); - uint32_t height = buffer->getHeight(); - memset(bits, 0, stride * height * 4); - for (uint32_t y = 0; y < kDebugData.height; y++) { - for (uint32_t x = 0; x < kDebugData.width; x++) { - bits[x] = (kDebugData.bits[y + kDebugData.width + x] == 'X') ? 0xFF000000 - : 0xFFFFFFFF; - } - bits += stride; - } - buffer->unlock(); - sReleasedTexImageBuffer = buffer; - } - return sReleasedTexImageBuffer; -} - -void EGLConsumer::onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st) { - // If item->mGraphicBuffer is not null, this buffer has not been acquired - // before, so any prior EglImage created is using a stale buffer. This - // replaces any old EglImage with a new one (using the new buffer). - int slot = item->mSlot; - if (item->mGraphicBuffer != nullptr || mEglSlots[slot].mEglImage.get() == nullptr) { - mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer); - } -} - -void EGLConsumer::onReleaseBufferLocked(int buf) { - mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; -} - -status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease, - SurfaceTexture& st) { - status_t err = NO_ERROR; - - int slot = item.mSlot; - - if (st.mOpMode != SurfaceTexture::OpMode::attachedToGL) { - EGC_LOGE( - "updateAndRelease: EGLConsumer is not attached to an OpenGL " - "ES context"); - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); - return INVALID_OPERATION; - } - - // Confirm state. - err = checkAndUpdateEglStateLocked(st); - if (err != NO_ERROR) { - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); - return err; - } - - // Ensure we have a valid EglImageKHR for the slot, creating an EglImage - // if nessessary, for the gralloc buffer currently in the slot in - // ConsumerBase. - // We may have to do this even when item.mGraphicBuffer == NULL (which - // means the buffer was previously acquired). - err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay); - if (err != NO_ERROR) { - EGC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay, - slot); - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); - return UNKNOWN_ERROR; - } - - // Do whatever sync ops we need to do before releasing the old slot. - if (slot != st.mCurrentTexture) { - err = syncForReleaseLocked(mEglDisplay, st); - if (err != NO_ERROR) { - // Release the buffer we just acquired. It's not safe to - // release the old buffer, so instead we just drop the new frame. - // As we are still under lock since acquireBuffer, it is safe to - // release by slot. - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, - EGL_NO_SYNC_KHR); - return err; - } - } - - EGC_LOGV( - "updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", st.mCurrentTexture, - mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle() : nullptr, - slot, st.mSlots[slot].mGraphicBuffer->handle); - - // Hang onto the pointer so that it isn't freed in the call to - // releaseBufferLocked() if we're in shared buffer mode and both buffers are - // the same. - sp<EglImage> nextTextureImage = mEglSlots[slot].mEglImage; - - // release old buffer - if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - if (pendingRelease == nullptr) { - status_t status = st.releaseBufferLocked( - st.mCurrentTexture, mCurrentTextureImage->graphicBuffer(), mEglDisplay, - mEglSlots[st.mCurrentTexture].mEglFence); - if (status < NO_ERROR) { - EGC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), - status); - err = status; - // keep going, with error raised [?] - } - } else { - pendingRelease->currentTexture = st.mCurrentTexture; - pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer(); - pendingRelease->display = mEglDisplay; - pendingRelease->fence = mEglSlots[st.mCurrentTexture].mEglFence; - pendingRelease->isPending = true; - } - } - - // Update the EGLConsumer state. - st.mCurrentTexture = slot; - mCurrentTextureImage = nextTextureImage; - st.mCurrentCrop = item.mCrop; - st.mCurrentTransform = item.mTransform; - st.mCurrentScalingMode = item.mScalingMode; - st.mCurrentTimestamp = item.mTimestamp; - st.mCurrentDataSpace = item.mDataSpace; - st.mCurrentFence = item.mFence; - st.mCurrentFenceTime = item.mFenceTime; - st.mCurrentFrameNumber = item.mFrameNumber; - - st.computeCurrentTransformMatrixLocked(); - - return err; -} - -status_t EGLConsumer::bindTextureImageLocked(SurfaceTexture& st) { - if (mEglDisplay == EGL_NO_DISPLAY) { - ALOGE("bindTextureImage: invalid display"); - return INVALID_OPERATION; - } - - GLenum error; - while ((error = glGetError()) != GL_NO_ERROR) { - EGC_LOGW("bindTextureImage: clearing GL error: %#04x", error); - } - - glBindTexture(st.mTexTarget, st.mTexName); - if (st.mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) { - EGC_LOGE("bindTextureImage: no currently-bound texture"); - return NO_INIT; - } - - status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay); - if (err != NO_ERROR) { - EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, - st.mCurrentTexture); - return UNKNOWN_ERROR; - } - mCurrentTextureImage->bindToTextureTarget(st.mTexTarget); - - // In the rare case that the display is terminated and then initialized - // again, we can't detect that the display changed (it didn't), but the - // image is invalid. In this case, repeat the exact same steps while - // forcing the creation of a new image. - if ((error = glGetError()) != GL_NO_ERROR) { - glBindTexture(st.mTexTarget, st.mTexName); - status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true); - if (result != NO_ERROR) { - EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, - st.mCurrentTexture); - return UNKNOWN_ERROR; - } - mCurrentTextureImage->bindToTextureTarget(st.mTexTarget); - if ((error = glGetError()) != GL_NO_ERROR) { - EGC_LOGE("bindTextureImage: error binding external image: %#04x", error); - return UNKNOWN_ERROR; - } - } - - // Wait for the new buffer to be ready. - return doGLFenceWaitLocked(st); -} - -status_t EGLConsumer::checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck) { - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if (!contextCheck) { - // if this is the first time we're called, mEglDisplay/mEglContext have - // never been set, so don't error out (below). - if (mEglDisplay == EGL_NO_DISPLAY) { - mEglDisplay = dpy; - } - if (mEglContext == EGL_NO_CONTEXT) { - mEglContext = ctx; - } - } - - if (mEglDisplay != dpy || dpy == EGL_NO_DISPLAY) { - EGC_LOGE("checkAndUpdateEglState: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if (mEglContext != ctx || ctx == EGL_NO_CONTEXT) { - EGC_LOGE("checkAndUpdateEglState: invalid current EGLContext"); - return INVALID_OPERATION; - } - - mEglDisplay = dpy; - mEglContext = ctx; - return NO_ERROR; -} - -status_t EGLConsumer::detachFromContext(SurfaceTexture& st) { - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) { - EGC_LOGE("detachFromContext: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) { - EGC_LOGE("detachFromContext: invalid current EGLContext"); - return INVALID_OPERATION; - } - - if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) { - status_t err = syncForReleaseLocked(dpy, st); - if (err != OK) { - return err; - } - - glDeleteTextures(1, &st.mTexName); - } - - mEglDisplay = EGL_NO_DISPLAY; - mEglContext = EGL_NO_CONTEXT; - - return OK; -} - -status_t EGLConsumer::attachToContext(uint32_t tex, SurfaceTexture& st) { - // Initialize mCurrentTextureImage if there is a current buffer from past attached state. - int slot = st.mCurrentTexture; - if (slot != BufferItem::INVALID_BUFFER_SLOT) { - if (!mEglSlots[slot].mEglImage.get()) { - mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer); - } - mCurrentTextureImage = mEglSlots[slot].mEglImage; - } - - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if (dpy == EGL_NO_DISPLAY) { - EGC_LOGE("attachToContext: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if (ctx == EGL_NO_CONTEXT) { - EGC_LOGE("attachToContext: invalid current EGLContext"); - return INVALID_OPERATION; - } - - // We need to bind the texture regardless of whether there's a current - // buffer. - glBindTexture(st.mTexTarget, GLuint(tex)); - - mEglDisplay = dpy; - mEglContext = ctx; - st.mTexName = tex; - st.mOpMode = SurfaceTexture::OpMode::attachedToGL; - - if (mCurrentTextureImage != nullptr) { - // This may wait for a buffer a second time. This is likely required if - // this is a different context, since otherwise the wait could be skipped - // by bouncing through another context. For the same context the extra - // wait is redundant. - status_t err = bindTextureImageLocked(st); - if (err != NO_ERROR) { - return err; - } - } - - return OK; -} - -status_t EGLConsumer::syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st) { - EGC_LOGV("syncForReleaseLocked"); - - if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - if (SyncFeatures::getInstance().useNativeFenceSync()) { - EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); - if (sync == EGL_NO_SYNC_KHR) { - EGC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError()); - return UNKNOWN_ERROR; - } - glFlush(); - int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync); - eglDestroySyncKHR(dpy, sync); - if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { - EGC_LOGE( - "syncForReleaseLocked: error dup'ing native fence " - "fd: %#x", - eglGetError()); - return UNKNOWN_ERROR; - } - sp<Fence> fence(new Fence(fenceFd)); - status_t err = st.addReleaseFenceLocked(st.mCurrentTexture, - mCurrentTextureImage->graphicBuffer(), fence); - if (err != OK) { - EGC_LOGE( - "syncForReleaseLocked: error adding release fence: " - "%s (%d)", - strerror(-err), err); - return err; - } - } else if (st.mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) { - EGLSyncKHR fence = mEglSlots[st.mCurrentTexture].mEglFence; - if (fence != EGL_NO_SYNC_KHR) { - // There is already a fence for the current slot. We need to - // wait on that before replacing it with another fence to - // ensure that all outstanding buffer accesses have completed - // before the producer accesses it. - EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); - if (result == EGL_FALSE) { - EGC_LOGE( - "syncForReleaseLocked: error waiting for previous " - "fence: %#x", - eglGetError()); - return UNKNOWN_ERROR; - } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { - EGC_LOGE( - "syncForReleaseLocked: timeout waiting for previous " - "fence"); - return TIMED_OUT; - } - eglDestroySyncKHR(dpy, fence); - } - - // Create a fence for the outstanding accesses in the current - // OpenGL ES context. - fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr); - if (fence == EGL_NO_SYNC_KHR) { - EGC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError()); - return UNKNOWN_ERROR; - } - glFlush(); - mEglSlots[st.mCurrentTexture].mEglFence = fence; - } - } - - return OK; -} - -status_t EGLConsumer::doGLFenceWaitLocked(SurfaceTexture& st) const { - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) { - EGC_LOGE("doGLFenceWait: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) { - EGC_LOGE("doGLFenceWait: invalid current EGLContext"); - return INVALID_OPERATION; - } - - if (st.mCurrentFence->isValid()) { - if (SyncFeatures::getInstance().useWaitSync() && - SyncFeatures::getInstance().useNativeFenceSync()) { - // Create an EGLSyncKHR from the current fence. - int fenceFd = st.mCurrentFence->dup(); - if (fenceFd == -1) { - EGC_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno); - return -errno; - } - EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE}; - EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); - if (sync == EGL_NO_SYNC_KHR) { - close(fenceFd); - EGC_LOGE("doGLFenceWait: error creating EGL fence: %#x", eglGetError()); - return UNKNOWN_ERROR; - } - - // XXX: The spec draft is inconsistent as to whether this should - // return an EGLint or void. Ignore the return value for now, as - // it's not strictly needed. - eglWaitSyncKHR(dpy, sync, 0); - EGLint eglErr = eglGetError(); - eglDestroySyncKHR(dpy, sync); - if (eglErr != EGL_SUCCESS) { - EGC_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", eglErr); - return UNKNOWN_ERROR; - } - } else { - status_t err = st.mCurrentFence->waitForever("EGLConsumer::doGLFenceWaitLocked"); - if (err != NO_ERROR) { - EGC_LOGE("doGLFenceWait: error waiting for fence: %d", err); - return err; - } - } - } - - return NO_ERROR; -} - -void EGLConsumer::onFreeBufferLocked(int slotIndex) { - mEglSlots[slotIndex].mEglImage.clear(); -} - -void EGLConsumer::onAbandonLocked() { - mCurrentTextureImage.clear(); -} - -EGLConsumer::EglImage::EglImage(sp<GraphicBuffer> graphicBuffer) - : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), mEglDisplay(EGL_NO_DISPLAY) {} - -EGLConsumer::EglImage::~EglImage() { - if (mEglImage != EGL_NO_IMAGE_KHR) { - if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { - ALOGE("~EglImage: eglDestroyImageKHR failed"); - } - eglTerminate(mEglDisplay); - } -} - -status_t EGLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, bool forceCreation) { - // If there's an image and it's no longer valid, destroy it. - bool haveImage = mEglImage != EGL_NO_IMAGE_KHR; - bool displayInvalid = mEglDisplay != eglDisplay; - if (haveImage && (displayInvalid || forceCreation)) { - if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { - ALOGE("createIfNeeded: eglDestroyImageKHR failed"); - } - eglTerminate(mEglDisplay); - mEglImage = EGL_NO_IMAGE_KHR; - mEglDisplay = EGL_NO_DISPLAY; - } - - // If there's no image, create one. - if (mEglImage == EGL_NO_IMAGE_KHR) { - mEglDisplay = eglDisplay; - mEglImage = createImage(mEglDisplay, mGraphicBuffer); - } - - // Fail if we can't create a valid image. - if (mEglImage == EGL_NO_IMAGE_KHR) { - mEglDisplay = EGL_NO_DISPLAY; - const sp<GraphicBuffer>& buffer = mGraphicBuffer; - ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", - buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(), - buffer->getPixelFormat()); - return UNKNOWN_ERROR; - } - - return OK; -} - -void EGLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) { - glEGLImageTargetTexture2DOES(texTarget, static_cast<GLeglImageOES>(mEglImage)); -} - -EGLImageKHR EGLConsumer::EglImage::createImage(EGLDisplay dpy, - const sp<GraphicBuffer>& graphicBuffer) { - EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(graphicBuffer->getNativeBuffer()); - const bool createProtectedImage = - (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent(); - EGLint attrs[] = { - EGL_IMAGE_PRESERVED_KHR, - EGL_TRUE, - createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, - createProtectedImage ? EGL_TRUE : EGL_NONE, - EGL_NONE, - }; - eglInitialize(dpy, nullptr, nullptr); - EGLImageKHR image = - eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); - if (image == EGL_NO_IMAGE_KHR) { - EGLint error = eglGetError(); - ALOGE("error creating EGLImage: %#x", error); - eglTerminate(dpy); - } - return image; -} - -} // namespace android diff --git a/libs/hwui/surfacetexture/EGLConsumer.h b/libs/hwui/surfacetexture/EGLConsumer.h deleted file mode 100644 index 7dac3ef0f44a..000000000000 --- a/libs/hwui/surfacetexture/EGLConsumer.h +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#pragma once - -#include <EGL/egl.h> -#include <EGL/eglext.h> - -#include <gui/BufferQueueDefs.h> - -#include <ui/FenceTime.h> -#include <ui/GraphicBuffer.h> -#include <utils/Mutex.h> - -namespace android { - -class SurfaceTexture; - -/* - * EGLConsumer implements the parts of SurfaceTexture that deal with - * textures attached to an GL context. - */ -class EGLConsumer { -public: - EGLConsumer(); - - /** - * updateTexImage acquires the most recently queued buffer, and sets the - * image contents of the target texture to it. - * - * This call may only be made while the OpenGL ES context to which the - * target texture belongs is bound to the calling thread. - * - * This calls doGLFenceWait to ensure proper synchronization. - */ - status_t updateTexImage(SurfaceTexture& st); - - /* - * releaseTexImage releases the texture acquired in updateTexImage(). - * This is intended to be used in single buffer mode. - * - * This call may only be made while the OpenGL ES context to which the - * target texture belongs is bound to the calling thread. - */ - status_t releaseTexImage(SurfaceTexture& st); - - /** - * detachFromContext detaches the EGLConsumer from the calling thread's - * current OpenGL ES context. This context must be the same as the context - * that was current for previous calls to updateTexImage. - * - * Detaching a EGLConsumer from an OpenGL ES context will result in the - * deletion of the OpenGL ES texture object into which the images were being - * streamed. After a EGLConsumer has been detached from the OpenGL ES - * context calls to updateTexImage will fail returning INVALID_OPERATION - * until the EGLConsumer is attached to a new OpenGL ES context using the - * attachToContext method. - */ - status_t detachFromContext(SurfaceTexture& st); - - /** - * attachToContext attaches a EGLConsumer that is currently in the - * 'detached' state to the current OpenGL ES context. A EGLConsumer is - * in the 'detached' state iff detachFromContext has successfully been - * called and no calls to attachToContext have succeeded since the last - * detachFromContext call. Calls to attachToContext made on a - * EGLConsumer that is not in the 'detached' state will result in an - * INVALID_OPERATION error. - * - * The tex argument specifies the OpenGL ES texture object name in the - * new context into which the image contents will be streamed. A successful - * call to attachToContext will result in this texture object being bound to - * the texture target and populated with the image contents that were - * current at the time of the last call to detachFromContext. - */ - status_t attachToContext(uint32_t tex, SurfaceTexture& st); - - /** - * onAcquireBufferLocked amends the ConsumerBase method to update the - * mEglSlots array in addition to the ConsumerBase behavior. - */ - void onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st); - - /** - * onReleaseBufferLocked amends the ConsumerBase method to update the - * mEglSlots array in addition to the ConsumerBase. - */ - void onReleaseBufferLocked(int slot); - - /** - * onFreeBufferLocked frees up the given buffer slot. If the slot has been - * initialized this will release the reference to the GraphicBuffer in that - * slot and destroy the EGLImage in that slot. Otherwise it has no effect. - */ - void onFreeBufferLocked(int slotIndex); - - /** - * onAbandonLocked amends the ConsumerBase method to clear - * mCurrentTextureImage in addition to the ConsumerBase behavior. - */ - void onAbandonLocked(); - -protected: - struct PendingRelease { - PendingRelease() - : isPending(false) - , currentTexture(-1) - , graphicBuffer() - , display(nullptr) - , fence(nullptr) {} - - bool isPending; - int currentTexture; - sp<GraphicBuffer> graphicBuffer; - EGLDisplay display; - EGLSyncKHR fence; - }; - - /** - * This releases the buffer in the slot referenced by mCurrentTexture, - * then updates state to refer to the BufferItem, which must be a - * newly-acquired buffer. If pendingRelease is not null, the parameters - * which would have been passed to releaseBufferLocked upon the successful - * completion of the method will instead be returned to the caller, so that - * it may call releaseBufferLocked itself later. - */ - status_t updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease, - SurfaceTexture& st); - - /** - * Binds mTexName and the current buffer to mTexTarget. Uses - * mCurrentTexture if it's set, mCurrentTextureImage if not. If the - * bind succeeds, this calls doGLFenceWait. - */ - status_t bindTextureImageLocked(SurfaceTexture& st); - - /** - * Gets the current EGLDisplay and EGLContext values, and compares them - * to mEglDisplay and mEglContext. If the fields have been previously - * set, the values must match; if not, the fields are set to the current - * values. - * The contextCheck argument is used to ensure that a GL context is - * properly set; when set to false, the check is not performed. - */ - status_t checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck = false); - - /** - * EglImage is a utility class for tracking and creating EGLImageKHRs. There - * is primarily just one image per slot, but there is also special cases: - * - For releaseTexImage, we use a debug image (mReleasedTexImage) - * - After freeBuffer, we must still keep the current image/buffer - * Reference counting EGLImages lets us handle all these cases easily while - * also only creating new EGLImages from buffers when required. - */ - class EglImage : public LightRefBase<EglImage> { - public: - EglImage(sp<GraphicBuffer> graphicBuffer); - - /** - * createIfNeeded creates an EGLImage if required (we haven't created - * one yet, or the EGLDisplay or crop-rect has changed). - */ - status_t createIfNeeded(EGLDisplay display, bool forceCreate = false); - - /** - * This calls glEGLImageTargetTexture2DOES to bind the image to the - * texture in the specified texture target. - */ - void bindToTextureTarget(uint32_t texTarget); - - const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; } - const native_handle* graphicBufferHandle() { - return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle; - } - - private: - // Only allow instantiation using ref counting. - friend class LightRefBase<EglImage>; - virtual ~EglImage(); - - // createImage creates a new EGLImage from a GraphicBuffer. - EGLImageKHR createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer); - - // Disallow copying - EglImage(const EglImage& rhs); - void operator=(const EglImage& rhs); - - // mGraphicBuffer is the buffer that was used to create this image. - sp<GraphicBuffer> mGraphicBuffer; - - // mEglImage is the EGLImage created from mGraphicBuffer. - EGLImageKHR mEglImage; - - // mEGLDisplay is the EGLDisplay that was used to create mEglImage. - EGLDisplay mEglDisplay; - - // mCropRect is the crop rectangle passed to EGL when mEglImage - // was created. - Rect mCropRect; - }; - - /** - * doGLFenceWaitLocked inserts a wait command into the OpenGL ES command - * stream to ensure that it is safe for future OpenGL ES commands to - * access the current texture buffer. - */ - status_t doGLFenceWaitLocked(SurfaceTexture& st) const; - - /** - * syncForReleaseLocked performs the synchronization needed to release the - * current slot from an OpenGL ES context. If needed it will set the - * current slot's fence to guard against a producer accessing the buffer - * before the outstanding accesses have completed. - */ - status_t syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st); - - /** - * returns a graphic buffer used when the texture image has been released - */ - static sp<GraphicBuffer> getDebugTexImageBuffer(); - - /** - * The default consumer usage flags that EGLConsumer always sets on its - * BufferQueue instance; these will be OR:d with any additional flags passed - * from the EGLConsumer user. In particular, EGLConsumer will always - * consume buffers as hardware textures. - */ - static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; - - /** - * mCurrentTextureImage is the EglImage/buffer of the current texture. It's - * possible that this buffer is not associated with any buffer slot, so we - * must track it separately in order to support the getCurrentBuffer method. - */ - sp<EglImage> mCurrentTextureImage; - - /** - * EGLSlot contains the information and object references that - * EGLConsumer maintains about a BufferQueue buffer slot. - */ - struct EglSlot { - EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {} - - /** - * mEglImage is the EGLImage created from mGraphicBuffer. - */ - sp<EglImage> mEglImage; - - /** - * mFence is the EGL sync object that must signal before the buffer - * associated with this buffer slot may be dequeued. It is initialized - * to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based - * on a compile-time option) set to a new sync object in updateTexImage. - */ - EGLSyncKHR mEglFence; - }; - - /** - * mEglDisplay is the EGLDisplay with which this EGLConsumer is currently - * associated. It is intialized to EGL_NO_DISPLAY and gets set to the - * current display when updateTexImage is called for the first time and when - * attachToContext is called. - */ - EGLDisplay mEglDisplay; - - /** - * mEglContext is the OpenGL ES context with which this EGLConsumer is - * currently associated. It is initialized to EGL_NO_CONTEXT and gets set - * to the current GL context when updateTexImage is called for the first - * time and when attachToContext is called. - */ - EGLContext mEglContext; - - /** - * mEGLSlots stores the buffers that have been allocated by the BufferQueue - * for each buffer slot. It is initialized to null pointers, and gets - * filled in with the result of BufferQueue::acquire when the - * client dequeues a buffer from a - * slot that has not yet been used. The buffer allocated to a slot will also - * be replaced if the requested buffer usage or geometry differs from that - * of the buffer allocated to a slot. - */ - EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; - - /** - * protects static initialization - */ - static Mutex sStaticInitLock; - - /** - * mReleasedTexImageBuffer is a dummy buffer used when in single buffer - * mode and releaseTexImage() has been called - */ - static sp<GraphicBuffer> sReleasedTexImageBuffer; - sp<EglImage> mReleasedTexImage; -}; - -} // namespace android diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp deleted file mode 100644 index 17ee17d5cd1d..000000000000 --- a/libs/hwui/surfacetexture/ImageConsumer.cpp +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#include "ImageConsumer.h" -#include <gui/BufferQueue.h> -#include "Properties.h" -#include "SurfaceTexture.h" -#include "renderstate/RenderState.h" -#include "renderthread/EglManager.h" -#include "renderthread/RenderThread.h" -#include "renderthread/VulkanManager.h" -#include "utils/Color.h" -#include <GrAHardwareBufferUtils.h> -#include <GrBackendSurface.h> - -// Macro for including the SurfaceTexture name in log messages -#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) - -using namespace android::uirenderer::renderthread; - -namespace android { - -void ImageConsumer::onFreeBufferLocked(int slotIndex) { - // This callback may be invoked on any thread. - mImageSlots[slotIndex].clear(); -} - -void ImageConsumer::onAcquireBufferLocked(BufferItem* item) { - // If item->mGraphicBuffer is not null, this buffer has not been acquired - // before, so any prior SkImage is created with a stale buffer. This resets the stale SkImage. - if (item->mGraphicBuffer != nullptr) { - mImageSlots[item->mSlot].clear(); - } -} - -void ImageConsumer::onReleaseBufferLocked(int buf) { - mImageSlots[buf].eglFence() = EGL_NO_SYNC_KHR; -} - -/** - * AutoBackendTextureRelease manages EglImage/VkImage lifetime. It is a ref-counted object - * that keeps GPU resources alive until the last SKImage object using them is destroyed. - */ -class AutoBackendTextureRelease { -public: - static void releaseProc(SkImage::ReleaseContext releaseContext); - - AutoBackendTextureRelease(GrContext* context, GraphicBuffer* buffer); - - const GrBackendTexture& getTexture() const { return mBackendTexture; } - - void ref() { mUsageCount++; } - - void unref(bool releaseImage); - - inline sk_sp<SkImage> getImage() { return mImage; } - - void makeImage(sp<GraphicBuffer>& graphicBuffer, android_dataspace dataspace, - GrContext* context); - - void newBufferContent(GrContext* context); - -private: - // The only way to invoke dtor is with unref, when mUsageCount is 0. - ~AutoBackendTextureRelease() {} - - GrBackendTexture mBackendTexture; - GrAHardwareBufferUtils::DeleteImageProc mDeleteProc; - GrAHardwareBufferUtils::UpdateImageProc mUpdateProc; - GrAHardwareBufferUtils::TexImageCtx mImageCtx; - - // Starting with refcount 1, because the first ref is held by SurfaceTexture. Additional refs - // are held by SkImages. - int mUsageCount = 1; - - // mImage is the SkImage created from mBackendTexture. - sk_sp<SkImage> mImage; -}; - -AutoBackendTextureRelease::AutoBackendTextureRelease(GrContext* context, GraphicBuffer* buffer) { - bool createProtectedImage = - 0 != (buffer->getUsage() & GraphicBuffer::USAGE_PROTECTED); - GrBackendFormat backendFormat = GrAHardwareBufferUtils::GetBackendFormat( - context, - reinterpret_cast<AHardwareBuffer*>(buffer), - buffer->getPixelFormat(), - false); - mBackendTexture = GrAHardwareBufferUtils::MakeBackendTexture( - context, - reinterpret_cast<AHardwareBuffer*>(buffer), - buffer->getWidth(), - buffer->getHeight(), - &mDeleteProc, - &mUpdateProc, - &mImageCtx, - createProtectedImage, - backendFormat, - false); -} - -void AutoBackendTextureRelease::unref(bool releaseImage) { - if (!RenderThread::isCurrent()) { - // EGLImage needs to be destroyed on RenderThread to prevent memory leak. - // ~SkImage dtor for both pipelines needs to be invoked on RenderThread, because it is not - // thread safe. - RenderThread::getInstance().queue().post([this, releaseImage]() { unref(releaseImage); }); - return; - } - - if (releaseImage) { - mImage.reset(); - } - - mUsageCount--; - if (mUsageCount <= 0) { - if (mBackendTexture.isValid()) { - mDeleteProc(mImageCtx); - mBackendTexture = {}; - } - delete this; - } -} - -void AutoBackendTextureRelease::releaseProc(SkImage::ReleaseContext releaseContext) { - AutoBackendTextureRelease* textureRelease = - reinterpret_cast<AutoBackendTextureRelease*>(releaseContext); - textureRelease->unref(false); -} - -void AutoBackendTextureRelease::makeImage(sp<GraphicBuffer>& graphicBuffer, - android_dataspace dataspace, GrContext* context) { - SkColorType colorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat( - graphicBuffer->getPixelFormat()); - mImage = SkImage::MakeFromTexture(context, - mBackendTexture, - kTopLeft_GrSurfaceOrigin, - colorType, - kPremul_SkAlphaType, - uirenderer::DataSpaceToColorSpace(dataspace), - releaseProc, - this); - if (mImage.get()) { - // The following ref will be counteracted by releaseProc, when SkImage is discarded. - ref(); - } -} - -void AutoBackendTextureRelease::newBufferContent(GrContext* context) { - if (mBackendTexture.isValid()) { - mUpdateProc(mImageCtx, context); - } -} - -void ImageConsumer::ImageSlot::createIfNeeded(sp<GraphicBuffer> graphicBuffer, - android_dataspace dataspace, bool forceCreate, - GrContext* context) { - if (!mTextureRelease || !mTextureRelease->getImage().get() || dataspace != mDataspace - || forceCreate) { - if (!graphicBuffer.get()) { - clear(); - return; - } - - if (!mTextureRelease) { - mTextureRelease = new AutoBackendTextureRelease(context, graphicBuffer.get()); - } else { - mTextureRelease->newBufferContent(context); - } - - mDataspace = dataspace; - mTextureRelease->makeImage(graphicBuffer, dataspace, context); - } -} - -void ImageConsumer::ImageSlot::clear() { - if (mTextureRelease) { - // The following unref counteracts the initial mUsageCount of 1, set by default initializer. - mTextureRelease->unref(true); - mTextureRelease = nullptr; - } -} - -sk_sp<SkImage> ImageConsumer::ImageSlot::getImage() { - return mTextureRelease ? mTextureRelease->getImage() : nullptr; -} - -sk_sp<SkImage> ImageConsumer::dequeueImage(bool* queueEmpty, SurfaceTexture& st, - uirenderer::RenderState& renderState) { - BufferItem item; - status_t err; - err = st.acquireBufferLocked(&item, 0); - if (err != OK) { - if (err != BufferQueue::NO_BUFFER_AVAILABLE) { - IMG_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); - } else { - int slot = st.mCurrentTexture; - if (slot != BufferItem::INVALID_BUFFER_SLOT) { - *queueEmpty = true; - mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, - st.mCurrentDataSpace, false, renderState.getRenderThread().getGrContext()); - return mImageSlots[slot].getImage(); - } - } - return nullptr; - } - - int slot = item.mSlot; - if (item.mFence->isValid()) { - // Wait on the producer fence for the buffer to be ready. - if (uirenderer::Properties::getRenderPipelineType() == - uirenderer::RenderPipelineType::SkiaGL) { - err = renderState.getRenderThread().eglManager().fenceWait(item.mFence); - } else { - err = renderState.getRenderThread().vulkanManager().fenceWait( - item.mFence, renderState.getRenderThread().getGrContext()); - } - if (err != OK) { - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); - return nullptr; - } - } - - // Release old buffer. - if (st.mCurrentTexture != BufferItem::INVALID_BUFFER_SLOT) { - // If needed, set the released slot's fence to guard against a producer accessing the - // buffer before the outstanding accesses have completed. - sp<Fence> releaseFence; - EGLDisplay display = EGL_NO_DISPLAY; - if (uirenderer::Properties::getRenderPipelineType() == - uirenderer::RenderPipelineType::SkiaGL) { - auto& eglManager = renderState.getRenderThread().eglManager(); - display = eglManager.eglDisplay(); - err = eglManager.createReleaseFence(st.mUseFenceSync, &mImageSlots[slot].eglFence(), - releaseFence); - } else { - err = renderState.getRenderThread().vulkanManager().createReleaseFence( - releaseFence, renderState.getRenderThread().getGrContext()); - } - if (OK != err) { - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); - return nullptr; - } - - if (releaseFence.get()) { - status_t err = st.addReleaseFenceLocked( - st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, releaseFence); - if (err != OK) { - IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err); - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); - return nullptr; - } - } - - // Finally release the old buffer. - status_t status = st.releaseBufferLocked( - st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, display, - mImageSlots[st.mCurrentTexture].eglFence()); - if (status < NO_ERROR) { - IMG_LOGE("dequeueImage: failed to release buffer: %s (%d)", strerror(-status), status); - err = status; - // Keep going, with error raised. - } - } - - // Update the state. - st.mCurrentTexture = slot; - st.mCurrentCrop = item.mCrop; - st.mCurrentTransform = item.mTransform; - st.mCurrentScalingMode = item.mScalingMode; - st.mCurrentTimestamp = item.mTimestamp; - st.mCurrentDataSpace = item.mDataSpace; - st.mCurrentFence = item.mFence; - st.mCurrentFenceTime = item.mFenceTime; - st.mCurrentFrameNumber = item.mFrameNumber; - st.computeCurrentTransformMatrixLocked(); - - *queueEmpty = false; - mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace, true, - renderState.getRenderThread().getGrContext()); - return mImageSlots[slot].getImage(); -} - -} /* namespace android */ diff --git a/libs/hwui/surfacetexture/ImageConsumer.h b/libs/hwui/surfacetexture/ImageConsumer.h deleted file mode 100644 index 3e2a91a251f7..000000000000 --- a/libs/hwui/surfacetexture/ImageConsumer.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#pragma once - -#include <EGL/egl.h> -#include <EGL/eglext.h> - -#include <gui/BufferQueueDefs.h> - -#include <SkImage.h> -#include <cutils/compiler.h> -#include <gui/BufferItem.h> -#include <system/graphics.h> - -namespace android { - -namespace uirenderer { -class RenderState; -} - -class AutoBackendTextureRelease; -class SurfaceTexture; - -/* - * ImageConsumer implements the parts of SurfaceTexture that deal with - * images consumed by HWUI view system. - */ -class ImageConsumer { -public: - sk_sp<SkImage> dequeueImage(bool* queueEmpty, SurfaceTexture& cb, - uirenderer::RenderState& renderState); - - /** - * onAcquireBufferLocked amends the ConsumerBase method to update the - * mImageSlots array in addition to the ConsumerBase behavior. - */ - void onAcquireBufferLocked(BufferItem* item); - - /** - * onReleaseBufferLocked amends the ConsumerBase method to update the - * mImageSlots array in addition to the ConsumerBase. - */ - void onReleaseBufferLocked(int slot); - - /** - * onFreeBufferLocked frees up the given buffer slot. If the slot has been - * initialized this will release the reference to the GraphicBuffer in that - * slot and destroy the SkImage in that slot. Otherwise it has no effect. - */ - void onFreeBufferLocked(int slotIndex); - -private: - /** - * ImageSlot contains the information and object references that - * ImageConsumer maintains about a BufferQueue buffer slot. - */ - class ImageSlot { - public: - ImageSlot() : mDataspace(HAL_DATASPACE_UNKNOWN), mEglFence(EGL_NO_SYNC_KHR) {} - - ~ImageSlot() { clear(); } - - void createIfNeeded(sp<GraphicBuffer> graphicBuffer, android_dataspace dataspace, - bool forceCreate, GrContext* context); - - void clear(); - - inline EGLSyncKHR& eglFence() { return mEglFence; } - - sk_sp<SkImage> getImage(); - - private: - // the dataspace associated with the current image - android_dataspace mDataspace; - - /** - * mEglFence is the EGL sync object that must signal before the buffer - * associated with this buffer slot may be dequeued. - */ - EGLSyncKHR mEglFence; - - /** - * mTextureRelease may outlive ImageConsumer, if the last ref is held by an SkImage. - * ImageConsumer holds one ref to mTextureRelease, which is decremented by "clear". - */ - AutoBackendTextureRelease* mTextureRelease = nullptr; - }; - - /** - * ImageConsumer stores the SkImages that have been allocated by the BufferQueue - * for each buffer slot. It is initialized to null pointers, and gets - * filled in with the result of BufferQueue::acquire when the - * client dequeues a buffer from a - * slot that has not yet been used. The buffer allocated to a slot will also - * be replaced if the requested buffer usage or geometry differs from that - * of the buffer allocated to a slot. - */ - ImageSlot mImageSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; -}; - -} /* namespace android */ diff --git a/libs/hwui/surfacetexture/SurfaceTexture.cpp b/libs/hwui/surfacetexture/SurfaceTexture.cpp deleted file mode 100644 index a27db6591d6a..000000000000 --- a/libs/hwui/surfacetexture/SurfaceTexture.cpp +++ /dev/null @@ -1,499 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#include <cutils/compiler.h> -#include <gui/BufferQueue.h> -#include <math/mat4.h> -#include <system/window.h> - -#include <utils/Trace.h> - -#include "Matrix.h" -#include "SurfaceTexture.h" -#include "ImageConsumer.h" - -namespace android { - -// Macros for including the SurfaceTexture name in log messages -#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) -#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) -#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) -#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) - -static const mat4 mtxIdentity; - -SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, - uint32_t texTarget, bool useFenceSync, bool isControlledByApp) - : ConsumerBase(bq, isControlledByApp) - , mCurrentCrop(Rect::EMPTY_RECT) - , mCurrentTransform(0) - , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE) - , mCurrentFence(Fence::NO_FENCE) - , mCurrentTimestamp(0) - , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN) - , mCurrentFrameNumber(0) - , mDefaultWidth(1) - , mDefaultHeight(1) - , mFilteringEnabled(true) - , mTexName(tex) - , mUseFenceSync(useFenceSync) - , mTexTarget(texTarget) - , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) - , mOpMode(OpMode::attachedToGL) { - SFT_LOGV("SurfaceTexture"); - - memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); - - mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); -} - -SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget, - bool useFenceSync, bool isControlledByApp) - : ConsumerBase(bq, isControlledByApp) - , mCurrentCrop(Rect::EMPTY_RECT) - , mCurrentTransform(0) - , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE) - , mCurrentFence(Fence::NO_FENCE) - , mCurrentTimestamp(0) - , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN) - , mCurrentFrameNumber(0) - , mDefaultWidth(1) - , mDefaultHeight(1) - , mFilteringEnabled(true) - , mTexName(0) - , mUseFenceSync(useFenceSync) - , mTexTarget(texTarget) - , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) - , mOpMode(OpMode::detached) { - SFT_LOGV("SurfaceTexture"); - - memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); - - mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); -} - -status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) { - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - SFT_LOGE("setDefaultBufferSize: SurfaceTexture is abandoned!"); - return NO_INIT; - } - mDefaultWidth = w; - mDefaultHeight = h; - return mConsumer->setDefaultBufferSize(w, h); -} - -status_t SurfaceTexture::updateTexImage() { - ATRACE_CALL(); - SFT_LOGV("updateTexImage"); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - SFT_LOGE("updateTexImage: SurfaceTexture is abandoned!"); - return NO_INIT; - } - - return mEGLConsumer.updateTexImage(*this); -} - -status_t SurfaceTexture::releaseTexImage() { - // releaseTexImage can be invoked even when not attached to a GL context. - ATRACE_CALL(); - SFT_LOGV("releaseTexImage"); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - SFT_LOGE("releaseTexImage: SurfaceTexture is abandoned!"); - return NO_INIT; - } - - return mEGLConsumer.releaseTexImage(*this); -} - -status_t SurfaceTexture::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, - uint64_t maxFrameNumber) { - status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber); - if (err != NO_ERROR) { - return err; - } - - switch (mOpMode) { - case OpMode::attachedToView: - mImageConsumer.onAcquireBufferLocked(item); - break; - case OpMode::attachedToGL: - mEGLConsumer.onAcquireBufferLocked(item, *this); - break; - case OpMode::detached: - break; - } - - return NO_ERROR; -} - -status_t SurfaceTexture::releaseBufferLocked(int buf, sp<GraphicBuffer> graphicBuffer, - EGLDisplay display, EGLSyncKHR eglFence) { - // release the buffer if it hasn't already been discarded by the - // BufferQueue. This can happen, for example, when the producer of this - // buffer has reallocated the original buffer slot after this buffer - // was acquired. - status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence); - // We could be releasing an EGL/Vulkan buffer, even if not currently attached to a GL context. - mImageConsumer.onReleaseBufferLocked(buf); - mEGLConsumer.onReleaseBufferLocked(buf); - return err; -} - -status_t SurfaceTexture::detachFromContext() { - ATRACE_CALL(); - SFT_LOGV("detachFromContext"); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - SFT_LOGE("detachFromContext: abandoned SurfaceTexture"); - return NO_INIT; - } - - if (mOpMode != OpMode::attachedToGL) { - SFT_LOGE("detachFromContext: SurfaceTexture is not attached to a GL context"); - return INVALID_OPERATION; - } - - status_t err = mEGLConsumer.detachFromContext(*this); - if (err == OK) { - mOpMode = OpMode::detached; - } - - return err; -} - -status_t SurfaceTexture::attachToContext(uint32_t tex) { - ATRACE_CALL(); - SFT_LOGV("attachToContext"); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - SFT_LOGE("attachToContext: abandoned SurfaceTexture"); - return NO_INIT; - } - - if (mOpMode != OpMode::detached) { - SFT_LOGE( - "attachToContext: SurfaceTexture is already attached to a " - "context"); - return INVALID_OPERATION; - } - - if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - // release possible ImageConsumer cache - mImageConsumer.onFreeBufferLocked(mCurrentTexture); - } - - return mEGLConsumer.attachToContext(tex, *this); -} - -void SurfaceTexture::attachToView() { - ATRACE_CALL(); - Mutex::Autolock _l(mMutex); - if (mAbandoned) { - SFT_LOGE("attachToView: abandoned SurfaceTexture"); - return; - } - if (mOpMode == OpMode::detached) { - mOpMode = OpMode::attachedToView; - - if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - // release possible EGLConsumer texture cache - mEGLConsumer.onFreeBufferLocked(mCurrentTexture); - mEGLConsumer.onAbandonLocked(); - } - } else { - SFT_LOGE("attachToView: already attached"); - } -} - -void SurfaceTexture::detachFromView() { - ATRACE_CALL(); - Mutex::Autolock _l(mMutex); - - if (mAbandoned) { - SFT_LOGE("detachFromView: abandoned SurfaceTexture"); - return; - } - - if (mOpMode == OpMode::attachedToView) { - mOpMode = OpMode::detached; - // Free all EglImage and VkImage before the context is destroyed. - for (int i=0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) { - mImageConsumer.onFreeBufferLocked(i); - } - } else { - SFT_LOGE("detachFromView: not attached to View"); - } -} - -uint32_t SurfaceTexture::getCurrentTextureTarget() const { - return mTexTarget; -} - -void SurfaceTexture::getTransformMatrix(float mtx[16]) { - Mutex::Autolock lock(mMutex); - memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); -} - -void SurfaceTexture::setFilteringEnabled(bool enabled) { - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - SFT_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!"); - return; - } - bool needsRecompute = mFilteringEnabled != enabled; - mFilteringEnabled = enabled; - - if (needsRecompute && mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) { - SFT_LOGD("setFilteringEnabled called with no current item"); - } - - if (needsRecompute && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - computeCurrentTransformMatrixLocked(); - } -} - -void SurfaceTexture::computeCurrentTransformMatrixLocked() { - SFT_LOGV("computeCurrentTransformMatrixLocked"); - sp<GraphicBuffer> buf = (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) - ? nullptr - : mSlots[mCurrentTexture].mGraphicBuffer; - if (buf == nullptr) { - SFT_LOGD("computeCurrentTransformMatrixLocked: no current item"); - } - computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform, - mFilteringEnabled); -} - -void SurfaceTexture::computeTransformMatrix(float outTransform[16], const sp<GraphicBuffer>& buf, - const Rect& cropRect, uint32_t transform, - bool filtering) { - // Transform matrices - static const mat4 mtxFlipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); - static const mat4 mtxFlipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1); - static const mat4 mtxRot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); - - mat4 xform; - if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { - xform *= mtxFlipH; - } - if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { - xform *= mtxFlipV; - } - if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { - xform *= mtxRot90; - } - - if (!cropRect.isEmpty() && buf.get()) { - float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; - float bufferWidth = buf->getWidth(); - float bufferHeight = buf->getHeight(); - float shrinkAmount = 0.0f; - if (filtering) { - // In order to prevent bilinear sampling beyond the edge of the - // crop rectangle we may need to shrink it by 2 texels in each - // dimension. Normally this would just need to take 1/2 a texel - // off each end, but because the chroma channels of YUV420 images - // are subsampled we may need to shrink the crop region by a whole - // texel on each side. - switch (buf->getPixelFormat()) { - case PIXEL_FORMAT_RGBA_8888: - case PIXEL_FORMAT_RGBX_8888: - case PIXEL_FORMAT_RGBA_FP16: - case PIXEL_FORMAT_RGBA_1010102: - case PIXEL_FORMAT_RGB_888: - case PIXEL_FORMAT_RGB_565: - case PIXEL_FORMAT_BGRA_8888: - // We know there's no subsampling of any channels, so we - // only need to shrink by a half a pixel. - shrinkAmount = 0.5; - break; - - default: - // If we don't recognize the format, we must assume the - // worst case (that we care about), which is YUV420. - shrinkAmount = 1.0; - break; - } - } - - // Only shrink the dimensions that are not the size of the buffer. - if (cropRect.width() < bufferWidth) { - tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; - sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / bufferWidth; - } - if (cropRect.height() < bufferHeight) { - ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / bufferHeight; - sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / bufferHeight; - } - - mat4 crop(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1); - xform = crop * xform; - } - - // SurfaceFlinger expects the top of its window textures to be at a Y - // coordinate of 0, so SurfaceTexture must behave the same way. We don't - // want to expose this to applications, however, so we must add an - // additional vertical flip to the transform after all the other transforms. - xform = mtxFlipV * xform; - - memcpy(outTransform, xform.asArray(), sizeof(xform)); -} - -Rect SurfaceTexture::scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight) { - Rect outCrop = crop; - - uint32_t newWidth = static_cast<uint32_t>(crop.width()); - uint32_t newHeight = static_cast<uint32_t>(crop.height()); - - if (newWidth * bufferHeight > newHeight * bufferWidth) { - newWidth = newHeight * bufferWidth / bufferHeight; - ALOGV("too wide: newWidth = %d", newWidth); - } else if (newWidth * bufferHeight < newHeight * bufferWidth) { - newHeight = newWidth * bufferHeight / bufferWidth; - ALOGV("too tall: newHeight = %d", newHeight); - } - - uint32_t currentWidth = static_cast<uint32_t>(crop.width()); - uint32_t currentHeight = static_cast<uint32_t>(crop.height()); - - // The crop is too wide - if (newWidth < currentWidth) { - uint32_t dw = currentWidth - newWidth; - auto halfdw = dw / 2; - outCrop.left += halfdw; - // Not halfdw because it would subtract 1 too few when dw is odd - outCrop.right -= (dw - halfdw); - // The crop is too tall - } else if (newHeight < currentHeight) { - uint32_t dh = currentHeight - newHeight; - auto halfdh = dh / 2; - outCrop.top += halfdh; - // Not halfdh because it would subtract 1 too few when dh is odd - outCrop.bottom -= (dh - halfdh); - } - - ALOGV("getCurrentCrop final crop [%d,%d,%d,%d]", outCrop.left, outCrop.top, outCrop.right, - outCrop.bottom); - - return outCrop; -} - -nsecs_t SurfaceTexture::getTimestamp() { - SFT_LOGV("getTimestamp"); - Mutex::Autolock lock(mMutex); - return mCurrentTimestamp; -} - -android_dataspace SurfaceTexture::getCurrentDataSpace() { - SFT_LOGV("getCurrentDataSpace"); - Mutex::Autolock lock(mMutex); - return mCurrentDataSpace; -} - -uint64_t SurfaceTexture::getFrameNumber() { - SFT_LOGV("getFrameNumber"); - Mutex::Autolock lock(mMutex); - return mCurrentFrameNumber; -} - -Rect SurfaceTexture::getCurrentCrop() const { - Mutex::Autolock lock(mMutex); - return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) - ? scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight) - : mCurrentCrop; -} - -uint32_t SurfaceTexture::getCurrentTransform() const { - Mutex::Autolock lock(mMutex); - return mCurrentTransform; -} - -uint32_t SurfaceTexture::getCurrentScalingMode() const { - Mutex::Autolock lock(mMutex); - return mCurrentScalingMode; -} - -sp<Fence> SurfaceTexture::getCurrentFence() const { - Mutex::Autolock lock(mMutex); - return mCurrentFence; -} - -std::shared_ptr<FenceTime> SurfaceTexture::getCurrentFenceTime() const { - Mutex::Autolock lock(mMutex); - return mCurrentFenceTime; -} - -void SurfaceTexture::freeBufferLocked(int slotIndex) { - SFT_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); - if (slotIndex == mCurrentTexture) { - mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; - } - // The slotIndex buffer could have EGL or SkImage cache, but there is no way to tell for sure. - // Buffers can be freed after SurfaceTexture has detached from GL context or View. - mImageConsumer.onFreeBufferLocked(slotIndex); - mEGLConsumer.onFreeBufferLocked(slotIndex); - ConsumerBase::freeBufferLocked(slotIndex); -} - -void SurfaceTexture::abandonLocked() { - SFT_LOGV("abandonLocked"); - mEGLConsumer.onAbandonLocked(); - ConsumerBase::abandonLocked(); -} - -status_t SurfaceTexture::setConsumerUsageBits(uint64_t usage) { - return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS); -} - -void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const { - result.appendFormat( - "%smTexName=%d mCurrentTexture=%d\n" - "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n", - prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left, mCurrentCrop.top, - mCurrentCrop.right, mCurrentCrop.bottom, mCurrentTransform); - - ConsumerBase::dumpLocked(result, prefix); -} - -sk_sp<SkImage> SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, bool* queueEmpty, - uirenderer::RenderState& renderState) { - Mutex::Autolock _l(mMutex); - - if (mAbandoned) { - SFT_LOGE("dequeueImage: SurfaceTexture is abandoned!"); - return nullptr; - } - - if (mOpMode != OpMode::attachedToView) { - SFT_LOGE("dequeueImage: SurfaceTexture is not attached to a View"); - return nullptr; - } - - auto image = mImageConsumer.dequeueImage(queueEmpty, *this, renderState); - if (image.get()) { - uirenderer::mat4(mCurrentTransformMatrix).copyTo(transformMatrix); - } - return image; -} - -} // namespace android diff --git a/libs/hwui/surfacetexture/SurfaceTexture.h b/libs/hwui/surfacetexture/SurfaceTexture.h deleted file mode 100644 index b5d136ff3058..000000000000 --- a/libs/hwui/surfacetexture/SurfaceTexture.h +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#pragma once - -#include <gui/BufferQueueDefs.h> -#include <gui/ConsumerBase.h> - -#include <ui/FenceTime.h> -#include <ui/GraphicBuffer.h> - -#include <utils/Mutex.h> -#include <utils/String8.h> - -#include "EGLConsumer.h" -#include "ImageConsumer.h" - -namespace android { - -namespace uirenderer { -class RenderState; -} - -/* - * SurfaceTexture consumes buffers of graphics data from a BufferQueue, - * and makes them available to HWUI render thread as a SkImage and to - * an application GL render thread as an OpenGL texture. - * - * When attached to an application GL render thread, a typical usage - * pattern is to set up the SurfaceTexture with the - * desired options, and call updateTexImage() when a new frame is desired. - * If a new frame is available, the texture will be updated. If not, - * the previous contents are retained. - * - * When attached to a HWUI render thread, the TextureView implementation - * calls dequeueImage, which either pulls a new SkImage or returns the - * last cached SkImage if BufferQueue is empty. - * When attached to HWUI render thread, SurfaceTexture is compatible to - * both Vulkan and GL drawing pipelines. - */ -class ANDROID_API SurfaceTexture : public ConsumerBase { -public: - enum { TEXTURE_EXTERNAL = 0x8D65 }; // GL_TEXTURE_EXTERNAL_OES - typedef ConsumerBase::FrameAvailableListener FrameAvailableListener; - - /** - * SurfaceTexture constructs a new SurfaceTexture object. If the constructor with - * the tex parameter is used, tex indicates the name of the OpenGL ES - * texture to which images are to be streamed. texTarget specifies the - * OpenGL ES texture target to which the texture will be bound in - * updateTexImage. useFenceSync specifies whether fences should be used to - * synchronize access to buffers if that behavior is enabled at - * compile-time. - * - * A SurfaceTexture may be detached from one OpenGL ES context and then - * attached to a different context using the detachFromContext and - * attachToContext methods, respectively. The intention of these methods is - * purely to allow a SurfaceTexture to be transferred from one consumer - * context to another. If such a transfer is not needed there is no - * requirement that either of these methods be called. - * - * If the constructor with the tex parameter is used, the SurfaceTexture is - * created in a state where it is considered attached to an OpenGL ES - * context for the purposes of the attachToContext and detachFromContext - * methods. However, despite being considered "attached" to a context, the - * specific OpenGL ES context doesn't get latched until the first call to - * updateTexImage. After that point, all calls to updateTexImage must be - * made with the same OpenGL ES context current. - * - * If the constructor without the tex parameter is used, the SurfaceTexture is - * created in a detached state, and attachToContext must be called before - * calls to updateTexImage. - */ - SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t texureTarget, - bool useFenceSync, bool isControlledByApp); - - SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t texureTarget, bool useFenceSync, - bool isControlledByApp); - - /** - * updateTexImage acquires the most recently queued buffer, and sets the - * image contents of the target texture to it. - * - * This call may only be made while the OpenGL ES context to which the - * target texture belongs is bound to the calling thread. - * - * This calls doGLFenceWait to ensure proper synchronization. - */ - status_t updateTexImage(); - - /** - * releaseTexImage releases the texture acquired in updateTexImage(). - * This is intended to be used in single buffer mode. - * - * This call may only be made while the OpenGL ES context to which the - * target texture belongs is bound to the calling thread. - */ - status_t releaseTexImage(); - - /** - * getTransformMatrix retrieves the 4x4 texture coordinate transform matrix - * associated with the texture image set by the most recent call to - * updateTexImage. - * - * This transform matrix maps 2D homogeneous texture coordinates of the form - * (s, t, 0, 1) with s and t in the inclusive range [0, 1] to the texture - * coordinate that should be used to sample that location from the texture. - * Sampling the texture outside of the range of this transform is undefined. - * - * This transform is necessary to compensate for transforms that the stream - * content producer may implicitly apply to the content. By forcing users of - * a SurfaceTexture to apply this transform we avoid performing an extra - * copy of the data that would be needed to hide the transform from the - * user. - * - * The matrix is stored in column-major order so that it may be passed - * directly to OpenGL ES via the glLoadMatrixf or glUniformMatrix4fv - * functions. - */ - void getTransformMatrix(float mtx[16]); - - /** - * Computes the transform matrix documented by getTransformMatrix - * from the BufferItem sub parts. - */ - static void computeTransformMatrix(float outTransform[16], const sp<GraphicBuffer>& buf, - const Rect& cropRect, uint32_t transform, bool filtering); - - /** - * Scale the crop down horizontally or vertically such that it has the - * same aspect ratio as the buffer does. - */ - static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight); - - /** - * getTimestamp retrieves the timestamp associated with the texture image - * set by the most recent call to updateTexImage. - * - * The timestamp is in nanoseconds, and is monotonically increasing. Its - * other semantics (zero point, etc) are source-dependent and should be - * documented by the source. - */ - int64_t getTimestamp(); - - /** - * getDataSpace retrieves the DataSpace associated with the texture image - * set by the most recent call to updateTexImage. - */ - android_dataspace getCurrentDataSpace(); - - /** - * getFrameNumber retrieves the frame number associated with the texture - * image set by the most recent call to updateTexImage. - * - * The frame number is an incrementing counter set to 0 at the creation of - * the BufferQueue associated with this consumer. - */ - uint64_t getFrameNumber(); - - /** - * setDefaultBufferSize is used to set the size of buffers returned by - * requestBuffers when a with and height of zero is requested. - * A call to setDefaultBufferSize() may trigger requestBuffers() to - * be called from the client. - * The width and height parameters must be no greater than the minimum of - * GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). - * An error due to invalid dimensions might not be reported until - * updateTexImage() is called. - */ - status_t setDefaultBufferSize(uint32_t width, uint32_t height); - - /** - * setFilteringEnabled sets whether the transform matrix should be computed - * for use with bilinear filtering. - */ - void setFilteringEnabled(bool enabled); - - /** - * getCurrentTextureTarget returns the texture target of the current - * texture as returned by updateTexImage(). - */ - uint32_t getCurrentTextureTarget() const; - - /** - * getCurrentCrop returns the cropping rectangle of the current buffer. - */ - Rect getCurrentCrop() const; - - /** - * getCurrentTransform returns the transform of the current buffer. - */ - uint32_t getCurrentTransform() const; - - /** - * getCurrentScalingMode returns the scaling mode of the current buffer. - */ - uint32_t getCurrentScalingMode() const; - - /** - * getCurrentFence returns the fence indicating when the current buffer is - * ready to be read from. - */ - sp<Fence> getCurrentFence() const; - - /** - * getCurrentFence returns the FenceTime indicating when the current - * buffer is ready to be read from. - */ - std::shared_ptr<FenceTime> getCurrentFenceTime() const; - - /** - * setConsumerUsageBits overrides the ConsumerBase method to OR - * DEFAULT_USAGE_FLAGS to usage. - */ - status_t setConsumerUsageBits(uint64_t usage); - - /** - * detachFromContext detaches the SurfaceTexture from the calling thread's - * current OpenGL ES context. This context must be the same as the context - * that was current for previous calls to updateTexImage. - * - * Detaching a SurfaceTexture from an OpenGL ES context will result in the - * deletion of the OpenGL ES texture object into which the images were being - * streamed. After a SurfaceTexture has been detached from the OpenGL ES - * context calls to updateTexImage will fail returning INVALID_OPERATION - * until the SurfaceTexture is attached to a new OpenGL ES context using the - * attachToContext method. - */ - status_t detachFromContext(); - - /** - * attachToContext attaches a SurfaceTexture that is currently in the - * 'detached' state to the current OpenGL ES context. A SurfaceTexture is - * in the 'detached' state iff detachFromContext has successfully been - * called and no calls to attachToContext have succeeded since the last - * detachFromContext call. Calls to attachToContext made on a - * SurfaceTexture that is not in the 'detached' state will result in an - * INVALID_OPERATION error. - * - * The tex argument specifies the OpenGL ES texture object name in the - * new context into which the image contents will be streamed. A successful - * call to attachToContext will result in this texture object being bound to - * the texture target and populated with the image contents that were - * current at the time of the last call to detachFromContext. - */ - status_t attachToContext(uint32_t tex); - - sk_sp<SkImage> dequeueImage(SkMatrix& transformMatrix, bool* queueEmpty, - uirenderer::RenderState& renderState); - - /** - * attachToView attaches a SurfaceTexture that is currently in the - * 'detached' state to HWUI View system. - */ - void attachToView(); - - /** - * detachFromView detaches a SurfaceTexture from HWUI View system. - */ - void detachFromView(); - -protected: - /** - * abandonLocked overrides the ConsumerBase method to clear - * mCurrentTextureImage in addition to the ConsumerBase behavior. - */ - virtual void abandonLocked(); - - /** - * dumpLocked overrides the ConsumerBase method to dump SurfaceTexture- - * specific info in addition to the ConsumerBase behavior. - */ - virtual void dumpLocked(String8& result, const char* prefix) const override; - - /** - * acquireBufferLocked overrides the ConsumerBase method to update the - * mEglSlots array in addition to the ConsumerBase behavior. - */ - virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, - uint64_t maxFrameNumber = 0) override; - - /** - * releaseBufferLocked overrides the ConsumerBase method to update the - * mEglSlots array in addition to the ConsumerBase. - */ - virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer, - EGLDisplay display, EGLSyncKHR eglFence) override; - - /** - * freeBufferLocked frees up the given buffer slot. If the slot has been - * initialized this will release the reference to the GraphicBuffer in that - * slot and destroy the EGLImage in that slot. Otherwise it has no effect. - * - * This method must be called with mMutex locked. - */ - virtual void freeBufferLocked(int slotIndex); - - /** - * computeCurrentTransformMatrixLocked computes the transform matrix for the - * current texture. It uses mCurrentTransform and the current GraphicBuffer - * to compute this matrix and stores it in mCurrentTransformMatrix. - * mCurrentTextureImage must not be NULL. - */ - void computeCurrentTransformMatrixLocked(); - - /** - * The default consumer usage flags that SurfaceTexture always sets on its - * BufferQueue instance; these will be OR:d with any additional flags passed - * from the SurfaceTexture user. In particular, SurfaceTexture will always - * consume buffers as hardware textures. - */ - static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; - - /** - * mCurrentCrop is the crop rectangle that applies to the current texture. - * It gets set each time updateTexImage is called. - */ - Rect mCurrentCrop; - - /** - * mCurrentTransform is the transform identifier for the current texture. It - * gets set each time updateTexImage is called. - */ - uint32_t mCurrentTransform; - - /** - * mCurrentScalingMode is the scaling mode for the current texture. It gets - * set each time updateTexImage is called. - */ - uint32_t mCurrentScalingMode; - - /** - * mCurrentFence is the fence received from BufferQueue in updateTexImage. - */ - sp<Fence> mCurrentFence; - - /** - * The FenceTime wrapper around mCurrentFence. - */ - std::shared_ptr<FenceTime> mCurrentFenceTime{FenceTime::NO_FENCE}; - - /** - * mCurrentTransformMatrix is the transform matrix for the current texture. - * It gets computed by computeTransformMatrix each time updateTexImage is - * called. - */ - float mCurrentTransformMatrix[16]; - - /** - * mCurrentTimestamp is the timestamp for the current texture. It - * gets set each time updateTexImage is called. - */ - int64_t mCurrentTimestamp; - - /** - * mCurrentDataSpace is the dataspace for the current texture. It - * gets set each time updateTexImage is called. - */ - android_dataspace mCurrentDataSpace; - - /** - * mCurrentFrameNumber is the frame counter for the current texture. - * It gets set each time updateTexImage is called. - */ - uint64_t mCurrentFrameNumber; - - uint32_t mDefaultWidth, mDefaultHeight; - - /** - * mFilteringEnabled indicates whether the transform matrix is computed for - * use with bilinear filtering. It defaults to true and is changed by - * setFilteringEnabled(). - */ - bool mFilteringEnabled; - - /** - * mTexName is the name of the OpenGL texture to which streamed images will - * be bound when updateTexImage is called. It is set at construction time - * and can be changed with a call to attachToContext. - */ - uint32_t mTexName; - - /** - * mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync - * extension should be used to prevent buffers from being dequeued before - * it's safe for them to be written. It gets set at construction time and - * never changes. - */ - const bool mUseFenceSync; - - /** - * mTexTarget is the GL texture target with which the GL texture object is - * associated. It is set in the constructor and never changed. It is - * almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android - * Browser. In that case it is set to GL_TEXTURE_2D to allow - * glCopyTexSubImage to read from the texture. This is a hack to work - * around a GL driver limitation on the number of FBO attachments, which the - * browser's tile cache exceeds. - */ - const uint32_t mTexTarget; - - /** - * mCurrentTexture is the buffer slot index of the buffer that is currently - * bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, - * indicating that no buffer slot is currently bound to the texture. Note, - * however, that a value of INVALID_BUFFER_SLOT does not necessarily mean - * that no buffer is bound to the texture. A call to setBufferCount will - * reset mCurrentTexture to INVALID_BUFFER_SLOT. - */ - int mCurrentTexture; - - enum class OpMode { detached, attachedToView, attachedToGL }; - /** - * mOpMode indicates whether the SurfaceTexture is currently attached to - * an OpenGL ES context or the HWUI view system. For legacy reasons, this is initialized to, - * "attachedToGL" indicating that the SurfaceTexture is considered to be attached to - * whatever GL context is current at the time of the first updateTexImage call. - * It is set to "detached" by detachFromContext, and then set to "attachedToGL" again by - * attachToContext. - * attachToView/detachFromView are used to attach/detach from HWUI view system. - */ - OpMode mOpMode; - - /** - * mEGLConsumer has SurfaceTexture logic used when attached to GL context. - */ - EGLConsumer mEGLConsumer; - - /** - * mImageConsumer has SurfaceTexture logic used when attached to HWUI view system. - */ - ImageConsumer mImageConsumer; - - friend class ImageConsumer; - friend class EGLConsumer; -}; - -// ---------------------------------------------------------------------------- -} // namespace android diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp index 0a54aca4970d..06f158f25fc5 100644 --- a/libs/hwui/tests/common/TestContext.cpp +++ b/libs/hwui/tests/common/TestContext.cpp @@ -22,43 +22,50 @@ namespace android { namespace uirenderer { namespace test { -static const int IDENT_DISPLAYEVENT = 1; - -static android::DisplayInfo DUMMY_DISPLAY{ - 1080, // w - 1920, // h - 320.0, // xdpi - 320.0, // ydpi - 60.0, // fps - 2.0, // density - 0, // orientation - false, // secure? - 0, // appVsyncOffset - 0, // presentationDeadline -}; - -DisplayInfo getInternalDisplay() { -#if !HWUI_NULL_GPU - DisplayInfo display; - const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken(); - LOG_ALWAYS_FATAL_IF(token == nullptr, - "Failed to get display info because internal display is disconnected\n"); - status_t status = SurfaceComposerClient::getDisplayInfo(token, &display); - LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n"); - return display; +const DisplayInfo& getDisplayInfo() { + static DisplayInfo info = [] { + DisplayInfo info; +#if HWUI_NULL_GPU + info.density = 2.f; #else - return DUMMY_DISPLAY; + const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken(); + LOG_ALWAYS_FATAL_IF(!token, "%s: No internal display", __FUNCTION__); + + const status_t status = SurfaceComposerClient::getDisplayInfo(token, &info); + LOG_ALWAYS_FATAL_IF(status, "%s: Failed to get display info", __FUNCTION__); #endif + return info; + }(); + + return info; } -// Initialize to a dummy default -android::DisplayInfo gDisplay = DUMMY_DISPLAY; +const DisplayConfig& getActiveDisplayConfig() { + static DisplayConfig config = [] { + DisplayConfig config; +#if HWUI_NULL_GPU + config.resolution = ui::Size(1080, 1920); + config.xDpi = config.yDpi = 320.f; + config.refreshRate = 60.f; +#else + const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken(); + LOG_ALWAYS_FATAL_IF(!token, "%s: No internal display", __FUNCTION__); + + const status_t status = SurfaceComposerClient::getActiveDisplayConfig(token, &config); + LOG_ALWAYS_FATAL_IF(status, "%s: Failed to get active display config", __FUNCTION__); +#endif + return config; + }(); + + return config; +} TestContext::TestContext() { mLooper = new Looper(true); mSurfaceComposerClient = new SurfaceComposerClient(); - mLooper->addFd(mDisplayEventReceiver.getFd(), IDENT_DISPLAYEVENT, Looper::EVENT_INPUT, nullptr, - nullptr); + + constexpr int EVENT_ID = 1; + mLooper->addFd(mDisplayEventReceiver.getFd(), EVENT_ID, Looper::EVENT_INPUT, nullptr, nullptr); } TestContext::~TestContext() {} @@ -79,8 +86,10 @@ void TestContext::createSurface() { } void TestContext::createWindowSurface() { - mSurfaceControl = mSurfaceComposerClient->createSurface(String8("HwuiTest"), gDisplay.w, - gDisplay.h, PIXEL_FORMAT_RGBX_8888); + const ui::Size& resolution = getActiveDisplayResolution(); + mSurfaceControl = + mSurfaceComposerClient->createSurface(String8("HwuiTest"), resolution.getWidth(), + resolution.getHeight(), PIXEL_FORMAT_RGBX_8888); SurfaceComposerClient::Transaction t; t.setLayer(mSurfaceControl, 0x7FFFFFF).show(mSurfaceControl).apply(); @@ -94,7 +103,8 @@ void TestContext::createOffscreenSurface() { producer->setMaxDequeuedBufferCount(3); producer->setAsyncMode(true); mConsumer = new BufferItemConsumer(consumer, GRALLOC_USAGE_HW_COMPOSER, 4); - mConsumer->setDefaultBufferSize(gDisplay.w, gDisplay.h); + const ui::Size& resolution = getActiveDisplayResolution(); + mConsumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight()); mSurface = new Surface(producer); } diff --git a/libs/hwui/tests/common/TestContext.h b/libs/hwui/tests/common/TestContext.h index 116d4de8090a..a012ecb1a1d3 100644 --- a/libs/hwui/tests/common/TestContext.h +++ b/libs/hwui/tests/common/TestContext.h @@ -23,20 +23,25 @@ #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <gui/SurfaceControl.h> +#include <ui/DisplayConfig.h> #include <ui/DisplayInfo.h> #include <utils/Looper.h> #include <atomic> #include <thread> +#define dp(x) ((x) * android::uirenderer::test::getDisplayInfo().density) + namespace android { namespace uirenderer { namespace test { -extern DisplayInfo gDisplay; -#define dp(x) ((x)*android::uirenderer::test::gDisplay.density) +const DisplayInfo& getDisplayInfo(); +const DisplayConfig& getActiveDisplayConfig(); -DisplayInfo getInternalDisplay(); +inline const ui::Size& getActiveDisplayResolution() { + return getActiveDisplayConfig().resolution; +} class TestContext { public: diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index e7124df72beb..91a808df3657 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -29,6 +29,7 @@ #include <gtest/gtest.h> #include <memory> +#include <unordered_map> namespace android { namespace uirenderer { diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp index 510766073b08..c4067af388e3 100644 --- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp +++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp @@ -15,6 +15,7 @@ */ #include <SkImagePriv.h> +#include "hwui/Paint.h" #include "TestSceneBase.h" #include "tests/common/BitmapAllocationTestUtils.h" #include "utils/Color.h" @@ -43,17 +44,15 @@ public: skCanvas.drawRect(SkRect::MakeXYWH(100, 100, 100, 100), skPaint); }); - SkPaint paint; + Paint paint; sk_sp<SkImage> image = hwuiBitmap->makeImage(); sk_sp<SkShader> repeatShader = - image->makeShader(SkShader::TileMode::kRepeat_TileMode, - SkShader::TileMode::kRepeat_TileMode, nullptr); + image->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat); paint.setShader(std::move(repeatShader)); canvas.drawRoundRect(0, 0, 500, 500, 50.0f, 50.0f, paint); sk_sp<SkShader> mirrorShader = - image->makeShader(SkShader::TileMode::kMirror_TileMode, - SkShader::TileMode::kMirror_TileMode, nullptr); + image->makeShader(SkTileMode::kMirror, SkTileMode::kMirror); paint.setShader(std::move(mirrorShader)); canvas.drawRoundRect(0, 600, 500, 1100, 50.0f, 50.0f, paint); } diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp index 2af955fbb711..5886ea39acce 100644 --- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp +++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp @@ -50,7 +50,7 @@ public: pixels[4000 + 4 * i + 3] = 255; } buffer->unlock(); - sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer, kRGBA_8888_SkColorType, + sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer->toAHardwareBuffer(), SkColorSpace::MakeSRGB())); sk_sp<SkShader> hardwareShader(createBitmapShader(*hardwareBitmap)); @@ -60,12 +60,12 @@ public: colors[0] = Color::Black; colors[1] = Color::White; sk_sp<SkShader> gradientShader = SkGradientShader::MakeRadial( - center, 50, colors, nullptr, 2, SkShader::TileMode::kRepeat_TileMode); + center, 50, colors, nullptr, 2, SkTileMode::kRepeat); sk_sp<SkShader> compositeShader( - SkShader::MakeComposeShader(hardwareShader, gradientShader, SkBlendMode::kDstATop)); + SkShaders::Blend(SkBlendMode::kDstATop, hardwareShader, gradientShader)); - SkPaint paint; + Paint paint; paint.setShader(std::move(compositeShader)); canvas.drawRoundRect(0, 0, 400, 200, 10.0f, 10.0f, paint); } @@ -74,7 +74,6 @@ public: sk_sp<SkShader> createBitmapShader(Bitmap& bitmap) { sk_sp<SkImage> image = bitmap.makeImage(); - return image->makeShader(SkShader::TileMode::kClamp_TileMode, - SkShader::TileMode::kClamp_TileMode); + return image->makeShader(); } }; diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp index ecaaf487e4f8..a9449b62a1f8 100644 --- a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp @@ -44,12 +44,12 @@ class ListOfFadedTextAnimation : public TestListViewSceneBase { SkColor colors[2] = {Color::Black, Color::Transparent}; sk_sp<SkShader> s( - SkGradientShader::MakeLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode)); + SkGradientShader::MakeLinear(pts, colors, NULL, 2, SkTileMode::kClamp)); SkMatrix matrix; matrix.setScale(1, length); matrix.postRotate(-90); - SkPaint fadingPaint; + Paint fadingPaint; fadingPaint.setShader(s->makeWithLocalMatrix(matrix)); fadingPaint.setBlendMode(SkBlendMode::kDstOut); canvas.drawRect(0, 0, length, itemHeight, fadingPaint); diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp index feb881f654f8..d031923a112b 100644 --- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp @@ -53,7 +53,7 @@ class ListViewAnimation : public TestListViewSceneBase { char charToShow = 'A' + (rand() % 26); const SkPoint pos = {SkIntToScalar(size / 2), /*approximate centering*/ SkFloatToScalar(size * 0.7f)}; - canvas.drawSimpleText(&charToShow, 1, kUTF8_SkTextEncoding, pos.fX, pos.fY, font, paint); + canvas.drawSimpleText(&charToShow, 1, SkTextEncoding::kUTF8, pos.fX, pos.fY, font, paint); return bitmap; } @@ -79,7 +79,7 @@ class ListViewAnimation : public TestListViewSceneBase { static sk_sp<Bitmap> filledBox(createBoxBitmap(true)); static sk_sp<Bitmap> strokedBox(createBoxBitmap(false)); // TODO: switch to using round rect clipping, once merging correctly handles that - SkPaint roundRectPaint; + Paint roundRectPaint; roundRectPaint.setAntiAlias(true); roundRectPaint.setColor(Color::White); canvas.drawRoundRect(0, 0, itemWidth, itemHeight, dp(6), dp(6), roundRectPaint); diff --git a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp index f6cff1c643a1..f4fce277454d 100644 --- a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp +++ b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp @@ -70,7 +70,7 @@ public: magnifier->getSkBitmap(&temp); constexpr int x = 90; constexpr int y = 325; - RenderProxy::copySurfaceInto(renderTarget, x, y, x + magnifier->width(), + RenderProxy::copySurfaceInto(renderTarget.get(), x, y, x + magnifier->width(), y + magnifier->height(), &temp); } } diff --git a/libs/hwui/tests/common/scenes/OvalAnimation.cpp b/libs/hwui/tests/common/scenes/OvalAnimation.cpp index 4ff868b9d068..402c1ece2146 100644 --- a/libs/hwui/tests/common/scenes/OvalAnimation.cpp +++ b/libs/hwui/tests/common/scenes/OvalAnimation.cpp @@ -28,7 +28,7 @@ public: void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(Color::White, SkBlendMode::kSrcOver); card = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, Canvas& canvas) { - SkPaint paint; + Paint paint; paint.setAntiAlias(true); paint.setColor(Color::Black); canvas.drawOval(0, 0, 200, 200, paint); diff --git a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp index 6a3b6a57b28a..80b5cc191089 100644 --- a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp +++ b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp @@ -37,11 +37,11 @@ public: SkRegion region; for (int xOffset = 0; xOffset < 200; xOffset += 2) { for (int yOffset = 0; yOffset < 200; yOffset += 2) { - region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op); + region.op({xOffset, yOffset, xOffset + 1, yOffset + 1}, SkRegion::kUnion_Op); } } - SkPaint paint; + Paint paint; paint.setColor(0xff00ffff); canvas.drawRegion(region, paint); }); diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp index 02dd42ff2ae8..97bfba34c790 100644 --- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp @@ -45,7 +45,7 @@ public: canvas.save(SaveFlags::MatrixClip); canvas.translate(0, 400); canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::Flags(0)); // unclipped - SkPaint paint; + Paint paint; paint.setAntiAlias(true); paint.setColor(Color::Green_700); canvas.drawCircle(200, 200, 200, paint); diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp index d189a9379c33..70a1557dcf6a 100644 --- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp @@ -30,14 +30,14 @@ public: void createContent(int width, int height, Canvas& canvas) override { card = TestUtils::createNode( 0, 0, width, height, [width](RenderProperties& props, Canvas& canvas) { - std::function<void(Canvas&, float, const SkPaint&)> ops[] = { - [](Canvas& canvas, float size, const SkPaint& paint) { + std::function<void(Canvas&, float, const Paint&)> ops[] = { + [](Canvas& canvas, float size, const Paint& paint) { canvas.drawArc(0, 0, size, size, 50, 189, true, paint); }, - [](Canvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const Paint& paint) { canvas.drawOval(0, 0, size, size, paint); }, - [](Canvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const Paint& paint) { SkPath diamondPath; diamondPath.moveTo(size / 2, 0); diamondPath.lineTo(size, size / 2); @@ -46,18 +46,18 @@ public: diamondPath.close(); canvas.drawPath(diamondPath, paint); }, - [](Canvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const Paint& paint) { float data[] = {0, 0, size, size, 0, size, size, 0}; canvas.drawLines(data, sizeof(data) / sizeof(float), paint); }, - [](Canvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const Paint& paint) { float data[] = {0, 0, size, size, 0, size, size, 0}; canvas.drawPoints(data, sizeof(data) / sizeof(float), paint); }, - [](Canvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const Paint& paint) { canvas.drawRect(0, 0, size, size, paint); }, - [](Canvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const Paint& paint) { float rad = size / 4; canvas.drawRoundRect(0, 0, size, size, rad, rad, paint); }}; @@ -66,7 +66,7 @@ public: // each combination of strokeWidth + style gets a column int outerCount = canvas.save(SaveFlags::MatrixClip); - SkPaint paint; + Paint paint; paint.setAntiAlias(true); SkPaint::Style styles[] = {SkPaint::kStroke_Style, SkPaint::kFill_Style, SkPaint::kStrokeAndFill_Style}; diff --git a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp index ff0cb3705cb8..a0bc5aa245d5 100644 --- a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp @@ -52,21 +52,13 @@ private: return TestUtils::createNode( x, y, x + width, y + height, [width, height](RenderProperties& props, Canvas& canvas) { - SkPaint paint; - float matrix[20] = {0}; - + Paint paint; // Simple scale/translate case where R, G, and B are all treated equivalently - matrix[SkColorMatrix::kR_Scale] = 1.1f; - matrix[SkColorMatrix::kG_Scale] = 1.1f; - matrix[SkColorMatrix::kB_Scale] = 1.1f; - matrix[SkColorMatrix::kA_Scale] = 0.5f; - - matrix[SkColorMatrix::kR_Trans] = 5.0f; - matrix[SkColorMatrix::kG_Trans] = 5.0f; - matrix[SkColorMatrix::kB_Trans] = 5.0f; - matrix[SkColorMatrix::kA_Trans] = 10.0f; + SkColorMatrix cm; + cm.setScale(1.1f, 1.1f, 1.1f, 0.5f); + cm.postTranslate(5.0f/255, 5.0f/255, 5.0f/255, 10.0f/255); - paint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(matrix)); + paint.setColorFilter(SkColorFilters::Matrix(cm)); // set a shader so it's not likely for the matrix to be optimized away (since a // clever @@ -75,7 +67,7 @@ private: SkPoint pts[] = {SkPoint::Make(0, 0), SkPoint::Make(width, height)}; SkColor colors[2] = {Color::DeepPurple_500, Color::DeepOrange_500}; paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 2, - SkShader::kClamp_TileMode)); + SkTileMode::kClamp)); // overdraw several times to emphasize shader cost for (int i = 0; i < 10; i++) { diff --git a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp index 016c65c17c4c..57a260c8d234 100644 --- a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp @@ -51,13 +51,13 @@ private: [width, height](RenderProperties& props, Canvas& canvas) { float pos[] = {0, 1}; SkPoint pts[] = {SkPoint::Make(0, 0), SkPoint::Make(width, height)}; - SkPaint paint; + Paint paint; // overdraw several times to emphasize shader cost for (int i = 0; i < 10; i++) { // use i%2 start position to pick 2 color combo with black in it SkColor colors[3] = {Color::Transparent, Color::Black, Color::Cyan_500}; paint.setShader(SkGradientShader::MakeLinear(pts, colors + (i % 2), pos, 2, - SkShader::kClamp_TileMode)); + SkTileMode::kClamp)); canvas.drawRect(i, i, width, height, paint); } }); diff --git a/libs/hwui/tests/common/scenes/TestSceneBase.h b/libs/hwui/tests/common/scenes/TestSceneBase.h index 6f76a502ae3e..24d35857c60d 100644 --- a/libs/hwui/tests/common/scenes/TestSceneBase.h +++ b/libs/hwui/tests/common/scenes/TestSceneBase.h @@ -17,6 +17,7 @@ #pragma once #include "hwui/Canvas.h" +#include "hwui/Paint.h" #include "RenderNode.h" #include "tests/common/TestContext.h" #include "tests/common/TestScene.h" diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp index 229c7f392629..bac887053d2f 100644 --- a/libs/hwui/tests/common/scenes/TvApp.cpp +++ b/libs/hwui/tests/common/scenes/TvApp.cpp @@ -217,9 +217,9 @@ private: std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas( image->stagingProperties().getWidth(), image->stagingProperties().getHeight(), image.get())); - SkPaint paint; + Paint paint; sk_sp<SkColorFilter> filter( - SkColorFilter::MakeModeFilter((curFrame % 150) << 24, SkBlendMode::kSrcATop)); + SkColorFilters::Blend((curFrame % 150) << 24, SkBlendMode::kSrcATop)); paint.setColorFilter(filter); sk_sp<Bitmap> bitmap = mCachedBitmaps[ci]; canvas->drawBitmap(*bitmap, 0, 0, &paint); diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp index 9c845f04e820..801cb7d9e8c5 100644 --- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp +++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp @@ -109,16 +109,14 @@ void outputBenchmarkReport(const TestScene::Info& info, const TestScene::Options void run(const TestScene::Info& info, const TestScene::Options& opts, benchmark::BenchmarkReporter* reporter) { - // Switch to the real display - gDisplay = getInternalDisplay(); - Properties::forceDrawFrame = true; TestContext testContext; testContext.setRenderOffscreen(opts.renderOffscreen); // create the native surface - const int width = gDisplay.w; - const int height = gDisplay.h; + const ui::Size& resolution = getActiveDisplayResolution(); + const int width = resolution.getWidth(); + const int height = resolution.getHeight(); sp<Surface> surface = testContext.surface(); std::unique_ptr<TestScene> scene(info.createScene(opts)); @@ -133,7 +131,7 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, ContextFactory factory; std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode.get(), &factory)); proxy->loadSystemProperties(); - proxy->setSurface(surface); + proxy->setSurface(surface.get()); float lightX = width / 2.0; proxy->setLightAlpha(255 * 0.075, 255 * 0.15); proxy->setLightGeometry((Vector3){lightX, dp(-200.0f), dp(800.0f)}, dp(800.0f)); @@ -146,7 +144,7 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, } for (int i = 0; i < warmupFrameCount; i++) { testContext.waitForVsync(); - nsecs_t vsync = systemTime(CLOCK_MONOTONIC); + nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC); UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync); proxy->syncAndDrawFrame(); } @@ -161,10 +159,10 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, ModifiedMovingAverage<double> avgMs(opts.reportFrametimeWeight); - nsecs_t start = systemTime(CLOCK_MONOTONIC); + nsecs_t start = systemTime(SYSTEM_TIME_MONOTONIC); for (int i = 0; i < opts.count; i++) { testContext.waitForVsync(); - nsecs_t vsync = systemTime(CLOCK_MONOTONIC); + nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC); { ATRACE_NAME("UI-Draw Frame"); UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync); @@ -173,7 +171,7 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, } if (opts.reportFrametimeWeight) { proxy->fence(); - nsecs_t done = systemTime(CLOCK_MONOTONIC); + nsecs_t done = systemTime(SYSTEM_TIME_MONOTONIC); avgMs.add((done - vsync) / 1000000.0); if (i % 10 == 9) { printf("Average frametime %.3fms\n", avgMs.average()); @@ -181,7 +179,7 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, } } proxy->fence(); - nsecs_t end = systemTime(CLOCK_MONOTONIC); + nsecs_t end = systemTime(SYSTEM_TIME_MONOTONIC); if (reporter) { outputBenchmarkReport(info, opts, reporter, proxy.get(), (end - start) / (double)s2ns(1)); diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp index 70423a70157b..4ce6c32470ea 100644 --- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp +++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp @@ -18,6 +18,7 @@ #include "DisplayList.h" #include "hwui/Canvas.h" +#include "hwui/Paint.h" #include "pipeline/skia/SkiaDisplayList.h" #include "tests/common/TestUtils.h" @@ -93,7 +94,7 @@ void BM_DisplayListCanvas_record_simpleBitmapView(benchmark::State& benchState) std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); delete canvas->finishRecording(); - SkPaint rectPaint; + Paint rectPaint; sk_sp<Bitmap> iconBitmap(TestUtils::createBitmap(80, 80)); while (benchState.KeepRunning()) { diff --git a/libs/hwui/tests/microbench/main.cpp b/libs/hwui/tests/microbench/main.cpp index b5abf5bc5efa..9c4d25968d60 100644 --- a/libs/hwui/tests/microbench/main.cpp +++ b/libs/hwui/tests/microbench/main.cpp @@ -14,9 +14,6 @@ * limitations under the License. */ -#include "debug/GlesDriver.h" -#include "debug/NullGlesDriver.h" - #include "hwui/Typeface.h" #include <benchmark/benchmark.h> @@ -24,10 +21,8 @@ #include <memory> using namespace android; -using namespace android::uirenderer; int main(int argc, char** argv) { - debug::GlesDriver::replace(std::make_unique<debug::NullGlesDriver>()); benchmark::Initialize(&argc, argv); Typeface::setRobotoTypefaceForTest(); benchmark::RunSpecifiedBenchmarks(); diff --git a/libs/hwui/tests/scripts/skp-capture.sh b/libs/hwui/tests/scripts/skp-capture.sh index 54fa22929586..4b46fbf86818 100755 --- a/libs/hwui/tests/scripts/skp-capture.sh +++ b/libs/hwui/tests/scripts/skp-capture.sh @@ -4,6 +4,12 @@ # # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# +# Before this can be used, the device must be rooted and the filesystem must be writable by Skia +# - These steps are necessary once after flashing to enable capture - +# adb root +# adb remount +# adb reboot if [ -z "$1" ]; then printf 'Usage:\n skp-capture.sh PACKAGE_NAME OPTIONAL_FRAME_COUNT\n\n' @@ -20,20 +26,27 @@ if ! command -v adb > /dev/null 2>&1; then exit 2 fi fi -phase1_timeout_seconds=15 -phase2_timeout_seconds=60 +phase1_timeout_seconds=60 +phase2_timeout_seconds=300 package="$1" -filename="$(date '+%H%M%S').skp" +extension="skp" +if (( "$2" > 1 )); then # 2nd arg is number of frames + extension="mskp" # use different extension for multi frame files. +fi +filename="$(date '+%H%M%S').${extension}" remote_path="/data/data/${package}/cache/${filename}" local_path_prefix="$(date '+%Y-%m-%d_%H%M%S')_${package}" -local_path="${local_path_prefix}.skp" +local_path="${local_path_prefix}.${extension}" enable_capture_key='debug.hwui.capture_skp_enabled' enable_capture_value=$(adb shell "getprop '${enable_capture_key}'") -#printf 'captureflag=' "$enable_capture_value" '\n' + +# TODO(nifong): check if filesystem is writable here with "avbctl get-verity" +# result will either start with "verity is disabled" or "verity is enabled" + if [ -z "$enable_capture_value" ]; then - printf 'Capture SKP property need to be enabled first. Please use\n' - printf "\"adb shell setprop debug.hwui.capture_skp_enabled true\" and then restart\n" - printf "the process.\n\n" + printf 'debug.hwui.capture_skp_enabled was found to be disabled, enabling it now.\n' + printf " restart the process you want to capture on the device, then retry this script.\n\n" + adb shell "setprop '${enable_capture_key}' true" exit 1 fi if [ ! -z "$2" ]; then @@ -57,12 +70,18 @@ banner() { printf ' %s' "$*" printf '\n=====================\n' } -banner '...WAITING...' -adb_test_exist() { - test '0' = "$(adb shell "test -e \"$1\"; echo \$?")"; +banner '...WAITING FOR APP INTERACTION...' +# Waiting for nonzero file is an indication that the pipeline has both opened the file and written +# the header. With multiple frames this does not occur until the last frame has been recorded, +# so we continue to show the "waiting for app interaction" message as long as the app still requires +# interaction to draw more frames. +adb_test_file_nonzero() { + # grab first byte of `du` output + X="$(adb shell "du \"$1\" 2> /dev/null | dd bs=1 count=1 2> /dev/null")" + test "$X" && test "$X" -ne 0 } timeout=$(( $(date +%s) + $phase1_timeout_seconds)) -while ! adb_test_exist "$remote_path"; do +while ! adb_test_file_nonzero "$remote_path"; do spin 0.05 if [ $(date +%s) -gt $timeout ] ; then printf '\bTimed out.\n' @@ -72,20 +91,27 @@ while ! adb_test_exist "$remote_path"; do done printf '\b' -#read -n1 -r -p "Press any key to continue..." key +# Disable further capturing +adb shell "setprop '${filename_key}' ''" banner '...SAVING...' -adb_test_file_nonzero() { - # grab first byte of `du` output - X="$(adb shell "du \"$1\" 2> /dev/null | dd bs=1 count=1 2> /dev/null")" - test "$X" && test "$X" -ne 0 +# return the size of a file in bytes +adb_filesize() { + adb shell "wc -c \"$1\"" 2> /dev/null | awk '{print $1}' } -#adb_filesize() { -# adb shell "wc -c \"$1\"" 2> /dev/null | awk '{print $1}' -#} timeout=$(( $(date +%s) + $phase2_timeout_seconds)) -while ! adb_test_file_nonzero "$remote_path"; do +last_size='0' # output of last size check command +unstable=true # false once the file size stops changing +counter=0 # used to perform size check only 1/sec though we update spinner 20/sec +# loop until the file size is unchanged for 1 second. +while [ $unstable != 0 ] ; do spin 0.05 + counter=$(( $counter+1 )) + if ! (( $counter % 20)) ; then + new_size=$(adb_filesize "$remote_path") + unstable=$(($(adb_filesize "$remote_path") != last_size)) + last_size=$new_size + fi if [ $(date +%s) -gt $timeout ] ; then printf '\bTimed out.\n' adb shell "setprop '${filename_key}' ''" @@ -94,7 +120,7 @@ while ! adb_test_file_nonzero "$remote_path"; do done printf '\b' -adb shell "setprop '${filename_key}' ''" +printf "SKP file serialized: %s\n" $(echo $last_size | numfmt --to=iec) i=0; while [ $i -lt 10 ]; do spin 0.10; i=$(($i + 1)); done; echo @@ -105,12 +131,4 @@ if ! [ -f "$local_path" ] ; then fi adb shell rm "$remote_path" printf '\nSKP saved to file:\n %s\n\n' "$local_path" -if [ ! -z "$2" ]; then - bridge="_" - adb shell "setprop 'debug.hwui.capture_skp_frames' ''" - for i in $(seq 2 $2); do - adb pull "${remote_path}_${i}" "${local_path_prefix}_${i}.skp" - adb shell rm "${remote_path}_${i}" - done -fi diff --git a/libs/hwui/tests/unit/ABitmapTests.cpp b/libs/hwui/tests/unit/ABitmapTests.cpp new file mode 100644 index 000000000000..8e2f7e09d406 --- /dev/null +++ b/libs/hwui/tests/unit/ABitmapTests.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2020 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. + */ + +#include <gtest/gtest.h> + +#include "android/graphics/bitmap.h" +#include "apex/TypeCast.h" +#include "hwui/Bitmap.h" +#include "tests/common/TestUtils.h" + +using namespace android; +using namespace android::uirenderer; + +TEST(ABitmap, notifyPixelsChanged) { + // generate a bitmap and its public API handle + sk_sp<Bitmap> bitmap(TestUtils::createBitmap(1, 1)); + ABitmap* abmp = android::TypeCast::toABitmap(bitmap.get()); + + // verify that notification changes the genID + uint32_t genID = bitmap->getGenerationID(); + ABitmap_notifyPixelsChanged(abmp); + ASSERT_TRUE(bitmap->getGenerationID() != genID); + + // mark the bitmap as immutable + ASSERT_FALSE(bitmap->isImmutable()); + bitmap->setImmutable(); + ASSERT_TRUE(bitmap->isImmutable()); + + // attempt to notify that the pixels have changed + genID = bitmap->getGenerationID(); + ABitmap_notifyPixelsChanged(abmp); + ASSERT_TRUE(bitmap->getGenerationID() == genID); +} diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp index 3f1ef93c878c..c83a3c88cbdd 100644 --- a/libs/hwui/tests/unit/CacheManagerTests.cpp +++ b/libs/hwui/tests/unit/CacheManagerTests.cpp @@ -33,7 +33,8 @@ static size_t getCacheUsage(GrContext* grContext) { } RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) { - DisplayInfo displayInfo = DeviceInfo::get()->displayInfo(); + int32_t width = DeviceInfo::get()->getWidth(); + int32_t height = DeviceInfo::get()->getHeight(); GrContext* grContext = renderThread.getGrContext(); ASSERT_TRUE(grContext != nullptr); @@ -42,7 +43,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) { std::vector<sk_sp<SkSurface>> surfaces; while (getCacheUsage(grContext) <= renderThread.cacheManager().getBackgroundCacheSize()) { - SkImageInfo info = SkImageInfo::MakeA8(displayInfo.w, displayInfo.h); + SkImageInfo info = SkImageInfo::MakeA8(width, height); sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(grContext, SkBudgeted::kYes, info); surface->getCanvas()->drawColor(SK_AlphaTRANSPARENT); @@ -52,8 +53,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) { } // create an image and pin it so that we have something with a unique key in the cache - sk_sp<Bitmap> bitmap = - Bitmap::allocateHeapBitmap(SkImageInfo::MakeA8(displayInfo.w, displayInfo.h)); + sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(SkImageInfo::MakeA8(width, height)); sk_sp<SkImage> image = bitmap->makeImage(); ASSERT_TRUE(SkImage_pinAsTexture(image.get(), grContext)); diff --git a/libs/hwui/tests/unit/FatVectorTests.cpp b/libs/hwui/tests/unit/FatVectorTests.cpp index 8523e6c9e973..6585a6249b44 100644 --- a/libs/hwui/tests/unit/FatVectorTests.cpp +++ b/libs/hwui/tests/unit/FatVectorTests.cpp @@ -15,7 +15,7 @@ */ #include <gtest/gtest.h> -#include <utils/FatVector.h> +#include <ui/FatVector.h> #include <tests/common/TestUtils.h> diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp deleted file mode 100644 index dac888cd79ca..000000000000 --- a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#include <GpuMemoryTracker.h> -#include <gtest/gtest.h> - -#include "renderthread/EglManager.h" -#include "renderthread/RenderThread.h" -#include "tests/common/TestUtils.h" - -#include <utils/StrongPointer.h> - -using namespace android; -using namespace android::uirenderer; -using namespace android::uirenderer::renderthread; - -class TestGPUObject : public GpuMemoryTracker { -public: - TestGPUObject() : GpuMemoryTracker(GpuObjectType::Texture) {} - - void changeSize(int newSize) { notifySizeChanged(newSize); } -}; - -// Other tests may have created a renderthread and EGL context. -// This will destroy the EGLContext on RenderThread if it exists so that the -// current thread can spoof being a GPU thread -static void destroyEglContext() { - if (TestUtils::isRenderThreadRunning()) { - TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyRenderingContext(); }); - } -} - -TEST(GpuMemoryTracker, sizeCheck) { - destroyEglContext(); - - GpuMemoryTracker::onGpuContextCreated(); - ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); - ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture)); - { - TestGPUObject myObj; - ASSERT_EQ(1, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture)); - myObj.changeSize(500); - ASSERT_EQ(500, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); - myObj.changeSize(1000); - ASSERT_EQ(1000, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); - myObj.changeSize(300); - ASSERT_EQ(300, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); - } - ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); - ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture)); - GpuMemoryTracker::onGpuContextDestroyed(); -} diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index c813cd945905..3632be06c45f 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -24,6 +24,7 @@ #include "DamageAccumulator.h" #include "FatalTestCanvas.h" #include "IContextFactory.h" +#include "hwui/Paint.h" #include "RecordingCanvas.h" #include "SkiaCanvas.h" #include "pipeline/skia/SkiaDisplayList.h" @@ -59,7 +60,7 @@ TEST(RenderNodeDrawable, create) { namespace { static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) { - SkPaint paint; + Paint paint; // order put in blue channel, transparent so overlapped content doesn't get rejected paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder)); canvas->drawRect(0, 0, 100, 100, paint); @@ -211,7 +212,7 @@ TEST(RenderNodeDrawable, saveLayerClipAndMatrixRestore) { ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder)); EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity()); - SkPaint paint; + Paint paint; paint.setAntiAlias(true); paint.setColor(SK_ColorGREEN); recorder.drawRect(0.0f, 400.0f, 400.0f, 800.0f, paint); @@ -291,7 +292,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) { properties.setTranslationX(SCROLL_X); properties.setTranslationY(SCROLL_Y); - SkPaint paint; + Paint paint; paint.setColor(SK_ColorWHITE); canvas.drawRect(0, 0, 100, 100, paint); }, @@ -302,7 +303,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) { [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { properties.setProjectBackwards(true); properties.setClipToBounds(false); - SkPaint paint; + Paint paint; paint.setColor(SK_ColorDKGRAY); canvas.drawRect(-10, -10, 60, 60, paint); }, @@ -310,7 +311,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) { auto child = TestUtils::createSkiaNode( 0, 50, 100, 100, [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) { - SkPaint paint; + Paint paint; paint.setColor(SK_ColorBLUE); canvas.drawRect(0, 0, 100, 50, paint); canvas.drawRenderNode(projectingRipple.get()); @@ -375,14 +376,14 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) { [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { properties.setProjectBackwards(true); properties.setClipToBounds(false); - SkPaint paint; + Paint paint; canvas.drawRect(0, 0, 100, 100, paint); }, "P"); auto child = TestUtils::createSkiaNode( 0, 0, 100, 100, [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) { - SkPaint paint; + Paint paint; canvas.drawRect(0, 0, 100, 100, paint); canvas.drawRenderNode(projectingRipple.get()); }, @@ -483,7 +484,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) { properties.setTranslationX(SCROLL_X); properties.setTranslationY(SCROLL_Y); - canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint()); + canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, Paint()); }, "B"); // B auto projectingRipple = TestUtils::createSkiaNode( @@ -491,14 +492,14 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) { [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { properties.setProjectBackwards(true); properties.setClipToBounds(false); - canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds + canvas.drawOval(100, 100, 300, 300, Paint()); // drawn mostly out of layer bounds }, "R"); // R auto child = TestUtils::createSkiaNode( 100, 100, 300, 300, [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) { canvas.drawRenderNode(projectingRipple.get()); - canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, SkPaint()); + canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, Paint()); }, "C"); // C auto parent = TestUtils::createSkiaNode( @@ -578,7 +579,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) { 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { properties.setProjectionReceiver(true); - canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint()); + canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, Paint()); }, "B"); // B auto projectingRipple = TestUtils::createSkiaNode( @@ -591,7 +592,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) { properties.setTranslationY(SCROLL_Y); properties.setProjectBackwards(true); properties.setClipToBounds(false); - canvas.drawOval(0, 0, 200, 200, SkPaint()); + canvas.drawOval(0, 0, 200, 200, Paint()); }, "R"); // R auto child = TestUtils::createSkiaNode( @@ -946,7 +947,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, simple) { [](RenderProperties& props, SkiaRecordingCanvas& canvas) { sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25)); canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, - SkPaint()); + Paint()); canvas.drawBitmap(*bitmap, 10, 10, nullptr); }); @@ -1022,7 +1023,7 @@ TEST(RenderNodeDrawable, renderNode) { auto child = TestUtils::createSkiaNode( 10, 10, 110, 110, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - SkPaint paint; + Paint paint; paint.setColor(SK_ColorWHITE); canvas.drawRect(0, 0, 100, 100, paint); }); @@ -1030,7 +1031,7 @@ TEST(RenderNodeDrawable, renderNode) { auto parent = TestUtils::createSkiaNode( 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, [&child](RenderProperties& props, SkiaRecordingCanvas& canvas) { - SkPaint paint; + Paint paint; paint.setColor(SK_ColorDKGRAY); canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint); @@ -1065,7 +1066,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) { auto layerNode = TestUtils::createSkiaNode( 0, 0, LAYER_WIDTH, LAYER_HEIGHT, [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { - canvas.drawPaint(SkPaint()); + canvas.drawPaint(Paint()); }); layerNode->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer); @@ -1096,10 +1097,8 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) { int getDrawCounter() { return mDrawCounter; } virtual void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override { - // expect to draw 2 RenderNodeDrawable, 1 StartReorderBarrierDrawable, - // 1 EndReorderBarrierDrawable - mDrawCounter++; - SkCanvas::onDrawDrawable(drawable, matrix); + // Do not expect this to be called. See RecordingCanvas.cpp DrawDrawable for context. + EXPECT_TRUE(false); } virtual void didTranslate(SkScalar dx, SkScalar dy) override { @@ -1159,8 +1158,8 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) { // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection ShadowTestCanvas canvas(CANVAS_WIDTH, CANVAS_HEIGHT); RenderNodeDrawable drawable(parent.get(), &canvas, false); - canvas.drawDrawable(&drawable); - EXPECT_EQ(9, canvas.getDrawCounter()); + drawable.draw(&canvas); + EXPECT_EQ(5, canvas.getDrawCounter()); } // Draw a vector drawable twice but with different bounds and verify correct bounds are used. diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp index df5f45618070..7951537e1525 100644 --- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp +++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp @@ -48,14 +48,14 @@ TEST(SkiaBehavior, lightingColorFilter_simplify) { SkColor observedColor; SkBlendMode observedMode; - ASSERT_TRUE(filter->asColorMode(&observedColor, &observedMode)); + ASSERT_TRUE(filter->asAColorMode(&observedColor, &observedMode)); EXPECT_EQ(0xFF223344, observedColor); EXPECT_EQ(SkBlendMode::kModulate, observedMode); } { sk_sp<SkColorFilter> failFilter(SkColorMatrixFilter::MakeLightingFilter(0x11223344, 0x1)); - EXPECT_FALSE(failFilter->asColorMode(nullptr, nullptr)); + EXPECT_FALSE(failFilter->asAColorMode(nullptr, nullptr)); } } diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp index f6178aff0c2e..fcc64fdd0be6 100644 --- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp +++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp @@ -16,6 +16,7 @@ #include "tests/common/TestUtils.h" +#include <hwui/Paint.h> #include <SkBlurDrawLooper.h> #include <SkCanvasStateUtils.h> #include <SkPicture.h> @@ -32,7 +33,7 @@ TEST(SkiaCanvas, drawShadowLayer) { // clear to white canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrc); - SkPaint paint; + Paint paint; // it is transparent to ensure that we still draw the rect since it has a looper paint.setColor(SK_ColorTRANSPARENT); // this is how view's shadow layers are implemented @@ -78,7 +79,7 @@ TEST(SkiaCanvas, colorSpaceXform) { sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture(); // Playback to a software sRGB canvas. The result should be fully red. - canvas.asSkCanvas()->drawPicture(picture); + canvas.drawPicture(*picture); ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0)); } diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp index 6fb164a99ae4..d08aea668b2a 100644 --- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp +++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp @@ -208,10 +208,9 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscr test::TestContext testContext; testContext.setRenderOffscreen(true); auto surface = testContext.surface(); - int width, height; - surface->query(NATIVE_WINDOW_WIDTH, &width); - surface->query(NATIVE_WINDOW_HEIGHT, &height); - canvasContext->setSurface(std::move(surface)); + int width = ANativeWindow_getWidth(surface.get()); + int height = ANativeWindow_getHeight(surface.get()); + canvasContext->setSurface(surface.get()); TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get()); DamageAccumulator damageAccumulator; diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index a671bdada09a..e7a889d08cfd 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -23,12 +23,14 @@ #include "AnimationContext.h" #include "DamageAccumulator.h" #include "IContextFactory.h" +#include "hwui/Paint.h" #include "SkiaCanvas.h" #include "pipeline/skia/SkiaDisplayList.h" #include "pipeline/skia/SkiaOpenGLPipeline.h" #include "pipeline/skia/SkiaRecordingCanvas.h" #include "pipeline/skia/SkiaUtils.h" #include "renderthread/CanvasContext.h" +#include "tests/common/TestContext.h" #include "tests/common/TestUtils.h" #include <gui/BufferItemConsumer.h> @@ -59,43 +61,10 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) { ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, testOnPrepareTree) { - auto redNode = TestUtils::createSkiaNode( - 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { - redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); - }); - - LayerUpdateQueue layerUpdateQueue; - SkRect dirty = SkRectMakeLargest(); - std::vector<sp<RenderNode>> renderNodes; - renderNodes.push_back(redNode); - bool opaque = true; - android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1); - auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); - { - // add a pointer to a deleted vector drawable object in the pipeline - sp<VectorDrawableRoot> dirtyVD(new VectorDrawableRoot(new VectorDrawable::Group())); - dirtyVD->mutateProperties()->setScaledSize(5, 5); - pipeline->getVectorDrawables()->push_back(dirtyVD.get()); - } - - // pipeline should clean list of dirty vector drawables before prepare tree - pipeline->onPrepareTree(); - - auto surface = SkSurface::MakeRasterN32Premul(1, 1); - surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); - ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); - - // drawFrame will crash if "SkiaPipeline::onPrepareTree" did not clean invalid VD pointer - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, - SkMatrix::I()); - ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED); -} - RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) { auto halfGreenNode = TestUtils::createSkiaNode( 0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) { - SkPaint greenPaint; + Paint greenPaint; greenPaint.setColor(SK_ColorGREEN); greenPaint.setStyle(SkPaint::kFill_Style); bottomHalfGreenCanvas.drawRect(0, 1, 2, 2, greenPaint); @@ -293,7 +262,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) { }; std::vector<sp<RenderNode>> nodes; - SkPaint transparentPaint; + Paint transparentPaint; transparentPaint.setAlpha(128); // backdrop @@ -424,21 +393,50 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) { EXPECT_EQ(1, surface->canvas()->mDrawCounter); } -static sp<Surface> createDummySurface() { - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&producer, &consumer); - producer->setMaxDequeuedBufferCount(1); - producer->setAsyncMode(true); - return new Surface(producer); -} - RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) { - auto surface = createDummySurface(); + test::TestContext context; + auto surface = context.surface(); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); EXPECT_FALSE(pipeline->isSurfaceReady()); - EXPECT_TRUE(pipeline->setSurface(surface.get(), SwapBehavior::kSwap_default, ColorMode::SRGB, 0)); + EXPECT_TRUE(pipeline->setSurface(surface.get(), SwapBehavior::kSwap_default)); EXPECT_TRUE(pipeline->isSurfaceReady()); renderThread.destroyRenderingContext(); EXPECT_FALSE(pipeline->isSurfaceReady()); } + +RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) { + // create a pipeline and add a picture callback + auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); + int callbackCount = 0; + pipeline->setPictureCapturedCallback( + [&callbackCount](sk_sp<SkPicture>&& picture) { callbackCount += 1; }); + + // create basic red frame and render it + auto redNode = TestUtils::createSkiaNode( + 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { + redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); + }); + LayerUpdateQueue layerUpdateQueue; + SkRect dirty = SkRectMakeLargest(); + std::vector<sp<RenderNode>> renderNodes; + renderNodes.push_back(redNode); + bool opaque = true; + android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1); + auto surface = SkSurface::MakeRasterN32Premul(1, 1); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); + + // verify the callback was called + EXPECT_EQ(1, callbackCount); + + // render a second frame and check the callback count + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); + EXPECT_EQ(2, callbackCount); + + // unset the callback, render another frame, check callback was not invoked + pipeline->setPictureCapturedCallback(nullptr); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); + EXPECT_EQ(2, callbackCount); +} diff --git a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp index 635429dea359..eec25c6bd40d 100644 --- a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp +++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp @@ -24,6 +24,7 @@ #include "DamageAccumulator.h" #include "FatalTestCanvas.h" #include "IContextFactory.h" +#include "hwui/Paint.h" #include "SkiaCanvas.h" #include "pipeline/skia/SkiaDisplayList.h" #include "pipeline/skia/SkiaPipeline.h" @@ -60,7 +61,7 @@ static void testProperty(std::function<void(RenderProperties&)> propSetupCallbac 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, [propSetupCallback](RenderProperties& props, SkiaRecordingCanvas& canvas) { propSetupCallback(props); - SkPaint paint; + Paint paint; paint.setColor(SK_ColorWHITE); canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint); }); diff --git a/libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp b/libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp deleted file mode 100644 index 0c95fdd42851..000000000000 --- a/libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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. - */ - -#include <gtest/gtest.h> - -#include <GrRectanizer.h> -#include "pipeline/skia/VectorDrawableAtlas.h" -#include "tests/common/TestUtils.h" - -using namespace android; -using namespace android::uirenderer; -using namespace android::uirenderer::renderthread; -using namespace android::uirenderer::skiapipeline; - -RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, addGetRemove) { - VectorDrawableAtlas atlas(100 * 100); - atlas.prepareForDraw(renderThread.getGrContext()); - // create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects) - const int MAX_RECTS = 150; - AtlasEntry VDRects[MAX_RECTS]; - - sk_sp<SkSurface> atlasSurface; - - // check we are able to allocate new rects - // check that rects in the atlas do not intersect - for (uint32_t i = 0; i < MAX_RECTS; i++) { - VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext()); - if (0 == i) { - atlasSurface = VDRects[0].surface; - } - ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY); - ASSERT_TRUE(VDRects[i].surface.get() != nullptr); - ASSERT_TRUE(VDRects[i].rect.width() == 10 && VDRects[i].rect.height() == 10); - - // nothing in the atlas should intersect - if (atlasSurface.get() == VDRects[i].surface.get()) { - for (uint32_t j = 0; j < i; j++) { - if (atlasSurface.get() == VDRects[j].surface.get()) { - ASSERT_FALSE(VDRects[i].rect.intersect(VDRects[j].rect)); - } - } - } - } - - // first 1/3 rects should all be in the same surface - for (uint32_t i = 1; i < MAX_RECTS / 3; i++) { - ASSERT_NE(VDRects[i].key, VDRects[0].key); - ASSERT_EQ(VDRects[i].surface.get(), atlasSurface.get()); - } - - // first rect is using atlas and last is a standalone surface - ASSERT_NE(VDRects[0].surface.get(), VDRects[MAX_RECTS - 1].surface.get()); - - // check getEntry returns the same surfaces that we had created - for (uint32_t i = 0; i < MAX_RECTS; i++) { - auto VDRect = atlas.getEntry(VDRects[i].key); - ASSERT_TRUE(VDRect.key != INVALID_ATLAS_KEY); - ASSERT_EQ(VDRects[i].key, VDRect.key); - ASSERT_EQ(VDRects[i].surface.get(), VDRect.surface.get()); - ASSERT_EQ(VDRects[i].rect, VDRect.rect); - atlas.releaseEntry(VDRect.key); - } - - // check that any new rects will be allocated in the atlas, even that rectanizer is full. - // rects in the atlas should not intersect. - for (uint32_t i = 0; i < MAX_RECTS / 3; i++) { - VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext()); - ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY); - ASSERT_EQ(VDRects[i].surface.get(), atlasSurface.get()); - ASSERT_TRUE(VDRects[i].rect.width() == 10 && VDRects[i].rect.height() == 10); - for (uint32_t j = 0; j < i; j++) { - ASSERT_FALSE(VDRects[i].rect.intersect(VDRects[j].rect)); - } - } -} - -RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, disallowSharedSurface) { - VectorDrawableAtlas atlas(100 * 100); - // don't allow to use a shared surface - atlas.setStorageMode(VectorDrawableAtlas::StorageMode::disallowSharedSurface); - atlas.prepareForDraw(renderThread.getGrContext()); - // create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects) - const int MAX_RECTS = 150; - AtlasEntry VDRects[MAX_RECTS]; - - // check we are able to allocate new rects - // check that rects in the atlas use unique surfaces - for (uint32_t i = 0; i < MAX_RECTS; i++) { - VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext()); - ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY); - ASSERT_TRUE(VDRects[i].surface.get() != nullptr); - ASSERT_TRUE(VDRects[i].rect.width() == 10 && VDRects[i].rect.height() == 10); - - // nothing in the atlas should use the same surface - for (uint32_t j = 0; j < i; j++) { - ASSERT_NE(VDRects[i].surface.get(), VDRects[j].surface.get()); - } - } -} - -RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, repack) { - VectorDrawableAtlas atlas(100 * 100); - ASSERT_FALSE(atlas.isFragmented()); - atlas.prepareForDraw(renderThread.getGrContext()); - ASSERT_FALSE(atlas.isFragmented()); - // create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects) - const int MAX_RECTS = 150; - AtlasEntry VDRects[MAX_RECTS]; - - sk_sp<SkSurface> atlasSurface; - - // fill the atlas with check we are able to allocate new rects - for (uint32_t i = 0; i < MAX_RECTS; i++) { - VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext()); - if (0 == i) { - atlasSurface = VDRects[0].surface; - } - ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY); - } - - ASSERT_FALSE(atlas.isFragmented()); - - // first 1/3 rects should all be in the same surface - for (uint32_t i = 1; i < MAX_RECTS / 3; i++) { - ASSERT_NE(VDRects[i].key, VDRects[0].key); - ASSERT_EQ(VDRects[i].surface.get(), atlasSurface.get()); - } - - // release all entries - for (uint32_t i = 0; i < MAX_RECTS; i++) { - auto VDRect = atlas.getEntry(VDRects[i].key); - ASSERT_TRUE(VDRect.key != INVALID_ATLAS_KEY); - atlas.releaseEntry(VDRect.key); - } - - ASSERT_FALSE(atlas.isFragmented()); - - // allocate 4x4 rects, which will fragment the atlas badly, because each entry occupies a 10x10 - // area - for (uint32_t i = 0; i < 4 * MAX_RECTS; i++) { - AtlasEntry entry = atlas.requestNewEntry(4, 4, renderThread.getGrContext()); - ASSERT_TRUE(entry.key != INVALID_ATLAS_KEY); - } - - ASSERT_TRUE(atlas.isFragmented()); - - atlas.repackIfNeeded(renderThread.getGrContext()); - - ASSERT_FALSE(atlas.isFragmented()); -}
\ No newline at end of file diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp index 5db002862fcd..6d4c57413f00 100644 --- a/libs/hwui/tests/unit/VectorDrawableTests.cpp +++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp @@ -85,9 +85,9 @@ const static TestData sTestDataSet[] = { outPath->rCubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0); outPath->cubicTo(16.0, 16.0, 9.0, 9.0, 9.0, 9.0); outPath->rCubicTo(0.0, 0.0, 9.0, 9.0, 9.0, 9.0); - outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 10.0, + outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPathDirection::kCW, 10.0, 10.0); - outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 20.0, + outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPathDirection::kCW, 20.0, 20.0); }}, @@ -159,7 +159,7 @@ const static TestData sTestDataSet[] = { }, [](SkPath* outPath) { outPath->moveTo(300.0, 70.0); - outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCCW_Direction, + outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPathDirection::kCCW, 301.0, 70.0); outPath->close(); outPath->moveTo(300.0, 70.0); @@ -395,7 +395,7 @@ TEST(VectorDrawable, drawPathWithoutIncrementingShaderRefCount) { bitmap.allocN32Pixels(5, 5, false); SkCanvas canvas(bitmap); - sk_sp<SkShader> shader = SkShader::MakeColorShader(SK_ColorBLACK); + sk_sp<SkShader> shader = SkShaders::Color(SK_ColorBLACK); // Initial ref count is 1 EXPECT_TRUE(shader->unique()); diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp index 83d888c310f0..402cb5814366 100644 --- a/libs/hwui/tests/unit/main.cpp +++ b/libs/hwui/tests/unit/main.cpp @@ -18,8 +18,6 @@ #include "gtest/gtest.h" #include "Properties.h" -#include "debug/GlesDriver.h" -#include "debug/NullGlesDriver.h" #include "hwui/Typeface.h" #include "tests/common/LeakChecker.h" @@ -65,7 +63,6 @@ int main(int argc, char* argv[]) { } // Replace the default GLES driver - debug::GlesDriver::replace(std::make_unique<debug::NullGlesDriver>()); Properties::isolatedProcess = true; // Run the tests diff --git a/libs/hwui/thread/WorkQueue.h b/libs/hwui/thread/WorkQueue.h index 42f8503fd000..46b8bc07b432 100644 --- a/libs/hwui/thread/WorkQueue.h +++ b/libs/hwui/thread/WorkQueue.h @@ -31,7 +31,7 @@ namespace android::uirenderer { struct MonotonicClock { - static nsecs_t now() { return systemTime(CLOCK_MONOTONIC); } + static nsecs_t now() { return systemTime(SYSTEM_TIME_MONOTONIC); } }; class WorkQueue { diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp index cc7725b7b9de..71a27ced2e09 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -19,12 +19,61 @@ #include <utils/Log.h> #include <ui/ColorSpace.h> +#ifdef __ANDROID__ // Layoutlib does not support hardware buffers or native windows +#include <android/hardware_buffer.h> +#include <android/native_window.h> +#endif + #include <algorithm> #include <cmath> namespace android { namespace uirenderer { +#ifdef __ANDROID__ // Layoutlib does not support hardware buffers or native windows +static inline SkImageInfo createImageInfo(int32_t width, int32_t height, int32_t format, + sk_sp<SkColorSpace> colorSpace) { + SkColorType colorType = kUnknown_SkColorType; + SkAlphaType alphaType = kOpaque_SkAlphaType; + switch (format) { + case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: + colorType = kN32_SkColorType; + alphaType = kPremul_SkAlphaType; + break; + case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM: + colorType = kN32_SkColorType; + alphaType = kOpaque_SkAlphaType; + break; + case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM: + colorType = kRGB_565_SkColorType; + alphaType = kOpaque_SkAlphaType; + break; + case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM: + colorType = kRGBA_1010102_SkColorType; + alphaType = kPremul_SkAlphaType; + break; + case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT: + colorType = kRGBA_F16_SkColorType; + alphaType = kPremul_SkAlphaType; + break; + default: + ALOGV("Unsupported format: %d, return unknown by default", format); + break; + } + return SkImageInfo::Make(width, height, colorType, alphaType, colorSpace); +} + +SkImageInfo ANativeWindowToImageInfo(const ANativeWindow_Buffer& buffer, + sk_sp<SkColorSpace> colorSpace) { + return createImageInfo(buffer.width, buffer.height, buffer.format, colorSpace); +} + +SkImageInfo BufferDescriptionToImageInfo(const AHardwareBuffer_Desc& bufferDesc, + sk_sp<SkColorSpace> colorSpace) { + return createImageInfo(bufferDesc.width, bufferDesc.height, bufferDesc.format, colorSpace); +} +#endif + android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType) { switch (colorType) { case kRGBA_8888_SkColorType: @@ -59,10 +108,109 @@ SkColorType PixelFormatToColorType(android::PixelFormat format) { } } +namespace { +static constexpr skcms_TransferFunction k2Dot6 = {2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; + +// Skia's SkNamedGamut::kDCIP3 is based on a white point of D65. This gamut +// matches the white point used by ColorSpace.Named.DCIP3. +static constexpr skcms_Matrix3x3 kDCIP3 = {{ + {0.486143, 0.323835, 0.154234}, + {0.226676, 0.710327, 0.0629966}, + {0.000800549, 0.0432385, 0.78275}, +}}; + +static bool nearlyEqual(float a, float b) { + // By trial and error, this is close enough to match for the ADataSpaces we + // compare for. + return ::fabs(a - b) < .002f; +} + +static bool nearlyEqual(const skcms_TransferFunction& x, const skcms_TransferFunction& y) { + return nearlyEqual(x.g, y.g) + && nearlyEqual(x.a, y.a) + && nearlyEqual(x.b, y.b) + && nearlyEqual(x.c, y.c) + && nearlyEqual(x.d, y.d) + && nearlyEqual(x.e, y.e) + && nearlyEqual(x.f, y.f); +} + +static bool nearlyEqual(const skcms_Matrix3x3& x, const skcms_Matrix3x3& y) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (!nearlyEqual(x.vals[i][j], y.vals[i][j])) return false; + } + } + return true; +} + +} // anonymous namespace + +android_dataspace ColorSpaceToADataSpace(SkColorSpace* colorSpace, SkColorType colorType) { + if (!colorSpace) { + return HAL_DATASPACE_UNKNOWN; + } + + if (colorSpace->isSRGB()) { + if (colorType == kRGBA_F16_SkColorType) { + return HAL_DATASPACE_V0_SCRGB; + } + return HAL_DATASPACE_V0_SRGB; + } + + skcms_TransferFunction fn; + LOG_ALWAYS_FATAL_IF(!colorSpace->isNumericalTransferFn(&fn)); + + skcms_Matrix3x3 gamut; + LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&gamut)); + + if (nearlyEqual(gamut, SkNamedGamut::kSRGB)) { + if (nearlyEqual(fn, SkNamedTransferFn::kLinear)) { + // Skia doesn't differentiate amongst the RANGES. In Java, we associate + // LINEAR_EXTENDED_SRGB with F16, and LINEAR_SRGB with other Configs. + // Make the same association here. + if (colorType == kRGBA_F16_SkColorType) { + return HAL_DATASPACE_V0_SCRGB_LINEAR; + } + return HAL_DATASPACE_V0_SRGB_LINEAR; + } + + if (nearlyEqual(fn, SkNamedTransferFn::kRec2020)) { + return HAL_DATASPACE_V0_BT709; + } + } + + if (nearlyEqual(fn, SkNamedTransferFn::kSRGB) && nearlyEqual(gamut, SkNamedGamut::kDCIP3)) { + return HAL_DATASPACE_DISPLAY_P3; + } + + if (nearlyEqual(fn, SkNamedTransferFn::k2Dot2) && nearlyEqual(gamut, SkNamedGamut::kAdobeRGB)) { + return HAL_DATASPACE_ADOBE_RGB; + } + + if (nearlyEqual(fn, SkNamedTransferFn::kRec2020) && + nearlyEqual(gamut, SkNamedGamut::kRec2020)) { + return HAL_DATASPACE_BT2020; + } + + if (nearlyEqual(fn, k2Dot6) && nearlyEqual(gamut, kDCIP3)) { + return HAL_DATASPACE_DCI_P3; + } + + return HAL_DATASPACE_UNKNOWN; +} + sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) { if (dataspace == HAL_DATASPACE_UNKNOWN) { return SkColorSpace::MakeSRGB(); } + if (dataspace == HAL_DATASPACE_DCI_P3) { + // This cannot be handled by the switch statements below because it + // needs to use the locally-defined kDCIP3 gamut, rather than the one in + // Skia (SkNamedGamut), which is used for other data spaces with + // HAL_DATASPACE_STANDARD_DCI_P3 (e.g. HAL_DATASPACE_DISPLAY_P3). + return SkColorSpace::MakeRGB(k2Dot6, kDCIP3); + } skcms_Matrix3x3 gamut; switch (dataspace & HAL_DATASPACE_STANDARD_MASK) { @@ -100,13 +248,15 @@ sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) { case HAL_DATASPACE_TRANSFER_GAMMA2_2: return SkColorSpace::MakeRGB({2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut); case HAL_DATASPACE_TRANSFER_GAMMA2_6: - return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut); + return SkColorSpace::MakeRGB(k2Dot6, gamut); case HAL_DATASPACE_TRANSFER_GAMMA2_8: return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut); + case HAL_DATASPACE_TRANSFER_ST2084: + return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut); + case HAL_DATASPACE_TRANSFER_SMPTE_170M: + return SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, gamut); case HAL_DATASPACE_TRANSFER_UNSPECIFIED: return nullptr; - case HAL_DATASPACE_TRANSFER_SMPTE_170M: - case HAL_DATASPACE_TRANSFER_ST2084: case HAL_DATASPACE_TRANSFER_HLG: default: ALOGV("Unsupported Gamma: %d", dataspace); diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h index 79400de08ee0..a76f7e499c37 100644 --- a/libs/hwui/utils/Color.h +++ b/libs/hwui/utils/Color.h @@ -25,6 +25,9 @@ #include <SkColorSpace.h> #include <SkImageInfo.h> +struct ANativeWindow_Buffer; +struct AHardwareBuffer_Desc; + namespace android { namespace uirenderer { namespace Color { @@ -89,11 +92,35 @@ static constexpr float EOCF_sRGB(float srgb) { return srgb <= 0.04045f ? srgb / 12.92f : powf((srgb + 0.055f) / 1.055f, 2.4f); } +#ifdef __ANDROID__ // Layoutlib does not support hardware buffers or native windows +ANDROID_API SkImageInfo ANativeWindowToImageInfo(const ANativeWindow_Buffer& buffer, + sk_sp<SkColorSpace> colorSpace); + +SkImageInfo BufferDescriptionToImageInfo(const AHardwareBuffer_Desc& bufferDesc, + sk_sp<SkColorSpace> colorSpace); +#endif + android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType); ANDROID_API SkColorType PixelFormatToColorType(android::PixelFormat format); ANDROID_API sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace); +/** + * Return the android_dataspace corresponding to colorSpace. + * + * Note: This currently only returns android_dataspaces with corresponding + * ADataSpaces. The NDK relies on this, so if you need to update it to return + * an android_dataspace *without* an ADataSpace, the NDK methods need to be + * updated. + * + * @param colorSpace May be null, in which case this will return + * HAL_DATASPACE_UNKNOWN. + * @param colorType Some SkColorSpaces are associated with more than one + * android_dataspace. In that case, the SkColorType is used to + * determine which one to return. + */ +ANDROID_API android_dataspace ColorSpaceToADataSpace(SkColorSpace*, SkColorType); + struct Lab { float L; float a; diff --git a/libs/hwui/utils/FatVector.h b/libs/hwui/utils/FatVector.h deleted file mode 100644 index 8cc4d1010ab6..000000000000 --- a/libs/hwui/utils/FatVector.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2015, The Android Open Source Project - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ANDROID_FAT_VECTOR_H -#define ANDROID_FAT_VECTOR_H - -#include "utils/Macros.h" - -#include <stddef.h> -#include <stdlib.h> -#include <utils/Log.h> -#include <type_traits> - -#include <vector> - -namespace android { -namespace uirenderer { - -template <typename T, size_t SIZE> -class InlineStdAllocator { -public: - struct Allocation { - PREVENT_COPY_AND_ASSIGN(Allocation); - - public: - Allocation(){}; - // char array instead of T array, so memory is uninitialized, with no destructors run - char array[sizeof(T) * SIZE]; - bool inUse = false; - }; - - typedef T value_type; // needed to implement std::allocator - typedef T* pointer; // needed to implement std::allocator - - explicit InlineStdAllocator(Allocation& allocation) : mAllocation(allocation) {} - InlineStdAllocator(const InlineStdAllocator& other) : mAllocation(other.mAllocation) {} - ~InlineStdAllocator() {} - - T* allocate(size_t num, const void* = 0) { - if (!mAllocation.inUse && num <= SIZE) { - mAllocation.inUse = true; - return (T*)mAllocation.array; - } else { - return (T*)malloc(num * sizeof(T)); - } - } - - void deallocate(pointer p, size_t num) { - if (p == (T*)mAllocation.array) { - mAllocation.inUse = false; - } else { - // 'free' instead of delete here - destruction handled separately - free(p); - } - } - Allocation& mAllocation; -}; - -/** - * std::vector with SIZE elements preallocated into an internal buffer. - * - * Useful for avoiding the cost of malloc in cases where only SIZE or - * fewer elements are needed in the common case. - */ -template <typename T, size_t SIZE> -class FatVector : public std::vector<T, InlineStdAllocator<T, SIZE>> { -public: - FatVector() - : std::vector<T, InlineStdAllocator<T, SIZE>>( - InlineStdAllocator<T, SIZE>(mAllocation)) { - this->reserve(SIZE); - } - - explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); } - -private: - typename InlineStdAllocator<T, SIZE>::Allocation mAllocation; -}; - -} // namespace uirenderer -} // namespace android - -#endif // ANDROID_FAT_VECTOR_H diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp index c694e93f7e21..61897627d842 100644 --- a/libs/hwui/utils/GLUtils.cpp +++ b/libs/hwui/utils/GLUtils.cpp @@ -21,19 +21,10 @@ #include "GLUtils.h" -#if DEBUG_OPENGL >= DEBUG_LEVEL_HIGH && !defined(HWUI_GLES_WRAP_ENABLED) -#error Setting DEBUG_OPENGL to HIGH requires setting HWUI_ENABLE_OPENGL_VALIDATION to true in the Android.mk! -#endif - namespace android { namespace uirenderer { bool GLUtils::dumpGLErrors() { -#if DEBUG_OPENGL >= DEBUG_LEVEL_HIGH - // If DEBUG_LEVEL_HIGH is set then every GLES call is already wrapped - // and asserts that there was no error. So this can just return success. - return false; -#else bool errorObserved = false; GLenum status = GL_NO_ERROR; while ((status = glGetError()) != GL_NO_ERROR) { @@ -56,7 +47,6 @@ bool GLUtils::dumpGLErrors() { } } return errorObserved; -#endif } const char* GLUtils::getGLFramebufferError() { diff --git a/libs/hwui/utils/HostColorSpace.cpp b/libs/hwui/utils/HostColorSpace.cpp new file mode 100644 index 000000000000..77a6820c6999 --- /dev/null +++ b/libs/hwui/utils/HostColorSpace.cpp @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2019 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. + */ +// This is copied from framework/native/libs/ui in order not to include libui in host build + +#include <ui/ColorSpace.h> + +using namespace std::placeholders; + +namespace android { + +static constexpr float linearResponse(float v) { + return v; +} + +static constexpr float rcpResponse(float x, const ColorSpace::TransferParameters& p) { + return x >= p.d * p.c ? (std::pow(x, 1.0f / p.g) - p.b) / p.a : x / p.c; +} + +static constexpr float response(float x, const ColorSpace::TransferParameters& p) { + return x >= p.d ? std::pow(p.a * x + p.b, p.g) : p.c * x; +} + +static constexpr float rcpFullResponse(float x, const ColorSpace::TransferParameters& p) { + return x >= p.d * p.c ? (std::pow(x - p.e, 1.0f / p.g) - p.b) / p.a : (x - p.f) / p.c; +} + +static constexpr float fullResponse(float x, const ColorSpace::TransferParameters& p) { + return x >= p.d ? std::pow(p.a * x + p.b, p.g) + p.e : p.c * x + p.f; +} + +static float absRcpResponse(float x, float g,float a, float b, float c, float d) { + float xx = std::abs(x); + return std::copysign(xx >= d * c ? (std::pow(xx, 1.0f / g) - b) / a : xx / c, x); +} + +static float absResponse(float x, float g, float a, float b, float c, float d) { + float xx = std::abs(x); + return std::copysign(xx >= d ? std::pow(a * xx + b, g) : c * xx, x); +} + +static float safePow(float x, float e) { + return powf(x < 0.0f ? 0.0f : x, e); +} + +static ColorSpace::transfer_function toOETF(const ColorSpace::TransferParameters& parameters) { + if (parameters.e == 0.0f && parameters.f == 0.0f) { + return std::bind(rcpResponse, _1, parameters); + } + return std::bind(rcpFullResponse, _1, parameters); +} + +static ColorSpace::transfer_function toEOTF( const ColorSpace::TransferParameters& parameters) { + if (parameters.e == 0.0f && parameters.f == 0.0f) { + return std::bind(response, _1, parameters); + } + return std::bind(fullResponse, _1, parameters); +} + +static ColorSpace::transfer_function toOETF(float gamma) { + if (gamma == 1.0f) { + return linearResponse; + } + return std::bind(safePow, _1, 1.0f / gamma); +} + +static ColorSpace::transfer_function toEOTF(float gamma) { + if (gamma == 1.0f) { + return linearResponse; + } + return std::bind(safePow, _1, gamma); +} + +static constexpr std::array<float2, 3> computePrimaries(const mat3& rgbToXYZ) { + float3 r(rgbToXYZ * float3{1, 0, 0}); + float3 g(rgbToXYZ * float3{0, 1, 0}); + float3 b(rgbToXYZ * float3{0, 0, 1}); + + return {{r.xy / dot(r, float3{1}), + g.xy / dot(g, float3{1}), + b.xy / dot(b, float3{1})}}; +} + +static constexpr float2 computeWhitePoint(const mat3& rgbToXYZ) { + float3 w(rgbToXYZ * float3{1}); + return w.xy / dot(w, float3{1}); +} + +ColorSpace::ColorSpace( + const std::string& name, + const mat3& rgbToXYZ, + transfer_function OETF, + transfer_function EOTF, + clamping_function clamper) noexcept + : mName(name) + , mRGBtoXYZ(rgbToXYZ) + , mXYZtoRGB(inverse(rgbToXYZ)) + , mOETF(std::move(OETF)) + , mEOTF(std::move(EOTF)) + , mClamper(std::move(clamper)) + , mPrimaries(computePrimaries(rgbToXYZ)) + , mWhitePoint(computeWhitePoint(rgbToXYZ)) { +} + +ColorSpace::ColorSpace( + const std::string& name, + const mat3& rgbToXYZ, + const TransferParameters parameters, + clamping_function clamper) noexcept + : mName(name) + , mRGBtoXYZ(rgbToXYZ) + , mXYZtoRGB(inverse(rgbToXYZ)) + , mParameters(parameters) + , mOETF(toOETF(mParameters)) + , mEOTF(toEOTF(mParameters)) + , mClamper(std::move(clamper)) + , mPrimaries(computePrimaries(rgbToXYZ)) + , mWhitePoint(computeWhitePoint(rgbToXYZ)) { +} + +ColorSpace::ColorSpace( + const std::string& name, + const mat3& rgbToXYZ, + float gamma, + clamping_function clamper) noexcept + : mName(name) + , mRGBtoXYZ(rgbToXYZ) + , mXYZtoRGB(inverse(rgbToXYZ)) + , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}) + , mOETF(toOETF(gamma)) + , mEOTF(toEOTF(gamma)) + , mClamper(std::move(clamper)) + , mPrimaries(computePrimaries(rgbToXYZ)) + , mWhitePoint(computeWhitePoint(rgbToXYZ)) { +} + +ColorSpace::ColorSpace( + const std::string& name, + const std::array<float2, 3>& primaries, + const float2& whitePoint, + transfer_function OETF, + transfer_function EOTF, + clamping_function clamper) noexcept + : mName(name) + , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint)) + , mXYZtoRGB(inverse(mRGBtoXYZ)) + , mOETF(std::move(OETF)) + , mEOTF(std::move(EOTF)) + , mClamper(std::move(clamper)) + , mPrimaries(primaries) + , mWhitePoint(whitePoint) { +} + +ColorSpace::ColorSpace( + const std::string& name, + const std::array<float2, 3>& primaries, + const float2& whitePoint, + const TransferParameters parameters, + clamping_function clamper) noexcept + : mName(name) + , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint)) + , mXYZtoRGB(inverse(mRGBtoXYZ)) + , mParameters(parameters) + , mOETF(toOETF(mParameters)) + , mEOTF(toEOTF(mParameters)) + , mClamper(std::move(clamper)) + , mPrimaries(primaries) + , mWhitePoint(whitePoint) { +} + +ColorSpace::ColorSpace( + const std::string& name, + const std::array<float2, 3>& primaries, + const float2& whitePoint, + float gamma, + clamping_function clamper) noexcept + : mName(name) + , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint)) + , mXYZtoRGB(inverse(mRGBtoXYZ)) + , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}) + , mOETF(toOETF(gamma)) + , mEOTF(toEOTF(gamma)) + , mClamper(std::move(clamper)) + , mPrimaries(primaries) + , mWhitePoint(whitePoint) { +} + +constexpr mat3 ColorSpace::computeXYZMatrix( + const std::array<float2, 3>& primaries, const float2& whitePoint) { + const float2& R = primaries[0]; + const float2& G = primaries[1]; + const float2& B = primaries[2]; + const float2& W = whitePoint; + + float oneRxRy = (1 - R.x) / R.y; + float oneGxGy = (1 - G.x) / G.y; + float oneBxBy = (1 - B.x) / B.y; + float oneWxWy = (1 - W.x) / W.y; + + float RxRy = R.x / R.y; + float GxGy = G.x / G.y; + float BxBy = B.x / B.y; + float WxWy = W.x / W.y; + + float BY = + ((oneWxWy - oneRxRy) * (GxGy - RxRy) - (WxWy - RxRy) * (oneGxGy - oneRxRy)) / + ((oneBxBy - oneRxRy) * (GxGy - RxRy) - (BxBy - RxRy) * (oneGxGy - oneRxRy)); + float GY = (WxWy - RxRy - BY * (BxBy - RxRy)) / (GxGy - RxRy); + float RY = 1 - GY - BY; + + float RYRy = RY / R.y; + float GYGy = GY / G.y; + float BYBy = BY / B.y; + + return { + float3{RYRy * R.x, RY, RYRy * (1 - R.x - R.y)}, + float3{GYGy * G.x, GY, GYGy * (1 - G.x - G.y)}, + float3{BYBy * B.x, BY, BYBy * (1 - B.x - B.y)} + }; +} + +const ColorSpace ColorSpace::sRGB() { + return { + "sRGB IEC61966-2.1", + {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f}, + {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f, 0.0f, 0.0f} + }; +} + +const ColorSpace ColorSpace::linearSRGB() { + return { + "sRGB IEC61966-2.1 (Linear)", + {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f} + }; +} + +const ColorSpace ColorSpace::extendedSRGB() { + return { + "scRGB-nl IEC 61966-2-2:2003", + {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f}, + std::bind(absRcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f), + std::bind(absResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f), + std::bind(clamp<float>, _1, -0.799f, 2.399f) + }; +} + +const ColorSpace ColorSpace::linearExtendedSRGB() { + return { + "scRGB IEC 61966-2-2:2003", + {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f}, + 1.0f, + std::bind(clamp<float>, _1, -0.5f, 7.499f) + }; +} + +const ColorSpace ColorSpace::NTSC() { + return { + "NTSC (1953)", + {{float2{0.67f, 0.33f}, {0.21f, 0.71f}, {0.14f, 0.08f}}}, + {0.310f, 0.316f}, + {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} + }; +} + +const ColorSpace ColorSpace::BT709() { + return { + "Rec. ITU-R BT.709-5", + {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f}, + {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} + }; +} + +const ColorSpace ColorSpace::BT2020() { + return { + "Rec. ITU-R BT.2020-1", + {{float2{0.708f, 0.292f}, {0.170f, 0.797f}, {0.131f, 0.046f}}}, + {0.3127f, 0.3290f}, + {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} + }; +} + +const ColorSpace ColorSpace::AdobeRGB() { + return { + "Adobe RGB (1998)", + {{float2{0.64f, 0.33f}, {0.21f, 0.71f}, {0.15f, 0.06f}}}, + {0.3127f, 0.3290f}, + 2.2f + }; +} + +const ColorSpace ColorSpace::ProPhotoRGB() { + return { + "ROMM RGB ISO 22028-2:2013", + {{float2{0.7347f, 0.2653f}, {0.1596f, 0.8404f}, {0.0366f, 0.0001f}}}, + {0.34567f, 0.35850f}, + {1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f, 0.0f, 0.0f} + }; +} + +const ColorSpace ColorSpace::DisplayP3() { + return { + "Display P3", + {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}}, + {0.3127f, 0.3290f}, + {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f, 0.0f, 0.0f} + }; +} + +const ColorSpace ColorSpace::DCIP3() { + return { + "SMPTE RP 431-2-2007 DCI (P3)", + {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}}, + {0.314f, 0.351f}, + 2.6f + }; +} + +const ColorSpace ColorSpace::ACES() { + return { + "SMPTE ST 2065-1:2012 ACES", + {{float2{0.73470f, 0.26530f}, {0.0f, 1.0f}, {0.00010f, -0.0770f}}}, + {0.32168f, 0.33767f}, + 1.0f, + std::bind(clamp<float>, _1, -65504.0f, 65504.0f) + }; +} + +const ColorSpace ColorSpace::ACEScg() { + return { + "Academy S-2014-004 ACEScg", + {{float2{0.713f, 0.293f}, {0.165f, 0.830f}, {0.128f, 0.044f}}}, + {0.32168f, 0.33767f}, + 1.0f, + std::bind(clamp<float>, _1, -65504.0f, 65504.0f) + }; +} + +std::unique_ptr<float3[]> ColorSpace::createLUT(uint32_t size, const ColorSpace& src, + const ColorSpace& dst) { + size = clamp(size, 2u, 256u); + float m = 1.0f / float(size - 1); + + std::unique_ptr<float3[]> lut(new float3[size * size * size]); + float3* data = lut.get(); + + ColorSpaceConnector connector(src, dst); + + for (uint32_t z = 0; z < size; z++) { + for (int32_t y = int32_t(size - 1); y >= 0; y--) { + for (uint32_t x = 0; x < size; x++) { + *data++ = connector.transform({x * m, y * m, z * m}); + } + } + } + + return lut; +} + +static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f}; +static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f}; +static const mat3 BRADFORD = mat3{ + float3{ 0.8951f, -0.7502f, 0.0389f}, + float3{ 0.2664f, 1.7135f, -0.0685f}, + float3{-0.1614f, 0.0367f, 1.0296f} +}; + +static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) { + float3 srcLMS = matrix * srcWhitePoint; + float3 dstLMS = matrix * dstWhitePoint; + return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix; +} + +ColorSpaceConnector::ColorSpaceConnector( + const ColorSpace& src, + const ColorSpace& dst) noexcept + : mSource(src) + , mDestination(dst) { + + if (all(lessThan(abs(src.getWhitePoint() - dst.getWhitePoint()), float2{1e-3f}))) { + mTransform = dst.getXYZtoRGB() * src.getRGBtoXYZ(); + } else { + mat3 rgbToXYZ(src.getRGBtoXYZ()); + mat3 xyzToRGB(dst.getXYZtoRGB()); + + float3 srcXYZ = ColorSpace::XYZ(float3{src.getWhitePoint(), 1}); + float3 dstXYZ = ColorSpace::XYZ(float3{dst.getWhitePoint(), 1}); + + if (any(greaterThan(abs(src.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) { + rgbToXYZ = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * src.getRGBtoXYZ(); + } + + if (any(greaterThan(abs(dst.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) { + xyzToRGB = inverse(adaptation(BRADFORD, dstXYZ, ILLUMINANT_D50_XYZ) * dst.getRGBtoXYZ()); + } + + mTransform = xyzToRGB * rgbToXYZ; + } +} + +}; // namespace android diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h index 9c4a1be4b269..539e6544ef02 100644 --- a/libs/hwui/utils/LinearAllocator.h +++ b/libs/hwui/utils/LinearAllocator.h @@ -115,6 +115,7 @@ public: * wasted) */ size_t usedSize() const { return mTotalAllocated - mWastedSpace; } + size_t allocatedSize() const { return mTotalAllocated; } private: LinearAllocator(const LinearAllocator& other); diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h index ebf2343c5518..e2fdf2fcb5a5 100644 --- a/libs/hwui/utils/PaintUtils.h +++ b/libs/hwui/utils/PaintUtils.h @@ -67,29 +67,6 @@ public: return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0; } - struct TextShadow { - SkScalar radius; - float dx; - float dy; - SkColor color; - }; - - static inline bool getTextShadow(const SkPaint* paint, TextShadow* textShadow) { - SkDrawLooper::BlurShadowRec blur; - if (paint && paint->getLooper() && paint->getLooper()->asABlurShadow(&blur)) { - if (textShadow) { - textShadow->radius = Blur::convertSigmaToRadius(blur.fSigma); - textShadow->dx = blur.fOffset.fX; - textShadow->dy = blur.fOffset.fY; - textShadow->color = blur.fColor; - } - return true; - } - return false; - } - - static inline bool hasTextShadow(const SkPaint* paint) { return getTextShadow(paint, nullptr); } - static inline SkBlendMode getBlendModeDirect(const SkPaint* paint) { return paint ? paint->getBlendMode() : SkBlendMode::kSrcOver; } diff --git a/libs/hwui/utils/TraceUtils.h b/libs/hwui/utils/TraceUtils.h index 1869d00396c0..e61b4be1784e 100644 --- a/libs/hwui/utils/TraceUtils.h +++ b/libs/hwui/utils/TraceUtils.h @@ -16,6 +16,7 @@ #ifndef TRACE_UTILS_H #define TRACE_UTILS_H +#include <cutils/trace.h> #include <utils/Trace.h> #define ATRACE_FORMAT(fmt, ...) \ diff --git a/libs/hwui/utils/VectorDrawableUtils.cpp b/libs/hwui/utils/VectorDrawableUtils.cpp index 6b8f3154dd36..bd963df6750e 100644 --- a/libs/hwui/utils/VectorDrawableUtils.cpp +++ b/libs/hwui/utils/VectorDrawableUtils.cpp @@ -300,7 +300,7 @@ void PathResolver::addCommand(SkPath* outPath, char previousCmd, char cmd, // (rx ry x-axis-rotation large-arc-flag sweep-flag x y) outPath->arcTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), (SkPath::ArcSize) (points->at(k + 3) != 0), - (SkPath::Direction) (points->at(k + 4) == 0), + (SkPathDirection) (points->at(k + 4) == 0), points->at(k + 5) + currentX, points->at(k + 6) + currentY); currentX += points->at(k + 5); currentY += points->at(k + 6); @@ -310,7 +310,7 @@ void PathResolver::addCommand(SkPath* outPath, char previousCmd, char cmd, case 'A': // Draws an elliptical arc outPath->arcTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), (SkPath::ArcSize) (points->at(k + 3) != 0), - (SkPath::Direction) (points->at(k + 4) == 0), + (SkPathDirection) (points->at(k + 4) == 0), points->at(k + 5), points->at(k + 6)); currentX = points->at(k + 5); currentY = points->at(k + 6); diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp index 150f6dcde5d0..d291ec001daf 100644 --- a/libs/incident/Android.bp +++ b/libs/incident/Android.bp @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -cc_library_shared { - name: "libincident", + +cc_defaults { + name: "libincidentpriv_defaults", cflags: [ "-Wall", @@ -50,6 +51,80 @@ cc_library_shared { ":libincident_aidl", "src/IncidentReportArgs.cpp", ], +} + +cc_library_shared { + name: "libincidentpriv", + defaults: ["libincidentpriv_defaults"], + export_include_dirs: ["include_priv"], +} + +cc_library_shared { + name: "libincident", + + cflags: [ + "-Wall", + "-Werror", + "-Wno-missing-field-initializers", + "-Wno-unused-variable", + "-Wunused-parameter", + ], + + shared_libs: [ + "libbinder", + "liblog", + "libutils", + "libincidentpriv", + ], + + srcs: [ + "src/incident_report.cpp", + ], export_include_dirs: ["include"], + + stubs: { + symbol_file: "libincident.map.txt", + versions: [ + "30", + ], + }, } + +cc_test { + name: "libincident_test", + test_config: "AndroidTest.xml", + defaults: ["libincidentpriv_defaults"], + test_suites: ["device-tests", "mts"], + compile_multilib: "both", + multilib: { + lib64: { + suffix: "64", + }, + lib32: { + suffix: "32", + }, + }, + require_root: true, + + include_dirs: [ + "frameworks/base/libs/incident/include", + "frameworks/base/libs/incident/include_priv", + ], + + srcs: [ + "tests/IncidentReportArgs_test.cpp", + "tests/IncidentReportRequest_test.cpp", + ], + + shared_libs: [ + "libincident", + ], + + static_libs: [ + "libgmock", + ], +} + + + diff --git a/libs/incident/AndroidTest.xml b/libs/incident/AndroidTest.xml new file mode 100644 index 000000000000..b6b3f8596826 --- /dev/null +++ b/libs/incident/AndroidTest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 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. +--> +<configuration description="Config for libincident_test"> + <option name="test-suite-tag" value="device-tests" /> + <option name="config-descriptor:metadata" key="component" value="misc" /> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> + <option name="cleanup" value="true" /> + <option name="push" value="libincident_test->/data/local/tmp/libincident_test" /> + <option name="append-bitness" value="true" /> + </target_preparer> + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/local/tmp" /> + <option name="module-name" value="libincident_test" /> + </test> + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController"> + <option name="mainline-module-package-name" value="com.google.android.os.statsd" /> + </object> +</configuration> + diff --git a/libs/incident/TEST_MAPPING b/libs/incident/TEST_MAPPING new file mode 100644 index 000000000000..25e000092ecf --- /dev/null +++ b/libs/incident/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "libincident_test" + } + ] +} diff --git a/libs/incident/include/incident/incident_report.h b/libs/incident/include/incident/incident_report.h new file mode 100644 index 000000000000..4fbac9681214 --- /dev/null +++ b/libs/incident/include/incident/incident_report.h @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2020, 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. + */ + +/** + * @file incident_report.h + */ + +#pragma once + +#include <stdbool.h> +#include <stdint.h> + +#if __cplusplus +extern "C" { +#endif // __cplusplus + +struct AIncidentReportArgs; +/** + * Opaque class to represent the arguments to an incident report request. + * Incident reports contain debugging data about the device at runtime. + * For more information see the android.os.IncidentManager java class. + */ +typedef struct AIncidentReportArgs AIncidentReportArgs; + +// Privacy policy enum value, sync with frameworks/base/core/proto/android/privacy.proto, +// IncidentReportArgs.h and IncidentReportArgs.java. +enum { + /** + * Flag marking fields and incident reports than can be taken + * off the device only via adb. + */ + INCIDENT_REPORT_PRIVACY_POLICY_LOCAL = 0, + + /** + * Flag marking fields and incident reports than can be taken + * off the device with contemporary consent. + */ + INCIDENT_REPORT_PRIVACY_POLICY_EXPLICIT = 100, + + /** + * Flag marking fields and incident reports than can be taken + * off the device with prior consent. + */ + INCIDENT_REPORT_PRIVACY_POLICY_AUTOMATIC = 200, + + /** + * Flag to indicate that a given field has not been marked + * with a privacy policy. + */ + INCIDENT_REPORT_PRIVACY_POLICY_UNSET = 255 +}; + +/** + * Allocate and initialize an AIncidentReportArgs object. + */ +AIncidentReportArgs* AIncidentReportArgs_init(); + +/** + * Duplicate an existing AIncidentReportArgs object. + */ +AIncidentReportArgs* AIncidentReportArgs_clone(AIncidentReportArgs* that); + +/** + * Clean up and delete an AIncidentReportArgs object. + */ +void AIncidentReportArgs_delete(AIncidentReportArgs* args); + +/** + * Set this incident report to include all sections. + */ +void AIncidentReportArgs_setAll(AIncidentReportArgs* args, bool all); + +/** + * Set this incident report privacy policy spec. + */ +void AIncidentReportArgs_setPrivacyPolicy(AIncidentReportArgs* args, int privacyPolicy); + +/** + * Add this section to the incident report. The section IDs are the field numbers + * from the android.os.IncidentProto protobuf message. + */ +void AIncidentReportArgs_addSection(AIncidentReportArgs* args, int section); + +/** + * Set the apk package name that will be sent a broadcast when the incident + * report completes. Must be called in conjunction with AIncidentReportArgs_setReceiverClass. + */ +void AIncidentReportArgs_setReceiverPackage(AIncidentReportArgs* args, char const* pkg); + +/** + * Set the fully qualified class name of the java BroadcastReceiver class that will be + * sent a broadcast when the report completes. Must be called in conjunction with + * AIncidentReportArgs_setReceiverPackage. + */ +void AIncidentReportArgs_setReceiverClass(AIncidentReportArgs* args, char const* cls); + +/** + * Add protobuf data as a header to the incident report. The buffer should be a serialized + * android.os.IncidentHeaderProto object. + */ +void AIncidentReportArgs_addHeader(AIncidentReportArgs* args, uint8_t const* buf, size_t size); + +/** + * Initiate taking the report described in the args object. Returns 0 on success, + * and non-zero otherwise. + */ +int AIncidentReportArgs_takeReport(AIncidentReportArgs* args); + +#if __cplusplus +} // extern "C" +#endif // __cplusplus + diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include_priv/android/os/IncidentReportArgs.h index 94b4ad6eae31..ec3aabb3b49d 100644 --- a/libs/incident/include/android/os/IncidentReportArgs.h +++ b/libs/incident/include_priv/android/os/IncidentReportArgs.h @@ -14,9 +14,10 @@ * limitations under the License. */ -#ifndef ANDROID_OS_DUMPSTATE_ARGS_H_ -#define ANDROID_OS_DUMPSTATE_ARGS_H_ +#ifndef ANDROID_OS_INCIDENT_REPORT_ARGS_H +#define ANDROID_OS_INCIDENT_REPORT_ARGS_H +#include <binder/IServiceManager.h> #include <binder/Parcel.h> #include <binder/Parcelable.h> #include <utils/String16.h> @@ -29,7 +30,8 @@ namespace os { using namespace std; -// DESTINATION enum value, sync with frameworks/base/core/proto/android/privacy.proto +// DESTINATION enum value, sync with frameworks/base/core/proto/android/privacy.proto, +// incident/incident_report.h and IncidentReportArgs.java const uint8_t PRIVACY_POLICY_LOCAL = 0; const uint8_t PRIVACY_POLICY_EXPLICIT = 100; const uint8_t PRIVACY_POLICY_AUTOMATIC = 200; @@ -51,6 +53,7 @@ public: void setReceiverPkg(const string& pkg); void setReceiverCls(const string& cls); void addHeader(const vector<uint8_t>& headerProto); + void setGzip(bool gzip); inline bool all() const { return mAll; } bool containsSection(int section, bool specific) const; @@ -59,6 +62,7 @@ public: inline const string& receiverPkg() const { return mReceiverPkg; } inline const string& receiverCls() const { return mReceiverCls; } inline const vector<vector<uint8_t>>& headers() const { return mHeaders; } + inline bool gzip() const {return mGzip; } void merge(const IncidentReportArgs& that); @@ -69,9 +73,10 @@ private: int mPrivacyPolicy; string mReceiverPkg; string mReceiverCls; + bool mGzip; }; } } -#endif // ANDROID_OS_DUMPSTATE_ARGS_H_ +#endif // ANDROID_OS_INCIDENT_REPORT_ARGS_H diff --git a/libs/incident/libincident.map.txt b/libs/incident/libincident.map.txt new file mode 100644 index 000000000000..f157763f1a03 --- /dev/null +++ b/libs/incident/libincident.map.txt @@ -0,0 +1,15 @@ +LIBINCIDENT { + global: + AIncidentReportArgs_init; # apex # introduced=30 + AIncidentReportArgs_clone; # apex # introduced=30 + AIncidentReportArgs_delete; # apex # introduced=30 + AIncidentReportArgs_setAll; # apex # introduced=30 + AIncidentReportArgs_setPrivacyPolicy; # apex # introduced=30 + AIncidentReportArgs_addSection; # apex # introduced=30 + AIncidentReportArgs_setReceiverPackage; # apex # introduced=30 + AIncidentReportArgs_setReceiverClass; # apex # introduced=30 + AIncidentReportArgs_addHeader; # apex # introduced=30 + AIncidentReportArgs_takeReport; # apex # introduced=30 + local: + *; +}; diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp index 9d8a98338ef0..db495cfbf7e1 100644 --- a/libs/incident/src/IncidentReportArgs.cpp +++ b/libs/incident/src/IncidentReportArgs.cpp @@ -26,7 +26,8 @@ namespace os { IncidentReportArgs::IncidentReportArgs() :mSections(), mAll(false), - mPrivacyPolicy(-1) + mPrivacyPolicy(-1), + mGzip(false) { } @@ -36,7 +37,8 @@ IncidentReportArgs::IncidentReportArgs(const IncidentReportArgs& that) mAll(that.mAll), mPrivacyPolicy(that.mPrivacyPolicy), mReceiverPkg(that.mReceiverPkg), - mReceiverCls(that.mReceiverCls) + mReceiverCls(that.mReceiverCls), + mGzip(that.mGzip) { } @@ -93,6 +95,11 @@ IncidentReportArgs::writeToParcel(Parcel* out) const return err; } + err = out->writeInt32(mGzip); + if (err != NO_ERROR) { + return err; + } + return NO_ERROR; } @@ -149,6 +156,15 @@ IncidentReportArgs::readFromParcel(const Parcel* in) mReceiverPkg = String8(in->readString16()).string(); mReceiverCls = String8(in->readString16()).string(); + int32_t gzip; + err = in->readInt32(&gzip); + if (err != NO_ERROR) { + return err; + } + if (gzip != 0) { + mGzip = gzip; + } + return OK; } @@ -193,6 +209,12 @@ IncidentReportArgs::addHeader(const vector<uint8_t>& headerProto) mHeaders.push_back(headerProto); } +void +IncidentReportArgs::setGzip(bool gzip) +{ + mGzip = gzip; +} + bool IncidentReportArgs::containsSection(int section, bool specific) const { diff --git a/libs/incident/src/incident_report.cpp b/libs/incident/src/incident_report.cpp new file mode 100644 index 000000000000..7897ddf6d251 --- /dev/null +++ b/libs/incident/src/incident_report.cpp @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2020, 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. + */ + +#define LOG_TAG "libincident" + +#include <incident/incident_report.h> + +#include <android/os/IIncidentManager.h> +#include <android/os/IncidentReportArgs.h> +#include <binder/IServiceManager.h> +#include <binder/Status.h> +#include <log/log.h> + +using android::sp; +using android::binder::Status; +using android::os::IncidentReportArgs; +using android::os::IIncidentManager; +using std::string; +using std::vector; + +AIncidentReportArgs* AIncidentReportArgs_init() { + return reinterpret_cast<AIncidentReportArgs*>(new IncidentReportArgs()); +} + +AIncidentReportArgs* AIncidentReportArgs_clone(AIncidentReportArgs* that) { + return reinterpret_cast<AIncidentReportArgs*>( + new IncidentReportArgs(*reinterpret_cast<IncidentReportArgs*>(that))); +} + +void AIncidentReportArgs_delete(AIncidentReportArgs* args) { + delete reinterpret_cast<IncidentReportArgs*>(args); +} + +void AIncidentReportArgs_setAll(AIncidentReportArgs* args, bool all) { + reinterpret_cast<IncidentReportArgs*>(args)->setAll(all); +} + +void AIncidentReportArgs_setPrivacyPolicy(AIncidentReportArgs* args, int privacyPolicy) { + reinterpret_cast<IncidentReportArgs*>(args)->setPrivacyPolicy(privacyPolicy); +} + +void AIncidentReportArgs_addSection(AIncidentReportArgs* args, int section) { + reinterpret_cast<IncidentReportArgs*>(args)->addSection(section); +} + +void AIncidentReportArgs_setReceiverPackage(AIncidentReportArgs* args, char const* pkg) { + reinterpret_cast<IncidentReportArgs*>(args)->setReceiverPkg(string(pkg)); +} + +void AIncidentReportArgs_setReceiverClass(AIncidentReportArgs* args, char const* cls) { + reinterpret_cast<IncidentReportArgs*>(args)->setReceiverCls(string(cls)); +} + +void AIncidentReportArgs_addHeader(AIncidentReportArgs* args, uint8_t const* buf, size_t size) { + vector<uint8_t> vec(buf, buf+size); + reinterpret_cast<IncidentReportArgs*>(args)->addHeader(vec); +} + +int AIncidentReportArgs_takeReport(AIncidentReportArgs* argp) { + IncidentReportArgs* args = reinterpret_cast<IncidentReportArgs*>(argp); + + sp<IIncidentManager> service = android::interface_cast<IIncidentManager>( + android::defaultServiceManager()->getService(android::String16("incident"))); + if (service == nullptr) { + ALOGW("Failed to fetch incident service."); + return false; + } + Status s = service->reportIncident(*args); + return s.transactionError(); +} diff --git a/libs/incident/tests/IncidentReportArgs_test.cpp b/libs/incident/tests/IncidentReportArgs_test.cpp new file mode 100644 index 000000000000..224b343c554a --- /dev/null +++ b/libs/incident/tests/IncidentReportArgs_test.cpp @@ -0,0 +1,74 @@ +// Copyright (C) 2018 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. + +#include <android/os/IncidentReportArgs.h> + +#include <gtest/gtest.h> + +namespace android { +namespace os { +namespace statsd { + +// Checks that all of the inline methods on IncidentReportRequest and the real C functions +// result in a working IncidentReportArgs. +TEST(IncidentReportArgsTest, testSerialization) { + IncidentReportArgs args; + args.setAll(0); + args.addSection(1000); + args.addSection(1001); + + vector<uint8_t> header1; + header1.push_back(0x1); + header1.push_back(0x2); + vector<uint8_t> header2; + header1.push_back(0x22); + header1.push_back(0x33); + + args.addHeader(header1); + args.addHeader(header2); + + args.setPrivacyPolicy(1); + + args.setReceiverPkg("com.android.os"); + args.setReceiverCls("com.android.os.Receiver"); + + Parcel out; + status_t err = args.writeToParcel(&out); + EXPECT_EQ(NO_ERROR, err); + + out.setDataPosition(0); + + IncidentReportArgs args2; + err = args2.readFromParcel(&out); + EXPECT_EQ(NO_ERROR, err); + + EXPECT_EQ(0, args2.all()); + set<int> sections; + sections.insert(1000); + sections.insert(1001); + EXPECT_EQ(sections, args2.sections()); + EXPECT_EQ(1, args2.getPrivacyPolicy()); + + EXPECT_EQ(string("com.android.os"), args2.receiverPkg()); + EXPECT_EQ(string("com.android.os.Receiver"), args2.receiverCls()); + + vector<vector<uint8_t>> headers; + headers.push_back(header1); + headers.push_back(header2); + EXPECT_EQ(headers, args2.headers()); +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/libs/incident/tests/IncidentReportRequest_test.cpp b/libs/incident/tests/IncidentReportRequest_test.cpp new file mode 100644 index 000000000000..5619bb6c2220 --- /dev/null +++ b/libs/incident/tests/IncidentReportRequest_test.cpp @@ -0,0 +1,120 @@ +// Copyright (C) 2018 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. + +#include <android/os/IncidentReportArgs.h> +#include <incident/incident_report.h> + +#include <gtest/gtest.h> + +#include <vector> +#include <string> + +using namespace std; +using namespace android::os; + +class IncidentReportRequest { +public: + inline IncidentReportRequest() { + mImpl = AIncidentReportArgs_init(); + } + + inline IncidentReportRequest(const IncidentReportRequest& that) { + mImpl = AIncidentReportArgs_clone(that.mImpl); + } + + inline ~IncidentReportRequest() { + AIncidentReportArgs_delete(mImpl); + } + + inline AIncidentReportArgs* getImpl() { + return mImpl; + } + + inline void setAll(bool all) { + AIncidentReportArgs_setAll(mImpl, all); + } + + inline void setPrivacyPolicy(int privacyPolicy) { + AIncidentReportArgs_setPrivacyPolicy(mImpl, privacyPolicy); + } + + inline void addSection(int section) { + AIncidentReportArgs_addSection(mImpl, section); + } + + inline void setReceiverPackage(const string& pkg) { + AIncidentReportArgs_setReceiverPackage(mImpl, pkg.c_str()); + }; + + inline void setReceiverClass(const string& cls) { + AIncidentReportArgs_setReceiverClass(mImpl, cls.c_str()); + }; + + inline void addHeader(const vector<uint8_t>& headerProto) { + AIncidentReportArgs_addHeader(mImpl, headerProto.data(), headerProto.size()); + }; + + inline void addHeader(const uint8_t* buf, size_t size) { + AIncidentReportArgs_addHeader(mImpl, buf, size); + }; + + // returns a status_t + inline int takeReport() { + return AIncidentReportArgs_takeReport(mImpl); + } + +private: + AIncidentReportArgs* mImpl; +}; + + +TEST(IncidentReportRequestTest, testWrite) { + IncidentReportRequest request; + request.setAll(0); + request.addSection(1000); + request.addSection(1001); + + vector<uint8_t> header1; + header1.push_back(0x1); + header1.push_back(0x2); + vector<uint8_t> header2; + header1.push_back(0x22); + header1.push_back(0x33); + + request.addHeader(header1); + request.addHeader(header2); + + request.setPrivacyPolicy(1); + + request.setReceiverPackage("com.android.os"); + request.setReceiverClass("com.android.os.Receiver"); + + IncidentReportArgs* args = reinterpret_cast<IncidentReportArgs*>(request.getImpl()); + + EXPECT_EQ(0, args->all()); + set<int> sections; + sections.insert(1000); + sections.insert(1001); + EXPECT_EQ(sections, args->sections()); + EXPECT_EQ(1, args->getPrivacyPolicy()); + + EXPECT_EQ(string("com.android.os"), args->receiverPkg()); + EXPECT_EQ(string("com.android.os.Receiver"), args->receiverCls()); + + vector<vector<uint8_t>> headers; + headers.push_back(header1); + headers.push_back(header2); + EXPECT_EQ(headers, args->headers()); +} + diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 7942806bc86a..dca35012cbdd 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -24,11 +24,12 @@ cc_library_shared { ], shared_libs: [ + "libandroid_runtime", "libbinder", "libcutils", + "libhwui", "liblog", "libutils", - "libhwui", "libgui", "libui", "libinput", diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp index 5f481ca67ba6..acd8bced0612 100644 --- a/libs/input/SpriteController.cpp +++ b/libs/input/SpriteController.cpp @@ -360,12 +360,7 @@ void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) { uint32_t dirty; if (icon.isValid()) { - SkBitmap* bitmapCopy = &mLocked.state.icon.bitmap; - if (bitmapCopy->tryAllocPixels(icon.bitmap.info().makeColorType(kN32_SkColorType))) { - icon.bitmap.readPixels(bitmapCopy->info(), bitmapCopy->getPixels(), - bitmapCopy->rowBytes(), 0, 0); - } - + mLocked.state.icon.bitmap = icon.bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888); if (!mLocked.state.icon.isValid() || mLocked.state.icon.hotSpotX != icon.hotSpotX || mLocked.state.icon.hotSpotY != icon.hotSpotY) { diff --git a/libs/input/SpriteIcon.cpp b/libs/input/SpriteIcon.cpp index 10cc458b9289..b7e51e22a214 100644 --- a/libs/input/SpriteIcon.cpp +++ b/libs/input/SpriteIcon.cpp @@ -16,11 +16,9 @@ #include "SpriteIcon.h" -#include <SkBitmap.h> -#include <SkCanvas.h> -#include <SkColor.h> -#include <SkPaint.h> - +#include <android/graphics/bitmap.h> +#include <android/graphics/canvas.h> +#include <android/graphics/paint.h> #include <android/native_window.h> #include <log/log.h> @@ -34,25 +32,22 @@ bool SpriteIcon::draw(sp<Surface> surface) const { return false; } - SkBitmap surfaceBitmap; - ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format); - surfaceBitmap.installPixels(SkImageInfo::MakeN32Premul(outBuffer.width, outBuffer.height), - outBuffer.bits, bpr); + graphics::Paint paint; + paint.setBlendMode(ABLEND_MODE_SRC); - SkCanvas surfaceCanvas(surfaceBitmap); + graphics::Canvas canvas(outBuffer, (int32_t)surface->getBuffersDataSpace()); + canvas.drawBitmap(bitmap, 0, 0, &paint); - SkPaint paint; - paint.setBlendMode(SkBlendMode::kSrc); - surfaceCanvas.drawBitmap(bitmap, 0, 0, &paint); + const int iconWidth = width(); + const int iconHeight = height(); - if (outBuffer.width > width()) { - paint.setColor(0); // transparent fill color - surfaceCanvas.drawRect(SkRect::MakeLTRB(width(), 0, outBuffer.width, height()), paint); + if (outBuffer.width > iconWidth) { + paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent + canvas.drawRect({iconWidth, 0, outBuffer.width, iconHeight}, paint); } - if (outBuffer.height > height()) { - paint.setColor(0); // transparent fill color - surfaceCanvas.drawRect(SkRect::MakeLTRB(0, height(), outBuffer.width, outBuffer.height), - paint); + if (outBuffer.height > iconHeight) { + paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent + canvas.drawRect({0, iconHeight, outBuffer.width, outBuffer.height}, paint); } status = surface->unlockAndPost(); diff --git a/libs/input/SpriteIcon.h b/libs/input/SpriteIcon.h index c535faf624f7..a257d7e89ebc 100644 --- a/libs/input/SpriteIcon.h +++ b/libs/input/SpriteIcon.h @@ -17,7 +17,7 @@ #ifndef _UI_SPRITE_ICON_H #define _UI_SPRITE_ICON_H -#include <SkBitmap.h> +#include <android/graphics/bitmap.h> #include <gui/Surface.h> namespace android { @@ -26,22 +26,17 @@ namespace android { * Icon that a sprite displays, including its hotspot. */ struct SpriteIcon { - inline SpriteIcon() : style(0), hotSpotX(0), hotSpotY(0) { } - inline SpriteIcon(const SkBitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) : - bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { } + inline SpriteIcon() : style(0), hotSpotX(0), hotSpotY(0) {} + inline SpriteIcon(const graphics::Bitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) + : bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) {} - SkBitmap bitmap; + graphics::Bitmap bitmap; int32_t style; float hotSpotX; float hotSpotY; inline SpriteIcon copy() const { - SkBitmap bitmapCopy; - if (bitmapCopy.tryAllocPixels(bitmap.info().makeColorType(kN32_SkColorType))) { - bitmap.readPixels(bitmapCopy.info(), bitmapCopy.getPixels(), bitmapCopy.rowBytes(), - 0, 0); - } - return SpriteIcon(bitmapCopy, style, hotSpotX, hotSpotY); + return SpriteIcon(bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888), style, hotSpotX, hotSpotY); } inline void reset() { @@ -51,17 +46,16 @@ struct SpriteIcon { hotSpotY = 0; } - inline bool isValid() const { return !bitmap.isNull() && !bitmap.empty(); } + inline bool isValid() const { return bitmap.isValid() && !bitmap.isEmpty(); } - inline int32_t width() const { return bitmap.width(); } - inline int32_t height() const { return bitmap.height(); } + inline int32_t width() const { return bitmap.getInfo().width; } + inline int32_t height() const { return bitmap.getInfo().height; } // Draw the bitmap onto the given surface. Returns true if it's successful, or false otherwise. // Note it doesn't set any metadata to the surface. bool draw(const sp<Surface> surface) const; }; - } // namespace android #endif // _UI_SPRITE_ICON_H diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index e83b2a78d180..213b3adfb2a8 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -18,9 +18,10 @@ cc_test { "PointerController_test.cpp", ], shared_libs: [ + "libandroid_runtime", "libinputservice", - "libgui", "libhwui", + "libgui", "libutils", ], static_libs: [ diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp index b0af99732ddb..d2b7d5c75d58 100644 --- a/libs/protoutil/Android.bp +++ b/libs/protoutil/Android.bp @@ -45,6 +45,12 @@ cc_library { defaults: ["libprotoutil_defaults"], export_include_dirs: ["include"], + + apex_available: [ + "//apex_available:platform", + "com.android.os.statsd", + "test_com.android.os.statsd", + ], } cc_test { diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h index 42bf03e6de05..f4a358de1c41 100644 --- a/libs/protoutil/include/android/util/ProtoOutputStream.h +++ b/libs/protoutil/include/android/util/ProtoOutputStream.h @@ -90,6 +90,7 @@ class ProtoOutputStream { public: ProtoOutputStream(); + ProtoOutputStream(sp<EncodedBuffer> buffer); ~ProtoOutputStream(); /** diff --git a/libs/protoutil/src/EncodedBuffer.cpp b/libs/protoutil/src/EncodedBuffer.cpp index 7ffd8874a8fb..96b54c63a836 100644 --- a/libs/protoutil/src/EncodedBuffer.cpp +++ b/libs/protoutil/src/EncodedBuffer.cpp @@ -16,6 +16,7 @@ #define LOG_TAG "libprotoutil" #include <stdlib.h> +#include <sys/mman.h> #include <android/util/EncodedBuffer.h> #include <android/util/protobuf.h> @@ -82,14 +83,16 @@ EncodedBuffer::Pointer::copy() const } // =========================================================== -EncodedBuffer::EncodedBuffer() : EncodedBuffer(0) +EncodedBuffer::EncodedBuffer() : EncodedBuffer(BUFFER_SIZE) { } EncodedBuffer::EncodedBuffer(size_t chunkSize) :mBuffers() { - mChunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize; + // Align chunkSize to memory page size + chunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize; + mChunkSize = (chunkSize / PAGE_SIZE + ((chunkSize % PAGE_SIZE == 0) ? 0 : 1)) * PAGE_SIZE; mWp = Pointer(mChunkSize); mEp = Pointer(mChunkSize); } @@ -98,7 +101,7 @@ EncodedBuffer::~EncodedBuffer() { for (size_t i=0; i<mBuffers.size(); i++) { uint8_t* buf = mBuffers[i]; - free(buf); + munmap(buf, mChunkSize); } } @@ -135,7 +138,10 @@ EncodedBuffer::writeBuffer() if (mWp.index() > mBuffers.size()) return NULL; uint8_t* buf = NULL; if (mWp.index() == mBuffers.size()) { - buf = (uint8_t*)malloc(mChunkSize); + // Use mmap instead of malloc to ensure memory alignment i.e. no fragmentation so that + // the mem region can be immediately reused by the allocator after calling munmap() + buf = (uint8_t*)mmap(NULL, mChunkSize, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); if (buf == NULL) return NULL; // This indicates NO_MEMORY diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp index ea9b79a0353f..fcf82eed4eb1 100644 --- a/libs/protoutil/src/ProtoOutputStream.cpp +++ b/libs/protoutil/src/ProtoOutputStream.cpp @@ -26,8 +26,12 @@ namespace android { namespace util { -ProtoOutputStream::ProtoOutputStream() - :mBuffer(new EncodedBuffer()), +ProtoOutputStream::ProtoOutputStream(): ProtoOutputStream(new EncodedBuffer()) +{ +} + +ProtoOutputStream::ProtoOutputStream(sp<EncodedBuffer> buffer) + :mBuffer(buffer), mCopyBegin(0), mCompact(false), mDepth(0), diff --git a/libs/services/Android.bp b/libs/services/Android.bp index 1b9939d9a598..1e621079b532 100644 --- a/libs/services/Android.bp +++ b/libs/services/Android.bp @@ -20,18 +20,16 @@ cc_library_shared { ":IDropBoxManagerService.aidl", "src/content/ComponentName.cpp", "src/os/DropBoxManager.cpp", - "src/os/StatsDimensionsValue.cpp", - "src/os/StatsLogEventWrapper.cpp", ], shared_libs: [ "libbinder", - "liblog", "libcutils", + "liblog", "libutils", ], header_libs: [ - "libbase_headers", + "libbase_headers", ], aidl: { include_dirs: ["frameworks/base/core/java/"], diff --git a/libs/services/include/android/os/StatsDimensionsValue.h b/libs/services/include/android/os/StatsDimensionsValue.h deleted file mode 100644 index cc0b05644f2c..000000000000 --- a/libs/services/include/android/os/StatsDimensionsValue.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ -#ifndef STATS_DIMENSIONS_VALUE_H -#define STATS_DIMENSIONS_VALUE_H - -#include <binder/Parcel.h> -#include <binder/Parcelable.h> -#include <binder/Status.h> -#include <utils/String16.h> -#include <vector> - -namespace android { -namespace os { - -// Represents a parcelable object. Used to send data from statsd to StatsCompanionService.java. -class StatsDimensionsValue : public android::Parcelable { -public: - StatsDimensionsValue(); - - StatsDimensionsValue(int32_t field, String16 value); - StatsDimensionsValue(int32_t field, int32_t value); - StatsDimensionsValue(int32_t field, int64_t value); - StatsDimensionsValue(int32_t field, bool value); - StatsDimensionsValue(int32_t field, float value); - StatsDimensionsValue(int32_t field, std::vector<StatsDimensionsValue> value); - - virtual ~StatsDimensionsValue(); - - virtual android::status_t writeToParcel(android::Parcel* out) const override; - virtual android::status_t readFromParcel(const android::Parcel* in) override; - -private: - // Keep constants in sync with android/os/StatsDimensionsValue.java - // and stats_log.proto's DimensionValue. - static const int kStrValueType = 2; - static const int kIntValueType = 3; - static const int kLongValueType = 4; - static const int kBoolValueType = 5; - static const int kFloatValueType = 6; - static const int kTupleValueType = 7; - - int32_t mField; - int32_t mValueType; - - // This isn't very clever, but it isn't used for long-term storage, so it'll do. - String16 mStrValue; - int32_t mIntValue; - int64_t mLongValue; - bool mBoolValue; - float mFloatValue; - std::vector<StatsDimensionsValue> mTupleValue; -}; - -} // namespace os -} // namespace android - -#endif // STATS_DIMENSIONS_VALUE_H diff --git a/libs/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h deleted file mode 100644 index 8de2ab49f42b..000000000000 --- a/libs/services/include/android/os/StatsLogEventWrapper.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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. - */ -#ifndef STATS_LOG_EVENT_WRAPPER_H -#define STATS_LOG_EVENT_WRAPPER_H - -#include <binder/Parcel.h> -#include <binder/Parcelable.h> -#include <binder/Status.h> -#include <utils/RefBase.h> -#include <vector> - -namespace android { -namespace os { - -/** - * A wrapper for a union type to contain multiple types of values. - * - */ -struct StatsLogValue { - // Keep in sync with FieldValue.h - enum STATS_LOG_VALUE_TYPE { - UNKNOWN = 0, - INT = 1, - LONG = 2, - FLOAT = 3, - DOUBLE = 4, - STRING = 5, - STORAGE = 6 - }; - - StatsLogValue() : type(UNKNOWN) {} - - StatsLogValue(int32_t v) { - int_value = v; - type = INT; - } - - StatsLogValue(int64_t v) { - long_value = v; - type = LONG; - } - - StatsLogValue(float v) { - float_value = v; - type = FLOAT; - } - - StatsLogValue(double v) { - double_value = v; - type = DOUBLE; - } - - StatsLogValue(const std::string& v) { - str_value = v; - type = STRING; - } - - void setType(STATS_LOG_VALUE_TYPE t) { type = t; } - - union { - int32_t int_value; - int64_t long_value; - float float_value; - double double_value; - }; - std::string str_value; - std::vector<uint8_t> storage_value; - - STATS_LOG_VALUE_TYPE type; -}; - -struct WorkChain { - std::vector<int32_t> uids; - std::vector<std::string> tags; -}; - -// Represents a parcelable object. Only used to send data from Android OS to statsd. -class StatsLogEventWrapper : public android::Parcelable { - public: - StatsLogEventWrapper(); - - StatsLogEventWrapper(StatsLogEventWrapper&& in) = default; - - android::status_t writeToParcel(android::Parcel* out) const; - - android::status_t readFromParcel(const android::Parcel* in); - - int getTagId() const { return mTagId; } - - int64_t getElapsedRealTimeNs() const { return mElapsedRealTimeNs; } - - int64_t getWallClockTimeNs() const { return mWallClockTimeNs; } - - const std::vector<StatsLogValue>& getElements() const { return mElements; } - - const std::vector<WorkChain>& getWorkChains() const { return mWorkChains; } - - private: - int mTagId; - - int64_t mElapsedRealTimeNs; - - int64_t mWallClockTimeNs; - - std::vector<StatsLogValue> mElements; - - std::vector<WorkChain> mWorkChains; -}; -} // Namespace os -} // Namespace android - - -#endif // STATS_LOG_EVENT_WRAPPER_H - diff --git a/libs/services/src/os/StatsDimensionsValue.cpp b/libs/services/src/os/StatsDimensionsValue.cpp deleted file mode 100644 index 0052e0baa905..000000000000 --- a/libs/services/src/os/StatsDimensionsValue.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#define LOG_TAG "StatsDimensionsValue" - -#include "android/os/StatsDimensionsValue.h" - -#include <cutils/log.h> - -using android::Parcel; -using android::Parcelable; -using android::status_t; -using std::vector; - -namespace android { -namespace os { - -StatsDimensionsValue::StatsDimensionsValue() {}; - -StatsDimensionsValue::StatsDimensionsValue(int32_t field, String16 value) : - mField(field), - mValueType(kStrValueType), - mStrValue(value) { -} -StatsDimensionsValue::StatsDimensionsValue(int32_t field, int32_t value) : - mField(field), - mValueType(kIntValueType), - mIntValue(value) { -} -StatsDimensionsValue::StatsDimensionsValue(int32_t field, int64_t value) : - mField(field), - mValueType(kLongValueType), - mLongValue(value) { -} -StatsDimensionsValue::StatsDimensionsValue(int32_t field, bool value) : - mField(field), - mValueType(kBoolValueType), - mBoolValue(value) { -} -StatsDimensionsValue::StatsDimensionsValue(int32_t field, float value) : - mField(field), - mValueType(kFloatValueType), - mFloatValue(value) { -} -StatsDimensionsValue::StatsDimensionsValue(int32_t field, vector<StatsDimensionsValue> value) : - mField(field), - mValueType(kTupleValueType), - mTupleValue(value) { -} - -StatsDimensionsValue::~StatsDimensionsValue() {} - -status_t -StatsDimensionsValue::writeToParcel(Parcel* out) const { - status_t err ; - - err = out->writeInt32(mField); - if (err != NO_ERROR) { - return err; - } - err = out->writeInt32(mValueType); - if (err != NO_ERROR) { - return err; - } - switch (mValueType) { - case kStrValueType: - err = out->writeString16(mStrValue); - break; - case kIntValueType: - err = out->writeInt32(mIntValue); - break; - case kLongValueType: - err = out->writeInt64(mLongValue); - break; - case kBoolValueType: - err = out->writeBool(mBoolValue); - break; - case kFloatValueType: - err = out->writeFloat(mFloatValue); - break; - case kTupleValueType: - { - int sz = mTupleValue.size(); - err = out->writeInt32(sz); - if (err != NO_ERROR) { - return err; - } - for (int i = 0; i < sz; ++i) { - err = mTupleValue[i].writeToParcel(out); - if (err != NO_ERROR) { - return err; - } - } - } - break; - default: - err = UNKNOWN_ERROR; - break; - } - return err; -} - -status_t -StatsDimensionsValue::readFromParcel(const Parcel* in) -{ - // Implement me if desired. We don't currently use this. - ALOGE("Cannot do c++ StatsDimensionsValue.readFromParcel(); it is not implemented."); - (void)in; // To prevent compile error of unused parameter 'in' - return UNKNOWN_ERROR; -} - -} // namespace os -} // namespace android diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp deleted file mode 100644 index f6dfdef16e19..000000000000 --- a/libs/services/src/os/StatsLogEventWrapper.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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. - */ -#include <android/os/StatsLogEventWrapper.h> - -#include <binder/Parcel.h> -#include <binder/Parcelable.h> -#include <binder/Status.h> -#include <utils/RefBase.h> -#include <vector> - -using android::Parcel; -using android::Parcelable; -using android::status_t; -using std::vector; - -namespace android { -namespace os { - -StatsLogEventWrapper::StatsLogEventWrapper(){}; - -status_t StatsLogEventWrapper::writeToParcel(Parcel* out) const { - // Implement me if desired. We don't currently use this. - ALOGE( - "Cannot do c++ StatsLogEventWrapper.writeToParcel(); it is not " - "implemented."); - (void)out; // To prevent compile error of unused parameter 'in' - return UNKNOWN_ERROR; -}; - -status_t StatsLogEventWrapper::readFromParcel(const Parcel* in) { - status_t res = OK; - if (in == NULL) { - ALOGE("statsd received parcel argument was NULL."); - return BAD_VALUE; - } - if ((res = in->readInt32(&mTagId)) != OK) { - ALOGE("statsd could not read tagId from parcel"); - return res; - } - if ((res = in->readInt64(&mElapsedRealTimeNs)) != OK) { - ALOGE("statsd could not read elapsed real time from parcel"); - return res; - } - if ((res = in->readInt64(&mWallClockTimeNs)) != OK) { - ALOGE("statsd could not read wall clock time from parcel"); - return res; - } - int numWorkChain = 0; - if ((res = in->readInt32(&numWorkChain)) != OK) { - ALOGE("statsd could not read number of work chains from parcel"); - return res; - } - if (numWorkChain > 0) { - for (int i = 0; i < numWorkChain; i++) { - int numNodes = 0; - if ((res = in->readInt32(&numNodes)) != OK) { - ALOGE( - "statsd could not read number of nodes in work chain from parcel"); - return res; - } - if (numNodes == 0) { - ALOGE("empty work chain"); - return BAD_VALUE; - } - WorkChain wc; - for (int j = 0; j < numNodes; j++) { - wc.uids.push_back(in->readInt32()); - wc.tags.push_back(std::string(String8(in->readString16()).string())); - } - mWorkChains.push_back(wc); - } - } - int dataSize = 0; - if ((res = in->readInt32(&dataSize)) != OK) { - ALOGE("statsd could not read data size from parcel"); - return res; - } - if (mTagId <= 0 || mElapsedRealTimeNs <= 0 || mWallClockTimeNs <= 0 || - dataSize <= 0) { - ALOGE("statsd received invalid parcel"); - return BAD_VALUE; - } - - for (int i = 0; i < dataSize; i++) { - int type = in->readInt32(); - switch (type) { - case StatsLogValue::INT: - mElements.push_back(StatsLogValue(in->readInt32())); - break; - case StatsLogValue::LONG: - mElements.push_back(StatsLogValue(in->readInt64())); - break; - case StatsLogValue::STRING: - mElements.push_back( - StatsLogValue(std::string(String8(in->readString16()).string()))); - break; - case StatsLogValue::FLOAT: - mElements.push_back(StatsLogValue(in->readFloat())); - break; - case StatsLogValue::STORAGE: - mElements.push_back(StatsLogValue()); - mElements.back().setType(StatsLogValue::STORAGE); - in->readByteVector(&(mElements.back().storage_value)); - break; - default: - ALOGE("unrecognized data type: %d", type); - return BAD_TYPE; - } - } - return NO_ERROR; -}; - -} // Namespace os -} // Namespace android |