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, const SkSurfaceProps*, bool doClear);
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, const SkSurfaceProps* props,
37                             bool doClear)
38        : INHERITED(renderTarget->width(), renderTarget->height(), props)
39{
40    int deviceFlags = 0;
41    deviceFlags |= cached ? SkGpuDevice::kCached_Flag : 0;
42    deviceFlags |= this->props().isUseDistanceFieldFonts() ? SkGpuDevice::kDFFonts_Flag : 0;
43    fDevice = SkGpuDevice::Create(renderTarget, this->props(), deviceFlags);
44
45    if (kRGB_565_GrPixelConfig != renderTarget->config() && doClear) {
46        fDevice->clear(0x0);
47    }
48}
49
50SkSurface_Gpu::~SkSurface_Gpu() {
51    SkSafeUnref(fDevice);
52}
53
54SkCanvas* SkSurface_Gpu::onNewCanvas() {
55    SkCanvas::InitFlags flags = SkCanvas::kDefault_InitFlags;
56    // When we think this works...
57//    flags |= SkCanvas::kConservativeRasterClip_InitFlag;
58
59    return SkNEW_ARGS(SkCanvas, (fDevice, &this->props(), flags));
60}
61
62SkSurface* SkSurface_Gpu::onNewSurface(const SkImageInfo& info) {
63    GrRenderTarget* rt = fDevice->accessRenderTarget();
64    int sampleCount = rt->numSamples();
65    return SkSurface::NewRenderTarget(fDevice->context(), info, sampleCount, &this->props());
66}
67
68SkImage* SkSurface_Gpu::onNewImageSnapshot() {
69    return SkImage::NewTexture(fDevice->accessBitmap(false));
70}
71
72void SkSurface_Gpu::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y,
73                              const SkPaint* paint) {
74    canvas->drawBitmap(fDevice->accessBitmap(false), x, y, paint);
75}
76
77// Create a new SkGpuDevice and, if necessary, copy the contents of the old
78// device into it. Note that this flushes the SkGpuDevice but
79// doesn't force an OpenGL flush.
80void SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) {
81    GrRenderTarget* rt = fDevice->accessRenderTarget();
82    // are we sharing our render target with the image?
83    SkASSERT(this->getCachedImage());
84    if (rt->asTexture() == SkTextureImageGetTexture(this->getCachedImage())) {
85        // We call createCompatibleDevice because it uses the texture cache. This isn't
86        // necessarily correct (http://skbug.com/2252), but never using the cache causes
87        // a Chromium regression. (http://crbug.com/344020)
88        SkGpuDevice* newDevice = static_cast<SkGpuDevice*>(
89            fDevice->createCompatibleDevice(fDevice->imageInfo()));
90        SkAutoTUnref<SkGpuDevice> aurd(newDevice);
91        if (kRetain_ContentChangeMode == mode) {
92            fDevice->context()->copyTexture(rt->asTexture(), newDevice->accessRenderTarget());
93        }
94        SkASSERT(this->getCachedCanvas());
95        SkASSERT(this->getCachedCanvas()->getDevice() == fDevice);
96
97        this->getCachedCanvas()->setRootDevice(newDevice);
98        SkRefCnt_SafeAssign(fDevice, newDevice);
99    } else if (kDiscard_ContentChangeMode == mode) {
100        this->SkSurface_Gpu::onDiscard();
101    }
102}
103
104void SkSurface_Gpu::onDiscard() {
105    fDevice->accessRenderTarget()->discard();
106}
107
108///////////////////////////////////////////////////////////////////////////////
109
110SkSurface* SkSurface::NewRenderTargetDirect(GrRenderTarget* target, const SkSurfaceProps* props) {
111    if (NULL == target) {
112        return NULL;
113    }
114    return SkNEW_ARGS(SkSurface_Gpu, (target, false, props, false));
115}
116
117SkSurface* SkSurface::NewRenderTarget(GrContext* ctx, const SkImageInfo& info, int sampleCount,
118                                      const SkSurfaceProps* props) {
119    if (NULL == ctx) {
120        return NULL;
121    }
122
123    GrTextureDesc desc;
124    desc.fFlags = kRenderTarget_GrTextureFlagBit | kCheckAllocation_GrTextureFlagBit;
125    desc.fWidth = info.width();
126    desc.fHeight = info.height();
127    desc.fConfig = SkImageInfo2GrPixelConfig(info);
128    desc.fSampleCnt = sampleCount;
129
130    SkAutoTUnref<GrTexture> tex(ctx->createUncachedTexture(desc, NULL, 0));
131    if (NULL == tex) {
132        return NULL;
133    }
134
135    return SkNEW_ARGS(SkSurface_Gpu, (tex->asRenderTarget(), false, props, true));
136}
137
138SkSurface* SkSurface::NewScratchRenderTarget(GrContext* ctx, const SkImageInfo& info,
139                                             int sampleCount, const SkSurfaceProps* props) {
140    if (NULL == ctx) {
141        return NULL;
142    }
143
144    GrTextureDesc desc;
145    desc.fFlags = kRenderTarget_GrTextureFlagBit | kCheckAllocation_GrTextureFlagBit;
146    desc.fWidth = info.width();
147    desc.fHeight = info.height();
148    desc.fConfig = SkImageInfo2GrPixelConfig(info);
149    desc.fSampleCnt = sampleCount;
150
151    SkAutoTUnref<GrTexture> tex(ctx->lockAndRefScratchTexture(desc, GrContext::kExact_ScratchTexMatch));
152
153    if (NULL == tex) {
154        return NULL;
155    }
156
157    return SkNEW_ARGS(SkSurface_Gpu, (tex->asRenderTarget(), true, props, true));
158}
159