summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--compiled-classes-phone11
-rw-r--r--core/java/android/view/IAssetAtlas.aidl54
-rw-r--r--core/java/android/view/ThreadedRenderer.java28
-rw-r--r--core/jni/Android.mk8
-rwxr-xr-xcore/jni/android/graphics/Bitmap.cpp26
-rw-r--r--core/jni/android/graphics/BitmapFactory.cpp19
-rw-r--r--core/jni/android/graphics/Graphics.cpp8
-rw-r--r--core/jni/android/graphics/GraphicsJNI.h3
-rw-r--r--core/jni/android_graphics_Canvas.cpp5
-rw-r--r--core/jni/android_view_GraphicBuffer.cpp3
-rw-r--r--core/jni/android_view_Surface.cpp6
-rw-r--r--core/jni/android_view_SurfaceControl.cpp4
-rw-r--r--core/jni/android_view_TextureView.cpp3
-rw-r--r--core/jni/android_view_ThreadedRenderer.cpp16
-rw-r--r--graphics/java/android/graphics/Atlas.java416
-rw-r--r--graphics/java/android/graphics/Bitmap.java11
-rw-r--r--graphics/java/android/graphics/drawable/BitmapDrawable.java9
-rw-r--r--graphics/java/android/graphics/drawable/Drawable.java14
-rw-r--r--graphics/java/android/graphics/drawable/DrawableContainer.java16
-rw-r--r--graphics/java/android/graphics/drawable/DrawableWrapper.java11
-rw-r--r--graphics/java/android/graphics/drawable/LayerDrawable.java18
-rw-r--r--graphics/java/android/graphics/drawable/NinePatchDrawable.java10
-rw-r--r--libs/hwui/Android.mk11
-rw-r--r--libs/hwui/AssetAtlas.cpp130
-rw-r--r--libs/hwui/AssetAtlas.h174
-rw-r--r--libs/hwui/BakedOpDispatcher.cpp52
-rw-r--r--libs/hwui/BakedOpRenderer.cpp6
-rw-r--r--libs/hwui/Caches.cpp2
-rw-r--r--libs/hwui/Caches.h15
-rw-r--r--libs/hwui/Dither.cpp98
-rw-r--r--libs/hwui/Dither.h56
-rw-r--r--libs/hwui/Extensions.cpp9
-rw-r--r--libs/hwui/Extensions.h4
-rw-r--r--libs/hwui/FloatColor.h9
-rw-r--r--libs/hwui/FontRenderer.cpp13
-rw-r--r--libs/hwui/FrameBuilder.cpp2
-rw-r--r--libs/hwui/GammaFontRenderer.cpp3
-rw-r--r--libs/hwui/GammaFontRenderer.h13
-rw-r--r--libs/hwui/GlopBuilder.cpp41
-rw-r--r--libs/hwui/GlopBuilder.h2
-rw-r--r--libs/hwui/GradientCache.cpp72
-rw-r--r--libs/hwui/GradientCache.h23
-rw-r--r--libs/hwui/Layer.h2
-rw-r--r--libs/hwui/PatchCache.cpp6
-rw-r--r--libs/hwui/PatchCache.h4
-rw-r--r--libs/hwui/Program.h7
-rw-r--r--libs/hwui/ProgramCache.cpp172
-rw-r--r--libs/hwui/ProgramCache.h1
-rw-r--r--libs/hwui/Properties.h4
-rw-r--r--libs/hwui/PropertyValuesHolder.cpp23
-rw-r--r--libs/hwui/Readback.cpp2
-rw-r--r--libs/hwui/RecordedOp.h1
-rw-r--r--libs/hwui/SkiaShader.cpp23
-rw-r--r--libs/hwui/SkiaShader.h5
-rw-r--r--libs/hwui/Texture.cpp83
-rw-r--r--libs/hwui/Texture.h15
-rw-r--r--libs/hwui/TextureCache.cpp25
-rw-r--r--libs/hwui/TextureCache.h30
-rw-r--r--libs/hwui/VectorDrawable.cpp8
-rw-r--r--libs/hwui/Vertex.h17
-rw-r--r--libs/hwui/font/CacheTexture.cpp7
-rw-r--r--libs/hwui/renderstate/OffscreenBufferPool.cpp3
-rw-r--r--libs/hwui/renderstate/RenderState.cpp12
-rw-r--r--libs/hwui/renderstate/RenderState.h3
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp5
-rw-r--r--libs/hwui/renderthread/CanvasContext.h3
-rw-r--r--libs/hwui/renderthread/EglManager.cpp42
-rw-r--r--libs/hwui/renderthread/EglManager.h7
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp17
-rw-r--r--libs/hwui/renderthread/RenderProxy.h1
-rw-r--r--libs/hwui/tests/common/TestUtils.h3
-rw-r--r--libs/hwui/tests/unit/SkiaBehaviorTests.cpp7
-rw-r--r--libs/hwui/utils/Color.h24
-rw-r--r--libs/hwui/utils/TestWindowContext.cpp3
-rw-r--r--preloaded-classes3
-rw-r--r--services/core/java/com/android/server/AssetAtlasService.java717
-rw-r--r--services/core/jni/Android.mk1
-rw-r--r--services/core/jni/com_android_server_AssetAtlasService.cpp216
-rw-r--r--services/core/jni/onload.cpp2
-rw-r--r--services/java/com/android/server/SystemServer.java20
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java8
82 files changed, 492 insertions, 2475 deletions
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<SkColorSpace>(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<SkBitmap> 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<Bitmap*>(bitmapHandle);
androidBitmap->getSkBitmap(&bitmap);
+ sk_sp<SkColorSpace> 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<Bitmap*>(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<jlong>(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<SkColorSpace> 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<SkColorSpace> 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 <jni.h>
#include <hwui/Canvas.h>
@@ -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<SkColorSpace> 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<GraphicBuffer> 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<RenderProxy*>(proxyPtr);
- proxy->setTextureAtlas(buffer, map, len);
-}
-
static void android_view_ThreadedRenderer_setProcessStatsBuffer(JNIEnv* env, jobject clazz,
jlong proxyPtr, jint fd) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(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
- * <code>freeWidth</code> and <code>freeHeight</code>
- * 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<Bitmap> 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<Bitmap> 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<Bitmap> 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<Bitmap> 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<Bitmap> 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<Bitmap> 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 <GLES2/gl2ext.h>
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Lifecycle
-///////////////////////////////////////////////////////////////////////////////
-
-void AssetAtlas::init(const sp<GraphicBuffer>& 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<SkPixelRef*>(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<int>(map[i++]);
- const int y = static_cast<int>(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> 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 <cutils/compiler.h>
-#include <GLES2/gl2.h>
-#include <ui/GraphicBuffer.h>
-#include <SkBitmap.h>
-
-#include <memory>
-#include <unordered_map>
-
-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<GraphicBuffer>& 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<const SkPixelRef*, std::unique_ptr<Entry>> 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<const BitmapOp*>(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<const PatchOp*>(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<ColorTextureVertex[]> 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 <GLES3/gl3.h>
-
-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 <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
+
#include <utils/Log.h>
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<mergeid_t>(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<mergeid_t>(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 <SkPaint.h>
-
-#include <utils/String8.h>
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<FontRenderer> 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 <utils/LruCache.h>
#include <utils/Mutex.h>
+#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<GradientCacheEntry, Texture*> 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 <androidfw/ResourceTypes.h>
-#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 <utils/String8.h>
#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<programid, std::unique_ptr<Program>> 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 <utils/Log.h>
@@ -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<SkColorSpace> 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 <utils/Mutex.h>
-#include "AssetAtlas.h"
#include "Caches.h"
#include "Texture.h"
#include "TextureCache.h"
@@ -36,8 +35,7 @@ TextureCache::TextureCache()
: mCache(LruCache<uint32_t, Texture*>::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<uint32_t, Texture*>::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<uint32_t, Texture*> mCache;
@@ -155,8 +135,6 @@ private:
std::vector<uint32_t> 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<SkColorSpace> colorSpace = nullptr;
+#else
+ sk_sp<SkColorSpace> 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 <utils/Color.h>
#include <utils/Log.h>
#include <GLES2/gl2.h>
@@ -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<Layer*> mActiveLayers;
std::set<renderthread::CanvasContext*> 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<GraphicBuffer>& 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<GraphicBuffer>& 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<GraphicBuffer>& 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<GraphicBuffer>& 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<GraphicBuffer> 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<GraphicBuffer>& 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<GraphicBuffer>& 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<SkColorSpace> 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 <gtest/gtest.h>
#include <SkColorMatrixFilter.h>
+#include <SkColorSpace.h>
#include <SkImagePriv.h>
#include <SkShader.h>
@@ -82,3 +83,9 @@ TEST(SkiaBehavior, porterDuffCreateIsCached) {
paint.setXfermodeMode(SkXfermode::kOverlay_Mode);
ASSERT_EQ(expected, paint.getXfermode());
}
+
+TEST(SkiaBehavior, srgbColorSpaceIsSingleton) {
+ sk_sp<SkColorSpace> sRGB1 = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
+ sk_sp<SkColorSpace> 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 <math.h>
+
#include <SkColor.h>
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<SkColorSpace> 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 <code>AssetAtlasService</code>.
- */
- 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<Bitmap> bitmaps = new HashSet<Bitmap>(300);
- int totalPixelCount = 0;
-
- // We only care about drawables that hold bitmaps
- final Resources resources = context.getResources();
- final LongSparseArray<Drawable.ConstantState> 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<Bitmap> sortedBitmaps = new ArrayList<Bitmap>(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<Bitmap>() {
- @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<Bitmap> mBitmaps;
- private final int mPixelCount;
-
- Renderer(ArrayList<Bitmap> 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<Bitmap> bitmaps, int pixelCount) {
- if (DEBUG_ATLAS) Log.d(LOG_TAG, "Computing best atlas configuration...");
-
- long begin = System.nanoTime();
- List<WorkerResult> results = Collections.synchronizedList(new ArrayList<WorkerResult>());
-
- // 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<WorkerResult>() {
- @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<Bitmap> 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<Bitmap> mBitmaps;
- private final List<WorkerResult> 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<Bitmap> bitmaps, int pixelCount,
- List<WorkerResult> 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 <android_view_GraphicBuffer.h>
-#include <cutils/log.h>
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-// Disable warnings for Skia.
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-#include <SkCanvas.h>
-#include <SkBitmap.h>
-#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<GraphicBuffer> 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) {