Merge "Convert bitmaps to sRGB/scRGB when they have a color profile" into oc-dev
This commit is contained in:
@@ -50,6 +50,7 @@ hwui_src_files := \
|
||||
service/GraphicsStatsService.cpp \
|
||||
thread/TaskManager.cpp \
|
||||
utils/Blur.cpp \
|
||||
utils/Color.cpp \
|
||||
utils/GLUtils.cpp \
|
||||
utils/LinearAllocator.cpp \
|
||||
utils/StringUtils.cpp \
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
}
|
||||
|
||||
void setSize(uint32_t width, uint32_t height) override {
|
||||
texture.updateSize(width, height, texture.internalFormat(), texture.format(),
|
||||
texture.updateLayout(width, height, texture.internalFormat(), texture.format(),
|
||||
texture.target());
|
||||
}
|
||||
|
||||
|
||||
@@ -605,7 +605,11 @@ void GlopBuilder::build() {
|
||||
} else {
|
||||
mDescription.hasExternalTexture = true;
|
||||
}
|
||||
mDescription.hasLinearTexture = mOutGlop->fill.texture.texture->isLinear();
|
||||
Texture* texture = mOutGlop->fill.texture.texture;
|
||||
mDescription.hasLinearTexture = texture->isLinear();
|
||||
mDescription.hasColorSpaceConversion = texture->hasColorSpaceConversion();
|
||||
mDescription.transferFunction = texture->getTransferFunctionType();
|
||||
mDescription.hasTranslucentConversion = texture->blend;
|
||||
}
|
||||
|
||||
mDescription.hasColors = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Color;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "FloatColor.h"
|
||||
#include "Matrix.h"
|
||||
#include "Properties.h"
|
||||
#include "utils/Color.h"
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
@@ -56,11 +57,11 @@ namespace uirenderer {
|
||||
#define PROGRAM_KEY_BITMAP_NPOT 0x80
|
||||
#define PROGRAM_KEY_BITMAP_EXTERNAL 0x100
|
||||
|
||||
#define PROGRAM_KEY_SWAP_SRC_DST 0x2000
|
||||
|
||||
#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600
|
||||
#define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800
|
||||
|
||||
#define PROGRAM_KEY_SWAP_SRC_DST_SHIFT 13
|
||||
|
||||
// Encode the xfermodes on 6 bits
|
||||
#define PROGRAM_MAX_XFERMODE 0x1f
|
||||
#define PROGRAM_XFERMODE_SHADER_SHIFT 26
|
||||
@@ -89,6 +90,10 @@ namespace uirenderer {
|
||||
#define PROGRAM_HAS_GAMMA_CORRECTION 44
|
||||
#define PROGRAM_HAS_LINEAR_TEXTURE 45
|
||||
|
||||
#define PROGRAM_HAS_COLOR_SPACE_CONVERSION 46
|
||||
#define PROGRAM_TRANSFER_FUNCTION 47 // 2 bits for transfer function
|
||||
#define PROGRAM_HAS_TRANSLUCENT_CONVERSION 49
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Types
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -105,13 +110,13 @@ typedef uint64_t programid;
|
||||
* A ProgramDescription must be used in conjunction with a ProgramCache.
|
||||
*/
|
||||
struct ProgramDescription {
|
||||
enum class ColorFilterMode {
|
||||
enum class ColorFilterMode : int8_t {
|
||||
None = 0,
|
||||
Matrix,
|
||||
Blend
|
||||
};
|
||||
|
||||
enum Gradient {
|
||||
enum Gradient : int8_t {
|
||||
kGradientLinear = 0,
|
||||
kGradientCircular,
|
||||
kGradientSweep
|
||||
@@ -168,6 +173,11 @@ struct ProgramDescription {
|
||||
// Set when sampling an image in linear space
|
||||
bool hasLinearTexture;
|
||||
|
||||
bool hasColorSpaceConversion;
|
||||
TransferFunctionType transferFunction;
|
||||
// Indicates whether the bitmap to convert between color spaces is translucent
|
||||
bool hasTranslucentConversion;
|
||||
|
||||
/**
|
||||
* Resets this description. All fields are reset back to the default
|
||||
* values they hold after building a new instance.
|
||||
@@ -210,6 +220,10 @@ struct ProgramDescription {
|
||||
|
||||
hasGammaCorrection = false;
|
||||
hasLinearTexture = false;
|
||||
|
||||
hasColorSpaceConversion = false;
|
||||
transferFunction = TransferFunctionType::None;
|
||||
hasTranslucentConversion = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -263,24 +277,27 @@ struct ProgramDescription {
|
||||
break;
|
||||
case ColorFilterMode::Blend:
|
||||
key |= PROGRAM_KEY_COLOR_BLEND;
|
||||
key |= ((int)colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT;
|
||||
key |= ((int) colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT;
|
||||
break;
|
||||
case ColorFilterMode::None:
|
||||
break;
|
||||
}
|
||||
key |= ((int)framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
|
||||
if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST;
|
||||
if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT;
|
||||
if (hasVertexAlpha) key |= programid(0x1) << PROGRAM_HAS_VERTEX_ALPHA_SHIFT;
|
||||
if (useShadowAlphaInterp) key |= programid(0x1) << PROGRAM_USE_SHADOW_ALPHA_INTERP_SHIFT;
|
||||
if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
|
||||
if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
|
||||
if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT;
|
||||
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;
|
||||
if (hasLinearTexture) key |= programid(0x1) << PROGRAM_HAS_LINEAR_TEXTURE;
|
||||
key |= ((int) framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
|
||||
key |= programid(swapSrcDst) << PROGRAM_KEY_SWAP_SRC_DST_SHIFT;
|
||||
key |= programid(modulate) << PROGRAM_MODULATE_SHIFT;
|
||||
key |= programid(hasVertexAlpha) << PROGRAM_HAS_VERTEX_ALPHA_SHIFT;
|
||||
key |= programid(useShadowAlphaInterp) << PROGRAM_USE_SHADOW_ALPHA_INTERP_SHIFT;
|
||||
key |= programid(hasExternalTexture) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
|
||||
key |= programid(hasTextureTransform) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
|
||||
key |= programid(isSimpleGradient) << PROGRAM_IS_SIMPLE_GRADIENT;
|
||||
key |= programid(hasColors) << PROGRAM_HAS_COLORS;
|
||||
key |= programid(hasDebugHighlight) << PROGRAM_HAS_DEBUG_HIGHLIGHT;
|
||||
key |= programid(hasRoundRectClip) << PROGRAM_HAS_ROUND_RECT_CLIP;
|
||||
key |= programid(hasGammaCorrection) << PROGRAM_HAS_GAMMA_CORRECTION;
|
||||
key |= programid(hasLinearTexture) << PROGRAM_HAS_LINEAR_TEXTURE;
|
||||
key |= programid(hasColorSpaceConversion) << PROGRAM_HAS_COLOR_SPACE_CONVERSION;
|
||||
key |= programid(transferFunction) << PROGRAM_TRANSFER_FUNCTION;
|
||||
key |= programid(hasTranslucentConversion) << PROGRAM_HAS_TRANSLUCENT_CONVERSION;
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
@@ -161,17 +161,61 @@ const char* gFS_Uniforms_HasRoundRectClip =
|
||||
"uniform vec4 roundRectInnerRectLTRB;\n"
|
||||
"uniform float roundRectRadius;\n";
|
||||
|
||||
const char* gFS_OETF[2] = {
|
||||
"\nvec4 OETF(const vec4 linear) {\n"
|
||||
" return linear;\n"
|
||||
"}\n",
|
||||
// We expect linear data to be scRGB so we mirror the gamma function
|
||||
"\nvec4 OETF(const vec4 linear) {"
|
||||
" return vec4(sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)), linear.a);\n"
|
||||
"}\n",
|
||||
const char* gFS_Uniforms_ColorSpaceConversion =
|
||||
// TODO: Should we use a 3D LUT to combine the matrix and transfer functions?
|
||||
// 32x32x32 fp16 LUTs (for scRGB output) are large and heavy to generate...
|
||||
"uniform mat3 colorSpaceMatrix;\n";
|
||||
|
||||
const char* gFS_Uniforms_TransferFunction[4] = {
|
||||
// In this order: g, a, b, c, d, e, f
|
||||
// See ColorSpace::TransferParameters
|
||||
// We'll use hardware sRGB conversion as much as possible
|
||||
"",
|
||||
"uniform float transferFunction[7];\n",
|
||||
"uniform float transferFunction[5];\n",
|
||||
"uniform float transferFunctionGamma;\n"
|
||||
};
|
||||
|
||||
const char* gFS_Transfer_Functions = R"__SHADER__(
|
||||
const char* gFS_OETF[2] = {
|
||||
R"__SHADER__(
|
||||
vec4 OETF(const vec4 linear) {
|
||||
return linear;
|
||||
}
|
||||
)__SHADER__",
|
||||
// We expect linear data to be scRGB so we mirror the gamma function
|
||||
R"__SHADER__(
|
||||
vec4 OETF(const vec4 linear) {
|
||||
return vec4(sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)), linear.a);
|
||||
}
|
||||
)__SHADER__"
|
||||
};
|
||||
|
||||
const char* gFS_ColorConvert[3] = {
|
||||
// Just OETF
|
||||
R"__SHADER__(
|
||||
vec4 colorConvert(const vec4 color) {
|
||||
return OETF(color);
|
||||
}
|
||||
)__SHADER__",
|
||||
// Full color conversion for opaque bitmaps
|
||||
R"__SHADER__(
|
||||
vec4 colorConvert(const vec4 color) {
|
||||
return OETF(vec4(colorSpaceMatrix * EOTF_Parametric(color.rgb), color.a));
|
||||
}
|
||||
)__SHADER__",
|
||||
// Full color conversion for translucent bitmaps
|
||||
// Note: 0.5/256=0.0019
|
||||
R"__SHADER__(
|
||||
vec4 colorConvert(in vec4 color) {
|
||||
color.rgb /= color.a + 0.0019;
|
||||
color = OETF(vec4(colorSpaceMatrix * EOTF_Parametric(color.rgb), color.a));
|
||||
color.rgb *= color.a + 0.0019;
|
||||
return color;
|
||||
}
|
||||
)__SHADER__",
|
||||
};
|
||||
|
||||
const char* gFS_sRGB_TransferFunctions = R"__SHADER__(
|
||||
float OETF_sRGB(const float linear) {
|
||||
// IEC 61966-2-1:1999
|
||||
return linear <= 0.0031308 ? linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
|
||||
@@ -187,12 +231,56 @@ const char* gFS_Transfer_Functions = R"__SHADER__(
|
||||
}
|
||||
)__SHADER__";
|
||||
|
||||
const char* gFS_TransferFunction[4] = {
|
||||
// Conversion done by the texture unit (sRGB)
|
||||
R"__SHADER__(
|
||||
vec3 EOTF_Parametric(const vec3 x) {
|
||||
return x;
|
||||
}
|
||||
)__SHADER__",
|
||||
// Full transfer function
|
||||
// TODO: We should probably use a 1D LUT (256x1 with texelFetch() since input is 8 bit)
|
||||
// TODO: That would cause 3 dependent texture fetches. Is it worth it?
|
||||
R"__SHADER__(
|
||||
float EOTF_Parametric(float x) {
|
||||
return x <= transferFunction[4]
|
||||
? transferFunction[3] * x + transferFunction[6]
|
||||
: pow(transferFunction[1] * x + transferFunction[2], transferFunction[0])
|
||||
+ transferFunction[5];
|
||||
}
|
||||
|
||||
vec3 EOTF_Parametric(const vec3 x) {
|
||||
return vec3(EOTF_Parametric(x.r), EOTF_Parametric(x.g), EOTF_Parametric(x.b));
|
||||
}
|
||||
)__SHADER__",
|
||||
// Limited transfer function, e = f = 0.0
|
||||
R"__SHADER__(
|
||||
float EOTF_Parametric(float x) {
|
||||
return x <= transferFunction[4]
|
||||
? transferFunction[3] * x
|
||||
: pow(transferFunction[1] * x + transferFunction[2], transferFunction[0]);
|
||||
}
|
||||
|
||||
vec3 EOTF_Parametric(const vec3 x) {
|
||||
return vec3(EOTF_Parametric(x.r), EOTF_Parametric(x.g), EOTF_Parametric(x.b));
|
||||
}
|
||||
)__SHADER__",
|
||||
// Gamma transfer function, e = f = 0.0
|
||||
R"__SHADER__(
|
||||
vec3 EOTF_Parametric(const vec3 x) {
|
||||
return vec3(pow(x.r, transferFunctionGamma),
|
||||
pow(x.g, transferFunctionGamma),
|
||||
pow(x.b, transferFunctionGamma));
|
||||
}
|
||||
)__SHADER__"
|
||||
};
|
||||
|
||||
// Dithering must be done in the quantization space
|
||||
// When we are writing to an sRGB framebuffer, we must do the following:
|
||||
// EOTF(OETF(color) + dither)
|
||||
// The dithering pattern is generated with a triangle noise generator in the range [-1.0,1.0]
|
||||
// TODO: Handle linear fp16 render targets
|
||||
const char* gFS_Gradient_Functions = R"__SHADER__(
|
||||
const char* gFS_GradientFunctions = R"__SHADER__(
|
||||
float triangleNoise(const highp vec2 n) {
|
||||
highp vec2 p = fract(n * vec2(5.3987, 5.4421));
|
||||
p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));
|
||||
@@ -200,7 +288,8 @@ const char* gFS_Gradient_Functions = R"__SHADER__(
|
||||
return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
|
||||
}
|
||||
)__SHADER__";
|
||||
const char* gFS_Gradient_Preamble[2] = {
|
||||
|
||||
const char* gFS_GradientPreamble[2] = {
|
||||
// Linear framebuffer
|
||||
R"__SHADER__(
|
||||
vec4 dither(const vec4 color) {
|
||||
@@ -259,9 +348,9 @@ const char* gFS_Main_ApplyVertexAlphaShadowInterp =
|
||||
" fragColor *= texture2D(baseSampler, vec2(alpha, 0.5)).a;\n";
|
||||
const char* gFS_Main_FetchTexture[2] = {
|
||||
// Don't modulate
|
||||
" fragColor = OETF(texture2D(baseSampler, outTexCoords));\n",
|
||||
" fragColor = colorConvert(texture2D(baseSampler, outTexCoords));\n",
|
||||
// Modulate
|
||||
" fragColor = color * texture2D(baseSampler, outTexCoords);\n"
|
||||
" fragColor = color * colorConvert(texture2D(baseSampler, outTexCoords));\n"
|
||||
};
|
||||
const char* gFS_Main_FetchA8Texture[4] = {
|
||||
// Don't modulate
|
||||
@@ -290,9 +379,9 @@ const char* gFS_Main_FetchGradient[6] = {
|
||||
" vec4 gradientColor = gradientMix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n"
|
||||
};
|
||||
const char* gFS_Main_FetchBitmap =
|
||||
" vec4 bitmapColor = OETF(texture2D(bitmapSampler, outBitmapTexCoords));\n";
|
||||
" vec4 bitmapColor = colorConvert(texture2D(bitmapSampler, outBitmapTexCoords));\n";
|
||||
const char* gFS_Main_FetchBitmapNpot =
|
||||
" vec4 bitmapColor = OETF(texture2D(bitmapSampler, wrap(outBitmapTexCoords)));\n";
|
||||
" vec4 bitmapColor = colorConvert(texture2D(bitmapSampler, wrap(outBitmapTexCoords)));\n";
|
||||
const char* gFS_Main_BlendShadersBG =
|
||||
" fragColor = blendShaders(gradientColor, bitmapColor)";
|
||||
const char* gFS_Main_BlendShadersGB =
|
||||
@@ -627,6 +716,11 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
|
||||
}
|
||||
shader.append(gFS_Uniforms_ColorOp[static_cast<int>(description.colorOp)]);
|
||||
|
||||
if (description.hasColorSpaceConversion) {
|
||||
shader.append(gFS_Uniforms_ColorSpaceConversion);
|
||||
}
|
||||
shader.append(gFS_Uniforms_TransferFunction[static_cast<int>(description.transferFunction)]);
|
||||
|
||||
// Generate required functions
|
||||
if (description.hasGradient && description.hasBitmap) {
|
||||
generateBlend(shader, "blendShaders", description.shadersMode);
|
||||
@@ -640,16 +734,21 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
|
||||
if (description.useShaderBasedWrap) {
|
||||
generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT);
|
||||
}
|
||||
if (description.hasGradient || description.hasLinearTexture) {
|
||||
shader.append(gFS_Transfer_Functions);
|
||||
if (description.hasGradient || description.hasLinearTexture
|
||||
|| description.hasColorSpaceConversion) {
|
||||
shader.append(gFS_sRGB_TransferFunctions);
|
||||
}
|
||||
if (description.hasBitmap || ((description.hasTexture || description.hasExternalTexture) &&
|
||||
!description.hasAlpha8Texture)) {
|
||||
shader.append(gFS_OETF[description.hasLinearTexture && !mHasLinearBlending]);
|
||||
shader.append(gFS_TransferFunction[static_cast<int>(description.transferFunction)]);
|
||||
shader.append(gFS_OETF[(description.hasLinearTexture || description.hasColorSpaceConversion)
|
||||
&& !mHasLinearBlending]);
|
||||
shader.append(gFS_ColorConvert[description.hasColorSpaceConversion
|
||||
? 1 + description.hasTranslucentConversion : 0]);
|
||||
}
|
||||
if (description.hasGradient) {
|
||||
shader.append(gFS_Gradient_Functions);
|
||||
shader.append(gFS_Gradient_Preamble[mHasLinearBlending]);
|
||||
shader.append(gFS_GradientFunctions);
|
||||
shader.append(gFS_GradientPreamble[mHasLinearBlending]);
|
||||
}
|
||||
|
||||
// Begin the shader
|
||||
|
||||
@@ -216,8 +216,13 @@ bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& model
|
||||
const float width = outData->bitmapTexture->width();
|
||||
const float height = outData->bitmapTexture->height();
|
||||
|
||||
Texture* texture = outData->bitmapTexture;
|
||||
|
||||
description->hasBitmap = true;
|
||||
description->hasLinearTexture = outData->bitmapTexture->isLinear();
|
||||
description->hasLinearTexture = texture->isLinear();
|
||||
description->hasColorSpaceConversion = texture->hasColorSpaceConversion();
|
||||
description->transferFunction = texture->getTransferFunctionType();
|
||||
description->hasTranslucentConversion = texture->blend;
|
||||
description->isShaderBitmapExternal = hwuiBitmap->isHardware();
|
||||
// gralloc doesn't support non-clamp modes
|
||||
if (hwuiBitmap->isHardware() || (!caches.extensions().hasNPot()
|
||||
|
||||
@@ -17,10 +17,13 @@
|
||||
#include "Caches.h"
|
||||
#include "Texture.h"
|
||||
#include "utils/GLUtils.h"
|
||||
#include "utils/MathUtils.h"
|
||||
#include "utils/TraceUtils.h"
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <SkCanvas.h>
|
||||
|
||||
namespace android {
|
||||
@@ -48,12 +51,7 @@ static int bytesPerPixel(GLint glFormat) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Texture::isLinear() const {
|
||||
return mInternalFormat == GL_RGBA16F;
|
||||
}
|
||||
|
||||
void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force) {
|
||||
|
||||
if (force || wrapS != mWrapS || wrapT != mWrapT) {
|
||||
mWrapS = wrapS;
|
||||
mWrapT = wrapT;
|
||||
@@ -94,7 +92,7 @@ void Texture::deleteTexture() {
|
||||
}
|
||||
}
|
||||
|
||||
bool Texture::updateSize(uint32_t width, uint32_t height, GLint internalFormat,
|
||||
bool Texture::updateLayout(uint32_t width, uint32_t height, GLint internalFormat,
|
||||
GLint format, GLenum target) {
|
||||
if (mWidth == width
|
||||
&& mHeight == height
|
||||
@@ -122,7 +120,7 @@ void Texture::resetCachedParams() {
|
||||
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, format, GL_TEXTURE_2D);
|
||||
bool needsAlloc = updateLayout(width, height, internalFormat, format, GL_TEXTURE_2D);
|
||||
if (!mId) {
|
||||
glGenTextures(1, &mId);
|
||||
needsAlloc = true;
|
||||
@@ -224,7 +222,6 @@ void Texture::colorTypeToGlFormatAndType(const Caches& caches, SkColorType color
|
||||
*outType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case kGray_8_SkColorType:
|
||||
// TODO: Handle sRGB
|
||||
*outFormat = GL_LUMINANCE;
|
||||
*outInternalFormat = GL_LUMINANCE;
|
||||
*outType = GL_UNSIGNED_BYTE;
|
||||
@@ -252,15 +249,14 @@ SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending,
|
||||
return rgbaBitmap;
|
||||
}
|
||||
|
||||
bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending,
|
||||
SkColorSpace* sRGB) {
|
||||
bool needSRGB = info.colorSpace() == sRGB;
|
||||
bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending) {
|
||||
return info.colorType() == kARGB_4444_SkColorType
|
||||
|| info.colorType() == kIndex_8_SkColorType
|
||||
|| (info.colorType() == kRGB_565_SkColorType && hasLinearBlending && needSRGB);
|
||||
|| (info.colorType() == kRGB_565_SkColorType
|
||||
&& hasLinearBlending
|
||||
&& info.colorSpace()->isSRGB());
|
||||
}
|
||||
|
||||
|
||||
void Texture::upload(Bitmap& bitmap) {
|
||||
if (!bitmap.readyToDraw()) {
|
||||
ALOGE("Cannot generate texture from bitmap");
|
||||
@@ -284,23 +280,59 @@ void Texture::upload(Bitmap& bitmap) {
|
||||
setDefaultParams = true;
|
||||
}
|
||||
|
||||
sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeSRGB();
|
||||
bool needSRGB = bitmap.info().colorSpace() == sRGB.get();
|
||||
bool hasLinearBlending = mCaches.extensions().hasLinearBlending();
|
||||
bool needSRGB = transferFunctionCloseToSRGB(bitmap.info().colorSpace());
|
||||
|
||||
GLint internalFormat, format, type;
|
||||
colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB, &internalFormat, &format, &type);
|
||||
colorTypeToGlFormatAndType(mCaches, bitmap.colorType(),
|
||||
needSRGB && hasLinearBlending, &internalFormat, &format, &type);
|
||||
|
||||
mConnector.reset();
|
||||
|
||||
// RGBA16F is always extended sRGB, alpha masks don't have color profiles
|
||||
if (internalFormat != GL_RGBA16F && internalFormat != GL_ALPHA) {
|
||||
SkColorSpace* colorSpace = bitmap.info().colorSpace();
|
||||
// If the bitmap is sRGB we don't need conversion
|
||||
if (colorSpace != nullptr && !colorSpace->isSRGB()) {
|
||||
SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor);
|
||||
if (!colorSpace->toXYZD50(&xyzMatrix)) {
|
||||
ALOGW("Incompatible color space!");
|
||||
} else {
|
||||
SkColorSpaceTransferFn fn;
|
||||
if (!colorSpace->isNumericalTransferFn(&fn)) {
|
||||
ALOGW("Incompatible color space, no numerical transfer function!");
|
||||
} else {
|
||||
float data[16];
|
||||
xyzMatrix.asColMajorf(data);
|
||||
|
||||
ColorSpace::TransferParameters p =
|
||||
{fn.fG, fn.fA, fn.fB, fn.fC, fn.fD, fn.fE, fn.fF};
|
||||
ColorSpace src("Unnamed", mat4f((const float*) &data[0]).upperLeft(), p);
|
||||
mConnector.reset(new ColorSpaceConnector(src, ColorSpace::sRGB()));
|
||||
|
||||
// A non-sRGB color space might have a transfer function close enough to sRGB
|
||||
// that we can save shader instructions by using an sRGB sampler
|
||||
// This is only possible if we have hardware support for sRGB textures
|
||||
if (needSRGB && internalFormat == GL_RGBA
|
||||
&& mCaches.extensions().hasSRGB() && !bitmap.isHardware()) {
|
||||
internalFormat = GL_SRGB8_ALPHA8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
|
||||
needsAlloc |= updateSize(bitmap.width(), bitmap.height(), internalFormat, format, target);
|
||||
needsAlloc |= updateLayout(bitmap.width(), bitmap.height(), internalFormat, format, target);
|
||||
|
||||
blend = !bitmap.isOpaque();
|
||||
mCaches.textureState().bindTexture(mTarget, mId);
|
||||
|
||||
// TODO: Handle sRGB gray bitmaps
|
||||
bool hasLinearBlending = mCaches.extensions().hasLinearBlending();
|
||||
if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasLinearBlending, sRGB.get()))) {
|
||||
if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasLinearBlending))) {
|
||||
SkBitmap skBitmap;
|
||||
bitmap.getSkBitmap(&skBitmap);
|
||||
sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeSRGB();
|
||||
SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB));
|
||||
uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(),
|
||||
rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(),
|
||||
@@ -333,9 +365,28 @@ void Texture::wrap(GLuint id, uint32_t width, uint32_t height,
|
||||
mFormat = format;
|
||||
mInternalFormat = internalFormat;
|
||||
mTarget = target;
|
||||
mConnector.reset();
|
||||
// We're wrapping an existing texture, so don't double count this memory
|
||||
notifySizeChanged(0);
|
||||
}
|
||||
|
||||
TransferFunctionType Texture::getTransferFunctionType() const {
|
||||
if (mConnector.get() != nullptr && mInternalFormat != GL_SRGB8_ALPHA8) {
|
||||
const ColorSpace::TransferParameters& p = mConnector->getSource().getTransferParameters();
|
||||
if (MathUtils::isZero(p.e) && MathUtils::isZero(p.f)) {
|
||||
if (MathUtils::areEqual(p.a, 1.0f) && MathUtils::isZero(p.b)
|
||||
&& MathUtils::isZero(p.c) && MathUtils::isZero(p.d)) {
|
||||
if (MathUtils::areEqual(p.g, 1.0f)) {
|
||||
return TransferFunctionType::None;
|
||||
}
|
||||
return TransferFunctionType::Gamma;
|
||||
}
|
||||
return TransferFunctionType::Limited;
|
||||
}
|
||||
return TransferFunctionType::Full;
|
||||
}
|
||||
return TransferFunctionType::None;
|
||||
}
|
||||
|
||||
}; // namespace uirenderer
|
||||
}; // namespace android
|
||||
|
||||
@@ -19,6 +19,13 @@
|
||||
|
||||
#include "GpuMemoryTracker.h"
|
||||
#include "hwui/Bitmap.h"
|
||||
#include "utils/Color.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <math/mat3.h>
|
||||
|
||||
#include <ui/ColorSpace.h>
|
||||
|
||||
#include <GLES2/gl2.h>
|
||||
#include <EGL/egl.h>
|
||||
@@ -42,8 +49,7 @@ class Texture : public GpuMemoryTracker {
|
||||
public:
|
||||
static SkBitmap uploadToN32(const SkBitmap& bitmap,
|
||||
bool hasLinearBlending, sk_sp<SkColorSpace> sRGB);
|
||||
static bool hasUnsupportedColorType(const SkImageInfo& info,
|
||||
bool hasLinearBlending, SkColorSpace* sRGB);
|
||||
static bool hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending);
|
||||
static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
|
||||
bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType);
|
||||
|
||||
@@ -129,10 +135,27 @@ public:
|
||||
return mTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns nullptr if this texture does not require color space conversion
|
||||
* to sRGB, or a valid pointer to a ColorSpaceConnector if a conversion
|
||||
* is required.
|
||||
*/
|
||||
constexpr const ColorSpaceConnector* getColorSpaceConnector() const {
|
||||
return mConnector.get();
|
||||
}
|
||||
|
||||
constexpr bool hasColorSpaceConversion() const {
|
||||
return mConnector.get() != nullptr;
|
||||
}
|
||||
|
||||
TransferFunctionType getTransferFunctionType() const;
|
||||
|
||||
/**
|
||||
* Returns true if this texture uses a linear encoding format.
|
||||
*/
|
||||
bool isLinear() const;
|
||||
constexpr bool isLinear() const {
|
||||
return mInternalFormat == GL_RGBA16F;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generation of the backing bitmap,
|
||||
@@ -171,8 +194,8 @@ private:
|
||||
// and external texture wrapper
|
||||
friend class GlLayer;
|
||||
|
||||
// Returns true if the size changed, false if it was the same
|
||||
bool updateSize(uint32_t width, uint32_t height, GLint internalFormat,
|
||||
// Returns true if the texture layout (size, format, etc.) changed, false if it was the same
|
||||
bool updateLayout(uint32_t width, uint32_t height, GLint internalFormat,
|
||||
GLint format, GLenum target);
|
||||
void uploadHardwareBitmapToTexture(GraphicBuffer* buffer);
|
||||
void resetCachedParams();
|
||||
@@ -196,6 +219,8 @@ private:
|
||||
GLenum mMagFilter = GL_LINEAR;
|
||||
|
||||
Caches& mCaches;
|
||||
|
||||
std::unique_ptr<ColorSpaceConnector> mConnector;
|
||||
}; // struct Texture
|
||||
|
||||
class AutoTexture {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "renderthread/EglManager.h"
|
||||
#include "renderthread/RenderThread.h"
|
||||
#include "renderthread/RenderProxy.h"
|
||||
#include "utils/Color.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
@@ -223,8 +224,7 @@ sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(uirenderer::renderthread::RenderThr
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeSRGB();
|
||||
bool needSRGB = skBitmap.info().colorSpace() == sRGB.get();
|
||||
bool needSRGB = uirenderer::transferFunctionCloseToSRGB(skBitmap.info().colorSpace());
|
||||
bool hasLinearBlending = caches.extensions().hasLinearBlending();
|
||||
GLint format, type, internalFormat;
|
||||
uirenderer::Texture::colorTypeToGlFormatAndType(caches, skBitmap.colorType(),
|
||||
@@ -245,7 +245,8 @@ sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(uirenderer::renderthread::RenderThr
|
||||
|
||||
SkBitmap bitmap;
|
||||
if (CC_UNLIKELY(uirenderer::Texture::hasUnsupportedColorType(skBitmap.info(),
|
||||
hasLinearBlending, sRGB.get()))) {
|
||||
hasLinearBlending))) {
|
||||
sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeSRGB();
|
||||
bitmap = uirenderer::Texture::uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB));
|
||||
} else {
|
||||
bitmap = skBitmap;
|
||||
|
||||
@@ -22,8 +22,11 @@
|
||||
#include "renderthread/CanvasContext.h"
|
||||
#include "renderthread/EglManager.h"
|
||||
#include "utils/GLUtils.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <ui/ColorSpace.h>
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
@@ -359,6 +362,40 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) {
|
||||
fill.skiaShaderData.bitmapData.bitmapTexture : nullptr;
|
||||
const AutoTexture autoCleanup(texture);
|
||||
|
||||
// If we have a shader and a base texture, the base texture is assumed to be an alpha mask
|
||||
// which means the color space conversion applies to the shader's bitmap
|
||||
Texture* colorSpaceTexture = texture != nullptr ? texture : fill.texture.texture;
|
||||
if (colorSpaceTexture != nullptr) {
|
||||
if (colorSpaceTexture->hasColorSpaceConversion()) {
|
||||
const ColorSpaceConnector* connector = colorSpaceTexture->getColorSpaceConnector();
|
||||
glUniformMatrix3fv(fill.program->getUniform("colorSpaceMatrix"), 1,
|
||||
GL_FALSE, connector->getTransform().asArray());
|
||||
}
|
||||
|
||||
TransferFunctionType transferFunction = colorSpaceTexture->getTransferFunctionType();
|
||||
if (transferFunction != TransferFunctionType::None) {
|
||||
const ColorSpaceConnector* connector = colorSpaceTexture->getColorSpaceConnector();
|
||||
const ColorSpace& source = connector->getSource();
|
||||
|
||||
switch (transferFunction) {
|
||||
case TransferFunctionType::None:
|
||||
break;
|
||||
case TransferFunctionType::Full:
|
||||
glUniform1fv(fill.program->getUniform("transferFunction"), 7,
|
||||
reinterpret_cast<const float*>(&source.getTransferParameters().g));
|
||||
break;
|
||||
case TransferFunctionType::Limited:
|
||||
glUniform1fv(fill.program->getUniform("transferFunction"), 5,
|
||||
reinterpret_cast<const float*>(&source.getTransferParameters().g));
|
||||
break;
|
||||
case TransferFunctionType::Gamma:
|
||||
glUniform1f(fill.program->getUniform("transferFunctionGamma"),
|
||||
source.getTransferParameters().g);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
// ---------- GL state setup ----------
|
||||
// ------------------------------------
|
||||
|
||||
58
libs/hwui/utils/Color.cpp
Normal file
58
libs/hwui/utils/Color.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "Color.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
static inline bool almostEqual(float a, float b) {
|
||||
return std::abs(a - b) < 1e-2f;
|
||||
}
|
||||
|
||||
bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace) {
|
||||
if (colorSpace == nullptr) return true;
|
||||
if (colorSpace->isSRGB()) return true;
|
||||
|
||||
SkColorSpaceTransferFn transferFunction;
|
||||
if (colorSpace->isNumericalTransferFn(&transferFunction)) {
|
||||
// sRGB transfer function params:
|
||||
const float sRGBParamA = 1 / 1.055f;
|
||||
const float sRGBParamB = 0.055f / 1.055f;
|
||||
const float sRGBParamC = 1 / 12.92f;
|
||||
const float sRGBParamD = 0.04045f;
|
||||
const float sRGBParamE = 0.0f;
|
||||
const float sRGBParamF = 0.0f;
|
||||
const float sRGBParamG = 2.4f;
|
||||
|
||||
// This comparison will catch Display P3
|
||||
return
|
||||
almostEqual(sRGBParamA, transferFunction.fA)
|
||||
&& almostEqual(sRGBParamB, transferFunction.fB)
|
||||
&& almostEqual(sRGBParamC, transferFunction.fC)
|
||||
&& almostEqual(sRGBParamD, transferFunction.fD)
|
||||
&& almostEqual(sRGBParamE, transferFunction.fE)
|
||||
&& almostEqual(sRGBParamF, transferFunction.fF)
|
||||
&& almostEqual(sRGBParamG, transferFunction.fG);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}; // namespace uirenderer
|
||||
}; // namespace android
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <math.h>
|
||||
|
||||
#include <SkColor.h>
|
||||
#include <SkColorSpace.h>
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
@@ -82,6 +83,13 @@ namespace uirenderer {
|
||||
};
|
||||
static constexpr int BrightColorsCount = sizeof(BrightColors) / sizeof(Color::Color);
|
||||
|
||||
enum class TransferFunctionType : int8_t {
|
||||
None = 0,
|
||||
Full,
|
||||
Limited,
|
||||
Gamma
|
||||
};
|
||||
|
||||
// Opto-electronic conversion function for the sRGB color space
|
||||
// Takes a linear sRGB value and converts it to a gamma-encoded sRGB value
|
||||
static constexpr float OECF_sRGB(float linear) {
|
||||
@@ -118,6 +126,11 @@ namespace uirenderer {
|
||||
return srgb;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns whether the specified color space's transfer function can be
|
||||
// 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);
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user