From 253f2c213f6ecda63b6872aee77bd30d5ec07c82 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Wed, 28 Sep 2016 17:34:42 -0700 Subject: [PATCH] Linear blending, step 1 NOTE: Linear blending is currently disabled in this CL as the feature is still a work in progress Android currently performs all blending (any kind of linear math on colors really) on gamma-encoded colors. Since Android assumes that the default color space is sRGB, all bitmaps and colors are encoded with the sRGB Opto-Electronic Conversion Function (OECF, which can be approximated with a power function). Since the power curve is not linear, our linear math is incorrect. The result is that we generate colors that tend to be too dark; this affects blending but also anti-aliasing, gradients, blurs, etc. The solution is to convert gamma-encoded colors back to linear space before doing any math on them, using the sRGB Electo-Optical Conversion Function (EOCF). This is achieved in different ways in different parts of the pipeline: - Using hardware conversions when sampling from OpenGL textures or writing into OpenGL frame buffers - Using software conversion functions, to translate app-supplied colors to and from sRGB - Using Skia's color spaces Any type of processing on colors must roughly ollow these steps: [sRGB input]->EOCF->[linear data]->[processing]->OECF->[sRGB output] For the sRGB color space, the conversion functions are defined as follows: OECF(linear) := linear <= 0.0031308 ? linear * 12.92 : (pow(linear, 1/2.4) * 1.055) - 0.055 EOCF(srgb) := srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4) The EOCF is simply the reciprocal of the OECF. While it is highly recommended to use the exact sRGB conversion functions everywhere possible, it is sometimes useful or beneficial to rely on approximations: - pow(x,2.2) and pow(x,1/2.2) - x^2 and sqrt(x) The latter is particularly useful in fragment shaders (for instance to apply dithering in sRGB space), especially if the sqrt() can be replaced with an inversesqrt(). Here is a fairly exhaustive list of modifications implemented in this CL: - Set TARGET_ENABLE_LINEAR_BLENDING := false in BoardConfig.mk to disable linear blending. This is only for GLES 2.0 GPUs with no hardware sRGB support. This flag is currently assumed to be false (see note above) - sRGB writes are disabled when entering a functor (WebView). This will need to be fixed at some point - Skia bitmaps are created with the sRGB color space - Bitmaps using a 565 config are expanded to 888 - Linear blending is disabled when entering a functor - External textures are not properly sampled (see below) - Gradients are interpolated in linear space - Texture-based dithering was replaced with analytical dithering - Dithering is done in the quantization color space, which is why we must do EOCF(OECF(color)+dither) - Text is now gamma corrected differently depending on the luminance of the source pixel. The asumption is that a bright pixel will be blended on a dark background and the other way around. The source alpha is gamma corrected to thicken dark on bright and thin bright on dark to match the intended design of fonts. This also matches the behavior of popular design/drawing applications - Removed the asset atlas. It did not contain anything useful and could not be sampled in sRGB without a yet-to-be-defined GL extension - The last column of color matrices is converted to linear space because its value are added to linear colors Missing features: - Resource qualifier? - Regeneration of goldeng images for automated tests - Handle alpha8/grey8 properly - Disable sRGB write for layers with external textures Test: Manual testing while work in progress Bug: 29940137 Change-Id: I6a07b15ab49b554377cd33a36b6d9971a15e9a0b --- Android.mk | 1 - compiled-classes-phone | 11 - core/java/android/view/IAssetAtlas.aidl | 54 -- core/java/android/view/ThreadedRenderer.java | 28 - core/jni/Android.mk | 8 + core/jni/android/graphics/Bitmap.cpp | 26 +- core/jni/android/graphics/BitmapFactory.cpp | 19 +- core/jni/android/graphics/Graphics.cpp | 8 + core/jni/android/graphics/GraphicsJNI.h | 3 + core/jni/android_graphics_Canvas.cpp | 5 +- core/jni/android_view_GraphicBuffer.cpp | 3 +- core/jni/android_view_Surface.cpp | 6 +- core/jni/android_view_SurfaceControl.cpp | 4 +- core/jni/android_view_TextureView.cpp | 3 +- core/jni/android_view_ThreadedRenderer.cpp | 16 - graphics/java/android/graphics/Atlas.java | 416 ------------ graphics/java/android/graphics/Bitmap.java | 11 - .../android/graphics/drawable/BitmapDrawable.java | 9 - .../java/android/graphics/drawable/Drawable.java | 14 - .../graphics/drawable/DrawableContainer.java | 16 - .../android/graphics/drawable/DrawableWrapper.java | 11 - .../android/graphics/drawable/LayerDrawable.java | 18 - .../graphics/drawable/NinePatchDrawable.java | 10 - libs/hwui/Android.mk | 11 +- libs/hwui/AssetAtlas.cpp | 130 ---- libs/hwui/AssetAtlas.h | 174 ----- libs/hwui/BakedOpDispatcher.cpp | 52 +- libs/hwui/BakedOpRenderer.cpp | 6 +- libs/hwui/Caches.cpp | 2 - libs/hwui/Caches.h | 15 +- libs/hwui/Dither.cpp | 98 --- libs/hwui/Dither.h | 56 -- libs/hwui/Extensions.cpp | 9 + libs/hwui/Extensions.h | 4 + libs/hwui/FloatColor.h | 9 +- libs/hwui/FontRenderer.cpp | 13 +- libs/hwui/FrameBuilder.cpp | 2 - libs/hwui/GammaFontRenderer.cpp | 3 +- libs/hwui/GammaFontRenderer.h | 13 +- libs/hwui/GlopBuilder.cpp | 41 +- libs/hwui/GlopBuilder.h | 2 + libs/hwui/GradientCache.cpp | 72 +-- libs/hwui/GradientCache.h | 23 +- libs/hwui/Layer.h | 2 +- libs/hwui/PatchCache.cpp | 6 +- libs/hwui/PatchCache.h | 4 +- libs/hwui/Program.h | 7 + libs/hwui/ProgramCache.cpp | 172 +++-- libs/hwui/ProgramCache.h | 1 + libs/hwui/Properties.h | 4 +- libs/hwui/PropertyValuesHolder.cpp | 23 +- libs/hwui/Readback.cpp | 2 +- libs/hwui/RecordedOp.h | 1 - libs/hwui/SkiaShader.cpp | 23 +- libs/hwui/SkiaShader.h | 5 +- libs/hwui/Texture.cpp | 83 ++- libs/hwui/Texture.h | 15 +- libs/hwui/TextureCache.cpp | 25 +- libs/hwui/TextureCache.h | 30 +- libs/hwui/VectorDrawable.cpp | 8 +- libs/hwui/Vertex.h | 17 +- libs/hwui/font/CacheTexture.cpp | 7 +- libs/hwui/renderstate/OffscreenBufferPool.cpp | 3 +- libs/hwui/renderstate/RenderState.cpp | 12 +- libs/hwui/renderstate/RenderState.h | 3 - libs/hwui/renderthread/CanvasContext.cpp | 5 - libs/hwui/renderthread/CanvasContext.h | 3 - libs/hwui/renderthread/EglManager.cpp | 42 +- libs/hwui/renderthread/EglManager.h | 7 - libs/hwui/renderthread/RenderProxy.cpp | 17 - libs/hwui/renderthread/RenderProxy.h | 1 - libs/hwui/tests/common/TestUtils.h | 3 +- libs/hwui/tests/unit/SkiaBehaviorTests.cpp | 7 + libs/hwui/utils/Color.h | 24 + libs/hwui/utils/TestWindowContext.cpp | 3 +- preloaded-classes | 3 - .../java/com/android/server/AssetAtlasService.java | 717 --------------------- services/core/jni/Android.mk | 1 - .../jni/com_android_server_AssetAtlasService.cpp | 216 ------- services/core/jni/onload.cpp | 2 - services/java/com/android/server/SystemServer.java | 20 - .../src/android/graphics/Bitmap_Delegate.java | 8 - 82 files changed, 492 insertions(+), 2475 deletions(-) delete mode 100644 core/java/android/view/IAssetAtlas.aidl delete mode 100644 graphics/java/android/graphics/Atlas.java delete mode 100644 libs/hwui/AssetAtlas.cpp delete mode 100644 libs/hwui/AssetAtlas.h delete mode 100644 libs/hwui/Dither.cpp delete mode 100644 libs/hwui/Dither.h delete mode 100644 services/core/java/com/android/server/AssetAtlasService.java delete mode 100644 services/core/jni/com_android_server_AssetAtlasService.cpp diff --git a/Android.mk b/Android.mk index e352bc70d085..1f3210f7af6c 100644 --- a/Android.mk +++ b/Android.mk @@ -288,7 +288,6 @@ LOCAL_SRC_FILES += \ core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \ core/java/android/view/IApplicationToken.aidl \ core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl \ - core/java/android/view/IAssetAtlas.aidl \ core/java/android/view/IDockedStackListener.aidl \ core/java/android/view/IGraphicsStats.aidl \ core/java/android/view/IInputFilter.aidl \ diff --git a/compiled-classes-phone b/compiled-classes-phone index 221d68739366..621430671eb4 100644 --- a/compiled-classes-phone +++ b/compiled-classes-phone @@ -1168,14 +1168,6 @@ android.drm.DrmManagerClient$OnEventListener android.drm.DrmManagerClient$OnInfoListener android.drm.DrmOutputStream android.drm.DrmSupportInfo -android.graphics.Atlas -android.graphics.Atlas$Entry -android.graphics.Atlas$Policy -android.graphics.Atlas$SlicePolicy -android.graphics.Atlas$SlicePolicy$Cell -android.graphics.Atlas$SlicePolicy$MinAreaSplitDecision -android.graphics.Atlas$SlicePolicy$SplitDecision -android.graphics.Atlas$Type android.graphics.Bitmap android.graphics.Bitmap$1 android.graphics.Bitmap$CompressFormat @@ -4264,9 +4256,6 @@ android.view.IAppTransitionAnimationSpecsFuture$Stub android.view.IAppTransitionAnimationSpecsFuture$Stub$Proxy android.view.IApplicationToken android.view.IApplicationToken$Stub -android.view.IAssetAtlas -android.view.IAssetAtlas$Stub -android.view.IAssetAtlas$Stub$Proxy android.view.IDockedStackListener android.view.IDockedStackListener$Stub android.view.IDockedStackListener$Stub$Proxy diff --git a/core/java/android/view/IAssetAtlas.aidl b/core/java/android/view/IAssetAtlas.aidl deleted file mode 100644 index edce05970b9a..000000000000 --- a/core/java/android/view/IAssetAtlas.aidl +++ /dev/null @@ -1,54 +0,0 @@ -/** - * 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. - */ - -package android.view; - -import android.view.GraphicBuffer; - -/** - * Programming interface to the system assets atlas. This atlas, when - * present, holds preloaded drawable in a single, shareable graphics - * buffer. This allows multiple processes to share the same data to - * save up on memory. - * - * @hide - */ -interface IAssetAtlas { - /** - * Indicates whether the atlas is compatible with the specified - * parent process id. If the atlas' ppid does not match, this - * method will return false. - */ - boolean isCompatible(int ppid); - - /** - * Returns the atlas buffer (texture) or null if the atlas is - * not available yet. - */ - GraphicBuffer getBuffer(); - - /** - * Returns the map of the bitmaps stored in the atlas or null - * if the atlas is not available yet. - * - * Each bitmap is represented by several entries in the array: - * long0: SkBitmap*, the native bitmap object - * long1: x position - * long2: y position - * long3: rotated, 1 if the bitmap must be rotated, 0 otherwise - */ - long[] getMap(); -} diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index ce390a239d2c..f71459e426dc 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -916,7 +916,6 @@ public final class ThreadedRenderer { mInitialized = true; initSched(context, renderProxy); initGraphicsStats(context, renderProxy); - initAssetAtlas(context, renderProxy); } private static void initSched(Context context, long renderProxy) { @@ -944,32 +943,6 @@ public final class ThreadedRenderer { Log.w(LOG_TAG, "Could not acquire gfx stats buffer", t); } } - - private static void initAssetAtlas(Context context, long renderProxy) { - IBinder binder = ServiceManager.getService("assetatlas"); - if (binder == null) return; - - IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder); - try { - if (atlas.isCompatible(android.os.Process.myPpid())) { - GraphicBuffer buffer = atlas.getBuffer(); - if (buffer != null) { - long[] map = atlas.getMap(); - if (map != null) { - nSetAtlas(renderProxy, buffer, map); - } - // If IAssetAtlas is not the same class as the IBinder - // we are using a remote service and we can safely - // destroy the graphic buffer - if (atlas.getClass() != binder.getClass()) { - buffer.destroy(); - } - } - } - } catch (RemoteException e) { - Log.w(LOG_TAG, "Could not acquire atlas", e); - } - } } void addFrameMetricsObserver(FrameMetricsObserver observer) { @@ -984,7 +957,6 @@ public final class ThreadedRenderer { static native void setupShadersDiskCache(String cacheFile); - private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map); private static native void nSetProcessStatsBuffer(long nativeProxy, int fd); private static native int nGetRenderThreadTid(long nativeProxy); diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 4c6350ba9ad6..a7b0fe9ad869 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -19,6 +19,14 @@ ifneq ($(ENABLE_CPUSETS),) LOCAL_CFLAGS += -DENABLE_CPUSETS endif +# TODO: Linear blending should be enabled by default, but we are +# TODO: making it an opt-in while it's a work in progress +# TODO: The final test should be: +# TODO: ifneq ($(TARGET_ENABLE_LINEAR_BLENDING),false) +ifeq ($(TARGET_ENABLE_LINEAR_BLENDING),true) + hwui_cflags += -DANDROID_ENABLE_LINEAR_BLENDING +endif + LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES LOCAL_CFLAGS += -DU_USING_ICU_NAMESPACE=0 diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index e79732100e53..9cfe1537a86a 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -693,7 +693,8 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, } SkBitmap bitmap; - bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType)); + bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType, + GraphicsJNI::defaultColorSpace())); PixelRef* nativeBitmap = GraphicsJNI::allocateHeapPixelRef(&bitmap, NULL); if (!nativeBitmap) { @@ -794,7 +795,8 @@ static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle, // Otherwise respect the premultiplied request. alphaType = requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType; } - bitmap->pixelRef()->reconfigure(SkImageInfo::Make(width, height, colorType, alphaType)); + bitmap->pixelRef()->reconfigure(SkImageInfo::Make(width, height, colorType, alphaType, + sk_sp(bitmap->info().colorSpace()))); } // These must match the int values in Bitmap.java @@ -927,6 +929,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { const bool isMutable = p->readInt32() != 0; const SkColorType colorType = (SkColorType)p->readInt32(); const SkAlphaType alphaType = (SkAlphaType)p->readInt32(); + const bool isSRGB = p->readInt32() != 0; const int width = p->readInt32(); const int height = p->readInt32(); const int rowBytes = p->readInt32(); @@ -943,7 +946,8 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { std::unique_ptr bitmap(new SkBitmap); - if (!bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType), rowBytes)) { + if (!bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType, + isSRGB ? SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named) : nullptr), rowBytes)) { return NULL; } @@ -1058,9 +1062,13 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, auto androidBitmap = reinterpret_cast(bitmapHandle); androidBitmap->getSkBitmap(&bitmap); + sk_sp sRGB = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); + bool isSRGB = bitmap.colorSpace() == sRGB.get(); + p->writeInt32(isMutable); p->writeInt32(bitmap.colorType()); p->writeInt32(bitmap.alphaType()); + p->writeInt32(isSRGB); // TODO: We should write the color space (b/32072280) p->writeInt32(bitmap.width()); p->writeInt32(bitmap.height()); p->writeInt32(bitmap.rowBytes()); @@ -1273,7 +1281,9 @@ static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, reinterpret_cast(bm1Handle)->getSkBitmap(&bm1); if (bm0.width() != bm1.width() || bm0.height() != bm1.height() || - bm0.colorType() != bm1.colorType()) { + bm0.colorType() != bm1.colorType() || + bm0.alphaType() != bm1.alphaType() || + bm0.colorSpace() != bm1.colorSpace()) { return JNI_FALSE; } @@ -1326,13 +1336,6 @@ static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, return JNI_TRUE; } -static jlong Bitmap_refPixelRef(JNIEnv* env, jobject, jlong bitmapHandle) { - LocalScopedBitmap bitmap(bitmapHandle); - SkPixelRef* pixelRef = bitmap->pixelRef(); - SkSafeRef(pixelRef); - return reinterpret_cast(pixelRef); -} - static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapPtr) { LocalScopedBitmap bitmapHandle(bitmapPtr); if (!bitmapHandle.valid()) return; @@ -1402,7 +1405,6 @@ static const JNINativeMethod gBitmapMethods[] = { { "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V", (void*)Bitmap_copyPixelsFromBuffer }, { "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs }, - { "nativeRefPixelRef", "(J)J", (void*)Bitmap_refPixelRef }, { "nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw }, { "nativeGetAllocationByteCount", "(J)I", (void*)Bitmap_getAllocationByteCount }, }; diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 5bb02746bf3a..5c24585cb182 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -385,11 +385,8 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding // Set the alpha type for the decode. SkAlphaType alphaType = codec->computeOutputAlphaType(requireUnpremultiplied); - // Enable legacy behavior to avoid any gamma correction. Android's assets are - // adjusted to expect a non-gamma correct premultiply. - sk_sp colorSpace = nullptr; - const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(), decodeColorType, - alphaType, colorSpace); + const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(), + decodeColorType, alphaType, GraphicsJNI::defaultColorSpace()); SkImageInfo bitmapInfo = decodeInfo; if (decodeColorType == kGray_8_SkColorType) { @@ -413,7 +410,7 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding // Use SkAndroidCodec to perform the decode. SkAndroidCodec::AndroidOptions codecOptions; - codecOptions.fZeroInitialized = decodeAllocator == &defaultAllocator ? + codecOptions.fZeroInitialized = decodeAllocator == &defaultAllocator ? SkCodec::kYes_ZeroInitialized : SkCodec::kNo_ZeroInitialized; codecOptions.fColorPtr = colorPtr; codecOptions.fColorCount = colorCount; @@ -452,8 +449,10 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding jobject ninePatchInsets = NULL; if (peeker.mHasInsets) { ninePatchInsets = env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID, - peeker.mOpticalInsets[0], peeker.mOpticalInsets[1], peeker.mOpticalInsets[2], peeker.mOpticalInsets[3], - peeker.mOutlineInsets[0], peeker.mOutlineInsets[1], peeker.mOutlineInsets[2], peeker.mOutlineInsets[3], + peeker.mOpticalInsets[0], peeker.mOpticalInsets[1], + peeker.mOpticalInsets[2], peeker.mOpticalInsets[3], + peeker.mOutlineInsets[0], peeker.mOutlineInsets[1], + peeker.mOutlineInsets[2], peeker.mOutlineInsets[3], peeker.mOutlineRadius, peeker.mOutlineAlpha, scale); if (ninePatchInsets == NULL) { return nullObjectReturn("nine patch insets == null"); @@ -495,11 +494,11 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding } SkPaint paint; - // kSrc_Mode instructs us to overwrite the unininitialized pixels in + // kSrc_Mode instructs us to overwrite the uninitialized pixels in // outputBitmap. Otherwise we would blend by default, which is not // what we want. paint.setXfermodeMode(SkXfermode::kSrc_Mode); - paint.setFilterQuality(kLow_SkFilterQuality); + paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering SkCanvas canvas(outputBitmap); canvas.scale(sx, sy); diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 8a55052376f7..82b70fc683dc 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -587,6 +587,14 @@ android::PixelRef* GraphicsJNI::mapAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap, return wrapper; } +sk_sp GraphicsJNI::defaultColorSpace() { +#ifdef ANDROID_ENABLE_LINEAR_BLENDING + return SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); +#else + return nullptr; +#endif +} + /////////////////////////////////////////////////////////////////////////////// bool HeapAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { mStorage.reset(GraphicsJNI::allocateHeapPixelRef(bitmap, ctable)); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index ceda3cd28ad4..34c4efb3362e 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -10,6 +10,7 @@ #include "SkMallocPixelRef.h" #include "SkPoint.h" #include "SkRect.h" +#include "SkColorSpace.h" #include #include @@ -93,6 +94,8 @@ public: static bool SetPixels(JNIEnv* env, jintArray colors, int srcOffset, int srcStride, int x, int y, int width, int height, const SkBitmap& dstBitmap); + + static sk_sp defaultColorSpace(); }; class HeapAllocator : public SkBRDAllocator { diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp index 87f754d261ff..43f7ca5c1237 100644 --- a/core/jni/android_graphics_Canvas.cpp +++ b/core/jni/android_graphics_Canvas.cpp @@ -442,9 +442,8 @@ static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle, 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); + SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType, + GraphicsJNI::defaultColorSpace()); SkBitmap bitmap; bitmap.setInfo(info); if (!GraphicsJNI::allocatePixels(env, &bitmap, NULL)) { diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp index 59c337b63f1d..17437318470d 100644 --- a/core/jni/android_view_GraphicBuffer.cpp +++ b/core/jni/android_view_GraphicBuffer.cpp @@ -182,7 +182,8 @@ static jboolean android_view_GraphicBuffer_lockCanvas(JNIEnv* env, jobject, SkBitmap bitmap; bitmap.setInfo(SkImageInfo::Make(buffer->getWidth(), buffer->getHeight(), convertPixelFormat(buffer->getPixelFormat()), - kPremul_SkAlphaType), + kPremul_SkAlphaType, + GraphicsJNI::defaultColorSpace()), bytesCount); if (buffer->getWidth() > 0 && buffer->getHeight() > 0) { diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 3f2b924f225f..b6c81cf8738a 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -318,11 +318,11 @@ static jlong nativeLockCanvas(JNIEnv* env, jclass clazz, return 0; } - SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height, convertPixelFormat(outBuffer.format), - outBuffer.format == PIXEL_FORMAT_RGBX_8888 ? - kOpaque_SkAlphaType : kPremul_SkAlphaType); + outBuffer.format == PIXEL_FORMAT_RGBX_8888 + ? kOpaque_SkAlphaType : kPremul_SkAlphaType, + GraphicsJNI::defaultColorSpace()); SkBitmap bitmap; ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format); diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 5d53b3f8bdce..65f12ac1907a 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -175,7 +175,9 @@ static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, } SkImageInfo screenshotInfo = SkImageInfo::Make(screenshot->getWidth(), screenshot->getHeight(), - colorType, alphaType); + colorType, + alphaType, + GraphicsJNI::defaultColorSpace()); const size_t rowBytes = screenshot->getStride() * android::bytesPerPixel(screenshot->getFormat()); diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp index e185281dad8a..268aec57032d 100644 --- a/core/jni/android_view_TextureView.cpp +++ b/core/jni/android_view_TextureView.cpp @@ -90,7 +90,8 @@ static inline SkImageInfo convertPixelFormat(const ANativeWindow_Buffer& buffer) default: break; } - return SkImageInfo::Make(buffer.width, buffer.height, colorType, alphaType); + return SkImageInfo::Make(buffer.width, buffer.height, colorType, alphaType, + GraphicsJNI::defaultColorSpace()); } /** diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 309bb2f7b0b9..14dcb3febe6f 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -613,21 +613,6 @@ static jboolean android_view_ThreadedRenderer_supportsOpenGL(JNIEnv* env, jobjec return atoi(prop) > 0 ? JNI_TRUE : JNI_FALSE; } -static void android_view_ThreadedRenderer_setAtlas(JNIEnv* env, jobject clazz, - jlong proxyPtr, jobject graphicBuffer, jlongArray atlasMapArray) { - sp buffer = graphicBufferForJavaObject(env, graphicBuffer); - jsize len = env->GetArrayLength(atlasMapArray); - if (len <= 0) { - ALOGW("Failed to initialize atlas, invalid map length: %d", len); - return; - } - int64_t* map = new int64_t[len]; - env->GetLongArrayRegion(atlasMapArray, 0, len, map); - - RenderProxy* proxy = reinterpret_cast(proxyPtr); - proxy->setTextureAtlas(buffer, map, len); -} - static void android_view_ThreadedRenderer_setProcessStatsBuffer(JNIEnv* env, jobject clazz, jlong proxyPtr, jint fd) { RenderProxy* proxy = reinterpret_cast(proxyPtr); @@ -955,7 +940,6 @@ const char* const kClassPathName = "android/view/ThreadedRenderer"; static const JNINativeMethod gMethods[] = { { "nSupportsOpenGL", "()Z", (void*) android_view_ThreadedRenderer_supportsOpenGL }, - { "nSetAtlas", "(JLandroid/view/GraphicBuffer;[J)V", (void*) android_view_ThreadedRenderer_setAtlas }, { "nSetProcessStatsBuffer", "(JI)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer }, { "nGetRenderThreadTid", "(J)I", (void*) android_view_ThreadedRenderer_getRenderThreadTid }, { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode }, diff --git a/graphics/java/android/graphics/Atlas.java b/graphics/java/android/graphics/Atlas.java deleted file mode 100644 index e0a534583b67..000000000000 --- a/graphics/java/android/graphics/Atlas.java +++ /dev/null @@ -1,416 +0,0 @@ -/* - * 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. - */ - -package android.graphics; - -/** - * @hide - */ -public class Atlas { - /** - * WARNING: These flag values are part of the on-disk configuration information, - * do not change their values. - */ - - /** DELETED: FLAG_ROTATION = 0x01 */ - - /** - * This flag indicates whether the packing algorithm should leave - * an empty 1 pixel wide border around each bitmap. This border can - * be useful if the content of the atlas will be used in OpenGL using - * bilinear filtering. - */ - public static final int FLAG_ADD_PADDING = 0x2; - /** - * Default flags: allow rotations and add padding. - */ - public static final int FLAG_DEFAULTS = FLAG_ADD_PADDING; - - /** - * Each type defines a different packing algorithm that can - * be used by an {@link Atlas}. The best algorithm to use - * will depend on the source dataset and the dimensions of - * the atlas. - */ - public enum Type { - SliceMinArea, - SliceMaxArea, - SliceShortAxis, - SliceLongAxis - } - - /** - * Represents a bitmap packed in the atlas. Each entry has a location in - * pixels in the atlas and a rotation flag. - */ - public static class Entry { - /** - * Location, in pixels, of the bitmap on the X axis in the atlas. - */ - public int x; - /** - * Location, in pixels, of the bitmap on the Y axis in the atlas. - */ - public int y; - } - - private final Policy mPolicy; - - /** - * Creates a new atlas with the specified algorithm and dimensions - * in pixels. Calling this constructor is equivalent to calling - * {@link #Atlas(Atlas.Type, int, int, int)} with {@link #FLAG_DEFAULTS}. - * - * @param type The algorithm to use to pack rectangles in the atlas - * @param width The width of the atlas in pixels - * @param height The height of the atlas in pixels - * - * @see #Atlas(Atlas.Type, int, int, int) - */ - public Atlas(Type type, int width, int height) { - this(type, width, height, FLAG_DEFAULTS); - } - - /** - * Creates a new atlas with the specified algorithm and dimensions - * in pixels. A set of flags can also be specified to control the - * behavior of the atlas. - * - * @param type The algorithm to use to pack rectangles in the atlas - * @param width The width of the atlas in pixels - * @param height The height of the atlas in pixels - * @param flags Optional flags to control the behavior of the atlas: - * {@link #FLAG_ADD_PADDING}, {@link #FLAG_ALLOW_ROTATIONS} - * - * @see #Atlas(Atlas.Type, int, int) - */ - public Atlas(Type type, int width, int height, int flags) { - mPolicy = findPolicy(type, width, height, flags); - } - - /** - * Packs a rectangle of the specified dimensions in this atlas. - * - * @param width The width of the rectangle to pack in the atlas - * @param height The height of the rectangle to pack in the atlas - * - * @return An {@link Entry} instance if the rectangle was packed in - * the atlas, or null if the rectangle could not fit - * - * @see #pack(int, int, Atlas.Entry) - */ - public Entry pack(int width, int height) { - return pack(width, height, null); - } - - /** - * Packs a rectangle of the specified dimensions in this atlas. - * - * @param width The width of the rectangle to pack in the atlas - * @param height The height of the rectangle to pack in the atlas - * @param entry Out parameter that will be filled in with the location - * and attributes of the packed rectangle, can be null - * - * @return An {@link Entry} instance if the rectangle was packed in - * the atlas, or null if the rectangle could not fit - * - * @see #pack(int, int) - */ - public Entry pack(int width, int height, Entry entry) { - if (entry == null) entry = new Entry(); - return mPolicy.pack(width, height, entry); - } - - private static Policy findPolicy(Type type, int width, int height, int flags) { - switch (type) { - case SliceMinArea: - return new SlicePolicy(width, height, flags, - new SlicePolicy.MinAreaSplitDecision()); - case SliceMaxArea: - return new SlicePolicy(width, height, flags, - new SlicePolicy.MaxAreaSplitDecision()); - case SliceShortAxis: - return new SlicePolicy(width, height, flags, - new SlicePolicy.ShorterFreeAxisSplitDecision()); - case SliceLongAxis: - return new SlicePolicy(width, height, flags, - new SlicePolicy.LongerFreeAxisSplitDecision()); - } - return null; - } - - /** - * A policy defines how the atlas performs the packing operation. - */ - private static abstract class Policy { - abstract Entry pack(int width, int height, Entry entry); - } - - /** - * The Slice algorightm divides the remaining empty space either - * horizontally or vertically after a bitmap is placed in the atlas. - * - * NOTE: the algorithm is explained below using a tree but is - * implemented using a linked list instead for performance reasons. - * - * The algorithm starts with a single empty cell covering the entire - * atlas: - * - * ----------------------- - * | | - * | | - * | | - * | Empty space | - * | (C0) | - * | | - * | | - * | | - * ----------------------- - * - * The tree of cells looks like this: - * - * N0(free) - * - * The algorithm then places a bitmap B1, if possible: - * - * ----------------------- - * | | | - * | B1 | | - * | | | - * |-------- | - * | | - * | | - * | | - * | | - * ----------------------- - * - * After placing a bitmap in an empty cell, the algorithm splits - * the remaining space in two new empty cells. The split can occur - * vertically or horizontally (this is controlled by the "split - * decision" parameter of the algorithm.) - * - * Here is for the instance the result of a vertical split: - * - * ----------------------- - * | | | - * | B1 | | - * | | | - * |--------| C2 | - * | | | - * | | | - * | C1 | | - * | | | - * ----------------------- - * - * The cells tree now looks like this: - * - * C0(occupied) - * / \ - * / \ - * / \ - * / \ - * C1(free) C2(free) - * - * For each bitmap to place in the atlas, the Slice algorithm - * will visit the free cells until it finds one where a bitmap can - * fit. It will then split the now occupied cell and proceed onto - * the next bitmap. - */ - private static class SlicePolicy extends Policy { - private final Cell mRoot = new Cell(); - - private final SplitDecision mSplitDecision; - - private final int mPadding; - - /** - * A cell represents a sub-rectangle of the atlas. A cell is - * a node in a linked list representing the available free - * space in the atlas. - */ - private static class Cell { - int x; - int y; - - int width; - int height; - - Cell next; - - @Override - public String toString() { - return String.format("cell[x=%d y=%d width=%d height=%d", x, y, width, height); - } - } - - SlicePolicy(int width, int height, int flags, SplitDecision splitDecision) { - mPadding = (flags & FLAG_ADD_PADDING) != 0 ? 1 : 0; - - // The entire atlas is empty at first, minus padding - Cell first = new Cell(); - first.x = first.y = mPadding; - first.width = width - 2 * mPadding; - first.height = height - 2 * mPadding; - - mRoot.next = first; - mSplitDecision = splitDecision; - } - - @Override - Entry pack(int width, int height, Entry entry) { - Cell cell = mRoot.next; - Cell prev = mRoot; - - while (cell != null) { - if (insert(cell, prev, width, height, entry)) { - return entry; - } - - prev = cell; - cell = cell.next; - } - - return null; - } - - /** - * Defines how the remaining empty space should be split up: - * vertically or horizontally. - */ - private static interface SplitDecision { - /** - * Returns true if the remaining space defined by - * freeWidth and freeHeight - * should be split horizontally. - * - * @param freeWidth The rectWidth of the free space after packing a rectangle - * @param freeHeight The rectHeight of the free space after packing a rectangle - * @param rectWidth The rectWidth of the rectangle that was packed in a cell - * @param rectHeight The rectHeight of the rectangle that was packed in a cell - */ - boolean splitHorizontal(int freeWidth, int freeHeight, - int rectWidth, int rectHeight); - } - - // Splits the free area horizontally to minimize the horizontal section area - private static class MinAreaSplitDecision implements SplitDecision { - @Override - public boolean splitHorizontal(int freeWidth, int freeHeight, - int rectWidth, int rectHeight) { - return rectWidth * freeHeight > freeWidth * rectHeight; - } - } - - // Splits the free area horizontally to maximize the horizontal section area - private static class MaxAreaSplitDecision implements SplitDecision { - @Override - public boolean splitHorizontal(int freeWidth, int freeHeight, - int rectWidth, int rectHeight) { - return rectWidth * freeHeight <= freeWidth * rectHeight; - } - } - - // Splits the free area horizontally if the horizontal axis is shorter - private static class ShorterFreeAxisSplitDecision implements SplitDecision { - @Override - public boolean splitHorizontal(int freeWidth, int freeHeight, - int rectWidth, int rectHeight) { - return freeWidth <= freeHeight; - } - } - - // Splits the free area horizontally if the vertical axis is shorter - private static class LongerFreeAxisSplitDecision implements SplitDecision { - @Override - public boolean splitHorizontal(int freeWidth, int freeHeight, - int rectWidth, int rectHeight) { - return freeWidth > freeHeight; - } - } - - /** - * Attempts to pack a rectangle of specified dimensions in the available - * empty space. - * - * @param cell The cell representing free space in which to pack the rectangle - * @param prev The previous cell in the free space linked list - * @param width The width of the rectangle to pack - * @param height The height of the rectangle to pack - * @param entry Stores the location of the packged rectangle, if it fits - * - * @return True if the rectangle was packed in the atlas, false otherwise - */ - private boolean insert(Cell cell, Cell prev, int width, int height, Entry entry) { - if (cell.width < width || cell.height < height) { - return false; - } - - // Remaining free space after packing the rectangle - int deltaWidth = cell.width - width; - int deltaHeight = cell.height - height; - - // Split the remaining free space into two new cells - Cell first = new Cell(); - Cell second = new Cell(); - - first.x = cell.x + width + mPadding; - first.y = cell.y; - first.width = deltaWidth - mPadding; - - second.x = cell.x; - second.y = cell.y + height + mPadding; - second.height = deltaHeight - mPadding; - - if (mSplitDecision.splitHorizontal(deltaWidth, deltaHeight, - width, height)) { - first.height = height; - second.width = cell.width; - } else { - first.height = cell.height; - second.width = width; - - // The order of the cells matters for efficient packing - // We want to give priority to the cell chosen by the - // split decision heuristic - Cell temp = first; - first = second; - second = temp; - } - - // Remove degenerate cases to keep the free list as small as possible - if (first.width > 0 && first.height > 0) { - prev.next = first; - prev = first; - } - - if (second.width > 0 && second.height > 0) { - prev.next = second; - second.next = cell.next; - } else { - prev.next = cell.next; - } - - // The cell is now completely removed from the free list - cell.next = null; - - // Return the location and rotation of the packed rectangle - entry.x = cell.x; - entry.y = cell.y; - - return true; - } - } -} diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 7ce750d6e000..f7082920a018 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -1654,16 +1654,6 @@ public final class Bitmap implements Parcelable { nativePrepareToDraw(mNativePtr); } - /** - * Refs the underlying SkPixelRef and returns a pointer to it. - * - * @hide - * */ - public final long refSkPixelRef() { - checkRecycled("Can't refSkPixelRef on a recycled bitmap!"); - return nativeRefPixelRef(mNativePtr); - } - //////////// native methods private static native Bitmap nativeCreate(int[] colors, int offset, @@ -1720,7 +1710,6 @@ public final class Bitmap implements Parcelable { private static native boolean nativeHasMipMap(long nativeBitmap); private static native void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap); private static native boolean nativeSameAs(long nativeBitmap0, long nativeBitmap1); - private static native long nativeRefPixelRef(long nativeBitmap); private static native void nativePrepareToDraw(long nativeBitmap); private static native int nativeGetAllocationByteCount(long nativeBitmap); } diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java index df107f5ce3ef..6deeb0d9af9a 100644 --- a/graphics/java/android/graphics/drawable/BitmapDrawable.java +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -49,7 +49,6 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; -import java.util.Collection; /** * A Drawable that wraps a bitmap and can be tiled, stretched, or aligned. You can create a @@ -957,14 +956,6 @@ public class BitmapDrawable extends Drawable { } @Override - public int addAtlasableBitmaps(Collection atlasList) { - if (isAtlasable(mBitmap) && atlasList.add(mBitmap)) { - return mBitmap.getWidth() * mBitmap.getHeight(); - } - return 0; - } - - @Override public Drawable newDrawable() { return new BitmapDrawable(this, null); } diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index 10f0dda5a976..6ddc2d7e192f 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -58,7 +58,6 @@ import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; import java.util.Arrays; -import java.util.Collection; /** * A Drawable is a general abstraction for "something that can be drawn." Most @@ -1367,19 +1366,6 @@ public abstract class Drawable { public abstract @Config int getChangingConfigurations(); /** - * @return Total pixel count - * @hide - */ - public int addAtlasableBitmaps(@NonNull Collection atlasList) { - return 0; - } - - /** @hide */ - protected final boolean isAtlasable(@Nullable Bitmap bitmap) { - return bitmap != null && bitmap.getConfig() == Bitmap.Config.ARGB_8888; - } - - /** * Return whether this constant state can have a theme applied. */ public boolean canApplyTheme() { diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java index c7a3c75f3545..abdc2b91c2d9 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -21,7 +21,6 @@ import android.content.pm.ActivityInfo.Config; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.Theme; -import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Insets; @@ -35,8 +34,6 @@ import android.util.LayoutDirection; import android.util.SparseArray; import android.view.View; -import java.util.Collection; - /** * A helper class that contains several {@link Drawable}s and selects which one to use. * @@ -1194,19 +1191,6 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { return true; } - /** @hide */ - @Override - public int addAtlasableBitmaps(Collection atlasList) { - final int N = mNumChildren; - int pixelCount = 0; - for (int i = 0; i < N; i++) { - final ConstantState state = getChild(i).getConstantState(); - if (state != null) { - pixelCount += state.addAtlasableBitmaps(atlasList); - } - } - return pixelCount; - } } protected void setConstantState(DrawableContainerState state) { diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java index 5abfc54bfce3..5887939b05a4 100644 --- a/graphics/java/android/graphics/drawable/DrawableWrapper.java +++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java @@ -28,7 +28,6 @@ import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; -import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Insets; @@ -41,7 +40,6 @@ import android.util.DisplayMetrics; import android.view.View; import java.io.IOException; -import java.util.Collection; /** * Drawable container with only one child element. @@ -508,15 +506,6 @@ public abstract class DrawableWrapper extends Drawable implements Drawable.Callb } @Override - public int addAtlasableBitmaps(Collection atlasList) { - final Drawable.ConstantState state = mDrawableState; - if (state != null) { - return state.addAtlasableBitmaps(atlasList); - } - return 0; - } - - @Override public Drawable newDrawable() { return newDrawable(null); } diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java index c30c4c2f02d6..e09fea5af3ea 100644 --- a/graphics/java/android/graphics/drawable/LayerDrawable.java +++ b/graphics/java/android/graphics/drawable/LayerDrawable.java @@ -23,7 +23,6 @@ import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; -import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Outline; @@ -42,7 +41,6 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; -import java.util.Collection; /** * A Drawable that manages an array of other Drawables. These are drawn in array @@ -2128,22 +2126,6 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { mHaveIsStateful = false; } - @Override - public int addAtlasableBitmaps(Collection atlasList) { - final ChildDrawable[] array = mChildren; - final int N = mNum; - int pixelCount = 0; - for (int i = 0; i < N; i++) { - final Drawable dr = array[i].mDrawable; - if (dr != null) { - final ConstantState state = dr.getConstantState(); - if (state != null) { - pixelCount += state.addAtlasableBitmaps(atlasList); - } - } - } - return pixelCount; - } } } diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java index d96238521f94..c7183d9e8f50 100644 --- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java +++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java @@ -49,7 +49,6 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.InputStream; -import java.util.Collection; /** * @@ -633,15 +632,6 @@ public class NinePatchDrawable extends Drawable { } @Override - public int addAtlasableBitmaps(Collection atlasList) { - final Bitmap bitmap = mNinePatch.getBitmap(); - if (isAtlasable(bitmap) && atlasList.add(bitmap)) { - return bitmap.getWidth() * bitmap.getHeight(); - } - return 0; - } - - @Override public Drawable newDrawable() { return new NinePatchDrawable(this, null); } diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 81a183126cce..2d9b764f5504 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -45,7 +45,6 @@ hwui_src_files := \ AnimationContext.cpp \ Animator.cpp \ AnimatorManager.cpp \ - AssetAtlas.cpp \ BakedOpDispatcher.cpp \ BakedOpRenderer.cpp \ BakedOpState.cpp \ @@ -56,7 +55,6 @@ hwui_src_files := \ DeferredLayerUpdater.cpp \ DeviceInfo.cpp \ DisplayList.cpp \ - Dither.cpp \ Extensions.cpp \ FboCache.cpp \ FontRenderer.cpp \ @@ -130,6 +128,14 @@ ifeq ($(TARGET_USES_HWC2),true) hwui_cflags += -DUSE_HWC2 endif +# TODO: Linear blending should be enabled by default, but we are +# TODO: making it an opt-in while it's a work in progress +# TODO: The final test should be: +# TODO: ifneq ($(TARGET_ENABLE_LINEAR_BLENDING),false) +ifeq ($(TARGET_ENABLE_LINEAR_BLENDING),true) + hwui_cflags += -DANDROID_ENABLE_LINEAR_BLENDING +endif + # GCC false-positives on this warning, and since we -Werror that's # a problem hwui_cflags += -Wno-free-nonheap-object @@ -143,7 +149,6 @@ ifeq (true, $(BUGREPORT_FONT_CACHE_USAGE)) hwui_cflags += -DBUGREPORT_FONT_CACHE_USAGE endif - ifndef HWUI_COMPILE_SYMBOLS hwui_cflags += -fvisibility=hidden endif diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp deleted file mode 100644 index e2e7037202b8..000000000000 --- a/libs/hwui/AssetAtlas.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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 "AssetAtlas.h" -#include "Caches.h" -#include "Image.h" - -#include - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Lifecycle -/////////////////////////////////////////////////////////////////////////////// - -void AssetAtlas::init(const sp& buffer, int64_t* map, int count) { - if (mImage) { - return; - } - - ATRACE_NAME("AssetAtlas::init"); - - mImage = new Image(buffer); - if (mImage->getTexture()) { - if (!mTexture) { - Caches& caches = Caches::getInstance(); - mTexture = new Texture(caches); - mTexture->wrap(mImage->getTexture(), - buffer->getWidth(), buffer->getHeight(), GL_RGBA); - createEntries(caches, map, count); - } - } else { - ALOGW("Could not create atlas image"); - terminate(); - } -} - -void AssetAtlas::terminate() { - delete mImage; - mImage = nullptr; - delete mTexture; - mTexture = nullptr; - mEntries.clear(); -} - -/////////////////////////////////////////////////////////////////////////////// -// Entries -/////////////////////////////////////////////////////////////////////////////// - -AssetAtlas::Entry* AssetAtlas::getEntry(const SkPixelRef* pixelRef) const { - auto result = mEntries.find(pixelRef); - return result != mEntries.end() ? result->second.get() : nullptr; -} - -Texture* AssetAtlas::getEntryTexture(const SkPixelRef* pixelRef) const { - auto result = mEntries.find(pixelRef); - return result != mEntries.end() ? result->second->texture : nullptr; -} - -/** - * Delegates changes to wrapping and filtering to the base atlas texture - * instead of applying the changes to the virtual textures. - */ -struct DelegateTexture: public Texture { - DelegateTexture(Caches& caches, Texture* delegate) - : Texture(caches), mDelegate(delegate) { } - - virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false, - bool force = false, GLenum renderTarget = GL_TEXTURE_2D) override { - mDelegate->setWrapST(wrapS, wrapT, bindTexture, force, renderTarget); - } - - virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false, - bool force = false, GLenum renderTarget = GL_TEXTURE_2D) override { - mDelegate->setFilterMinMag(min, mag, bindTexture, force, renderTarget); - } - -private: - Texture* const mDelegate; -}; // struct DelegateTexture - -void AssetAtlas::createEntries(Caches& caches, int64_t* map, int count) { - const float width = float(mTexture->width()); - const float height = float(mTexture->height()); - - for (int i = 0; i < count; ) { - SkPixelRef* pixelRef = reinterpret_cast(map[i++]); - // NOTE: We're converting from 64 bit signed values to 32 bit - // signed values. This is guaranteed to be safe because the "x" - // and "y" coordinate values are guaranteed to be representable - // with 32 bits. The array is 64 bits wide so that it can carry - // pointers on 64 bit architectures. - const int x = static_cast(map[i++]); - const int y = static_cast(map[i++]); - - // Bitmaps should never be null, we're just extra paranoid - if (!pixelRef) continue; - - const UvMapper mapper( - x / width, (x + pixelRef->info().width()) / width, - y / height, (y + pixelRef->info().height()) / height); - - Texture* texture = new DelegateTexture(caches, mTexture); - texture->blend = !SkAlphaTypeIsOpaque(pixelRef->info().alphaType()); - texture->wrap(mTexture->id(), pixelRef->info().width(), - pixelRef->info().height(), mTexture->format()); - - std::unique_ptr entry(new Entry(pixelRef, texture, mapper, *this)); - texture->uvMapper = &entry->uvMapper; - - mEntries.emplace(entry->pixelRef, std::move(entry)); - } -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h deleted file mode 100644 index b32e51851b94..000000000000 --- a/libs/hwui/AssetAtlas.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_HWUI_ASSET_ATLAS_H -#define ANDROID_HWUI_ASSET_ATLAS_H - -#include "Texture.h" -#include "UvMapper.h" - -#include -#include -#include -#include - -#include -#include - -namespace android { -namespace uirenderer { - -class Caches; -class Image; - -/** - * An asset atlas holds a collection of framework bitmaps in a single OpenGL - * texture. Each bitmap is associated with a location, defined in pixels, - * inside the atlas. The atlas is generated by the framework and bound as - * an external texture using the EGLImageKHR extension. - */ -class AssetAtlas { -public: - /** - * Entry representing the texture and uvMapper of a PixelRef in the - * atlas - */ - class Entry { - public: - /* - * A "virtual texture" object that represents the texture - * this entry belongs to. This texture should never be - * modified. - */ - Texture* texture; - - /** - * Maps texture coordinates in the [0..1] range into the - * correct range to sample this entry from the atlas. - */ - const UvMapper uvMapper; - - /** - * Unique identifier used to merge bitmaps and 9-patches stored - * in the atlas. - */ - const void* getMergeId() const { - return texture->blend ? &atlas.mBlendKey : &atlas.mOpaqueKey; - } - - ~Entry() { - delete texture; - } - - private: - /** - * The pixel ref that generated this atlas entry. - */ - SkPixelRef* pixelRef; - - /** - * Atlas this entry belongs to. - */ - const AssetAtlas& atlas; - - Entry(SkPixelRef* pixelRef, Texture* texture, const UvMapper& mapper, - const AssetAtlas& atlas) - : texture(texture) - , uvMapper(mapper) - , pixelRef(pixelRef) - , atlas(atlas) { - } - - friend class AssetAtlas; - }; - - AssetAtlas(): mTexture(nullptr), mImage(nullptr), - mBlendKey(true), mOpaqueKey(false) { } - ~AssetAtlas() { terminate(); } - - /** - * Initializes the atlas with the specified buffer and - * map. The buffer is a gralloc'd texture that will be - * used as an EGLImage. The map is a list of SkBitmap* - * and their (x, y) positions - * - * This method returns immediately if the atlas is already - * initialized. To re-initialize the atlas, you must - * first call terminate(). - */ - ANDROID_API void init(const sp& buffer, int64_t* map, int count); - - /** - * Destroys the atlas texture. This object can be - * re-initialized after calling this method. - * - * After calling this method, the width, height - * and texture are set to 0. - */ - void terminate(); - - /** - * Returns the width of this atlas in pixels. - * Can return 0 if the atlas is not initialized. - */ - uint32_t getWidth() const { - return mTexture ? mTexture->width() : 0; - } - - /** - * Returns the height of this atlas in pixels. - * Can return 0 if the atlas is not initialized. - */ - uint32_t getHeight() const { - return mTexture ? mTexture->height() : 0; - } - - /** - * Returns the OpenGL name of the texture backing this atlas. - * Can return 0 if the atlas is not initialized. - */ - GLuint getTexture() const { - return mTexture ? mTexture->id() : 0; - } - - /** - * Returns the entry in the atlas associated with the specified - * pixelRef. If the pixelRef is not in the atlas, return NULL. - */ - Entry* getEntry(const SkPixelRef* pixelRef) const; - - /** - * Returns the texture for the atlas entry associated with the - * specified pixelRef. If the pixelRef is not in the atlas, return NULL. - */ - Texture* getEntryTexture(const SkPixelRef* pixelRef) const; - -private: - void createEntries(Caches& caches, int64_t* map, int count); - - Texture* mTexture; - Image* mImage; - - const bool mBlendKey; - const bool mOpaqueKey; - - std::unordered_map> mEntries; -}; // class AssetAtlas - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_ASSET_ATLAS_H diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp index 8b3f1722dddf..699503991e02 100644 --- a/libs/hwui/BakedOpDispatcher.cpp +++ b/libs/hwui/BakedOpDispatcher.cpp @@ -35,11 +35,11 @@ namespace android { namespace uirenderer { -static void storeTexturedRect(TextureVertex* vertices, const Rect& bounds, const Rect& texCoord) { - vertices[0] = { bounds.left, bounds.top, texCoord.left, texCoord.top }; - vertices[1] = { bounds.right, bounds.top, texCoord.right, texCoord.top }; - vertices[2] = { bounds.left, bounds.bottom, texCoord.left, texCoord.bottom }; - vertices[3] = { bounds.right, bounds.bottom, texCoord.right, texCoord.bottom }; +static void storeTexturedRect(TextureVertex* vertices, const Rect& bounds) { + vertices[0] = { bounds.left, bounds.top, 0, 0 }; + vertices[1] = { bounds.right, bounds.top, 1, 0 }; + vertices[2] = { bounds.left, bounds.bottom, 0, 1 }; + vertices[3] = { bounds.right, bounds.bottom, 1, 1 }; } void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, @@ -48,16 +48,11 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, const BakedOpState& firstState = *(opList.states[0]); const SkBitmap* bitmap = (static_cast(opList.states[0]->op))->bitmap; - AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(bitmap->pixelRef()); - Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(bitmap); + Texture* texture = renderer.caches().textureCache.get(bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); TextureVertex vertices[opList.count * 4]; - Rect texCoords(0, 0, 1, 1); - if (entry) { - entry->uvMapper.map(texCoords); - } for (size_t i = 0; i < opList.count; i++) { const BakedOpState& state = *(opList.states[i]); TextureVertex* rectVerts = &vertices[i * 4]; @@ -69,7 +64,7 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, // pure translate, so snap (same behavior as onBitmapOp) opBounds.snapToPixelBoundaries(); } - storeTexturedRect(rectVerts, opBounds, texCoords); + storeTexturedRect(rectVerts, opBounds); renderer.dirtyRenderTarget(opBounds); } @@ -92,8 +87,6 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, const MergedBakedOpList& opList) { const PatchOp& firstOp = *(static_cast(opList.states[0]->op)); const BakedOpState& firstState = *(opList.states[0]); - AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry( - firstOp.bitmap->pixelRef()); // Batches will usually contain a small number of items so it's // worth performing a first iteration to count the exact number @@ -105,7 +98,7 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, // TODO: cache mesh lookups const Patch* opMesh = renderer.caches().patchCache.get( - entry, op.bitmap->width(), op.bitmap->height(), + op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); totalVertices += opMesh->verticesCount; } @@ -126,7 +119,7 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, // TODO: cache mesh lookups const Patch* opMesh = renderer.caches().patchCache.get( - entry, op.bitmap->width(), op.bitmap->height(), + op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); @@ -172,7 +165,7 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, } - Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(firstOp.bitmap); + Texture* texture = renderer.caches().textureCache.get(firstOp.bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); @@ -442,7 +435,12 @@ void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op } void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, const BakedOpState& state) { - const static UvMapper defaultUvMapper; + Texture* texture = renderer.caches().textureCache.get(op.bitmap); + if (!texture) { + return; + } + const AutoTexture autoCleanup(texture); + const uint32_t elementCount = op.meshWidth * op.meshHeight * 6; std::unique_ptr mesh(new ColorTextureVertex[elementCount]); @@ -457,9 +455,6 @@ void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMe colors = tempColors.get(); } - Texture* texture = renderer.renderState().assetAtlas().getEntryTexture(op.bitmap->pixelRef()); - const UvMapper& mapper(texture && texture->uvMapper ? *texture->uvMapper : defaultUvMapper); - for (int32_t y = 0; y < op.meshHeight; y++) { for (int32_t x = 0; x < op.meshWidth; x++) { uint32_t i = (y * (op.meshWidth + 1) + x) * 2; @@ -469,8 +464,6 @@ void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMe float v1 = float(y) / op.meshHeight; float v2 = float(y + 1) / op.meshHeight; - mapper.map(u1, v1, u2, v2); - int ax = i + (op.meshWidth + 1) * 2; int ay = ax + 1; int bx = i; @@ -491,14 +484,6 @@ void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMe } } - if (!texture) { - texture = renderer.caches().textureCache.get(op.bitmap); - if (!texture) { - return; - } - } - const AutoTexture autoCleanup(texture); - /* * TODO: handle alpha_8 textures correctly by applying paint color, but *not* * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh. @@ -599,12 +584,11 @@ void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, } // TODO: avoid redoing the below work each frame: - AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(op.bitmap->pixelRef()); const Patch* mesh = renderer.caches().patchCache.get( - entry, op.bitmap->width(), op.bitmap->height(), + op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); - Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap); + Texture* texture = renderer.caches().textureCache.get(op.bitmap); if (CC_LIKELY(texture)) { const AutoTexture autoCleanup(texture); Glop glop; diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp index 6db345ac0006..ac7a600af85f 100644 --- a/libs/hwui/BakedOpRenderer.cpp +++ b/libs/hwui/BakedOpRenderer.cpp @@ -182,11 +182,7 @@ void BakedOpRenderer::clearColorBuffer(const Rect& rect) { } Texture* BakedOpRenderer::getTexture(const SkBitmap* bitmap) { - Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap->pixelRef()); - if (!texture) { - return mCaches.textureCache.get(bitmap); - } - return texture; + return mCaches.textureCache.get(bitmap); } void BakedOpRenderer::drawRects(const float* rects, int count, const SkPaint* paint) { diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index 741cdccaa778..b463e45fb39b 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -53,7 +53,6 @@ Caches::Caches(RenderState& renderState) : gradientCache(mExtensions) , patchCache(renderState) , programCache(mExtensions) - , dither(*this) , mRenderState(&renderState) , mInitialized(false) { INIT_LOGD("Creating OpenGL renderer caches"); @@ -238,7 +237,6 @@ void Caches::flush(FlushMode mode) { gradientCache.clear(); fontRenderer.clear(); fboCache.clear(); - dither.clear(); // fall through case FlushMode::Moderate: fontRenderer.flush(); diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 344ee71a2cc5..7c2e78c7d3b8 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -16,8 +16,6 @@ #pragma once -#include "AssetAtlas.h" -#include "Dither.h" #include "Extensions.h" #include "FboCache.h" #include "GammaFontRenderer.h" @@ -130,6 +128,15 @@ public: TextureVertex* getRegionMesh(); /** + * Returns the GL RGBA internal format to use for the current device + * If the device supports linear blending and needSRGB is true, + * this function returns GL_SRGB8_ALPHA8, otherwise it returns GL_RGBA + */ + constexpr GLint rgbaInternalFormat(bool needSRGB = true) const { + return extensions().hasSRGB() && needSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA; + } + + /** * Displays the memory usage of each cache and the total sum. */ void dumpMemoryUsage(); @@ -157,8 +164,6 @@ public: TaskManager tasks; - Dither dither; - bool gpuPixelBuffersEnabled; // Debug methods @@ -169,7 +174,7 @@ public: void setProgram(const ProgramDescription& description); void setProgram(Program* program); - Extensions& extensions() { return mExtensions; } + const Extensions& extensions() const { return mExtensions; } Program& program() { return *mProgram; } PixelBufferState& pixelBufferState() { return *mPixelBufferState; } TextureState& textureState() { return *mTextureState; } diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp deleted file mode 100644 index ec2013e27401..000000000000 --- a/libs/hwui/Dither.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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. - */ - -#include "Caches.h" -#include "Dither.h" - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Lifecycle -/////////////////////////////////////////////////////////////////////////////// - -Dither::Dither(Caches& caches) - : mCaches(caches) - , mInitialized(false) - , mDitherTexture(0) { -} - -void Dither::bindDitherTexture() { - if (!mInitialized) { - glGenTextures(1, &mDitherTexture); - mCaches.textureState().bindTexture(mDitherTexture); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - - if (mCaches.extensions().hasFloatTextures()) { - // We use a R16F texture, let's remap the alpha channel to the - // red channel to avoid changing the shader sampling code on GL ES 3.0+ - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED); - - float dither = 1.0f / (255.0f * DITHER_KERNEL_SIZE * DITHER_KERNEL_SIZE); - const GLfloat pattern[] = { - 0 * dither, 8 * dither, 2 * dither, 10 * dither, - 12 * dither, 4 * dither, 14 * dither, 6 * dither, - 3 * dither, 11 * dither, 1 * dither, 9 * dither, - 15 * dither, 7 * dither, 13 * dither, 5 * dither - }; - - glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0, - GL_RED, GL_FLOAT, &pattern); - } else { - const uint8_t pattern[] = { - 0, 8, 2, 10, - 12, 4, 14, 6, - 3, 11, 1, 9, - 15, 7, 13, 5 - }; - - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0, - GL_ALPHA, GL_UNSIGNED_BYTE, &pattern); - } - - mInitialized = true; - } else { - mCaches.textureState().bindTexture(mDitherTexture); - } -} - -void Dither::clear() { - if (mInitialized) { - mCaches.textureState().deleteTexture(mDitherTexture); - mInitialized = false; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Program management -/////////////////////////////////////////////////////////////////////////////// - -void Dither::setupProgram(Program& program, GLuint* textureUnit) { - GLuint textureSlot = (*textureUnit)++; - mCaches.textureState().activateTexture(textureSlot); - - bindDitherTexture(); - - glUniform1i(program.getUniform("ditherSampler"), textureSlot); -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h deleted file mode 100644 index 6af3e8384472..000000000000 --- a/libs/hwui/Dither.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_HWUI_DITHER_H -#define ANDROID_HWUI_DITHER_H - -#include - -namespace android { -namespace uirenderer { - -class Caches; -class Extensions; -class Program; - -// Must be a power of two -#define DITHER_KERNEL_SIZE 4 -// These must not use the .0f notation as they are used from GLSL -#define DITHER_KERNEL_SIZE_INV (1.0 / 4.0) -#define DITHER_KERNEL_SIZE_INV_SQUARE (1.0 / 16.0) - -/** - * Handles dithering for programs. - */ -class Dither { -public: - explicit Dither(Caches& caches); - - void clear(); - void setupProgram(Program& program, GLuint* textureUnit); - -private: - void bindDitherTexture(); - - Caches& mCaches; - bool mInitialized; - GLuint mDitherTexture; -}; - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_DITHER_H diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp index 02caaa49e99c..4dc7536d60bc 100644 --- a/libs/hwui/Extensions.cpp +++ b/libs/hwui/Extensions.cpp @@ -22,6 +22,7 @@ #include #include + #include namespace android { @@ -44,6 +45,14 @@ Extensions::Extensions() { mHas1BitStencil = extensions.has("GL_OES_stencil1"); mHas4BitStencil = extensions.has("GL_OES_stencil4"); mHasUnpackSubImage = extensions.has("GL_EXT_unpack_subimage"); + mHasSRGB = extensions.has("GL_EXT_sRGB"); + mHasSRGBWriteControl = extensions.has("GL_EXT_sRGB_write_control"); + + // If linear blending is enabled, the device must have ES3.0 and GL_EXT_sRGB_write_control +#ifdef ANDROID_ENABLE_LINEAR_BLENDING + assert(mVersionMajor >= 3 || mHasSRGB); + assert(mHasSRGBWriteControl); +#endif const char* version = (const char*) glGetString(GL_VERSION); diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h index 67cc747015e0..cc73c2317720 100644 --- a/libs/hwui/Extensions.h +++ b/libs/hwui/Extensions.h @@ -43,6 +43,8 @@ public: inline bool hasPixelBufferObjects() const { return mVersionMajor >= 3; } inline bool hasOcclusionQueries() const { return mVersionMajor >= 3; } inline bool hasFloatTextures() const { return mVersionMajor >= 3; } + inline bool hasSRGB() const { return mVersionMajor >= 3 || mHasSRGB; } + inline bool hasSRGBWriteControl() const { return hasSRGB() && mHasSRGBWriteControl; } inline int getMajorGlVersion() const { return mVersionMajor; } inline int getMinorGlVersion() const { return mVersionMinor; } @@ -55,6 +57,8 @@ private: bool mHas1BitStencil; bool mHas4BitStencil; bool mHasUnpackSubImage; + bool mHasSRGB; + bool mHasSRGBWriteControl; int mVersionMajor; int mVersionMinor; diff --git a/libs/hwui/FloatColor.h b/libs/hwui/FloatColor.h index 9a39ec28aa3d..6d19b7ccdd79 100644 --- a/libs/hwui/FloatColor.h +++ b/libs/hwui/FloatColor.h @@ -16,6 +16,7 @@ #ifndef FLOATCOLOR_H #define FLOATCOLOR_H +#include "utils/Color.h" #include "utils/Macros.h" #include "utils/MathUtils.h" @@ -25,11 +26,13 @@ namespace android { namespace uirenderer { struct FloatColor { + // "color" is a gamma-encoded sRGB color + // After calling this method, the color is stored as a pre-multiplied linear color void set(uint32_t color) { a = ((color >> 24) & 0xff) / 255.0f; - r = a * ((color >> 16) & 0xff) / 255.0f; - g = a * ((color >> 8) & 0xff) / 255.0f; - b = a * ((color ) & 0xff) / 255.0f; + r = a * EOCF_sRGB(((color >> 16) & 0xff) / 255.0f); + g = a * EOCF_sRGB(((color >> 8) & 0xff) / 255.0f); + b = a * EOCF_sRGB(((color ) & 0xff) / 255.0f); } bool isNotBlack() { diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 9b60dfcb5867..effc65ea967f 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -60,11 +60,17 @@ void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) { } int transformFlags = pureTranslate ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None; +#ifdef ANDROID_ENABLE_LINEAR_BLENDING + bool gammaCorrection = true; +#else + bool gammaCorrection = false; +#endif Glop glop; GlopBuilder(renderer->renderState(), renderer->caches(), &glop) .setRoundRectClipState(bakedState->roundRectClipState) .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount()) .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha) + .setGammaCorrection(gammaCorrection) .setTransform(bakedState->computedState.transform, transformFlags) .setModelViewIdentityEmptyBounds() .build(); @@ -287,24 +293,23 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp // Copy the glyph image, taking the mask format into account switch (format) { case SkMask::kA8_Format: { - uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE; // write leading border line memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); // write glyph data if (mGammaTable) { - for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { + for (uint32_t cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { row = cacheY * cacheWidth; cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0; - for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { + for (uint32_t cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { uint8_t tempCol = bitmapBuffer[bY + bX]; cacheBuffer[row + cacheX] = mGammaTable[tempCol]; } cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0; } } else { - for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { + for (uint32_t cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { row = cacheY * cacheWidth; memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth); cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0; diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp index be4fdac5f800..17ad0e36fa90 100644 --- a/libs/hwui/FrameBuilder.cpp +++ b/libs/hwui/FrameBuilder.cpp @@ -612,7 +612,6 @@ void FrameBuilder::deferBitmapOp(const BitmapOp& op) { && op.bitmap->colorType() != kAlpha_8_SkColorType && hasMergeableClip(*bakedState)) { mergeid_t mergeId = reinterpret_cast(op.bitmap->getGenerationID()); - // TODO: AssetAtlas in mergeId currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::Bitmap, mergeId); } else { currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); @@ -687,7 +686,6 @@ void FrameBuilder::deferPatchOp(const PatchOp& op) { && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode && hasMergeableClip(*bakedState)) { mergeid_t mergeId = reinterpret_cast(op.bitmap->getGenerationID()); - // TODO: AssetAtlas in mergeId // Only use the MergedPatch batchId when merged, so Bitmap+Patch don't try to merge together currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::MergedPatch, mergeId); diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp index 96cac86386b5..8aff0a24b4f0 100644 --- a/libs/hwui/GammaFontRenderer.cpp +++ b/libs/hwui/GammaFontRenderer.cpp @@ -24,12 +24,13 @@ namespace uirenderer { GammaFontRenderer::GammaFontRenderer() { INIT_LOGD("Creating lookup gamma font renderer"); +#ifndef ANDROID_ENABLE_LINEAR_BLENDING // Compute the gamma tables const float gamma = 1.0f / Properties::textGamma; - for (uint32_t i = 0; i <= 255; i++) { mGammaTable[i] = uint8_t((float)::floor(pow(i / 255.0f, gamma) * 255.0f + 0.5f)); } +#endif } void GammaFontRenderer::endPrecaching() { diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h index bd27a1a72060..c9cf69bc7d9f 100644 --- a/libs/hwui/GammaFontRenderer.h +++ b/libs/hwui/GammaFontRenderer.h @@ -18,11 +18,6 @@ #define ANDROID_HWUI_GAMMA_FONT_RENDERER_H #include "FontRenderer.h" -#include "Program.h" - -#include - -#include namespace android { namespace uirenderer { @@ -43,7 +38,11 @@ public: FontRenderer& getFontRenderer() { if (!mRenderer) { - mRenderer.reset(new FontRenderer(&mGammaTable[0])); + const uint8_t* table = nullptr; +#ifndef ANDROID_ENABLE_LINEAR_BLENDING + table = &mGammaTable[0]; +#endif + mRenderer.reset(new FontRenderer(table)); } return *mRenderer; } @@ -64,7 +63,9 @@ public: private: std::unique_ptr mRenderer; +#ifndef ANDROID_ENABLE_LINEAR_BLENDING uint8_t mGammaTable[256]; +#endif }; }; // namespace uirenderer diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp index 1091736ab9c3..ff88d020030c 100644 --- a/libs/hwui/GlopBuilder.cpp +++ b/libs/hwui/GlopBuilder.cpp @@ -223,16 +223,16 @@ void GlopBuilder::setFill(int color, float alphaScale, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage, const SkShader* shader, const SkColorFilter* colorFilter) { if (mode != SkXfermode::kClear_Mode) { - float alpha = (SkColorGetA(color) / 255.0f) * alphaScale; if (!shader) { - float colorScale = alpha / 255.0f; - mOutGlop->fill.color = { - colorScale * SkColorGetR(color), - colorScale * SkColorGetG(color), - colorScale * SkColorGetB(color), - alpha - }; + FloatColor c; + c.set(color); + c.r *= alphaScale; + c.g *= alphaScale; + c.b *= alphaScale; + c.a *= alphaScale; + mOutGlop->fill.color = c; } else { + float alpha = (SkColorGetA(color) / 255.0f) * alphaScale; mOutGlop->fill.color = { 1, 1, 1, alpha }; } } else { @@ -276,15 +276,7 @@ void GlopBuilder::setFill(int color, float alphaScale, if (colorFilter->asColorMode(&color, &mode)) { mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Blend; mDescription.colorMode = mode; - - const float alpha = SkColorGetA(color) / 255.0f; - float colorScale = alpha / 255.0f; - mOutGlop->fill.filter.color = { - colorScale * SkColorGetR(color), - colorScale * SkColorGetG(color), - colorScale * SkColorGetB(color), - alpha, - }; + mOutGlop->fill.filter.color.set(color); } else if (colorFilter->asColorMatrix(srcColorMatrix)) { mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Matrix; @@ -297,10 +289,10 @@ void GlopBuilder::setFill(int color, float alphaScale, // Skia uses the range [0..255] for the addition vector, but we need // the [0..1] range to apply the vector in GLSL float* colorVector = mOutGlop->fill.filter.matrix.vector; - colorVector[0] = srcColorMatrix[4] / 255.0f; - colorVector[1] = srcColorMatrix[9] / 255.0f; - colorVector[2] = srcColorMatrix[14] / 255.0f; - colorVector[3] = srcColorMatrix[19] / 255.0f; + colorVector[0] = EOCF_sRGB(srcColorMatrix[4] / 255.0f); + colorVector[1] = EOCF_sRGB(srcColorMatrix[9] / 255.0f); + colorVector[2] = EOCF_sRGB(srcColorMatrix[14] / 255.0f); + colorVector[3] = EOCF_sRGB(srcColorMatrix[19] / 255.0f); } else { LOG_ALWAYS_FATAL("unsupported ColorFilter"); } @@ -481,6 +473,13 @@ GlopBuilder& GlopBuilder::setFillExternalTexture(Texture& texture, Matrix4& text return *this; } +GlopBuilder& GlopBuilder::setGammaCorrection(bool enabled) { + REQUIRE_STAGES(kFillStage); + + mDescription.hasGammaCorrection = enabled; + return *this; +} + //////////////////////////////////////////////////////////////////////////////// // Transform //////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h index 11524615074e..1f3b53abdb29 100644 --- a/libs/hwui/GlopBuilder.h +++ b/libs/hwui/GlopBuilder.h @@ -105,6 +105,8 @@ public: GlopBuilder& setRoundRectClipState(const RoundRectClipState* roundRectClipState); + GlopBuilder& setGammaCorrection(bool enabled); + void build(); static void dump(const Glop& glop); diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index c8f5e9435594..8573ab078aaf 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -67,7 +67,8 @@ GradientCache::GradientCache(Extensions& extensions) , mSize(0) , mMaxSize(Properties::gradientCacheSize) , mUseFloatTexture(extensions.hasFloatTextures()) - , mHasNpot(extensions.hasNPot()){ + , mHasNpot(extensions.hasNPot()) + , mHasSRGB(extensions.hasSRGB()) { glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); mCache.setOnEntryRemovedListener(this); @@ -176,71 +177,50 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, size_t GradientCache::bytesPerPixel() const { // We use 4 channels (RGBA) - return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t)); -} - -void GradientCache::splitToBytes(uint32_t inColor, GradientColor& outColor) const { - outColor.r = (inColor >> 16) & 0xff; - outColor.g = (inColor >> 8) & 0xff; - outColor.b = (inColor >> 0) & 0xff; - outColor.a = (inColor >> 24) & 0xff; + return 4 * (mUseFloatTexture ? /* fp16 */ 2 : sizeof(uint8_t)); } -void GradientCache::splitToFloats(uint32_t inColor, GradientColor& outColor) const { - outColor.r = ((inColor >> 16) & 0xff) / 255.0f; - outColor.g = ((inColor >> 8) & 0xff) / 255.0f; - outColor.b = ((inColor >> 0) & 0xff) / 255.0f; - outColor.a = ((inColor >> 24) & 0xff) / 255.0f; +size_t GradientCache::sourceBytesPerPixel() const { + // We use 4 channels (RGBA) and upload from floats (not half floats) + return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t)); } -void GradientCache::mixBytes(GradientColor& start, GradientColor& end, float amount, +void GradientCache::mixBytes(FloatColor& start, FloatColor& end, float amount, uint8_t*& dst) const { float oppAmount = 1.0f - amount; - const float alpha = start.a * oppAmount + end.a * amount; - const float a = alpha / 255.0f; - - *dst++ = uint8_t(a * (start.r * oppAmount + end.r * amount)); - *dst++ = uint8_t(a * (start.g * oppAmount + end.g * amount)); - *dst++ = uint8_t(a * (start.b * oppAmount + end.b * amount)); - *dst++ = uint8_t(alpha); + *dst++ = uint8_t(OECF_sRGB((start.r * oppAmount + end.r * amount) * 255.0f)); + *dst++ = uint8_t(OECF_sRGB((start.g * oppAmount + end.g * amount) * 255.0f)); + *dst++ = uint8_t(OECF_sRGB((start.b * oppAmount + end.b * amount) * 255.0f)); + *dst++ = uint8_t( (start.a * oppAmount + end.a * amount) * 255.0f); } -void GradientCache::mixFloats(GradientColor& start, GradientColor& end, float amount, +void GradientCache::mixFloats(FloatColor& start, FloatColor& end, float amount, uint8_t*& dst) const { float oppAmount = 1.0f - amount; - const float a = start.a * oppAmount + end.a * amount; - float* d = (float*) dst; - *d++ = a * (start.r * oppAmount + end.r * amount); - *d++ = a * (start.g * oppAmount + end.g * amount); - *d++ = a * (start.b * oppAmount + end.b * amount); - *d++ = a; - + *d++ = start.r * oppAmount + end.r * amount; + *d++ = start.g * oppAmount + end.g * amount; + *d++ = start.b * oppAmount + end.b * amount; + *d++ = start.a * oppAmount + end.a * amount; dst += 4 * sizeof(float); } void GradientCache::generateTexture(uint32_t* colors, float* positions, const uint32_t width, const uint32_t height, Texture* texture) { - const GLsizei rowBytes = width * bytesPerPixel(); + const GLsizei rowBytes = width * sourceBytesPerPixel(); uint8_t pixels[rowBytes * height]; - static ChannelSplitter gSplitters[] = { - &android::uirenderer::GradientCache::splitToBytes, - &android::uirenderer::GradientCache::splitToFloats, - }; - ChannelSplitter split = gSplitters[mUseFloatTexture]; - static ChannelMixer gMixers[] = { - &android::uirenderer::GradientCache::mixBytes, - &android::uirenderer::GradientCache::mixFloats, + &android::uirenderer::GradientCache::mixBytes, // colors are stored gamma-encoded + &android::uirenderer::GradientCache::mixFloats, // colors are stored in linear }; ChannelMixer mix = gMixers[mUseFloatTexture]; - GradientColor start; - (this->*split)(colors[0], start); + FloatColor start; + start.set(colors[0]); - GradientColor end; - (this->*split)(colors[1], end); + FloatColor end; + end.set(colors[1]); int currentPos = 1; float startPos = positions[0]; @@ -255,7 +235,7 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, currentPos++; - (this->*split)(colors[currentPos], end); + end.set(colors[currentPos]); distance = positions[currentPos] - startPos; } @@ -266,10 +246,10 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, memcpy(pixels + rowBytes, pixels, rowBytes); if (mUseFloatTexture) { - // We have to use GL_RGBA16F because GL_RGBA32F does not support filtering texture->upload(GL_RGBA16F, width, height, GL_RGBA, GL_FLOAT, pixels); } else { - texture->upload(GL_RGBA, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + GLint internalFormat = mHasSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA; + texture->upload(internalFormat, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); } texture->setFilter(GL_LINEAR); diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h index 49be19ab1c81..3fd8e80dd64b 100644 --- a/libs/hwui/GradientCache.h +++ b/libs/hwui/GradientCache.h @@ -26,6 +26,8 @@ #include #include +#include "FloatColor.h" + namespace android { namespace uirenderer { @@ -150,25 +152,13 @@ private: void getGradientInfo(const uint32_t* colors, const int count, GradientInfo& info); size_t bytesPerPixel() const; + size_t sourceBytesPerPixel() const; - struct GradientColor { - float r; - float g; - float b; - float a; - }; - - typedef void (GradientCache::*ChannelSplitter)(uint32_t inColor, - GradientColor& outColor) const; - - void splitToBytes(uint32_t inColor, GradientColor& outColor) const; - void splitToFloats(uint32_t inColor, GradientColor& outColor) const; - - typedef void (GradientCache::*ChannelMixer)(GradientColor& start, GradientColor& end, + typedef void (GradientCache::*ChannelMixer)(FloatColor& start, FloatColor& end, float amount, uint8_t*& dst) const; - void mixBytes(GradientColor& start, GradientColor& end, float amount, uint8_t*& dst) const; - void mixFloats(GradientColor& start, GradientColor& end, float amount, uint8_t*& dst) const; + void mixBytes(FloatColor& start, FloatColor& end, float amount, uint8_t*& dst) const; + void mixFloats(FloatColor& start, FloatColor& end, float amount, uint8_t*& dst) const; LruCache mCache; @@ -178,6 +168,7 @@ private: GLint mMaxTextureSize; bool mUseFloatTexture; bool mHasNpot; + bool mHasSRGB; mutable Mutex mLock; }; // class GradientCache diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index c688a96aa8cd..01650ef5745f 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -75,7 +75,7 @@ public: } void setSize(uint32_t width, uint32_t height) { - texture.updateSize(width, height, texture.format()); + texture.updateSize(width, height, texture.internalFormat(), texture.format()); } inline void setBlend(bool blend) { diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp index a6c281dc9839..52c62cc34670 100644 --- a/libs/hwui/PatchCache.cpp +++ b/libs/hwui/PatchCache.cpp @@ -225,17 +225,15 @@ void PatchCache::setupMesh(Patch* newMesh) { static const UvMapper sIdentity; -const Patch* PatchCache::get(const AssetAtlas::Entry* entry, - const uint32_t bitmapWidth, const uint32_t bitmapHeight, +const Patch* PatchCache::get( const uint32_t bitmapWidth, const uint32_t bitmapHeight, const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) { const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch); const Patch* mesh = mCache.get(description); if (!mesh) { - const UvMapper& mapper = entry ? entry->uvMapper : sIdentity; Patch* newMesh = new Patch(bitmapWidth, bitmapHeight, - pixelWidth, pixelHeight, mapper, patch); + pixelWidth, pixelHeight, sIdentity, patch); if (newMesh->vertices) { setupMesh(newMesh); diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h index 6e6a730ffe2b..0624c355332c 100644 --- a/libs/hwui/PatchCache.h +++ b/libs/hwui/PatchCache.h @@ -22,7 +22,6 @@ #include -#include "AssetAtlas.h" #include "Debug.h" #include "utils/Pair.h" @@ -54,8 +53,7 @@ public: explicit PatchCache(RenderState& renderState); ~PatchCache(); - const Patch* get(const AssetAtlas::Entry* entry, - const uint32_t bitmapWidth, const uint32_t bitmapHeight, + const Patch* get(const uint32_t bitmapWidth, const uint32_t bitmapHeight, const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch); void clear(); diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index e5200a516777..f5beb62a84ed 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -85,6 +85,8 @@ namespace uirenderer { #define PROGRAM_HAS_DEBUG_HIGHLIGHT 42 #define PROGRAM_HAS_ROUND_RECT_CLIP 43 +#define PROGRAM_HAS_GAMMA_CORRECTION 44 + /////////////////////////////////////////////////////////////////////////////// // Types /////////////////////////////////////////////////////////////////////////////// @@ -158,6 +160,8 @@ struct ProgramDescription { bool hasDebugHighlight; bool hasRoundRectClip; + bool hasGammaCorrection; + /** * Resets this description. All fields are reset back to the default * values they hold after building a new instance. @@ -196,6 +200,8 @@ struct ProgramDescription { hasDebugHighlight = false; hasRoundRectClip = false; + + hasGammaCorrection = false; } /** @@ -262,6 +268,7 @@ struct ProgramDescription { if (hasColors) key |= programid(0x1) << PROGRAM_HAS_COLORS; if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT; if (hasRoundRectClip) key |= programid(0x1) << PROGRAM_HAS_ROUND_RECT_CLIP; + if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION; return key; } diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 59225e108ac7..315c60eccb3e 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -17,8 +17,8 @@ #include #include "Caches.h" -#include "Dither.h" #include "ProgramCache.h" +#include "Properties.h" namespace android { namespace uirenderer { @@ -69,22 +69,16 @@ const char* gVS_Header_Varyings_HasBitmap = "varying highp vec2 outBitmapTexCoords;\n"; const char* gVS_Header_Varyings_HasGradient[6] = { // Linear - "varying highp vec2 linear;\n" - "varying vec2 ditherTexCoords;\n", - "varying float linear;\n" - "varying vec2 ditherTexCoords;\n", + "varying highp vec2 linear;\n", + "varying float linear;\n", // Circular - "varying highp vec2 circular;\n" - "varying vec2 ditherTexCoords;\n", - "varying highp vec2 circular;\n" - "varying vec2 ditherTexCoords;\n", + "varying highp vec2 circular;\n", + "varying highp vec2 circular;\n", // Sweep - "varying highp vec2 sweep;\n" - "varying vec2 ditherTexCoords;\n", - "varying highp vec2 sweep;\n" - "varying vec2 ditherTexCoords;\n", + "varying highp vec2 sweep;\n", + "varying highp vec2 sweep;\n", }; const char* gVS_Header_Varyings_HasRoundRectClip = "varying highp vec2 roundRectPos;\n"; @@ -98,22 +92,16 @@ const char* gVS_Main_OutTransformedTexCoords = " outTexCoords = (mainTextureTransform * vec4(texCoords, 0.0, 1.0)).xy;\n"; const char* gVS_Main_OutGradient[6] = { // Linear - " linear = vec2((screenSpace * position).x, 0.5);\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", - " linear = (screenSpace * position).x;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", + " linear = vec2((screenSpace * position).x, 0.5);\n", + " linear = (screenSpace * position).x;\n", // Circular - " circular = (screenSpace * position).xy;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", - " circular = (screenSpace * position).xy;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", + " circular = (screenSpace * position).xy;\n", + " circular = (screenSpace * position).xy;\n", // Sweep + " sweep = (screenSpace * position).xy;\n", " sweep = (screenSpace * position).xy;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", - " sweep = (screenSpace * position).xy;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", }; const char* gVS_Main_OutBitmapTexCoords = " outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n"; @@ -147,12 +135,11 @@ const char* gFS_Uniforms_TextureSampler = "uniform sampler2D baseSampler;\n"; const char* gFS_Uniforms_ExternalTextureSampler = "uniform samplerExternalOES baseSampler;\n"; -const char* gFS_Uniforms_Dither = - "uniform sampler2D ditherSampler;"; const char* gFS_Uniforms_GradientSampler[2] = { - "%s\n" + "uniform vec2 screenSize;\n" "uniform sampler2D gradientSampler;\n", - "%s\n" + + "uniform vec2 screenSize;\n" "uniform vec4 startColor;\n" "uniform vec4 endColor;\n" }; @@ -172,18 +159,51 @@ const char* gFS_Uniforms_HasRoundRectClip = "uniform vec4 roundRectInnerRectLTRB;\n" "uniform float roundRectRadius;\n"; +// Dithering must be done in the quantization space +// When we are writing to an sRGB framebuffer, we must do the following: +// EOCF(OECF(color) + dither) +// We approximate the transfer functions with gamma 2.0 to avoid branches and pow() +// The dithering pattern is generated with a triangle noise generator in the range [-0.5,1.5[ +// TODO: Handle linear fp16 render targets +const char* gFS_Dither_Functions = + "\nmediump float triangleNoise(const highp vec2 n) {\n" + " highp vec2 p = fract(n * vec2(5.3987, 5.4421));\n" + " p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));\n" + " highp float xy = p.x * p.y;\n" + " return fract(xy * 95.4307) + fract(xy * 75.04961) - 0.5;\n" + "}\n"; +const char* gFS_Dither_Preamble[2] = { + // Linear framebuffer + "\nvec4 dither(const vec4 color) {\n" + " return vec4(color.rgb + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0), color.a);" + "}\n", + // sRGB framebuffer + "\nvec4 dither(const vec4 color) {\n" + " vec3 dithered = sqrt(color.rgb) + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0);\n" + " return vec4(dithered * dithered, color.a);\n" + "}\n" +}; + +// Uses luminance coefficients from Rec.709 to choose the appropriate gamma +// The gamma() function assumes that bright text will be displayed on a dark +// background and that dark text will be displayed on bright background +// The gamma coefficient is chosen to thicken or thin the text accordingly +// The dot product used to compute the luminance could be approximated with +// a simple max(color.r, color.g, color.b) +const char* gFS_Gamma_Preamble = + "\n#define GAMMA (%.2f)\n" + "#define GAMMA_INV (%.2f)\n" + "\nfloat gamma(float a, const vec3 color) {\n" + " float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));\n" + " return pow(a, luminance < 0.5 ? GAMMA_INV : GAMMA);\n" + "}\n"; + const char* gFS_Main = "\nvoid main(void) {\n" - " lowp vec4 fragColor;\n"; + " vec4 fragColor;\n"; -const char* gFS_Main_Dither[2] = { - // ES 2.0 - "texture2D(ditherSampler, ditherTexCoords).a * " STR(DITHER_KERNEL_SIZE_INV_SQUARE), - // ES 3.0 - "texture2D(ditherSampler, ditherTexCoords).a" -}; -const char* gFS_Main_AddDitherToGradient = - " gradientColor += %s;\n"; +const char* gFS_Main_AddDither = + " fragColor = dither(fragColor);\n"; // Fast cases const char* gFS_Fast_SingleColor = @@ -202,24 +222,32 @@ const char* gFS_Fast_SingleA8Texture = "\nvoid main(void) {\n" " gl_FragColor = texture2D(baseSampler, outTexCoords);\n" "}\n\n"; +const char* gFS_Fast_SingleA8Texture_ApplyGamma = + "\nvoid main(void) {\n" + " gl_FragColor = vec4(0.0, 0.0, 0.0, pow(texture2D(baseSampler, outTexCoords).a, GAMMA));\n" + "}\n\n"; const char* gFS_Fast_SingleModulateA8Texture = "\nvoid main(void) {\n" " gl_FragColor = color * texture2D(baseSampler, outTexCoords).a;\n" "}\n\n"; +const char* gFS_Fast_SingleModulateA8Texture_ApplyGamma = + "\nvoid main(void) {\n" + " gl_FragColor = color * gamma(texture2D(baseSampler, outTexCoords).a, color.rgb);\n" + "}\n\n"; const char* gFS_Fast_SingleGradient[2] = { "\nvoid main(void) {\n" - " gl_FragColor = %s + texture2D(gradientSampler, linear);\n" + " gl_FragColor = dither(texture2D(gradientSampler, linear));\n" "}\n\n", "\nvoid main(void) {\n" - " gl_FragColor = %s + mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n" + " gl_FragColor = dither(mix(startColor, endColor, clamp(linear, 0.0, 1.0)));\n" "}\n\n", }; const char* gFS_Fast_SingleModulateGradient[2] = { "\nvoid main(void) {\n" - " gl_FragColor = %s + color.a * texture2D(gradientSampler, linear);\n" + " gl_FragColor = dither(color.a * texture2D(gradientSampler, linear));\n" "}\n\n", "\nvoid main(void) {\n" - " gl_FragColor = %s + color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n" + " gl_FragColor = dither(color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0)));\n" "}\n\n" }; @@ -239,11 +267,13 @@ const char* gFS_Main_FetchTexture[2] = { // Modulate " fragColor = color * texture2D(baseSampler, outTexCoords);\n" }; -const char* gFS_Main_FetchA8Texture[2] = { +const char* gFS_Main_FetchA8Texture[4] = { // Don't modulate " fragColor = texture2D(baseSampler, outTexCoords);\n", + " fragColor = texture2D(baseSampler, outTexCoords);\n", // Modulate " fragColor = color * texture2D(baseSampler, outTexCoords).a;\n", + " fragColor = color * gamma(texture2D(baseSampler, outTexCoords).a, color.rgb);\n", }; const char* gFS_Main_FetchGradient[6] = { // Linear @@ -271,29 +301,38 @@ const char* gFS_Main_BlendShadersBG = " fragColor = blendShaders(gradientColor, bitmapColor)"; const char* gFS_Main_BlendShadersGB = " fragColor = blendShaders(bitmapColor, gradientColor)"; -const char* gFS_Main_BlendShaders_Modulate[3] = { +const char* gFS_Main_BlendShaders_Modulate[6] = { // Don't modulate ";\n", + ";\n", // Modulate " * color.a;\n", + " * color.a;\n", // Modulate with alpha 8 texture " * texture2D(baseSampler, outTexCoords).a;\n", + " * gamma(texture2D(baseSampler, outTexCoords).a, color.rgb);\n", }; -const char* gFS_Main_GradientShader_Modulate[3] = { +const char* gFS_Main_GradientShader_Modulate[6] = { // Don't modulate " fragColor = gradientColor;\n", + " fragColor = gradientColor;\n", // Modulate " fragColor = gradientColor * color.a;\n", + " fragColor = gradientColor * color.a;\n", // Modulate with alpha 8 texture " fragColor = gradientColor * texture2D(baseSampler, outTexCoords).a;\n", + " fragColor = gradientColor * gamma(texture2D(baseSampler, outTexCoords).a, gradientColor.rgb);\n", }; -const char* gFS_Main_BitmapShader_Modulate[3] = { +const char* gFS_Main_BitmapShader_Modulate[6] = { // Don't modulate " fragColor = bitmapColor;\n", + " fragColor = bitmapColor;\n", // Modulate " fragColor = bitmapColor * color.a;\n", + " fragColor = bitmapColor * color.a;\n", // Modulate with alpha 8 texture " fragColor = bitmapColor * texture2D(baseSampler, outTexCoords).a;\n", + " fragColor = bitmapColor * gamma(texture2D(baseSampler, outTexCoords).a, bitmapColor.rgb);\n", }; const char* gFS_Main_FragColor = " gl_FragColor = fragColor;\n"; @@ -385,7 +424,8 @@ const char* gBlendOps[18] = { /////////////////////////////////////////////////////////////////////////////// ProgramCache::ProgramCache(Extensions& extensions) - : mHasES3(extensions.getMajorGlVersion() >= 3) { + : mHasES3(extensions.getMajorGlVersion() >= 3) + , mHasSRGB(extensions.hasSRGB()) { } ProgramCache::~ProgramCache() { @@ -518,6 +558,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description static bool shaderOp(const ProgramDescription& description, String8& shader, const int modulateOp, const char** snippets) { int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp; + op = op * 2 + description.hasGammaCorrection; shader.append(snippets[op]); return description.hasAlpha8Texture; } @@ -570,13 +611,16 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti shader.append(gFS_Uniforms_ExternalTextureSampler); } if (description.hasGradient) { - shader.appendFormat(gFS_Uniforms_GradientSampler[description.isSimpleGradient], - gFS_Uniforms_Dither); + shader.append(gFS_Uniforms_GradientSampler[description.isSimpleGradient]); } if (description.hasRoundRectClip) { shader.append(gFS_Uniforms_HasRoundRectClip); } + if (description.hasGammaCorrection) { + shader.appendFormat(gFS_Gamma_Preamble, Properties::textGamma, 1.0f / Properties::textGamma); + } + // Optimization for common cases if (!description.hasVertexAlpha && !blendFramebuffer @@ -607,18 +651,26 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti fast = true; } else if (singleA8Texture) { if (!description.modulate) { - shader.append(gFS_Fast_SingleA8Texture); + if (description.hasGammaCorrection) { + shader.append(gFS_Fast_SingleA8Texture_ApplyGamma); + } else { + shader.append(gFS_Fast_SingleA8Texture); + } } else { - shader.append(gFS_Fast_SingleModulateA8Texture); + if (description.hasGammaCorrection) { + shader.append(gFS_Fast_SingleModulateA8Texture_ApplyGamma); + } else { + shader.append(gFS_Fast_SingleModulateA8Texture); + } } fast = true; } else if (singleGradient) { + shader.append(gFS_Dither_Functions); + shader.append(gFS_Dither_Preamble[mHasSRGB]); if (!description.modulate) { - shader.appendFormat(gFS_Fast_SingleGradient[description.isSimpleGradient], - gFS_Main_Dither[mHasES3]); + shader.append(gFS_Fast_SingleGradient[description.isSimpleGradient]); } else { - shader.appendFormat(gFS_Fast_SingleModulateGradient[description.isSimpleGradient], - gFS_Main_Dither[mHasES3]); + shader.append(gFS_Fast_SingleModulateGradient[description.isSimpleGradient]); } fast = true; } @@ -652,6 +704,10 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (description.isBitmapNpot) { generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT); } + if (description.hasGradient) { + shader.append(gFS_Dither_Functions); + shader.append(gFS_Dither_Preamble[mHasSRGB]); + } // Begin the shader shader.append(gFS_Main); { @@ -659,7 +715,8 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (description.hasTexture || description.hasExternalTexture) { if (description.hasAlpha8Texture) { if (!description.hasGradient && !description.hasBitmap) { - shader.append(gFS_Main_FetchA8Texture[modulateOp]); + shader.append( + gFS_Main_FetchA8Texture[modulateOp * 2 + description.hasGammaCorrection]); } } else { shader.append(gFS_Main_FetchTexture[modulateOp]); @@ -671,7 +728,6 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti } if (description.hasGradient) { shader.append(gFS_Main_FetchGradient[gradientIndex(description)]); - shader.appendFormat(gFS_Main_AddDitherToGradient, gFS_Main_Dither[mHasES3]); } if (description.hasBitmap) { if (!description.isBitmapNpot) { @@ -715,6 +771,10 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti } } + if (description.hasGradient) { + shader.append(gFS_Main_AddDither); + } + // Output the fragment if (!blendFramebuffer) { shader.append(gFS_Main_FragColor); diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h index 9ac885b665e7..292ecdfdc2b6 100644 --- a/libs/hwui/ProgramCache.h +++ b/libs/hwui/ProgramCache.h @@ -59,6 +59,7 @@ private: std::map> mCache; const bool mHasES3; + const bool mHasSRGB; }; // class ProgramCache }; // namespace uirenderer diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index eedc9e7d5333..92a49d28c408 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -203,7 +203,7 @@ enum DebugLevel { #define PROPERTY_TEXT_LARGE_CACHE_WIDTH "ro.hwui.text_large_cache_width" #define PROPERTY_TEXT_LARGE_CACHE_HEIGHT "ro.hwui.text_large_cache_height" -// Gamma (>= 1.0, <= 10.0) +// Gamma (>= 1.0, <= 3.0) #define PROPERTY_TEXT_GAMMA "hwui.text_gamma" /////////////////////////////////////////////////////////////////////////////// @@ -222,7 +222,7 @@ enum DebugLevel { #define DEFAULT_TEXTURE_CACHE_FLUSH_RATE 0.6f -#define DEFAULT_TEXT_GAMMA 1.4f +#define DEFAULT_TEXT_GAMMA 1.45f // Match design tools // cap to 256 to limite paths in the path cache #define DEFAULT_PATH_TEXTURE_CAP 256 diff --git a/libs/hwui/PropertyValuesHolder.cpp b/libs/hwui/PropertyValuesHolder.cpp index 6ba0ab59a88c..2a03e6a3ebc5 100644 --- a/libs/hwui/PropertyValuesHolder.cpp +++ b/libs/hwui/PropertyValuesHolder.cpp @@ -16,6 +16,7 @@ #include "PropertyValuesHolder.h" +#include "utils/Color.h" #include "utils/VectorDrawableUtils.h" #include @@ -25,18 +26,26 @@ namespace uirenderer { using namespace VectorDrawable; -inline U8CPU lerp(U8CPU fromValue, U8CPU toValue, float fraction) { - return (U8CPU) (fromValue * (1 - fraction) + toValue * fraction); +inline constexpr float lerp(float fromValue, float toValue, float fraction) { + return float (fromValue * (1 - fraction) + toValue * fraction); +} + +inline constexpr float linearize(U8CPU component) { + return EOCF_sRGB(component / 255.0f); } // TODO: Add a test for this void ColorEvaluator::evaluate(SkColor* outColor, const SkColor& fromColor, const SkColor& toColor, float fraction) const { - U8CPU alpha = lerp(SkColorGetA(fromColor), SkColorGetA(toColor), fraction); - U8CPU red = lerp(SkColorGetR(fromColor), SkColorGetR(toColor), fraction); - U8CPU green = lerp(SkColorGetG(fromColor), SkColorGetG(toColor), fraction); - U8CPU blue = lerp(SkColorGetB(fromColor), SkColorGetB(toColor), fraction); - *outColor = SkColorSetARGB(alpha, red, green, blue); + float a = lerp(SkColorGetA(fromColor) / 255.0f, SkColorGetA(toColor) / 255.0f, fraction); + float r = lerp(linearize(SkColorGetR(fromColor)), linearize(SkColorGetR(toColor)), fraction); + float g = lerp(linearize(SkColorGetG(fromColor)), linearize(SkColorGetG(toColor)), fraction); + float b = lerp(linearize(SkColorGetB(fromColor)), linearize(SkColorGetB(toColor)), fraction); + *outColor = SkColorSetARGB( + (U8CPU) roundf(a * 255.0f), + (U8CPU) roundf(OECF_sRGB(r) * 255.0f), + (U8CPU) roundf(OECF_sRGB(g) * 255.0f), + (U8CPU) roundf(OECF_sRGB(b) * 255.0f)); } void PathEvaluator::evaluate(PathData* out, diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp index ddca122788d1..22c6dfc6b55a 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -197,7 +197,7 @@ CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread, Texture sourceTexture(caches); sourceTexture.wrap(sourceTexId, - sourceBuffer->getWidth(), sourceBuffer->getHeight(), 0 /* total lie */); + sourceBuffer->getWidth(), sourceBuffer->getHeight(), 0, 0 /* total lie */); CopyResult copyResult = copyTextureInto(caches, renderThread.renderState(), sourceTexture, texTransform, srcRect, bitmap); diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h index a65c22c3a555..ebc41b18ed8c 100644 --- a/libs/hwui/RecordedOp.h +++ b/libs/hwui/RecordedOp.h @@ -216,7 +216,6 @@ struct BitmapOp : RecordedOp { : SUPER(BitmapOp) , bitmap(bitmap) {} const SkBitmap* bitmap; - // TODO: asset atlas/texture id lookup? }; struct BitmapMeshOp : RecordedOp { diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp index 6f4a6839be4e..9838df2f6013 100644 --- a/libs/hwui/SkiaShader.cpp +++ b/libs/hwui/SkiaShader.cpp @@ -177,11 +177,12 @@ bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 mode outData->endColor.set(gradInfo.fColors[1]); } - outData->ditherSampler = (*textureUnit)++; return true; } -void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& data) { +void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& data, + const GLsizei width, const GLsizei height) { + if (CC_UNLIKELY(data.gradientTexture)) { caches.textureState().activateTexture(data.gradientSampler); bindTexture(&caches, data.gradientTexture, data.wrapST, data.wrapST); @@ -191,10 +192,7 @@ void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& dat bindUniformColor(caches.program().getUniform("endColor"), data.endColor); } - // TODO: remove sampler slot incrementing from dither.setupProgram, - // since this assignment of slots is done at store, not apply time - GLuint ditherSampler = data.ditherSampler; - caches.dither.setupProgram(caches.program(), &ditherSampler); + glUniform2f(caches.program().getUniform("screenSize"), 1.0f / width, 1.0f / height); glUniformMatrix4fv(caches.program().getUniform("screenSpace"), 1, GL_FALSE, &data.screenSpace.data[0]); } @@ -208,13 +206,7 @@ bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& model return false; } - /* - * Bypass the AssetAtlas, since those textures: - * 1) require UV mapping, which isn't implemented in matrix computation below - * 2) can't handle REPEAT simply - * 3) are safe to upload here (outside of sync stage), since they're static - */ - outData->bitmapTexture = caches.textureCache.getAndBypassAtlas(&bitmap); + outData->bitmapTexture = caches.textureCache.get(&bitmap); if (!outData->bitmapTexture) return false; outData->bitmapSampler = (*textureUnit)++; @@ -388,11 +380,12 @@ void SkiaShader::store(Caches& caches, const SkShader& shader, const Matrix4& mo outData->skiaShaderType = kNone_SkiaShaderType; } -void SkiaShader::apply(Caches& caches, const SkiaShaderData& data) { +void SkiaShader::apply(Caches& caches, const SkiaShaderData& data, + const GLsizei width, const GLsizei height) { if (!data.skiaShaderType) return; if (data.skiaShaderType & kGradient_SkiaShaderType) { - applyGradient(caches, data.gradientData); + applyGradient(caches, data.gradientData, width, height); } if (data.skiaShaderType & kBitmap_SkiaShaderType) { applyBitmap(caches, data.bitmapData); diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h index 884196d9fc62..5854289f49a7 100644 --- a/libs/hwui/SkiaShader.h +++ b/libs/hwui/SkiaShader.h @@ -62,7 +62,6 @@ struct SkiaShaderData { } bitmapData; struct GradientShaderData { Matrix4 screenSpace; - GLuint ditherSampler; // simple gradient FloatColor startColor; @@ -72,7 +71,6 @@ struct SkiaShaderData { Texture* gradientTexture; GLuint gradientSampler; GLenum wrapST; - } gradientData; struct LayerShaderData { Layer* layer; @@ -90,7 +88,8 @@ public: static void store(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, GLuint* textureUnit, ProgramDescription* description, SkiaShaderData* outData); - static void apply(Caches& caches, const SkiaShaderData& data); + static void apply(Caches& caches, const SkiaShaderData& data, + const GLsizei width, const GLsizei height); }; }; // namespace uirenderer diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp index 4f49a3518be0..908f57265906 100644 --- a/libs/hwui/Texture.cpp +++ b/libs/hwui/Texture.cpp @@ -26,19 +26,23 @@ namespace android { namespace uirenderer { +// Number of bytes used by a texture in the given format static int bytesPerPixel(GLint glFormat) { switch (glFormat) { // The wrapped-texture case, usually means a SurfaceTexture case 0: return 0; + case GL_LUMINANCE: case GL_ALPHA: return 1; + case GL_SRGB8: case GL_RGB: return 3; + case GL_SRGB8_ALPHA8: case GL_RGBA: return 4; case GL_RGBA16F: - return 16; + return 8; default: LOG_ALWAYS_FATAL("UNKNOWN FORMAT %d", glFormat); } @@ -83,14 +87,16 @@ void Texture::deleteTexture() { mId = 0; } -bool Texture::updateSize(uint32_t width, uint32_t height, GLint format) { - if (mWidth == width && mHeight == height && mFormat == format) { +bool Texture::updateSize(uint32_t width, uint32_t height, GLint internalFormat, GLint format) { + if (mWidth == width && mHeight == height && + mFormat == format && mInternalFormat == internalFormat) { return false; } mWidth = width; mHeight = height; mFormat = format; - notifySizeChanged(mWidth * mHeight * bytesPerPixel(mFormat)); + mInternalFormat = internalFormat; + notifySizeChanged(mWidth * mHeight * bytesPerPixel(internalFormat)); return true; } @@ -101,10 +107,10 @@ void Texture::resetCachedParams() { mMagFilter = GL_LINEAR; } -void Texture::upload(GLint internalformat, uint32_t width, uint32_t height, +void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height, GLenum format, GLenum type, const void* pixels) { GL_CHECKPOINT(MODERATE); - bool needsAlloc = updateSize(width, height, internalformat); + bool needsAlloc = updateSize(width, height, internalFormat, format); if (!mId) { glGenTextures(1, &mId); needsAlloc = true; @@ -112,17 +118,17 @@ void Texture::upload(GLint internalformat, uint32_t width, uint32_t height, } mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId); if (needsAlloc) { - glTexImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0, + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); } else if (pixels) { - glTexSubImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0, + glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); } GL_CHECKPOINT(MODERATE); } -static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei stride, GLsizei bpp, - GLsizei width, GLsizei height, const GLvoid * data) { +static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GLenum type, + GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, const GLvoid * data) { const bool useStride = stride != width && Caches::getInstance().extensions().hasUnpackRowLength(); @@ -132,7 +138,7 @@ static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei str } if (resize) { - glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, data); } else { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); } @@ -156,7 +162,7 @@ static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei str } if (resize) { - glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, temp); } else { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp); } @@ -166,31 +172,44 @@ static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei str } static void uploadSkBitmapToTexture(const SkBitmap& bitmap, - bool resize, GLenum format, GLenum type) { - uploadToTexture(resize, format, type, bitmap.rowBytesAsPixels(), bitmap.bytesPerPixel(), - bitmap.width(), bitmap.height(), bitmap.getPixels()); + bool resize, GLint internalFormat, GLenum format, GLenum type) { + uploadToTexture(resize, internalFormat, format, type, bitmap.rowBytesAsPixels(), + bitmap.bytesPerPixel(), bitmap.width(), bitmap.height(), bitmap.getPixels()); } -static void colorTypeToGlFormatAndType(SkColorType colorType, - GLint* outFormat, GLint* outType) { +static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType, + bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType) { switch (colorType) { case kAlpha_8_SkColorType: *outFormat = GL_ALPHA; + *outInternalFormat = GL_ALPHA; *outType = GL_UNSIGNED_BYTE; break; case kRGB_565_SkColorType: - *outFormat = GL_RGB; - *outType = GL_UNSIGNED_SHORT_5_6_5; + if (needSRGB) { + // We would ideally use a GL_RGB/GL_SRGB8 texture but the + // intermediate Skia bitmap needs to be ARGB_8888 + *outFormat = GL_RGBA; + *outInternalFormat = caches.rgbaInternalFormat(); + *outType = GL_UNSIGNED_BYTE; + } else { + *outFormat = GL_RGB; + *outInternalFormat = GL_RGB; + *outType = GL_UNSIGNED_SHORT_5_6_5; + } break; // ARGB_4444 and Index_8 are both upconverted to RGBA_8888 case kARGB_4444_SkColorType: case kIndex_8_SkColorType: case kN32_SkColorType: *outFormat = GL_RGBA; + *outInternalFormat = caches.rgbaInternalFormat(needSRGB); *outType = GL_UNSIGNED_BYTE; break; case kGray_8_SkColorType: + // TODO: Handle sRGB *outFormat = GL_LUMINANCE; + *outInternalFormat = GL_LUMINANCE; *outType = GL_UNSIGNED_BYTE; break; default: @@ -224,29 +243,36 @@ void Texture::upload(const SkBitmap& bitmap) { setDefaultParams = true; } - GLint format, type; - colorTypeToGlFormatAndType(bitmap.colorType(), &format, &type); + sk_sp sRGB = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); + bool needSRGB = bitmap.colorSpace() == sRGB.get(); - if (updateSize(bitmap.width(), bitmap.height(), format)) { + GLint internalFormat, format, type; + colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB, &internalFormat, &format, &type); + + if (updateSize(bitmap.width(), bitmap.height(), internalFormat, format)) { needsAlloc = true; } blend = !bitmap.isOpaque(); mCaches.textureState().bindTexture(mId); + // TODO: Handle sRGB gray bitmaps + bool hasSRGB = mCaches.extensions().hasSRGB(); if (CC_UNLIKELY(bitmap.colorType() == kARGB_4444_SkColorType - || bitmap.colorType() == kIndex_8_SkColorType)) { + || bitmap.colorType() == kIndex_8_SkColorType + || (bitmap.colorType() == kRGB_565_SkColorType && hasSRGB && needSRGB))) { + SkBitmap rgbaBitmap; - rgbaBitmap.allocPixels(SkImageInfo::MakeN32(mWidth, mHeight, - bitmap.alphaType())); + rgbaBitmap.allocPixels(SkImageInfo::MakeN32( + mWidth, mHeight, bitmap.alphaType(), hasSRGB ? sRGB : nullptr)); rgbaBitmap.eraseColor(0); SkCanvas canvas(rgbaBitmap); canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr); - uploadSkBitmapToTexture(rgbaBitmap, needsAlloc, format, type); + uploadSkBitmapToTexture(rgbaBitmap, needsAlloc, internalFormat, format, type); } else { - uploadSkBitmapToTexture(bitmap, needsAlloc, format, type); + uploadSkBitmapToTexture(bitmap, needsAlloc, internalFormat, format, type); } if (canMipMap) { @@ -262,11 +288,12 @@ void Texture::upload(const SkBitmap& bitmap) { } } -void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint format) { +void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, GLint format) { mId = id; mWidth = width; mHeight = height; mFormat = format; + mInternalFormat = internalFormat; // We're wrapping an existing texture, so don't double count this memory notifySizeChanged(0); } diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h index b72742f45654..aa8a6d3fae82 100644 --- a/libs/hwui/Texture.h +++ b/libs/hwui/Texture.h @@ -69,8 +69,8 @@ public: * * The image data is undefined after calling this. */ - void resize(uint32_t width, uint32_t height, GLint format) { - upload(format, width, height, format, GL_UNSIGNED_BYTE, nullptr); + void resize(uint32_t width, uint32_t height, GLint internalFormat, GLint format) { + upload(internalFormat, width, height, format, GL_UNSIGNED_BYTE, nullptr); } /** @@ -85,13 +85,13 @@ public: /** * Basically glTexImage2D/glTexSubImage2D. */ - void upload(GLint internalformat, uint32_t width, uint32_t height, + void upload(GLint internalFormat, uint32_t width, uint32_t height, GLenum format, GLenum type, const void* pixels); /** * Wraps an existing texture. */ - void wrap(GLuint id, uint32_t width, uint32_t height, GLint format); + void wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, GLint format); GLuint id() const { return mId; @@ -109,6 +109,10 @@ public: return mFormat; } + GLint internalFormat() const { + return mInternalFormat; + } + /** * Generation of the backing bitmap, */ @@ -148,13 +152,14 @@ private: friend class Layer; // Returns true if the size changed, false if it was the same - bool updateSize(uint32_t width, uint32_t height, GLint format); + bool updateSize(uint32_t width, uint32_t height, GLint internalFormat, GLint format); void resetCachedParams(); GLuint mId = 0; uint32_t mWidth = 0; uint32_t mHeight = 0; GLint mFormat = 0; + GLint mInternalFormat = 0; /* See GLES spec section 3.8.14 * "In the initial state, the value assigned to TEXTURE_MIN_FILTER is diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index 523924af5ef1..5ccdbda67e74 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -18,7 +18,6 @@ #include -#include "AssetAtlas.h" #include "Caches.h" #include "Texture.h" #include "TextureCache.h" @@ -36,8 +35,7 @@ TextureCache::TextureCache() : mCache(LruCache::kUnlimitedCapacity) , mSize(0) , mMaxSize(Properties::textureCacheSize) - , mFlushRate(Properties::textureCacheFlushRate) - , mAssetAtlas(nullptr) { + , mFlushRate(Properties::textureCacheFlushRate) { mCache.setOnEntryRemovedListener(this); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); @@ -84,10 +82,6 @@ void TextureCache::operator()(uint32_t&, Texture*& texture) { // Caching /////////////////////////////////////////////////////////////////////////////// -void TextureCache::setAssetAtlas(AssetAtlas* assetAtlas) { - mAssetAtlas = assetAtlas; -} - void TextureCache::resetMarkInUse(void* ownerToken) { LruCache::Iterator iter(mCache); while (iter.next()) { @@ -108,14 +102,7 @@ bool TextureCache::canMakeTextureFromBitmap(const SkBitmap* bitmap) { // Returns a prepared Texture* that either is already in the cache or can fit // in the cache (and is thus added to the cache) -Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap, AtlasUsageType atlasUsageType) { - if (CC_LIKELY(mAssetAtlas != nullptr) && atlasUsageType == AtlasUsageType::Use) { - AssetAtlas::Entry* entry = mAssetAtlas->getEntry(bitmap->pixelRef()); - if (CC_UNLIKELY(entry)) { - return entry->texture; - } - } - +Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap) { Texture* texture = mCache.get(bitmap->pixelRef()->getStableID()); if (!texture) { @@ -160,7 +147,7 @@ Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap, AtlasUsageType a } bool TextureCache::prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap) { - Texture* texture = getCachedTexture(bitmap, AtlasUsageType::Use); + Texture* texture = getCachedTexture(bitmap); if (texture) { texture->isInUse = ownerToken; } @@ -168,11 +155,11 @@ bool TextureCache::prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap } bool TextureCache::prefetch(const SkBitmap* bitmap) { - return getCachedTexture(bitmap, AtlasUsageType::Use); + return getCachedTexture(bitmap); } -Texture* TextureCache::get(const SkBitmap* bitmap, AtlasUsageType atlasUsageType) { - Texture* texture = getCachedTexture(bitmap, atlasUsageType); +Texture* TextureCache::get(const SkBitmap* bitmap) { + Texture* texture = getCachedTexture(bitmap); if (!texture) { if (!canMakeTextureFromBitmap(bitmap)) { diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index 0a61b6b1a522..88ef7711e844 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -47,8 +47,6 @@ class Texture; // Classes /////////////////////////////////////////////////////////////////////////////// -class AssetAtlas; - /** * A simple LRU texture cache. The cache has a maximum size expressed in bytes. * Any texture added to the cache causing the cache to grow beyond the maximum @@ -85,20 +83,10 @@ public: bool prefetch(const SkBitmap* bitmap); /** - * Returns the texture associated with the specified bitmap from either within the cache, or - * the AssetAtlas. If the texture cannot be found in the cache, a new texture is generated. - */ - Texture* get(const SkBitmap* bitmap) { - return get(bitmap, AtlasUsageType::Use); - } - - /** - * Returns the texture associated with the specified bitmap. If the texture cannot be found in - * the cache, a new texture is generated, even if it resides in the AssetAtlas. + * Returns the texture associated with the specified bitmap from within the cache. + * If the texture cannot be found in the cache, a new texture is generated. */ - Texture* getAndBypassAtlas(const SkBitmap* bitmap) { - return get(bitmap, AtlasUsageType::Bypass); - } + Texture* get(const SkBitmap* bitmap); /** * Removes the texture associated with the specified pixelRef. This is meant @@ -130,18 +118,10 @@ public: */ void flush(); - void setAssetAtlas(AssetAtlas* assetAtlas); - private: - enum class AtlasUsageType { - Use, - Bypass, - }; - bool canMakeTextureFromBitmap(const SkBitmap* bitmap); - Texture* get(const SkBitmap* bitmap, AtlasUsageType atlasUsageType); - Texture* getCachedTexture(const SkBitmap* bitmap, AtlasUsageType atlasUsageType); + Texture* getCachedTexture(const SkBitmap* bitmap); LruCache mCache; @@ -155,8 +135,6 @@ private: std::vector mGarbage; mutable Mutex mLock; - - AssetAtlas* mAssetAtlas; }; // class TextureCache }; // namespace uirenderer diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 2b7994139641..4e5b9ad2f0a3 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -561,8 +561,12 @@ void Tree::updateBitmapCache(SkBitmap* outCache, bool useStagingData) { bool Tree::allocateBitmapIfNeeded(SkBitmap* outCache, int width, int height) { if (!canReuseBitmap(*outCache, width, height)) { - SkImageInfo info = SkImageInfo::Make(width, height, - kN32_SkColorType, kPremul_SkAlphaType); +#ifndef ANDROID_ENABLE_LINEAR_BLENDING + sk_sp colorSpace = nullptr; +#else + sk_sp colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); +#endif + SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType, colorSpace); outCache->setInfo(info); // TODO: Count the bitmap cache against app's java heap outCache->allocPixels(info); diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h index c1bf980658b2..db982ad0c8f4 100644 --- a/libs/hwui/Vertex.h +++ b/libs/hwui/Vertex.h @@ -19,6 +19,7 @@ #include "Vector.h" +#include "FloatColor.h" #include "utils/Macros.h" namespace android { @@ -76,21 +77,19 @@ struct TextureVertex { REQUIRE_COMPATIBLE_LAYOUT(TextureVertex); /** - * Simple structure to describe a vertex with a position, texture UV and ARGB color. + * Simple structure to describe a vertex with a position, texture UV and an + * sRGB color with alpha. The color is stored pre-multiplied in linear space. */ struct ColorTextureVertex { float x, y; float u, v; - float r, g, b, a; + float r, g, b, a; // pre-multiplied linear static inline void set(ColorTextureVertex* vertex, float x, float y, - float u, float v, int color) { - - float a = ((color >> 24) & 0xff) / 255.0f; - float r = a * ((color >> 16) & 0xff) / 255.0f; - float g = a * ((color >> 8) & 0xff) / 255.0f; - float b = a * ((color) & 0xff) / 255.0f; - *vertex = { x, y, u, v, r, g, b, a }; + float u, float v, uint32_t color) { + FloatColor c; + c.set(color); + *vertex = { x, y, u, v, c.r, c.g, c.b, c.a }; } }; // struct ColorTextureVertex diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp index 49e9f65582ae..e2844ad0649a 100644 --- a/libs/hwui/font/CacheTexture.cpp +++ b/libs/hwui/font/CacheTexture.cpp @@ -180,7 +180,12 @@ void CacheTexture::allocatePixelBuffer() { mPixelBuffer = PixelBuffer::create(mFormat, getWidth(), getHeight()); } - mTexture.resize(mWidth, mHeight, mFormat); + GLint internalFormat = mFormat; + if (mFormat == GL_RGBA) { + internalFormat = mCaches.rgbaInternalFormat(); + } + + mTexture.resize(mWidth, mHeight, internalFormat, mFormat); mTexture.setFilter(getLinearFiltering() ? GL_LINEAR : GL_NEAREST); mTexture.setWrap(GL_CLAMP_TO_EDGE); } diff --git a/libs/hwui/renderstate/OffscreenBufferPool.cpp b/libs/hwui/renderstate/OffscreenBufferPool.cpp index 10a26e08f897..a9bbb273dbb5 100644 --- a/libs/hwui/renderstate/OffscreenBufferPool.cpp +++ b/libs/hwui/renderstate/OffscreenBufferPool.cpp @@ -22,6 +22,7 @@ #include "utils/FatVector.h" #include "utils/TraceUtils.h" +#include #include #include @@ -44,7 +45,7 @@ OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches, uint32_t height = computeIdealDimension(viewportHeight); ATRACE_FORMAT("Allocate %ux%u HW Layer", width, height); caches.textureState().activateTexture(0); - texture.resize(width, height, GL_RGBA); + texture.resize(width, height, caches.rgbaInternalFormat(), GL_RGBA); texture.blend = true; texture.setWrap(GL_CLAMP_TO_EDGE); // not setting filter on texture, since it's set when drawing, based on transform diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index ee4619d2c222..84ab3f31e7ab 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -52,7 +52,6 @@ void RenderState::onGLContextCreated() { mCaches = &Caches::createInstance(*this); } mCaches->init(); - mCaches->textureCache.setAssetAtlas(&mAssetAtlas); } static void layerLostGlContext(Layer* layer) { @@ -64,7 +63,6 @@ void RenderState::onGLContextDestroyed() { // TODO: reset all cached state in state objects std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext); - mAssetAtlas.terminate(); mCaches->terminate(); @@ -147,9 +145,17 @@ void RenderState::interruptForFunctorInvoke() { meshState().resetVertexPointers(); meshState().disableTexCoordsVertexArray(); debugOverdraw(false, false); + // TODO: We need a way to know whether the functor is sRGB aware (b/32072673) + if (mCaches->extensions().hasSRGBWriteControl()) { + glDisable(GL_FRAMEBUFFER_SRGB_EXT); + } } void RenderState::resumeFromFunctorInvoke() { + if (mCaches->extensions().hasSRGBWriteControl()) { + glEnable(GL_FRAMEBUFFER_SRGB_EXT); + } + glViewport(0, 0, mViewportWidth, mViewportHeight); glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); debugOverdraw(false, false); @@ -308,7 +314,7 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) { glVertexAttribPointer(alphaLocation, 1, GL_FLOAT, GL_FALSE, vertices.stride, alphaCoords); } // Shader uniforms - SkiaShader::apply(*mCaches, fill.skiaShaderData); + SkiaShader::apply(*mCaches, fill.skiaShaderData, mViewportWidth, mViewportHeight); GL_CHECKPOINT(MODERATE); Texture* texture = (fill.skiaShaderData.skiaShaderType & kBitmap_SkiaShaderType) ? diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h index 9e0fb121be65..3d119dc9e290 100644 --- a/libs/hwui/renderstate/RenderState.h +++ b/libs/hwui/renderstate/RenderState.h @@ -16,7 +16,6 @@ #ifndef RENDERSTATE_H #define RENDERSTATE_H -#include "AssetAtlas.h" #include "Caches.h" #include "Glop.h" #include "renderstate/Blend.h" @@ -92,7 +91,6 @@ public: void render(const Glop& glop, const Matrix4& orthoMatrix); - AssetAtlas& assetAtlas() { return mAssetAtlas; } Blend& blend() { return *mBlend; } MeshState& meshState() { return *mMeshState; } Scissor& scissor() { return *mScissor; } @@ -120,7 +118,6 @@ private: OffscreenBufferPool mLayerPool; - AssetAtlas mAssetAtlas; std::set mActiveLayers; std::set mRegisteredContexts; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 0f2d55bc209d..fe0f56ad1332 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -545,11 +545,6 @@ DeferredLayerUpdater* CanvasContext::createTextureLayer() { return mRenderPipeline->createTextureLayer(); } -void CanvasContext::setTextureAtlas(RenderThread& thread, - const sp& buffer, int64_t* map, size_t mapSize) { - thread.eglManager().setTextureAtlas(buffer, map, mapSize); -} - void CanvasContext::dumpFrames(int fd) { FILE* file = fdopen(fd, "a"); fprintf(file, "\n\n---PROFILEDATA---\n"); diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 652cddd968bb..41b658e083da 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -119,9 +119,6 @@ public: DeferredLayerUpdater* createTextureLayer(); - ANDROID_API static void setTextureAtlas(RenderThread& thread, - const sp& buffer, int64_t* map, size_t mapSize); - void stopDrawing(); void notifyFramePending(); diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 86731c9581be..beda0455c145 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -91,9 +91,7 @@ EglManager::EglManager(RenderThread& thread) , mEglConfig(nullptr) , mEglContext(EGL_NO_CONTEXT) , mPBufferSurface(EGL_NO_SURFACE) - , mCurrentSurface(EGL_NO_SURFACE) - , mAtlasMap(nullptr) - , mAtlasMapSize(0) { + , mCurrentSurface(EGL_NO_SURFACE) { } void EglManager::initialize() { @@ -128,7 +126,6 @@ void EglManager::initialize() { makeCurrent(mPBufferSurface); DeviceInfo::initialize(); mRenderThread.renderState().onGLContextCreated(); - initAtlas(); } void EglManager::initExtensions() { @@ -191,32 +188,6 @@ void EglManager::createContext() { "Failed to create context, error = %s", egl_error_str()); } -void EglManager::setTextureAtlas(const sp& buffer, - int64_t* map, size_t mapSize) { - - // Already initialized - if (mAtlasBuffer.get()) { - ALOGW("Multiple calls to setTextureAtlas!"); - delete map; - return; - } - - mAtlasBuffer = buffer; - mAtlasMap = map; - mAtlasMapSize = mapSize; - - if (hasEglContext()) { - initAtlas(); - } -} - -void EglManager::initAtlas() { - if (mAtlasBuffer.get()) { - mRenderThread.renderState().assetAtlas().init(mAtlasBuffer, - mAtlasMap, mAtlasMapSize); - } -} - void EglManager::createPBufferSurface() { LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, "usePBufferSurface() called on uninitialized GlobalContext!"); @@ -229,7 +200,16 @@ void EglManager::createPBufferSurface() { EGLSurface EglManager::createSurface(EGLNativeWindowType window) { initialize(); - EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, nullptr); + + EGLint attribs[] = { +#ifdef ANDROID_ENABLE_LINEAR_BLENDING + EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR, + EGL_COLORSPACE, EGL_COLORSPACE_sRGB, +#endif + EGL_NONE + }; + + EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, attribs); LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, "Failed to create EGLSurface for window %p, eglErr = %s", (void*) window, egl_error_str()); diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index 41047fecf960..ba4a3e1c5192 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -79,8 +79,6 @@ public: // Returns true iff the surface is now preserving buffers. bool setPreserveBuffer(EGLSurface surface, bool preserve); - void setTextureAtlas(const sp& buffer, int64_t* map, size_t mapSize); - void fence(); private: @@ -94,7 +92,6 @@ private: void createPBufferSurface(); void loadConfig(); void createContext(); - void initAtlas(); EGLint queryBufferAge(EGLSurface surface); RenderThread& mRenderThread; @@ -106,10 +103,6 @@ private: EGLSurface mCurrentSurface; - sp mAtlasBuffer; - int64_t* mAtlasMap; - size_t mAtlasMapSize; - enum class SwapBehavior { Discard, Preserved, diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index dcbc980763ec..c2ed8643c0ad 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -480,23 +480,6 @@ void RenderProxy::dumpGraphicsMemory(int fd) { staticPostAndWait(task); } -CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map, - size_t size) { - CanvasContext::setTextureAtlas(*args->thread, args->buffer, args->map, args->size); - args->buffer->decStrong(nullptr); - return nullptr; -} - -void RenderProxy::setTextureAtlas(const sp& buffer, int64_t* map, size_t size) { - SETUP_TASK(setTextureAtlas); - args->thread = &mRenderThread; - args->buffer = buffer.get(); - args->buffer->incStrong(nullptr); - args->map = map; - args->size = size; - post(task); -} - CREATE_BRIDGE2(setProcessStatsBuffer, RenderThread* thread, int fd) { args->thread->jankTracker().switchStorageToAshmem(args->fd); close(args->fd); diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index d4aaea6d7280..50a6f64fe5ca 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -112,7 +112,6 @@ public: uint32_t frameTimePercentile(int p); ANDROID_API static void dumpGraphicsMemory(int fd); - ANDROID_API void setTextureAtlas(const sp& buffer, int64_t* map, size_t size); ANDROID_API void setProcessStatsBuffer(int fd); ANDROID_API int getRenderThreadTid(); diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 78e9bc475826..51c0a05fb6d7 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -124,8 +124,9 @@ public: static SkBitmap createSkBitmap(int width, int height, SkColorType colorType = kN32_SkColorType) { SkBitmap bitmap; + sk_sp colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); SkImageInfo info = SkImageInfo::Make(width, height, - colorType, kPremul_SkAlphaType); + colorType, kPremul_SkAlphaType, colorSpace); bitmap.setInfo(info); bitmap.allocPixels(info); return bitmap; diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp index a30ada0df453..5cab04d26c2a 100644 --- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp +++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -82,3 +83,9 @@ TEST(SkiaBehavior, porterDuffCreateIsCached) { paint.setXfermodeMode(SkXfermode::kOverlay_Mode); ASSERT_EQ(expected, paint.getXfermode()); } + +TEST(SkiaBehavior, srgbColorSpaceIsSingleton) { + sk_sp sRGB1 = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); + sk_sp sRGB2 = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); + ASSERT_EQ(sRGB1.get(), sRGB2.get()); +} diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h index b5157f401438..c8f8c7071075 100644 --- a/libs/hwui/utils/Color.h +++ b/libs/hwui/utils/Color.h @@ -16,6 +16,8 @@ #ifndef COLOR_H #define COLOR_H +#include + #include namespace android { @@ -80,6 +82,28 @@ namespace uirenderer { }; static constexpr int BrightColorsCount = sizeof(BrightColors) / sizeof(Color::Color); + // Opto-electronic conversion function for the sRGB color space + // Takes a gamma-encoded sRGB value and converts it to a linear sRGB value + static constexpr float OECF_sRGB(float linear) { +#ifdef ANDROID_ENABLE_LINEAR_BLENDING + // IEC 61966-2-1:1999 + return linear <= 0.0031308f ? + linear * 12.92f : (powf(linear, 1.0f / 2.4f) * 1.055f) - 0.055f; +#else + return linear; +#endif + } + + // Electro-optical conversion function for the sRGB color space + // Takes a linear sRGB value and converts it to a gamma-encoded sRGB value + static constexpr float EOCF_sRGB(float srgb) { +#ifdef ANDROID_ENABLE_LINEAR_BLENDING + // IEC 61966-2-1:1999 + return srgb <= 0.04045f ? srgb / 12.92f : powf((srgb + 0.055f) / 1.055f, 2.4f); +#else + return srgb; +#endif + } } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp index b879f781bce1..624d20763384 100644 --- a/libs/hwui/utils/TestWindowContext.cpp +++ b/libs/hwui/utils/TestWindowContext.cpp @@ -110,9 +110,10 @@ public: } bool capturePixels(SkBitmap* bmp) { + sk_sp colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); SkImageInfo destinationConfig = SkImageInfo::Make(mSize.width(), mSize.height(), - kRGBA_8888_SkColorType, kPremul_SkAlphaType); + kRGBA_8888_SkColorType, kPremul_SkAlphaType, colorSpace); bmp->allocPixels(destinationConfig); android_memset32((uint32_t*) bmp->getPixels(), SK_ColorRED, mSize.width() * mSize.height() * 4); diff --git a/preloaded-classes b/preloaded-classes index 42f290e22b3f..5ddd08b8cddf 100644 --- a/preloaded-classes +++ b/preloaded-classes @@ -2077,9 +2077,6 @@ android.view.Gravity android.view.HandlerActionQueue android.view.HandlerActionQueue$HandlerAction android.view.HardwareLayer -android.view.IAssetAtlas -android.view.IAssetAtlas$Stub -android.view.IAssetAtlas$Stub$Proxy android.view.IGraphicsStats android.view.IGraphicsStats$Stub android.view.IGraphicsStats$Stub$Proxy diff --git a/services/core/java/com/android/server/AssetAtlasService.java b/services/core/java/com/android/server/AssetAtlasService.java deleted file mode 100644 index b0f60482df1d..000000000000 --- a/services/core/java/com/android/server/AssetAtlasService.java +++ /dev/null @@ -1,717 +0,0 @@ -/* - * 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. - */ - -package com.android.server; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.graphics.Atlas; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.PixelFormat; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; -import android.graphics.drawable.Drawable; -import android.os.Environment; -import android.os.RemoteException; -import android.os.SystemProperties; -import android.util.Log; -import android.util.LongSparseArray; -import android.view.GraphicBuffer; -import android.view.IAssetAtlas; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * This service is responsible for packing preloaded bitmaps into a single - * atlas texture. The resulting texture can be shared across processes to - * reduce overall memory usage. - * - * @hide - */ -public class AssetAtlasService extends IAssetAtlas.Stub { - /** - * Name of the AssetAtlasService. - */ - public static final String ASSET_ATLAS_SERVICE = "assetatlas"; - - private static final String LOG_TAG = "AssetAtlas"; - - // Turns debug logs on/off. Debug logs are kept to a minimum and should - // remain on to diagnose issues - private static final boolean DEBUG_ATLAS = true; - - // When set to true the content of the atlas will be saved to disk - // in /data/system/atlas.png. The shared GraphicBuffer may be empty - private static final boolean DEBUG_ATLAS_TEXTURE = false; - - // Minimum size in pixels to consider for the resulting texture - private static final int MIN_SIZE = 512; - // Maximum size in pixels to consider for the resulting texture - private static final int MAX_SIZE = 2048; - // Increment in number of pixels between size variants when looking - // for the best texture dimensions - private static final int STEP = 64; - - // This percentage of the total number of pixels represents the minimum - // number of pixels we want to be able to pack in the atlas - private static final float PACKING_THRESHOLD = 0.8f; - - // Defines the number of int fields used to represent a single entry - // in the atlas map. This number defines the size of the array returned - // by the getMap(). See the mAtlasMap field for more information - private static final int ATLAS_MAP_ENTRY_FIELD_COUNT = 3; - - // Specifies how our GraphicBuffer will be used. To get proper swizzling - // the buffer will be written to using OpenGL (from JNI) so we can leave - // the software flag set to "never" - private static final int GRAPHIC_BUFFER_USAGE = GraphicBuffer.USAGE_SW_READ_NEVER | - GraphicBuffer.USAGE_SW_WRITE_NEVER | GraphicBuffer.USAGE_HW_TEXTURE; - - // This boolean is set to true if an atlas was successfully - // computed and rendered - private final AtomicBoolean mAtlasReady = new AtomicBoolean(false); - - private final Context mContext; - - // Version name of the current build, used to identify changes to assets list - private final String mVersionName; - - // Holds the atlas' data. This buffer can be mapped to - // OpenGL using an EGLImage - private GraphicBuffer mBuffer; - - // Describes how bitmaps are placed in the atlas. Each bitmap is - // represented by several entries in the array: - // long0: SkBitmap*, the native bitmap object - // long1: x position - // long2: y position - private long[] mAtlasMap; - - /** - * Creates a new service. Upon creating, the service will gather the list of - * assets to consider for packing into the atlas and spawn a new thread to - * start the packing work. - * - * @param context The context giving access to preloaded resources - */ - public AssetAtlasService(Context context) { - mContext = context; - mVersionName = queryVersionName(context); - - Collection bitmaps = new HashSet(300); - int totalPixelCount = 0; - - // We only care about drawables that hold bitmaps - final Resources resources = context.getResources(); - final LongSparseArray drawables = resources.getPreloadedDrawables(); - - final int count = drawables.size(); - for (int i = 0; i < count; i++) { - try { - totalPixelCount += drawables.valueAt(i).addAtlasableBitmaps(bitmaps); - } catch (Throwable t) { - Log.e("AssetAtlas", "Failed to fetch preloaded drawable state", t); - throw t; - } - } - - ArrayList sortedBitmaps = new ArrayList(bitmaps); - // Our algorithms perform better when the bitmaps are first sorted - // The comparator will sort the bitmap by width first, then by height - Collections.sort(sortedBitmaps, new Comparator() { - @Override - public int compare(Bitmap b1, Bitmap b2) { - if (b1.getWidth() == b2.getWidth()) { - return b2.getHeight() - b1.getHeight(); - } - return b2.getWidth() - b1.getWidth(); - } - }); - - // Kick off the packing work on a worker thread - new Thread(new Renderer(sortedBitmaps, totalPixelCount)).start(); - } - - /** - * Queries the version name stored in framework's AndroidManifest. - * The version name can be used to identify possible changes to - * framework resources. - * - * @see #getBuildIdentifier(String) - */ - private static String queryVersionName(Context context) { - try { - String packageName = context.getPackageName(); - PackageInfo info = context.getPackageManager().getPackageInfo(packageName, - PackageManager.MATCH_DEBUG_TRIAGED_MISSING); - return info.versionName; - } catch (PackageManager.NameNotFoundException e) { - Log.w(LOG_TAG, "Could not get package info", e); - } - return null; - } - - /** - * Callback invoked by the server thread to indicate we can now run - * 3rd party code. - */ - public void systemRunning() { - } - - /** - * The renderer does all the work: - */ - private class Renderer implements Runnable { - private final ArrayList mBitmaps; - private final int mPixelCount; - - Renderer(ArrayList bitmaps, int pixelCount) { - mBitmaps = bitmaps; - mPixelCount = pixelCount; - } - - /** - * 1. On first boot or after every update, brute-force through all the - * possible atlas configurations and look for the best one (maximimize - * number of packed assets and minimize texture size) - * a. If a best configuration was computed, write it out to disk for - * future use - * 2. Read best configuration from disk - * 3. Compute the packing using the best configuration - * 4. Allocate a GraphicBuffer - * 5. Render assets in the buffer - */ - @Override - public void run() { - Configuration config = chooseConfiguration(mBitmaps, mPixelCount, mVersionName); - if (DEBUG_ATLAS) Log.d(LOG_TAG, "Loaded configuration: " + config); - - if (config != null) { - mBuffer = GraphicBuffer.create(config.width, config.height, - PixelFormat.RGBA_8888, GRAPHIC_BUFFER_USAGE); - - if (mBuffer != null) { - Atlas atlas = new Atlas(config.type, config.width, config.height, config.flags); - if (renderAtlas(mBuffer, atlas, config.count)) { - mAtlasReady.set(true); - } - } - } - } - - /** - * Renders a list of bitmaps into the atlas. The position of each bitmap - * was decided by the packing algorithm and will be honored by this - * method. - * - * @param buffer The buffer to render the atlas entries into - * @param atlas The atlas to pack the bitmaps into - * @param packCount The number of bitmaps that will be packed in the atlas - * - * @return true if the atlas was rendered, false otherwise - */ - @SuppressWarnings("MismatchedReadAndWriteOfArray") - private boolean renderAtlas(GraphicBuffer buffer, Atlas atlas, int packCount) { - // Use a Source blend mode to improve performance, the target bitmap - // will be zero'd out so there's no need to waste time applying blending - final Paint paint = new Paint(); - paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); - - // We always render the atlas into a bitmap. This bitmap is then - // uploaded into the GraphicBuffer using OpenGL to swizzle the content - final Bitmap atlasBitmap = Bitmap.createBitmap( - buffer.getWidth(), buffer.getHeight(), Bitmap.Config.ARGB_8888); - final Canvas canvas = new Canvas(atlasBitmap); - - final Atlas.Entry entry = new Atlas.Entry(); - - mAtlasMap = new long[packCount * ATLAS_MAP_ENTRY_FIELD_COUNT]; - long[] atlasMap = mAtlasMap; - int mapIndex = 0; - - boolean result = false; - final long startRender = System.nanoTime(); - final int count = mBitmaps.size(); - - for (int i = 0; i < count; i++) { - final Bitmap bitmap = mBitmaps.get(i); - if (atlas.pack(bitmap.getWidth(), bitmap.getHeight(), entry) != null) { - // We have more bitmaps to pack than the current configuration - // says, we were most likely not able to detect a change in the - // list of preloaded drawables, abort and delete the configuration - if (mapIndex >= mAtlasMap.length) { - deleteDataFile(); - break; - } - - canvas.save(); - canvas.translate(entry.x, entry.y); - canvas.drawBitmap(bitmap, 0.0f, 0.0f, null); - canvas.restore(); - atlasMap[mapIndex++] = bitmap.refSkPixelRef(); - atlasMap[mapIndex++] = entry.x; - atlasMap[mapIndex++] = entry.y; - } - } - - final long endRender = System.nanoTime(); - releaseCanvas(canvas, atlasBitmap); - result = nUploadAtlas(buffer, atlasBitmap); - atlasBitmap.recycle(); - final long endUpload = System.nanoTime(); - - if (DEBUG_ATLAS) { - float renderDuration = (endRender - startRender) / 1000.0f / 1000.0f; - float uploadDuration = (endUpload - endRender) / 1000.0f / 1000.0f; - Log.d(LOG_TAG, String.format("Rendered atlas in %.2fms (%.2f+%.2fms)", - renderDuration + uploadDuration, renderDuration, uploadDuration)); - } - - return result; - } - - /** - * Releases the canvas used to render into the buffer. Calling this method - * will release any resource previously acquired. If {@link #DEBUG_ATLAS_TEXTURE} - * is turend on, calling this method will write the content of the atlas - * to disk in /data/system/atlas.png for debugging. - */ - private void releaseCanvas(Canvas canvas, Bitmap atlasBitmap) { - canvas.setBitmap(null); - if (DEBUG_ATLAS_TEXTURE) { - - File systemDirectory = new File(Environment.getDataDirectory(), "system"); - File dataFile = new File(systemDirectory, "atlas.png"); - - try { - FileOutputStream out = new FileOutputStream(dataFile); - atlasBitmap.compress(Bitmap.CompressFormat.PNG, 100, out); - out.close(); - } catch (FileNotFoundException e) { - // Ignore - } catch (IOException e) { - // Ignore - } - } - } - } - - private static native boolean nUploadAtlas(GraphicBuffer buffer, Bitmap bitmap); - - @Override - public boolean isCompatible(int ppid) { - return ppid == android.os.Process.myPpid(); - } - - @Override - public GraphicBuffer getBuffer() throws RemoteException { - return mAtlasReady.get() ? mBuffer : null; - } - - @Override - public long[] getMap() throws RemoteException { - return mAtlasReady.get() ? mAtlasMap : null; - } - - /** - * Finds the best atlas configuration to pack the list of supplied bitmaps. - * This method takes advantage of multi-core systems by spawning a number - * of threads equal to the number of available cores. - */ - private static Configuration computeBestConfiguration( - ArrayList bitmaps, int pixelCount) { - if (DEBUG_ATLAS) Log.d(LOG_TAG, "Computing best atlas configuration..."); - - long begin = System.nanoTime(); - List results = Collections.synchronizedList(new ArrayList()); - - // Don't bother with an extra thread if there's only one processor - int cpuCount = Runtime.getRuntime().availableProcessors(); - if (cpuCount == 1) { - new ComputeWorker(MIN_SIZE, MAX_SIZE, STEP, bitmaps, pixelCount, results, null).run(); - } else { - int start = MIN_SIZE + (cpuCount - 1) * STEP; - int end = MAX_SIZE; - int step = STEP * cpuCount; - - final CountDownLatch signal = new CountDownLatch(cpuCount); - - for (int i = 0; i < cpuCount; i++, start -= STEP, end -= STEP) { - ComputeWorker worker = new ComputeWorker(start, end, step, - bitmaps, pixelCount, results, signal); - new Thread(worker, "Atlas Worker #" + (i + 1)).start(); - } - - boolean isAllWorkerFinished; - try { - isAllWorkerFinished = signal.await(10, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Log.w(LOG_TAG, "Could not complete configuration computation"); - return null; - } - - if (!isAllWorkerFinished) { - // We have to abort here, otherwise the async updates on "results" would crash the - // sort later. - Log.w(LOG_TAG, "Could not complete configuration computation before timeout."); - return null; - } - } - - // Maximize the number of packed bitmaps, minimize the texture size - Collections.sort(results, new Comparator() { - @Override - public int compare(WorkerResult r1, WorkerResult r2) { - int delta = r2.count - r1.count; - if (delta != 0) return delta; - return r1.width * r1.height - r2.width * r2.height; - } - }); - - if (DEBUG_ATLAS) { - float delay = (System.nanoTime() - begin) / 1000.0f / 1000.0f / 1000.0f; - Log.d(LOG_TAG, String.format("Found best atlas configuration (out of %d) in %.2fs", - results.size(), delay)); - } - - WorkerResult result = results.get(0); - return new Configuration(result.type, result.width, result.height, result.count); - } - - /** - * Returns the path to the file containing the best computed - * atlas configuration. - */ - private static File getDataFile() { - File systemDirectory = new File(Environment.getDataDirectory(), "system"); - return new File(systemDirectory, "framework_atlas.config"); - } - - private static void deleteDataFile() { - Log.w(LOG_TAG, "Current configuration inconsistent with assets list"); - if (!getDataFile().delete()) { - Log.w(LOG_TAG, "Could not delete the current configuration"); - } - } - - private File getFrameworkResourcesFile() { - return new File(mContext.getApplicationInfo().sourceDir); - } - - /** - * Returns the best known atlas configuration. This method will either - * read the configuration from disk or start a brute-force search - * and save the result out to disk. - */ - private Configuration chooseConfiguration(ArrayList bitmaps, int pixelCount, - String versionName) { - Configuration config = null; - - final File dataFile = getDataFile(); - if (dataFile.exists()) { - config = readConfiguration(dataFile, versionName); - } - - if (config == null) { - config = computeBestConfiguration(bitmaps, pixelCount); - if (config != null) writeConfiguration(config, dataFile, versionName); - } - - return config; - } - - /** - * Writes the specified atlas configuration to the specified file. - */ - private void writeConfiguration(Configuration config, File file, String versionName) { - BufferedWriter writer = null; - try { - writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))); - writer.write(getBuildIdentifier(versionName)); - writer.newLine(); - writer.write(config.type.toString()); - writer.newLine(); - writer.write(String.valueOf(config.width)); - writer.newLine(); - writer.write(String.valueOf(config.height)); - writer.newLine(); - writer.write(String.valueOf(config.count)); - writer.newLine(); - writer.write(String.valueOf(config.flags)); - writer.newLine(); - } catch (FileNotFoundException e) { - Log.w(LOG_TAG, "Could not write " + file, e); - } catch (IOException e) { - Log.w(LOG_TAG, "Could not write " + file, e); - } finally { - if (writer != null) { - try { - writer.close(); - } catch (IOException e) { - // Ignore - } - } - } - } - - /** - * Reads an atlas configuration from the specified file. This method - * returns null if an error occurs or if the configuration is invalid. - */ - private Configuration readConfiguration(File file, String versionName) { - BufferedReader reader = null; - Configuration config = null; - try { - reader = new BufferedReader(new InputStreamReader(new FileInputStream(file))); - - if (checkBuildIdentifier(reader, versionName)) { - Atlas.Type type = Atlas.Type.valueOf(reader.readLine()); - int width = readInt(reader, MIN_SIZE, MAX_SIZE); - int height = readInt(reader, MIN_SIZE, MAX_SIZE); - int count = readInt(reader, 0, Integer.MAX_VALUE); - int flags = readInt(reader, Integer.MIN_VALUE, Integer.MAX_VALUE); - - config = new Configuration(type, width, height, count, flags); - } - } catch (IllegalArgumentException e) { - Log.w(LOG_TAG, "Invalid parameter value in " + file, e); - } catch (FileNotFoundException e) { - Log.w(LOG_TAG, "Could not read " + file, e); - } catch (IOException e) { - Log.w(LOG_TAG, "Could not read " + file, e); - } finally { - if (reader != null) { - try { - reader.close(); - } catch (IOException e) { - // Ignore - } - } - } - return config; - } - - private static int readInt(BufferedReader reader, int min, int max) throws IOException { - return Math.max(min, Math.min(max, Integer.parseInt(reader.readLine()))); - } - - /** - * Compares the next line in the specified buffered reader to the current - * build identifier. Returns whether the two values are equal. - * - * @see #getBuildIdentifier(String) - */ - private boolean checkBuildIdentifier(BufferedReader reader, String versionName) - throws IOException { - String deviceBuildId = getBuildIdentifier(versionName); - String buildId = reader.readLine(); - return deviceBuildId.equals(buildId); - } - - /** - * Returns an identifier for the current build that can be used to detect - * likely changes to framework resources. The build identifier is made of - * several distinct values: - * - * build fingerprint/framework version name/file size of framework resources apk - * - * Only the build fingerprint should be necessary on user builds but - * the other values are useful to detect changes on eng builds during - * development. - * - * This identifier does not attempt to be exact: a new identifier does not - * necessarily mean the preloaded drawables have changed. It is important - * however that whenever the list of preloaded drawables changes, this - * identifier changes as well. - * - * @see #checkBuildIdentifier(java.io.BufferedReader, String) - */ - private String getBuildIdentifier(String versionName) { - return SystemProperties.get("ro.build.fingerprint", "") + '/' + versionName + '/' + - String.valueOf(getFrameworkResourcesFile().length()); - } - - /** - * Atlas configuration. Specifies the algorithm, dimensions and flags to use. - */ - private static class Configuration { - final Atlas.Type type; - final int width; - final int height; - final int count; - final int flags; - - Configuration(Atlas.Type type, int width, int height, int count) { - this(type, width, height, count, Atlas.FLAG_DEFAULTS); - } - - Configuration(Atlas.Type type, int width, int height, int count, int flags) { - this.type = type; - this.width = width; - this.height = height; - this.count = count; - this.flags = flags; - } - - @Override - public String toString() { - return type.toString() + " (" + width + "x" + height + ") flags=0x" + - Integer.toHexString(flags) + " count=" + count; - } - } - - /** - * Used during the brute-force search to gather information about each - * variant of the packing algorithm. - */ - private static class WorkerResult { - Atlas.Type type; - int width; - int height; - int count; - - WorkerResult(Atlas.Type type, int width, int height, int count) { - this.type = type; - this.width = width; - this.height = height; - this.count = count; - } - - @Override - public String toString() { - return String.format("%s %dx%d", type.toString(), width, height); - } - } - - /** - * A compute worker will try a finite number of variations of the packing - * algorithms and save the results in a supplied list. - */ - private static class ComputeWorker implements Runnable { - private final int mStart; - private final int mEnd; - private final int mStep; - private final List mBitmaps; - private final List mResults; - private final CountDownLatch mSignal; - private final int mThreshold; - - /** - * Creates a new compute worker to brute-force through a range of - * packing algorithms variants. - * - * @param start The minimum texture width to try - * @param end The maximum texture width to try - * @param step The number of pixels to increment the texture width by at each step - * @param bitmaps The list of bitmaps to pack in the atlas - * @param pixelCount The total number of pixels occupied by the list of bitmaps - * @param results The list of results in which to save the brute-force search results - * @param signal Latch to decrement when this worker is done, may be null - */ - ComputeWorker(int start, int end, int step, List bitmaps, int pixelCount, - List results, CountDownLatch signal) { - mStart = start; - mEnd = end; - mStep = step; - mBitmaps = bitmaps; - mResults = results; - mSignal = signal; - - // Minimum number of pixels we want to be able to pack - int threshold = (int) (pixelCount * PACKING_THRESHOLD); - // Make sure we can find at least one configuration - while (threshold > MAX_SIZE * MAX_SIZE) { - threshold >>= 1; - } - mThreshold = threshold; - } - - @Override - public void run() { - if (DEBUG_ATLAS) Log.d(LOG_TAG, "Running " + Thread.currentThread().getName()); - - Atlas.Entry entry = new Atlas.Entry(); - - for (int width = mEnd; width > mStart; width -= mStep) { - for (int height = MAX_SIZE; height > MIN_SIZE; height -= STEP) { - // If the atlas is not big enough, skip it - if (width * height <= mThreshold) continue; - - boolean packSuccess = false; - - for (Atlas.Type type : Atlas.Type.values()) { - final int count = packBitmaps(type, width, height, entry); - if (count > 0) { - mResults.add(new WorkerResult(type, width, height, count)); - if (count == mBitmaps.size()) { - // If we were able to pack everything let's stop here - // Changing the type further won't make things better - packSuccess = true; - break; - } - } - } - - // If we were not able to pack everything let's stop here - // Decreasing the height further won't make things better - if (!packSuccess) { - break; - } - } - } - - if (mSignal != null) { - mSignal.countDown(); - } - } - - private int packBitmaps(Atlas.Type type, int width, int height, Atlas.Entry entry) { - int total = 0; - Atlas atlas = new Atlas(type, width, height); - - final int count = mBitmaps.size(); - for (int i = 0; i < count; i++) { - final Bitmap bitmap = mBitmaps.get(i); - if (atlas.pack(bitmap.getWidth(), bitmap.getHeight(), entry) != null) { - total++; - } - } - - return total; - } - } -} diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk index 9459517caed7..a65dd99bf640 100644 --- a/services/core/jni/Android.mk +++ b/services/core/jni/Android.mk @@ -7,7 +7,6 @@ LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter LOCAL_SRC_FILES += \ $(LOCAL_REL_DIR)/com_android_server_AlarmManagerService.cpp \ $(LOCAL_REL_DIR)/com_android_server_am_BatteryStatsService.cpp \ - $(LOCAL_REL_DIR)/com_android_server_AssetAtlasService.cpp \ $(LOCAL_REL_DIR)/com_android_server_connectivity_Vpn.cpp \ $(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \ $(LOCAL_REL_DIR)/com_android_server_HardwarePropertiesManagerService.cpp \ diff --git a/services/core/jni/com_android_server_AssetAtlasService.cpp b/services/core/jni/com_android_server_AssetAtlasService.cpp deleted file mode 100644 index d004e3022230..000000000000 --- a/services/core/jni/com_android_server_AssetAtlasService.cpp +++ /dev/null @@ -1,216 +0,0 @@ -/* - * 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 "AssetAtlasService" - -#include "jni.h" -#include "JNIHelp.h" -#include "android/graphics/GraphicsJNI.h" - -#include -#include - -#include -#include - -#include -#include - -// Disable warnings for Skia. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#include -#include -#pragma GCC diagnostic pop - -namespace android { - -// ---------------------------------------------------------------------------- -// Defines -// ---------------------------------------------------------------------------- - -// Defines how long to wait for the GPU when uploading the atlas -// This timeout is defined in nanoseconds (see EGL_KHR_fence_sync extension) -#define FENCE_TIMEOUT 2000000000 - -// ---------------------------------------------------------------------------- -// Canvas management -// ---------------------------------------------------------------------------- - -#define CLEANUP_GL_AND_RETURN(result) \ - if (fence != EGL_NO_SYNC_KHR) eglDestroySyncKHR(display, fence); \ - if (image) eglDestroyImageKHR(display, image); \ - if (texture) glDeleteTextures(1, &texture); \ - if (surface != EGL_NO_SURFACE) eglDestroySurface(display, surface); \ - if (context != EGL_NO_CONTEXT) eglDestroyContext(display, context); \ - eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); \ - eglReleaseThread(); \ - eglTerminate(display); \ - return result; - -static jboolean com_android_server_AssetAtlasService_upload(JNIEnv* env, jobject, - jobject graphicBuffer, jobject bitmapHandle) { - - SkBitmap bitmap; - GraphicsJNI::getSkBitmap(env, bitmapHandle, &bitmap); - SkAutoLockPixels alp(bitmap); - - // The goal of this method is to copy the bitmap into the GraphicBuffer - // using the GPU to swizzle the texture content - sp buffer(graphicBufferForJavaObject(env, graphicBuffer)); - - if (buffer != NULL) { - EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (display == EGL_NO_DISPLAY) return JNI_FALSE; - - EGLint major; - EGLint minor; - if (!eglInitialize(display, &major, &minor)) { - ALOGW("Could not initialize EGL"); - return JNI_FALSE; - } - - // We're going to use a 1x1 pbuffer surface later on - // The configuration doesn't really matter for what we're trying to do - EGLint configAttrs[] = { - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_ALPHA_SIZE, 0, - EGL_DEPTH_SIZE, 0, - EGL_STENCIL_SIZE, 0, - EGL_NONE - }; - EGLConfig configs[1]; - EGLint configCount; - if (!eglChooseConfig(display, configAttrs, configs, 1, &configCount)) { - ALOGW("Could not select EGL configuration"); - eglReleaseThread(); - eglTerminate(display); - return JNI_FALSE; - } - if (configCount <= 0) { - ALOGW("Could not find EGL configuration"); - eglReleaseThread(); - eglTerminate(display); - return JNI_FALSE; - } - - // These objects are initialized below but the default "null" - // values are used to cleanup properly at any point in the - // initialization sequence - GLuint texture = 0; - EGLImageKHR image = EGL_NO_IMAGE_KHR; - EGLSurface surface = EGL_NO_SURFACE; - EGLSyncKHR fence = EGL_NO_SYNC_KHR; - - EGLint attrs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; - EGLContext context = eglCreateContext(display, configs[0], EGL_NO_CONTEXT, attrs); - if (context == EGL_NO_CONTEXT) { - ALOGW("Could not create EGL context"); - CLEANUP_GL_AND_RETURN(JNI_FALSE); - } - - // Create the 1x1 pbuffer - EGLint surfaceAttrs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; - surface = eglCreatePbufferSurface(display, configs[0], surfaceAttrs); - if (surface == EGL_NO_SURFACE) { - ALOGW("Could not create EGL surface"); - CLEANUP_GL_AND_RETURN(JNI_FALSE); - } - - if (!eglMakeCurrent(display, surface, surface, context)) { - ALOGW("Could not change current EGL context"); - CLEANUP_GL_AND_RETURN(JNI_FALSE); - } - - // We use an EGLImage to access the content of the GraphicBuffer - // The EGL image is later bound to a 2D texture - EGLClientBuffer clientBuffer = (EGLClientBuffer) buffer->getNativeBuffer(); - EGLint imageAttrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; - image = eglCreateImageKHR(display, EGL_NO_CONTEXT, - EGL_NATIVE_BUFFER_ANDROID, clientBuffer, imageAttrs); - if (image == EGL_NO_IMAGE_KHR) { - ALOGW("Could not create EGL image"); - CLEANUP_GL_AND_RETURN(JNI_FALSE); - } - - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); - if (glGetError() != GL_NO_ERROR) { - ALOGW("Could not create/bind texture"); - CLEANUP_GL_AND_RETURN(JNI_FALSE); - } - - // Upload the content of the bitmap in the GraphicBuffer - glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap.bytesPerPixel()); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(), - GL_RGBA, GL_UNSIGNED_BYTE, bitmap.getPixels()); - if (glGetError() != GL_NO_ERROR) { - ALOGW("Could not upload to texture"); - CLEANUP_GL_AND_RETURN(JNI_FALSE); - } - - // The fence is used to wait for the texture upload to finish - // properly. We cannot rely on glFlush() and glFinish() as - // some drivers completely ignore these API calls - fence = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, NULL); - if (fence == EGL_NO_SYNC_KHR) { - ALOGW("Could not create sync fence %#x", eglGetError()); - CLEANUP_GL_AND_RETURN(JNI_FALSE); - } - - // The flag EGL_SYNC_FLUSH_COMMANDS_BIT_KHR will trigger a - // pipeline flush (similar to what a glFlush() would do.) - EGLint waitStatus = eglClientWaitSyncKHR(display, fence, - EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, FENCE_TIMEOUT); - if (waitStatus != EGL_CONDITION_SATISFIED_KHR) { - ALOGW("Failed to wait for the fence %#x", eglGetError()); - CLEANUP_GL_AND_RETURN(JNI_FALSE); - } - - CLEANUP_GL_AND_RETURN(JNI_TRUE); - } - - return JNI_FALSE; -} - -// ---------------------------------------------------------------------------- -// JNI Glue -// ---------------------------------------------------------------------------- - -#define FIND_CLASS(var, className) \ - var = env->FindClass(className); \ - LOG_FATAL_IF(! (var), "Unable to find class " className); - -#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ - var = env->GetMethodID(clazz, methodName, methodDescriptor); \ - LOG_FATAL_IF(!(var), "Unable to find method " methodName); - -const char* const kClassPathName = "com/android/server/AssetAtlasService"; - -static const JNINativeMethod gMethods[] = { - { "nUploadAtlas", "(Landroid/view/GraphicBuffer;Landroid/graphics/Bitmap;)Z", - (void*) com_android_server_AssetAtlasService_upload }, -}; - -int register_android_server_AssetAtlasService(JNIEnv* env) { - return jniRegisterNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); -} - -}; diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 327019d6bbd5..d69c37f0afea 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -21,7 +21,6 @@ namespace android { int register_android_server_AlarmManagerService(JNIEnv* env); -int register_android_server_AssetAtlasService(JNIEnv* env); int register_android_server_BatteryStatsService(JNIEnv* env); int register_android_server_ConsumerIrService(JNIEnv *env); int register_android_server_InputApplicationHandle(JNIEnv* env); @@ -76,7 +75,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_location_GnssLocationProvider(env); register_android_server_location_FlpHardwareProvider(env); register_android_server_connectivity_Vpn(env); - register_android_server_AssetAtlasService(env); register_android_server_ConsumerIrService(env); register_android_server_BatteryStatsService(env); register_android_server_hdmi_HdmiCecController(env); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 769b5ee92997..deb52383fb6b 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -747,7 +747,6 @@ public final class SystemServer { LocationManagerService location = null; CountryDetectorService countryDetector = null; ILockSettings lockSettings = null; - AssetAtlasService atlas = null; MediaRouterService mediaRouter = null; // Bring up services needed for UI. @@ -1235,17 +1234,6 @@ public final class SystemServer { traceEnd(); } - if (!disableNonCoreServices && ZygoteInit.PRELOAD_RESOURCES) { - traceBeginAndSlog("StartAssetAtlasService"); - try { - atlas = new AssetAtlasService(context); - ServiceManager.addService(AssetAtlasService.ASSET_ATLAS_SERVICE, atlas); - } catch (Throwable e) { - reportWtf("starting AssetAtlasService", e); - } - traceEnd(); - } - if (!disableNonCoreServices) { traceBeginAndSlog("AddGraphicsStatsService"); ServiceManager.addService(GraphicsStatsService.GRAPHICS_STATS_SERVICE, @@ -1465,7 +1453,6 @@ public final class SystemServer { final CountryDetectorService countryDetectorF = countryDetector; final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater; final CommonTimeManagementService commonTimeMgmtServiceF = commonTimeMgmtService; - final AssetAtlasService atlasF = atlas; final InputManagerService inputManagerF = inputManager; final TelephonyRegistry telephonyRegistryF = telephonyRegistry; final MediaRouterService mediaRouterF = mediaRouter; @@ -1583,13 +1570,6 @@ public final class SystemServer { reportWtf("Notifying CommonTimeManagementService running", e); } traceEnd(); - traceBeginAndSlog("MakeAtlasServiceReady"); - try { - if (atlasF != null) atlasF.systemRunning(); - } catch (Throwable e) { - reportWtf("Notifying AssetAtlasService running", e); - } - traceEnd(); traceBeginAndSlog("MakeInputManagerServiceReady"); try { // TODO(BT) Pass parameter to input manager diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java index bcfe3bffae17..090cee81bf95 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java @@ -601,14 +601,6 @@ public final class Bitmap_Delegate { return Arrays.equals(argb1, argb2); } - // Only used by AssetAtlasService, which we don't care about. - @LayoutlibDelegate - /*package*/ static long nativeRefPixelRef(long nativeBitmap) { - // Hack: This is called by Bitmap.refSkPixelRef() and LayoutLib uses that method to get - // the native pointer from a Bitmap. So, we return nativeBitmap here. - return nativeBitmap; - } - // ---- Private delegate/helper methods ---- private Bitmap_Delegate(BufferedImage image, Config config) { -- 2.11.0