skia2/bench/ColorCodecBench.cpp
msarett 200877eeca Optimize color xforms when src and dst are matching
Useful when:
(1) Client does not realize src and dst match (calls color
    xform anyway).
(2) Client wants half floats, src and dst have matching
    gamuts
(3) Client wants premul (done correctly in linear space),
    src and dst have matching gamuts.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2206403003

Review-Url: https://codereview.chromium.org/2206403003
2016-08-15 08:10:44 -07:00

210 lines
6.5 KiB
C++

/*
* 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<SkData> 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<SkCodec> codec(SkCodec::NewFromData(fEncoded.get()));
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<SkCodec> codec(SkCodec::NewFromData(fEncoded.get()));
#ifdef SK_DEBUG
const SkCodec::Result result =
#endif
codec->startScanlineDecode(fSrcInfo);
SkASSERT(SkCodec::kSuccess == result);
SkAutoTCallVProc<qcms_profile, qcms_profile_release>
srcSpace(qcms_profile_from_memory(fSrcData->data(), fSrcData->size()));
SkASSERT(srcSpace);
SkAutoTCallVProc<qcms_transform, qcms_transform_release>
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<void>(dst, fDstInfo.minRowBytes());
}
}
#endif
void ColorCodecBench::xformOnly() {
sk_sp<SkColorSpace> srcSpace = SkColorSpace::NewICC(fSrcData->data(), fSrcData->size());
if (!srcSpace) {
srcSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
}
std::unique_ptr<SkColorSpaceXform> 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<void>(dst, fDstInfo.minRowBytes());
src = SkTAddOffset<void>(src, fSrcInfo.minRowBytes());
}
}
#if defined(SK_TEST_QCMS)
void ColorCodecBench::xformOnlyQCMS() {
SkAutoTCallVProc<qcms_profile, qcms_profile_release>
srcSpace(qcms_profile_from_memory(fSrcData->data(), fSrcData->size()));
SkASSERT(srcSpace);
SkAutoTCallVProc<qcms_transform, qcms_transform_release>
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<void>(dst, fDstInfo.minRowBytes());
src = SkTAddOffset<void>(src, fSrcInfo.minRowBytes());
}
}
#endif
void ColorCodecBench::onDelayedSetup() {
SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(fEncoded.get()));
fSrcData = codec->getICCData();
sk_sp<SkData> 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.makeColorSpace(fDstSpace);
if (FLAGS_half) {
fDstInfo = fDstInfo.makeColorType(kRGBA_F16_SkColorType);
}
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();
}
}
}
}