Bug 1915543. Do color management on avifs on non-premultiplied alpha image data. r=gfx-reviewers,lsalzman

ConvertYCbCrToRGB32 was doing the premultiplied conversion, which happens before the surface pipe, which is where color management happens.

Differential Revision: https://phabricator.services.mozilla.com/D220517
This commit is contained in:
Timothy Nikkel 2024-09-07 21:43:05 +00:00
parent 59302983cc
commit ae74e50baf
3 changed files with 104 additions and 35 deletions

View File

@ -595,6 +595,9 @@ class SurfacePipeFactory {
* B8G8R8X8.
* @param aOrientation The orientation of the image.
*
* @param aFlags Note that only PREMULTIPLY_ALPHA is supported by this
* function.
*
* @return A SurfacePipe if the parameters allowed one to be created
* successfully, or Nothing() if the SurfacePipe could not be
* initialized.
@ -602,9 +605,15 @@ class SurfacePipeFactory {
static Maybe<SurfacePipe> CreateReorientSurfacePipe(
Decoder* aDecoder, const OrientedIntSize& aInputSize,
const OrientedIntSize& aOutputSize, gfx::SurfaceFormat aFormat,
qcms_transform* aTransform, const Orientation& aOrientation) {
qcms_transform* aTransform, const Orientation& aOrientation,
SurfacePipeFlags aFlags) {
MOZ_ASSERT(aFlags == SurfacePipeFlags() ||
aFlags == SurfacePipeFlags::PREMULTIPLY_ALPHA);
const bool downscale = aInputSize != aOutputSize;
const bool colorManagement = aTransform != nullptr;
const bool premultiplyAlpha =
bool(aFlags & SurfacePipeFlags::PREMULTIPLY_ALPHA);
// Construct configurations for the SurfaceFilters. Note that the order of
// these filters is significant. We want to deinterlace or interpolate raw
@ -618,39 +627,78 @@ class SurfacePipeFactory {
SurfaceConfig surfaceConfig{aDecoder, aOutputSize.ToUnknownSize(), aFormat,
/* mFlipVertically */ false,
/* mAnimParams */ Nothing()};
SwizzleConfig premultiplyConfig{aFormat, aFormat, premultiplyAlpha};
ReorientSurfaceConfig reorientSurfaceConfig{aDecoder, aOutputSize, aFormat,
aOrientation};
Maybe<SurfacePipe> pipe;
if (aOrientation.IsIdentity()) {
if (colorManagement) {
if (downscale) {
pipe =
MakePipe(downscalingConfig, colorManagementConfig, surfaceConfig);
} else { // (downscale is false)
pipe = MakePipe(colorManagementConfig, surfaceConfig);
if (premultiplyAlpha) {
if (aOrientation.IsIdentity()) {
if (colorManagement) {
if (downscale) {
pipe = MakePipe(downscalingConfig, colorManagementConfig,
premultiplyConfig, surfaceConfig);
} else { // (downscale is false)
pipe = MakePipe(colorManagementConfig, premultiplyConfig,
surfaceConfig);
}
} else { // (colorManagement is false)
if (downscale) {
pipe =
MakePipe(downscalingConfig, premultiplyConfig, surfaceConfig);
} else { // (downscale is false)
pipe = MakePipe(premultiplyConfig, surfaceConfig);
}
}
} else { // (colorManagement is false)
if (downscale) {
pipe = MakePipe(downscalingConfig, surfaceConfig);
} else { // (downscale is false)
pipe = MakePipe(surfaceConfig);
} else { // (orientation is not identity)
if (colorManagement) {
if (downscale) {
pipe = MakePipe(downscalingConfig, colorManagementConfig,
premultiplyConfig, reorientSurfaceConfig);
} else { // (downscale is false)
pipe = MakePipe(colorManagementConfig, premultiplyConfig,
reorientSurfaceConfig);
}
} else { // (colorManagement is false)
if (downscale) {
pipe = MakePipe(downscalingConfig, premultiplyConfig,
reorientSurfaceConfig);
} else { // (downscale is false)
pipe = MakePipe(premultiplyConfig, reorientSurfaceConfig);
}
}
}
} else { // (orientation is not identity)
if (colorManagement) {
if (downscale) {
pipe = MakePipe(downscalingConfig, colorManagementConfig,
reorientSurfaceConfig);
} else { // (downscale is false)
pipe = MakePipe(colorManagementConfig, reorientSurfaceConfig);
} else { // (premultiplyAlpha is false)
if (aOrientation.IsIdentity()) {
if (colorManagement) {
if (downscale) {
pipe = MakePipe(downscalingConfig, colorManagementConfig,
surfaceConfig);
} else { // (downscale is false)
pipe = MakePipe(colorManagementConfig, surfaceConfig);
}
} else { // (colorManagement is false)
if (downscale) {
pipe = MakePipe(downscalingConfig, surfaceConfig);
} else { // (downscale is false)
pipe = MakePipe(surfaceConfig);
}
}
} else { // (colorManagement is false)
if (downscale) {
pipe = MakePipe(downscalingConfig, reorientSurfaceConfig);
} else { // (downscale is false)
pipe = MakePipe(reorientSurfaceConfig);
} else { // (orientation is not identity)
if (colorManagement) {
if (downscale) {
pipe = MakePipe(downscalingConfig, colorManagementConfig,
reorientSurfaceConfig);
} else { // (downscale is false)
pipe = MakePipe(colorManagementConfig, reorientSurfaceConfig);
}
} else { // (colorManagement is false)
if (downscale) {
pipe = MakePipe(downscalingConfig, reorientSurfaceConfig);
} else { // (downscale is false)
pipe = MakePipe(reorientSurfaceConfig);
}
}
}
}

View File

@ -1907,15 +1907,26 @@ nsAVIFDecoder::DecodeResult nsAVIFDecoder::DoDecodeInternal(
}
PremultFunc premultOp = nullptr;
const auto wantPremultiply =
!bool(GetSurfaceFlags() & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
if (decodedData->mAlpha) {
const auto wantPremultiply =
!bool(GetSurfaceFlags() & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
const bool& hasPremultiply = decodedData->mAlpha->mPremultiplied;
if (wantPremultiply && !hasPremultiply) {
premultOp = libyuv::ARGBAttenuate;
} else if (!wantPremultiply && hasPremultiply) {
premultOp = libyuv::ARGBUnattenuate;
if (mTransform) {
// Color management needs to be done on non-premult data, so
// ConvertYCbCrToRGB32 needs to produce non-premult data, then color
// management can happen and then later in the surface pipe we will
// convert to premult if needed.
if (hasPremultiply) {
premultOp = libyuv::ARGBUnattenuate;
}
} else {
// no color management, so premult conversion (if needed) can be done by
// ConvertYCbCrToRGB32 before surface pipe
if (wantPremultiply && !hasPremultiply) {
premultOp = libyuv::ARGBAttenuate;
} else if (!wantPremultiply && hasPremultiply) {
premultOp = libyuv::ARGBUnattenuate;
}
}
}
@ -1935,6 +1946,15 @@ nsAVIFDecoder::DecodeResult nsAVIFDecoder::DoDecodeInternal(
Maybe<SurfacePipe> pipe = Nothing();
SurfacePipeFlags pipeFlags = SurfacePipeFlags();
if (decodedData->mAlpha && mTransform) {
// we know data is non-premult in this case, see above, so if we
// wantPremultiply then we have to ask the surface pipe to convert for us
if (wantPremultiply) {
pipeFlags |= SurfacePipeFlags::PREMULTIPLY_ALPHA;
}
}
if (mIsAnimated) {
SurfaceFormat outFormat =
decodedData->mAlpha ? SurfaceFormat::OS_RGBA : SurfaceFormat::OS_RGBX;
@ -1946,10 +1966,11 @@ nsAVIFDecoder::DecodeResult nsAVIFDecoder::DoDecodeInternal(
}
pipe = SurfacePipeFactory::CreateSurfacePipe(
this, Size(), OutputSize(), FullFrame(), format, outFormat, animParams,
mTransform, SurfacePipeFlags());
mTransform, pipeFlags);
} else {
pipe = SurfacePipeFactory::CreateReorientSurfacePipe(
this, Size(), OutputSize(), format, mTransform, GetOrientation());
this, Size(), OutputSize(), format, mTransform, GetOrientation(),
pipeFlags);
}
if (pipe.isNothing()) {

View File

@ -401,7 +401,7 @@ LexerTransition<nsJPEGDecoder::State> nsJPEGDecoder::ReadJPEGData(
Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateReorientSurfacePipe(
this, Size(), OutputSize(), SurfaceFormat::OS_RGBX, pipeTransform,
GetOrientation());
GetOrientation(), SurfacePipeFlags());
if (!pipe) {
mState = JPEG_ERROR;
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,