blob: d6ebada82a57a3cfb12acd5a4016c42c5c4e1471 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
bsalomon@google.com27847de2011-02-22 20:59:41 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
bsalomon@google.com27847de2011-02-22 20:59:41 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
bsalomon@google.com1fadb202011-12-12 16:10:08 +000010#include "GrContext.h"
11
tomhudson@google.com278cbb42011-06-30 19:37:01 +000012#include "GrBufferAllocPool.h"
13#include "GrClipIterator.h"
bsalomon@google.com05ef5102011-05-02 21:14:59 +000014#include "GrGpu.h"
bsalomon@google.com27847de2011-02-22 20:59:41 +000015#include "GrIndexBuffer.h"
16#include "GrInOrderDrawBuffer.h"
bsalomon@google.com27847de2011-02-22 20:59:41 +000017#include "GrPathRenderer.h"
tomhudson@google.comd22b6e42011-06-24 15:53:40 +000018#include "GrPathUtils.h"
bsalomon@google.com50398bf2011-07-26 20:45:30 +000019#include "GrResourceCache.h"
bsalomon@google.com558a75b2011-08-08 17:01:14 +000020#include "GrStencilBuffer.h"
tomhudson@google.com278cbb42011-06-30 19:37:01 +000021#include "GrTextStrike.h"
bsalomon@google.com8c2fe992011-09-13 15:27:18 +000022#include "SkTLazy.h"
tomhudson@google.com0c8d93a2011-07-01 17:08:26 +000023#include "SkTrace.h"
bsalomon@google.com27847de2011-02-22 20:59:41 +000024
25#define DEFER_TEXT_RENDERING 1
26
27#define BATCH_RECT_TO_RECT (1 && !GR_STATIC_RECT_VB)
28
bsalomon@google.comd46e2422011-09-23 17:40:07 +000029// When we're using coverage AA but the blend is incompatible (given gpu
30// limitations) should we disable AA or draw wrong?
bsalomon@google.com950d7a82011-09-28 15:05:33 +000031#define DISABLE_COVERAGE_AA_FOR_BLEND 1
bsalomon@google.comd46e2422011-09-23 17:40:07 +000032
bsalomon@google.com8ccaddd2011-08-09 16:49:03 +000033static const size_t MAX_TEXTURE_CACHE_COUNT = 256;
34static const size_t MAX_TEXTURE_CACHE_BYTES = 16 * 1024 * 1024;
bsalomon@google.com27847de2011-02-22 20:59:41 +000035
36static const size_t DRAW_BUFFER_VBPOOL_BUFFER_SIZE = 1 << 18;
37static const int DRAW_BUFFER_VBPOOL_PREALLOC_BUFFERS = 4;
38
39// We are currently only batching Text and drawRectToRect, both
40// of which use the quad index buffer.
41static const size_t DRAW_BUFFER_IBPOOL_BUFFER_SIZE = 0;
42static const int DRAW_BUFFER_IBPOOL_PREALLOC_BUFFERS = 0;
43
bsalomon@google.combc4b6542011-11-19 13:56:11 +000044#define ASSERT_OWNED_RESOURCE(R) GrAssert(!(R) || (R)->getContext() == this)
45
bsalomon@google.com05ef5102011-05-02 21:14:59 +000046GrContext* GrContext::Create(GrEngine engine,
47 GrPlatform3DContext context3D) {
bsalomon@google.com27847de2011-02-22 20:59:41 +000048 GrContext* ctx = NULL;
49 GrGpu* fGpu = GrGpu::Create(engine, context3D);
50 if (NULL != fGpu) {
51 ctx = new GrContext(fGpu);
52 fGpu->unref();
53 }
54 return ctx;
55}
56
bsalomon@google.com27847de2011-02-22 20:59:41 +000057GrContext::~GrContext() {
bsalomon@google.com8fe72472011-03-30 21:26:44 +000058 this->flush();
bsalomon@google.com27847de2011-02-22 20:59:41 +000059 delete fTextureCache;
60 delete fFontCache;
61 delete fDrawBuffer;
62 delete fDrawBufferVBAllocPool;
bsalomon@google.comde6ac2d2011-02-25 21:50:42 +000063 delete fDrawBufferIBAllocPool;
bsalomon@google.com30085192011-08-19 15:42:31 +000064
bsalomon@google.com205d4602011-04-25 12:43:45 +000065 GrSafeUnref(fAAFillRectIndexBuffer);
66 GrSafeUnref(fAAStrokeRectIndexBuffer);
67 fGpu->unref();
bsalomon@google.com30085192011-08-19 15:42:31 +000068 GrSafeUnref(fPathRendererChain);
bsalomon@google.com27847de2011-02-22 20:59:41 +000069}
70
bsalomon@google.com8fe72472011-03-30 21:26:44 +000071void GrContext::contextLost() {
junov@google.com53a55842011-06-08 22:55:10 +000072 contextDestroyed();
73 this->setupDrawBuffer();
74}
75
76void GrContext::contextDestroyed() {
bsalomon@google.com205d4602011-04-25 12:43:45 +000077 // abandon first to so destructors
78 // don't try to free the resources in the API.
79 fGpu->abandonResources();
80
bsalomon@google.com30085192011-08-19 15:42:31 +000081 // a path renderer may be holding onto resources that
82 // are now unusable
83 GrSafeSetNull(fPathRendererChain);
84
bsalomon@google.com8fe72472011-03-30 21:26:44 +000085 delete fDrawBuffer;
86 fDrawBuffer = NULL;
bsalomon@google.com205d4602011-04-25 12:43:45 +000087
bsalomon@google.com8fe72472011-03-30 21:26:44 +000088 delete fDrawBufferVBAllocPool;
89 fDrawBufferVBAllocPool = NULL;
bsalomon@google.com205d4602011-04-25 12:43:45 +000090
bsalomon@google.com8fe72472011-03-30 21:26:44 +000091 delete fDrawBufferIBAllocPool;
92 fDrawBufferIBAllocPool = NULL;
93
bsalomon@google.com205d4602011-04-25 12:43:45 +000094 GrSafeSetNull(fAAFillRectIndexBuffer);
95 GrSafeSetNull(fAAStrokeRectIndexBuffer);
96
bsalomon@google.com8fe72472011-03-30 21:26:44 +000097 fTextureCache->removeAll();
98 fFontCache->freeAll();
99 fGpu->markContextDirty();
bsalomon@google.com8fe72472011-03-30 21:26:44 +0000100}
101
102void GrContext::resetContext() {
103 fGpu->markContextDirty();
104}
105
106void GrContext::freeGpuResources() {
107 this->flush();
108 fTextureCache->removeAll();
109 fFontCache->freeAll();
bsalomon@google.com30085192011-08-19 15:42:31 +0000110 // a path renderer may be holding onto resources
111 GrSafeSetNull(fPathRendererChain);
bsalomon@google.com27847de2011-02-22 20:59:41 +0000112}
113
twiz@google.com05e70242012-01-27 19:12:00 +0000114size_t GrContext::getGpuTextureCacheBytes() const {
115 return fTextureCache->getCachedResourceBytes();
116}
117
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000118////////////////////////////////////////////////////////////////////////////////
119
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000120int GrContext::PaintStageVertexLayoutBits(
121 const GrPaint& paint,
122 const bool hasTexCoords[GrPaint::kTotalStages]) {
123 int stageMask = paint.getActiveStageMask();
124 int layout = 0;
125 for (int i = 0; i < GrPaint::kTotalStages; ++i) {
126 if ((1 << i) & stageMask) {
127 if (NULL != hasTexCoords && hasTexCoords[i]) {
128 layout |= GrDrawTarget::StageTexCoordVertexLayoutBit(i, i);
129 } else {
130 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
131 }
132 }
133 }
134 return layout;
135}
136
137
138////////////////////////////////////////////////////////////////////////////////
139
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000140enum {
bsalomon@google.com558a75b2011-08-08 17:01:14 +0000141 // flags for textures
142 kNPOTBit = 0x1,
143 kFilterBit = 0x2,
144 kScratchBit = 0x4,
145
146 // resource type
147 kTextureBit = 0x8,
148 kStencilBufferBit = 0x10
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000149};
150
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000151GrTexture* GrContext::TextureCacheEntry::texture() const {
152 if (NULL == fEntry) {
153 return NULL;
154 } else {
155 return (GrTexture*) fEntry->resource();
156 }
157}
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000158
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000159namespace {
160// returns true if this is a "special" texture because of gpu NPOT limitations
161bool gen_texture_key_values(const GrGpu* gpu,
bsalomon@google.com1fadb202011-12-12 16:10:08 +0000162 const GrSamplerState* sampler,
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000163 GrContext::TextureKey clientKey,
164 int width,
165 int height,
bsalomon@google.com78d6cf92012-01-30 18:09:31 +0000166 int sampleCnt,
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000167 bool scratch,
168 uint32_t v[4]) {
169 GR_STATIC_ASSERT(sizeof(GrContext::TextureKey) == sizeof(uint64_t));
170 // we assume we only need 16 bits of width and height
171 // assert that texture creation will fail anyway if this assumption
172 // would cause key collisions.
bsalomon@google.com18c9c192011-09-22 21:01:31 +0000173 GrAssert(gpu->getCaps().fMaxTextureSize <= SK_MaxU16);
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000174 v[0] = clientKey & 0xffffffffUL;
175 v[1] = (clientKey >> 32) & 0xffffffffUL;
176 v[2] = width | (height << 16);
177
bsalomon@google.com78d6cf92012-01-30 18:09:31 +0000178 v[3] = (sampleCnt << 24);
179 GrAssert(sampleCnt >= 0 && sampleCnt < 256);
180
bsalomon@google.com18c9c192011-09-22 21:01:31 +0000181 if (!gpu->getCaps().fNPOTTextureTileSupport) {
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000182 bool isPow2 = GrIsPow2(width) && GrIsPow2(height);
183
bsalomon@google.com1fadb202011-12-12 16:10:08 +0000184 bool tiled = NULL != sampler &&
185 ((sampler->getWrapX() != GrSamplerState::kClamp_WrapMode) ||
186 (sampler->getWrapY() != GrSamplerState::kClamp_WrapMode));
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000187
188 if (tiled && !isPow2) {
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000189 v[3] |= kNPOTBit;
bsalomon@google.com1fadb202011-12-12 16:10:08 +0000190 if (GrSamplerState::kNearest_Filter != sampler->getFilter()) {
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000191 v[3] |= kFilterBit;
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000192 }
193 }
194 }
195
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000196 if (scratch) {
197 v[3] |= kScratchBit;
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000198 }
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000199
bsalomon@google.com558a75b2011-08-08 17:01:14 +0000200 v[3] |= kTextureBit;
201
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000202 return v[3] & kNPOTBit;
203}
bsalomon@google.com558a75b2011-08-08 17:01:14 +0000204
205// we should never have more than one stencil buffer with same combo of
206// (width,height,samplecount)
207void gen_stencil_key_values(int width, int height,
208 int sampleCnt, uint32_t v[4]) {
209 v[0] = width;
210 v[1] = height;
211 v[2] = sampleCnt;
212 v[3] = kStencilBufferBit;
213}
214
215void gen_stencil_key_values(const GrStencilBuffer* sb,
216 uint32_t v[4]) {
217 gen_stencil_key_values(sb->width(), sb->height(),
218 sb->numSamples(), v);
219}
bsalomon@google.com82c7bd82011-11-09 15:32:29 +0000220
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000221}
222
bsalomon@google.com1fadb202011-12-12 16:10:08 +0000223GrContext::TextureCacheEntry GrContext::findAndLockTexture(
224 TextureKey key,
225 int width,
226 int height,
227 const GrSamplerState* sampler) {
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000228 uint32_t v[4];
bsalomon@google.com78d6cf92012-01-30 18:09:31 +0000229 gen_texture_key_values(fGpu, sampler, key, width, height, 0, false, v);
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000230 GrResourceKey resourceKey(v);
bsalomon@google.com558a75b2011-08-08 17:01:14 +0000231 return TextureCacheEntry(fTextureCache->findAndLock(resourceKey,
232 GrResourceCache::kNested_LockType));
233}
234
bsalomon@google.comfb309512011-11-30 14:13:48 +0000235bool GrContext::isTextureInCache(TextureKey key,
236 int width,
237 int height,
bsalomon@google.com1fadb202011-12-12 16:10:08 +0000238 const GrSamplerState* sampler) const {
bsalomon@google.comfb309512011-11-30 14:13:48 +0000239 uint32_t v[4];
bsalomon@google.com78d6cf92012-01-30 18:09:31 +0000240 gen_texture_key_values(fGpu, sampler, key, width, height, 0, false, v);
bsalomon@google.comfb309512011-11-30 14:13:48 +0000241 GrResourceKey resourceKey(v);
242 return fTextureCache->hasKey(resourceKey);
243}
244
bsalomon@google.com558a75b2011-08-08 17:01:14 +0000245GrResourceEntry* GrContext::addAndLockStencilBuffer(GrStencilBuffer* sb) {
bsalomon@google.combc4b6542011-11-19 13:56:11 +0000246 ASSERT_OWNED_RESOURCE(sb);
bsalomon@google.com558a75b2011-08-08 17:01:14 +0000247 uint32_t v[4];
248 gen_stencil_key_values(sb, v);
249 GrResourceKey resourceKey(v);
250 return fTextureCache->createAndLock(resourceKey, sb);
251}
252
253GrStencilBuffer* GrContext::findStencilBuffer(int width, int height,
254 int sampleCnt) {
255 uint32_t v[4];
256 gen_stencil_key_values(width, height, sampleCnt, v);
257 GrResourceKey resourceKey(v);
258 GrResourceEntry* entry = fTextureCache->findAndLock(resourceKey,
259 GrResourceCache::kSingle_LockType);
260 if (NULL != entry) {
261 GrStencilBuffer* sb = (GrStencilBuffer*) entry->resource();
262 return sb;
263 } else {
264 return NULL;
265 }
266}
267
268void GrContext::unlockStencilBuffer(GrResourceEntry* sbEntry) {
bsalomon@google.combc4b6542011-11-19 13:56:11 +0000269 ASSERT_OWNED_RESOURCE(sbEntry->resource());
bsalomon@google.com558a75b2011-08-08 17:01:14 +0000270 fTextureCache->unlock(sbEntry);
bsalomon@google.com27847de2011-02-22 20:59:41 +0000271}
272
273static void stretchImage(void* dst,
274 int dstW,
275 int dstH,
276 void* src,
277 int srcW,
278 int srcH,
279 int bpp) {
280 GrFixed dx = (srcW << 16) / dstW;
281 GrFixed dy = (srcH << 16) / dstH;
282
283 GrFixed y = dy >> 1;
284
285 int dstXLimit = dstW*bpp;
286 for (int j = 0; j < dstH; ++j) {
287 GrFixed x = dx >> 1;
288 void* srcRow = (uint8_t*)src + (y>>16)*srcW*bpp;
289 void* dstRow = (uint8_t*)dst + j*dstW*bpp;
290 for (int i = 0; i < dstXLimit; i += bpp) {
291 memcpy((uint8_t*) dstRow + i,
292 (uint8_t*) srcRow + (x>>16)*bpp,
293 bpp);
294 x += dx;
295 }
296 y += dy;
297 }
298}
299
bsalomon@google.com1fadb202011-12-12 16:10:08 +0000300GrContext::TextureCacheEntry GrContext::createAndLockTexture(
301 TextureKey key,
302 const GrSamplerState* sampler,
303 const GrTextureDesc& desc,
304 void* srcData,
305 size_t rowBytes) {
tomhudson@google.com278cbb42011-06-30 19:37:01 +0000306 SK_TRACE_EVENT0("GrContext::createAndLockTexture");
bsalomon@google.com27847de2011-02-22 20:59:41 +0000307
308#if GR_DUMP_TEXTURE_UPLOAD
309 GrPrintf("GrContext::createAndLockTexture [%d %d]\n", desc.fWidth, desc.fHeight);
310#endif
311
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000312 TextureCacheEntry entry;
313 uint32_t v[4];
314 bool special = gen_texture_key_values(fGpu, sampler, key,
bsalomon@google.com78d6cf92012-01-30 18:09:31 +0000315 desc.fWidth, desc.fHeight,
316 desc.fSampleCnt, false, v);
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000317 GrResourceKey resourceKey(v);
bsalomon@google.com27847de2011-02-22 20:59:41 +0000318
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000319 if (special) {
bsalomon@google.com1fadb202011-12-12 16:10:08 +0000320 GrAssert(NULL != sampler);
321 TextureCacheEntry clampEntry = this->findAndLockTexture(key,
322 desc.fWidth,
323 desc.fHeight,
324 NULL);
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000325
326 if (NULL == clampEntry.texture()) {
bsalomon@google.com1fadb202011-12-12 16:10:08 +0000327 clampEntry = this->createAndLockTexture(key, NULL, desc,
328 srcData, rowBytes);
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000329 GrAssert(NULL != clampEntry.texture());
330 if (NULL == clampEntry.texture()) {
331 return entry;
bsalomon@google.com27847de2011-02-22 20:59:41 +0000332 }
333 }
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000334 GrTextureDesc rtDesc = desc;
335 rtDesc.fFlags = rtDesc.fFlags |
336 kRenderTarget_GrTextureFlagBit |
337 kNoStencil_GrTextureFlagBit;
bsalomon@google.com99621082011-11-15 16:47:16 +0000338 rtDesc.fWidth = GrNextPow2(GrMax(desc.fWidth, 64));
339 rtDesc.fHeight = GrNextPow2(GrMax(desc.fHeight, 64));
bsalomon@google.com27847de2011-02-22 20:59:41 +0000340
341 GrTexture* texture = fGpu->createTexture(rtDesc, NULL, 0);
342
343 if (NULL != texture) {
344 GrDrawTarget::AutoStateRestore asr(fGpu);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000345 GrDrawState* drawState = fGpu->drawState();
bsalomon@google.com52a5dcb2012-01-17 16:01:37 +0000346 drawState->reset();
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000347 drawState->setRenderTarget(texture->asRenderTarget());
348 drawState->setTexture(0, clampEntry.texture());
bsalomon@google.com82c7bd82011-11-09 15:32:29 +0000349
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000350 GrSamplerState::Filter filter;
351 // if filtering is not desired then we want to ensure all
352 // texels in the resampled image are copies of texels from
353 // the original.
bsalomon@google.com1fadb202011-12-12 16:10:08 +0000354 if (GrSamplerState::kNearest_Filter == sampler->getFilter()) {
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000355 filter = GrSamplerState::kNearest_Filter;
356 } else {
357 filter = GrSamplerState::kBilinear_Filter;
358 }
bsalomon@google.com1e266f82011-12-12 16:11:33 +0000359 drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode,
360 filter);
bsalomon@google.com27847de2011-02-22 20:59:41 +0000361
362 static const GrVertexLayout layout =
363 GrDrawTarget::StageTexCoordVertexLayoutBit(0,0);
364 GrDrawTarget::AutoReleaseGeometry arg(fGpu, layout, 4, 0);
365
366 if (arg.succeeded()) {
367 GrPoint* verts = (GrPoint*) arg.vertices();
368 verts[0].setIRectFan(0, 0,
369 texture->width(),
370 texture->height(),
371 2*sizeof(GrPoint));
372 verts[1].setIRectFan(0, 0, 1, 1, 2*sizeof(GrPoint));
373 fGpu->drawNonIndexed(kTriangleFan_PrimitiveType,
374 0, 4);
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000375 entry.set(fTextureCache->createAndLock(resourceKey, texture));
bsalomon@google.com27847de2011-02-22 20:59:41 +0000376 }
bsalomon@google.com1da07462011-03-10 14:51:57 +0000377 texture->releaseRenderTarget();
bsalomon@google.com27847de2011-02-22 20:59:41 +0000378 } else {
379 // TODO: Our CPU stretch doesn't filter. But we create separate
380 // stretched textures when the sampler state is either filtered or
381 // not. Either implement filtered stretch blit on CPU or just create
382 // one when FBO case fails.
383
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000384 rtDesc.fFlags = kNone_GrTextureFlags;
bsalomon@google.com27847de2011-02-22 20:59:41 +0000385 // no longer need to clamp at min RT size.
386 rtDesc.fWidth = GrNextPow2(desc.fWidth);
387 rtDesc.fHeight = GrNextPow2(desc.fHeight);
bsalomon@google.com64c4fe42011-11-05 14:51:01 +0000388 int bpp = GrBytesPerPixel(desc.fConfig);
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000389 SkAutoSMalloc<128*128*4> stretchedPixels(bpp *
bsalomon@google.com27847de2011-02-22 20:59:41 +0000390 rtDesc.fWidth *
391 rtDesc.fHeight);
392 stretchImage(stretchedPixels.get(), rtDesc.fWidth, rtDesc.fHeight,
393 srcData, desc.fWidth, desc.fHeight, bpp);
394
395 size_t stretchedRowBytes = rtDesc.fWidth * bpp;
396
397 GrTexture* texture = fGpu->createTexture(rtDesc,
398 stretchedPixels.get(),
399 stretchedRowBytes);
400 GrAssert(NULL != texture);
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000401 entry.set(fTextureCache->createAndLock(resourceKey, texture));
bsalomon@google.com27847de2011-02-22 20:59:41 +0000402 }
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000403 fTextureCache->unlock(clampEntry.cacheEntry());
bsalomon@google.com27847de2011-02-22 20:59:41 +0000404
405 } else {
406 GrTexture* texture = fGpu->createTexture(desc, srcData, rowBytes);
407 if (NULL != texture) {
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000408 entry.set(fTextureCache->createAndLock(resourceKey, texture));
bsalomon@google.com27847de2011-02-22 20:59:41 +0000409 }
410 }
411 return entry;
412}
413
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000414namespace {
415inline void gen_scratch_tex_key_values(const GrGpu* gpu,
416 const GrTextureDesc& desc,
417 uint32_t v[4]) {
418 // Instead of a client-provided key of the texture contents
419 // we create a key of from the descriptor.
bsalomon@google.com78d6cf92012-01-30 18:09:31 +0000420 GrContext::TextureKey descKey = (desc.fFlags << 8) |
bsalomon@google.com64c4fe42011-11-05 14:51:01 +0000421 ((uint64_t) desc.fConfig << 32);
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000422 // this code path isn't friendly to tiling with NPOT restricitons
423 // We just pass ClampNoFilter()
bsalomon@google.com1fadb202011-12-12 16:10:08 +0000424 gen_texture_key_values(gpu, NULL, descKey, desc.fWidth,
bsalomon@google.com78d6cf92012-01-30 18:09:31 +0000425 desc.fHeight, desc.fSampleCnt, true, v);
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000426}
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000427}
428
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000429GrContext::TextureCacheEntry GrContext::lockScratchTexture(
430 const GrTextureDesc& inDesc,
431 ScratchTexMatch match) {
432
bsalomon@google.comb5b31682011-06-16 18:05:35 +0000433 GrTextureDesc desc = inDesc;
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000434 if (kExact_ScratchTexMatch != match) {
435 // bin by pow2 with a reasonable min
436 static const int MIN_SIZE = 256;
437 desc.fWidth = GrMax(MIN_SIZE, GrNextPow2(desc.fWidth));
438 desc.fHeight = GrMax(MIN_SIZE, GrNextPow2(desc.fHeight));
439 }
bsalomon@google.comb5b31682011-06-16 18:05:35 +0000440
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000441 GrResourceEntry* entry;
bsalomon@google.comb5b31682011-06-16 18:05:35 +0000442 int origWidth = desc.fWidth;
443 int origHeight = desc.fHeight;
444 bool doubledW = false;
445 bool doubledH = false;
446
447 do {
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000448 uint32_t v[4];
449 gen_scratch_tex_key_values(fGpu, desc, v);
450 GrResourceKey key(v);
bsalomon@google.com558a75b2011-08-08 17:01:14 +0000451 entry = fTextureCache->findAndLock(key,
452 GrResourceCache::kNested_LockType);
bsalomon@google.comb5b31682011-06-16 18:05:35 +0000453 // if we miss, relax the fit of the flags...
454 // then try doubling width... then height.
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000455 if (NULL != entry || kExact_ScratchTexMatch == match) {
bsalomon@google.comb5b31682011-06-16 18:05:35 +0000456 break;
457 }
458 if (!(desc.fFlags & kRenderTarget_GrTextureFlagBit)) {
459 desc.fFlags = desc.fFlags | kRenderTarget_GrTextureFlagBit;
460 } else if (desc.fFlags & kNoStencil_GrTextureFlagBit) {
461 desc.fFlags = desc.fFlags & ~kNoStencil_GrTextureFlagBit;
462 } else if (!doubledW) {
463 desc.fFlags = inDesc.fFlags;
464 desc.fWidth *= 2;
465 doubledW = true;
466 } else if (!doubledH) {
467 desc.fFlags = inDesc.fFlags;
468 desc.fWidth = origWidth;
469 desc.fHeight *= 2;
470 doubledH = true;
471 } else {
472 break;
473 }
474
475 } while (true);
476
477 if (NULL == entry) {
478 desc.fFlags = inDesc.fFlags;
479 desc.fWidth = origWidth;
480 desc.fHeight = origHeight;
481 GrTexture* texture = fGpu->createTexture(desc, NULL, 0);
482 if (NULL != texture) {
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000483 uint32_t v[4];
484 gen_scratch_tex_key_values(fGpu, desc, v);
485 GrResourceKey key(v);
bsalomon@google.comb5b31682011-06-16 18:05:35 +0000486 entry = fTextureCache->createAndLock(key, texture);
487 }
488 }
489
490 // If the caller gives us the same desc/sampler twice we don't want
491 // to return the same texture the second time (unless it was previously
492 // released). So we detach the entry from the cache and reattach at release.
493 if (NULL != entry) {
494 fTextureCache->detach(entry);
495 }
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000496 return TextureCacheEntry(entry);
bsalomon@google.comb5b31682011-06-16 18:05:35 +0000497}
498
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000499void GrContext::unlockTexture(TextureCacheEntry entry) {
bsalomon@google.combc4b6542011-11-19 13:56:11 +0000500 ASSERT_OWNED_RESOURCE(entry.texture());
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000501 // If this is a scratch texture we detached it from the cache
502 // while it was locked (to avoid two callers simultaneously getting
503 // the same texture).
504 if (kScratchBit & entry.cacheEntry()->key().getValue32(3)) {
505 fTextureCache->reattachAndUnlock(entry.cacheEntry());
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000506 } else {
bsalomon@google.com50398bf2011-07-26 20:45:30 +0000507 fTextureCache->unlock(entry.cacheEntry());
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000508 }
bsalomon@google.com27847de2011-02-22 20:59:41 +0000509}
510
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000511GrTexture* GrContext::createUncachedTexture(const GrTextureDesc& desc,
bsalomon@google.com27847de2011-02-22 20:59:41 +0000512 void* srcData,
513 size_t rowBytes) {
514 return fGpu->createTexture(desc, srcData, rowBytes);
515}
516
517void GrContext::getTextureCacheLimits(int* maxTextures,
518 size_t* maxTextureBytes) const {
519 fTextureCache->getLimits(maxTextures, maxTextureBytes);
520}
521
522void GrContext::setTextureCacheLimits(int maxTextures, size_t maxTextureBytes) {
523 fTextureCache->setLimits(maxTextures, maxTextureBytes);
524}
525
bsalomon@google.com91958362011-06-13 17:58:13 +0000526int GrContext::getMaxTextureSize() const {
bsalomon@google.com18c9c192011-09-22 21:01:31 +0000527 return fGpu->getCaps().fMaxTextureSize;
bsalomon@google.com91958362011-06-13 17:58:13 +0000528}
529
530int GrContext::getMaxRenderTargetSize() const {
bsalomon@google.com18c9c192011-09-22 21:01:31 +0000531 return fGpu->getCaps().fMaxRenderTargetSize;
bsalomon@google.com27847de2011-02-22 20:59:41 +0000532}
533
534///////////////////////////////////////////////////////////////////////////////
535
bsalomon@google.come269f212011-11-07 13:29:52 +0000536GrTexture* GrContext::createPlatformTexture(const GrPlatformTextureDesc& desc) {
537 return fGpu->createPlatformTexture(desc);
538}
539
540GrRenderTarget* GrContext::createPlatformRenderTarget(const GrPlatformRenderTargetDesc& desc) {
541 return fGpu->createPlatformRenderTarget(desc);
542}
543
bsalomon@google.com5877ffd2011-04-11 17:58:48 +0000544///////////////////////////////////////////////////////////////////////////////
545
bsalomon@google.com1fadb202011-12-12 16:10:08 +0000546bool GrContext::supportsIndex8PixelConfig(const GrSamplerState* sampler,
bsalomon@google.com1f221a72011-08-23 20:54:07 +0000547 int width, int height) const {
bsalomon@google.com18c9c192011-09-22 21:01:31 +0000548 const GrDrawTarget::Caps& caps = fGpu->getCaps();
549 if (!caps.f8BitPaletteSupport) {
bsalomon@google.com27847de2011-02-22 20:59:41 +0000550 return false;
551 }
552
bsalomon@google.com27847de2011-02-22 20:59:41 +0000553 bool isPow2 = GrIsPow2(width) && GrIsPow2(height);
554
555 if (!isPow2) {
bsalomon@google.com1fadb202011-12-12 16:10:08 +0000556 bool tiled = NULL != sampler &&
557 (sampler->getWrapX() != GrSamplerState::kClamp_WrapMode ||
558 sampler->getWrapY() != GrSamplerState::kClamp_WrapMode);
bsalomon@google.com18c9c192011-09-22 21:01:31 +0000559 if (tiled && !caps.fNPOTTextureTileSupport) {
bsalomon@google.com27847de2011-02-22 20:59:41 +0000560 return false;
561 }
562 }
563 return true;
564}
565
566////////////////////////////////////////////////////////////////////////////////
567
bsalomon@google.com05ef5102011-05-02 21:14:59 +0000568const GrClip& GrContext::getClip() const { return fGpu->getClip(); }
569
bsalomon@google.com27847de2011-02-22 20:59:41 +0000570void GrContext::setClip(const GrClip& clip) {
571 fGpu->setClip(clip);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000572 fGpu->drawState()->enableState(GrDrawState::kClip_StateBit);
bsalomon@google.com27847de2011-02-22 20:59:41 +0000573}
574
575void GrContext::setClip(const GrIRect& rect) {
576 GrClip clip;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000577 clip.setFromIRect(rect);
bsalomon@google.com27847de2011-02-22 20:59:41 +0000578 fGpu->setClip(clip);
579}
580
581////////////////////////////////////////////////////////////////////////////////
582
bsalomon@google.com6aa25c32011-04-27 19:55:29 +0000583void GrContext::clear(const GrIRect* rect, const GrColor color) {
bsalomon@google.com398109c2011-04-14 18:40:27 +0000584 this->flush();
bsalomon@google.com6aa25c32011-04-27 19:55:29 +0000585 fGpu->clear(rect, color);
bsalomon@google.com27847de2011-02-22 20:59:41 +0000586}
587
588void GrContext::drawPaint(const GrPaint& paint) {
589 // set rect to be big enough to fill the space, but not super-huge, so we
590 // don't overflow fixed-point implementations
bsalomon@google.comd302f142011-03-03 13:54:13 +0000591 GrRect r;
592 r.setLTRB(0, 0,
593 GrIntToScalar(getRenderTarget()->width()),
594 GrIntToScalar(getRenderTarget()->height()));
bsalomon@google.com27847de2011-02-22 20:59:41 +0000595 GrMatrix inverse;
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000596 SkTLazy<GrPaint> tmpPaint;
597 const GrPaint* p = &paint;
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000598 GrDrawState* drawState = fGpu->drawState();
599 GrAutoMatrix am;
600
bsalomon@google.com4f83be82011-09-12 13:52:51 +0000601 // We attempt to map r by the inverse matrix and draw that. mapRect will
602 // map the four corners and bound them with a new rect. This will not
603 // produce a correct result for some perspective matrices.
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000604 if (!this->getMatrix().hasPerspective()) {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000605 if (!drawState->getViewInverse(&inverse)) {
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000606 GrPrintf("Could not invert matrix");
607 return;
608 }
bsalomon@google.com27847de2011-02-22 20:59:41 +0000609 inverse.mapRect(&r);
610 } else {
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000611 if (paint.getActiveMaskStageMask() || paint.getActiveStageMask()) {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000612 if (!drawState->getViewInverse(&inverse)) {
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000613 GrPrintf("Could not invert matrix");
614 return;
615 }
616 tmpPaint.set(paint);
617 tmpPaint.get()->preConcatActiveSamplerMatrices(inverse);
618 p = tmpPaint.get();
619 }
bsalomon@google.com4f83be82011-09-12 13:52:51 +0000620 am.set(this, GrMatrix::I());
bsalomon@google.com27847de2011-02-22 20:59:41 +0000621 }
bsalomon@google.com4f83be82011-09-12 13:52:51 +0000622 // by definition this fills the entire clip, no need for AA
623 if (paint.fAntiAlias) {
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000624 if (!tmpPaint.isValid()) {
625 tmpPaint.set(paint);
626 p = tmpPaint.get();
627 }
628 GrAssert(p == tmpPaint.get());
629 tmpPaint.get()->fAntiAlias = false;
bsalomon@google.com4f83be82011-09-12 13:52:51 +0000630 }
631 this->drawRect(*p, r);
bsalomon@google.com27847de2011-02-22 20:59:41 +0000632}
633
bsalomon@google.com205d4602011-04-25 12:43:45 +0000634////////////////////////////////////////////////////////////////////////////////
635
bsalomon@google.comd46e2422011-09-23 17:40:07 +0000636namespace {
637inline bool disable_coverage_aa_for_blend(GrDrawTarget* target) {
638 return DISABLE_COVERAGE_AA_FOR_BLEND && !target->canApplyCoverage();
639}
640}
641
bsalomon@google.com06afe7b2011-04-26 15:31:40 +0000642////////////////////////////////////////////////////////////////////////////////
643
bsalomon@google.com27847de2011-02-22 20:59:41 +0000644/* create a triangle strip that strokes the specified triangle. There are 8
645 unique vertices, but we repreat the last 2 to close up. Alternatively we
646 could use an indices array, and then only send 8 verts, but not sure that
647 would be faster.
648 */
bsalomon@google.com205d4602011-04-25 12:43:45 +0000649static void setStrokeRectStrip(GrPoint verts[10], GrRect rect,
bsalomon@google.com27847de2011-02-22 20:59:41 +0000650 GrScalar width) {
651 const GrScalar rad = GrScalarHalf(width);
bsalomon@google.com205d4602011-04-25 12:43:45 +0000652 rect.sort();
bsalomon@google.com27847de2011-02-22 20:59:41 +0000653
654 verts[0].set(rect.fLeft + rad, rect.fTop + rad);
655 verts[1].set(rect.fLeft - rad, rect.fTop - rad);
656 verts[2].set(rect.fRight - rad, rect.fTop + rad);
657 verts[3].set(rect.fRight + rad, rect.fTop - rad);
658 verts[4].set(rect.fRight - rad, rect.fBottom - rad);
659 verts[5].set(rect.fRight + rad, rect.fBottom + rad);
660 verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
661 verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
662 verts[8] = verts[0];
663 verts[9] = verts[1];
664}
665
bsalomon@google.com205d4602011-04-25 12:43:45 +0000666static void setInsetFan(GrPoint* pts, size_t stride,
667 const GrRect& r, GrScalar dx, GrScalar dy) {
668 pts->setRectFan(r.fLeft + dx, r.fTop + dy, r.fRight - dx, r.fBottom - dy, stride);
669}
670
671static const uint16_t gFillAARectIdx[] = {
672 0, 1, 5, 5, 4, 0,
673 1, 2, 6, 6, 5, 1,
674 2, 3, 7, 7, 6, 2,
675 3, 0, 4, 4, 7, 3,
676 4, 5, 6, 6, 7, 4,
677};
678
679int GrContext::aaFillRectIndexCount() const {
680 return GR_ARRAY_COUNT(gFillAARectIdx);
681}
682
683GrIndexBuffer* GrContext::aaFillRectIndexBuffer() {
684 if (NULL == fAAFillRectIndexBuffer) {
685 fAAFillRectIndexBuffer = fGpu->createIndexBuffer(sizeof(gFillAARectIdx),
686 false);
bsalomon@google.com9b09c9e2011-08-31 13:33:40 +0000687 if (NULL != fAAFillRectIndexBuffer) {
688 #if GR_DEBUG
689 bool updated =
690 #endif
691 fAAFillRectIndexBuffer->updateData(gFillAARectIdx,
692 sizeof(gFillAARectIdx));
693 GR_DEBUGASSERT(updated);
694 }
bsalomon@google.com205d4602011-04-25 12:43:45 +0000695 }
696 return fAAFillRectIndexBuffer;
697}
698
699static const uint16_t gStrokeAARectIdx[] = {
700 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
701 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
702 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
703 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
704
705 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
706 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
707 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
708 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
709
710 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
711 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
712 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
713 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
714};
715
716int GrContext::aaStrokeRectIndexCount() const {
717 return GR_ARRAY_COUNT(gStrokeAARectIdx);
718}
719
720GrIndexBuffer* GrContext::aaStrokeRectIndexBuffer() {
721 if (NULL == fAAStrokeRectIndexBuffer) {
722 fAAStrokeRectIndexBuffer = fGpu->createIndexBuffer(sizeof(gStrokeAARectIdx),
723 false);
bsalomon@google.com9b09c9e2011-08-31 13:33:40 +0000724 if (NULL != fAAStrokeRectIndexBuffer) {
725 #if GR_DEBUG
726 bool updated =
727 #endif
728 fAAStrokeRectIndexBuffer->updateData(gStrokeAARectIdx,
729 sizeof(gStrokeAARectIdx));
730 GR_DEBUGASSERT(updated);
731 }
bsalomon@google.com205d4602011-04-25 12:43:45 +0000732 }
733 return fAAStrokeRectIndexBuffer;
734}
735
bsalomon@google.coma3108262011-10-10 14:08:47 +0000736static GrVertexLayout aa_rect_layout(const GrDrawTarget* target,
737 bool useCoverage) {
738 GrVertexLayout layout = 0;
tomhudson@google.com93813632011-10-27 20:21:16 +0000739 for (int s = 0; s < GrDrawState::kNumStages; ++s) {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000740 if (NULL != target->getDrawState().getTexture(s)) {
bsalomon@google.coma3108262011-10-10 14:08:47 +0000741 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
742 }
743 }
744 if (useCoverage) {
745 layout |= GrDrawTarget::kCoverage_VertexLayoutBit;
746 } else {
747 layout |= GrDrawTarget::kColor_VertexLayoutBit;
748 }
749 return layout;
750}
751
bsalomon@google.com205d4602011-04-25 12:43:45 +0000752void GrContext::fillAARect(GrDrawTarget* target,
bsalomon@google.coma3108262011-10-10 14:08:47 +0000753 const GrRect& devRect,
754 bool useVertexCoverage) {
755 GrVertexLayout layout = aa_rect_layout(target, useVertexCoverage);
bsalomon@google.com205d4602011-04-25 12:43:45 +0000756
757 size_t vsize = GrDrawTarget::VertexSize(layout);
758
759 GrDrawTarget::AutoReleaseGeometry geo(target, layout, 8, 0);
bsalomon@google.com6513cd02011-08-05 20:12:30 +0000760 if (!geo.succeeded()) {
761 GrPrintf("Failed to get space for vertices!\n");
762 return;
763 }
bsalomon@google.com9b09c9e2011-08-31 13:33:40 +0000764 GrIndexBuffer* indexBuffer = this->aaFillRectIndexBuffer();
765 if (NULL == indexBuffer) {
766 GrPrintf("Failed to create index buffer!\n");
767 return;
768 }
bsalomon@google.com205d4602011-04-25 12:43:45 +0000769
770 intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices());
771
772 GrPoint* fan0Pos = reinterpret_cast<GrPoint*>(verts);
773 GrPoint* fan1Pos = reinterpret_cast<GrPoint*>(verts + 4 * vsize);
774
775 setInsetFan(fan0Pos, vsize, devRect, -GR_ScalarHalf, -GR_ScalarHalf);
776 setInsetFan(fan1Pos, vsize, devRect, GR_ScalarHalf, GR_ScalarHalf);
777
778 verts += sizeof(GrPoint);
779 for (int i = 0; i < 4; ++i) {
780 *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
781 }
782
bsalomon@google.coma3108262011-10-10 14:08:47 +0000783 GrColor innerColor;
784 if (useVertexCoverage) {
bsalomon@google.come10f6fd2011-10-11 20:15:26 +0000785 innerColor = 0xffffffff;
bsalomon@google.coma3108262011-10-10 14:08:47 +0000786 } else {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000787 innerColor = target->getDrawState().getColor();
bsalomon@google.coma3108262011-10-10 14:08:47 +0000788 }
789
bsalomon@google.com205d4602011-04-25 12:43:45 +0000790 verts += 4 * vsize;
791 for (int i = 0; i < 4; ++i) {
792 *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor;
793 }
794
bsalomon@google.com9b09c9e2011-08-31 13:33:40 +0000795 target->setIndexSourceToBuffer(indexBuffer);
bsalomon@google.com205d4602011-04-25 12:43:45 +0000796
797 target->drawIndexed(kTriangles_PrimitiveType, 0,
798 0, 8, this->aaFillRectIndexCount());
799}
800
bsalomon@google.coma3108262011-10-10 14:08:47 +0000801void GrContext::strokeAARect(GrDrawTarget* target,
802 const GrRect& devRect,
803 const GrVec& devStrokeSize,
804 bool useVertexCoverage) {
bsalomon@google.com205d4602011-04-25 12:43:45 +0000805 const GrScalar& dx = devStrokeSize.fX;
806 const GrScalar& dy = devStrokeSize.fY;
807 const GrScalar rx = GrMul(dx, GR_ScalarHalf);
808 const GrScalar ry = GrMul(dy, GR_ScalarHalf);
809
bsalomon@google.com205d4602011-04-25 12:43:45 +0000810 GrScalar spare;
811 {
812 GrScalar w = devRect.width() - dx;
813 GrScalar h = devRect.height() - dy;
814 spare = GrMin(w, h);
815 }
816
817 if (spare <= 0) {
818 GrRect r(devRect);
819 r.inset(-rx, -ry);
bsalomon@google.coma3108262011-10-10 14:08:47 +0000820 fillAARect(target, r, useVertexCoverage);
bsalomon@google.com205d4602011-04-25 12:43:45 +0000821 return;
822 }
bsalomon@google.coma3108262011-10-10 14:08:47 +0000823 GrVertexLayout layout = aa_rect_layout(target, useVertexCoverage);
bsalomon@google.com205d4602011-04-25 12:43:45 +0000824 size_t vsize = GrDrawTarget::VertexSize(layout);
825
826 GrDrawTarget::AutoReleaseGeometry geo(target, layout, 16, 0);
bsalomon@google.com6513cd02011-08-05 20:12:30 +0000827 if (!geo.succeeded()) {
828 GrPrintf("Failed to get space for vertices!\n");
829 return;
830 }
bsalomon@google.com9b09c9e2011-08-31 13:33:40 +0000831 GrIndexBuffer* indexBuffer = this->aaStrokeRectIndexBuffer();
832 if (NULL == indexBuffer) {
833 GrPrintf("Failed to create index buffer!\n");
834 return;
835 }
bsalomon@google.com205d4602011-04-25 12:43:45 +0000836
837 intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices());
838
839 GrPoint* fan0Pos = reinterpret_cast<GrPoint*>(verts);
840 GrPoint* fan1Pos = reinterpret_cast<GrPoint*>(verts + 4 * vsize);
841 GrPoint* fan2Pos = reinterpret_cast<GrPoint*>(verts + 8 * vsize);
842 GrPoint* fan3Pos = reinterpret_cast<GrPoint*>(verts + 12 * vsize);
843
844 setInsetFan(fan0Pos, vsize, devRect, -rx - GR_ScalarHalf, -ry - GR_ScalarHalf);
845 setInsetFan(fan1Pos, vsize, devRect, -rx + GR_ScalarHalf, -ry + GR_ScalarHalf);
846 setInsetFan(fan2Pos, vsize, devRect, rx - GR_ScalarHalf, ry - GR_ScalarHalf);
847 setInsetFan(fan3Pos, vsize, devRect, rx + GR_ScalarHalf, ry + GR_ScalarHalf);
848
849 verts += sizeof(GrPoint);
850 for (int i = 0; i < 4; ++i) {
851 *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
852 }
853
bsalomon@google.coma3108262011-10-10 14:08:47 +0000854 GrColor innerColor;
855 if (useVertexCoverage) {
bsalomon@google.come10f6fd2011-10-11 20:15:26 +0000856 innerColor = 0xffffffff;
bsalomon@google.coma3108262011-10-10 14:08:47 +0000857 } else {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000858 innerColor = target->getDrawState().getColor();
bsalomon@google.coma3108262011-10-10 14:08:47 +0000859 }
bsalomon@google.com205d4602011-04-25 12:43:45 +0000860 verts += 4 * vsize;
861 for (int i = 0; i < 8; ++i) {
862 *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor;
863 }
864
865 verts += 8 * vsize;
866 for (int i = 0; i < 8; ++i) {
867 *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
868 }
869
bsalomon@google.com9b09c9e2011-08-31 13:33:40 +0000870 target->setIndexSourceToBuffer(indexBuffer);
bsalomon@google.com205d4602011-04-25 12:43:45 +0000871 target->drawIndexed(kTriangles_PrimitiveType,
872 0, 0, 16, aaStrokeRectIndexCount());
873}
874
reed@google.com20efde72011-05-09 17:00:02 +0000875/**
876 * Returns true if the rects edges are integer-aligned.
877 */
878static bool isIRect(const GrRect& r) {
879 return GrScalarIsInt(r.fLeft) && GrScalarIsInt(r.fTop) &&
880 GrScalarIsInt(r.fRight) && GrScalarIsInt(r.fBottom);
881}
882
bsalomon@google.com205d4602011-04-25 12:43:45 +0000883static bool apply_aa_to_rect(GrDrawTarget* target,
bsalomon@google.com205d4602011-04-25 12:43:45 +0000884 const GrRect& rect,
885 GrScalar width,
886 const GrMatrix* matrix,
887 GrMatrix* combinedMatrix,
bsalomon@google.coma3108262011-10-10 14:08:47 +0000888 GrRect* devRect,
889 bool* useVertexCoverage) {
bsalomon@google.com2eba7952012-01-12 13:47:37 +0000890 // we use a simple coverage ramp to do aa on axis-aligned rects
891 // we check if the rect will be axis-aligned, and the rect won't land on
892 // integer coords.
bsalomon@google.comd46e2422011-09-23 17:40:07 +0000893
bsalomon@google.coma3108262011-10-10 14:08:47 +0000894 // we are keeping around the "tweak the alpha" trick because
895 // it is our only hope for the fixed-pipe implementation.
896 // In a shader implementation we can give a separate coverage input
bsalomon@google.com289533a2011-10-27 12:34:25 +0000897 // TODO: remove this ugliness when we drop the fixed-pipe impl
bsalomon@google.coma3108262011-10-10 14:08:47 +0000898 *useVertexCoverage = false;
bsalomon@google.comd46e2422011-09-23 17:40:07 +0000899 if (!target->canTweakAlphaForCoverage()) {
bsalomon@google.com2eba7952012-01-12 13:47:37 +0000900 if (disable_coverage_aa_for_blend(target)) {
bsalomon@google.com1983f392011-10-10 15:17:58 +0000901#if GR_DEBUG
bsalomon@google.com2eba7952012-01-12 13:47:37 +0000902 //GrPrintf("Turning off AA to correctly apply blend.\n");
bsalomon@google.com1983f392011-10-10 15:17:58 +0000903#endif
bsalomon@google.coma3108262011-10-10 14:08:47 +0000904 return false;
bsalomon@google.com2eba7952012-01-12 13:47:37 +0000905 } else {
906 *useVertexCoverage = true;
bsalomon@google.coma3108262011-10-10 14:08:47 +0000907 }
bsalomon@google.com205d4602011-04-25 12:43:45 +0000908 }
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000909 const GrDrawState& drawState = target->getDrawState();
910 if (drawState.getRenderTarget()->isMultisampled()) {
bsalomon@google.com205d4602011-04-25 12:43:45 +0000911 return false;
912 }
913
bsalomon@google.com471d4712011-08-23 15:45:25 +0000914 if (0 == width && target->willUseHWAALines()) {
bsalomon@google.com205d4602011-04-25 12:43:45 +0000915 return false;
916 }
917
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000918 if (!drawState.getViewMatrix().preservesAxisAlignment()) {
bsalomon@google.com205d4602011-04-25 12:43:45 +0000919 return false;
920 }
921
922 if (NULL != matrix &&
923 !matrix->preservesAxisAlignment()) {
924 return false;
925 }
926
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000927 *combinedMatrix = drawState.getViewMatrix();
bsalomon@google.com205d4602011-04-25 12:43:45 +0000928 if (NULL != matrix) {
929 combinedMatrix->preConcat(*matrix);
930 GrAssert(combinedMatrix->preservesAxisAlignment());
931 }
932
933 combinedMatrix->mapRect(devRect, rect);
934 devRect->sort();
935
936 if (width < 0) {
reed@google.com20efde72011-05-09 17:00:02 +0000937 return !isIRect(*devRect);
bsalomon@google.com205d4602011-04-25 12:43:45 +0000938 } else {
939 return true;
940 }
941}
942
bsalomon@google.com27847de2011-02-22 20:59:41 +0000943void GrContext::drawRect(const GrPaint& paint,
944 const GrRect& rect,
945 GrScalar width,
946 const GrMatrix* matrix) {
tomhudson@google.com278cbb42011-06-30 19:37:01 +0000947 SK_TRACE_EVENT0("GrContext::drawRect");
bsalomon@google.com27847de2011-02-22 20:59:41 +0000948
949 GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000950 int stageMask = paint.getActiveStageMask();
bsalomon@google.com27847de2011-02-22 20:59:41 +0000951
bsalomon@google.com205d4602011-04-25 12:43:45 +0000952 GrRect devRect = rect;
953 GrMatrix combinedMatrix;
bsalomon@google.coma3108262011-10-10 14:08:47 +0000954 bool useVertexCoverage;
bsalomon@google.com289533a2011-10-27 12:34:25 +0000955 bool needAA = paint.fAntiAlias &&
956 !this->getRenderTarget()->isMultisampled();
957 bool doAA = needAA && apply_aa_to_rect(target, rect, width, matrix,
958 &combinedMatrix, &devRect,
959 &useVertexCoverage);
bsalomon@google.com205d4602011-04-25 12:43:45 +0000960
961 if (doAA) {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000962 GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
bsalomon@google.com205d4602011-04-25 12:43:45 +0000963 if (width >= 0) {
964 GrVec strokeSize;;
965 if (width > 0) {
966 strokeSize.set(width, width);
bsalomon@google.comcc4dac32011-05-10 13:52:42 +0000967 combinedMatrix.mapVectors(&strokeSize, 1);
bsalomon@google.com205d4602011-04-25 12:43:45 +0000968 strokeSize.setAbs(strokeSize);
969 } else {
970 strokeSize.set(GR_Scalar1, GR_Scalar1);
971 }
bsalomon@google.coma3108262011-10-10 14:08:47 +0000972 strokeAARect(target, devRect, strokeSize, useVertexCoverage);
bsalomon@google.com205d4602011-04-25 12:43:45 +0000973 } else {
bsalomon@google.coma3108262011-10-10 14:08:47 +0000974 fillAARect(target, devRect, useVertexCoverage);
bsalomon@google.com205d4602011-04-25 12:43:45 +0000975 }
976 return;
977 }
978
bsalomon@google.com27847de2011-02-22 20:59:41 +0000979 if (width >= 0) {
980 // TODO: consider making static vertex buffers for these cases.
981 // Hairline could be done by just adding closing vertex to
982 // unitSquareVertexBuffer()
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000983 GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL);
984
bsalomon@google.com27847de2011-02-22 20:59:41 +0000985 static const int worstCaseVertCount = 10;
986 GrDrawTarget::AutoReleaseGeometry geo(target, layout, worstCaseVertCount, 0);
987
988 if (!geo.succeeded()) {
bsalomon@google.com6513cd02011-08-05 20:12:30 +0000989 GrPrintf("Failed to get space for vertices!\n");
bsalomon@google.com27847de2011-02-22 20:59:41 +0000990 return;
991 }
992
993 GrPrimitiveType primType;
994 int vertCount;
995 GrPoint* vertex = geo.positions();
996
997 if (width > 0) {
998 vertCount = 10;
999 primType = kTriangleStrip_PrimitiveType;
1000 setStrokeRectStrip(vertex, rect, width);
1001 } else {
1002 // hairline
1003 vertCount = 5;
1004 primType = kLineStrip_PrimitiveType;
1005 vertex[0].set(rect.fLeft, rect.fTop);
1006 vertex[1].set(rect.fRight, rect.fTop);
1007 vertex[2].set(rect.fRight, rect.fBottom);
1008 vertex[3].set(rect.fLeft, rect.fBottom);
1009 vertex[4].set(rect.fLeft, rect.fTop);
1010 }
1011
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001012 GrDrawState::AutoViewMatrixRestore avmr;
bsalomon@google.com27847de2011-02-22 20:59:41 +00001013 if (NULL != matrix) {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001014 GrDrawState* drawState = target->drawState();
1015 avmr.set(drawState);
1016 drawState->preConcatViewMatrix(*matrix);
1017 drawState->preConcatSamplerMatrices(stageMask, *matrix);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001018 }
1019
1020 target->drawNonIndexed(primType, 0, vertCount);
1021 } else {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001022#if GR_STATIC_RECT_VB
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001023 GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL);
bsalomon@google.com6513cd02011-08-05 20:12:30 +00001024 const GrVertexBuffer* sqVB = fGpu->getUnitSquareVertexBuffer();
1025 if (NULL == sqVB) {
1026 GrPrintf("Failed to create static rect vb.\n");
1027 return;
1028 }
1029 target->setVertexSourceToBuffer(layout, sqVB);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001030 GrDrawState* drawState = target->drawState();
1031 GrDrawState::AutoViewMatrixRestore avmr(drawState);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001032 GrMatrix m;
bsalomon@google.com8295dc12011-05-02 12:53:34 +00001033 m.setAll(rect.width(), 0, rect.fLeft,
bsalomon@google.com205d4602011-04-25 12:43:45 +00001034 0, rect.height(), rect.fTop,
1035 0, 0, GrMatrix::I()[8]);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001036
1037 if (NULL != matrix) {
1038 m.postConcat(*matrix);
1039 }
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001040 drawState->preConcatViewMatrix(m);
1041 drawState->preConcatSamplerMatrices(stageMask, m);
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001042
bsalomon@google.com27847de2011-02-22 20:59:41 +00001043 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, 4);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001044#else
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001045 target->drawSimpleRect(rect, matrix, stageMask);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001046#endif
bsalomon@google.com27847de2011-02-22 20:59:41 +00001047 }
1048}
1049
1050void GrContext::drawRectToRect(const GrPaint& paint,
1051 const GrRect& dstRect,
1052 const GrRect& srcRect,
1053 const GrMatrix* dstMatrix,
1054 const GrMatrix* srcMatrix) {
tomhudson@google.com278cbb42011-06-30 19:37:01 +00001055 SK_TRACE_EVENT0("GrContext::drawRectToRect");
bsalomon@google.com27847de2011-02-22 20:59:41 +00001056
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001057 // srcRect refers to paint's first texture
1058 if (NULL == paint.getTexture(0)) {
bsalomon@google.com27847de2011-02-22 20:59:41 +00001059 drawRect(paint, dstRect, -1, dstMatrix);
1060 return;
1061 }
bsalomon@google.comde6ac2d2011-02-25 21:50:42 +00001062
bsalomon@google.com27847de2011-02-22 20:59:41 +00001063 GR_STATIC_ASSERT(!BATCH_RECT_TO_RECT || !GR_STATIC_RECT_VB);
1064
1065#if GR_STATIC_RECT_VB
1066 GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001067 GrDrawState* drawState = target->drawState();
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001068 GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001069 GrDrawState::AutoViewMatrixRestore avmr(drawState);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001070
1071 GrMatrix m;
1072
1073 m.setAll(dstRect.width(), 0, dstRect.fLeft,
1074 0, dstRect.height(), dstRect.fTop,
1075 0, 0, GrMatrix::I()[8]);
1076 if (NULL != dstMatrix) {
1077 m.postConcat(*dstMatrix);
1078 }
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001079 drawState->preConcatViewMatrix(m);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001080
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001081 // srcRect refers to first stage
1082 int otherStageMask = paint.getActiveStageMask() &
1083 (~(1 << GrPaint::kFirstTextureStage));
1084 if (otherStageMask) {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001085 drawState->preConcatSamplerMatrices(otherStageMask, m);
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001086 }
1087
bsalomon@google.com27847de2011-02-22 20:59:41 +00001088 m.setAll(srcRect.width(), 0, srcRect.fLeft,
1089 0, srcRect.height(), srcRect.fTop,
1090 0, 0, GrMatrix::I()[8]);
1091 if (NULL != srcMatrix) {
1092 m.postConcat(*srcMatrix);
1093 }
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001094 drawState->sampler(GrPaint::kFirstTextureStage)->preConcatMatrix(m);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001095
bsalomon@google.com6513cd02011-08-05 20:12:30 +00001096 const GrVertexBuffer* sqVB = fGpu->getUnitSquareVertexBuffer();
1097 if (NULL == sqVB) {
1098 GrPrintf("Failed to create static rect vb.\n");
1099 return;
1100 }
1101 target->setVertexSourceToBuffer(layout, sqVB);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001102 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, 4);
1103#else
1104
1105 GrDrawTarget* target;
bsalomon@google.comde6ac2d2011-02-25 21:50:42 +00001106#if BATCH_RECT_TO_RECT
bsalomon@google.com27847de2011-02-22 20:59:41 +00001107 target = this->prepareToDraw(paint, kBuffered_DrawCategory);
bsalomon@google.comde6ac2d2011-02-25 21:50:42 +00001108#else
bsalomon@google.com27847de2011-02-22 20:59:41 +00001109 target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
1110#endif
1111
tomhudson@google.com93813632011-10-27 20:21:16 +00001112 const GrRect* srcRects[GrDrawState::kNumStages] = {NULL};
1113 const GrMatrix* srcMatrices[GrDrawState::kNumStages] = {NULL};
bsalomon@google.com27847de2011-02-22 20:59:41 +00001114 srcRects[0] = &srcRect;
1115 srcMatrices[0] = srcMatrix;
1116
1117 target->drawRect(dstRect, dstMatrix, 1, srcRects, srcMatrices);
1118#endif
1119}
1120
1121void GrContext::drawVertices(const GrPaint& paint,
1122 GrPrimitiveType primitiveType,
1123 int vertexCount,
1124 const GrPoint positions[],
1125 const GrPoint texCoords[],
1126 const GrColor colors[],
1127 const uint16_t indices[],
1128 int indexCount) {
tomhudson@google.com278cbb42011-06-30 19:37:01 +00001129 SK_TRACE_EVENT0("GrContext::drawVertices");
bsalomon@google.com27847de2011-02-22 20:59:41 +00001130
1131 GrDrawTarget::AutoReleaseGeometry geo;
1132
1133 GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
1134
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001135 bool hasTexCoords[GrPaint::kTotalStages] = {
1136 NULL != texCoords, // texCoordSrc provides explicit stage 0 coords
1137 0 // remaining stages use positions
1138 };
1139
1140 GrVertexLayout layout = PaintStageVertexLayoutBits(paint, hasTexCoords);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001141
1142 if (NULL != colors) {
1143 layout |= GrDrawTarget::kColor_VertexLayoutBit;
bsalomon@google.com27847de2011-02-22 20:59:41 +00001144 }
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001145 int vertexSize = GrDrawTarget::VertexSize(layout);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001146
1147 if (sizeof(GrPoint) != vertexSize) {
1148 if (!geo.set(target, layout, vertexCount, 0)) {
bsalomon@google.com6513cd02011-08-05 20:12:30 +00001149 GrPrintf("Failed to get space for vertices!\n");
bsalomon@google.com27847de2011-02-22 20:59:41 +00001150 return;
1151 }
tomhudson@google.com93813632011-10-27 20:21:16 +00001152 int texOffsets[GrDrawState::kMaxTexCoords];
bsalomon@google.com27847de2011-02-22 20:59:41 +00001153 int colorOffset;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001154 GrDrawTarget::VertexSizeAndOffsetsByIdx(layout,
1155 texOffsets,
bsalomon@google.comaeb21602011-08-30 18:13:44 +00001156 &colorOffset,
bsalomon@google.coma3108262011-10-10 14:08:47 +00001157 NULL,
1158 NULL);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001159 void* curVertex = geo.vertices();
1160
1161 for (int i = 0; i < vertexCount; ++i) {
1162 *((GrPoint*)curVertex) = positions[i];
1163
1164 if (texOffsets[0] > 0) {
1165 *(GrPoint*)((intptr_t)curVertex + texOffsets[0]) = texCoords[i];
1166 }
1167 if (colorOffset > 0) {
1168 *(GrColor*)((intptr_t)curVertex + colorOffset) = colors[i];
1169 }
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001170 curVertex = (void*)((intptr_t)curVertex + vertexSize);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001171 }
1172 } else {
1173 target->setVertexSourceToArray(layout, positions, vertexCount);
1174 }
1175
bsalomon@google.com91958362011-06-13 17:58:13 +00001176 // we don't currently apply offscreen AA to this path. Need improved
1177 // management of GrDrawTarget's geometry to avoid copying points per-tile.
bsalomon@google.coma47a48d2011-04-26 20:22:11 +00001178
bsalomon@google.com8295dc12011-05-02 12:53:34 +00001179 if (NULL != indices) {
bsalomon@google.com91958362011-06-13 17:58:13 +00001180 target->setIndexSourceToArray(indices, indexCount);
bsalomon@google.com8295dc12011-05-02 12:53:34 +00001181 target->drawIndexed(primitiveType, 0, 0, vertexCount, indexCount);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001182 } else {
bsalomon@google.com8295dc12011-05-02 12:53:34 +00001183 target->drawNonIndexed(primitiveType, 0, vertexCount);
1184 }
bsalomon@google.com27847de2011-02-22 20:59:41 +00001185}
1186
bsalomon@google.com06afe7b2011-04-26 15:31:40 +00001187///////////////////////////////////////////////////////////////////////////////
bsalomon@google.com150d2842012-01-12 20:19:56 +00001188#include "SkDraw.h"
1189#include "SkRasterClip.h"
1190
1191namespace {
1192
1193SkPath::FillType gr_fill_to_sk_fill(GrPathFill fill) {
1194 switch (fill) {
1195 case kWinding_PathFill:
1196 return SkPath::kWinding_FillType;
1197 case kEvenOdd_PathFill:
1198 return SkPath::kEvenOdd_FillType;
1199 case kInverseWinding_PathFill:
1200 return SkPath::kInverseWinding_FillType;
1201 case kInverseEvenOdd_PathFill:
1202 return SkPath::kInverseEvenOdd_FillType;
1203 default:
1204 GrCrash("Unexpected fill.");
1205 return SkPath::kWinding_FillType;
1206 }
1207}
1208
1209// gets device coord bounds of path (not considering the fill) and clip. The
1210// path bounds will be a subset of the clip bounds. returns false if path bounds
1211// would be empty.
1212bool get_path_and_clip_bounds(const GrDrawTarget* target,
1213 const GrPath& path,
1214 const GrVec* translate,
1215 GrIRect* pathBounds,
1216 GrIRect* clipBounds) {
1217 // compute bounds as intersection of rt size, clip, and path
1218 const GrRenderTarget* rt = target->getDrawState().getRenderTarget();
1219 if (NULL == rt) {
1220 return false;
1221 }
1222 *pathBounds = GrIRect::MakeWH(rt->width(), rt->height());
1223 const GrClip& clip = target->getClip();
1224 if (clip.hasConservativeBounds()) {
1225 clip.getConservativeBounds().roundOut(clipBounds);
1226 if (!pathBounds->intersect(*clipBounds)) {
1227 return false;
1228 }
1229 } else {
1230 // pathBounds is currently the rt extent, set clip bounds to that rect.
1231 *clipBounds = *pathBounds;
1232 }
1233 GrRect pathSBounds = path.getBounds();
1234 if (!pathSBounds.isEmpty()) {
1235 if (NULL != translate) {
1236 pathSBounds.offset(*translate);
1237 }
1238 target->getDrawState().getViewMatrix().mapRect(&pathSBounds,
1239 pathSBounds);
1240 GrIRect pathIBounds;
1241 pathSBounds.roundOut(&pathIBounds);
1242 if (!pathBounds->intersect(pathIBounds)) {
1243 return false;
1244 }
1245 } else {
1246 return false;
1247 }
1248 return true;
1249}
1250
1251/**
1252 * sw rasterizes path to A8 mask using the context's matrix and uploads to a
1253 * scratch texture.
1254 */
1255
1256bool sw_draw_path_to_mask_texture(const GrPath& clientPath,
1257 const GrIRect& pathDevBounds,
1258 GrPathFill fill,
1259 GrContext* context,
1260 const GrPoint* translate,
1261 GrAutoScratchTexture* tex) {
1262 SkPaint paint;
1263 SkPath tmpPath;
1264 const SkPath* pathToDraw = &clientPath;
1265 if (kHairLine_PathFill == fill) {
1266 paint.setStyle(SkPaint::kStroke_Style);
1267 paint.setStrokeWidth(SK_Scalar1);
1268 } else {
1269 paint.setStyle(SkPaint::kFill_Style);
1270 SkPath::FillType skfill = gr_fill_to_sk_fill(fill);
1271 if (skfill != pathToDraw->getFillType()) {
1272 tmpPath = *pathToDraw;
1273 tmpPath.setFillType(skfill);
1274 pathToDraw = &tmpPath;
1275 }
1276 }
1277 paint.setAntiAlias(true);
1278 paint.setColor(SK_ColorWHITE);
1279
1280 GrMatrix matrix = context->getMatrix();
1281 if (NULL != translate) {
1282 matrix.postTranslate(translate->fX, translate->fY);
1283 }
1284
1285 matrix.postTranslate(-pathDevBounds.fLeft * SK_Scalar1,
1286 -pathDevBounds.fTop * SK_Scalar1);
1287 GrIRect bounds = GrIRect::MakeWH(pathDevBounds.width(),
1288 pathDevBounds.height());
1289
1290 SkBitmap bm;
1291 bm.setConfig(SkBitmap::kA8_Config, bounds.fRight, bounds.fBottom);
1292 if (!bm.allocPixels()) {
1293 return false;
1294 }
1295 sk_bzero(bm.getPixels(), bm.getSafeSize());
1296
1297 SkDraw draw;
1298 sk_bzero(&draw, sizeof(draw));
1299 SkRasterClip rc(bounds);
1300 draw.fRC = &rc;
1301 draw.fClip = &rc.bwRgn();
1302 draw.fMatrix = &matrix;
1303 draw.fBitmap = &bm;
1304 draw.drawPath(*pathToDraw, paint);
1305
1306 const GrTextureDesc desc = {
1307 kNone_GrTextureFlags,
bsalomon@google.com150d2842012-01-12 20:19:56 +00001308 bounds.fRight,
1309 bounds.fBottom,
bsalomon@google.com78d6cf92012-01-30 18:09:31 +00001310 kAlpha_8_GrPixelConfig,
1311 {0} // samples
bsalomon@google.com150d2842012-01-12 20:19:56 +00001312 };
1313
1314 tex->set(context, desc);
1315 GrTexture* texture = tex->texture();
1316
1317 if (NULL == texture) {
1318 return false;
1319 }
1320 SkAutoLockPixels alp(bm);
1321 texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
1322 bm.getPixels(), bm.rowBytes());
1323 return true;
1324}
1325
1326void draw_around_inv_path(GrDrawTarget* target,
1327 GrDrawState::StageMask stageMask,
1328 const GrIRect& clipBounds,
1329 const GrIRect& pathBounds) {
1330 GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
1331 GrRect rect;
1332 if (clipBounds.fTop < pathBounds.fTop) {
1333 rect.iset(clipBounds.fLeft, clipBounds.fTop,
1334 clipBounds.fRight, pathBounds.fTop);
1335 target->drawSimpleRect(rect, NULL, stageMask);
1336 }
1337 if (clipBounds.fLeft < pathBounds.fLeft) {
1338 rect.iset(clipBounds.fLeft, pathBounds.fTop,
1339 pathBounds.fLeft, pathBounds.fBottom);
1340 target->drawSimpleRect(rect, NULL, stageMask);
1341 }
1342 if (clipBounds.fRight > pathBounds.fRight) {
1343 rect.iset(pathBounds.fRight, pathBounds.fTop,
1344 clipBounds.fRight, pathBounds.fBottom);
1345 target->drawSimpleRect(rect, NULL, stageMask);
1346 }
1347 if (clipBounds.fBottom > pathBounds.fBottom) {
1348 rect.iset(clipBounds.fLeft, pathBounds.fBottom,
1349 clipBounds.fRight, clipBounds.fBottom);
1350 target->drawSimpleRect(rect, NULL, stageMask);
1351 }
1352}
1353
1354}
bsalomon@google.com27847de2011-02-22 20:59:41 +00001355
reed@google.com07f3ee12011-05-16 17:21:57 +00001356void GrContext::drawPath(const GrPaint& paint, const GrPath& path,
1357 GrPathFill fill, const GrPoint* translate) {
bsalomon@google.com27847de2011-02-22 20:59:41 +00001358
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001359 if (path.isEmpty()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001360 if (GrIsFillInverted(fill)) {
1361 this->drawPaint(paint);
1362 }
1363 return;
1364 }
1365
bsalomon@google.com27847de2011-02-22 20:59:41 +00001366 GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
bsalomon@google.com150d2842012-01-12 20:19:56 +00001367 GrDrawState::StageMask stageMask = paint.getActiveStageMask();
bsalomon@google.comd46e2422011-09-23 17:40:07 +00001368
bsalomon@google.com289533a2011-10-27 12:34:25 +00001369 bool prAA = paint.fAntiAlias && !this->getRenderTarget()->isMultisampled();
1370
bsalomon@google.comd46e2422011-09-23 17:40:07 +00001371 // An Assumption here is that path renderer would use some form of tweaking
1372 // the src color (either the input alpha or in the frag shader) to implement
1373 // aa. If we have some future driver-mojo path AA that can do the right
1374 // thing WRT to the blend then we'll need some query on the PR.
1375 if (disable_coverage_aa_for_blend(target)) {
bsalomon@google.com1983f392011-10-10 15:17:58 +00001376#if GR_DEBUG
bsalomon@google.com979432b2011-11-05 21:38:22 +00001377 //GrPrintf("Turning off AA to correctly apply blend.\n");
bsalomon@google.com1983f392011-10-10 15:17:58 +00001378#endif
bsalomon@google.com289533a2011-10-27 12:34:25 +00001379 prAA = false;
bsalomon@google.comd46e2422011-09-23 17:40:07 +00001380 }
bsalomon@google.com289533a2011-10-27 12:34:25 +00001381
bsalomon@google.com289533a2011-10-27 12:34:25 +00001382 GrPathRenderer* pr = NULL;
1383 if (prAA) {
1384 pr = this->getPathRenderer(path, fill, true);
1385 if (NULL == pr) {
bsalomon@google.com150d2842012-01-12 20:19:56 +00001386 GrAutoScratchTexture ast;
1387 GrIRect pathBounds, clipBounds;
1388 if (!get_path_and_clip_bounds(target, path, translate,
1389 &pathBounds, &clipBounds)) {
1390 return;
1391 }
bsalomon@google.com150d2842012-01-12 20:19:56 +00001392 if (NULL == pr && sw_draw_path_to_mask_texture(path, pathBounds,
1393 fill, this,
1394 translate, &ast)) {
1395 GrTexture* texture = ast.texture();
1396 GrAssert(NULL != texture);
1397 GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
1398 enum {
1399 kPathMaskStage = GrPaint::kTotalStages,
1400 };
1401 target->drawState()->setTexture(kPathMaskStage, texture);
1402 target->drawState()->sampler(kPathMaskStage)->reset();
1403 GrScalar w = GrIntToScalar(pathBounds.width());
1404 GrScalar h = GrIntToScalar(pathBounds.height());
1405 GrRect maskRect = GrRect::MakeWH(w / texture->width(),
1406 h / texture->height());
1407 const GrRect* srcRects[GrDrawState::kNumStages] = {NULL};
1408 srcRects[kPathMaskStage] = &maskRect;
1409 stageMask |= 1 << kPathMaskStage;
bsalomon@google.com5db3b6c2012-01-12 20:38:57 +00001410 GrRect dstRect = GrRect::MakeLTRB(
1411 SK_Scalar1* pathBounds.fLeft,
1412 SK_Scalar1* pathBounds.fTop,
1413 SK_Scalar1* pathBounds.fRight,
1414 SK_Scalar1* pathBounds.fBottom);
bsalomon@google.com150d2842012-01-12 20:19:56 +00001415 target->drawRect(dstRect, NULL, stageMask, srcRects, NULL);
1416 target->drawState()->setTexture(kPathMaskStage, NULL);
1417 if (GrIsFillInverted(fill)) {
1418 draw_around_inv_path(target, stageMask,
1419 clipBounds, pathBounds);
1420 }
1421 return;
1422 }
bsalomon@google.com289533a2011-10-27 12:34:25 +00001423 }
1424 } else {
1425 pr = this->getPathRenderer(path, fill, false);
1426 }
1427
bsalomon@google.com30085192011-08-19 15:42:31 +00001428 if (NULL == pr) {
bsalomon@google.com1983f392011-10-10 15:17:58 +00001429#if GR_DEBUG
bsalomon@google.com30085192011-08-19 15:42:31 +00001430 GrPrintf("Unable to find path renderer compatible with path.\n");
bsalomon@google.com1983f392011-10-10 15:17:58 +00001431#endif
bsalomon@google.com30085192011-08-19 15:42:31 +00001432 return;
1433 }
1434
bsalomon@google.com289533a2011-10-27 12:34:25 +00001435 GrPathRenderer::AutoClearPath arp(pr, target, &path, fill, prAA, translate);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001436
bsalomon@google.comee435122011-07-01 14:57:55 +00001437 pr->drawPath(stageMask);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001438}
bsalomon@google.com8295dc12011-05-02 12:53:34 +00001439
bsalomon@google.com27847de2011-02-22 20:59:41 +00001440////////////////////////////////////////////////////////////////////////////////
1441
bsalomon@google.coma7f84e12011-03-10 14:13:19 +00001442void GrContext::flush(int flagsBitfield) {
1443 if (kDiscard_FlushBit & flagsBitfield) {
1444 fDrawBuffer->reset();
1445 } else {
bsalomon@google.comc4364992011-11-07 15:54:49 +00001446 this->flushDrawBuffer();
bsalomon@google.coma7f84e12011-03-10 14:13:19 +00001447 }
bsalomon@google.coma7f84e12011-03-10 14:13:19 +00001448 if (kForceCurrentRenderTarget_FlushBit & flagsBitfield) {
bsalomon@google.com27847de2011-02-22 20:59:41 +00001449 fGpu->forceRenderTargetFlush();
1450 }
1451}
1452
1453void GrContext::flushText() {
1454 if (kText_DrawCategory == fLastDrawCategory) {
1455 flushDrawBuffer();
1456 }
1457}
1458
1459void GrContext::flushDrawBuffer() {
1460#if BATCH_RECT_TO_RECT || DEFER_TEXT_RENDERING
junov@google.com53a55842011-06-08 22:55:10 +00001461 if (fDrawBuffer) {
1462 fDrawBuffer->playback(fGpu);
1463 fDrawBuffer->reset();
1464 }
bsalomon@google.com27847de2011-02-22 20:59:41 +00001465#endif
1466}
1467
bsalomon@google.com6f379512011-11-16 20:36:03 +00001468void GrContext::internalWriteTexturePixels(GrTexture* texture,
1469 int left, int top,
1470 int width, int height,
1471 GrPixelConfig config,
1472 const void* buffer,
1473 size_t rowBytes,
1474 uint32_t flags) {
1475 SK_TRACE_EVENT0("GrContext::writeTexturePixels");
bsalomon@google.combc4b6542011-11-19 13:56:11 +00001476 ASSERT_OWNED_RESOURCE(texture);
1477
bsalomon@google.com6f379512011-11-16 20:36:03 +00001478 if (!(kDontFlush_PixelOpsFlag & flags)) {
1479 this->flush();
1480 }
1481 // TODO: use scratch texture to perform conversion
1482 if (GrPixelConfigIsUnpremultiplied(texture->config()) !=
1483 GrPixelConfigIsUnpremultiplied(config)) {
1484 return;
1485 }
1486
1487 fGpu->writeTexturePixels(texture, left, top, width, height,
1488 config, buffer, rowBytes);
1489}
1490
1491bool GrContext::internalReadTexturePixels(GrTexture* texture,
1492 int left, int top,
1493 int width, int height,
1494 GrPixelConfig config,
1495 void* buffer,
1496 size_t rowBytes,
1497 uint32_t flags) {
tomhudson@google.com278cbb42011-06-30 19:37:01 +00001498 SK_TRACE_EVENT0("GrContext::readTexturePixels");
bsalomon@google.combc4b6542011-11-19 13:56:11 +00001499 ASSERT_OWNED_RESOURCE(texture);
bsalomon@google.com669fdc42011-04-05 17:08:27 +00001500
bsalomon@google.coma85449d2011-11-19 02:36:05 +00001501 // TODO: code read pixels for textures that aren't also rendertargets
bsalomon@google.com669fdc42011-04-05 17:08:27 +00001502 GrRenderTarget* target = texture->asRenderTarget();
1503 if (NULL != target) {
bsalomon@google.com6f379512011-11-16 20:36:03 +00001504 return this->internalReadRenderTargetPixels(target,
1505 left, top, width, height,
1506 config, buffer, rowBytes,
1507 flags);
bsalomon@google.com669fdc42011-04-05 17:08:27 +00001508 } else {
1509 return false;
1510 }
1511}
1512
bsalomon@google.coma91e9232012-02-23 15:39:54 +00001513#include "SkConfig8888.h"
1514
1515namespace {
1516/**
1517 * Converts a GrPixelConfig to a SkCanvas::Config8888. Only byte-per-channel
1518 * formats are representable as Config8888 and so the function returns false
1519 * if the GrPixelConfig has no equivalent Config8888.
1520 */
1521bool grconfig_to_config8888(GrPixelConfig config,
1522 SkCanvas::Config8888* config8888) {
1523 switch (config) {
1524 case kRGBA_8888_PM_GrPixelConfig:
1525 *config8888 = SkCanvas::kRGBA_Premul_Config8888;
1526 return true;
1527 case kRGBA_8888_UPM_GrPixelConfig:
1528 *config8888 = SkCanvas::kRGBA_Unpremul_Config8888;
1529 return true;
1530 case kBGRA_8888_PM_GrPixelConfig:
1531 *config8888 = SkCanvas::kBGRA_Premul_Config8888;
1532 return true;
1533 case kBGRA_8888_UPM_GrPixelConfig:
1534 *config8888 = SkCanvas::kBGRA_Unpremul_Config8888;
1535 return true;
1536 default:
1537 return false;
1538 }
1539}
1540}
1541
bsalomon@google.com6f379512011-11-16 20:36:03 +00001542bool GrContext::internalReadRenderTargetPixels(GrRenderTarget* target,
1543 int left, int top,
1544 int width, int height,
1545 GrPixelConfig config,
1546 void* buffer,
1547 size_t rowBytes,
1548 uint32_t flags) {
tomhudson@google.com278cbb42011-06-30 19:37:01 +00001549 SK_TRACE_EVENT0("GrContext::readRenderTargetPixels");
bsalomon@google.combc4b6542011-11-19 13:56:11 +00001550 ASSERT_OWNED_RESOURCE(target);
1551
bsalomon@google.com669fdc42011-04-05 17:08:27 +00001552 if (NULL == target) {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001553 target = fGpu->drawState()->getRenderTarget();
bsalomon@google.comc4364992011-11-07 15:54:49 +00001554 if (NULL == target) {
1555 return false;
1556 }
1557 }
bsalomon@google.com669fdc42011-04-05 17:08:27 +00001558
bsalomon@google.com6f379512011-11-16 20:36:03 +00001559 if (!(kDontFlush_PixelOpsFlag & flags)) {
1560 this->flush();
1561 }
bsalomon@google.comc4364992011-11-07 15:54:49 +00001562
bsalomon@google.coma91e9232012-02-23 15:39:54 +00001563 if (!GrPixelConfigIsUnpremultiplied(target->config()) &&
1564 GrPixelConfigIsUnpremultiplied(config) &&
1565 !fGpu->canPreserveReadWriteUnpremulPixels()) {
1566 SkCanvas::Config8888 srcConfig8888, dstConfig8888;
1567 if (!grconfig_to_config8888(target->config(), &srcConfig8888) ||
1568 !grconfig_to_config8888(config, &dstConfig8888)) {
1569 return false;
1570 }
1571 // do read back using target's own config
1572 this->internalReadRenderTargetPixels(target,
1573 left, top,
1574 width, height,
1575 target->config(),
1576 buffer, rowBytes,
1577 kDontFlush_PixelOpsFlag);
1578 // sw convert the pixels to unpremul config
1579 uint32_t* pixels = reinterpret_cast<uint32_t*>(buffer);
1580 SkConvertConfig8888Pixels(pixels, rowBytes, dstConfig8888,
1581 pixels, rowBytes, srcConfig8888,
1582 width, height);
1583 return true;
1584 }
1585
bsalomon@google.comc4364992011-11-07 15:54:49 +00001586 GrTexture* src = target->asTexture();
bsalomon@google.com0a97be22011-11-08 19:20:57 +00001587 bool swapRAndB = NULL != src &&
1588 fGpu->preferredReadPixelsConfig(config) ==
1589 GrPixelConfigSwapRAndB(config);
bsalomon@google.comc4364992011-11-07 15:54:49 +00001590
1591 bool flipY = NULL != src &&
1592 fGpu->readPixelsWillPayForYFlip(target, left, top,
1593 width, height, config,
1594 rowBytes);
bsalomon@google.com0a97be22011-11-08 19:20:57 +00001595 bool alphaConversion = (!GrPixelConfigIsUnpremultiplied(target->config()) &&
1596 GrPixelConfigIsUnpremultiplied(config));
bsalomon@google.comc4364992011-11-07 15:54:49 +00001597
bsalomon@google.com0a97be22011-11-08 19:20:57 +00001598 if (NULL == src && alphaConversion) {
1599 // we should fallback to cpu conversion here. This could happen when
1600 // we were given an external render target by the client that is not
1601 // also a texture (e.g. FBO 0 in GL)
1602 return false;
1603 }
bsalomon@google.com0a97be22011-11-08 19:20:57 +00001604 // we draw to a scratch texture if any of these conversion are applied
bsalomon@google.comc4ff22a2011-11-10 21:56:21 +00001605 GrAutoScratchTexture ast;
bsalomon@google.com0a97be22011-11-08 19:20:57 +00001606 if (flipY || swapRAndB || alphaConversion) {
1607 GrAssert(NULL != src);
1608 if (swapRAndB) {
1609 config = GrPixelConfigSwapRAndB(config);
1610 GrAssert(kUnknown_GrPixelConfig != config);
bsalomon@google.comc4364992011-11-07 15:54:49 +00001611 }
1612 // Make the scratch a render target because we don't have a robust
1613 // readTexturePixels as of yet (it calls this function).
1614 const GrTextureDesc desc = {
1615 kRenderTarget_GrTextureFlagBit,
bsalomon@google.comc4364992011-11-07 15:54:49 +00001616 width, height,
bsalomon@google.com78d6cf92012-01-30 18:09:31 +00001617 config,
1618 {0}, // samples
bsalomon@google.comc4364992011-11-07 15:54:49 +00001619 };
bsalomon@google.comc4ff22a2011-11-10 21:56:21 +00001620
bsalomon@google.com56d11e02011-11-30 19:59:08 +00001621 // When a full readback is faster than a partial we could always make
1622 // the scratch exactly match the passed rect. However, if we see many
1623 // different size rectangles we will trash our texture cache and pay the
1624 // cost of creating and destroying many textures. So, we only request
1625 // an exact match when the caller is reading an entire RT.
1626 ScratchTexMatch match = kApprox_ScratchTexMatch;
1627 if (0 == left &&
1628 0 == top &&
1629 target->width() == width &&
1630 target->height() == height &&
1631 fGpu->fullReadPixelsIsFasterThanPartial()) {
1632 match = kExact_ScratchTexMatch;
1633 }
1634 ast.set(this, desc, match);
bsalomon@google.comc4364992011-11-07 15:54:49 +00001635 GrTexture* texture = ast.texture();
1636 if (!texture) {
1637 return false;
1638 }
1639 target = texture->asRenderTarget();
bsalomon@google.comc4364992011-11-07 15:54:49 +00001640 GrAssert(NULL != target);
1641
1642 GrDrawTarget::AutoStateRestore asr(fGpu);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001643 GrDrawState* drawState = fGpu->drawState();
bsalomon@google.com52a5dcb2012-01-17 16:01:37 +00001644 drawState->reset();
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001645 drawState->setRenderTarget(target);
bsalomon@google.comc4364992011-11-07 15:54:49 +00001646
bsalomon@google.comc4364992011-11-07 15:54:49 +00001647 GrMatrix matrix;
1648 if (flipY) {
1649 matrix.setTranslate(SK_Scalar1 * left,
1650 SK_Scalar1 * (top + height));
1651 matrix.set(GrMatrix::kMScaleY, -GR_Scalar1);
1652 } else {
1653 matrix.setTranslate(SK_Scalar1 *left, SK_Scalar1 *top);
1654 }
1655 matrix.postIDiv(src->width(), src->height());
bsalomon@google.com1e266f82011-12-12 16:11:33 +00001656 drawState->sampler(0)->reset(matrix);
1657 drawState->sampler(0)->setRAndBSwap(swapRAndB);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001658 drawState->setTexture(0, src);
bsalomon@google.comc4364992011-11-07 15:54:49 +00001659 GrRect rect;
1660 rect.setXYWH(0, 0, SK_Scalar1 * width, SK_Scalar1 * height);
1661 fGpu->drawSimpleRect(rect, NULL, 0x1);
1662 left = 0;
1663 top = 0;
1664 }
bsalomon@google.com669fdc42011-04-05 17:08:27 +00001665 return fGpu->readPixels(target,
bsalomon@google.comc4364992011-11-07 15:54:49 +00001666 left, top, width, height,
1667 config, buffer, rowBytes, flipY);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001668}
1669
bsalomon@google.com75f9f252012-01-31 13:35:56 +00001670void GrContext::resolveRenderTarget(GrRenderTarget* target) {
1671 GrAssert(target);
1672 ASSERT_OWNED_RESOURCE(target);
1673 // In the future we may track whether there are any pending draws to this
1674 // target. We don't today so we always perform a flush. We don't promise
1675 // this to our clients, though.
1676 this->flush();
1677 fGpu->resolveRenderTarget(target);
1678}
1679
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +00001680void GrContext::copyTexture(GrTexture* src, GrRenderTarget* dst) {
1681 if (NULL == src || NULL == dst) {
1682 return;
1683 }
1684 ASSERT_OWNED_RESOURCE(src);
1685
1686 GrDrawTarget::AutoStateRestore asr(fGpu);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001687 GrDrawState* drawState = fGpu->drawState();
bsalomon@google.com52a5dcb2012-01-17 16:01:37 +00001688 drawState->reset();
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001689 drawState->setRenderTarget(dst);
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +00001690 GrMatrix sampleM;
1691 sampleM.setIDiv(src->width(), src->height());
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001692 drawState->setTexture(0, src);
bsalomon@google.com1e266f82011-12-12 16:11:33 +00001693 drawState->sampler(0)->reset(sampleM);
bsalomon@google.com5db3b6c2012-01-12 20:38:57 +00001694 SkRect rect = SkRect::MakeXYWH(0, 0,
1695 SK_Scalar1 * src->width(),
1696 SK_Scalar1 * src->height());
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +00001697 fGpu->drawSimpleRect(rect, NULL, 1 << 0);
1698}
1699
bsalomon@google.com6f379512011-11-16 20:36:03 +00001700void GrContext::internalWriteRenderTargetPixels(GrRenderTarget* target,
1701 int left, int top,
1702 int width, int height,
1703 GrPixelConfig config,
1704 const void* buffer,
1705 size_t rowBytes,
1706 uint32_t flags) {
1707 SK_TRACE_EVENT0("GrContext::writeRenderTargetPixels");
bsalomon@google.combc4b6542011-11-19 13:56:11 +00001708 ASSERT_OWNED_RESOURCE(target);
bsalomon@google.com6f379512011-11-16 20:36:03 +00001709
1710 if (NULL == target) {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001711 target = fGpu->drawState()->getRenderTarget();
bsalomon@google.com6f379512011-11-16 20:36:03 +00001712 if (NULL == target) {
1713 return;
1714 }
1715 }
bsalomon@google.com27847de2011-02-22 20:59:41 +00001716
1717 // TODO: when underlying api has a direct way to do this we should use it
1718 // (e.g. glDrawPixels on desktop GL).
1719
bsalomon@google.coma85449d2011-11-19 02:36:05 +00001720 // If the RT is also a texture and we don't have to do PM/UPM conversion
1721 // then take the texture path, which we expect to be at least as fast or
1722 // faster since it doesn't use an intermediate texture as we do below.
1723
1724#if !GR_MAC_BUILD
1725 // At least some drivers on the Mac get confused when glTexImage2D is called
1726 // on a texture attached to an FBO. The FBO still sees the old image. TODO:
1727 // determine what OS versions and/or HW is affected.
1728 if (NULL != target->asTexture() &&
1729 GrPixelConfigIsUnpremultiplied(target->config()) ==
1730 GrPixelConfigIsUnpremultiplied(config)) {
1731
1732 this->internalWriteTexturePixels(target->asTexture(),
1733 left, top, width, height,
1734 config, buffer, rowBytes, flags);
1735 return;
1736 }
1737#endif
bsalomon@google.coma91e9232012-02-23 15:39:54 +00001738 if (!GrPixelConfigIsUnpremultiplied(target->config()) &&
1739 GrPixelConfigIsUnpremultiplied(config) &&
1740 !fGpu->canPreserveReadWriteUnpremulPixels()) {
1741 SkCanvas::Config8888 srcConfig8888, dstConfig8888;
1742 if (!grconfig_to_config8888(config, &srcConfig8888) ||
1743 !grconfig_to_config8888(target->config(), &dstConfig8888)) {
1744 return;
1745 }
1746 // allocate a tmp buffer and sw convert the pixels to premul
1747 SkAutoSTMalloc<128 * 128, uint32_t> tmpPixels(width * height);
1748 const uint32_t* src = reinterpret_cast<const uint32_t*>(buffer);
1749 SkConvertConfig8888Pixels(tmpPixels.get(), 4 * width, dstConfig8888,
1750 src, rowBytes, srcConfig8888,
1751 width, height);
1752 // upload the already premul pixels
1753 this->internalWriteRenderTargetPixels(target,
1754 left, top,
1755 width, height,
1756 target->config(),
1757 tmpPixels, 4 * width, flags);
1758 return;
1759 }
bsalomon@google.coma85449d2011-11-19 02:36:05 +00001760
1761 bool swapRAndB = fGpu->preferredReadPixelsConfig(config) ==
1762 GrPixelConfigSwapRAndB(config);
1763 if (swapRAndB) {
1764 config = GrPixelConfigSwapRAndB(config);
1765 }
1766
bsalomon@google.comfea37b52011-04-25 15:51:06 +00001767 const GrTextureDesc desc = {
bsalomon@google.com78d6cf92012-01-30 18:09:31 +00001768 kNone_GrTextureFlags, width, height, config, {0}
bsalomon@google.com27847de2011-02-22 20:59:41 +00001769 };
bsalomon@google.com50398bf2011-07-26 20:45:30 +00001770 GrAutoScratchTexture ast(this, desc);
1771 GrTexture* texture = ast.texture();
bsalomon@google.com27847de2011-02-22 20:59:41 +00001772 if (NULL == texture) {
1773 return;
1774 }
bsalomon@google.com6f379512011-11-16 20:36:03 +00001775 this->internalWriteTexturePixels(texture, 0, 0, width, height,
1776 config, buffer, rowBytes, flags);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001777
bsalomon@google.com27847de2011-02-22 20:59:41 +00001778 GrDrawTarget::AutoStateRestore asr(fGpu);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001779 GrDrawState* drawState = fGpu->drawState();
bsalomon@google.com52a5dcb2012-01-17 16:01:37 +00001780 drawState->reset();
bsalomon@google.com27847de2011-02-22 20:59:41 +00001781
1782 GrMatrix matrix;
1783 matrix.setTranslate(GrIntToScalar(left), GrIntToScalar(top));
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001784 drawState->setViewMatrix(matrix);
1785 drawState->setRenderTarget(target);
1786 drawState->setTexture(0, texture);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001787
bsalomon@google.com5c638652011-07-18 19:31:59 +00001788 matrix.setIDiv(texture->width(), texture->height());
bsalomon@google.com1e266f82011-12-12 16:11:33 +00001789 drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode,
1790 GrSamplerState::kNearest_Filter,
1791 matrix);
1792 drawState->sampler(0)->setRAndBSwap(swapRAndB);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001793
1794 GrVertexLayout layout = GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
1795 static const int VCOUNT = 4;
bsalomon@google.com6513cd02011-08-05 20:12:30 +00001796 // TODO: Use GrGpu::drawRect here
bsalomon@google.com27847de2011-02-22 20:59:41 +00001797 GrDrawTarget::AutoReleaseGeometry geo(fGpu, layout, VCOUNT, 0);
1798 if (!geo.succeeded()) {
bsalomon@google.com6513cd02011-08-05 20:12:30 +00001799 GrPrintf("Failed to get space for vertices!\n");
bsalomon@google.com27847de2011-02-22 20:59:41 +00001800 return;
1801 }
1802 ((GrPoint*)geo.vertices())->setIRectFan(0, 0, width, height);
1803 fGpu->drawNonIndexed(kTriangleFan_PrimitiveType, 0, VCOUNT);
1804}
1805////////////////////////////////////////////////////////////////////////////////
1806
bsalomon@google.combc4b6542011-11-19 13:56:11 +00001807void GrContext::setPaint(const GrPaint& paint, GrDrawTarget* target) {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001808 GrDrawState* drawState = target->drawState();
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001809
1810 for (int i = 0; i < GrPaint::kMaxTextures; ++i) {
1811 int s = i + GrPaint::kFirstTextureStage;
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001812 drawState->setTexture(s, paint.getTexture(i));
bsalomon@google.combc4b6542011-11-19 13:56:11 +00001813 ASSERT_OWNED_RESOURCE(paint.getTexture(i));
bsalomon@google.comf864ec42011-12-12 21:57:03 +00001814 if (paint.getTexture(i)) {
1815 *drawState->sampler(s) = paint.getTextureSampler(i);
1816 }
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001817 }
1818
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001819 drawState->setFirstCoverageStage(GrPaint::kFirstMaskStage);
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001820
1821 for (int i = 0; i < GrPaint::kMaxMasks; ++i) {
1822 int s = i + GrPaint::kFirstMaskStage;
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001823 drawState->setTexture(s, paint.getMask(i));
bsalomon@google.combc4b6542011-11-19 13:56:11 +00001824 ASSERT_OWNED_RESOURCE(paint.getMask(i));
bsalomon@google.comf864ec42011-12-12 21:57:03 +00001825 if (paint.getMask(i)) {
1826 *drawState->sampler(s) = paint.getMaskSampler(i);
1827 }
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001828 }
1829
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001830 drawState->setColor(paint.fColor);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001831
1832 if (paint.fDither) {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001833 drawState->enableState(GrDrawState::kDither_StateBit);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001834 } else {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001835 drawState->disableState(GrDrawState::kDither_StateBit);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001836 }
1837 if (paint.fAntiAlias) {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001838 drawState->enableState(GrDrawState::kHWAntialias_StateBit);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001839 } else {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001840 drawState->disableState(GrDrawState::kHWAntialias_StateBit);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001841 }
senorblanco@chromium.org50bdad82012-01-03 20:51:57 +00001842 if (paint.fColorMatrixEnabled) {
1843 drawState->enableState(GrDrawState::kColorMatrix_StateBit);
1844 } else {
1845 drawState->disableState(GrDrawState::kColorMatrix_StateBit);
1846 }
bsalomon@google.com6b67e212011-12-09 16:10:24 +00001847 drawState->setBlendFunc(paint.fSrcBlendCoeff, paint.fDstBlendCoeff);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001848 drawState->setColorFilter(paint.fColorFilterColor, paint.fColorFilterXfermode);
senorblanco@chromium.org50bdad82012-01-03 20:51:57 +00001849 drawState->setColorMatrix(paint.fColorMatrix);
bsalomon@google.comdd1be602012-01-18 20:34:00 +00001850 drawState->setCoverage(paint.fCoverage);
bsalomon@google.comd46e2422011-09-23 17:40:07 +00001851
1852 if (paint.getActiveMaskStageMask() && !target->canApplyCoverage()) {
1853 GrPrintf("Partial pixel coverage will be incorrectly blended.\n");
1854 }
bsalomon@google.com27847de2011-02-22 20:59:41 +00001855}
1856
bsalomon@google.comde6ac2d2011-02-25 21:50:42 +00001857GrDrawTarget* GrContext::prepareToDraw(const GrPaint& paint,
bsalomon@google.com27847de2011-02-22 20:59:41 +00001858 DrawCategory category) {
1859 if (category != fLastDrawCategory) {
1860 flushDrawBuffer();
1861 fLastDrawCategory = category;
1862 }
bsalomon@google.combc4b6542011-11-19 13:56:11 +00001863 this->setPaint(paint, fGpu);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001864 GrDrawTarget* target = fGpu;
1865 switch (category) {
1866 case kText_DrawCategory:
1867#if DEFER_TEXT_RENDERING
1868 target = fDrawBuffer;
1869 fDrawBuffer->initializeDrawStateAndClip(*fGpu);
1870#else
1871 target = fGpu;
1872#endif
1873 break;
1874 case kUnbuffered_DrawCategory:
1875 target = fGpu;
1876 break;
1877 case kBuffered_DrawCategory:
1878 target = fDrawBuffer;
1879 fDrawBuffer->initializeDrawStateAndClip(*fGpu);
1880 break;
1881 }
1882 return target;
1883}
1884
bsalomon@google.com289533a2011-10-27 12:34:25 +00001885GrPathRenderer* GrContext::getPathRenderer(const GrPath& path,
1886 GrPathFill fill,
1887 bool antiAlias) {
bsalomon@google.com30085192011-08-19 15:42:31 +00001888 if (NULL == fPathRendererChain) {
1889 fPathRendererChain =
1890 new GrPathRendererChain(this, GrPathRendererChain::kNone_UsageFlag);
1891 }
bsalomon@google.com289533a2011-10-27 12:34:25 +00001892 return fPathRendererChain->getPathRenderer(fGpu->getCaps(), path,
1893 fill, antiAlias);
bsalomon@google.com30085192011-08-19 15:42:31 +00001894}
1895
bsalomon@google.com27847de2011-02-22 20:59:41 +00001896////////////////////////////////////////////////////////////////////////////////
1897
bsalomon@google.com27847de2011-02-22 20:59:41 +00001898void GrContext::setRenderTarget(GrRenderTarget* target) {
bsalomon@google.combc4b6542011-11-19 13:56:11 +00001899 ASSERT_OWNED_RESOURCE(target);
bsalomon@google.com8fe72472011-03-30 21:26:44 +00001900 this->flush(false);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001901 fGpu->drawState()->setRenderTarget(target);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001902}
1903
1904GrRenderTarget* GrContext::getRenderTarget() {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001905 return fGpu->drawState()->getRenderTarget();
bsalomon@google.com27847de2011-02-22 20:59:41 +00001906}
1907
1908const GrRenderTarget* GrContext::getRenderTarget() const {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001909 return fGpu->getDrawState().getRenderTarget();
bsalomon@google.com27847de2011-02-22 20:59:41 +00001910}
1911
1912const GrMatrix& GrContext::getMatrix() const {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001913 return fGpu->getDrawState().getViewMatrix();
bsalomon@google.com27847de2011-02-22 20:59:41 +00001914}
1915
1916void GrContext::setMatrix(const GrMatrix& m) {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001917 fGpu->drawState()->setViewMatrix(m);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001918}
1919
1920void GrContext::concatMatrix(const GrMatrix& m) const {
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00001921 fGpu->drawState()->preConcatViewMatrix(m);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001922}
1923
1924static inline intptr_t setOrClear(intptr_t bits, int shift, intptr_t pred) {
1925 intptr_t mask = 1 << shift;
1926 if (pred) {
1927 bits |= mask;
1928 } else {
1929 bits &= ~mask;
1930 }
1931 return bits;
1932}
1933
1934void GrContext::resetStats() {
1935 fGpu->resetStats();
1936}
1937
bsalomon@google.com05ef5102011-05-02 21:14:59 +00001938const GrGpuStats& GrContext::getStats() const {
bsalomon@google.com27847de2011-02-22 20:59:41 +00001939 return fGpu->getStats();
1940}
1941
1942void GrContext::printStats() const {
1943 fGpu->printStats();
1944}
1945
bsalomon@google.com583a1e32011-08-17 13:42:46 +00001946GrContext::GrContext(GrGpu* gpu) {
bsalomon@google.com27847de2011-02-22 20:59:41 +00001947 fGpu = gpu;
1948 fGpu->ref();
bsalomon@google.com669fdc42011-04-05 17:08:27 +00001949 fGpu->setContext(this);
bsalomon@google.com8fe72472011-03-30 21:26:44 +00001950
bsalomon@google.com30085192011-08-19 15:42:31 +00001951 fPathRendererChain = NULL;
bsalomon@google.comdfe75bc2011-03-25 12:31:16 +00001952
bsalomon@google.com50398bf2011-07-26 20:45:30 +00001953 fTextureCache = new GrResourceCache(MAX_TEXTURE_CACHE_COUNT,
1954 MAX_TEXTURE_CACHE_BYTES);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001955 fFontCache = new GrFontCache(fGpu);
1956
1957 fLastDrawCategory = kUnbuffered_DrawCategory;
1958
bsalomon@google.com8fe72472011-03-30 21:26:44 +00001959 fDrawBuffer = NULL;
1960 fDrawBufferVBAllocPool = NULL;
1961 fDrawBufferIBAllocPool = NULL;
1962
bsalomon@google.com205d4602011-04-25 12:43:45 +00001963 fAAFillRectIndexBuffer = NULL;
1964 fAAStrokeRectIndexBuffer = NULL;
bsalomon@google.com91958362011-06-13 17:58:13 +00001965
bsalomon@google.com8fe72472011-03-30 21:26:44 +00001966 this->setupDrawBuffer();
1967}
1968
1969void GrContext::setupDrawBuffer() {
1970
1971 GrAssert(NULL == fDrawBuffer);
1972 GrAssert(NULL == fDrawBufferVBAllocPool);
1973 GrAssert(NULL == fDrawBufferIBAllocPool);
1974
bsalomon@google.com27847de2011-02-22 20:59:41 +00001975#if DEFER_TEXT_RENDERING || BATCH_RECT_TO_RECT
bsalomon@google.comde6ac2d2011-02-25 21:50:42 +00001976 fDrawBufferVBAllocPool =
bsalomon@google.com8fe72472011-03-30 21:26:44 +00001977 new GrVertexBufferAllocPool(fGpu, false,
bsalomon@google.com27847de2011-02-22 20:59:41 +00001978 DRAW_BUFFER_VBPOOL_BUFFER_SIZE,
1979 DRAW_BUFFER_VBPOOL_PREALLOC_BUFFERS);
bsalomon@google.comde6ac2d2011-02-25 21:50:42 +00001980 fDrawBufferIBAllocPool =
bsalomon@google.com8fe72472011-03-30 21:26:44 +00001981 new GrIndexBufferAllocPool(fGpu, false,
bsalomon@google.comde6ac2d2011-02-25 21:50:42 +00001982 DRAW_BUFFER_IBPOOL_BUFFER_SIZE,
bsalomon@google.com27847de2011-02-22 20:59:41 +00001983 DRAW_BUFFER_IBPOOL_PREALLOC_BUFFERS);
1984
bsalomon@google.com471d4712011-08-23 15:45:25 +00001985 fDrawBuffer = new GrInOrderDrawBuffer(fGpu,
1986 fDrawBufferVBAllocPool,
bsalomon@google.com27847de2011-02-22 20:59:41 +00001987 fDrawBufferIBAllocPool);
bsalomon@google.com27847de2011-02-22 20:59:41 +00001988#endif
1989
1990#if BATCH_RECT_TO_RECT
1991 fDrawBuffer->setQuadIndexBuffer(this->getQuadIndexBuffer());
1992#endif
bsalomon@google.com27847de2011-02-22 20:59:41 +00001993}
1994
bsalomon@google.com27847de2011-02-22 20:59:41 +00001995GrDrawTarget* GrContext::getTextTarget(const GrPaint& paint) {
1996 GrDrawTarget* target;
1997#if DEFER_TEXT_RENDERING
1998 target = prepareToDraw(paint, kText_DrawCategory);
1999#else
2000 target = prepareToDraw(paint, kUnbuffered_DrawCategory);
2001#endif
bsalomon@google.combc4b6542011-11-19 13:56:11 +00002002 this->setPaint(paint, target);
bsalomon@google.com27847de2011-02-22 20:59:41 +00002003 return target;
2004}
2005
2006const GrIndexBuffer* GrContext::getQuadIndexBuffer() const {
2007 return fGpu->getQuadIndexBuffer();
2008}
bsalomon@google.comdfe75bc2011-03-25 12:31:16 +00002009
senorblanco@chromium.orgaadd9f82011-07-12 19:44:51 +00002010void GrContext::convolve(GrTexture* texture,
2011 const SkRect& rect,
senorblanco@chromium.orgaadd9f82011-07-12 19:44:51 +00002012 const float* kernel,
senorblanco@chromium.org05054f12012-03-02 21:05:45 +00002013 int kernelWidth,
2014 GrSamplerState::FilterDirection direction) {
bsalomon@google.combc4b6542011-11-19 13:56:11 +00002015 ASSERT_OWNED_RESOURCE(texture);
2016
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +00002017 GrDrawTarget::AutoStateRestore asr(fGpu);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00002018 GrDrawState* drawState = fGpu->drawState();
bsalomon@google.comdd1be602012-01-18 20:34:00 +00002019 GrRenderTarget* target = drawState->getRenderTarget();
2020 drawState->reset();
2021 drawState->setRenderTarget(target);
bsalomon@google.com1e266f82011-12-12 16:11:33 +00002022 GrMatrix sampleM;
2023 sampleM.setIDiv(texture->width(), texture->height());
2024 drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode,
2025 GrSamplerState::kConvolution_Filter,
2026 sampleM);
senorblanco@chromium.org05054f12012-03-02 21:05:45 +00002027 drawState->sampler(0)->setConvolutionParams(kernelWidth, kernel);
2028 drawState->sampler(0)->setFilterDirection(direction);
2029 drawState->setTexture(0, texture);
2030 fGpu->drawSimpleRect(rect, NULL, 1 << 0);
2031}
bsalomon@google.com1e266f82011-12-12 16:11:33 +00002032
senorblanco@chromium.org05054f12012-03-02 21:05:45 +00002033void GrContext::applyMorphology(GrTexture* texture,
2034 const SkRect& rect,
2035 int radius,
2036 GrSamplerState::Filter filter,
2037 GrSamplerState::FilterDirection direction) {
2038 ASSERT_OWNED_RESOURCE(texture);
2039 GrAssert(filter == GrSamplerState::kErode_Filter ||
2040 filter == GrSamplerState::kDilate_Filter);
2041
2042 GrDrawTarget::AutoStateRestore asr(fGpu);
2043 GrDrawState* drawState = fGpu->drawState();
2044 GrRenderTarget* target = drawState->getRenderTarget();
2045 drawState->reset();
2046 drawState->setRenderTarget(target);
2047 GrMatrix sampleM;
2048 sampleM.setIDiv(texture->width(), texture->height());
2049 drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode,
2050 filter,
2051 sampleM);
2052 drawState->sampler(0)->setMorphologyRadius(radius);
2053 drawState->sampler(0)->setFilterDirection(direction);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +00002054 drawState->setTexture(0, texture);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +00002055 fGpu->drawSimpleRect(rect, NULL, 1 << 0);
2056}
bsalomon@google.comc4364992011-11-07 15:54:49 +00002057
2058///////////////////////////////////////////////////////////////////////////////