1/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkSurface_Base.h"
9#include "SkImagePriv.h"
10#include "SkCanvas.h"
11#include "SkGpuDevice.h"
12
13class SkSurface_Gpu : public SkSurface_Base {
14public:
15    SK_DECLARE_INST_COUNT(SkSurface_Gpu)
16
17    SkSurface_Gpu(GrRenderTarget*, bool cached, TextRenderMode trm);
18    virtual ~SkSurface_Gpu();
19
20    virtual SkCanvas* onNewCanvas() SK_OVERRIDE;
21    virtual SkSurface* onNewSurface(const SkImageInfo&) SK_OVERRIDE;
22    virtual SkImage* onNewImageSnapshot() SK_OVERRIDE;
23    virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y,
24                        const SkPaint*) SK_OVERRIDE;
25    virtual void onCopyOnWrite(ContentChangeMode) SK_OVERRIDE;
26    virtual void onDiscard() SK_OVERRIDE;
27
28private:
29    SkGpuDevice* fDevice;
30
31    typedef SkSurface_Base INHERITED;
32};
33
34///////////////////////////////////////////////////////////////////////////////
35
36SkSurface_Gpu::SkSurface_Gpu(GrRenderTarget* renderTarget, bool cached, TextRenderMode trm)
37        : INHERITED(renderTarget->width(), renderTarget->height()) {
38    int flags = 0;
39    flags |= cached ? SkGpuDevice::kCached_Flag : 0;
40    flags |= (kDistanceField_TextRenderMode == trm) ? SkGpuDevice::kDFFonts_Flag : 0;
41    fDevice = SkGpuDevice::Create(renderTarget, flags);
42
43    if (kRGB_565_GrPixelConfig != renderTarget->config()) {
44        fDevice->clear(0x0);
45    }
46}
47
48SkSurface_Gpu::~SkSurface_Gpu() {
49    SkSafeUnref(fDevice);
50}
51
52SkCanvas* SkSurface_Gpu::onNewCanvas() {
53    return SkNEW_ARGS(SkCanvas, (fDevice));
54}
55
56SkSurface* SkSurface_Gpu::onNewSurface(const SkImageInfo& info) {
57    GrRenderTarget* rt = fDevice->accessRenderTarget();
58    int sampleCount = rt->numSamples();
59    return SkSurface::NewRenderTarget(fDevice->context(), info, sampleCount);
60}
61
62SkImage* SkSurface_Gpu::onNewImageSnapshot() {
63    return SkImage::NewTexture(fDevice->accessBitmap(false));
64}
65
66void SkSurface_Gpu::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y,
67                              const SkPaint* paint) {
68    canvas->drawBitmap(fDevice->accessBitmap(false), x, y, paint);
69}
70
71// Create a new SkGpuDevice and, if necessary, copy the contents of the old
72// device into it. Note that this flushes the SkGpuDevice but
73// doesn't force an OpenGL flush.
74void SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) {
75    GrRenderTarget* rt = fDevice->accessRenderTarget();
76    // are we sharing our render target with the image?
77    SkASSERT(NULL != this->getCachedImage());
78    if (rt->asTexture() == SkTextureImageGetTexture(this->getCachedImage())) {
79        // We call createCompatibleDevice because it uses the texture cache. This isn't
80        // necessarily correct (http://skbug.com/2252), but never using the cache causes
81        // a Chromium regression. (http://crbug.com/344020)
82        SkGpuDevice* newDevice = static_cast<SkGpuDevice*>(
83            fDevice->createCompatibleDevice(fDevice->imageInfo()));
84        SkAutoTUnref<SkGpuDevice> aurd(newDevice);
85        if (kRetain_ContentChangeMode == mode) {
86            fDevice->context()->copyTexture(rt->asTexture(), newDevice->accessRenderTarget());
87        }
88        SkASSERT(NULL != this->getCachedCanvas());
89        SkASSERT(this->getCachedCanvas()->getDevice() == fDevice);
90
91        this->getCachedCanvas()->setRootDevice(newDevice);
92        SkRefCnt_SafeAssign(fDevice, newDevice);
93    } else if (kDiscard_ContentChangeMode == mode) {
94        this->SkSurface_Gpu::onDiscard();
95    }
96}
97
98void SkSurface_Gpu::onDiscard() {
99    fDevice->accessRenderTarget()->discard();
100}
101
102///////////////////////////////////////////////////////////////////////////////
103
104SkSurface* SkSurface::NewRenderTargetDirect(GrRenderTarget* target, TextRenderMode trm) {
105    if (NULL == target) {
106        return NULL;
107    }
108    return SkNEW_ARGS(SkSurface_Gpu, (target, false, trm));
109}
110
111SkSurface* SkSurface::NewRenderTarget(GrContext* ctx, const SkImageInfo& info, int sampleCount,
112                                      TextRenderMode trm) {
113    if (NULL == ctx) {
114        return NULL;
115    }
116
117    GrTextureDesc desc;
118    desc.fFlags = kRenderTarget_GrTextureFlagBit | kCheckAllocation_GrTextureFlagBit;
119    desc.fWidth = info.width();
120    desc.fHeight = info.height();
121    desc.fConfig = SkImageInfo2GrPixelConfig(info);
122    desc.fSampleCnt = sampleCount;
123
124    SkAutoTUnref<GrTexture> tex(ctx->createUncachedTexture(desc, NULL, 0));
125    if (NULL == tex) {
126        return NULL;
127    }
128
129    return SkNEW_ARGS(SkSurface_Gpu, (tex->asRenderTarget(), false, trm));
130}
131
132SkSurface* SkSurface::NewScratchRenderTarget(GrContext* ctx, const SkImageInfo& info,
133                                             int sampleCount, TextRenderMode trm) {
134    if (NULL == ctx) {
135        return NULL;
136    }
137
138    GrTextureDesc desc;
139    desc.fFlags = kRenderTarget_GrTextureFlagBit | kCheckAllocation_GrTextureFlagBit;
140    desc.fWidth = info.width();
141    desc.fHeight = info.height();
142    desc.fConfig = SkImageInfo2GrPixelConfig(info);
143    desc.fSampleCnt = sampleCount;
144
145    SkAutoTUnref<GrTexture> tex(ctx->lockAndRefScratchTexture(desc, GrContext::kExact_ScratchTexMatch));
146
147    if (NULL == tex) {
148        return NULL;
149    }
150
151    return SkNEW_ARGS(SkSurface_Gpu, (tex->asRenderTarget(), true, trm));
152}
153