Refactor pixel conversion: premul and unpremul

Just going for simpler and more code sharing.

BUG=skia:

Change-Id: I84c20cd4dbb6950f7b4d0bc659c4b3b5a2af201c
Reviewed-on: https://skia-review.googlesource.com/8287
Reviewed-by: Mike Klein <mtklein@chromium.org>
Commit-Queue: Matt Sarett <msarett@google.com>
This commit is contained in:
Matt Sarett 2017-02-09 16:22:39 -05:00 committed by Skia Commit-Bot
parent 5881e82060
commit c7b2908947
5 changed files with 117 additions and 187 deletions

View File

@ -14,9 +14,10 @@
#include "SkDither.h" #include "SkDither.h"
#include "SkImageInfoPriv.h" #include "SkImageInfoPriv.h"
#include "SkMathPriv.h" #include "SkMathPriv.h"
#include "SkOpts.h"
#include "SkPM4fPriv.h" #include "SkPM4fPriv.h"
#include "SkRasterPipeline.h" #include "SkRasterPipeline.h"
#include "SkUnPreMultiply.h" #include "SkUnPreMultiplyPriv.h"
// Fast Path 1: The memcpy() case. // Fast Path 1: The memcpy() case.
static inline bool can_memcpy(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo) { static inline bool can_memcpy(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo) {
@ -36,6 +37,53 @@ static inline bool can_memcpy(const SkImageInfo& dstInfo, const SkImageInfo& src
SkColorSpace::Equals(dstInfo.colorSpace(), srcInfo.colorSpace()); SkColorSpace::Equals(dstInfo.colorSpace(), srcInfo.colorSpace());
} }
enum AlphaVerb {
kNothing_AlphaVerb,
kPremul_AlphaVerb,
kUnpremul_AlphaVerb,
};
template <bool kSwapRB>
static void wrap_unpremultiply(uint32_t* dst, const void* src, int count) {
SkUnpremultiplyRow<kSwapRB>(dst, (const uint32_t*) src, count);
}
// Fast Path 2: Simple swizzles and premuls.
void swizzle_and_multiply(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
const SkImageInfo& srcInfo, const void* srcPixels, size_t srcRB) {
void (*proc)(uint32_t* dst, const void* src, int count);
const bool swapRB = dstInfo.colorType() != srcInfo.colorType();
AlphaVerb alphaVerb = kNothing_AlphaVerb;
if (kPremul_SkAlphaType == dstInfo.alphaType() &&
kUnpremul_SkAlphaType == srcInfo.alphaType())
{
alphaVerb = kPremul_AlphaVerb;
} else if (kUnpremul_SkAlphaType == dstInfo.alphaType() &&
kPremul_SkAlphaType == srcInfo.alphaType()) {
alphaVerb = kUnpremul_AlphaVerb;
}
switch (alphaVerb) {
case kNothing_AlphaVerb:
// If we do not need to swap or multiply, we should hit the memcpy case.
SkASSERT(swapRB);
proc = SkOpts::RGBA_to_BGRA;
break;
case kPremul_AlphaVerb:
proc = swapRB ? SkOpts::RGBA_to_bgrA : SkOpts::RGBA_to_rgbA;
break;
case kUnpremul_AlphaVerb:
proc = swapRB ? wrap_unpremultiply<true> : wrap_unpremultiply<false>;
break;
}
for (int y = 0; y < dstInfo.height(); y++) {
proc((uint32_t*) dstPixels, srcPixels, dstInfo.width());
dstPixels = SkTAddOffset<void>(dstPixels, dstRB);
srcPixels = SkTAddOffset<const void>(srcPixels, srcRB);
}
}
// Default: Use the pipeline. // Default: Use the pipeline.
static bool copy_pipeline_pixels(const SkImageInfo& dstInfo, void* dstRow, size_t dstRB, static bool copy_pipeline_pixels(const SkImageInfo& dstInfo, void* dstRow, size_t dstRB,
const SkImageInfo& srcInfo, const void* srcRow, size_t srcRB, const SkImageInfo& srcInfo, const void* srcRow, size_t srcRB,
@ -114,107 +162,6 @@ static bool copy_pipeline_pixels(const SkImageInfo& dstInfo, void* dstRow, size_
return true; return true;
} }
enum AlphaVerb {
kNothing_AlphaVerb,
kPremul_AlphaVerb,
kUnpremul_AlphaVerb,
};
template <bool doSwapRB, AlphaVerb doAlpha> uint32_t convert32(uint32_t c) {
if (doSwapRB) {
c = SkSwizzle_RB(c);
}
// Lucky for us, in both RGBA and BGRA, the alpha component is always in the same place, so
// we can perform premul or unpremul the same way without knowing the swizzles for RGB.
switch (doAlpha) {
case kNothing_AlphaVerb:
// no change
break;
case kPremul_AlphaVerb:
c = SkPreMultiplyARGB(SkGetPackedA32(c), SkGetPackedR32(c),
SkGetPackedG32(c), SkGetPackedB32(c));
break;
case kUnpremul_AlphaVerb:
c = SkUnPreMultiply::UnPreMultiplyPreservingByteOrder(c);
break;
}
return c;
}
template <bool doSwapRB, AlphaVerb doAlpha>
void convert32_row(uint32_t* dst, const uint32_t* src, int count) {
// This has to be correct if src == dst (but not partial overlap)
for (int i = 0; i < count; ++i) {
dst[i] = convert32<doSwapRB, doAlpha>(src[i]);
}
}
static bool is_32bit_colortype(SkColorType ct) {
return kRGBA_8888_SkColorType == ct || kBGRA_8888_SkColorType == ct;
}
static AlphaVerb compute_AlphaVerb(SkAlphaType src, SkAlphaType dst) {
SkASSERT(kUnknown_SkAlphaType != src);
SkASSERT(kUnknown_SkAlphaType != dst);
if (kOpaque_SkAlphaType == src || kOpaque_SkAlphaType == dst || src == dst) {
return kNothing_AlphaVerb;
}
if (kPremul_SkAlphaType == dst) {
SkASSERT(kUnpremul_SkAlphaType == src);
return kPremul_AlphaVerb;
} else {
SkASSERT(kPremul_SkAlphaType == src);
SkASSERT(kUnpremul_SkAlphaType == dst);
return kUnpremul_AlphaVerb;
}
}
bool SkSrcPixelInfo::convertPixelsTo(SkDstPixelInfo* dst, int width, int height) const {
SkASSERT(width > 0 && height > 0);
if (!is_32bit_colortype(fColorType) || !is_32bit_colortype(dst->fColorType)) {
return false;
}
void (*proc)(uint32_t* dst, const uint32_t* src, int count);
AlphaVerb doAlpha = compute_AlphaVerb(fAlphaType, dst->fAlphaType);
bool doSwapRB = fColorType != dst->fColorType;
switch (doAlpha) {
case kNothing_AlphaVerb:
SkASSERT(doSwapRB);
proc = convert32_row<true, kNothing_AlphaVerb>;
break;
case kPremul_AlphaVerb:
if (doSwapRB) {
proc = convert32_row<true, kPremul_AlphaVerb>;
} else {
proc = convert32_row<false, kPremul_AlphaVerb>;
}
break;
case kUnpremul_AlphaVerb:
if (doSwapRB) {
proc = convert32_row<true, kUnpremul_AlphaVerb>;
} else {
proc = convert32_row<false, kUnpremul_AlphaVerb>;
}
break;
}
uint32_t* dstP = static_cast<uint32_t*>(dst->fPixels);
const uint32_t* srcP = static_cast<const uint32_t*>(fPixels);
size_t srcInc = fRowBytes >> 2;
size_t dstInc = dst->fRowBytes >> 2;
for (int y = 0; y < height; ++y) {
proc(dstP, srcP, width);
dstP += dstInc;
srcP += srcInc;
}
return true;
}
static bool extract_alpha(void* dst, size_t dstRB, const void* src, size_t srcRB, static bool extract_alpha(void* dst, size_t dstRB, const void* src, size_t srcRB,
const SkImageInfo& srcInfo, SkColorTable* ctable) { const SkImageInfo& srcInfo, SkColorTable* ctable) {
uint8_t* SK_RESTRICT dst8 = (uint8_t*)dst; uint8_t* SK_RESTRICT dst8 = (uint8_t*)dst;
@ -358,21 +305,10 @@ bool SkPixelInfo::CopyPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t
const bool isColorAware = dstInfo.colorSpace(); const bool isColorAware = dstInfo.colorSpace();
SkASSERT(srcInfo.colorSpace() || !isColorAware); SkASSERT(srcInfo.colorSpace() || !isColorAware);
// Handle fancy alpha swizzling if both are ARGB32 // Fast Path 2: Simple swizzles and premuls.
if (4 == srcInfo.bytesPerPixel() && 4 == dstInfo.bytesPerPixel() && !isColorAware) { if (4 == srcInfo.bytesPerPixel() && 4 == dstInfo.bytesPerPixel() && !isColorAware) {
SkDstPixelInfo dstPI; swizzle_and_multiply(dstInfo, dstPixels, dstRB, srcInfo, srcPixels, srcRB);
dstPI.fColorType = dstInfo.colorType(); return true;
dstPI.fAlphaType = dstInfo.alphaType();
dstPI.fPixels = dstPixels;
dstPI.fRowBytes = dstRB;
SkSrcPixelInfo srcPI;
srcPI.fColorType = srcInfo.colorType();
srcPI.fAlphaType = srcInfo.alphaType();
srcPI.fPixels = srcPixels;
srcPI.fRowBytes = srcRB;
return srcPI.convertPixelsTo(&dstPI, width, height);
} }
if (isColorAware && optimized_color_xform(dstInfo, srcInfo)) { if (isColorAware && optimized_color_xform(dstInfo, srcInfo)) {

View File

@ -23,18 +23,6 @@ struct SkPixelInfo {
SkColorTable* srcCTable = nullptr); SkColorTable* srcCTable = nullptr);
}; };
struct SkDstPixelInfo : SkPixelInfo {
void* fPixels;
};
struct SkSrcPixelInfo : SkPixelInfo {
const void* fPixels;
// Guaranteed to work even if src.fPixels and dst.fPixels are the same
// (but not if they overlap partially)
bool convertPixelsTo(SkDstPixelInfo* dst, int width, int height) const;
};
static inline void SkRectMemcpy(void* dst, size_t dstRB, const void* src, size_t srcRB, static inline void SkRectMemcpy(void* dst, size_t dstRB, const void* src, size_t srcRB,
size_t bytesPerRow, int rowCount) { size_t bytesPerRow, int rowCount) {
SkASSERT(bytesPerRow <= dstRB); SkASSERT(bytesPerRow <= dstRB);

View File

@ -0,0 +1,43 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkUnPreMultiplyPriv_DEFINED
#define SkUnPreMultiplyPriv_DEFINED
#include "SkColor.h"
template <bool kSwapRB>
void SkUnpremultiplyRow(uint32_t* dst, const uint32_t* src, int count) {
const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
for (int i = 0; i < count; i++) {
uint32_t c = *src++;
uint8_t r, g, b, a;
if (kSwapRB) {
r = (c >> 16) & 0xFF;
g = (c >> 8) & 0xFF;
b = (c >> 0) & 0xFF;
a = (c >> 24) & 0xFF;
} else {
r = (c >> 0) & 0xFF;
g = (c >> 8) & 0xFF;
b = (c >> 16) & 0xFF;
a = (c >> 24) & 0xFF;
}
if (0 != a && 255 != a) {
SkUnPreMultiply::Scale scale = table[a];
r = SkUnPreMultiply::ApplyScale(scale, r);
g = SkUnPreMultiply::ApplyScale(scale, g);
b = SkUnPreMultiply::ApplyScale(scale, b);
}
*dst++ = (r << 0) | (g << 8) | (b << 16) | (a << 24);
}
}
#endif

View File

@ -21,6 +21,7 @@
#include "SkConfig8888.h" #include "SkConfig8888.h"
#include "SkGrPriv.h" #include "SkGrPriv.h"
#include "SkUnPreMultiplyPriv.h"
#include "effects/GrConfigConversionEffect.h" #include "effects/GrConfigConversionEffect.h"
#include "text/GrTextBlobCache.h" #include "text/GrTextBlobCache.h"
@ -226,21 +227,20 @@ void GrContext::flush() {
bool sw_convert_to_premul(GrPixelConfig srcConfig, int width, int height, size_t inRowBytes, bool sw_convert_to_premul(GrPixelConfig srcConfig, int width, int height, size_t inRowBytes,
const void* inPixels, size_t outRowBytes, void* outPixels) { const void* inPixels, size_t outRowBytes, void* outPixels) {
SkSrcPixelInfo srcPI; SkColorType colorType;
if (!GrPixelConfigToColorType(srcConfig, &srcPI.fColorType)) { if (!GrPixelConfigToColorType(srcConfig, &colorType) ||
4 != SkColorTypeBytesPerPixel(colorType))
{
return false; return false;
} }
srcPI.fAlphaType = kUnpremul_SkAlphaType;
srcPI.fPixels = inPixels;
srcPI.fRowBytes = inRowBytes;
SkDstPixelInfo dstPI; for (int y = 0; y < height; y++) {
dstPI.fColorType = srcPI.fColorType; SkOpts::RGBA_to_rgbA((uint32_t*) outPixels, inPixels, width);
dstPI.fAlphaType = kPremul_SkAlphaType; outPixels = SkTAddOffset<void>(outPixels, outRowBytes);
dstPI.fPixels = outPixels; inPixels = SkTAddOffset<const void>(inPixels, inRowBytes);
dstPI.fRowBytes = outRowBytes; }
return srcPI.convertPixelsTo(&dstPI, width, height); return true;
} }
bool GrContext::writeSurfacePixels(GrSurface* surface, SkColorSpace* dstColorSpace, bool GrContext::writeSurfacePixels(GrSurface* surface, SkColorSpace* dstColorSpace,
@ -522,21 +522,17 @@ bool GrContext::readSurfacePixels(GrSurface* src, SkColorSpace* srcColorSpace,
// Perform umpremul conversion if we weren't able to perform it as a draw. // Perform umpremul conversion if we weren't able to perform it as a draw.
if (unpremul) { if (unpremul) {
SkDstPixelInfo dstPI; SkColorType colorType;
if (!GrPixelConfigToColorType(dstConfig, &dstPI.fColorType)) { if (!GrPixelConfigToColorType(dstConfig, &colorType) ||
4 != SkColorTypeBytesPerPixel(colorType))
{
return false; return false;
} }
dstPI.fAlphaType = kUnpremul_SkAlphaType;
dstPI.fPixels = buffer;
dstPI.fRowBytes = rowBytes;
SkSrcPixelInfo srcPI; for (int y = 0; y < height; y++) {
srcPI.fColorType = dstPI.fColorType; SkUnpremultiplyRow<false>((uint32_t*) buffer, (const uint32_t*) buffer, width);
srcPI.fAlphaType = kPremul_SkAlphaType; buffer = SkTAddOffset<void>(buffer, rowBytes);
srcPI.fPixels = buffer; }
srcPI.fRowBytes = rowBytes;
return srcPI.convertPixelsTo(&dstPI, width, height);
} }
return true; return true;
} }

View File

@ -15,6 +15,7 @@
#include "SkPreConfig.h" #include "SkPreConfig.h"
#include "SkRasterPipeline.h" #include "SkRasterPipeline.h"
#include "SkUnPreMultiply.h" #include "SkUnPreMultiply.h"
#include "SkUnPreMultiplyPriv.h"
/** /**
* Function template for transforming scanlines. * Function template for transforming scanlines.
@ -128,46 +129,12 @@ static inline void transform_scanline_444(char* SK_RESTRICT dst, const char* SK_
} }
} }
template <bool kIsRGBA>
static inline void transform_scanline_unpremultiply(char* SK_RESTRICT dst,
const char* SK_RESTRICT src, int width) {
const uint32_t* srcP = (const SkPMColor*)src;
const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
for (int i = 0; i < width; i++) {
uint32_t c = *srcP++;
unsigned r, g, b, a;
if (kIsRGBA) {
r = (c >> 0) & 0xFF;
g = (c >> 8) & 0xFF;
b = (c >> 16) & 0xFF;
a = (c >> 24) & 0xFF;
} else {
r = (c >> 16) & 0xFF;
g = (c >> 8) & 0xFF;
b = (c >> 0) & 0xFF;
a = (c >> 24) & 0xFF;
}
if (0 != a && 255 != a) {
SkUnPreMultiply::Scale scale = table[a];
r = SkUnPreMultiply::ApplyScale(scale, r);
g = SkUnPreMultiply::ApplyScale(scale, g);
b = SkUnPreMultiply::ApplyScale(scale, b);
}
*dst++ = r;
*dst++ = g;
*dst++ = b;
*dst++ = a;
}
}
/** /**
* Transform from legacy kPremul, kRGBA_8888_SkColorType to 4-bytes-per-pixel unpremultiplied RGBA. * Transform from legacy kPremul, kRGBA_8888_SkColorType to 4-bytes-per-pixel unpremultiplied RGBA.
*/ */
static inline void transform_scanline_rgbA(char* SK_RESTRICT dst, const char* SK_RESTRICT src, static inline void transform_scanline_rgbA(char* SK_RESTRICT dst, const char* SK_RESTRICT src,
int width, int, const SkPMColor*) { int width, int, const SkPMColor*) {
transform_scanline_unpremultiply<true>(dst, src, width); SkUnpremultiplyRow<false>((uint32_t*) dst, (const uint32_t*) src, width);
} }
/** /**
@ -175,7 +142,7 @@ static inline void transform_scanline_rgbA(char* SK_RESTRICT dst, const char* SK
*/ */
static inline void transform_scanline_bgrA(char* SK_RESTRICT dst, const char* SK_RESTRICT src, static inline void transform_scanline_bgrA(char* SK_RESTRICT dst, const char* SK_RESTRICT src,
int width, int, const SkPMColor*) { int width, int, const SkPMColor*) {
transform_scanline_unpremultiply<false>(dst, src, width); SkUnpremultiplyRow<true>((uint32_t*) dst, (const uint32_t*) src, width);
} }
template <bool kIsRGBA> template <bool kIsRGBA>