DEF_TEST(SkColorSpace_New_TransferFnStages, r) {
    // We'll create a little SkRasterPipelineBlitter-like scenario,
    // blending the same src color over the same dst color, but with
    // three different transfer functions, for simplicity the same for src and dst.
    SkColor src = 0x7f7f0000;

    SkColor dsts[3];
    for (SkColor& dst : dsts) {
        dst = 0xff007f00;
    }

    auto gamut = SkMatrix44::I();
    auto blending = SkColorSpace_New::Blending::Linear;
    SkColorSpace_New linear{SkColorSpace_New::TransferFn::MakeLinear(), gamut, blending},
                       srgb{SkColorSpace_New::TransferFn::MakeSRGB(),   gamut, blending},
                      gamma{SkColorSpace_New::TransferFn::MakeGamma(3), gamut, blending};
    SkColor* dst = dsts;
    for (const SkColorSpace_New* cs : {&linear, &srgb, &gamma}) {
        SkJumper_MemoryCtx src_ctx = {  &src, 0 },
                           dst_ctx = { dst++, 0 };

        SkRasterPipeline_<256> p;

        p.append(SkRasterPipeline::load_8888, &src_ctx);
        cs->transferFn().linearizeSrc(&p);
        p.append(SkRasterPipeline::premul);

        p.append(SkRasterPipeline::load_8888_dst, &dst_ctx);
        cs->transferFn().linearizeDst(&p);
        p.append(SkRasterPipeline::premul_dst);

        p.append(SkRasterPipeline::srcover);
        p.append(SkRasterPipeline::unpremul);
        cs->transferFn().encodeSrc(&p);
        p.append(SkRasterPipeline::store_8888, &dst_ctx);
        p.run(0,0,1,1);
    }

    // Double check the uninteresting channels: alpha's opaque, no blue.
    REPORTER_ASSERT(r, SkColorGetA(dsts[0]) == 0xff && SkColorGetB(dsts[0]) == 0x00);
    REPORTER_ASSERT(r, SkColorGetA(dsts[1]) == 0xff && SkColorGetB(dsts[1]) == 0x00);
    REPORTER_ASSERT(r, SkColorGetA(dsts[2]) == 0xff && SkColorGetB(dsts[2]) == 0x00);

    // Because we're doing linear blending, a more-exponential transfer function will
    // brighten the encoded values more when linearizing. So we expect to see that
    // linear is darker than sRGB, and sRGB in turn is darker than gamma 3.
    REPORTER_ASSERT(r, SkColorGetR(dsts[0]) < SkColorGetR(dsts[1]));
    REPORTER_ASSERT(r, SkColorGetR(dsts[1]) < SkColorGetR(dsts[2]));

    REPORTER_ASSERT(r, SkColorGetG(dsts[0]) < SkColorGetG(dsts[1]));
    REPORTER_ASSERT(r, SkColorGetG(dsts[1]) < SkColorGetG(dsts[2]));

}
Пример #2
0
DEF_TEST(sk_pipeline_srgb_edge_cases, r) {
    // We need to run at least 4 pixels to make sure we hit all specializations.
    SkPM4f colors[4] = { {{0,1,1,1}}, {{0,0,0,0}}, {{0,0,0,0}}, {{0,0,0,0}} };
    auto& color = colors[0];
    void* dst = &color;

    SkRasterPipeline_<256> p;
    p.append(SkRasterPipeline::uniform_color, &color);
    p.append(SkRasterPipeline::to_srgb);
    p.append(SkRasterPipeline::store_f32, &dst);
    p.run(0,0,4);

    if (color.r() != 0.0f) {
        ERRORF(r, "expected to_srgb() to map 0.0f to 0.0f, got %f", color.r());
    }
    if (color.g() != 1.0f) {
        float f = color.g();
        uint32_t x;
        memcpy(&x, &f, 4);
        ERRORF(r, "expected to_srgb() to map 1.0f to 1.0f, got %f (%08x)", color.g(), x);
    }
}
Пример #3
0
DEF_TEST(sk_pipeline_srgb_roundtrip, r) {
    uint32_t reds[256];
    for (int i = 0; i < 256; i++) {
        reds[i] = i;
    }

    SkJumper_MemoryCtx ptr = { reds, 0 };

    SkRasterPipeline_<256> p;
    p.append(SkRasterPipeline::load_8888,  &ptr);
    p.append(SkRasterPipeline::from_srgb);
    p.append(SkRasterPipeline::to_srgb);
    p.append(SkRasterPipeline::store_8888, &ptr);

    p.run(0,0,256,1);

    for (int i = 0; i < 256; i++) {
        if (reds[i] != (uint32_t)i) {
            ERRORF(r, "%d doesn't round trip, %d", i, reds[i]);
        }
    }
}
Пример #4
0
DEF_TEST(sk_pipeline_srgb_roundtrip, r) {
    uint32_t reds[256];
    for (int i = 0; i < 256; i++) {
        reds[i] = i;
    }

    auto ptr = (void*)reds;

    SkRasterPipeline_<256> p;
    p.append(SkRasterPipeline::load_8888,  &ptr);
    p.append_from_srgb(kUnpremul_SkAlphaType);
    p.append(SkRasterPipeline::to_srgb);
    p.append(SkRasterPipeline::store_8888, &ptr);

    p.run(0,0,256);

    for (int i = 0; i < 256; i++) {
        if (reds[i] != (uint32_t)i) {
            ERRORF(r, "%d doesn't round trip, %d", i, reds[i]);
        }
    }
}
SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst,
                                           const SkPaint& paint,
                                           SkArenaAlloc* alloc,
                                           const SkRasterPipeline& shaderPipeline,
                                           SkShaderBase::Context* burstCtx,
                                           bool is_opaque,
                                           bool is_constant) {
    auto blitter = alloc->make<SkRasterPipelineBlitter>(dst,
                                                        paint.getBlendMode(),
                                                        alloc,
                                                        burstCtx);

    // Our job in this factory is to fill out the blitter's color pipeline.
    // This is the common front of the full blit pipelines, each constructed lazily on first use.
    // The full blit pipelines handle reading and writing the dst, blending, coverage, dithering.
    auto colorPipeline = &blitter->fColorPipeline;

    // Let's get the shader in first.
    if (burstCtx) {
        colorPipeline->append(SkRasterPipeline::load_f32, &blitter->fShaderOutput);
    } else {
        colorPipeline->extend(shaderPipeline);
    }

    // If there's a color filter it comes next.
    if (auto colorFilter = paint.getColorFilter()) {
        colorFilter->appendStages(colorPipeline, dst.colorSpace(), alloc, is_opaque);
        is_opaque = is_opaque && (colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag);
    }

    // Not all formats make sense to dither (think, F16).  We set their dither rate to zero.
    // We need to decide if we're going to dither now to keep is_constant accurate.
    if (paint.isDither()) {
        switch (dst.info().colorType()) {
            default:                     blitter->fDitherRate =     0.0f; break;
            case kARGB_4444_SkColorType: blitter->fDitherRate =  1/15.0f; break;
            case   kRGB_565_SkColorType: blitter->fDitherRate =  1/63.0f; break;
            case    kGray_8_SkColorType:
            case kRGBA_8888_SkColorType:
            case kBGRA_8888_SkColorType: blitter->fDitherRate = 1/255.0f; break;
        }
        // TODO: for constant colors, we could try to measure the effect of dithering, and if
        //       it has no value (i.e. all variations result in the same 32bit color, then we
        //       could disable it (for speed, by not adding the stage).
    }
    is_constant = is_constant && (blitter->fDitherRate == 0.0f);

    // We're logically done here.  The code between here and return blitter is all optimization.

    // A pipeline that's still constant here can collapse back into a constant color.
    if (is_constant) {
        SkPM4f storage;
        SkPM4f* constantColor = &storage;
        colorPipeline->append(SkRasterPipeline::store_f32, &constantColor);
        colorPipeline->run(0,0,1);
        colorPipeline->reset();
        colorPipeline->append_uniform_color(alloc, *constantColor);

        is_opaque = constantColor->a() == 1.0f;
    }

    // We can strength-reduce SrcOver into Src when opaque.
    if (is_opaque && blitter->fBlend == SkBlendMode::kSrcOver) {
        blitter->fBlend = SkBlendMode::kSrc;
    }

    // When we're drawing a constant color in Src mode, we can sometimes just memset.
    // (The previous two optimizations help find more opportunities for this one.)
    if (is_constant && blitter->fBlend == SkBlendMode::kSrc) {
        // Run our color pipeline all the way through to produce what we'd memset when we can.
        // Not all blits can memset, so we need to keep colorPipeline too.
        SkRasterPipeline_<256> p;
        p.extend(*colorPipeline);
        blitter->fDstPtr = &blitter->fMemsetColor;
        blitter->append_store(&p);
        p.run(0,0,1);

        blitter->fCanMemsetInBlitH = true;
    }

    return blitter;
}