blob: cd96e7f1c6aebb3457582dbce886739b24eb0f40 [file] [log] [blame]
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkColorSpacePriv.h"
#include "SkConvertPixels.h"
#include "SkHalf.h"
#include "SkImageInfoPriv.h"
#include "SkOpts.h"
#include "SkPM4fPriv.h"
#include "SkRasterPipeline.h"
#include "SkUnPreMultiply.h"
#include "SkUnPreMultiplyPriv.h"
#include "../jumper/SkJumper.h"
// Fast Path 1: The memcpy() case.
static inline bool can_memcpy(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo) {
if (dstInfo.colorType() != srcInfo.colorType()) {
return false;
}
if (kAlpha_8_SkColorType == dstInfo.colorType()) {
return true;
}
if (dstInfo.alphaType() != srcInfo.alphaType() &&
kOpaque_SkAlphaType != dstInfo.alphaType() &&
kOpaque_SkAlphaType != srcInfo.alphaType())
{
// We need to premultiply or unpremultiply.
return false;
}
return !dstInfo.colorSpace() ||
SkColorSpace::Equals(dstInfo.colorSpace(), srcInfo.colorSpace());
}
// Fast Path 2: Simple swizzles and premuls.
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);
}
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);
}
}
// Fast Path 3: Alpha 8 dsts.
static void convert_to_alpha8(uint8_t* dst, size_t dstRB, const SkImageInfo& srcInfo,
const void* src, size_t srcRB) {
if (srcInfo.isOpaque()) {
for (int y = 0; y < srcInfo.height(); ++y) {
memset(dst, 0xFF, srcInfo.width());
dst = SkTAddOffset<uint8_t>(dst, dstRB);
}
return;
}
switch (srcInfo.colorType()) {
case kBGRA_8888_SkColorType:
case kRGBA_8888_SkColorType: {
auto src32 = (const uint32_t*) src;
for (int y = 0; y < srcInfo.height(); y++) {
for (int x = 0; x < srcInfo.width(); x++) {
dst[x] = src32[x] >> 24;
}
dst = SkTAddOffset<uint8_t>(dst, dstRB);
src32 = SkTAddOffset<const uint32_t>(src32, srcRB);
}
break;
}
case kRGBA_1010102_SkColorType: {
auto src32 = (const uint32_t*) src;
for (int y = 0; y < srcInfo.height(); y++) {
for (int x = 0; x < srcInfo.width(); x++) {
switch (src32[x] >> 30) {
case 0:
dst[x] = 0;
break;
case 1:
dst[x] = 0x55;
break;
case 2:
dst[x] = 0xAA;
break;
case 3:
dst[x] = 0xFF;
break;
}
}
dst = SkTAddOffset<uint8_t>(dst, dstRB);
src32 = SkTAddOffset<const uint32_t>(src32, srcRB);
}
break;
}
case kARGB_4444_SkColorType: {
auto src16 = (const uint16_t*) src;
for (int y = 0; y < srcInfo.height(); y++) {
for (int x = 0; x < srcInfo.width(); x++) {
dst[x] = SkPacked4444ToA32(src16[x]);
}
dst = SkTAddOffset<uint8_t>(dst, dstRB);
src16 = SkTAddOffset<const uint16_t>(src16, srcRB);
}
break;
}
case kRGBA_F16_SkColorType: {
auto src64 = (const uint64_t*) src;
for (int y = 0; y < srcInfo.height(); y++) {
for (int x = 0; x < srcInfo.width(); x++) {
dst[x] = (uint8_t) (255.0f * SkHalfToFloat(src64[x] >> 48));
}
dst = SkTAddOffset<uint8_t>(dst, dstRB);
src64 = SkTAddOffset<const uint64_t>(src64, srcRB);
}
break;
}
case kRGBA_F32_SkColorType: {
auto rgba = (const float*)src;
for (int y = 0; y < srcInfo.height(); y++) {
for (int x = 0; x < srcInfo.width(); x++) {
dst[x] = (uint8_t)(255.0f * rgba[4*x+3]);
}
dst = SkTAddOffset<uint8_t>(dst, dstRB);
rgba = SkTAddOffset<const float>(rgba, srcRB);
}
} break;
default:
SkASSERT(false);
break;
}
}
// Default: Use the pipeline.
static void convert_with_pipeline(const SkImageInfo& dstInfo, void* dstRow, size_t dstRB,
const SkImageInfo& srcInfo, const void* srcRow, size_t srcRB) {
SkJumper_MemoryCtx src = { (void*)srcRow, (int)(srcRB / srcInfo.bytesPerPixel()) },
dst = { (void*)dstRow, (int)(dstRB / dstInfo.bytesPerPixel()) };
SkRasterPipeline_<256> pipeline;
switch (srcInfo.colorType()) {
case kRGBA_8888_SkColorType:
pipeline.append(SkRasterPipeline::load_8888, &src);
break;
case kRGB_888x_SkColorType:
pipeline.append(SkRasterPipeline::load_8888, &src);
pipeline.append(SkRasterPipeline::force_opaque);
break;
case kBGRA_8888_SkColorType:
pipeline.append(SkRasterPipeline::load_bgra, &src);
break;
case kRGBA_1010102_SkColorType:
pipeline.append(SkRasterPipeline::load_1010102, &src);
break;
case kRGB_101010x_SkColorType:
pipeline.append(SkRasterPipeline::load_1010102, &src);
pipeline.append(SkRasterPipeline::force_opaque);
break;
case kRGB_565_SkColorType:
pipeline.append(SkRasterPipeline::load_565, &src);
break;
case kRGBA_F16_SkColorType:
pipeline.append(SkRasterPipeline::load_f16, &src);
break;
case kRGBA_F32_SkColorType:
pipeline.append(SkRasterPipeline::load_f32, &src);
break;
case kGray_8_SkColorType:
pipeline.append(SkRasterPipeline::load_g8, &src);
break;
case kAlpha_8_SkColorType:
pipeline.append(SkRasterPipeline::load_a8, &src);
break;
case kARGB_4444_SkColorType:
pipeline.append(SkRasterPipeline::load_4444, &src);
break;
case kUnknown_SkColorType:
SkASSERT(false);
break;
}
SkColorSpaceXformSteps steps{srcInfo.colorSpace(), srcInfo.alphaType(),
dstInfo.colorSpace(), dstInfo.alphaType()};
steps.apply(&pipeline);
// We'll dither if we're decreasing precision below 32-bit.
float dither_rate = 0.0f;
if (srcInfo.bytesPerPixel() > dstInfo.bytesPerPixel()) {
switch (dstInfo.colorType()) {
case kRGB_565_SkColorType: dither_rate = 1/63.0f; break;
case kARGB_4444_SkColorType: dither_rate = 1/15.0f; break;
default: dither_rate = 0.0f; break;
}
}
if (dither_rate > 0) {
pipeline.append(SkRasterPipeline::dither, &dither_rate);
}
switch (dstInfo.colorType()) {
case kRGBA_8888_SkColorType:
pipeline.append(SkRasterPipeline::store_8888, &dst);
break;
case kRGB_888x_SkColorType:
pipeline.append(SkRasterPipeline::force_opaque);
pipeline.append(SkRasterPipeline::store_8888, &dst);
break;
case kBGRA_8888_SkColorType:
pipeline.append(SkRasterPipeline::store_bgra, &dst);
break;
case kRGBA_1010102_SkColorType:
pipeline.append(SkRasterPipeline::store_1010102, &dst);
break;
case kRGB_101010x_SkColorType:
pipeline.append(SkRasterPipeline::force_opaque);
pipeline.append(SkRasterPipeline::store_1010102, &dst);
break;
case kRGB_565_SkColorType:
pipeline.append(SkRasterPipeline::store_565, &dst);
break;
case kRGBA_F16_SkColorType:
pipeline.append(SkRasterPipeline::store_f16, &dst);
break;
case kRGBA_F32_SkColorType:
pipeline.append(SkRasterPipeline::store_f32, &dst);
break;
case kARGB_4444_SkColorType:
pipeline.append(SkRasterPipeline::store_4444, &dst);
break;
case kAlpha_8_SkColorType:
pipeline.append(SkRasterPipeline::store_a8, &dst);
break;
case kGray_8_SkColorType:
pipeline.append(SkRasterPipeline::luminance_to_alpha);
pipeline.append(SkRasterPipeline::store_a8, &dst);
break;
case kUnknown_SkColorType:
SkASSERT(false);
break;
}
pipeline.run(0,0, srcInfo.width(), srcInfo.height());
}
static bool swizzle_and_multiply_color_type(SkColorType ct) {
switch (ct) {
case kRGBA_8888_SkColorType:
case kBGRA_8888_SkColorType:
return true;
default:
return false;
}
}
void SkConvertPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
const SkImageInfo& srcInfo, const void* srcPixels, size_t srcRB) {
SkASSERT(dstInfo.dimensions() == srcInfo.dimensions());
SkASSERT(SkImageInfoValidConversion(dstInfo, srcInfo));
// Fast Path 1: The memcpy() case.
if (can_memcpy(dstInfo, srcInfo)) {
SkRectMemcpy(dstPixels, dstRB, srcPixels, srcRB, dstInfo.minRowBytes(), dstInfo.height());
return;
}
// Fast Path 2: Simple swizzles and premuls.
if (swizzle_and_multiply_color_type(srcInfo.colorType()) &&
swizzle_and_multiply_color_type(dstInfo.colorType()) && !dstInfo.colorSpace()) {
swizzle_and_multiply(dstInfo, dstPixels, dstRB, srcInfo, srcPixels, srcRB);
return;
}
// Fast Path 3: Alpha 8 dsts.
if (kAlpha_8_SkColorType == dstInfo.colorType()) {
convert_to_alpha8((uint8_t*) dstPixels, dstRB, srcInfo, srcPixels, srcRB);
return;
}
// Default: Use the pipeline.
convert_with_pipeline(dstInfo, dstPixels, dstRB, srcInfo, srcPixels, srcRB);
}