Skip to content

Commit

Permalink
Add function to GrDataUtils to handle color conversions.
Browse files Browse the repository at this point in the history
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 <[email protected]>
Reviewed-by: Greg Daniel <[email protected]>
  • Loading branch information
bsalomon authored and Skia Commit-Bot committed Jun 17, 2019
1 parent b97824d commit 5509102
Show file tree
Hide file tree
Showing 14 changed files with 429 additions and 207 deletions.
6 changes: 6 additions & 0 deletions src/gpu/GrCaps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)};
}
22 changes: 15 additions & 7 deletions src/gpu/GrCaps.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,18 +205,26 @@ class GrCaps : public SkRefCnt {
* 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; }
Expand Down
190 changes: 189 additions & 1 deletion src/gpu/GrDataUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
}
19 changes: 19 additions & 0 deletions src/gpu/GrDataUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Loading

0 comments on commit 5509102

Please sign in to comment.