blob: 9b94b7a6aef6081f540889ef0633df4799d5ccaf [file] [log] [blame]
reed@google.com873cb1e2010-12-23 15:00:45 +00001/*
2 Copyright 2010 Google Inc.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
reed@google.comac10a2d2010-12-22 21:39:39 +000017
18#include "GrContext.h"
19#include "GrTextureCache.h"
20#include "GrTextStrike.h"
21#include "GrMemory.h"
22#include "GrPathIter.h"
23#include "GrClipIterator.h"
24#include "GrIndexBuffer.h"
25
26#define DEFER_TEXT_RENDERING 1
27
28static const size_t MAX_TEXTURE_CACHE_COUNT = 128;
29static const size_t MAX_TEXTURE_CACHE_BYTES = 8 * 1024 * 1024;
30
31#if DEFER_TEXT_RENDERING
32 static const uint32_t POOL_VB_SIZE = 2048 *
bsalomon@google.com2e7b43d2011-01-18 20:57:22 +000033 GrDrawTarget::VertexSize(
34 GrDrawTarget::kTextFormat_VertexLayoutBit |
bsalomon@google.com1572b072011-01-18 15:30:57 +000035 GrDrawTarget::StageTexCoordVertexLayoutBit(0,0));
reed@google.comac10a2d2010-12-22 21:39:39 +000036 static const uint32_t NUM_POOL_VBS = 8;
37#else
38 static const uint32_t POOL_VB_SIZE = 0;
39 static const uint32_t NUM_POOL_VBS = 0;
40
41#endif
42
43GrContext* GrContext::Create(GrGpu::Engine engine,
44 GrGpu::Platform3DContext context3D) {
45 GrContext* ctx = NULL;
46 GrGpu* fGpu = GrGpu::Create(engine, context3D);
47 if (NULL != fGpu) {
48 ctx = new GrContext(fGpu);
49 fGpu->unref();
50 }
51 return ctx;
52}
53
reed@google.com873cb1e2010-12-23 15:00:45 +000054GrContext* GrContext::CreateGLShaderContext() {
55 return GrContext::Create(GrGpu::kOpenGL_Shaders_Engine, NULL);
56}
57
reed@google.comac10a2d2010-12-22 21:39:39 +000058GrContext::~GrContext() {
59 fGpu->unref();
60 delete fTextureCache;
61 delete fFontCache;
62}
63
64void GrContext::abandonAllTextures() {
65 fTextureCache->deleteAll(GrTextureCache::kAbandonTexture_DeleteMode);
66 fFontCache->abandonAll();
67}
68
69GrTextureEntry* GrContext::findAndLockTexture(GrTextureKey* key,
70 const GrSamplerState& sampler) {
71 finalizeTextureKey(key, sampler);
72 return fTextureCache->findAndLock(*key);
73}
74
75static void stretchImage(void* dst,
76 int dstW,
77 int dstH,
78 void* src,
79 int srcW,
80 int srcH,
81 int bpp) {
82 GrFixed dx = (srcW << 16) / dstW;
83 GrFixed dy = (srcH << 16) / dstH;
84
85 GrFixed y = dy >> 1;
86
87 int dstXLimit = dstW*bpp;
88 for (int j = 0; j < dstH; ++j) {
89 GrFixed x = dx >> 1;
90 void* srcRow = (uint8_t*)src + (y>>16)*srcW*bpp;
91 void* dstRow = (uint8_t*)dst + j*dstW*bpp;
92 for (int i = 0; i < dstXLimit; i += bpp) {
93 memcpy((uint8_t*) dstRow + i,
94 (uint8_t*) srcRow + (x>>16)*bpp,
95 bpp);
96 x += dx;
97 }
98 y += dy;
99 }
100}
101
102GrTextureEntry* GrContext::createAndLockTexture(GrTextureKey* key,
103 const GrSamplerState& sampler,
104 const GrGpu::TextureDesc& desc,
105 void* srcData, size_t rowBytes) {
106 GrAssert(key->width() == desc.fWidth);
107 GrAssert(key->height() == desc.fHeight);
108
109#if GR_DUMP_TEXTURE_UPLOAD
110 GrPrintf("GrContext::createAndLockTexture [%d %d]\n", desc.fWidth, desc.fHeight);
111#endif
112
113 GrTextureEntry* entry = NULL;
114 bool special = finalizeTextureKey(key, sampler);
115 if (special) {
116 GrTextureEntry* clampEntry;
117 GrTextureKey clampKey(*key);
118 clampEntry = findAndLockTexture(&clampKey, GrSamplerState::ClampNoFilter());
119
120 if (NULL == clampEntry) {
121 clampEntry = createAndLockTexture(&clampKey,
122 GrSamplerState::ClampNoFilter(),
123 desc, srcData, rowBytes);
124 GrAssert(NULL != clampEntry);
125 if (NULL == clampEntry) {
126 return NULL;
127 }
128 }
129 GrTexture* clampTexture = clampEntry->texture();
130 GrGpu::TextureDesc rtDesc = desc;
131 rtDesc.fFlags |= GrGpu::kRenderTarget_TextureFlag |
132 GrGpu::kNoPathRendering_TextureFlag;
133 rtDesc.fWidth = GrNextPow2(GrMax<int>(desc.fWidth,
134 fGpu->minRenderTargetWidth()));
135 rtDesc.fHeight = GrNextPow2(GrMax<int>(desc.fHeight,
136 fGpu->minRenderTargetHeight()));
137
138 GrTexture* texture = fGpu->createTexture(rtDesc, NULL, 0);
139
140 if (NULL != texture) {
bsalomon@google.com5782d712011-01-21 21:03:59 +0000141 GrDrawTarget::AutoStateRestore asr(fGpu);
reed@google.comac10a2d2010-12-22 21:39:39 +0000142 fGpu->setRenderTarget(texture->asRenderTarget());
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000143 fGpu->setTexture(0, clampEntry->texture());
bsalomon@google.com5782d712011-01-21 21:03:59 +0000144 fGpu->setStencilPass(GrDrawTarget::kNone_StencilPass);
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000145 fGpu->setTextureMatrix(0, GrMatrix::I());
reed@google.comac10a2d2010-12-22 21:39:39 +0000146 fGpu->setViewMatrix(GrMatrix::I());
147 fGpu->setAlpha(0xff);
bsalomon@google.com5782d712011-01-21 21:03:59 +0000148 fGpu->setBlendFunc(GrDrawTarget::kOne_BlendCoeff, GrDrawTarget::kZero_BlendCoeff);
149 fGpu->disableState(GrDrawTarget::kDither_StateBit |
150 GrDrawTarget::kClip_StateBit |
151 GrDrawTarget::kAntialias_StateBit);
reed@google.comac10a2d2010-12-22 21:39:39 +0000152 GrSamplerState stretchSampler(GrSamplerState::kClamp_WrapMode,
153 GrSamplerState::kClamp_WrapMode,
154 sampler.isFilter());
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000155 fGpu->setSamplerState(0, stretchSampler);
reed@google.comac10a2d2010-12-22 21:39:39 +0000156
157 static const GrVertexLayout layout =
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000158 GrDrawTarget::StageTexCoordVertexLayoutBit(0,0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000159 GrDrawTarget::AutoReleaseGeometry arg(fGpu, layout, 4, 0);
160
161 if (arg.succeeded()) {
162 GrPoint* verts = (GrPoint*) arg.vertices();
163 verts[0].setIRectFan(0, 0,
164 texture->contentWidth(),
165 texture->contentHeight(),
166 2*sizeof(GrPoint));
167 GrScalar tw = GrFixedToScalar(GR_Fixed1 *
168 clampTexture->contentWidth() /
169 clampTexture->allocWidth());
170 GrScalar th = GrFixedToScalar(GR_Fixed1 *
171 clampTexture->contentHeight() /
172 clampTexture->allocHeight());
173 verts[1].setRectFan(0, 0, tw, th, 2*sizeof(GrPoint));
bsalomon@google.com5782d712011-01-21 21:03:59 +0000174 fGpu->drawNonIndexed(GrDrawTarget::kTriangleFan_PrimitiveType,
reed@google.comac10a2d2010-12-22 21:39:39 +0000175 0, 4);
176 entry = fTextureCache->createAndLock(*key, texture);
177 }
178 texture->removeRenderTarget();
179 } else {
180 // TODO: Our CPU stretch doesn't filter. But we create separate
181 // stretched textures when the sampler state is either filtered or
182 // not. Either implement filtered stretch blit on CPU or just create
183 // one when FBO case fails.
184
185 rtDesc.fFlags = 0;
186 // no longer need to clamp at min RT size.
187 rtDesc.fWidth = GrNextPow2(desc.fWidth);
188 rtDesc.fHeight = GrNextPow2(desc.fHeight);
189 int bpp = GrTexture::BytesPerPixel(desc.fFormat);
190 GrAutoSMalloc<128*128*4> stretchedPixels(bpp *
191 rtDesc.fWidth *
192 rtDesc.fHeight);
193 stretchImage(stretchedPixels.get(), rtDesc.fWidth, rtDesc.fHeight,
194 srcData, desc.fWidth, desc.fHeight, bpp);
195
196 size_t stretchedRowBytes = rtDesc.fWidth * bpp;
197
198 GrTexture* texture = fGpu->createTexture(rtDesc,
199 stretchedPixels.get(),
200 stretchedRowBytes);
201 GrAssert(NULL != texture);
202 entry = fTextureCache->createAndLock(*key, texture);
203 }
204 fTextureCache->unlock(clampEntry);
205
206 } else {
207 GrTexture* texture = fGpu->createTexture(desc, srcData, rowBytes);
208 if (NULL != texture) {
209 entry = fTextureCache->createAndLock(*key, texture);
210 } else {
211 entry = NULL;
212 }
213 }
214 return entry;
215}
216
217void GrContext::unlockTexture(GrTextureEntry* entry) {
218 fTextureCache->unlock(entry);
219}
220
221void GrContext::detachCachedTexture(GrTextureEntry* entry) {
222 fTextureCache->detach(entry);
223}
224
225void GrContext::reattachAndUnlockCachedTexture(GrTextureEntry* entry) {
226 fTextureCache->reattachAndUnlock(entry);
227}
228
229GrTexture* GrContext::createUncachedTexture(const GrGpu::TextureDesc& desc,
230 void* srcData,
231 size_t rowBytes) {
232 return fGpu->createTexture(desc, srcData, rowBytes);
233}
234
reed@google.com01804b42011-01-18 21:50:41 +0000235void GrContext::getTextureCacheLimits(int* maxTextures,
236 size_t* maxTextureBytes) const {
237 fTextureCache->getLimits(maxTextures, maxTextureBytes);
238}
239
240void GrContext::setTextureCacheLimits(int maxTextures, size_t maxTextureBytes) {
241 fTextureCache->setLimits(maxTextures, maxTextureBytes);
242}
243
reed@google.com02a7e6c2011-01-28 21:21:49 +0000244int GrContext::getMaxTextureDimension() {
245 return fGpu->maxTextureDimension();
246}
247
reed@google.com01804b42011-01-18 21:50:41 +0000248///////////////////////////////////////////////////////////////////////////////
249
reed@google.comac10a2d2010-12-22 21:39:39 +0000250GrRenderTarget* GrContext::createPlatformRenderTarget(intptr_t platformRenderTarget,
251 int width, int height) {
252 return fGpu->createPlatformRenderTarget(platformRenderTarget,
253 width, height);
254}
255
256bool GrContext::supportsIndex8PixelConfig(const GrSamplerState& sampler,
257 int width, int height) {
258 if (!fGpu->supports8BitPalette()) {
259 return false;
260 }
261
262 bool needsRepeat = sampler.getWrapX() != GrSamplerState::kClamp_WrapMode ||
263 sampler.getWrapY() != GrSamplerState::kClamp_WrapMode;
264 bool isPow2 = GrIsPow2(width) && GrIsPow2(height);
265
266 switch (fGpu->npotTextureSupport()) {
267 case GrGpu::kNone_NPOTTextureType:
268 return isPow2;
269 case GrGpu::kNoRepeat_NPOTTextureType:
270 return isPow2 || !needsRepeat;
271 case GrGpu::kNonRendertarget_NPOTTextureType:
272 case GrGpu::kFull_NPOTTextureType:
273 return true;
274 }
275 // should never get here
276 GrAssert(!"Bad enum from fGpu->npotTextureSupport");
277 return false;
278}
279
280////////////////////////////////////////////////////////////////////////////////
281
bsalomon@google.com5782d712011-01-21 21:03:59 +0000282void GrContext::setClip(const GrClip& clip) {
283 fGpu->setClip(clip);
284 fGpu->enableState(GrDrawTarget::kClip_StateBit);
285}
286
287void GrContext::setClip(const GrIRect& rect) {
288 GrClip clip;
289 clip.setRect(rect);
290 fGpu->setClip(clip);
291}
292
293////////////////////////////////////////////////////////////////////////////////
294
reed@google.comac10a2d2010-12-22 21:39:39 +0000295void GrContext::eraseColor(GrColor color) {
296 fGpu->eraseColor(color);
297}
298
bsalomon@google.com5782d712011-01-21 21:03:59 +0000299void GrContext::drawPaint(const GrPaint& paint) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000300 // set rect to be big enough to fill the space, but not super-huge, so we
301 // don't overflow fixed-point implementations
302 GrRect r(fGpu->getClip().getBounds());
303 GrMatrix inverse;
304 if (fGpu->getViewInverse(&inverse)) {
305 inverse.mapRect(&r);
306 } else {
307 GrPrintf("---- fGpu->getViewInverse failed\n");
308 }
bsalomon@google.com5782d712011-01-21 21:03:59 +0000309 this->drawRect(paint, r);
reed@google.comac10a2d2010-12-22 21:39:39 +0000310}
311
312/* create a triangle strip that strokes the specified triangle. There are 8
313 unique vertices, but we repreat the last 2 to close up. Alternatively we
314 could use an indices array, and then only send 8 verts, but not sure that
315 would be faster.
316 */
317static void setStrokeRectStrip(GrPoint verts[10], const GrRect& rect,
318 GrScalar width) {
319 const GrScalar rad = GrScalarHalf(width);
320
321 verts[0].set(rect.fLeft + rad, rect.fTop + rad);
322 verts[1].set(rect.fLeft - rad, rect.fTop - rad);
323 verts[2].set(rect.fRight - rad, rect.fTop + rad);
324 verts[3].set(rect.fRight + rad, rect.fTop - rad);
325 verts[4].set(rect.fRight - rad, rect.fBottom - rad);
326 verts[5].set(rect.fRight + rad, rect.fBottom + rad);
327 verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
328 verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
329 verts[8] = verts[0];
330 verts[9] = verts[1];
331}
332
bsalomon@google.com5782d712011-01-21 21:03:59 +0000333void GrContext::drawRect(const GrPaint& paint,
334 const GrRect& rect,
335 GrScalar width) {
336
337 GrVertexLayout layout = (NULL != paint.getTexture()) ?
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000338 GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0) :
reed@google.comac10a2d2010-12-22 21:39:39 +0000339 0;
340
341 static const int worstCaseVertCount = 10;
342 GrDrawTarget::AutoReleaseGeometry geo(fGpu, layout, worstCaseVertCount, 0);
343 if (!geo.succeeded()) {
344 return;
345 }
346
bsalomon@google.com5782d712011-01-21 21:03:59 +0000347 this->prepareToDraw(paint);
reed@google.comac10a2d2010-12-22 21:39:39 +0000348
349 int vertCount;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000350 GrDrawTarget::PrimitiveType primType;
reed@google.comac10a2d2010-12-22 21:39:39 +0000351 GrPoint* vertex = geo.positions();
352
353 if (width >= 0) {
354 if (width > 0) {
355 vertCount = 10;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000356 primType = GrDrawTarget::kTriangleStrip_PrimitiveType;
reed@google.comac10a2d2010-12-22 21:39:39 +0000357 setStrokeRectStrip(vertex, rect, width);
358 } else {
359 // hairline
360 vertCount = 5;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000361 primType = GrDrawTarget::kLineStrip_PrimitiveType;
reed@google.comac10a2d2010-12-22 21:39:39 +0000362 vertex[0].set(rect.fLeft, rect.fTop);
363 vertex[1].set(rect.fRight, rect.fTop);
364 vertex[2].set(rect.fRight, rect.fBottom);
365 vertex[3].set(rect.fLeft, rect.fBottom);
366 vertex[4].set(rect.fLeft, rect.fTop);
367 }
368 } else {
369 vertCount = 4;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000370 primType = GrDrawTarget::kTriangleFan_PrimitiveType;
reed@google.comac10a2d2010-12-22 21:39:39 +0000371 vertex->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
372 }
373
374 fGpu->drawNonIndexed(primType, 0, vertCount);
375}
376
bsalomon@google.com5782d712011-01-21 21:03:59 +0000377void GrContext::drawRectToRect(const GrPaint& paint,
378 const GrRect& dstRect,
379 const GrRect& srcRect) {
380
381 if (NULL == paint.getTexture()) {
382 drawRect(paint, dstRect);
383 return;
384 }
385
386 GrVertexLayout layout = GrDrawTarget::StageTexCoordVertexLayoutBit(0,0);
387 static const int VCOUNT = 4;
388
389 GrDrawTarget::AutoReleaseGeometry geo(fGpu, layout, VCOUNT, 0);
390 if (!geo.succeeded()) {
391 return;
392 }
393
394 this->prepareToDraw(paint);
395
396 GrPoint* vertex = (GrPoint*) geo.vertices();
397
398 vertex[0].setRectFan(dstRect.fLeft, dstRect.fTop,
399 dstRect.fRight, dstRect.fBottom,
400 2 * sizeof(GrPoint));
401 vertex[1].setRectFan(srcRect.fLeft, srcRect.fTop,
402 srcRect.fRight, srcRect.fBottom,
403 2 * sizeof(GrPoint));
404
405 fGpu->drawNonIndexed(GrDrawTarget::kTriangleFan_PrimitiveType, 0, VCOUNT);
406}
407
408void GrContext::drawVertices(const GrPaint& paint,
409 GrDrawTarget::PrimitiveType primitiveType,
410 int vertexCount,
411 const GrPoint positions[],
412 const GrPoint texCoords[],
413 const GrColor colors[],
414 const uint16_t indices[],
415 int indexCount) {
416 GrVertexLayout layout = 0;
417 bool interLeave = false;
418
419 GrDrawTarget::AutoReleaseGeometry geo;
420
421 this->prepareToDraw(paint);
422
423 if (NULL != paint.getTexture()) {
424 if (NULL == texCoords) {
425 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
426 } else {
427 layout |= GrDrawTarget::StageTexCoordVertexLayoutBit(0,0);
428 interLeave = true;
429 }
430 }
431
432 if (NULL != colors) {
433 layout |= GrDrawTarget::kColor_VertexLayoutBit;
434 }
435
436 static const GrVertexLayout interleaveMask =
437 (GrDrawTarget::StageTexCoordVertexLayoutBit(0,0) |
438 GrDrawTarget::kColor_VertexLayoutBit);
439 if (interleaveMask & layout) {
440 if (!geo.set(fGpu, layout, vertexCount, 0)) {
441 GrPrintf("Failed to get space for vertices!");
442 return;
443 }
444 int texOffsets[GrDrawTarget::kMaxTexCoords];
445 int colorOffset;
446 int vsize = GrDrawTarget::VertexSizeAndOffsetsByIdx(layout,
447 texOffsets,
448 &colorOffset);
449 void* curVertex = geo.vertices();
450
451 for (int i = 0; i < vertexCount; ++i) {
452 *((GrPoint*)curVertex) = positions[i];
453
454 if (texOffsets[0] > 0) {
455 *(GrPoint*)((intptr_t)curVertex + texOffsets[0]) = texCoords[i];
456 }
457 if (colorOffset > 0) {
458 *(GrColor*)((intptr_t)curVertex + colorOffset) = colors[i];
459 }
460 curVertex = (void*)((intptr_t)curVertex + vsize);
461 }
462 } else {
463 fGpu->setVertexSourceToArray(positions, layout);
464 }
465
466 if (NULL != indices) {
467 fGpu->setIndexSourceToArray(indices);
468 fGpu->drawIndexed(primitiveType, 0, 0, vertexCount, indexCount);
469 } else {
470 fGpu->drawNonIndexed(primitiveType, 0, vertexCount);
471 }
472}
473
474
reed@google.comac10a2d2010-12-22 21:39:39 +0000475////////////////////////////////////////////////////////////////////////////////
476
477#define NEW_EVAL 1 // Use adaptive path tesselation
478#define STENCIL_OFF 0 // Always disable stencil (even when needed)
479#define CPU_TRANSFORM 0 // Transform path verts on CPU
480
481#if NEW_EVAL
482
483#define EVAL_TOL GR_Scalar1
484
485static uint32_t quadratic_point_count(const GrPoint points[], GrScalar tol) {
486 GrScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]);
487 // TODO: fixed points sqrt
488 if (d < tol) {
489 return 1;
490 } else {
491 // Each time we subdivide, d should be cut in 4. So we need to
492 // subdivide x = log4(d/tol) times. x subdivisions creates 2^(x)
493 // points.
494 // 2^(log4(x)) = sqrt(x);
495 d = ceilf(sqrtf(d/tol));
496 return GrNextPow2((uint32_t)d);
497 }
498}
499
500static uint32_t generate_quadratic_points(const GrPoint& p0,
501 const GrPoint& p1,
502 const GrPoint& p2,
503 GrScalar tolSqd,
504 GrPoint** points,
505 uint32_t pointsLeft) {
506 if (pointsLeft < 2 ||
507 (p1.distanceToLineSegmentBetweenSqd(p0, p2)) < tolSqd) {
508 (*points)[0] = p2;
509 *points += 1;
510 return 1;
511 }
512
513 GrPoint q[] = {
514 GrPoint(GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY)),
515 GrPoint(GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY)),
516 };
517 GrPoint r(GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY));
518
519 pointsLeft >>= 1;
520 uint32_t a = generate_quadratic_points(p0, q[0], r, tolSqd, points, pointsLeft);
521 uint32_t b = generate_quadratic_points(r, q[1], p2, tolSqd, points, pointsLeft);
522 return a + b;
523}
524
525static uint32_t cubic_point_count(const GrPoint points[], GrScalar tol) {
526 GrScalar d = GrMax(points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]),
527 points[2].distanceToLineSegmentBetweenSqd(points[0], points[3]));
528 d = sqrtf(d);
529 if (d < tol) {
530 return 1;
531 } else {
532 d = ceilf(sqrtf(d/tol));
533 return GrNextPow2((uint32_t)d);
534 }
535}
536
537static uint32_t generate_cubic_points(const GrPoint& p0,
538 const GrPoint& p1,
539 const GrPoint& p2,
540 const GrPoint& p3,
541 GrScalar tolSqd,
542 GrPoint** points,
543 uint32_t pointsLeft) {
544 if (pointsLeft < 2 ||
545 (p1.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd &&
546 p2.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd)) {
547 (*points)[0] = p3;
548 *points += 1;
549 return 1;
550 }
551 GrPoint q[] = {
552 GrPoint(GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY)),
553 GrPoint(GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY)),
554 GrPoint(GrScalarAve(p2.fX, p3.fX), GrScalarAve(p2.fY, p3.fY))
555 };
556 GrPoint r[] = {
557 GrPoint(GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY)),
558 GrPoint(GrScalarAve(q[1].fX, q[2].fX), GrScalarAve(q[1].fY, q[2].fY))
559 };
560 GrPoint s(GrScalarAve(r[0].fX, r[1].fX), GrScalarAve(r[0].fY, r[1].fY));
561 pointsLeft >>= 1;
562 uint32_t a = generate_cubic_points(p0, q[0], r[0], s, tolSqd, points, pointsLeft);
563 uint32_t b = generate_cubic_points(s, r[1], q[2], p3, tolSqd, points, pointsLeft);
564 return a + b;
565}
566
567#else // !NEW_EVAL
568
569static GrScalar gr_eval_quad(const GrScalar coord[], GrScalar t) {
570 GrScalar A = coord[0] - 2 * coord[2] + coord[4];
571 GrScalar B = 2 * (coord[2] - coord[0]);
572 GrScalar C = coord[0];
573
574 return GrMul(GrMul(A, t) + B, t) + C;
575}
576
577static void gr_eval_quad_at(const GrPoint src[3], GrScalar t, GrPoint* pt) {
578 GrAssert(src);
579 GrAssert(pt);
580 GrAssert(t >= 0 && t <= GR_Scalar1);
581 pt->set(gr_eval_quad(&src[0].fX, t), gr_eval_quad(&src[0].fY, t));
582}
583
584static GrScalar gr_eval_cubic(const GrScalar coord[], GrScalar t) {
585 GrScalar A = coord[6] - coord[0] + 3 * (coord[2] - coord[4]);
586 GrScalar B = 3 * (coord[0] - 2 * coord[2] + coord[4]);
587 GrScalar C = 3 * (coord[2] - coord[0]);
588 GrScalar D = coord[0];
589
590 return GrMul(GrMul(GrMul(A, t) + B, t) + C, t) + D;
591}
592
593static void gr_eval_cubic_at(const GrPoint src[4], GrScalar t, GrPoint* pt) {
594 GrAssert(src);
595 GrAssert(pt);
596 GrAssert(t >= 0 && t <= GR_Scalar1);
597
598 pt->set(gr_eval_cubic(&src[0].fX, t), gr_eval_cubic(&src[0].fY, t));
599}
600
601#endif // !NEW_EVAL
602
603static int worst_case_point_count(GrPathIter* path,
604 int* subpaths,
605 const GrMatrix& matrix,
606 GrScalar tol) {
607 int pointCount = 0;
608 *subpaths = 1;
609
610 bool first = true;
611
612 GrPathIter::Command cmd;
613
614 GrPoint pts[4];
615 while ((cmd = path->next(pts)) != GrPathIter::kEnd_Command) {
616
617 switch (cmd) {
618 case GrPathIter::kLine_Command:
619 pointCount += 1;
620 break;
621 case GrPathIter::kQuadratic_Command:
622#if NEW_EVAL
623 matrix.mapPoints(pts, pts, 3);
624 pointCount += quadratic_point_count(pts, tol);
625#else
626 pointCount += 9;
627#endif
628 break;
629 case GrPathIter::kCubic_Command:
630#if NEW_EVAL
631 matrix.mapPoints(pts, pts, 4);
632 pointCount += cubic_point_count(pts, tol);
633#else
634 pointCount += 17;
635#endif
636 break;
637 case GrPathIter::kMove_Command:
638 pointCount += 1;
639 if (!first) {
640 ++(*subpaths);
641 }
642 break;
643 default:
644 break;
645 }
646 first = false;
647 }
648 return pointCount;
649}
650
651static inline bool single_pass_path(const GrPathIter& path,
652 GrContext::PathFills fill,
reed@google.comac10a2d2010-12-22 21:39:39 +0000653 const GrGpu& gpu) {
654#if STENCIL_OFF
655 return true;
656#else
657 if (GrContext::kEvenOdd_PathFill == fill) {
658 GrPathIter::ConvexHint hint = path.hint();
659 return hint == GrPathIter::kConvex_ConvexHint ||
660 hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint;
661 } else if (GrContext::kWinding_PathFill == fill) {
662 GrPathIter::ConvexHint hint = path.hint();
663 return hint == GrPathIter::kConvex_ConvexHint ||
664 hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint ||
665 (hint == GrPathIter::kSameWindingConvexPieces_ConvexHint &&
666 gpu.canDisableBlend() && !gpu.isDitherState());
667
668 }
669 return false;
670#endif
671}
672
bsalomon@google.com5782d712011-01-21 21:03:59 +0000673void GrContext::drawPath(const GrPaint& paint,
674 GrPathIter* path,
675 PathFills fill,
676 const GrPoint* translate) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000677
reed@google.comac10a2d2010-12-22 21:39:39 +0000678
bsalomon@google.com5782d712011-01-21 21:03:59 +0000679 this->prepareToDraw(paint);
680
681 GrDrawTarget::AutoStateRestore asr(fGpu);
reed@google.comac10a2d2010-12-22 21:39:39 +0000682
683#if NEW_EVAL
bsalomon@google.com5782d712011-01-21 21:03:59 +0000684 GrMatrix viewM = fGpu->getViewMatrix();
reed@google.comac10a2d2010-12-22 21:39:39 +0000685 // In order to tesselate the path we get a bound on how much the matrix can
686 // stretch when mapping to screen coordinates.
687 GrScalar stretch = viewM.getMaxStretch();
688 bool useStretch = stretch > 0;
689 GrScalar tol = EVAL_TOL;
690 if (!useStretch) {
691 // TODO: deal with perspective in some better way.
692 tol /= 10;
693 } else {
694 // TODO: fixed point divide
695 GrScalar sinv = 1 / stretch;
696 tol = GrMul(tol, sinv);
697 viewM = GrMatrix::I();
698 }
699 GrScalar tolSqd = GrMul(tol, tol);
700#else
701 // pass to worst_case... but won't be used.
702 static const GrScalar tol = -1;
703#endif
704
705 int subpathCnt;
706 int maxPts = worst_case_point_count(path,
707 &subpathCnt,
708#if CPU_TRANSFORM
709 cpuMatrix,
710#else
711 GrMatrix::I(),
712#endif
713 tol);
714 GrVertexLayout layout = 0;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000715
716 if (NULL != paint.getTexture()) {
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000717 layout = GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000718 }
719 // add 4 to hold the bounding rect
720 GrDrawTarget::AutoReleaseGeometry arg(fGpu, layout, maxPts + 4, 0);
721
722 GrPoint* base = (GrPoint*) arg.vertices();
723 GrPoint* vert = base;
724 GrPoint* subpathBase = base;
725
726 GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
727
728 path->rewind();
729
730 // TODO: use primitve restart if available rather than multiple draws
bsalomon@google.com5782d712011-01-21 21:03:59 +0000731 GrDrawTarget::PrimitiveType type;
reed@google.comac10a2d2010-12-22 21:39:39 +0000732 int passCount = 0;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000733 GrDrawTarget::StencilPass passes[3];
reed@google.comac10a2d2010-12-22 21:39:39 +0000734 bool reverse = false;
735
736 if (kHairLine_PathFill == fill) {
bsalomon@google.com5782d712011-01-21 21:03:59 +0000737 type = GrDrawTarget::kLineStrip_PrimitiveType;
reed@google.comac10a2d2010-12-22 21:39:39 +0000738 passCount = 1;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000739 passes[0] = GrDrawTarget::kNone_StencilPass;
reed@google.comac10a2d2010-12-22 21:39:39 +0000740 } else {
bsalomon@google.com5782d712011-01-21 21:03:59 +0000741 type = GrDrawTarget::kTriangleFan_PrimitiveType;
742 if (single_pass_path(*path, fill, *fGpu)) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000743 passCount = 1;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000744 passes[0] = GrDrawTarget::kNone_StencilPass;
reed@google.comac10a2d2010-12-22 21:39:39 +0000745 } else {
746 switch (fill) {
747 case kInverseEvenOdd_PathFill:
748 reverse = true;
749 // fallthrough
750 case kEvenOdd_PathFill:
751 passCount = 2;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000752 passes[0] = GrDrawTarget::kEvenOddStencil_StencilPass;
753 passes[1] = GrDrawTarget::kEvenOddColor_StencilPass;
reed@google.comac10a2d2010-12-22 21:39:39 +0000754 break;
755
756 case kInverseWinding_PathFill:
757 reverse = true;
758 // fallthrough
759 case kWinding_PathFill:
bsalomon@google.com5782d712011-01-21 21:03:59 +0000760 passes[0] = GrDrawTarget::kWindingStencil1_StencilPass;
reed@google.comac10a2d2010-12-22 21:39:39 +0000761 if (fGpu->supportsSingleStencilPassWinding()) {
bsalomon@google.com5782d712011-01-21 21:03:59 +0000762 passes[1] = GrDrawTarget::kWindingColor_StencilPass;
reed@google.comac10a2d2010-12-22 21:39:39 +0000763 passCount = 2;
764 } else {
bsalomon@google.com5782d712011-01-21 21:03:59 +0000765 passes[1] = GrDrawTarget::kWindingStencil2_StencilPass;
766 passes[2] = GrDrawTarget::kWindingColor_StencilPass;
reed@google.comac10a2d2010-12-22 21:39:39 +0000767 passCount = 3;
768 }
769 break;
770 default:
771 GrAssert(!"Unknown path fill!");
772 return;
773 }
774 }
775 }
776 fGpu->setReverseFill(reverse);
777#if CPU_TRANSFORM
778 GrMatrix cpuMatrix;
779 fGpu->getViewMatrix(&cpuMatrix);
780 fGpu->setViewMatrix(GrMatrix::I());
781#endif
782
783 GrPoint pts[4];
784
785 bool first = true;
786 int subpath = 0;
787
788 for (;;) {
789 GrPathIter::Command cmd = path->next(pts);
790#if CPU_TRANSFORM
791 int numPts = GrPathIter::NumCommandPoints(cmd);
792 cpuMatrix.mapPoints(pts, pts, numPts);
793#endif
794 switch (cmd) {
795 case GrPathIter::kMove_Command:
796 if (!first) {
797 subpathVertCount[subpath] = vert-subpathBase;
798 subpathBase = vert;
799 ++subpath;
800 }
801 *vert = pts[0];
802 vert++;
803 break;
804 case GrPathIter::kLine_Command:
805 *vert = pts[1];
806 vert++;
807 break;
808 case GrPathIter::kQuadratic_Command: {
809#if NEW_EVAL
810
811 generate_quadratic_points(pts[0], pts[1], pts[2],
812 tolSqd, &vert,
813 quadratic_point_count(pts, tol));
814#else
815 const int n = 8;
816 const GrScalar dt = GR_Scalar1 / n;
817 GrScalar t = dt;
818 for (int i = 1; i < n; i++) {
819 gr_eval_quad_at(pts, t, (GrPoint*)vert);
820 t += dt;
821 vert++;
822 }
823 vert->set(pts[2].fX, pts[2].fY);
824 vert++;
825#endif
826 break;
827 }
828 case GrPathIter::kCubic_Command: {
829#if NEW_EVAL
830 generate_cubic_points(pts[0], pts[1], pts[2], pts[3],
831 tolSqd, &vert,
832 cubic_point_count(pts, tol));
833#else
834 const int n = 16;
835 const GrScalar dt = GR_Scalar1 / n;
836 GrScalar t = dt;
837 for (int i = 1; i < n; i++) {
838 gr_eval_cubic_at(pts, t, (GrPoint*)vert);
839 t += dt;
840 vert++;
841 }
842 vert->set(pts[3].fX, pts[3].fY);
843 vert++;
844#endif
845 break;
846 }
847 case GrPathIter::kClose_Command:
848 break;
849 case GrPathIter::kEnd_Command:
850 subpathVertCount[subpath] = vert-subpathBase;
851 ++subpath; // this could be only in debug
852 goto FINISHED;
853 }
854 first = false;
855 }
856FINISHED:
857 GrAssert(subpath == subpathCnt);
858 GrAssert((vert - base) <= maxPts);
859
860 if (translate) {
861 int count = vert - base;
862 for (int i = 0; i < count; i++) {
863 base[i].offset(translate->fX, translate->fY);
864 }
865 }
866
867 // arbitrary path complexity cutoff
868 bool useBounds = fill != kHairLine_PathFill &&
869 (reverse || (vert - base) > 8);
870 GrPoint* boundsVerts = base + maxPts;
871 if (useBounds) {
872 GrRect bounds;
873 if (reverse) {
bsalomon@google.com5782d712011-01-21 21:03:59 +0000874 GrAssert(NULL != fGpu->getRenderTarget());
reed@google.comac10a2d2010-12-22 21:39:39 +0000875 // draw over the whole world.
876 bounds.setLTRB(0, 0,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000877 GrIntToScalar(fGpu->getRenderTarget()->width()),
878 GrIntToScalar(fGpu->getRenderTarget()->height()));
879 GrMatrix vmi;
880 if (fGpu->getViewInverse(&vmi)) {
881 vmi.mapRect(&bounds);
882 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000883 } else {
884 bounds.setBounds((GrPoint*)base, vert - base);
885 }
886 boundsVerts[0].setRectFan(bounds.fLeft, bounds.fTop, bounds.fRight,
887 bounds.fBottom);
888 }
889
890 for (int p = 0; p < passCount; ++p) {
891 fGpu->setStencilPass(passes[p]);
bsalomon@google.com5782d712011-01-21 21:03:59 +0000892 if (useBounds && (GrDrawTarget::kEvenOddColor_StencilPass == passes[p] ||
893 GrDrawTarget::kWindingColor_StencilPass == passes[p])) {
894 fGpu->drawNonIndexed(GrDrawTarget::kTriangleFan_PrimitiveType,
reed@google.comac10a2d2010-12-22 21:39:39 +0000895 maxPts, 4);
bsalomon@google.com5782d712011-01-21 21:03:59 +0000896
reed@google.comac10a2d2010-12-22 21:39:39 +0000897 } else {
898 int baseVertex = 0;
899 for (int sp = 0; sp < subpathCnt; ++sp) {
900 fGpu->drawNonIndexed(type,
901 baseVertex,
902 subpathVertCount[sp]);
903 baseVertex += subpathVertCount[sp];
904 }
905 }
906 }
907}
908
bsalomon@google.com5782d712011-01-21 21:03:59 +0000909////////////////////////////////////////////////////////////////////////////////
910
reed@google.comac10a2d2010-12-22 21:39:39 +0000911void GrContext::flush(bool flushRenderTarget) {
912 flushText();
913 if (flushRenderTarget) {
914 fGpu->forceRenderTargetFlush();
915 }
916}
917
918void GrContext::flushText() {
919 fTextDrawBuffer.playback(fGpu);
920 fTextDrawBuffer.reset();
921}
922
923bool GrContext::readPixels(int left, int top, int width, int height,
924 GrTexture::PixelConfig config, void* buffer) {
925 this->flush(true);
926 return fGpu->readPixels(left, top, width, height, config, buffer);
927}
928
929void GrContext::writePixels(int left, int top, int width, int height,
930 GrTexture::PixelConfig config, const void* buffer,
931 size_t stride) {
bsalomon@google.com5782d712011-01-21 21:03:59 +0000932
933 // TODO: when underlying api has a direct way to do this we should use it
934 // (e.g. glDrawPixels on desktop GL).
935
reed@google.comac10a2d2010-12-22 21:39:39 +0000936 const GrGpu::TextureDesc desc = {
937 0, GrGpu::kNone_AALevel, width, height, config
938 };
939 GrTexture* texture = fGpu->createTexture(desc, buffer, stride);
940 if (NULL == texture) {
941 return;
942 }
943
944 this->flush(true);
945
946 GrAutoUnref aur(texture);
947 GrDrawTarget::AutoStateRestore asr(fGpu);
948
949 GrMatrix matrix;
950 matrix.setTranslate(GrIntToScalar(left), GrIntToScalar(top));
951 fGpu->setViewMatrix(matrix);
952 matrix.setScale(GR_Scalar1 / texture->allocWidth(),
953 GR_Scalar1 / texture->allocHeight());
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000954 fGpu->setTextureMatrix(0, matrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000955
956 fGpu->disableState(GrDrawTarget::kClip_StateBit);
957 fGpu->setAlpha(0xFF);
958 fGpu->setBlendFunc(GrDrawTarget::kOne_BlendCoeff,
959 GrDrawTarget::kZero_BlendCoeff);
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000960 fGpu->setTexture(0, texture);
961 fGpu->setSamplerState(0, GrSamplerState::ClampNoFilter());
reed@google.comac10a2d2010-12-22 21:39:39 +0000962
bsalomon@google.com5782d712011-01-21 21:03:59 +0000963 GrVertexLayout layout = GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
964 static const int VCOUNT = 4;
965
966 GrDrawTarget::AutoReleaseGeometry geo(fGpu, layout, VCOUNT, 0);
967 if (!geo.succeeded()) {
968 return;
969 }
970 ((GrPoint*)geo.vertices())->setIRectFan(0, 0, width, height);
971 fGpu->drawNonIndexed(GrDrawTarget::kTriangleFan_PrimitiveType, 0, VCOUNT);
972}
973////////////////////////////////////////////////////////////////////////////////
974
975void GrContext::SetPaint(const GrPaint& paint, GrDrawTarget* target) {
976 target->setTexture(0, paint.getTexture());
977 target->setTextureMatrix(0, paint.fTextureMatrix);
978 target->setSamplerState(0, paint.fSampler);
979 target->setColor(paint.fColor);
980
981 if (paint.fDither) {
982 target->enableState(GrDrawTarget::kDither_StateBit);
983 } else {
984 target->disableState(GrDrawTarget::kDither_StateBit);
985 }
986 if (paint.fAntiAlias) {
987 target->enableState(GrDrawTarget::kAntialias_StateBit);
988 } else {
989 target->disableState(GrDrawTarget::kAntialias_StateBit);
990 }
991 target->setBlendFunc(paint.fSrcBlendCoeff, paint.fDstBlendCoeff);
992}
993
994void GrContext::prepareToDraw(const GrPaint& paint) {
995
996 flushText();
997 SetPaint(paint, fGpu);
reed@google.comac10a2d2010-12-22 21:39:39 +0000998}
999
1000////////////////////////////////////////////////////////////////////////////////
1001
reed@google.comac10a2d2010-12-22 21:39:39 +00001002void GrContext::resetContext() {
1003 fGpu->resetContext();
1004}
1005
reed@google.comac10a2d2010-12-22 21:39:39 +00001006void GrContext::setRenderTarget(GrRenderTarget* target) {
1007 flushText();
1008 fGpu->setRenderTarget(target);
1009}
1010
bsalomon@google.com5782d712011-01-21 21:03:59 +00001011GrRenderTarget* GrContext::getRenderTarget() {
1012 return fGpu->getRenderTarget();
reed@google.comac10a2d2010-12-22 21:39:39 +00001013}
1014
bsalomon@google.com5782d712011-01-21 21:03:59 +00001015const GrRenderTarget* GrContext::getRenderTarget() const {
1016 return fGpu->getRenderTarget();
reed@google.comac10a2d2010-12-22 21:39:39 +00001017}
1018
bsalomon@google.com5782d712011-01-21 21:03:59 +00001019const GrMatrix& GrContext::getMatrix() const {
1020 return fGpu->getViewMatrix();
reed@google.comac10a2d2010-12-22 21:39:39 +00001021}
1022
bsalomon@google.com5782d712011-01-21 21:03:59 +00001023void GrContext::setMatrix(const GrMatrix& m) {
reed@google.comac10a2d2010-12-22 21:39:39 +00001024 fGpu->setViewMatrix(m);
1025}
1026
bsalomon@google.com5782d712011-01-21 21:03:59 +00001027void GrContext::concatMatrix(const GrMatrix& m) const {
1028 fGpu->concatViewMatrix(m);
reed@google.comac10a2d2010-12-22 21:39:39 +00001029}
1030
1031static inline intptr_t setOrClear(intptr_t bits, int shift, intptr_t pred) {
1032 intptr_t mask = 1 << shift;
1033 if (pred) {
1034 bits |= mask;
1035 } else {
1036 bits &= ~mask;
1037 }
1038 return bits;
1039}
1040
reed@google.comac10a2d2010-12-22 21:39:39 +00001041void GrContext::resetStats() {
1042 fGpu->resetStats();
1043}
1044
1045const GrGpu::Stats& GrContext::getStats() const {
1046 return fGpu->getStats();
1047}
1048
1049void GrContext::printStats() const {
1050 fGpu->printStats();
1051}
1052
1053GrContext::GrContext(GrGpu* gpu) :
1054 fVBAllocPool(gpu,
1055 gpu->supportsBufferLocking() ? POOL_VB_SIZE : 0,
1056 gpu->supportsBufferLocking() ? NUM_POOL_VBS : 0),
1057 fTextDrawBuffer(gpu->supportsBufferLocking() ? &fVBAllocPool : NULL) {
1058 fGpu = gpu;
1059 fGpu->ref();
1060 fTextureCache = new GrTextureCache(MAX_TEXTURE_CACHE_COUNT,
1061 MAX_TEXTURE_CACHE_BYTES);
1062 fFontCache = new GrFontCache(fGpu);
1063}
1064
1065bool GrContext::finalizeTextureKey(GrTextureKey* key,
1066 const GrSamplerState& sampler) const {
1067 uint32_t bits = 0;
1068 uint16_t width = key->width();
1069 uint16_t height = key->height();
1070 if (fGpu->npotTextureSupport() < GrGpu::kNonRendertarget_NPOTTextureType) {
1071 if ((sampler.getWrapX() != GrSamplerState::kClamp_WrapMode ||
1072 sampler.getWrapY() != GrSamplerState::kClamp_WrapMode) &&
1073 (!GrIsPow2(width) || !GrIsPow2(height))) {
1074 bits |= 1;
1075 bits |= sampler.isFilter() ? 2 : 0;
1076 }
1077 }
1078 key->finalize(bits);
1079 return 0 != bits;
1080}
1081
bsalomon@google.com5782d712011-01-21 21:03:59 +00001082GrDrawTarget* GrContext::getTextTarget(const GrPaint& paint) {
1083 GrDrawTarget* target;
reed@google.comac10a2d2010-12-22 21:39:39 +00001084#if DEFER_TEXT_RENDERING
1085 fTextDrawBuffer.initializeDrawStateAndClip(*fGpu);
bsalomon@google.com5782d712011-01-21 21:03:59 +00001086 target = &fTextDrawBuffer;
reed@google.comac10a2d2010-12-22 21:39:39 +00001087#else
bsalomon@google.com5782d712011-01-21 21:03:59 +00001088 target = fGpu;
reed@google.comac10a2d2010-12-22 21:39:39 +00001089#endif
bsalomon@google.com5782d712011-01-21 21:03:59 +00001090 SetPaint(paint, target);
1091 return target;
reed@google.comac10a2d2010-12-22 21:39:39 +00001092}
1093
1094const GrIndexBuffer* GrContext::quadIndexBuffer() const {
1095 return fGpu->quadIndexBuffer();
1096}
1097
1098int GrContext::maxQuadsInIndexBuffer() const {
1099 return fGpu->maxQuadsInIndexBuffer();
1100}
1101
1102
1103