summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/WindowManager/Jetpack/Android.bp38
-rw-r--r--libs/WindowManager/Jetpack/androidx.window.sidecar.xml21
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java222
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java126
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java41
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java85
-rw-r--r--libs/WindowManager/Jetpack/window-sidecar-release.aarbin0 -> 4366 bytes
-rw-r--r--libs/WindowManager/OWNERS3
-rw-r--r--libs/WindowManager/Shell/Android.bp29
-rw-r--r--libs/WindowManager/Shell/AndroidManifest.xml20
-rw-r--r--libs/WindowManager/Shell/OWNERS4
-rw-r--r--libs/WindowManager/Shell/res/values/config.xml21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java23
-rw-r--r--libs/WindowManager/Shell/tests/Android.bp45
-rw-r--r--libs/WindowManager/Shell/tests/AndroidManifest.xml32
-rw-r--r--libs/WindowManager/Shell/tests/AndroidTest.xml31
-rw-r--r--libs/WindowManager/Shell/tests/res/values/config.xml21
-rw-r--r--libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java40
-rw-r--r--libs/androidfw/Android.bp9
-rwxr-xr-x[-rw-r--r--]libs/androidfw/ApkAssets.cpp576
-rw-r--r--libs/androidfw/Asset.cpp57
-rw-r--r--libs/androidfw/AssetManager2.cpp497
-rw-r--r--libs/androidfw/CursorWindow.cpp6
-rw-r--r--libs/androidfw/DisplayEventDispatcher.cpp153
-rw-r--r--libs/androidfw/Idmap.cpp278
-rw-r--r--libs/androidfw/LoadedArsc.cpp77
-rw-r--r--libs/androidfw/ResourceTypes.cpp50
-rw-r--r--libs/androidfw/TEST_MAPPING3
-rw-r--r--libs/androidfw/include/androidfw/ApkAssets.h158
-rw-r--r--libs/androidfw/include/androidfw/Asset.h30
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h128
-rw-r--r--libs/androidfw/include/androidfw/DisplayEventDispatcher.h51
-rw-r--r--libs/androidfw/include/androidfw/Idmap.h173
-rw-r--r--libs/androidfw/include/androidfw/LoadedArsc.h73
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h156
-rw-r--r--libs/androidfw/include/androidfw/Util.h7
-rw-r--r--libs/androidfw/tests/ApkAssets_test.cpp51
-rw-r--r--libs/androidfw/tests/AssetManager2_test.cpp85
-rw-r--r--libs/androidfw/tests/AttributeResolution_test.cpp2
-rw-r--r--libs/androidfw/tests/Idmap_test.cpp289
-rw-r--r--libs/androidfw/tests/LoadedArsc_test.cpp111
-rw-r--r--libs/androidfw/tests/Theme_test.cpp2
-rw-r--r--libs/androidfw/tests/data/lib_two/R.h12
-rw-r--r--libs/androidfw/tests/data/lib_two/lib_two.apkbin1426 -> 1586 bytes
-rw-r--r--libs/androidfw/tests/data/lib_two/res/values/values.xml11
-rw-r--r--libs/androidfw/tests/data/libclient/R.h1
-rw-r--r--libs/androidfw/tests/data/libclient/libclient.apkbin1982 -> 2168 bytes
-rw-r--r--libs/androidfw/tests/data/libclient/res/values/values.xml6
-rw-r--r--libs/androidfw/tests/data/loader/AndroidManifest.xml20
-rwxr-xr-xlibs/androidfw/tests/data/loader/build27
-rw-r--r--libs/androidfw/tests/data/loader/res/values/public.xml19
-rw-r--r--libs/androidfw/tests/data/loader/res/values/values.xml19
-rw-r--r--libs/androidfw/tests/data/loader/resources.arscbin0 -> 652 bytes
-rw-r--r--libs/androidfw/tests/data/overlay/AndroidManifest.xml8
-rw-r--r--libs/androidfw/tests/data/overlay/R.h (renamed from libs/hwui/debug/DefaultGlesDriver.h)28
-rwxr-xr-xlibs/androidfw/tests/data/overlay/build11
-rw-r--r--libs/androidfw/tests/data/overlay/overlay.apkbin5211 -> 2992 bytes
-rw-r--r--libs/androidfw/tests/data/overlay/overlay.idmapbin0 -> 1090 bytes
-rw-r--r--libs/androidfw/tests/data/overlay/res/layout/hello_view.xml20
-rw-r--r--libs/androidfw/tests/data/overlay/res/values/values.xml14
-rw-r--r--libs/androidfw/tests/data/overlay/res/xml/overlays.xml47
-rw-r--r--libs/androidfw/tests/data/overlayable/R.h37
-rwxr-xr-xlibs/androidfw/tests/data/overlayable/build5
-rw-r--r--libs/androidfw/tests/data/overlayable/overlayable.apkbin3443 -> 6306 bytes
-rw-r--r--libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml20
-rw-r--r--libs/androidfw/tests/data/overlayable/res/values/overlayable.xml54
-rw-r--r--libs/androidfw/tests/data/overlayable/res/values/public.xml17
-rw-r--r--libs/androidfw/tests/data/overlayable/res/values/values.xml11
-rw-r--r--libs/androidfw/tests/data/system/R.h7
-rw-r--r--libs/androidfw/tests/data/system/res/values/public.xml20
-rw-r--r--libs/androidfw/tests/data/system/res/values/values.xml20
-rw-r--r--libs/androidfw/tests/data/system/system.apkbin1624 -> 1976 bytes
-rw-r--r--libs/hostgraphics/Android.bp31
-rw-r--r--libs/hostgraphics/Fence.cpp23
-rw-r--r--libs/hostgraphics/HostBufferQueue.cpp69
-rw-r--r--libs/hostgraphics/PublicFormat.cpp33
-rw-r--r--libs/hostgraphics/gui/BufferItem.h65
-rw-r--r--libs/hostgraphics/gui/BufferItemConsumer.h75
-rw-r--r--libs/hostgraphics/gui/BufferQueue.h (renamed from libs/hwui/debug/FatalBaseDriver.h)30
-rw-r--r--libs/hostgraphics/gui/ConsumerBase.h (renamed from libs/hwui/debug/MockGlesDriver.h)28
-rw-r--r--libs/hostgraphics/gui/IGraphicBufferConsumer.h65
-rw-r--r--libs/hostgraphics/gui/IGraphicBufferProducer.h (renamed from libs/hwui/debug/GlesErrorCheckWrapper.h)35
-rw-r--r--libs/hostgraphics/gui/Surface.h66
-rw-r--r--libs/hostgraphics/ui/Fence.h72
-rw-r--r--libs/hostgraphics/ui/GraphicBuffer.h64
-rw-r--r--libs/hwui/Android.bp502
-rw-r--r--libs/hwui/AndroidTest.xml2
-rw-r--r--libs/hwui/AutoBackendTextureRelease.cpp91
-rw-r--r--libs/hwui/AutoBackendTextureRelease.h67
-rw-r--r--libs/hwui/CanvasTransform.cpp4
-rw-r--r--libs/hwui/DamageAccumulator.cpp26
-rw-r--r--libs/hwui/DeferredLayerUpdater.cpp138
-rw-r--r--libs/hwui/DeferredLayerUpdater.h60
-rw-r--r--libs/hwui/DeviceInfo.cpp182
-rw-r--r--libs/hwui/DeviceInfo.h33
-rw-r--r--libs/hwui/DisplayListOps.in49
-rw-r--r--libs/hwui/FrameInfo.cpp3
-rw-r--r--libs/hwui/FrameInfo.h17
-rw-r--r--libs/hwui/GpuMemoryTracker.cpp122
-rw-r--r--libs/hwui/GpuMemoryTracker.h77
-rw-r--r--libs/hwui/HardwareBitmapUploader.cpp26
-rw-r--r--libs/hwui/HardwareBitmapUploader.h6
-rw-r--r--libs/hwui/JankTracker.cpp25
-rw-r--r--libs/hwui/JankTracker.h6
-rw-r--r--libs/hwui/LightingInfo.cpp (renamed from libs/hwui/debug/wrap_gles.cpp)24
-rw-r--r--libs/hwui/LightingInfo.h86
-rw-r--r--libs/hwui/Outline.h6
-rw-r--r--libs/hwui/ProfileData.cpp53
-rw-r--r--libs/hwui/ProfileData.h16
-rw-r--r--libs/hwui/Properties.cpp75
-rw-r--r--libs/hwui/Properties.h1
-rw-r--r--libs/hwui/Readback.cpp52
-rw-r--r--libs/hwui/Readback.h2
-rw-r--r--libs/hwui/RecordingCanvas.cpp49
-rw-r--r--libs/hwui/RecordingCanvas.h12
-rw-r--r--libs/hwui/RenderNode.cpp25
-rw-r--r--libs/hwui/RenderNode.h6
-rw-r--r--libs/hwui/RenderProperties.h7
-rw-r--r--libs/hwui/RootRenderNode.cpp306
-rw-r--r--libs/hwui/RootRenderNode.h90
-rw-r--r--libs/hwui/SkiaCanvas.cpp171
-rw-r--r--libs/hwui/SkiaCanvas.h86
-rw-r--r--libs/hwui/TEST_MAPPING8
-rw-r--r--libs/hwui/TreeInfo.cpp1
-rw-r--r--libs/hwui/TreeInfo.h1
-rw-r--r--libs/hwui/VectorDrawable.cpp124
-rw-r--r--libs/hwui/VectorDrawable.h33
-rw-r--r--libs/hwui/WebViewFunctorManager.h10
-rw-r--r--libs/hwui/apex/LayoutlibLoader.cpp191
-rw-r--r--libs/hwui/apex/TypeCast.h70
-rw-r--r--libs/hwui/apex/android_bitmap.cpp304
-rw-r--r--libs/hwui/apex/android_canvas.cpp109
-rw-r--r--libs/hwui/apex/android_matrix.cpp37
-rw-r--r--libs/hwui/apex/android_paint.cpp47
-rw-r--r--libs/hwui/apex/android_region.cpp60
-rw-r--r--libs/hwui/apex/include/android/graphics/bitmap.h146
-rw-r--r--libs/hwui/apex/include/android/graphics/canvas.h142
-rw-r--r--libs/hwui/apex/include/android/graphics/jni_runtime.h35
-rw-r--r--libs/hwui/apex/include/android/graphics/matrix.h39
-rw-r--r--libs/hwui/apex/include/android/graphics/paint.h67
-rw-r--r--libs/hwui/apex/include/android/graphics/region.h77
-rw-r--r--libs/hwui/apex/include/android/graphics/renderthread.h34
-rw-r--r--libs/hwui/apex/jni_runtime.cpp177
-rw-r--r--libs/hwui/apex/renderthread.cpp25
-rw-r--r--libs/hwui/debug/FatalBaseDriver.cpp40
-rw-r--r--libs/hwui/debug/GlesDriver.cpp46
-rw-r--r--libs/hwui/debug/GlesDriver.h55
-rw-r--r--libs/hwui/debug/GlesErrorCheckWrapper.cpp75
-rw-r--r--libs/hwui/debug/NullGlesDriver.cpp172
-rw-r--r--libs/hwui/debug/NullGlesDriver.h202
-rw-r--r--libs/hwui/debug/ScopedReplaceDriver.h45
-rw-r--r--libs/hwui/debug/gles_decls.in543
-rw-r--r--libs/hwui/debug/gles_redefine.h914
-rw-r--r--libs/hwui/debug/gles_stubs.in1629
-rw-r--r--libs/hwui/debug/gles_undefine.h913
-rw-r--r--libs/hwui/debug/nullegl.cpp175
-rw-r--r--libs/hwui/debug/wrap_gles.h40
-rw-r--r--libs/hwui/hwui/AnimatedImageDrawable.cpp10
-rw-r--r--libs/hwui/hwui/Bitmap.cpp121
-rw-r--r--libs/hwui/hwui/Bitmap.h55
-rw-r--r--libs/hwui/hwui/Canvas.cpp57
-rw-r--r--libs/hwui/hwui/Canvas.h64
-rw-r--r--libs/hwui/hwui/ImageDecoder.cpp217
-rw-r--r--libs/hwui/hwui/ImageDecoder.h73
-rw-r--r--libs/hwui/hwui/MinikinSkia.h2
-rw-r--r--libs/hwui/hwui/Paint.h7
-rw-r--r--libs/hwui/hwui/PaintImpl.cpp4
-rw-r--r--libs/hwui/hwui/Typeface.cpp4
-rw-r--r--libs/hwui/jni/AnimatedImageDrawable.cpp271
-rwxr-xr-xlibs/hwui/jni/Bitmap.cpp1189
-rw-r--r--libs/hwui/jni/Bitmap.h53
-rw-r--r--libs/hwui/jni/BitmapFactory.cpp667
-rw-r--r--libs/hwui/jni/BitmapFactory.h31
-rw-r--r--libs/hwui/jni/BitmapRegionDecoder.cpp288
-rw-r--r--libs/hwui/jni/ByteBufferStreamAdaptor.cpp316
-rw-r--r--libs/hwui/jni/ByteBufferStreamAdaptor.h37
-rw-r--r--libs/hwui/jni/Camera.cpp143
-rw-r--r--libs/hwui/jni/CanvasProperty.cpp50
-rw-r--r--libs/hwui/jni/ColorFilter.cpp89
-rw-r--r--libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp306
-rw-r--r--libs/hwui/jni/CreateJavaOutputStreamAdaptor.h42
-rw-r--r--libs/hwui/jni/FontFamily.cpp233
-rw-r--r--libs/hwui/jni/FontUtils.cpp62
-rw-r--r--libs/hwui/jni/FontUtils.h71
-rw-r--r--libs/hwui/jni/GIFMovie.cpp447
-rw-r--r--libs/hwui/jni/Graphics.cpp768
-rw-r--r--libs/hwui/jni/GraphicsJNI.h335
-rw-r--r--libs/hwui/jni/GraphicsStatsService.cpp197
-rw-r--r--libs/hwui/jni/ImageDecoder.cpp526
-rw-r--r--libs/hwui/jni/ImageDecoder.h25
-rw-r--r--libs/hwui/jni/Interpolator.cpp84
-rw-r--r--libs/hwui/jni/MaskFilter.cpp90
-rw-r--r--libs/hwui/jni/MimeType.h22
-rw-r--r--libs/hwui/jni/Movie.cpp164
-rw-r--r--libs/hwui/jni/Movie.h79
-rw-r--r--libs/hwui/jni/MovieImpl.cpp94
-rw-r--r--libs/hwui/jni/NinePatch.cpp168
-rw-r--r--libs/hwui/jni/NinePatchPeeker.cpp93
-rw-r--r--libs/hwui/jni/NinePatchPeeker.h59
-rw-r--r--libs/hwui/jni/Paint.cpp1159
-rw-r--r--libs/hwui/jni/PaintFilter.cpp80
-rw-r--r--libs/hwui/jni/Path.cpp560
-rw-r--r--libs/hwui/jni/PathEffect.cpp117
-rw-r--r--libs/hwui/jni/PathMeasure.cpp160
-rw-r--r--libs/hwui/jni/Picture.cpp119
-rw-r--r--libs/hwui/jni/Picture.h68
-rw-r--r--libs/hwui/jni/Region.cpp359
-rw-r--r--libs/hwui/jni/RtlProperties.h49
-rw-r--r--libs/hwui/jni/Shader.cpp305
-rw-r--r--libs/hwui/jni/TEST_MAPPING7
-rw-r--r--libs/hwui/jni/Typeface.cpp159
-rw-r--r--libs/hwui/jni/Utils.cpp171
-rw-r--r--libs/hwui/jni/Utils.h93
-rw-r--r--libs/hwui/jni/YuvToJpegEncoder.cpp268
-rw-r--r--libs/hwui/jni/YuvToJpegEncoder.h74
-rw-r--r--libs/hwui/jni/android_graphics_Canvas.cpp739
-rw-r--r--libs/hwui/jni/android_graphics_ColorSpace.cpp113
-rw-r--r--libs/hwui/jni/android_graphics_DisplayListCanvas.cpp213
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRenderer.cpp745
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp130
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRendererObserver.h75
-rw-r--r--libs/hwui/jni/android_graphics_Matrix.cpp396
-rw-r--r--libs/hwui/jni/android_graphics_Matrix.h30
-rw-r--r--libs/hwui/jni/android_graphics_Picture.cpp110
-rw-r--r--libs/hwui/jni/android_graphics_RenderNode.cpp759
-rw-r--r--libs/hwui/jni/android_graphics_TextureLayer.cpp85
-rw-r--r--libs/hwui/jni/android_graphics_animation_NativeInterpolatorFactory.cpp112
-rw-r--r--libs/hwui/jni/android_graphics_animation_RenderNodeAnimator.cpp221
-rw-r--r--libs/hwui/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp218
-rw-r--r--libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp423
-rw-r--r--libs/hwui/jni/android_nio_utils.cpp46
-rw-r--r--libs/hwui/jni/android_nio_utils.h83
-rw-r--r--libs/hwui/jni/android_util_PathParser.cpp118
-rw-r--r--libs/hwui/jni/fonts/Font.cpp142
-rw-r--r--libs/hwui/jni/fonts/FontFamily.cpp101
-rw-r--r--libs/hwui/jni/graphics_jni_helpers.h106
-rw-r--r--libs/hwui/jni/pdf/PdfDocument.cpp163
-rw-r--r--libs/hwui/jni/pdf/PdfEditor.cpp307
-rw-r--r--libs/hwui/jni/pdf/PdfRenderer.cpp134
-rw-r--r--libs/hwui/jni/pdf/PdfUtils.cpp136
-rw-r--r--libs/hwui/jni/pdf/PdfUtils.h (renamed from libs/hwui/debug/DefaultGlesDriver.cpp)29
-rw-r--r--libs/hwui/jni/scoped_nullable_primitive_array.h103
-rw-r--r--libs/hwui/jni/text/LineBreaker.cpp174
-rw-r--r--libs/hwui/jni/text/MeasuredText.cpp167
-rw-r--r--libs/hwui/pipeline/skia/ATraceMemoryDump.cpp177
-rw-r--r--libs/hwui/pipeline/skia/ATraceMemoryDump.h79
-rw-r--r--libs/hwui/pipeline/skia/GLFunctorDrawable.cpp36
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.cpp24
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.h4
-rw-r--r--libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp12
-rw-r--r--libs/hwui/pipeline/skia/ReorderBarrierDrawables.h2
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.cpp17
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.h5
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp24
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h7
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp429
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.h123
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp75
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.h8
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp11
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.h3
-rw-r--r--libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp283
-rw-r--r--libs/hwui/pipeline/skia/VectorDrawableAtlas.h212
-rw-r--r--libs/hwui/pipeline/skia/VkFunctorDrawable.cpp5
-rw-r--r--libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp4
-rw-r--r--libs/hwui/protos/graphicsstats.proto12
-rw-r--r--libs/hwui/renderstate/RenderState.cpp6
-rw-r--r--libs/hwui/renderstate/RenderState.h1
-rw-r--r--libs/hwui/renderthread/CacheManager.cpp51
-rw-r--r--libs/hwui/renderthread/CacheManager.h24
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp196
-rw-r--r--libs/hwui/renderthread/CanvasContext.h23
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp2
-rw-r--r--libs/hwui/renderthread/EglManager.cpp62
-rw-r--r--libs/hwui/renderthread/EglManager.h6
-rw-r--r--libs/hwui/renderthread/IRenderPipeline.h6
-rw-r--r--libs/hwui/renderthread/ReliableSurface.cpp313
-rw-r--r--libs/hwui/renderthread/ReliableSurface.h85
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp41
-rw-r--r--libs/hwui/renderthread/RenderProxy.h11
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp141
-rw-r--r--libs/hwui/renderthread/RenderThread.h46
-rw-r--r--libs/hwui/renderthread/TimeLord.cpp2
-rw-r--r--libs/hwui/renderthread/VulkanManager.cpp114
-rw-r--r--libs/hwui/renderthread/VulkanManager.h18
-rw-r--r--libs/hwui/renderthread/VulkanSurface.cpp374
-rw-r--r--libs/hwui/renderthread/VulkanSurface.h16
-rw-r--r--libs/hwui/service/GraphicsStatsService.cpp225
-rw-r--r--libs/hwui/service/GraphicsStatsService.h4
-rw-r--r--libs/hwui/surfacetexture/EGLConsumer.cpp675
-rw-r--r--libs/hwui/surfacetexture/EGLConsumer.h311
-rw-r--r--libs/hwui/surfacetexture/ImageConsumer.cpp299
-rw-r--r--libs/hwui/surfacetexture/ImageConsumer.h115
-rw-r--r--libs/hwui/surfacetexture/SurfaceTexture.cpp499
-rw-r--r--libs/hwui/surfacetexture/SurfaceTexture.h452
-rw-r--r--libs/hwui/tests/common/TestContext.cpp74
-rw-r--r--libs/hwui/tests/common/TestContext.h11
-rw-r--r--libs/hwui/tests/common/TestUtils.h1
-rw-r--r--libs/hwui/tests/common/scenes/BitmapShaders.cpp9
-rw-r--r--libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp11
-rw-r--r--libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp4
-rw-r--r--libs/hwui/tests/common/scenes/ListViewAnimation.cpp4
-rw-r--r--libs/hwui/tests/common/scenes/MagnifierAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/OvalAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/RectGridAnimation.cpp4
-rw-r--r--libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/ShapeAnimation.cpp18
-rw-r--r--libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp20
-rw-r--r--libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp4
-rw-r--r--libs/hwui/tests/common/scenes/TestSceneBase.h1
-rw-r--r--libs/hwui/tests/common/scenes/TvApp.cpp4
-rw-r--r--libs/hwui/tests/macrobench/TestSceneRunner.cpp20
-rw-r--r--libs/hwui/tests/microbench/DisplayListCanvasBench.cpp3
-rw-r--r--libs/hwui/tests/microbench/main.cpp5
-rwxr-xr-xlibs/hwui/tests/scripts/skp-capture.sh78
-rw-r--r--libs/hwui/tests/unit/ABitmapTests.cpp46
-rw-r--r--libs/hwui/tests/unit/CacheManagerTests.cpp8
-rw-r--r--libs/hwui/tests/unit/FatVectorTests.cpp2
-rw-r--r--libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp65
-rw-r--r--libs/hwui/tests/unit/RenderNodeDrawableTests.cpp43
-rw-r--r--libs/hwui/tests/unit/SkiaBehaviorTests.cpp4
-rw-r--r--libs/hwui/tests/unit/SkiaCanvasTests.cpp5
-rw-r--r--libs/hwui/tests/unit/SkiaDisplayListTests.cpp7
-rw-r--r--libs/hwui/tests/unit/SkiaPipelineTests.cpp90
-rw-r--r--libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp3
-rw-r--r--libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp163
-rw-r--r--libs/hwui/tests/unit/VectorDrawableTests.cpp8
-rw-r--r--libs/hwui/tests/unit/main.cpp3
-rw-r--r--libs/hwui/thread/WorkQueue.h2
-rw-r--r--libs/hwui/utils/Color.cpp156
-rw-r--r--libs/hwui/utils/Color.h27
-rw-r--r--libs/hwui/utils/FatVector.h105
-rw-r--r--libs/hwui/utils/GLUtils.cpp10
-rw-r--r--libs/hwui/utils/HostColorSpace.cpp417
-rw-r--r--libs/hwui/utils/LinearAllocator.h1
-rw-r--r--libs/hwui/utils/PaintUtils.h23
-rw-r--r--libs/hwui/utils/TraceUtils.h1
-rw-r--r--libs/hwui/utils/VectorDrawableUtils.cpp4
-rw-r--r--libs/incident/Android.bp79
-rw-r--r--libs/incident/AndroidTest.xml32
-rw-r--r--libs/incident/TEST_MAPPING7
-rw-r--r--libs/incident/include/incident/incident_report.h125
-rw-r--r--libs/incident/include_priv/android/os/IncidentReportArgs.h (renamed from libs/incident/include/android/os/IncidentReportArgs.h)13
-rw-r--r--libs/incident/libincident.map.txt15
-rw-r--r--libs/incident/src/IncidentReportArgs.cpp26
-rw-r--r--libs/incident/src/incident_report.cpp83
-rw-r--r--libs/incident/tests/IncidentReportArgs_test.cpp74
-rw-r--r--libs/incident/tests/IncidentReportRequest_test.cpp120
-rw-r--r--libs/input/Android.bp3
-rw-r--r--libs/input/SpriteController.cpp7
-rw-r--r--libs/input/SpriteIcon.cpp35
-rw-r--r--libs/input/SpriteIcon.h24
-rw-r--r--libs/input/tests/Android.bp3
-rw-r--r--libs/protoutil/Android.bp6
-rw-r--r--libs/protoutil/include/android/util/ProtoOutputStream.h1
-rw-r--r--libs/protoutil/src/EncodedBuffer.cpp14
-rw-r--r--libs/protoutil/src/ProtoOutputStream.cpp8
-rw-r--r--libs/services/Android.bp6
-rw-r--r--libs/services/include/android/os/StatsDimensionsValue.h70
-rw-r--r--libs/services/include/android/os/StatsLogEventWrapper.h127
-rw-r--r--libs/services/src/os/StatsDimensionsValue.cpp126
-rw-r--r--libs/services/src/os/StatsLogEventWrapper.cpp127
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
new file mode 100644
index 000000000000..50f101d7d181
--- /dev/null
+++ b/libs/WindowManager/Jetpack/window-sidecar-release.aar
Binary files differ
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
index 486c23000276..8193db637eed 100644
--- a/libs/androidfw/tests/data/lib_two/lib_two.apk
+++ b/libs/androidfw/tests/data/lib_two/lib_two.apk
Binary files differ
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
index 17990248e862..4b9a8833c64a 100644
--- a/libs/androidfw/tests/data/libclient/libclient.apk
+++ b/libs/androidfw/tests/data/libclient/libclient.apk
Binary files differ
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
new file mode 100644
index 000000000000..2bdb288dbcab
--- /dev/null
+++ b/libs/androidfw/tests/data/loader/resources.arsc
Binary files differ
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
index d37874dcbb40..f1ed59279fdb 100644
--- a/libs/androidfw/tests/data/overlay/overlay.apk
+++ b/libs/androidfw/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap
new file mode 100644
index 000000000000..29c5eb6a9ccf
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/overlay.idmap
Binary files differ
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
index 047e6afde86b..9dc9c15f68a9 100644
--- a/libs/androidfw/tests/data/overlayable/overlayable.apk
+++ b/libs/androidfw/tests/data/overlayable/overlayable.apk
Binary files differ
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
index 9045d6c4de21..1f7e00733366 100644
--- a/libs/androidfw/tests/data/system/system.apk
+++ b/libs/androidfw/tests/data/system/system.apk
Binary files differ
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(&params.transform[0]);
+ mat4.getColMajor(&params.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