Add function to GrDataUtils to handle color conversions.

Like SkConvertPixels but knows about all GrColorTypes, origin, and can
apply an arbitrary GrSwizzle.

Use in GrSurfaceContext read/write pixels methods.

Add support for '0' to GrSwizzle.


Change-Id: Ib9dd215fcb0ee8b33c4020893c22b4ab7ce1f40b
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/220761
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
This commit is contained in:
Brian Salomon 2019-06-17 14:08:58 -04:00 committed by Skia Commit-Bot
parent b97824d4d1
commit 5509102043
14 changed files with 431 additions and 209 deletions

View File

@ -348,3 +348,9 @@ bool GrCaps::validateSurfaceDesc(const GrSurfaceDesc& desc, GrMipMapped mipped)
GrBackendFormat GrCaps::getBackendFormatFromColorType(SkColorType ct) const {
return this->getBackendFormatFromGrColorType(SkColorTypeToGrColorType(ct), GrSRGBEncoded::kNo);
}
GrCaps::SupportedRead GrCaps::supportedReadPixelsColorType(GrPixelConfig config,
const GrBackendFormat&,
GrColorType dstColorType) const {
return SupportedRead{GrSwizzle::RGBA(), GrPixelConfigToColorType(config)};
}

View File

@ -205,18 +205,26 @@ public:
* the data into in order to use GrGpu::writePixels().
*/
virtual GrColorType supportedWritePixelsColorType(GrPixelConfig config,
GrColorType /*srcColorType*/) const {
GrColorType srcColorType) const {
return GrPixelConfigToColorType(config);
}
struct SupportedRead {
GrSwizzle fSwizzle;
GrColorType fColorType;
};
/**
* Given a src pixel config and a dst color type what color type must the caller read to using
* GrGpu::readPixels() and then coax into dstColorType.
* Given a src surface's pixel config and its backend format as well as a color type the caller
* would like read into, this provides a legal color type that the caller may pass to
* GrGpu::readPixels(). The returned color type may differ from the passed dstColorType, in
* which case the caller must convert the read pixel data (see GrConvertPixels). When converting
* to dstColorType the swizzle in the returned struct should be applied. The caller must check
* the returned color type for kUnknown.
*/
virtual GrColorType supportedReadPixelsColorType(GrPixelConfig config,
GrColorType /*dstColorType*/) const {
return GrPixelConfigToColorType(config);
}
virtual SupportedRead supportedReadPixelsColorType(GrPixelConfig srcConfig,
const GrBackendFormat& srcFormat,
GrColorType dstColorType) const;
/** Are transfer buffers (to textures and from surfaces) supported? */
bool transferBufferSupport() const { return fTransferBufferSupport; }

View File

@ -6,8 +6,9 @@
*/
#include "src/gpu/GrDataUtils.h"
#include "include/private/GrColor.h"
#include "src/core/SkColorSpaceXformSteps.h"
#include "src/core/SkTLazy.h"
#include "src/core/SkUtils.h"
struct ETC1Block {
@ -359,3 +360,190 @@ void GrFillInData(GrCompression compression, GrPixelConfig config,
}
}
static GrSwizzle get_load_and_get_swizzle(GrColorType ct, SkRasterPipeline::StockStage* load,
bool* isNormalized) {
GrSwizzle swizzle("rgba");
*isNormalized = true;
switch (ct) {
case GrColorType::kAlpha_8: *load = SkRasterPipeline::load_a8; break;
case GrColorType::kRGB_565: *load = SkRasterPipeline::load_565; break;
case GrColorType::kABGR_4444: *load = SkRasterPipeline::load_4444; break;
case GrColorType::kRGBA_8888: *load = SkRasterPipeline::load_8888; break;
case GrColorType::kRG_88: *load = SkRasterPipeline::load_rg88; break;
case GrColorType::kRGBA_1010102: *load = SkRasterPipeline::load_1010102; break;
case GrColorType::kAlpha_F16: *load = SkRasterPipeline::load_af16; break;
case GrColorType::kRGBA_F16_Clamped: *load = SkRasterPipeline::load_f16; break;
case GrColorType::kRG_1616: *load = SkRasterPipeline::load_rg1616; break;
case GrColorType::kRGBA_F16: *load = SkRasterPipeline::load_f16;
*isNormalized = false;
break;
case GrColorType::kRG_F32: *load = SkRasterPipeline::load_rgf32;
*isNormalized = false;
break;
case GrColorType::kRGBA_F32: *load = SkRasterPipeline::load_f32;
*isNormalized = false;
break;
case GrColorType::kR_16: *load = SkRasterPipeline::load_a16;
swizzle = GrSwizzle("a001");
break;
case GrColorType::kGray_8: *load = SkRasterPipeline::load_a8;
swizzle = GrSwizzle("aaa1");
break;
case GrColorType::kBGRA_8888: *load = SkRasterPipeline::load_8888;
swizzle = GrSwizzle("bgra");
break;
case GrColorType::kRGB_888x: *load = SkRasterPipeline::load_8888;
swizzle = GrSwizzle("rgb1");
break;
case GrColorType::kUnknown:
case GrColorType::kRGB_ETC1:
SK_ABORT("unexpected CT");
}
return swizzle;
}
static GrSwizzle get_dst_swizzle_and_store(GrColorType ct, SkRasterPipeline::StockStage* store,
bool* isNormalized) {
GrSwizzle swizzle("rgba");
*isNormalized = true;
switch (ct) {
case GrColorType::kAlpha_8: *store = SkRasterPipeline::store_a8; break;
case GrColorType::kRGB_565: *store = SkRasterPipeline::store_565; break;
case GrColorType::kABGR_4444: *store = SkRasterPipeline::store_4444; break;
case GrColorType::kRGBA_8888: *store = SkRasterPipeline::store_8888; break;
case GrColorType::kRG_88: *store = SkRasterPipeline::store_rg88; break;
case GrColorType::kRGBA_1010102: *store = SkRasterPipeline::store_1010102; break;
case GrColorType::kRGBA_F16_Clamped: *store = SkRasterPipeline::store_f16; break;
case GrColorType::kRG_1616: *store = SkRasterPipeline::store_rg1616; break;
case GrColorType::kAlpha_F16: *store = SkRasterPipeline::store_af16;
*isNormalized = false;
break;
case GrColorType::kRGBA_F16: *store = SkRasterPipeline::store_f16;
*isNormalized = false;
break;
case GrColorType::kRG_F32: *store = SkRasterPipeline::store_rgf32;
*isNormalized = false;
break;
case GrColorType::kRGBA_F32: *store = SkRasterPipeline::store_f32;
*isNormalized = false;
break;
case GrColorType::kR_16: swizzle = GrSwizzle("000r");
*store = SkRasterPipeline::store_a16;
break;
case GrColorType::kBGRA_8888: swizzle = GrSwizzle("bgra");
*store = SkRasterPipeline::store_8888;
break;
case GrColorType::kRGB_888x: swizzle = GrSwizzle("rgb1");
*store = SkRasterPipeline::store_8888;
break;
case GrColorType::kGray_8: // not currently supported as output
case GrColorType::kUnknown:
case GrColorType::kRGB_ETC1:
SK_ABORT("unexpected CT");
}
return swizzle;
}
static inline void append_clamp_gamut(SkRasterPipeline* pipeline) {
// SkRasterPipeline may not know our color type and also doesn't like caller to directly
// append clamp_gamut. Fake it out.
static SkImageInfo fakeII = SkImageInfo::MakeN32Premul(1, 1);
pipeline->append_gamut_clamp_if_normalized(fakeII);
}
bool GrConvertPixels(const GrPixelInfo& dstInfo, void* dst, const GrPixelInfo& srcInfo,
const void* src, GrSwizzle swizzle) {
if (dstInfo.fWidth != srcInfo.fWidth || srcInfo.fHeight != dstInfo.fHeight) {
return false;
}
if (dstInfo.fWidth <= 0 || dstInfo.fHeight <= 0) {
return false;
}
if (GrColorTypeComponentFlags(dstInfo.fColorInfo.fColorType) & kGray_SkColorTypeComponentFlag) {
// We don't currently support conversion to Gray.
return false;
}
size_t srcBpp = GrColorTypeBytesPerPixel(srcInfo.fColorInfo.fColorType);
size_t dstBpp = GrColorTypeBytesPerPixel(dstInfo.fColorInfo.fColorType);
if (!srcBpp || !dstBpp) {
// Either src or dst is compressed or kUnknown.
return false;
}
// SkRasterPipeline operates on row-pixels not row-bytes.
SkASSERT(dstInfo.fRowBytes % dstBpp == 0);
SkASSERT(srcInfo.fRowBytes % srcBpp == 0);
SkRasterPipeline::StockStage load;
bool srcIsNormalized;
auto loadSwizzle =
get_load_and_get_swizzle(srcInfo.fColorInfo.fColorType, &load, &srcIsNormalized);
loadSwizzle = GrSwizzle::Concat(loadSwizzle, swizzle);
SkRasterPipeline::StockStage store;
bool dstIsNormalized;
auto storeSwizzle =
get_dst_swizzle_and_store(dstInfo.fColorInfo.fColorType, &store, &dstIsNormalized);
bool alphaOrCSConversion =
(srcInfo.fColorInfo.fAlphaType != dstInfo.fColorInfo.fAlphaType &&
srcInfo.fColorInfo.fAlphaType != kOpaque_SkAlphaType) ||
!SkColorSpace::Equals(srcInfo.fColorInfo.fColorSpace, dstInfo.fColorInfo.fColorSpace);
bool clampGamut;
SkTLazy<SkColorSpaceXformSteps> steps;
GrSwizzle loadStoreSwizzle;
if (alphaOrCSConversion) {
steps.init(srcInfo.fColorInfo.fColorSpace, srcInfo.fColorInfo.fAlphaType,
dstInfo.fColorInfo.fColorSpace, dstInfo.fColorInfo.fAlphaType);
clampGamut = dstIsNormalized && dstInfo.fColorInfo.fAlphaType == kPremul_SkAlphaType;
} else {
clampGamut = dstIsNormalized && !srcIsNormalized &&
dstInfo.fColorInfo.fAlphaType == kPremul_SkAlphaType;
if (!clampGamut) {
loadStoreSwizzle = GrSwizzle::Concat(loadSwizzle, storeSwizzle);
}
}
int cnt = 1;
int height = srcInfo.fHeight;
SkRasterPipeline_MemoryCtx srcCtx{const_cast<void*>(src), SkToInt(srcInfo.fRowBytes / srcBpp)},
dstCtx{ dst , SkToInt(dstInfo.fRowBytes / dstBpp)};
if (srcInfo.fOrigin != dstInfo.fOrigin) {
// It *almost* works to point the src at the last row and negate the stride and run the
// whole rectangle. However, SkRasterPipeline::run()'s control loop uses size_t loop
// variables so it winds up relying on unsigned overflow math. It works out in practice
// but UBSAN says "no!" as it's technically undefined and in theory a compiler could emit
// code that didn't do what is intended. So we go one row at a time. :(
srcCtx.pixels = static_cast<char*>(srcCtx.pixels) + srcInfo.fRowBytes * (height - 1);
std::swap(cnt, height);
}
for (int i = 0; i < cnt; ++i) {
SkRasterPipeline_<256> pipeline;
pipeline.append(load, &srcCtx);
if (alphaOrCSConversion) {
loadSwizzle.apply(&pipeline);
steps->apply(&pipeline, srcIsNormalized);
if (clampGamut) {
append_clamp_gamut(&pipeline);
}
storeSwizzle.apply(&pipeline);
} else {
if (clampGamut) {
loadSwizzle.apply(&pipeline);
append_clamp_gamut(&pipeline);
storeSwizzle.apply(&pipeline);
} else {
loadStoreSwizzle.apply(&pipeline);
}
}
pipeline.append(store, &dstCtx);
pipeline.run(0, 0, srcInfo.fWidth, height);
srcCtx.pixels = static_cast<char*>(srcCtx.pixels) - srcInfo.fRowBytes;
dstCtx.pixels = static_cast<char*>(dstCtx.pixels) + dstInfo.fRowBytes;
}
return true;
}

View File

@ -10,6 +10,7 @@
#include "include/core/SkColor.h"
#include "include/private/GrTypesPriv.h"
#include "src/gpu/GrSwizzle.h"
// TODO: consolidate all the backend-specific flavors of this method to this
size_t GrETC1CompressedDataSize(int w, int h);
@ -33,4 +34,22 @@ void GrFillInData(GrCompression, GrPixelConfig,
const SkTArray<size_t>& individualMipOffsets,
char* dest, const SkColor4f& color);
struct GrColorInfo {
GrColorType fColorType = GrColorType::kUnknown;
SkColorSpace* fColorSpace = nullptr;
SkAlphaType fAlphaType = kPremul_SkAlphaType;
};
struct GrPixelInfo {
GrColorInfo fColorInfo = {};
GrSurfaceOrigin fOrigin = kTopLeft_GrSurfaceOrigin;
int fWidth = 0;
int fHeight = 0;
size_t fRowBytes = 0;
};
// Swizzle param is applied after loading and before converting from srcInfo to dstInfo.
bool GrConvertPixels(const GrPixelInfo& dstInfo, void* dst, const GrPixelInfo& srcInfo,
const void* src, GrSwizzle swizzle = GrSwizzle{});
#endif

View File

@ -5,6 +5,7 @@
* found in the LICENSE file.
*/
#include "src/gpu/GrRenderTargetContext.h"
#include "include/core/SkDrawable.h"
#include "include/gpu/GrBackendSemaphore.h"
#include "include/gpu/GrRenderTarget.h"
@ -26,13 +27,13 @@
#include "src/gpu/GrBlurUtils.h"
#include "src/gpu/GrCaps.h"
#include "src/gpu/GrContextPriv.h"
#include "src/gpu/GrDataUtils.h"
#include "src/gpu/GrDrawingManager.h"
#include "src/gpu/GrFixedClip.h"
#include "src/gpu/GrGpuResourcePriv.h"
#include "src/gpu/GrMemoryPool.h"
#include "src/gpu/GrPathRenderer.h"
#include "src/gpu/GrRecordingContextPriv.h"
#include "src/gpu/GrRenderTargetContext.h"
#include "src/gpu/GrRenderTargetContextPriv.h"
#include "src/gpu/GrResourceProvider.h"
#include "src/gpu/GrStencilAttachment.h"
@ -1960,19 +1961,21 @@ void GrRenderTargetContext::asyncRescaleAndReadPixels(
auto dstCT = SkColorTypeToGrColorType(info.colorType());
bool needsRescale = srcRect.width() != info.width() || srcRect.height() != info.height();
GrPixelConfig configOfFinalContext = fRenderTargetProxy->config();
auto backendFormatOfFinalContext = fRenderTargetProxy->backendFormat();
if (needsRescale) {
auto backendFormat = this->caps()->getBackendFormatFromColorType(info.colorType());
configOfFinalContext =
this->caps()->getConfigFromBackendFormat(backendFormat, info.colorType());
backendFormatOfFinalContext = this->caps()->getBackendFormatFromColorType(info.colorType());
configOfFinalContext = this->caps()->getConfigFromBackendFormat(backendFormatOfFinalContext,
info.colorType());
}
auto readCT = this->caps()->supportedReadPixelsColorType(configOfFinalContext, dstCT);
// Fail if we can't do a CPU conversion from readCT to dstCT.
if (GrColorTypeToSkColorType(readCT) == kUnknown_SkColorType) {
auto readInfo = this->caps()->supportedReadPixelsColorType(configOfFinalContext,
backendFormatOfFinalContext, dstCT);
// Fail if we can't read from the source surface's color type.
if (readInfo.fColorType == GrColorType::kUnknown) {
callback(context, nullptr, 0);
return;
}
// Fail if readCT does not have all of readCT's color channels.
if (GrColorTypeComponentFlags(dstCT) & ~GrColorTypeComponentFlags(readCT)) {
if (GrColorTypeComponentFlags(dstCT) & ~GrColorTypeComponentFlags(readInfo.fColorType)) {
callback(context, nullptr, 0);
return;
}
@ -2042,42 +2045,55 @@ GrRenderTargetContext::PixelTransferResult GrRenderTargetContext::transferPixels
if (fRenderTargetProxy->wrapsVkSecondaryCB()) {
return {};
}
auto readCT = this->caps()->supportedReadPixelsColorType(fRenderTargetProxy->config(), dstCT);
// Fail if we can't do a CPU conversion from readCT to dstCT.
if (readCT != dstCT && (GrColorTypeToSkColorType(readCT) == kUnknown_SkColorType ||
GrColorTypeToSkColorType(dstCT) == kUnknown_SkColorType)) {
return {};
}
auto supportedRead = this->caps()->supportedReadPixelsColorType(
fRenderTargetProxy->config(), fRenderTargetProxy->backendFormat(), dstCT);
// Fail if readCT does not have all of readCT's color channels.
if (GrColorTypeComponentFlags(dstCT) & ~GrColorTypeComponentFlags(readCT)) {
if (GrColorTypeComponentFlags(dstCT) & ~GrColorTypeComponentFlags(supportedRead.fColorType)) {
return {};
}
if (!this->caps()->transferBufferSupport() ||
!this->caps()->transferFromOffsetAlignment(readCT)) {
!this->caps()->transferFromOffsetAlignment(supportedRead.fColorType)) {
return {};
}
size_t rowBytes = GrColorTypeBytesPerPixel(readCT) * rect.width();
size_t rowBytes = GrColorTypeBytesPerPixel(supportedRead.fColorType) * rect.width();
size_t size = rowBytes * rect.height();
auto buffer = direct->priv().resourceProvider()->createBuffer(
size, GrGpuBufferType::kXferGpuToCpu, GrAccessPattern::kStream_GrAccessPattern);
if (!buffer) {
return {};
}
this->getRTOpList()->addOp(GrTransferFromOp::Make(fContext, rect, readCT, buffer, 0),
*this->caps());
auto srcRect = rect;
bool flip = this->origin() == kBottomLeft_GrSurfaceOrigin;
if (flip) {
srcRect = SkIRect::MakeLTRB(rect.fLeft, this->height() - rect.fBottom, rect.fRight,
this->height() - rect.fTop);
}
auto op = GrTransferFromOp::Make(fContext, srcRect, supportedRead.fColorType, buffer, 0);
this->getRTOpList()->addOp(std::move(op), *this->caps());
PixelTransferResult result;
result.fTransferBuffer = std::move(buffer);
if (readCT != dstCT) {
result.fPixelConverter =
[w = rect.width(), h = rect.height(), dstCT = GrColorTypeToSkColorType(dstCT),
srcCT = GrColorTypeToSkColorType(readCT)](void* dst, const void* src) {
auto dstII = SkImageInfo::Make(w, h, dstCT, kPremul_SkAlphaType, nullptr);
auto srcII = SkImageInfo::Make(w, h, srcCT, kPremul_SkAlphaType, nullptr);
SkConvertPixels(dstII, dst, w * SkColorTypeBytesPerPixel(dstCT), srcII, src,
w * SkColorTypeBytesPerPixel(srcCT));
};
if (supportedRead.fColorType != dstCT || supportedRead.fSwizzle != GrSwizzle("rgba") || flip) {
result.fPixelConverter = [w = rect.width(), h = rect.height(), dstCT, supportedRead](
void* dst, const void* src) {
GrPixelInfo srcInfo;
srcInfo.fColorInfo.fAlphaType = kPremul_SkAlphaType;
srcInfo.fColorInfo.fColorType = supportedRead.fColorType;
srcInfo.fColorInfo.fColorSpace = nullptr;
srcInfo.fRowBytes = GrColorTypeBytesPerPixel(supportedRead.fColorType) * w;
GrPixelInfo dstInfo;
dstInfo.fColorInfo.fAlphaType = kPremul_SkAlphaType;
dstInfo.fColorInfo.fColorType = dstCT;
dstInfo.fColorInfo.fColorSpace = nullptr;
dstInfo.fRowBytes = GrColorTypeBytesPerPixel(dstCT) * w;
srcInfo.fWidth = dstInfo.fWidth = w;
srcInfo.fHeight = dstInfo.fHeight = h;
GrConvertPixels(dstInfo, dst, srcInfo, src, supportedRead.fSwizzle);
};
}
return result;
}
@ -2209,26 +2225,16 @@ void GrRenderTargetContext::asyncRescaleAndReadPixelsYUV420(
callback(context, nullptr, nullptr);
return;
}
GrPixelConfig planeConfig = kAlpha_8_GrPixelConfig;
GrColorType planeColorType = GrColorType::kAlpha_8;
if (this->caps()->supportedReadPixelsColorType(planeConfig, planeColorType) !=
GrColorType::kAlpha_8) {
// TODO: Because there are issues with reading back/transferring A8 textures on GL, we are
// currently using RGBA textures for the planes. Fix this once the A8 read back/transfer
// issues are addressed.
planeConfig = kRGBA_8888_GrPixelConfig;
planeColorType = GrColorType::kRGBA_8888;
}
const auto backendFormat = this->caps()->getBackendFormatFromGrColorType(
planeColorType, GrSRGBEncoded::kNo);
const auto backendFormat = this->caps()->getBackendFormatFromGrColorType(GrColorType::kAlpha_8,
GrSRGBEncoded::kNo);
auto yRTC = direct->priv().makeDeferredRenderTargetContext(
backendFormat, SkBackingFit::kApprox, dstW, dstH, planeConfig, dstColorSpace,
backendFormat, SkBackingFit::kApprox, dstW, dstH, kAlpha_8_GrPixelConfig, dstColorSpace,
1, GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
auto uRTC = direct->priv().makeDeferredRenderTargetContext(
backendFormat, SkBackingFit::kApprox, dstW / 2, dstH / 2, planeConfig,
backendFormat, SkBackingFit::kApprox, dstW / 2, dstH / 2, kAlpha_8_GrPixelConfig,
dstColorSpace, 1, GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
auto vRTC = direct->priv().makeDeferredRenderTargetContext(
backendFormat, SkBackingFit::kApprox, dstW / 2, dstH / 2, planeConfig,
backendFormat, SkBackingFit::kApprox, dstW / 2, dstH / 2, kAlpha_8_GrPixelConfig,
dstColorSpace, 1, GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
if (!yRTC || !uRTC || !vRTC) {
callback(context, nullptr, nullptr);

View File

@ -13,6 +13,7 @@
#include "src/core/SkAutoPixmapStorage.h"
#include "src/gpu/GrClip.h"
#include "src/gpu/GrContextPriv.h"
#include "src/gpu/GrDataUtils.h"
#include "src/gpu/GrDrawingManager.h"
#include "src/gpu/GrGpu.h"
#include "src/gpu/GrRecordingContextPriv.h"
@ -224,71 +225,52 @@ bool GrSurfaceContext::readPixelsImpl(GrContext* direct, int left, int top, int
buffer, rowBytes, flags);
}
bool convert = unpremul || needColorConversion;
bool flip = srcProxy->origin() == kBottomLeft_GrSurfaceOrigin;
if (flip) {
top = srcSurface->height() - top - height;
}
auto supportedRead = caps->supportedReadPixelsColorType(
srcProxy->config(), srcProxy->backendFormat(), dstColorType);
GrColorType allowedColorType = caps->supportedReadPixelsColorType(srcProxy->config(),
dstColorType);
convert = convert || (dstColorType != allowedColorType);
bool convert = unpremul || needColorConversion || flip ||
(dstColorType != supportedRead.fColorType) ||
supportedRead.fSwizzle != GrSwizzle::RGBA();
SkAutoPixmapStorage tempPixmap;
SkPixmap finalPixmap;
std::unique_ptr<char[]> tmpPixels;
GrPixelInfo tmpInfo;
GrPixelInfo dstInfo;
void* readDst = buffer;
if (convert) {
SkColorType srcSkColorType = GrColorTypeToSkColorType(allowedColorType);
SkColorType dstSkColorType = GrColorTypeToSkColorType(dstColorType);
bool srcAlwaysOpaque = SkColorTypeIsAlwaysOpaque(srcSkColorType);
bool dstAlwaysOpaque = SkColorTypeIsAlwaysOpaque(dstSkColorType);
if (kUnknown_SkColorType == srcSkColorType || kUnknown_SkColorType == dstSkColorType) {
return false;
}
auto tempAT = srcAlwaysOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
auto tempII = SkImageInfo::Make(width, height, srcSkColorType, tempAT,
this->colorSpaceInfo().refColorSpace());
SkASSERT(!unpremul || !dstAlwaysOpaque);
auto finalAT = (srcAlwaysOpaque || dstAlwaysOpaque)
? kOpaque_SkAlphaType
: unpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType;
auto finalII =
SkImageInfo::Make(width, height, dstSkColorType, finalAT, sk_ref_sp(dstColorSpace));
if (!SkImageInfoValidConversion(finalII, tempII)) {
return false;
}
if (!tempPixmap.tryAlloc(tempII)) {
return false;
}
finalPixmap.reset(finalII, buffer, rowBytes);
buffer = tempPixmap.writable_addr();
rowBytes = tempPixmap.rowBytes();
// Chrome msan bots require this.
sk_bzero(buffer, tempPixmap.computeByteSize());
tmpInfo.fColorInfo.fColorType = supportedRead.fColorType;
tmpInfo.fColorInfo.fAlphaType = kPremul_SkAlphaType;
tmpInfo.fColorInfo.fColorSpace = this->colorSpaceInfo().colorSpace();
tmpInfo.fOrigin = srcProxy->origin();
tmpInfo.fRowBytes = GrColorTypeBytesPerPixel(supportedRead.fColorType) * width;
dstInfo.fColorInfo.fColorType = dstColorType;
dstInfo.fColorInfo.fAlphaType = unpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType;
dstInfo.fColorInfo.fColorSpace = dstColorSpace;
dstInfo.fOrigin = kTopLeft_GrSurfaceOrigin;
dstInfo.fRowBytes = rowBytes;
dstInfo.fWidth = tmpInfo.fWidth = width;
dstInfo.fHeight = tmpInfo.fHeight = height;
size_t size = tmpInfo.fRowBytes * height;
tmpPixels.reset(new char[size]);
// Chrome MSAN bots require this.
sk_bzero(tmpPixels.get(), size);
readDst = tmpPixels.get();
rowBytes = tmpInfo.fRowBytes;
top = flip ? srcSurface->height() - top - height : top;
}
direct->priv().flushSurface(srcProxy);
if (!direct->priv().getGpu()->readPixels(srcSurface, left, top, width, height, allowedColorType,
buffer, rowBytes)) {
if (!direct->priv().getGpu()->readPixels(srcSurface, left, top, width, height,
supportedRead.fColorType, readDst, rowBytes)) {
return false;
}
if (flip) {
size_t trimRowBytes = GrColorTypeBytesPerPixel(allowedColorType) * width;
std::unique_ptr<char[]> row(new char[trimRowBytes]);
char* upper = reinterpret_cast<char*>(buffer);
char* lower = reinterpret_cast<char*>(buffer) + (height - 1) * rowBytes;
for (int y = 0; y < height / 2; ++y, upper += rowBytes, lower -= rowBytes) {
memcpy(row.get(), upper, trimRowBytes);
memcpy(upper, lower, trimRowBytes);
memcpy(lower, row.get(), trimRowBytes);
}
}
if (convert) {
if (!tempPixmap.readPixels(finalPixmap)) {
return false;
}
return GrConvertPixels(dstInfo, buffer, tmpInfo, tmpPixels.get(), supportedRead.fSwizzle);
}
return true;
}
@ -383,8 +365,8 @@ bool GrSurfaceContext::writePixelsImpl(GrContext* direct, int left, int top, int
// data into it which requires the origins to match. If the final proxy is a render target
// we can use a draw instead which doesn't have this origin restriction. Thus for render
// targets we will use top left and otherwise we will make the origins match.
GrSurfaceOrigin tempOrigin = this->asRenderTargetContext() ? kTopLeft_GrSurfaceOrigin :
dstProxy->origin();
GrSurfaceOrigin tempOrigin =
this->asRenderTargetContext() ? kTopLeft_GrSurfaceOrigin : dstProxy->origin();
auto tempProxy = direct->priv().proxyProvider()->createProxy(
format, desc, tempOrigin, SkBackingFit::kApprox, SkBudgeted::kYes);
@ -438,63 +420,44 @@ bool GrSurfaceContext::writePixelsImpl(GrContext* direct, int left, int top, int
return true;
}
bool convert = premul || needColorConversion;
if (!valid_pixel_conversion(srcColorType, dstProxy->config(), premul)) {
return false;
}
GrColorType allowedColorType = caps->supportedWritePixelsColorType(dstProxy->config(),
srcColorType);
convert = convert || (srcColorType != allowedColorType);
bool convert = premul || needColorConversion || (srcColorType != allowedColorType) ||
dstProxy->origin() == kBottomLeft_GrSurfaceOrigin;
std::unique_ptr<char[]> tempBuffer;
std::unique_ptr<char[]> tmpPixels;
if (convert) {
auto srcSkColorType = GrColorTypeToSkColorType(srcColorType);
auto dstSkColorType = GrColorTypeToSkColorType(allowedColorType);
if (kUnknown_SkColorType == srcSkColorType || kUnknown_SkColorType == dstSkColorType) {
return false;
}
auto srcAlphaType = SkColorTypeIsAlwaysOpaque(srcSkColorType)
? kOpaque_SkAlphaType
: (premul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType);
SkPixmap src(SkImageInfo::Make(width, height, srcSkColorType, srcAlphaType,
sk_ref_sp(srcColorSpace)),
srcBuffer, srcRowBytes);
auto tempSrcII = SkImageInfo::Make(width, height, dstSkColorType, kPremul_SkAlphaType,
this->colorSpaceInfo().refColorSpace());
auto size = tempSrcII.computeMinByteSize();
if (!size) {
return false;
}
tempBuffer.reset(new char[size]);
SkPixmap tempSrc(tempSrcII, tempBuffer.get(), tempSrcII.minRowBytes());
if (!src.readPixels(tempSrc)) {
return false;
}
srcColorType = allowedColorType;
srcBuffer = tempSrc.addr();
srcRowBytes = tempSrc.rowBytes();
GrPixelInfo srcInfo;
srcInfo.fColorInfo.fAlphaType = (premul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType);
srcInfo.fColorInfo.fColorType = srcColorType;
srcInfo.fColorInfo.fColorSpace = srcColorSpace;
srcInfo.fRowBytes = srcRowBytes;
srcInfo.fOrigin = kTopLeft_GrSurfaceOrigin;
GrPixelInfo tmpInfo;
tmpInfo.fColorInfo.fAlphaType = kPremul_SkAlphaType;
tmpInfo.fColorInfo.fColorType = allowedColorType;
tmpInfo.fColorInfo.fColorSpace = this->colorSpaceInfo().colorSpace();
tmpInfo.fRowBytes = GrColorTypeBytesPerPixel(allowedColorType) * width;
tmpInfo.fOrigin = dstProxy->origin();
srcInfo.fWidth = tmpInfo.fWidth = width;
srcInfo.fHeight = tmpInfo.fHeight = height;
tmpPixels.reset(new char[tmpInfo.fRowBytes * height]);
GrConvertPixels(tmpInfo, tmpPixels.get(), srcInfo, srcBuffer);
srcColorType = tmpInfo.fColorInfo.fColorType;
srcBuffer = tmpPixels.get();
srcRowBytes = tmpInfo.fRowBytes;
if (dstProxy->origin() == kBottomLeft_GrSurfaceOrigin) {
std::unique_ptr<char[]> row(new char[srcRowBytes]);
for (int y = 0; y < height / 2; ++y) {
memcpy(row.get(), tempSrc.addr(0, y), srcRowBytes);
memcpy(tempSrc.writable_addr(0, y), tempSrc.addr(0, height - 1 - y), srcRowBytes);
memcpy(tempSrc.writable_addr(0, height - 1 - y), row.get(), srcRowBytes);
}
top = dstSurface->height() - top - height;
}
} else if (dstProxy->origin() == kBottomLeft_GrSurfaceOrigin) {
size_t trimRowBytes = GrColorTypeBytesPerPixel(srcColorType) * width;
tempBuffer.reset(new char[trimRowBytes * height]);
char* dst = reinterpret_cast<char*>(tempBuffer.get()) + trimRowBytes * (height - 1);
const char* src = reinterpret_cast<const char*>(srcBuffer);
for (int i = 0; i < height; ++i, src += srcRowBytes, dst -= trimRowBytes) {
memcpy(dst, src, trimRowBytes);
}
srcBuffer = tempBuffer.get();
srcRowBytes = trimRowBytes;
top = dstSurface->height() - top - height;
}
// On platforms that prefer flushes over VRAM use (i.e., ANGLE) we're better off forcing a

View File

@ -11,11 +11,17 @@
void GrSwizzle::apply(SkRasterPipeline* pipeline) const {
SkASSERT(pipeline);
switch (fKey) {
case GrSwizzle::RGBA().asKey():
case GrSwizzle("rgba").asKey():
return;
case GrSwizzle::BGRA().asKey():
case GrSwizzle("bgra").asKey():
pipeline->append(SkRasterPipeline::swap_rb);
return;
case GrSwizzle("aaa1").asKey():
pipeline->append(SkRasterPipeline::alpha_to_gray);
return;
case GrSwizzle("rgb1").asKey():
pipeline->append(SkRasterPipeline::force_opaque);
return;
default: {
GR_STATIC_ASSERT(sizeof(uintptr_t) >= 4 * sizeof(char));
// Rather than allocate the 4 control bytes on the heap somewhere, just jam them right

View File

@ -22,15 +22,18 @@ public:
constexpr GrSwizzle(const GrSwizzle&);
constexpr GrSwizzle& operator=(const GrSwizzle& that);
static constexpr GrSwizzle Concat(const GrSwizzle& a, const GrSwizzle& b);
/** Recreates a GrSwizzle from the output of asKey() */
constexpr void setFromKey(uint16_t key);
constexpr bool operator==(const GrSwizzle& that) const { return fKey == that.fKey; }
constexpr bool operator!=(const GrSwizzle& that) const { return !(*this == that); }
/** Compact representation of the swizzle suitable for a key. */
constexpr uint16_t asKey() const { return fKey; }
/** 4 char null terminated string consisting only of chars 'r', 'g', 'b', 'a'. */
/** 4 char null terminated string consisting only of chars 'r', 'g', 'b', 'a', '0', and '1'. */
constexpr const char* c_str() const { return fSwiz; }
constexpr char operator[](int i) const {
@ -55,9 +58,6 @@ private:
static constexpr int CToI(char c);
static constexpr char IToC(int idx);
// The normal component swizzles map to key values 0-3. We set the key for constant 1 to the
// next int.
static const int k1KeyValue = 4;
char fSwiz[5];
uint16_t fKey;
};
@ -111,32 +111,53 @@ constexpr float GrSwizzle::ComponentIndexToFloat(const SkPMColor4f& color, int i
if (idx <= 3) {
return color[idx];
}
if (idx == k1KeyValue) {
if (idx == CToI('1')) {
return 1.0f;
}
if (idx == CToI('0')) {
return 1.0f;
}
SkASSERT(false);
return -1.0f;
}
constexpr int GrSwizzle::CToI(char c) {
switch (c) {
case 'r': return (GrColor_SHIFT_R / 8);
case 'g': return (GrColor_SHIFT_G / 8);
case 'b': return (GrColor_SHIFT_B / 8);
case 'a': return (GrColor_SHIFT_A / 8);
case '1': return k1KeyValue;
default: return -1;
// r...a must map to 0...3 because other methods use them as indices into fSwiz.
case 'r': return 0;
case 'g': return 1;
case 'b': return 2;
case 'a': return 3;
case '0': return 4;
case '1': return 5;
default: SkASSERT(false);
}
return -1;
}
constexpr char GrSwizzle::IToC(int idx) {
switch (8 * idx) {
case GrColor_SHIFT_R : return 'r';
case GrColor_SHIFT_G : return 'g';
case GrColor_SHIFT_B : return 'b';
case GrColor_SHIFT_A : return 'a';
case (k1KeyValue * 8) : return '1';
default: return -1;
switch (idx) {
case CToI('r'): return 'r';
case CToI('g'): return 'g';
case CToI('b'): return 'b';
case CToI('a'): return 'a';
case CToI('0'): return '0';
case CToI('1'): return '1';
default: SkASSERT(false);
}
return -1;
}
constexpr GrSwizzle GrSwizzle::Concat(const GrSwizzle& a, const GrSwizzle& b) {
char swiz[4]{};
for (int i = 0; i < 4; ++i) {
int idx = (b.fKey >> (4U * i)) & 0xfU;
switch (idx) {
case CToI('0'): swiz[i] = '0'; break;
case CToI('1'): swiz[i] = '1'; break;
default: swiz[i] = a.fSwiz[idx]; break;
}
}
return GrSwizzle(swiz);
}
#endif

View File

@ -1333,13 +1333,6 @@ bool GrGLCaps::getExternalFormat(GrPixelConfig surfaceConfig, GrPixelConfig memo
bool surfaceIsAlphaOnly = GrPixelConfigIsAlphaOnly(surfaceConfig);
bool memoryIsAlphaOnly = GrPixelConfigIsAlphaOnly(memoryConfig);
// We don't currently support moving RGBA data into and out of ALPHA surfaces. It could be
// made to work. However, this is complicated by the use of GL_RED for alpha-only textures but
// is not needed currently.
if (surfaceIsAlphaOnly && !memoryIsAlphaOnly) {
return false;
}
*externalFormat = fConfigTable[memoryConfig].fFormats.fExternalFormat[usage];
*externalType = fConfigTable[memoryConfig].fFormats.fExternalType;
@ -1555,9 +1548,7 @@ void GrGLCaps::initConfigTable(const GrContextOptions& contextOptions,
fConfigTable[kRGB_888_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_RGB8;
// Our external RGB data always has a byte where alpha would be. When calling read pixels we
// want to read to kRGB_888x color type and ensure that gets 0xFF written. Using GL_RGB would
// read back unaligned 24bit RGB color values. Note that this all a bit moot as we don't
// currently expect to ever read back GrColorType::kRGB_888x because our implementation of
// supportedReadPixelsColorType never returns it.
// read back unaligned 24bit RGB color values.
fConfigTable[kRGB_888_GrPixelConfig].fFormats.fExternalFormat[kReadPixels_ExternalFormatUsage] = GR_GL_RGBA;
fConfigTable[kRGB_888_GrPixelConfig].fFormats.fExternalType = GR_GL_UNSIGNED_BYTE;
fConfigTable[kRGB_888_GrPixelConfig].fFormatType = kNormalizedFixedPoint_FormatType;
@ -1848,6 +1839,7 @@ void GrGLCaps::initConfigTable(const GrContextOptions& contextOptions,
alphaInfo.fFormats.fSizedInternalFormat = GR_GL_ALPHA8;
alphaInfo.fFormats.fExternalFormat[kReadPixels_ExternalFormatUsage] = GR_GL_ALPHA;
shaderCaps->fConfigTextureSwizzle[kAlpha_8_as_Alpha_GrPixelConfig] = GrSwizzle::AAAA();
alphaInfo.fRGBAReadSwizzle = GrSwizzle("000a");
if (fAlpha8IsRenderable && alpha8IsValidForGL) {
alphaInfo.fFlags |= allRenderFlags;
}
@ -1858,6 +1850,7 @@ void GrGLCaps::initConfigTable(const GrContextOptions& contextOptions,
redInfo.fFormats.fExternalFormat[kReadPixels_ExternalFormatUsage] = GR_GL_RED;
redInfo.fFormats.fExternalType = GR_GL_UNSIGNED_BYTE;
redInfo.fFormatType = kNormalizedFixedPoint_FormatType;
redInfo.fRGBAReadSwizzle = GrSwizzle("000r");
shaderCaps->fConfigTextureSwizzle[kAlpha_8_as_Red_GrPixelConfig] = GrSwizzle::RRRR();
// ES2 Command Buffer does not allow TexStorage with R8_EXT (so Alpha_8 and Gray_8)
@ -2042,6 +2035,7 @@ void GrGLCaps::initConfigTable(const GrContextOptions& contextOptions,
redHalf.fFormats.fSizedInternalFormat = GR_GL_R16F;
redHalf.fFormats.fExternalFormat[kReadPixels_ExternalFormatUsage] = GR_GL_RED;
shaderCaps->fConfigTextureSwizzle[kAlpha_half_as_Red_GrPixelConfig] = GrSwizzle::RRRR();
redHalf.fRGBAReadSwizzle = GrSwizzle("000r");
if (textureRedSupport && hasFP16Textures) {
redHalf.fFlags = ConfigInfo::kTextureable_Flag;
@ -3033,30 +3027,29 @@ bool GrGLCaps::surfaceSupportsReadPixels(const GrSurface* surface) const {
return true;
}
GrColorType GrGLCaps::supportedReadPixelsColorType(GrPixelConfig config,
GrColorType dstColorType) const {
GrCaps::SupportedRead GrGLCaps::supportedReadPixelsColorType(GrPixelConfig srcPixelConfig,
const GrBackendFormat& srcFormat,
GrColorType dstColorType) const {
// For now, we mostly report the read back format that is required by the ES spec without
// checking for implementation allowed formats or consider laxer rules in non-ES GL. TODO: Relax
// this as makes sense to increase performance and correctness.
switch (fConfigTable[config].fFormatType) {
case kNormalizedFixedPoint_FormatType:
if (kRGB_888X_GrPixelConfig == config) {
return GrColorType::kRGB_888x;
}
return GrColorType::kRGBA_8888;
case kFloat_FormatType:
if ((kAlpha_half_GrPixelConfig == config ||
kAlpha_half_as_Red_GrPixelConfig == config) &&
GrColorType::kAlpha_F16 == dstColorType) {
return GrColorType::kAlpha_F16;
}
// And similar for full float RG.
if (kRG_float_GrPixelConfig == config && GrColorType::kRG_F32 == dstColorType) {
return GrColorType::kRG_F32;
}
return GrColorType::kRGBA_F32;
const GrGLenum* glFormat = srcFormat.getGLFormat();
if (!glFormat) {
return {GrSwizzle{}, GrColorType::kUnknown};
}
return GrColorType::kUnknown;
auto swizzle = fConfigTable[srcPixelConfig].fRGBAReadSwizzle;
switch (fConfigTable[srcPixelConfig].fFormatType) {
case kNormalizedFixedPoint_FormatType:
if (kRGB_888X_GrPixelConfig == srcPixelConfig && *glFormat == GR_GL_RGBA8 &&
GrColorTypeHasAlpha(dstColorType)) {
// This can skip an unnecessary conversion.
return {swizzle, GrColorType::kRGB_888x};
}
return {swizzle, GrColorType::kRGBA_8888};
case kFloat_FormatType:
return {swizzle, GrColorType::kRGBA_F32};
}
return {GrSwizzle{}, GrColorType::kUnknown};
}
bool GrGLCaps::onIsWindowRectanglesSupportedForRT(const GrBackendRenderTarget& backendRT) const {

View File

@ -307,7 +307,8 @@ public:
bool useNonVBOVertexAndIndexDynamicData() const { return fUseNonVBOVertexAndIndexDynamicData; }
bool surfaceSupportsReadPixels(const GrSurface*) const override;
GrColorType supportedReadPixelsColorType(GrPixelConfig, GrColorType) const override;
SupportedRead supportedReadPixelsColorType(GrPixelConfig, const GrBackendFormat&,
GrColorType) const override;
/// Does ReadPixels support reading readConfig pixels from a FBO that is surfaceConfig?
bool readPixelsSupported(GrPixelConfig surfaceConfig,
@ -568,6 +569,11 @@ private:
// Index fStencilFormats.
int fStencilFormatIndex;
// If data from a surface of this config is read back to a GrColorType with all four
// color channels this indicates how each channel should be interpreted. May contain
// 0s and 1s.
GrSwizzle fRGBAReadSwizzle = GrSwizzle("rgba");
SkTDArray<int> fColorSampleCounts;
enum {

View File

@ -2973,6 +2973,7 @@ static void get_gl_swizzle_values(const GrSwizzle& swizzle, GrGLenum glValues[4]
case 'g': glValues[i] = GR_GL_GREEN; break;
case 'b': glValues[i] = GR_GL_BLUE; break;
case 'a': glValues[i] = GR_GL_ALPHA; break;
case '0': glValues[i] = GR_GL_ZERO; break;
case '1': glValues[i] = GR_GL_ONE; break;
default: SK_ABORT("Unsupported component");
}

View File

@ -2636,6 +2636,7 @@ STAGE(swizzle, void* ctx) {
case 'g': *o[i] = ig; break;
case 'b': *o[i] = ib; break;
case 'a': *o[i] = ia; break;
case '0': *o[i] = F(0); break;
case '1': *o[i] = F(1); break;
default: break;
}
@ -3441,7 +3442,7 @@ SI void store_88_(uint16_t* ptr, size_t tail, U16 r, U16 g) {
STAGE_PP(load_rg88, const SkRasterPipeline_MemoryCtx* ctx) {
b = 0;
a = 1;
a = 255;
load_88_(ptr_at_xy<const uint16_t>(ctx, dx,dy), tail, &r,&g);
}
STAGE_PP(store_rg88, const SkRasterPipeline_MemoryCtx* ctx) {
@ -3832,6 +3833,7 @@ STAGE_PP(swizzle, void* ctx) {
case 'g': *o[i] = ig; break;
case 'b': *o[i] = ib; break;
case 'a': *o[i] = ia; break;
case '0': *o[i] = U16(0); break;
case '1': *o[i] = U16(255); break;
default: break;
}

View File

@ -37,9 +37,6 @@ void testing_only_texture_test(skiatest::Reporter* reporter, GrContext* context,
return;
}
if (caps->supportedReadPixelsColorType(config, grCT) != grCT) {
return;
}
GrBackendTexture backendTex;
@ -61,6 +58,12 @@ void testing_only_texture_test(skiatest::Reporter* reporter, GrContext* context,
if (!backendTex.isValid()) {
return;
}
// skbug.com/9165
auto supportedRead =
caps->supportedReadPixelsColorType(config, backendTex.getBackendFormat(), grCT);
if (supportedRead.fColorType != grCT || supportedRead.fSwizzle != GrSwizzle("rgba")) {
return;
}
sk_sp<GrTextureProxy> wrappedProxy;
if (GrRenderable::kYes == renderable) {

View File

@ -526,7 +526,7 @@ DEF_TEST(SkRasterPipeline_swizzle, r) {
rg[i][1] = 2 * i + 1;
}
GrSwizzle swizzle("1gra");
GrSwizzle swizzle("0gra");
uint16_t buffer[64][4];
SkRasterPipeline_MemoryCtx src = { rg, 0 },
@ -539,7 +539,7 @@ DEF_TEST(SkRasterPipeline_swizzle, r) {
for (int i = 0; i < 64; i++) {
uint16_t want[4] {
h(1),
h(0),
h(2 * i + 1),
h(i + 1),
h(1),