Bug 1551088 - Part 6. Make image decoders use accelerated methods for swizzling and premultiplication. r=tnikkel

The PNG decoder lacks fast implementations for swizzling/unpacking
inside the library, and both PNG and WebP may need to perform
premultiplication due to the alpha channel. This patch adds a new filter
allowing us to take advantage of our accelerated implementations to
perform these transformations on their behalf.

Differential Revision: https://phabricator.services.mozilla.com/D46449

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Andrew Osmond 2019-09-24 20:42:47 +00:00
Родитель 1c40442ec4
Коммит 4bf69333a2
8 изменённых файлов: 592 добавлений и 239 удалений

Просмотреть файл

@ -20,6 +20,7 @@
#include "mozilla/Maybe.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Swizzle.h"
#include "skia/src/core/SkBlitRow.h"
#include "DownscalingFilter.h"
@ -29,6 +30,83 @@
namespace mozilla {
namespace image {
//////////////////////////////////////////////////////////////////////////////
// SwizzleFilter
//////////////////////////////////////////////////////////////////////////////
template <typename Next>
class SwizzleFilter;
/**
* A configuration struct for SwizzleFilter.
*/
struct SwizzleConfig {
template <typename Next>
using Filter = SwizzleFilter<Next>;
gfx::SurfaceFormat mInFormat;
gfx::SurfaceFormat mOutFormat;
bool mPremultiplyAlpha;
};
/**
* SwizzleFilter performs premultiplication, swizzling and unpacking on
* rows written to it. It can use accelerated methods to perform these
* operations if supported on the platform.
*
* The 'Next' template parameter specifies the next filter in the chain.
*/
template <typename Next>
class SwizzleFilter final : public SurfaceFilter {
public:
SwizzleFilter() : mSwizzleFn(nullptr) {}
template <typename... Rest>
nsresult Configure(const SwizzleConfig& aConfig, const Rest&... aRest) {
nsresult rv = mNext.Configure(aRest...);
if (NS_FAILED(rv)) {
return rv;
}
if (aConfig.mPremultiplyAlpha) {
mSwizzleFn = gfx::PremultiplyRow(aConfig.mInFormat, aConfig.mOutFormat);
} else {
mSwizzleFn = gfx::SwizzleRow(aConfig.mInFormat, aConfig.mOutFormat);
}
if (!mSwizzleFn) {
return NS_ERROR_INVALID_ARG;
}
ConfigureFilter(mNext.InputSize(), sizeof(uint32_t));
return NS_OK;
}
Maybe<SurfaceInvalidRect> TakeInvalidRect() override {
return mNext.TakeInvalidRect();
}
protected:
uint8_t* DoResetToFirstRow() override { return mNext.ResetToFirstRow(); }
uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) override {
uint8_t* rowPtr = mNext.CurrentRowPointer();
if (!rowPtr) {
return nullptr; // We already got all the input rows we expect.
}
mSwizzleFn(aInputRow, rowPtr, mNext.InputSize().width);
return mNext.AdvanceRow();
}
uint8_t* DoAdvanceRow() override {
return DoAdvanceRowFromBuffer(mNext.CurrentRowPointer());
}
Next mNext; /// The next SurfaceFilter in the chain.
gfx::SwizzleRowFn mSwizzleFn;
};
//////////////////////////////////////////////////////////////////////////////
// ColorManagementFilter
//////////////////////////////////////////////////////////////////////////////

Просмотреть файл

@ -59,6 +59,9 @@ enum class SurfacePipeFlags {
// result in a better user experience for
// progressive display but which may be more
// computationally expensive.
PREMULTIPLY_ALPHA = 1 << 4, // If set, we want to premultiply the alpha
// channel and the individual color channels.
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(SurfacePipeFlags)
@ -88,8 +91,9 @@ class SurfacePipeFactory {
static Maybe<SurfacePipe> CreateSurfacePipe(
Decoder* aDecoder, const nsIntSize& aInputSize,
const nsIntSize& aOutputSize, const nsIntRect& aFrameRect,
gfx::SurfaceFormat aFormat, const Maybe<AnimationParams>& aAnimParams,
qcms_transform* aTransform, SurfacePipeFlags aFlags) {
gfx::SurfaceFormat aInFormat, gfx::SurfaceFormat aOutFormat,
const Maybe<AnimationParams>& aAnimParams, qcms_transform* aTransform,
SurfacePipeFlags aFlags) {
const bool deinterlace = bool(aFlags & SurfacePipeFlags::DEINTERLACE);
const bool flipVertically =
bool(aFlags & SurfacePipeFlags::FLIP_VERTICALLY);
@ -100,6 +104,46 @@ class SurfacePipeFactory {
nsIntRect(0, 0, aInputSize.width, aInputSize.height));
const bool blendAnimation = aAnimParams.isSome();
const bool colorManagement = aTransform != nullptr;
const bool premultiplyAlpha =
bool(aFlags & SurfacePipeFlags::PREMULTIPLY_ALPHA);
// Early swizzles are for unpacking RGB or forcing RGBA/BGRA to RGBX/BGRX.
// We should never want to premultiply in either case, but the image's
// alpha channel will always be opaque.
bool earlySwizzle = aInFormat == gfx::SurfaceFormat::R8G8B8 ||
((aInFormat == gfx::SurfaceFormat::R8G8B8A8 &&
aOutFormat == gfx::SurfaceFormat::R8G8B8X8) ||
(aInFormat == gfx::SurfaceFormat::B8G8R8A8 &&
aOutFormat == gfx::SurfaceFormat::B8G8R8X8));
// Late swizzles are for premultiplying RGBA/BGRA and/or possible converting
// between RGBA and BGRA. It must happen after color management.
bool lateSwizzle = ((aInFormat == gfx::SurfaceFormat::R8G8B8A8 &&
aOutFormat == gfx::SurfaceFormat::B8G8R8A8) ||
(aInFormat == gfx::SurfaceFormat::B8G8R8A8 &&
aOutFormat == gfx::SurfaceFormat::R8G8B8A8)) ||
premultiplyAlpha;
MOZ_ASSERT(aInFormat == gfx::SurfaceFormat::R8G8B8 ||
aInFormat == gfx::SurfaceFormat::R8G8B8A8 ||
aInFormat == gfx::SurfaceFormat::R8G8B8X8 ||
aInFormat == gfx::SurfaceFormat::B8G8R8A8 ||
aInFormat == gfx::SurfaceFormat::B8G8R8X8);
MOZ_ASSERT(aOutFormat == gfx::SurfaceFormat::R8G8B8A8 ||
aOutFormat == gfx::SurfaceFormat::R8G8B8X8 ||
aOutFormat == gfx::SurfaceFormat::B8G8R8A8 ||
aOutFormat == gfx::SurfaceFormat::B8G8R8X8);
if (earlySwizzle && lateSwizzle) {
MOZ_ASSERT_UNREACHABLE("Early and late swizzles not supported");
return Nothing();
}
if (!earlySwizzle && !lateSwizzle && aInFormat != aOutFormat) {
MOZ_ASSERT_UNREACHABLE("Need to swizzle, but not configured to");
return Nothing();
}
// Don't interpolate if we're sure we won't show this surface to the user
// until it's completely decoded. The final pass of an ADAM7 image doesn't
@ -124,13 +168,285 @@ class SurfacePipeFactory {
ADAM7InterpolatingConfig interpolatingConfig;
RemoveFrameRectConfig removeFrameRectConfig{aFrameRect};
BlendAnimationConfig blendAnimationConfig{aDecoder};
DownscalingConfig downscalingConfig{aInputSize, aFormat};
DownscalingConfig downscalingConfig{aInputSize, aOutFormat};
ColorManagementConfig colorManagementConfig{aTransform};
SurfaceConfig surfaceConfig{aDecoder, aOutputSize, aFormat, flipVertically,
aAnimParams};
SwizzleConfig swizzleConfig{aInFormat, aOutFormat, premultiplyAlpha};
SurfaceConfig surfaceConfig{aDecoder, aOutputSize, aOutFormat,
flipVertically, aAnimParams};
Maybe<SurfacePipe> pipe;
if (earlySwizzle) {
if (colorManagement) {
if (downscale) {
MOZ_ASSERT(!blendAnimation);
if (removeFrameRect) {
if (deinterlace) {
pipe = MakePipe(swizzleConfig, deinterlacingConfig,
removeFrameRectConfig, downscalingConfig,
colorManagementConfig, surfaceConfig);
} else if (adam7Interpolate) {
pipe = MakePipe(swizzleConfig, interpolatingConfig,
removeFrameRectConfig, downscalingConfig,
colorManagementConfig, surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe = MakePipe(swizzleConfig, removeFrameRectConfig,
downscalingConfig, colorManagementConfig,
surfaceConfig);
}
} else { // (removeFrameRect is false)
if (deinterlace) {
pipe = MakePipe(swizzleConfig, deinterlacingConfig,
downscalingConfig, colorManagementConfig,
surfaceConfig);
} else if (adam7Interpolate) {
pipe = MakePipe(swizzleConfig, interpolatingConfig,
downscalingConfig, colorManagementConfig,
surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe = MakePipe(swizzleConfig, downscalingConfig,
colorManagementConfig, surfaceConfig);
}
}
} else { // (downscale is false)
if (blendAnimation) {
if (deinterlace) {
pipe = MakePipe(swizzleConfig, deinterlacingConfig,
colorManagementConfig, blendAnimationConfig,
surfaceConfig);
} else if (adam7Interpolate) {
pipe = MakePipe(swizzleConfig, interpolatingConfig,
colorManagementConfig, blendAnimationConfig,
surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe = MakePipe(swizzleConfig, colorManagementConfig,
blendAnimationConfig, surfaceConfig);
}
} else if (removeFrameRect) {
if (deinterlace) {
pipe = MakePipe(swizzleConfig, deinterlacingConfig,
colorManagementConfig, removeFrameRectConfig,
surfaceConfig);
} else if (adam7Interpolate) {
pipe = MakePipe(swizzleConfig, interpolatingConfig,
colorManagementConfig, removeFrameRectConfig,
surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe = MakePipe(swizzleConfig, colorManagementConfig,
removeFrameRectConfig, surfaceConfig);
}
} else { // (blendAnimation and removeFrameRect is false)
if (deinterlace) {
pipe = MakePipe(swizzleConfig, deinterlacingConfig,
colorManagementConfig, surfaceConfig);
} else if (adam7Interpolate) {
pipe = MakePipe(swizzleConfig, interpolatingConfig,
colorManagementConfig, surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe =
MakePipe(swizzleConfig, colorManagementConfig, surfaceConfig);
}
}
}
} else { // (colorManagement is false)
if (downscale) {
MOZ_ASSERT(!blendAnimation);
if (removeFrameRect) {
if (deinterlace) {
pipe = MakePipe(swizzleConfig, deinterlacingConfig,
removeFrameRectConfig, downscalingConfig,
surfaceConfig);
} else if (adam7Interpolate) {
pipe = MakePipe(swizzleConfig, interpolatingConfig,
removeFrameRectConfig, downscalingConfig,
surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe = MakePipe(swizzleConfig, removeFrameRectConfig,
downscalingConfig, surfaceConfig);
}
} else { // (removeFrameRect is false)
if (deinterlace) {
pipe = MakePipe(swizzleConfig, deinterlacingConfig,
downscalingConfig, surfaceConfig);
} else if (adam7Interpolate) {
pipe = MakePipe(swizzleConfig, interpolatingConfig,
downscalingConfig, surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe = MakePipe(swizzleConfig, downscalingConfig, surfaceConfig);
}
}
} else { // (downscale is false)
if (blendAnimation) {
if (deinterlace) {
pipe = MakePipe(swizzleConfig, deinterlacingConfig,
blendAnimationConfig, surfaceConfig);
} else if (adam7Interpolate) {
pipe = MakePipe(swizzleConfig, interpolatingConfig,
blendAnimationConfig, surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe =
MakePipe(swizzleConfig, blendAnimationConfig, surfaceConfig);
}
} else if (removeFrameRect) {
if (deinterlace) {
pipe = MakePipe(swizzleConfig, deinterlacingConfig,
removeFrameRectConfig, surfaceConfig);
} else if (adam7Interpolate) {
pipe = MakePipe(swizzleConfig, interpolatingConfig,
removeFrameRectConfig, surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe =
MakePipe(swizzleConfig, removeFrameRectConfig, surfaceConfig);
}
} else { // (blendAnimation and removeFrameRect is false)
if (deinterlace) {
pipe =
MakePipe(swizzleConfig, deinterlacingConfig, surfaceConfig);
} else if (adam7Interpolate) {
pipe =
MakePipe(swizzleConfig, interpolatingConfig, surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe = MakePipe(swizzleConfig, surfaceConfig);
}
}
}
}
} else if (lateSwizzle) {
if (colorManagement) {
if (downscale) {
MOZ_ASSERT(!blendAnimation);
if (removeFrameRect) {
if (deinterlace) {
pipe = MakePipe(deinterlacingConfig, removeFrameRectConfig,
downscalingConfig, colorManagementConfig,
swizzleConfig, surfaceConfig);
} else if (adam7Interpolate) {
pipe = MakePipe(interpolatingConfig, removeFrameRectConfig,
downscalingConfig, colorManagementConfig,
swizzleConfig, surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe =
MakePipe(removeFrameRectConfig, downscalingConfig,
colorManagementConfig, swizzleConfig, surfaceConfig);
}
} else { // (removeFrameRect is false)
if (deinterlace) {
pipe =
MakePipe(deinterlacingConfig, downscalingConfig,
colorManagementConfig, swizzleConfig, surfaceConfig);
} else if (adam7Interpolate) {
pipe =
MakePipe(interpolatingConfig, downscalingConfig,
colorManagementConfig, swizzleConfig, surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe = MakePipe(downscalingConfig, colorManagementConfig,
swizzleConfig, surfaceConfig);
}
}
} else { // (downscale is false)
if (blendAnimation) {
if (deinterlace) {
pipe =
MakePipe(deinterlacingConfig, colorManagementConfig,
swizzleConfig, blendAnimationConfig, surfaceConfig);
} else if (adam7Interpolate) {
pipe = MakePipe(interpolatingConfig, colorManagementConfig,
swizzleConfig, blendAnimationConfig,
swizzleConfig, surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe = MakePipe(colorManagementConfig, swizzleConfig,
blendAnimationConfig, surfaceConfig);
}
} else if (removeFrameRect) {
if (deinterlace) {
pipe =
MakePipe(deinterlacingConfig, colorManagementConfig,
swizzleConfig, removeFrameRectConfig, surfaceConfig);
} else if (adam7Interpolate) {
pipe =
MakePipe(interpolatingConfig, colorManagementConfig,
swizzleConfig, removeFrameRectConfig, surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe = MakePipe(colorManagementConfig, swizzleConfig,
removeFrameRectConfig, surfaceConfig);
}
} else { // (blendAnimation and removeFrameRect is false)
if (deinterlace) {
pipe = MakePipe(deinterlacingConfig, colorManagementConfig,
swizzleConfig, surfaceConfig);
} else if (adam7Interpolate) {
pipe = MakePipe(interpolatingConfig, colorManagementConfig,
swizzleConfig, surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe =
MakePipe(colorManagementConfig, swizzleConfig, surfaceConfig);
}
}
}
} else { // (colorManagement is false)
if (downscale) {
MOZ_ASSERT(!blendAnimation);
if (removeFrameRect) {
if (deinterlace) {
pipe = MakePipe(deinterlacingConfig, swizzleConfig,
removeFrameRectConfig, downscalingConfig,
surfaceConfig);
} else if (adam7Interpolate) {
pipe = MakePipe(interpolatingConfig, swizzleConfig,
removeFrameRectConfig, downscalingConfig,
surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe = MakePipe(swizzleConfig, removeFrameRectConfig,
downscalingConfig, surfaceConfig);
}
} else { // (removeFrameRect is false)
if (deinterlace) {
pipe = MakePipe(deinterlacingConfig, downscalingConfig,
swizzleConfig, surfaceConfig);
} else if (adam7Interpolate) {
pipe = MakePipe(interpolatingConfig, downscalingConfig,
swizzleConfig, surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe = MakePipe(downscalingConfig, swizzleConfig, surfaceConfig);
}
}
} else { // (downscale is false)
if (blendAnimation) {
if (deinterlace) {
pipe = MakePipe(deinterlacingConfig, swizzleConfig,
blendAnimationConfig, surfaceConfig);
} else if (adam7Interpolate) {
pipe = MakePipe(interpolatingConfig, swizzleConfig,
blendAnimationConfig, surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe =
MakePipe(swizzleConfig, blendAnimationConfig, surfaceConfig);
}
} else if (removeFrameRect) {
if (deinterlace) {
pipe = MakePipe(deinterlacingConfig, swizzleConfig,
removeFrameRectConfig, surfaceConfig);
} else if (adam7Interpolate) {
pipe = MakePipe(interpolatingConfig, swizzleConfig,
removeFrameRectConfig, surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe =
MakePipe(swizzleConfig, removeFrameRectConfig, surfaceConfig);
}
} else { // (blendAnimation and removeFrameRect is false)
if (deinterlace) {
pipe =
MakePipe(deinterlacingConfig, swizzleConfig, surfaceConfig);
} else if (adam7Interpolate) {
pipe =
MakePipe(interpolatingConfig, swizzleConfig, surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe = MakePipe(swizzleConfig, surfaceConfig);
}
}
}
}
} else { // (earlySwizzle and lateSwizzle are false)
if (colorManagement) {
if (downscale) {
MOZ_ASSERT(!blendAnimation);
@ -210,11 +526,11 @@ class SurfacePipeFactory {
}
} else { // (removeFrameRect is false)
if (deinterlace) {
pipe =
MakePipe(deinterlacingConfig, downscalingConfig, surfaceConfig);
pipe = MakePipe(deinterlacingConfig, downscalingConfig,
surfaceConfig);
} else if (adam7Interpolate) {
pipe =
MakePipe(interpolatingConfig, downscalingConfig, surfaceConfig);
pipe = MakePipe(interpolatingConfig, downscalingConfig,
surfaceConfig);
} else { // (deinterlace and adam7Interpolate are false)
pipe = MakePipe(downscalingConfig, surfaceConfig);
}
@ -251,6 +567,7 @@ class SurfacePipeFactory {
}
}
}
}
return pipe;
}

Просмотреть файл

@ -193,8 +193,8 @@ nsresult nsGIFDecoder2::BeginImageFrame(const IntRect& aFrameRect,
}
Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(
this, Size(), OutputSize(), aFrameRect, format, animParams, mTransform,
pipeFlags);
this, Size(), OutputSize(), aFrameRect, format, format, animParams,
mTransform, pipeFlags);
mCurrentFrameIndex = mGIFStruct.images_decoded;
if (!pipe) {

Просмотреть файл

@ -68,6 +68,7 @@ LexerTransition<nsIconDecoder::State> nsIconDecoder::ReadHeader(
MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(
this, Size(), OutputSize(), FullFrame(), SurfaceFormat::B8G8R8A8,
SurfaceFormat::B8G8R8A8,
/* aAnimParams */ Nothing(), mTransform, SurfacePipeFlags());
if (!pipe) {
return Transition::TerminateFailure();

Просмотреть файл

@ -357,7 +357,8 @@ LexerTransition<nsJPEGDecoder::State> nsJPEGDecoder::ReadJPEGData(
Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(
this, Size(), OutputSize(), FullFrame(), SurfaceFormat::B8G8R8X8,
Nothing(), pipeTransform, SurfacePipeFlags());
SurfaceFormat::B8G8R8X8, Nothing(), pipeTransform,
SurfacePipeFlags());
if (!pipe) {
mState = JPEG_ERROR;
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,

Просмотреть файл

@ -116,6 +116,7 @@ nsPNGDecoder::nsPNGDecoder(RasterImage* aImage)
mFrameIsHidden(false),
mDisablePremultipliedAlpha(false),
mGotInfoCallback(false),
mUsePipeTransform(false),
mNumFrames(0) {}
nsPNGDecoder::~nsPNGDecoder() {
@ -212,9 +213,29 @@ nsresult nsPNGDecoder::CreateFrame(const FrameInfo& aFrameInfo) {
pipeFlags |= SurfacePipeFlags::PROGRESSIVE_DISPLAY;
}
SurfaceFormat inFormat;
if (mTransform && !mUsePipeTransform) {
// QCMS will output in the correct format.
inFormat = mFormat;
} else if (transparency == TransparencyType::eAlpha) {
// We are outputting directly as RGBA, so we need to swap at this step.
inFormat = SurfaceFormat::R8G8B8A8;
} else {
// We have no alpha channel, so we need to unpack from RGB to BGRA.
inFormat = SurfaceFormat::R8G8B8;
}
// Only apply premultiplication if the frame has true alpha. If we ever
// support downscaling animated images, we will need to premultiply for frame
// rect transparency when downscaling as well.
if (transparency == TransparencyType::eAlpha && !mDisablePremultipliedAlpha) {
pipeFlags |= SurfacePipeFlags::PREMULTIPLY_ALPHA;
}
qcms_transform* pipeTransform = mUsePipeTransform ? mTransform : nullptr;
Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(
this, Size(), OutputSize(), aFrameInfo.mFrameRect, mFormat, animParams,
/*aTransform*/ nullptr, pipeFlags);
this, Size(), OutputSize(), aFrameInfo.mFrameRect, inFormat, mFormat,
animParams, pipeTransform, pipeFlags);
if (!pipe) {
mPipe = SurfacePipe();
@ -413,8 +434,7 @@ static void PNGDoGammaCorrection(png_structp png_ptr, png_infop info_ptr) {
// Adapted from http://www.littlecms.com/pngchrm.c example code
static qcms_profile* PNGGetColorProfile(png_structp png_ptr, png_infop info_ptr,
int color_type, qcms_data_type* inType,
uint32_t* intent) {
int color_type, uint32_t* intent) {
qcms_profile* profile = nullptr;
*intent = QCMS_INTENT_PERCEPTUAL; // Our default
@ -492,24 +512,6 @@ static qcms_profile* PNGGetColorProfile(png_structp png_ptr, png_infop info_ptr,
}
}
if (profile) {
uint32_t profileSpace = qcms_profile_get_color_space(profile);
if (profileSpace == icSigGrayData) {
if (color_type & PNG_COLOR_MASK_ALPHA) {
*inType = QCMS_DATA_GRAYA_8;
} else {
*inType = QCMS_DATA_GRAY_8;
}
} else {
if (color_type & PNG_COLOR_MASK_ALPHA ||
png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
*inType = QCMS_DATA_RGBA_8;
} else {
*inType = QCMS_DATA_RGB_8;
}
}
}
return profile;
}
@ -590,14 +592,13 @@ void nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) {
// We only need to extract the color profile for non-metadata decodes. It is
// fairly expensive to read the profile and create the transform so we should
// avoid it if not necessary.
qcms_data_type inType = QCMS_DATA_RGBA_8;
uint32_t intent = -1;
uint32_t pIntent;
if (!decoder->IsMetadataDecode()) {
if (decoder->mCMSMode != eCMSMode_Off) {
intent = gfxPlatform::GetRenderingIntent();
decoder->mInProfile =
PNGGetColorProfile(png_ptr, info_ptr, color_type, &inType, &pIntent);
PNGGetColorProfile(png_ptr, info_ptr, color_type, &pIntent);
// If we're not mandating an intent, use the one from the image.
if (intent == uint32_t(-1)) {
intent = pIntent;
@ -648,6 +649,7 @@ void nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) {
}
#endif
auto transparency = decoder->GetTransparencyType(frameRect);
if (decoder->IsMetadataDecode()) {
// If we are animated then the first frame rect is either:
// 1) the whole image if the IDAT chunk is part of the animation
@ -656,7 +658,6 @@ void nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) {
// PostHasTransparency in the metadata decode if we need to. So it's
// okay to pass IntRect(0, 0, width, height) here for animated images;
// they will call with the proper first frame rect in the full decode.
auto transparency = decoder->GetTransparencyType(frameRect);
decoder->PostHasTransparencyIfNeeded(transparency);
// We have the metadata we're looking for, so stop here, before we allocate
@ -665,23 +666,47 @@ void nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) {
}
if (decoder->mInProfile && gfxPlatform::GetCMSOutputProfile()) {
qcms_data_type inType;
qcms_data_type outType;
if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) {
uint32_t profileSpace = qcms_profile_get_color_space(decoder->mInProfile);
decoder->mUsePipeTransform = profileSpace != icSigGrayData;
if (decoder->mUsePipeTransform) {
// If the transform happens with SurfacePipe, it will be in RGBA if we
// have an alpha channel, because the swizzle and premultiplication
// happens after color management. Otherwise it will be in BGRA because
// the swizzle happens at the start.
if (transparency == TransparencyType::eAlpha) {
inType = QCMS_DATA_RGBA_8;
outType = QCMS_DATA_RGBA_8;
} else {
outType = QCMS_DATA_RGB_8;
inType = QCMS_DATA_BGRA_8;
outType = QCMS_DATA_BGRA_8;
}
} else {
if (color_type & PNG_COLOR_MASK_ALPHA) {
inType = QCMS_DATA_GRAYA_8;
outType = QCMS_DATA_BGRA_8;
} else {
inType = QCMS_DATA_GRAY_8;
outType = QCMS_DATA_BGRA_8;
}
}
decoder->mTransform = qcms_transform_create(
decoder->mInProfile, inType, gfxPlatform::GetCMSOutputProfile(),
outType, (qcms_intent)intent);
} else if (decoder->mCMSMode == eCMSMode_All) {
if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) {
// If the transform happens with SurfacePipe, it will be in RGBA if we
// have an alpha channel, because the swizzle and premultiplication
// happens after color management. Otherwise it will be in BGRA because
// the swizzle happens at the start.
if (transparency == TransparencyType::eAlpha) {
decoder->mTransform = gfxPlatform::GetCMSRGBATransform();
} else {
decoder->mTransform = gfxPlatform::GetCMSRGBTransform();
decoder->mTransform = gfxPlatform::GetCMSBGRATransform();
}
decoder->mUsePipeTransform = true;
}
#ifdef PNG_APNG_SUPPORTED
@ -703,10 +728,9 @@ void nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) {
}
#endif
if (decoder->mTransform && (channels <= 2 || isInterlaced)) {
uint32_t bpp[] = {0, 3, 4, 3, 4};
if (decoder->mTransform && !decoder->mUsePipeTransform) {
decoder->mCMSLine =
static_cast<uint8_t*>(malloc(bpp[channels] * frameRect.Width()));
static_cast<uint8_t*>(malloc(sizeof(uint32_t) * frameRect.Width()));
if (!decoder->mCMSLine) {
png_error(decoder->mPNG, "malloc of mCMSLine failed");
}
@ -740,29 +764,6 @@ void nsPNGDecoder::PostInvalidationIfNeeded() {
Some(invalidRect->mOutputSpaceRect));
}
static NextPixel<uint32_t> PackRGBPixelAndAdvance(uint8_t*& aRawPixelInOut) {
const uint32_t pixel = gfxPackedPixel(0xFF, aRawPixelInOut[0],
aRawPixelInOut[1], aRawPixelInOut[2]);
aRawPixelInOut += 3;
return AsVariant(pixel);
}
static NextPixel<uint32_t> PackRGBAPixelAndAdvance(uint8_t*& aRawPixelInOut) {
const uint32_t pixel = gfxPackedPixel(aRawPixelInOut[3], aRawPixelInOut[0],
aRawPixelInOut[1], aRawPixelInOut[2]);
aRawPixelInOut += 4;
return AsVariant(pixel);
}
static NextPixel<uint32_t> PackUnpremultipliedRGBAPixelAndAdvance(
uint8_t*& aRawPixelInOut) {
const uint32_t pixel =
gfxPackedPixelNoPreMultiply(aRawPixelInOut[3], aRawPixelInOut[0],
aRawPixelInOut[1], aRawPixelInOut[2]);
aRawPixelInOut += 4;
return AsVariant(pixel);
}
void nsPNGDecoder::row_callback(png_structp png_ptr, png_bytep new_row,
png_uint_32 row_num, int pass) {
/* libpng comments:
@ -847,38 +848,16 @@ void nsPNGDecoder::WriteRow(uint8_t* aRow) {
uint32_t width = uint32_t(mFrameRect.Width());
// Apply color management to the row, if necessary, before writing it out.
if (mTransform) {
if (mCMSLine) {
// This is only needed for grayscale images.
if (mTransform && !mUsePipeTransform) {
MOZ_ASSERT(mCMSLine);
qcms_transform_data(mTransform, rowToWrite, mCMSLine, width);
// Copy alpha over.
if (HasAlphaChannel()) {
for (uint32_t i = 0; i < width; ++i) {
mCMSLine[4 * i + 3] = rowToWrite[mChannels * i + mChannels - 1];
}
}
rowToWrite = mCMSLine;
} else {
qcms_transform_data(mTransform, rowToWrite, rowToWrite, width);
}
}
// Write this row to the SurfacePipe.
DebugOnly<WriteState> result;
if (HasAlphaChannel()) {
if (mDisablePremultipliedAlpha) {
result = mPipe.WritePixelsToRow<uint32_t>(
[&] { return PackUnpremultipliedRGBAPixelAndAdvance(rowToWrite); });
} else {
result = mPipe.WritePixelsToRow<uint32_t>(
[&] { return PackRGBAPixelAndAdvance(rowToWrite); });
}
} else {
result = mPipe.WritePixelsToRow<uint32_t>(
[&] { return PackRGBPixelAndAdvance(rowToWrite); });
}
DebugOnly<WriteState> result =
mPipe.WriteBuffer(reinterpret_cast<uint32_t*>(rowToWrite));
MOZ_ASSERT(WriteState(result) != WriteState::FAILURE);
PostInvalidationIfNeeded();

Просмотреть файл

@ -11,6 +11,7 @@
#include "png.h"
#include "StreamingLexer.h"
#include "SurfacePipe.h"
#include "mozilla/gfx/Swizzle.h"
namespace mozilla {
namespace image {
@ -101,6 +102,7 @@ class nsPNGDecoder : public Decoder {
bool mFrameIsHidden;
bool mDisablePremultipliedAlpha;
bool mGotInfoCallback;
bool mUsePipeTransform;
struct AnimFrameInfo {
AnimFrameInfo();

Просмотреть файл

@ -210,7 +210,7 @@ nsresult nsWebPDecoder::CreateFrame(const nsIntRect& aFrameRect) {
}
WebPInitDecBuffer(&mBuffer);
mBuffer.colorspace = MODE_RGBA;
mBuffer.colorspace = MODE_BGRA;
mDecoder = WebPINewDecoder(&mBuffer);
if (!mDecoder) {
@ -220,7 +220,16 @@ nsresult nsWebPDecoder::CreateFrame(const nsIntRect& aFrameRect) {
return NS_ERROR_FAILURE;
}
// WebP doesn't guarantee that the alpha generated matches the hint in the
// header, so we always need to claim the input is BGRA. If the output is
// BGRX, swizzling will mask off the alpha channel.
SurfaceFormat inFormat = SurfaceFormat::B8G8R8A8;
SurfacePipeFlags pipeFlags = SurfacePipeFlags();
if (mFormat == SurfaceFormat::B8G8R8A8 &&
!(GetSurfaceFlags() & SurfaceFlags::NO_PREMULTIPLY_ALPHA)) {
pipeFlags |= SurfacePipeFlags::PREMULTIPLY_ALPHA;
}
Maybe<AnimationParams> animParams;
if (!IsFirstFrameDecode()) {
@ -228,8 +237,8 @@ nsresult nsWebPDecoder::CreateFrame(const nsIntRect& aFrameRect) {
}
Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(
this, Size(), OutputSize(), aFrameRect, mFormat, animParams,
/*aTransform*/ nullptr, pipeFlags);
this, Size(), OutputSize(), aFrameRect, inFormat, mFormat, animParams,
mTransform, pipeFlags);
if (!pipe) {
MOZ_LOG(sWebPLog, LogLevel::Error,
("[this=%p] nsWebPDecoder::CreateFrame -- no pipe\n", this));
@ -281,7 +290,7 @@ void nsWebPDecoder::ApplyColorProfile(const char* aProfile, size_t aLength) {
("[this=%p] nsWebPDecoder::ApplyColorProfile -- not tagged, use "
"sRGB transform\n",
this));
mTransform = gfxPlatform::GetCMSRGBATransform();
mTransform = gfxPlatform::GetCMSBGRATransform();
return;
}
@ -311,9 +320,9 @@ void nsWebPDecoder::ApplyColorProfile(const char* aProfile, size_t aLength) {
}
// Create the color management transform.
mTransform = qcms_transform_create(mInProfile, QCMS_DATA_RGBA_8,
mTransform = qcms_transform_create(mInProfile, QCMS_DATA_BGRA_8,
gfxPlatform::GetCMSOutputProfile(),
QCMS_DATA_RGBA_8, (qcms_intent)intent);
QCMS_DATA_BGRA_8, (qcms_intent)intent);
MOZ_LOG(sWebPLog, LogLevel::Debug,
("[this=%p] nsWebPDecoder::ApplyColorProfile -- use tagged "
"transform\n",
@ -458,43 +467,9 @@ LexerResult nsWebPDecoder::ReadSingle(const uint8_t* aData, size_t aLength,
return LexerResult(TerminalState::FAILURE);
}
const bool noPremultiply =
bool(GetSurfaceFlags() & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
for (int row = mLastRow; row < lastRow; row++) {
uint8_t* src = rowStart + row * stride;
if (mTransform) {
qcms_transform_data(mTransform, src, src, width);
}
WriteState result;
if (mFormat == SurfaceFormat::B8G8R8A8) {
if (noPremultiply) {
result =
mPipe.WritePixelsToRow<uint32_t>([&]() -> NextPixel<uint32_t> {
const uint32_t pixel =
gfxPackedPixelNoPreMultiply(src[3], src[0], src[1], src[2]);
src += 4;
return AsVariant(pixel);
});
} else {
result =
mPipe.WritePixelsToRow<uint32_t>([&]() -> NextPixel<uint32_t> {
const uint32_t pixel =
gfxPackedPixel(src[3], src[0], src[1], src[2]);
src += 4;
return AsVariant(pixel);
});
}
} else {
// We are producing a surface without transparency. Ignore the alpha
// channel provided to us by the library.
result = mPipe.WritePixelsToRow<uint32_t>([&]() -> NextPixel<uint32_t> {
const uint32_t pixel = gfxPackedPixel(0xFF, src[0], src[1], src[2]);
src += 4;
return AsVariant(pixel);
});
}
uint32_t* src = reinterpret_cast<uint32_t*>(rowStart + row * stride);
WriteState result = mPipe.WriteBuffer(src);
Maybe<SurfaceInvalidRect> invalidRect = mPipe.TakeInvalidRect();
if (invalidRect) {