示例#1
0
void WebMediaPlayerClientImpl::paintOnAndroid(WebCore::GraphicsContext* context, const IntRect& rect, uint8_t alpha)
{
    OwnPtr<blink::WebGraphicsContext3DProvider> provider = adoptPtr(blink::Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
    if (!provider)
        return;
    WebGraphicsContext3D* context3D = provider->context3d();
    if (!context || !context3D || !m_webMediaPlayer || context->paintingDisabled())
        return;

    if (!context3D->makeContextCurrent())
        return;

    // Copy video texture into a RGBA texture based bitmap first as video texture on Android is GL_TEXTURE_EXTERNAL_OES
    // which is not supported by Skia yet. The bitmap's size needs to be the same as the video and use naturalSize() here.
    // Check if we could reuse existing texture based bitmap.
    // Otherwise, release existing texture based bitmap and allocate a new one based on video size.
    if (!ensureTextureBackedSkBitmap(provider->grContext(), m_bitmap, m_webMediaPlayer->naturalSize(), kTopLeft_GrSurfaceOrigin, kSkia8888_GrPixelConfig))
        return;

    // Copy video texture to bitmap texture.
    WebCanvas* canvas = context->canvas();
    unsigned textureId = static_cast<unsigned>((m_bitmap.getTexture())->getTextureHandle());
    if (!m_webMediaPlayer->copyVideoTextureToPlatformTexture(context3D, textureId, 0, GL_RGBA, GL_UNSIGNED_BYTE, true, false))
        return;

    // Draw the texture based bitmap onto the Canvas. If the canvas is hardware based, this will do a GPU-GPU texture copy. If the canvas is software based,
    // the texture based bitmap will be readbacked to system memory then draw onto the canvas.
    SkRect dest;
    dest.set(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height());
    SkPaint paint;
    paint.setAlpha(alpha);
    // It is not necessary to pass the dest into the drawBitmap call since all the context have been set up before calling paintCurrentFrameInContext.
    canvas->drawBitmapRect(m_bitmap, NULL, dest, &paint);
}
示例#2
0
static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkIRect& srcRect, const SkRect& destRect, const SkXfermode::Mode& compOp)
{
    SkPaint paint;
    paint.setXfermodeMode(compOp);
    paint.setFilterBitmap(true);
    int alpha = roundf(platformContext->getAlpha() * 256);
    if (alpha > 255)
        alpha = 255;
    else if (alpha < 0)
        alpha = 0;
    paint.setAlpha(alpha);

    skia::PlatformCanvas* canvas = platformContext->canvas();

    ResamplingMode resampling = platformContext->isPrinting() ? RESAMPLE_NONE :
        computeResamplingMode(bitmap, srcRect.width(), srcRect.height(),
                              SkScalarToFloat(destRect.width()),
                              SkScalarToFloat(destRect.height()));
    if (resampling == RESAMPLE_AWESOME) {
        drawResampledBitmap(*canvas, paint, bitmap, srcRect, destRect);
    } else {
        // No resampling necessary, we can just draw the bitmap. We want to
        // filter it if we decided to do linear interpolation above, or if there
        // is something interesting going on with the matrix (like a rotation).
        // Note: for serialization, we will want to subset the bitmap first so
        // we don't send extra pixels.
        canvas->drawBitmapRect(bitmap, &srcRect, destRect, &paint);
    }
}
示例#3
0
void SkSVGRenderContext::applyOpacity(SkScalar opacity, uint32_t flags) {
    if (opacity >= 1) {
        return;
    }

    const bool hasFill   = SkToBool(this->fillPaint());
    const bool hasStroke = SkToBool(this->strokePaint());

    // We can apply the opacity as paint alpha iif it only affects one atomic draw.
    // For now, this means a) the target node doesn't have any descendants, and
    // b) it only has a stroke or a fill (but not both).  Going forward, we may need
    // to refine this heuristic (e.g. to accommodate markers).
    if ((flags & kLeaf) && (hasFill ^ hasStroke)) {
        auto* pctx = fPresentationContext.writable();
        if (hasFill) {
            pctx->fFillPaint.setAlpha(
                SkScalarRoundToInt(opacity * pctx->fFillPaint.getAlpha()));
        } else {
            pctx->fStrokePaint.setAlpha(
                SkScalarRoundToInt(opacity * pctx->fStrokePaint.getAlpha()));
        }
    } else {
        // Expensive, layer-based fall back.
        SkPaint opacityPaint;
        opacityPaint.setAlpha(opacity_to_alpha(opacity));
        // Balanced in the destructor, via restoreToCount().
        fCanvas->saveLayer(nullptr, &opacityPaint);
    }
}
示例#4
0
static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkIRect& srcRect, const SkRect& destRect, const SkXfermode::Mode& compOp)
{
#if PLATFORM(CHROMIUM)
    TRACE_EVENT("paintSkBitmap", platformContext, 0);
#endif
    SkPaint paint;
    paint.setXfermodeMode(compOp);
    paint.setFilterBitmap(true);
    paint.setAlpha(platformContext->getNormalizedAlpha());
    paint.setLooper(platformContext->getDrawLooper());
    // only antialias if we're rotated or skewed
    paint.setAntiAlias(hasNon90rotation(platformContext));

    SkCanvas* canvas = platformContext->canvas();

    ResamplingMode resampling;
    if (platformContext->isAccelerated())
        resampling = RESAMPLE_LINEAR;
    else
        resampling = platformContext->printing() ? RESAMPLE_NONE :
            computeResamplingMode(platformContext, bitmap, srcRect.width(), srcRect.height(), SkScalarToFloat(destRect.width()), SkScalarToFloat(destRect.height()));
    if (resampling == RESAMPLE_AWESOME) {
        drawResampledBitmap(*canvas, paint, bitmap, srcRect, destRect);
    } else {
        // No resampling necessary, we can just draw the bitmap. We want to
        // filter it if we decided to do linear interpolation above, or if there
        // is something interesting going on with the matrix (like a rotation).
        // Note: for serialization, we will want to subset the bitmap first so
        // we don't send extra pixels.
        canvas->drawBitmapRect(bitmap.bitmap(), &srcRect, destRect, &paint);
    }
    platformContext->didDrawRect(destRect, paint, &bitmap.bitmap());
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, onLayerOp_bufferless) {
    SkPaint layerPaint;
    layerPaint.setAlpha(128);
    OffscreenBuffer* buffer = nullptr;  // no providing a buffer, should hit rect fallback case
    LayerOp op(Rect(10, 10), Matrix4::identity(), nullptr, &layerPaint, &buffer);
    testUnmergedGlopDispatch(renderThread, &op,
                             [](const Glop& glop) { ADD_FAILURE() << "Nothing should happen"; }, 0);
}
// Make sure our blits always map src==0 to a noop, and src==FF to full opaque
static void test_00_FF(skiatest::Reporter* reporter) {
    static const int W = 256;

    static const SkBitmap::Config gDstConfig[] = {
        SkBitmap::kARGB_8888_Config,
        SkBitmap::kRGB_565_Config,
//        SkBitmap::kARGB_4444_Config,
//        SkBitmap::kA8_Config,
    };

    static const struct {
        SkColor     fSrc;
        SkColor     fDst;
        SkPMColor   fResult32;
        uint16_t    fResult16;
        uint8_t     fResult8;
    } gSrcRec[] = {
        { 0,            0,          0,                                    0,      0 },
        { 0,            0xFFFFFFFF, SkPackARGB32(0xFF, 0xFF, 0xFF, 0xFF), 0xFFFF, 0xFF },
        { 0xFFFFFFFF,   0,          SkPackARGB32(0xFF, 0xFF, 0xFF, 0xFF), 0xFFFF, 0xFF },
        { 0xFFFFFFFF,   0xFFFFFFFF, SkPackARGB32(0xFF, 0xFF, 0xFF, 0xFF), 0xFFFF, 0xFF },
    };

    SkPaint paint;

    SkBitmap srcBM;
    srcBM.setConfig(SkBitmap::kARGB_8888_Config, W, 1);
    srcBM.allocPixels();

    for (size_t i = 0; i < SK_ARRAY_COUNT(gDstConfig); i++) {
        SkBitmap dstBM;
        dstBM.setConfig(gDstConfig[i], W, 1);
        dstBM.allocPixels();
        
        SkCanvas canvas(dstBM);
        for (size_t j = 0; j < SK_ARRAY_COUNT(gSrcRec); j++) {
            srcBM.eraseColor(gSrcRec[j].fSrc);
            dstBM.eraseColor(gSrcRec[j].fDst);

            for (int k = 0; k < 4; k++) {
                bool dither = (k & 1) != 0;
                bool blend = (k & 2) != 0;
                if (gSrcRec[j].fSrc != 0 && blend) {
                    // can't make a numerical promise about blending anything
                    // but 0
                 //   continue;
                }
                paint.setDither(dither);
                paint.setAlpha(blend ? 0x80 : 0xFF);
                canvas.drawBitmap(srcBM, 0, 0, &paint);
                if (!check_color(dstBM, gSrcRec[j].fResult32, gSrcRec[j].fResult16,
                                 gSrcRec[j].fResult8, reporter)) {
                    SkDebugf("--- src index %d dither %d blend %d\n", j, dither, blend);
                }
            }
        }
    }
}
示例#7
0
static void r1(SkLayerRasterizer* rast, SkPaint& p) {
    rast->addLayer(p);

    p.setAlpha(0x40);
    p.setXfermodeMode(SkXfermode::kSrc_Mode);
    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(SK_Scalar1*2);
    rast->addLayer(p);
}
static inline void drawInnerPath(PlatformContextSkia* context, const SkPath& path, SkPaint& paint, int width)
{
#if PLATFORM(CHROMIUM) && OS(DARWIN)
    paint.setAlpha(128);
    paint.setStrokeWidth(width * 0.5f);
    context->canvas()->drawPath(path, paint);
    context->didDrawPath(path, paint);
#endif
}
示例#9
0
 virtual void onDraw(SkCanvas* canvas) {
     SkShader* s = 
         SkShader::CreateBitmapShader(fBM, SkShader::kRepeat_TileMode,
                                      SkShader::kMirror_TileMode);
     SkPaint paint;
     paint.setAlpha(0x80);
     paint.setShader(s)->unref();
     canvas->drawPaint(paint);
 }
    ShaderMaskBench(void* param, bool isOpaque, FontQuality fq) : INHERITED(param) {
        fFQ = fq;
        fText.set(STR);

        fPaint.setAntiAlias(kBW != fq);
        fPaint.setLCDRenderText(kLCD == fq);
        fPaint.setAlpha(isOpaque ? 0xFF : 0x80);
        fPaint.setShader(new SkColorShader)->unref();
    }
示例#11
0
    void draw_mode(SkCanvas* canvas, SkXfermode* mode, int alpha,
                   SkScalar x, SkScalar y) {
        SkPaint p;

        canvas->drawBitmap(fSrcB, x, y, &p);
        p.setAlpha(alpha);
        p.setXfermode(mode);
        canvas->drawBitmap(fDstB, x, y, &p);
    }
示例#12
0
static void r3(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) {
    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(SK_Scalar1*3);
    rastBuilder->addLayer(p);

    p.setAlpha(0x20);
    p.setStyle(SkPaint::kFill_Style);
    p.setXfermodeMode(SkXfermode::kSrc_Mode);
    rastBuilder->addLayer(p);
}
示例#13
0
    virtual void onDraw(SkCanvas* canvas, SkScalar opacity) {
        SkRect r;
        r.set(0, 0, this->getWidth(), this->getHeight());

        SkPaint paint;
        paint.setColor(fColor);
        paint.setAlpha(SkScalarRound(opacity * 255));

        canvas->drawRect(r, paint);
    }
static void initPaint(SkPaint& paint) {
    // Make sure the paint is opaque, color, alpha, filter, etc.
    // will be applied later when compositing the alpha8 texture
    paint.setColor(SK_ColorBLACK);
    paint.setAlpha(255);
    paint.setColorFilter(NULL);
    paint.setMaskFilter(NULL);
    paint.setShader(NULL);
    SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
    SkSafeUnref(paint.setXfermode(mode));
}
示例#15
0
    void drawHairlines(SkCanvas* canvas, const SkPath& path,
                       const SkPath& clipA, const SkPath& clipB) {
        SkPaint paint;
        paint.setAntiAlias(true);
        paint.setStyle(SkPaint::kStroke_Style);
        const SkAlpha fade = 0x33;

        // draw path in hairline
        paint.setColor(gPathColor);
        paint.setAlpha(fade);
        canvas->drawPath(path, paint);

        // draw clips in hair line
        paint.setColor(gClipAColor);
        paint.setAlpha(fade);
        canvas->drawPath(clipA, paint);
        paint.setColor(gClipBColor);
        paint.setAlpha(fade);
        canvas->drawPath(clipB, paint);
    }
示例#16
0
static void r7(SkLayerRasterizer* rast, SkPaint& p, SkScalar interp) {
    p.setPathEffect(makepe(interp, NULL))->unref();
    rast->addLayer(p);
#if 0
    p.setPathEffect(new InverseFillPE())->unref();
    p.setXfermodeMode(SkXfermode::kSrcIn_Mode);
    p.setXfermodeMode(SkXfermode::kClear_Mode);
    p.setAlpha((1 - interp) * 255);
    rast->addLayer(p);
#endif
}
示例#17
0
    testrast()
    {
        SkPaint paint;
        paint.setAntiAlias(true);

#if 0        
        paint.setStyle(SkPaint::kStroke_Style);
        paint.setStrokeWidth(SK_Scalar1*4);
        this->addLayer(paint);
    
        paint.setStrokeWidth(SK_Scalar1*1);
        paint.setXfermode(SkXfermode::kClear_Mode);
        this->addLayer(paint);
#else
        paint.setAlpha(0x66);
        this->addLayer(paint, SkIntToScalar(4), SkIntToScalar(4));
    
        paint.setAlpha(0xFF);
        this->addLayer(paint);
#endif
    }
void PlatformGraphicsContextSkia::drawBitmapRect(const SkBitmap& bitmap,
                                             const SkIRect* src, const SkRect& dst,
                                             CompositeOperator op)
{
    SkPaint paint;
    setupPaintCommon(&paint);
    paint.setAlpha(getNormalizedAlpha());
    paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(op));
    fixPaintForBitmapsThatMaySeam(&paint);

    mCanvas->drawBitmapRect(bitmap, src, dst, &paint);
}
示例#19
0
    void onDraw(int loops, SkCanvas* canvas) override {
        SkRandom rand;

        SkPaint paint;
        this->setupPaint(&paint);
        paint.setFilterQuality(fFilterQuality);
        paint.setAlpha(fAlpha);

        for (int i = 0; i < loops; i++) {
            canvas->drawBitmapRect(fBitmap, fSrcR, fDstR, &paint,
                                   SkCanvas::kStrict_SrcRectConstraint);
        }
    }
示例#20
0
void draw(SkCanvas* canvas) {
   SkPaint paint;
   SkPoint center = { 50, 50 };
   SkScalar radius = 50;
   const SkColor colors[] = { 0xFFFFFFFF, 0xFF000000 };
   paint.setShader(SkGradientShader::MakeRadial(center, radius, colors,
        nullptr, SK_ARRAY_COUNT(colors), SkTileMode::kClamp));
   for (SkScalar a : { 0.3f, 0.6f, 1.0f } ) {
       paint.setAlpha((int) (a * 255));
       canvas->drawCircle(center.fX, center.fY, radius, paint);
       canvas->translate(70, 70);
   }
}
static inline void drawOuterPath(PlatformContextSkia* context, const SkPath& path, SkPaint& paint, int width)
{
#if PLATFORM(CHROMIUM) && OS(DARWIN)
    paint.setAlpha(64);
    paint.setStrokeWidth(width);
    paint.setPathEffect(new SkCornerPathEffect((width - 1) * 0.5f))->unref();
#else
    paint.setStrokeWidth(1);
    paint.setPathEffect(new SkCornerPathEffect(1))->unref();
#endif
    context->canvas()->drawPath(path, paint);
    context->didDrawPath(path, paint);
}
static SkScalar drawCell(SkCanvas* canvas, SkBlendMode mode, SkAlpha a0, SkAlpha a1) {
    SkPaint paint;
    paint.setAntiAlias(true);

    SkRect r = SkRect::MakeWH(W, H);
    r.inset(W/10, H/10);

    paint.setColor(SK_ColorBLUE);
    paint.setAlpha(a0);
    canvas->drawOval(r, paint);

    paint.setColor(SK_ColorRED);
    paint.setAlpha(a1);
    paint.setBlendMode(mode);
    for (int angle = 0; angle < 24; ++angle) {
        SkScalar x = SkScalarCos(SkIntToScalar(angle) * (SK_ScalarPI * 2) / 24) * gWidth;
        SkScalar y = SkScalarSin(SkIntToScalar(angle) * (SK_ScalarPI * 2) / 24) * gHeight;
        paint.setStrokeWidth(SK_Scalar1 * angle * 2 / 24);
        canvas->drawLine(W/2, H/2, W/2 + x, H/2 + y, paint);
    }

    return H;
}
static void run_test(SkWStream* out, SkBlendMode mode, U8CPU alpha) {
    sk_sp<SkDocument> pdfDoc(SkDocument::MakePDF(out));
    SkCanvas* c = pdfDoc->beginPage(612.0f, 792.0f);
    SkPaint black;
    SkPaint background;
    background.setColor(SK_ColorWHITE);
    background.setAlpha(alpha);
    background.setBlendMode(mode);
    c->drawRect(SkRect::MakeWH(612.0f, 792.0f), background);
    c->drawRect(SkRect::MakeXYWH(36.0f, 36.0f, 9.0f, 9.0f), black);
    c->drawRect(SkRect::MakeXYWH(72.0f, 72.0f, 468.0f, 648.0f), background);
    c->drawRect(SkRect::MakeXYWH(108.0f, 108.0f, 9.0f, 9.0f), black);
    pdfDoc->close();
}
示例#24
0
static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkIRect& srcRect, const SkRect& destRect, const SkXfermode::Mode& compOp)
{
#if PLATFORM(CHROMIUM)
    TRACE_EVENT0("skia", "paintSkBitmap");
#endif
    SkPaint paint;
    paint.setXfermodeMode(compOp);
    paint.setAlpha(platformContext->getNormalizedAlpha());
    paint.setLooper(platformContext->getDrawLooper());
    // only antialias if we're rotated or skewed
    paint.setAntiAlias(hasNon90rotation(platformContext));

    SkCanvas* canvas = platformContext->canvas();

    ResamplingMode resampling;
    if (platformContext->isAccelerated())
        resampling = RESAMPLE_LINEAR;
    else if (platformContext->printing())
        resampling = RESAMPLE_NONE;
    else {
        // Take into account scale applied to the canvas when computing sampling mode (e.g. CSS scale or page scale).
        SkRect destRectTarget = destRect;
        if (!(canvas->getTotalMatrix().getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)))
            canvas->getTotalMatrix().mapRect(&destRectTarget, destRect);

        resampling = computeResamplingMode(canvas->getTotalMatrix(), bitmap, srcRect.width(), srcRect.height(),
                                           SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTarget.height()));
    }

    if (resampling == RESAMPLE_NONE) {
      // FIXME: This is to not break tests (it results in the filter bitmap flag
      // being set to true). We need to decide if we respect RESAMPLE_NONE
      // being returned from computeResamplingMode.
        resampling = RESAMPLE_LINEAR;
    }
    resampling = limitResamplingMode(platformContext, resampling);
    paint.setFilterBitmap(resampling == RESAMPLE_LINEAR);
    if (resampling == RESAMPLE_AWESOME)
        drawResampledBitmap(*canvas, paint, bitmap, srcRect, destRect);
    else {
        // No resampling necessary, we can just draw the bitmap. We want to
        // filter it if we decided to do linear interpolation above, or if there
        // is something interesting going on with the matrix (like a rotation).
        // Note: for serialization, we will want to subset the bitmap first so
        // we don't send extra pixels.
        canvas->drawBitmapRect(bitmap.bitmap(), &srcRect, destRect, &paint);
    }
    platformContext->didDrawRect(destRect, paint, &bitmap.bitmap());
}
示例#25
0
void drawPlatformFocusRing(const PrimitiveType& primitive, SkCanvas* canvas, SkColor color, int width)
{
    SkPaint paint;
    paint.setAntiAlias(true);
    paint.setStyle(SkPaint::kStroke_Style);
    paint.setColor(color);
    paint.setStrokeWidth(GraphicsContext::focusRingWidth(width));

#if OS(MACOSX)
    paint.setAlpha(64);
    const float cornerRadius = (width - 1) * 0.5f;
#else
    const float cornerRadius = 1;
#endif

    drawFocusRingPrimitive(primitive, canvas, paint, cornerRadius);

#if OS(MACOSX)
    // Inner part
    paint.setAlpha(128);
    paint.setStrokeWidth(paint.getStrokeWidth() * 0.5f);
    drawFocusRingPrimitive(primitive, canvas, paint, cornerRadius);
#endif
}
示例#26
0
static void r0(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) {
    p.setMaskFilter(SkBlurMaskFilter::Create(kNormal_SkBlurStyle,
                              SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(3))))->unref();
    rastBuilder->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));

    p.setMaskFilter(nullptr);
    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(SK_Scalar1);
    rastBuilder->addLayer(p);

    p.setAlpha(0x11);
    p.setStyle(SkPaint::kFill_Style);
    p.setXfermodeMode(SkXfermode::kSrc_Mode);
    rastBuilder->addLayer(p);
}
示例#27
0
static void r0(SkLayerRasterizer* rast, SkPaint& p) {
    p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3),
                                             SkBlurMaskFilter::kNormal_BlurStyle))->unref();
    rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));

    p.setMaskFilter(NULL);
    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(SK_Scalar1);
    rast->addLayer(p);

    p.setAlpha(0x11);
    p.setStyle(SkPaint::kFill_Style);
    p.setXfermodeMode(SkXfermode::kSrc_Mode);
    rast->addLayer(p);
}
void PlatformGraphicsContextSkia::drawBitmapPattern(
        const SkBitmap& bitmap, const SkMatrix& matrix,
        CompositeOperator compositeOp, const FloatRect& destRect)
{
    SkShader* shader = SkShader::CreateBitmapShader(bitmap,
                                                    SkShader::kRepeat_TileMode,
                                                    SkShader::kRepeat_TileMode);
    shader->setLocalMatrix(matrix);
    SkPaint paint;
    setupPaintCommon(&paint);
    paint.setAlpha(getNormalizedAlpha());
    paint.setShader(shader)->unref();
    paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp));
    fixPaintForBitmapsThatMaySeam(&paint);
    mCanvas->drawRect(destRect, paint);
}
示例#29
0
DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) {
    // This test is from crbug.com/344987.
    // The commands are:
    //   saveLayer with paint that modifies alpha
    //     drawBitmapRect
    //     drawBitmapRect
    //   restore
    // The bug was that this structure was modified so that:
    //  - The saveLayer and restore were eliminated
    //  - The alpha was only applied to the first drawBitmapRectToRect

    // This test draws blue and red squares inside a 50% transparent
    // layer.  Both colours should show up muted.
    // When the bug is present, the red square (the second bitmap)
    // shows upwith full opacity.

    SkBitmap blueBM;
    make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true);
    SkBitmap redBM;
    make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true);
    SkPaint semiTransparent;
    semiTransparent.setAlpha(0x80);

    SkPictureRecorder recorder;
    SkCanvas* canvas = recorder.beginRecording(100, 100);
    canvas->drawARGB(0, 0, 0, 0);

    canvas->saveLayer(0, &semiTransparent);
    canvas->drawBitmap(blueBM, 25, 25);
    canvas->drawBitmap(redBM, 50, 50);
    canvas->restore();

    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());

    // Now replay the picture back on another canvas
    // and check a couple of its pixels.
    SkBitmap replayBM;
    make_bm(&replayBM, 100, 100, SK_ColorBLACK, false);
    SkCanvas replayCanvas(replayBM);
    picture->playback(&replayCanvas);
    replayCanvas.flush();

    // With the bug present, at (55, 55) we would get a fully opaque red
    // intead of a dark red.
    REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
    REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
}
void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
{
    if (paintingDisabled())
        return;

    // We need the "alpha" layer flag here because the base layer is opaque
    // (the surface of the page) but layers on top may have transparent parts.
    // Without explicitly setting the alpha flag, the layer will inherit the
    // opaque setting of the base and some things won't work properly.
    SkCanvas::SaveFlags saveFlags = static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag);

    SkPaint layerPaint;
    layerPaint.setAlpha(static_cast<unsigned char>(opacity * 255));
    layerPaint.setXfermodeMode(platformContext()->getXfermodeMode());

    platformContext()->saveLayer(0, &layerPaint, saveFlags);
}