Respect a Layer's (e.g. SurfaceTexture) colorSpace when compositing
This CL extracts the android_dataspace from the GLConsumer and converts it to a SkColorSpace. HWUI always expects to composite into an sRGB destination so when we draw the layer we run the draw through a colorFilter that converts the input colorSpace into that of the destination. Test: CtsViewTestCases Bug: 78016220 Merged-In: Ic0446a0d861e86a5a9d0382346b57fcc45c8a61b Change-Id: Ic0446a0d861e86a5a9d0382346b57fcc45c8a61b
This commit is contained in:
@@ -40,7 +40,6 @@ DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState, CreateLayer
|
||||
}
|
||||
|
||||
DeferredLayerUpdater::~DeferredLayerUpdater() {
|
||||
SkSafeUnref(mColorFilter);
|
||||
setTransform(nullptr);
|
||||
mRenderState.unregisterDeferredLayerUpdater(this);
|
||||
destroyLayer();
|
||||
@@ -67,8 +66,11 @@ void DeferredLayerUpdater::destroyLayer() {
|
||||
void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
|
||||
mAlpha = PaintUtils::getAlphaDirect(paint);
|
||||
mMode = PaintUtils::getBlendModeDirect(paint);
|
||||
SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : nullptr;
|
||||
SkRefCnt_SafeAssign(mColorFilter, colorFilter);
|
||||
if (paint) {
|
||||
mColorFilter = paint->refColorFilter();
|
||||
} else {
|
||||
mColorFilter.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void DeferredLayerUpdater::apply() {
|
||||
@@ -143,7 +145,7 @@ void DeferredLayerUpdater::doUpdateTexImage() {
|
||||
#endif
|
||||
mSurfaceTexture->getTransformMatrix(transform);
|
||||
|
||||
updateLayer(forceFilter, transform);
|
||||
updateLayer(forceFilter, transform, mSurfaceTexture->getCurrentDataSpace());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,17 +155,19 @@ void DeferredLayerUpdater::doUpdateVkTexImage() {
|
||||
Layer::Api::OpenGL, Layer::Api::Vulkan);
|
||||
|
||||
static const mat4 identityMatrix;
|
||||
updateLayer(false, identityMatrix.data);
|
||||
updateLayer(false, identityMatrix.data, HAL_DATASPACE_UNKNOWN);
|
||||
|
||||
VkLayer* vkLayer = static_cast<VkLayer*>(mLayer);
|
||||
vkLayer->updateTexture();
|
||||
}
|
||||
|
||||
void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform) {
|
||||
void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform,
|
||||
android_dataspace dataspace) {
|
||||
mLayer->setBlend(mBlend);
|
||||
mLayer->setForceFilter(forceFilter);
|
||||
mLayer->setSize(mWidth, mHeight);
|
||||
mLayer->getTexTransform().load(textureTransform);
|
||||
mLayer->setDataSpace(dataspace);
|
||||
}
|
||||
|
||||
void DeferredLayerUpdater::detachSurfaceTexture() {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <SkMatrix.h>
|
||||
#include <cutils/compiler.h>
|
||||
#include <gui/GLConsumer.h>
|
||||
#include <system/graphics.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
|
||||
#include <GLES2/gl2.h>
|
||||
@@ -41,7 +42,7 @@ public:
|
||||
// Note that DeferredLayerUpdater assumes it is taking ownership of the layer
|
||||
// and will not call incrementRef on it as a result.
|
||||
typedef std::function<Layer*(RenderState& renderState, uint32_t layerWidth,
|
||||
uint32_t layerHeight, SkColorFilter* colorFilter, int alpha,
|
||||
uint32_t layerHeight, sk_sp<SkColorFilter> colorFilter, int alpha,
|
||||
SkBlendMode mode, bool blend)>
|
||||
CreateLayerFn;
|
||||
ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn,
|
||||
@@ -96,7 +97,7 @@ public:
|
||||
|
||||
void detachSurfaceTexture();
|
||||
|
||||
void updateLayer(bool forceFilter, const float* textureTransform);
|
||||
void updateLayer(bool forceFilter, const float* textureTransform, android_dataspace dataspace);
|
||||
|
||||
void destroyLayer();
|
||||
|
||||
@@ -109,7 +110,7 @@ private:
|
||||
int mWidth = 0;
|
||||
int mHeight = 0;
|
||||
bool mBlend = false;
|
||||
SkColorFilter* mColorFilter = nullptr;
|
||||
sk_sp<SkColorFilter> mColorFilter;
|
||||
int mAlpha = 255;
|
||||
SkBlendMode mMode = SkBlendMode::kSrcOver;
|
||||
sp<GLConsumer> mSurfaceTexture;
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
GlLayer::GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
|
||||
SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend)
|
||||
sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend)
|
||||
: Layer(renderState, Api::OpenGL, colorFilter, alpha, mode)
|
||||
, caches(Caches::getInstance())
|
||||
, texture(caches) {
|
||||
|
||||
@@ -32,7 +32,7 @@ class Caches;
|
||||
class GlLayer : public Layer {
|
||||
public:
|
||||
GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
|
||||
SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend);
|
||||
sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend);
|
||||
virtual ~GlLayer();
|
||||
|
||||
uint32_t getWidth() const override { return texture.mWidth; }
|
||||
|
||||
@@ -18,34 +18,58 @@
|
||||
|
||||
#include "renderstate/RenderState.h"
|
||||
|
||||
#include <SkColorFilter.h>
|
||||
#include <SkToSRGBColorFilter.h>
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
Layer::Layer(RenderState& renderState, Api api, SkColorFilter* colorFilter, int alpha,
|
||||
Layer::Layer(RenderState& renderState, Api api, sk_sp<SkColorFilter> colorFilter, int alpha,
|
||||
SkBlendMode mode)
|
||||
: GpuMemoryTracker(GpuObjectType::Layer)
|
||||
, mRenderState(renderState)
|
||||
, mApi(api)
|
||||
, colorFilter(nullptr)
|
||||
, mColorFilter(colorFilter)
|
||||
, alpha(alpha)
|
||||
, mode(mode) {
|
||||
// TODO: This is a violation of Android's typical ref counting, but it
|
||||
// preserves the old inc/dec ref locations. This should be changed...
|
||||
incStrong(nullptr);
|
||||
|
||||
buildColorSpaceWithFilter();
|
||||
renderState.registerLayer(this);
|
||||
}
|
||||
|
||||
Layer::~Layer() {
|
||||
SkSafeUnref(colorFilter);
|
||||
|
||||
mRenderState.unregisterLayer(this);
|
||||
}
|
||||
|
||||
void Layer::setColorFilter(SkColorFilter* filter) {
|
||||
SkRefCnt_SafeAssign(colorFilter, filter);
|
||||
void Layer::setColorFilter(sk_sp<SkColorFilter> filter) {
|
||||
if (filter != mColorFilter) {
|
||||
mColorFilter = filter;
|
||||
buildColorSpaceWithFilter();
|
||||
}
|
||||
}
|
||||
|
||||
void Layer::setDataSpace(android_dataspace dataspace) {
|
||||
if (dataspace != mCurrentDataspace) {
|
||||
mCurrentDataspace = dataspace;
|
||||
buildColorSpaceWithFilter();
|
||||
}
|
||||
}
|
||||
|
||||
void Layer::buildColorSpaceWithFilter() {
|
||||
sk_sp<SkColorFilter> colorSpaceFilter;
|
||||
sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(mCurrentDataspace);
|
||||
if (colorSpace && !colorSpace->isSRGB()) {
|
||||
colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
|
||||
}
|
||||
|
||||
if (mColorFilter && colorSpaceFilter) {
|
||||
mColorSpaceWithFilter = mColorFilter->makeComposed(colorSpaceFilter);
|
||||
} else if (colorSpaceFilter) {
|
||||
mColorSpaceWithFilter = colorSpaceFilter;
|
||||
} else {
|
||||
mColorSpaceWithFilter = mColorFilter;
|
||||
}
|
||||
}
|
||||
|
||||
void Layer::postDecStrong() {
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include <GpuMemoryTracker.h>
|
||||
#include <utils/RefBase.h>
|
||||
|
||||
#include <SkColorFilter.h>
|
||||
#include <SkColorSpace.h>
|
||||
#include <SkBlendMode.h>
|
||||
#include <SkPaint.h>
|
||||
|
||||
@@ -72,9 +74,15 @@ public:
|
||||
|
||||
inline SkBlendMode getMode() const { return mode; }
|
||||
|
||||
inline SkColorFilter* getColorFilter() const { return colorFilter; }
|
||||
inline SkColorFilter* getColorFilter() const { return mColorFilter.get(); }
|
||||
|
||||
void setColorFilter(SkColorFilter* filter);
|
||||
void setColorFilter(sk_sp<SkColorFilter> filter);
|
||||
|
||||
void setDataSpace(android_dataspace dataspace);
|
||||
|
||||
void setColorSpace(sk_sp<SkColorSpace> colorSpace);
|
||||
|
||||
inline sk_sp<SkColorFilter> getColorSpaceWithFilter() const { return mColorSpaceWithFilter; }
|
||||
|
||||
inline mat4& getTexTransform() { return texTransform; }
|
||||
|
||||
@@ -87,18 +95,30 @@ public:
|
||||
void postDecStrong();
|
||||
|
||||
protected:
|
||||
Layer(RenderState& renderState, Api api, SkColorFilter* colorFilter, int alpha,
|
||||
Layer(RenderState& renderState, Api api, sk_sp<SkColorFilter>, int alpha,
|
||||
SkBlendMode mode);
|
||||
|
||||
RenderState& mRenderState;
|
||||
|
||||
private:
|
||||
void buildColorSpaceWithFilter();
|
||||
|
||||
Api mApi;
|
||||
|
||||
/**
|
||||
* Color filter used to draw this layer. Optional.
|
||||
*/
|
||||
SkColorFilter* colorFilter;
|
||||
sk_sp<SkColorFilter> mColorFilter;
|
||||
|
||||
/**
|
||||
* Colorspace of the contents of the layer. Optional.
|
||||
*/
|
||||
android_dataspace mCurrentDataspace = HAL_DATASPACE_UNKNOWN;
|
||||
|
||||
/**
|
||||
* A color filter that is the combination of the mColorFilter and mColorSpace. Optional.
|
||||
*/
|
||||
sk_sp<SkColorFilter> mColorSpaceWithFilter;
|
||||
|
||||
/**
|
||||
* Indicates raster data backing the layer is scaled, requiring filtration.
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace uirenderer {
|
||||
class VkLayer : public Layer {
|
||||
public:
|
||||
VkLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
|
||||
SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend)
|
||||
sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend)
|
||||
: Layer(renderState, Api::Vulkan, colorFilter, alpha, mode)
|
||||
, mWidth(layerWidth)
|
||||
, mHeight(layerHeight)
|
||||
|
||||
@@ -51,8 +51,13 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer
|
||||
GrGLTextureInfo externalTexture;
|
||||
externalTexture.fTarget = glLayer->getRenderTarget();
|
||||
externalTexture.fID = glLayer->getTextureId();
|
||||
GrBackendTexture backendTexture(layerWidth, layerHeight, kRGBA_8888_GrPixelConfig,
|
||||
externalTexture);
|
||||
// The format may not be GL_RGBA8, but given the DeferredLayerUpdater and GLConsumer don't
|
||||
// expose that info we use it as our default. Further, given that we only use this texture
|
||||
// as a source this will not impact how Skia uses the texture. The only potential affect
|
||||
// this is anticipated to have is that for some format types if we are not bound as an OES
|
||||
// texture we may get invalid results for SKP capture if we read back the texture.
|
||||
externalTexture.fFormat = GL_RGBA8;
|
||||
GrBackendTexture backendTexture(layerWidth, layerHeight, GrMipMapped::kNo, externalTexture);
|
||||
layerImage = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin,
|
||||
kPremul_SkAlphaType, nullptr);
|
||||
} else {
|
||||
@@ -82,7 +87,7 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer
|
||||
SkPaint paint;
|
||||
paint.setAlpha(layer->getAlpha());
|
||||
paint.setBlendMode(layer->getMode());
|
||||
paint.setColorFilter(sk_ref_sp(layer->getColorFilter()));
|
||||
paint.setColorFilter(layer->getColorSpaceWithFilter());
|
||||
|
||||
const bool nonIdentityMatrix = !matrix.isIdentity();
|
||||
if (nonIdentityMatrix) {
|
||||
|
||||
@@ -152,7 +152,7 @@ bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBi
|
||||
}
|
||||
|
||||
static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
|
||||
SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) {
|
||||
sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend) {
|
||||
GlLayer* layer =
|
||||
new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend);
|
||||
layer->generateTexture();
|
||||
|
||||
@@ -147,6 +147,7 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque,
|
||||
GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext();
|
||||
if (cachedContext.get() != currentContext) {
|
||||
if (cachedContext.get()) {
|
||||
ATRACE_NAME("flush layers (context changed)");
|
||||
cachedContext->flush();
|
||||
}
|
||||
cachedContext.reset(SkSafeRef(currentContext));
|
||||
@@ -155,6 +156,7 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque,
|
||||
}
|
||||
|
||||
if (cachedContext.get()) {
|
||||
ATRACE_NAME("flush layers");
|
||||
cachedContext->flush();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +115,8 @@ bool SkiaVulkanPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bi
|
||||
}
|
||||
|
||||
static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
|
||||
SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) {
|
||||
sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode,
|
||||
bool blend) {
|
||||
return new VkLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend);
|
||||
}
|
||||
|
||||
|
||||
@@ -123,7 +123,8 @@ bool OpenGLPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap
|
||||
}
|
||||
|
||||
static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
|
||||
SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) {
|
||||
sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode,
|
||||
bool blend) {
|
||||
GlLayer* layer =
|
||||
new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend);
|
||||
Caches::getInstance().textureState().activateTexture(0);
|
||||
|
||||
@@ -75,7 +75,7 @@ sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater(
|
||||
layerUpdater->setTransform(&transform);
|
||||
|
||||
// updateLayer so it's ready to draw
|
||||
layerUpdater->updateLayer(true, Matrix4::identity().data);
|
||||
layerUpdater->updateLayer(true, Matrix4::identity().data, HAL_DATASPACE_UNKNOWN);
|
||||
if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
|
||||
static_cast<GlLayer*>(layerUpdater->backingLayer())
|
||||
->setRenderTarget(GL_TEXTURE_EXTERNAL_OES);
|
||||
|
||||
@@ -44,7 +44,7 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) {
|
||||
// push the deferred updates to the layer
|
||||
Matrix4 scaledMatrix;
|
||||
scaledMatrix.loadScale(0.5, 0.5, 0.0);
|
||||
layerUpdater->updateLayer(true, scaledMatrix.data);
|
||||
layerUpdater->updateLayer(true, scaledMatrix.data, HAL_DATASPACE_UNKNOWN);
|
||||
if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
|
||||
GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer());
|
||||
glLayer->setRenderTarget(GL_TEXTURE_EXTERNAL_OES);
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#include "Color.h"
|
||||
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <cmath>
|
||||
|
||||
namespace android {
|
||||
@@ -53,5 +55,57 @@ bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) {
|
||||
|
||||
SkColorSpace::Gamut gamut;
|
||||
switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
|
||||
case HAL_DATASPACE_STANDARD_BT709:
|
||||
gamut = SkColorSpace::kSRGB_Gamut;
|
||||
break;
|
||||
case HAL_DATASPACE_STANDARD_BT2020:
|
||||
gamut = SkColorSpace::kRec2020_Gamut;
|
||||
break;
|
||||
case HAL_DATASPACE_STANDARD_DCI_P3:
|
||||
gamut = SkColorSpace::kDCIP3_D65_Gamut;
|
||||
break;
|
||||
case HAL_DATASPACE_STANDARD_ADOBE_RGB:
|
||||
gamut = SkColorSpace::kAdobeRGB_Gamut;
|
||||
break;
|
||||
case HAL_DATASPACE_STANDARD_UNSPECIFIED:
|
||||
return nullptr;
|
||||
case HAL_DATASPACE_STANDARD_BT601_625:
|
||||
case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
|
||||
case HAL_DATASPACE_STANDARD_BT601_525:
|
||||
case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
|
||||
case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
|
||||
case HAL_DATASPACE_STANDARD_BT470M:
|
||||
case HAL_DATASPACE_STANDARD_FILM:
|
||||
default:
|
||||
ALOGW("Unsupported Gamut: %d", dataspace);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
|
||||
case HAL_DATASPACE_TRANSFER_LINEAR:
|
||||
return SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, gamut);
|
||||
case HAL_DATASPACE_TRANSFER_SRGB:
|
||||
return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, gamut);
|
||||
case HAL_DATASPACE_TRANSFER_GAMMA2_2:
|
||||
return SkColorSpace::MakeRGB({2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
|
||||
case HAL_DATASPACE_TRANSFER_GAMMA2_6:
|
||||
return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
|
||||
case HAL_DATASPACE_TRANSFER_GAMMA2_8:
|
||||
return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
|
||||
case HAL_DATASPACE_TRANSFER_UNSPECIFIED:
|
||||
return nullptr;
|
||||
case HAL_DATASPACE_TRANSFER_SMPTE_170M:
|
||||
case HAL_DATASPACE_TRANSFER_ST2084:
|
||||
case HAL_DATASPACE_TRANSFER_HLG:
|
||||
default:
|
||||
ALOGW("Unsupported Gamma: %d", dataspace);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace uirenderer
|
||||
}; // namespace android
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#define COLOR_H
|
||||
|
||||
#include <math.h>
|
||||
#include <system/graphics.h>
|
||||
|
||||
#include <SkColor.h>
|
||||
#include <SkColorSpace.h>
|
||||
@@ -111,6 +112,8 @@ static constexpr float EOCF(float srgb) {
|
||||
// approximated with the native sRGB transfer function. This method
|
||||
// returns true for sRGB, gamma 2.2 and Display P3 for instance
|
||||
bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace);
|
||||
|
||||
sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user