/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "ColorCodecBench.h" #include "Resources.h" #include "SkCodec.h" #include "SkColorSpaceXform.h" #include "SkCommandLineFlags.h" #if defined(SK_TEST_QCMS) DEFINE_bool(qcms, false, "Bench qcms color conversion"); #endif DEFINE_bool(xform_only, false, "Only time the color xform, do not include the decode time"); DEFINE_bool(srgb, false, "Convert to srgb dst space"); DEFINE_bool(half, false, "Convert to half floats"); ColorCodecBench::ColorCodecBench(const char* name, sk_sp encoded) : fEncoded(std::move(encoded)) #if defined(SK_TEST_QCMS) , fDstSpaceQCMS(nullptr) #endif { fName.appendf("Color%s", FLAGS_xform_only ? "Xform" : "Codec"); #if defined(SK_TEST_QCMS) fName.appendf("%s", FLAGS_qcms ? "QCMS" : ""); #endif fName.appendf("_%s", name); } const char* ColorCodecBench::onGetName() { return fName.c_str(); } bool ColorCodecBench::isSuitableFor(Backend backend) { return kNonRendering_Backend == backend; } void ColorCodecBench::decodeAndXform() { SkAutoTDelete codec(SkCodec::NewFromData(fEncoded)); SkASSERT(codec); #ifdef SK_DEBUG SkCodec::Result result = #endif codec->getPixels(fDstInfo, fDst.get(), fDstInfo.minRowBytes()); SkASSERT(SkCodec::kSuccess == result); } #if defined(SK_TEST_QCMS) void ColorCodecBench::decodeAndXformQCMS() { SkAutoTDelete codec(SkCodec::NewFromData(fEncoded)); #ifdef SK_DEBUG const SkCodec::Result result = #endif codec->startScanlineDecode(fSrcInfo); SkASSERT(SkCodec::kSuccess == result); SkAutoTCallVProc srcSpace(qcms_profile_from_memory(fSrcData->data(), fSrcData->size())); SkASSERT(srcSpace); SkAutoTCallVProc transform (qcms_transform_create(srcSpace, QCMS_DATA_RGBA_8, fDstSpaceQCMS.get(), QCMS_DATA_RGBA_8, QCMS_INTENT_PERCEPTUAL)); SkASSERT(transform); #ifdef SK_PMCOLOR_IS_RGBA qcms_output_type outType = QCMS_OUTPUT_RGBX; #else qcms_output_type outType = QCMS_OUTPUT_BGRX; #endif void* dst = fDst.get(); for (int y = 0; y < fSrcInfo.height(); y++) { #ifdef SK_DEBUG const int rows = #endif codec->getScanlines(fSrc.get(), 1, 0); SkASSERT(1 == rows); qcms_transform_data_type(transform, fSrc.get(), dst, fSrcInfo.width(), outType); dst = SkTAddOffset(dst, fDstInfo.minRowBytes()); } } #endif void ColorCodecBench::xformOnly() { sk_sp srcSpace = SkColorSpace::NewICC(fSrcData->data(), fSrcData->size()); if (!srcSpace) { srcSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); } std::unique_ptr xform = SkColorSpaceXform::New(srcSpace, fDstSpace); SkASSERT(xform); void* dst = fDst.get(); void* src = fSrc.get(); for (int y = 0; y < fSrcInfo.height(); y++) { xform->apply(dst, (uint32_t*) src, fSrcInfo.width(), fDstInfo.colorType(), fDstInfo.alphaType()); dst = SkTAddOffset(dst, fDstInfo.minRowBytes()); src = SkTAddOffset(src, fSrcInfo.minRowBytes()); } } #if defined(SK_TEST_QCMS) void ColorCodecBench::xformOnlyQCMS() { SkAutoTCallVProc srcSpace(qcms_profile_from_memory(fSrcData->data(), fSrcData->size())); SkASSERT(srcSpace); SkAutoTCallVProc transform (qcms_transform_create(srcSpace, QCMS_DATA_RGBA_8, fDstSpaceQCMS.get(), QCMS_DATA_RGBA_8, QCMS_INTENT_PERCEPTUAL)); SkASSERT(transform); #ifdef SK_PMCOLOR_IS_RGBA qcms_output_type outType = QCMS_OUTPUT_RGBX; #else qcms_output_type outType = QCMS_OUTPUT_BGRX; #endif void* dst = fDst.get(); void* src = fSrc.get(); for (int y = 0; y < fSrcInfo.height(); y++) { // Transform in place qcms_transform_data_type(transform, src, dst, fSrcInfo.width(), outType); dst = SkTAddOffset(dst, fDstInfo.minRowBytes()); src = SkTAddOffset(src, fSrcInfo.minRowBytes()); } } #endif void ColorCodecBench::onDelayedSetup() { SkAutoTDelete codec(SkCodec::NewFromData(fEncoded)); fSrcData = codec->getICCData(); sk_sp dstData = SkData::MakeFromFileName( GetResourcePath("icc_profiles/HP_ZR30w.icc").c_str()); SkASSERT(dstData); fDstSpace = nullptr; #if defined(SK_TEST_QCMS) if (FLAGS_qcms) { fDstSpaceQCMS.reset(FLAGS_srgb ? qcms_profile_sRGB() : qcms_profile_from_memory(dstData->data(), dstData->size())); SkASSERT(fDstSpaceQCMS); // This call takes a non-trivial amount of time, but I think it's the most fair to // treat it as overhead. It only needs to happen once. qcms_profile_precache_output_transform(fDstSpaceQCMS); } else #endif { fDstSpace = FLAGS_srgb ? SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named) : SkColorSpace::NewICC(dstData->data(), dstData->size()); SkASSERT(fDstSpace); } fSrcInfo = codec->getInfo().makeColorType(kRGBA_8888_SkColorType); fDstInfo = fSrcInfo; if (FLAGS_half) { fDstInfo = fDstInfo.makeColorType(kRGBA_F16_SkColorType); fDstSpace = fDstSpace->makeLinearGamma(); } fDstInfo = fDstInfo.makeColorSpace(fDstSpace); fDst.reset(fDstInfo.getSafeSize(fDstInfo.minRowBytes())); if (FLAGS_xform_only) { fSrc.reset(fSrcInfo.getSafeSize(fSrcInfo.minRowBytes())); codec->getPixels(fSrcInfo, fSrc.get(), fSrcInfo.minRowBytes()); } #if defined(SK_TEST_QCMS) else if (FLAGS_qcms) { // Set-up a row buffer to decode into before transforming to dst. fSrc.reset(fSrcInfo.minRowBytes()); } #endif } void ColorCodecBench::onDraw(int n, SkCanvas*) { #if defined(SK_TEST_QCMS) if (FLAGS_qcms && FLAGS_half) { SkDebugf("Error: Contradicting flags.\n"); return; } #endif for (int i = 0; i < n; i++) { #if defined(SK_TEST_QCMS) if (FLAGS_qcms) { if (FLAGS_xform_only) { this->xformOnlyQCMS(); } else { this->decodeAndXformQCMS(); } } else #endif { if (FLAGS_xform_only) { this->xformOnly(); } else { this->decodeAndXform(); } } } }