add gpu backend (not hooked up yet)
git-svn-id: http://skia.googlecode.com/svn/trunk@649 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
new file mode 100644
index 0000000..6e94ef7
--- /dev/null
+++ b/gpu/src/GrContext.cpp
@@ -0,0 +1,1040 @@
+/*
+ Copyright 2010 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+
+#include "GrContext.h"
+#include "GrTextureCache.h"
+#include "GrTextStrike.h"
+#include "GrMemory.h"
+#include "GrPathIter.h"
+#include "GrClipIterator.h"
+#include "GrIndexBuffer.h"
+
+#define DEFER_TEXT_RENDERING 1
+
+static const size_t MAX_TEXTURE_CACHE_COUNT = 128;
+static const size_t MAX_TEXTURE_CACHE_BYTES = 8 * 1024 * 1024;
+
+#if DEFER_TEXT_RENDERING
+ static const uint32_t POOL_VB_SIZE = 2048 *
+ GrDrawTarget::VertexSize(GrDrawTarget::kTextFormat_VertexLayoutBit);
+ static const uint32_t NUM_POOL_VBS = 8;
+#else
+ static const uint32_t POOL_VB_SIZE = 0;
+ static const uint32_t NUM_POOL_VBS = 0;
+
+#endif
+
+GrContext* GrContext::Create(GrGpu::Engine engine,
+ GrGpu::Platform3DContext context3D) {
+ GrContext* ctx = NULL;
+ GrGpu* fGpu = GrGpu::Create(engine, context3D);
+ if (NULL != fGpu) {
+ ctx = new GrContext(fGpu);
+ fGpu->unref();
+ }
+ return ctx;
+}
+
+GrContext::~GrContext() {
+ fGpu->unref();
+ delete fTextureCache;
+ delete fFontCache;
+}
+
+void GrContext::abandonAllTextures() {
+ fTextureCache->deleteAll(GrTextureCache::kAbandonTexture_DeleteMode);
+ fFontCache->abandonAll();
+}
+
+GrTextureEntry* GrContext::findAndLockTexture(GrTextureKey* key,
+ const GrSamplerState& sampler) {
+ finalizeTextureKey(key, sampler);
+ return fTextureCache->findAndLock(*key);
+}
+
+static void stretchImage(void* dst,
+ int dstW,
+ int dstH,
+ void* src,
+ int srcW,
+ int srcH,
+ int bpp) {
+ GrFixed dx = (srcW << 16) / dstW;
+ GrFixed dy = (srcH << 16) / dstH;
+
+ GrFixed y = dy >> 1;
+
+ int dstXLimit = dstW*bpp;
+ for (int j = 0; j < dstH; ++j) {
+ GrFixed x = dx >> 1;
+ void* srcRow = (uint8_t*)src + (y>>16)*srcW*bpp;
+ void* dstRow = (uint8_t*)dst + j*dstW*bpp;
+ for (int i = 0; i < dstXLimit; i += bpp) {
+ memcpy((uint8_t*) dstRow + i,
+ (uint8_t*) srcRow + (x>>16)*bpp,
+ bpp);
+ x += dx;
+ }
+ y += dy;
+ }
+}
+
+GrTextureEntry* GrContext::createAndLockTexture(GrTextureKey* key,
+ const GrSamplerState& sampler,
+ const GrGpu::TextureDesc& desc,
+ void* srcData, size_t rowBytes) {
+ GrAssert(key->width() == desc.fWidth);
+ GrAssert(key->height() == desc.fHeight);
+
+#if GR_DUMP_TEXTURE_UPLOAD
+ GrPrintf("GrContext::createAndLockTexture [%d %d]\n", desc.fWidth, desc.fHeight);
+#endif
+
+ GrTextureEntry* entry = NULL;
+ bool special = finalizeTextureKey(key, sampler);
+ if (special) {
+ GrTextureEntry* clampEntry;
+ GrTextureKey clampKey(*key);
+ clampEntry = findAndLockTexture(&clampKey, GrSamplerState::ClampNoFilter());
+
+ if (NULL == clampEntry) {
+ clampEntry = createAndLockTexture(&clampKey,
+ GrSamplerState::ClampNoFilter(),
+ desc, srcData, rowBytes);
+ GrAssert(NULL != clampEntry);
+ if (NULL == clampEntry) {
+ return NULL;
+ }
+ }
+ GrTexture* clampTexture = clampEntry->texture();
+ GrGpu::TextureDesc rtDesc = desc;
+ rtDesc.fFlags |= GrGpu::kRenderTarget_TextureFlag |
+ GrGpu::kNoPathRendering_TextureFlag;
+ rtDesc.fWidth = GrNextPow2(GrMax<int>(desc.fWidth,
+ fGpu->minRenderTargetWidth()));
+ rtDesc.fHeight = GrNextPow2(GrMax<int>(desc.fHeight,
+ fGpu->minRenderTargetHeight()));
+
+ GrTexture* texture = fGpu->createTexture(rtDesc, NULL, 0);
+
+ if (NULL != texture) {
+ GrGpu::AutoStateRestore asr(fGpu);
+ fGpu->setRenderTarget(texture->asRenderTarget());
+ fGpu->setTexture(clampEntry->texture());
+ fGpu->setStencilPass(GrGpu::kNone_StencilPass);
+ fGpu->setTextureMatrix(GrMatrix::I());
+ fGpu->setViewMatrix(GrMatrix::I());
+ fGpu->setAlpha(0xff);
+ fGpu->setBlendFunc(GrGpu::kOne_BlendCoeff, GrGpu::kZero_BlendCoeff);
+ fGpu->disableState(GrGpu::kDither_StateBit |
+ GrGpu::kClip_StateBit |
+ GrGpu::kAntialias_StateBit);
+ GrSamplerState stretchSampler(GrSamplerState::kClamp_WrapMode,
+ GrSamplerState::kClamp_WrapMode,
+ sampler.isFilter());
+ fGpu->setSamplerState(stretchSampler);
+
+ static const GrVertexLayout layout =
+ GrDrawTarget::kSeparateTexCoord_VertexLayoutBit;
+ GrDrawTarget::AutoReleaseGeometry arg(fGpu, layout, 4, 0);
+
+ if (arg.succeeded()) {
+ GrPoint* verts = (GrPoint*) arg.vertices();
+ verts[0].setIRectFan(0, 0,
+ texture->contentWidth(),
+ texture->contentHeight(),
+ 2*sizeof(GrPoint));
+ GrScalar tw = GrFixedToScalar(GR_Fixed1 *
+ clampTexture->contentWidth() /
+ clampTexture->allocWidth());
+ GrScalar th = GrFixedToScalar(GR_Fixed1 *
+ clampTexture->contentHeight() /
+ clampTexture->allocHeight());
+ verts[1].setRectFan(0, 0, tw, th, 2*sizeof(GrPoint));
+ fGpu->drawNonIndexed(GrGpu::kTriangleFan_PrimitiveType,
+ 0, 4);
+ entry = fTextureCache->createAndLock(*key, texture);
+ }
+ texture->removeRenderTarget();
+ } else {
+ // TODO: Our CPU stretch doesn't filter. But we create separate
+ // stretched textures when the sampler state is either filtered or
+ // not. Either implement filtered stretch blit on CPU or just create
+ // one when FBO case fails.
+
+ rtDesc.fFlags = 0;
+ // no longer need to clamp at min RT size.
+ rtDesc.fWidth = GrNextPow2(desc.fWidth);
+ rtDesc.fHeight = GrNextPow2(desc.fHeight);
+ int bpp = GrTexture::BytesPerPixel(desc.fFormat);
+ GrAutoSMalloc<128*128*4> stretchedPixels(bpp *
+ rtDesc.fWidth *
+ rtDesc.fHeight);
+ stretchImage(stretchedPixels.get(), rtDesc.fWidth, rtDesc.fHeight,
+ srcData, desc.fWidth, desc.fHeight, bpp);
+
+ size_t stretchedRowBytes = rtDesc.fWidth * bpp;
+
+ GrTexture* texture = fGpu->createTexture(rtDesc,
+ stretchedPixels.get(),
+ stretchedRowBytes);
+ GrAssert(NULL != texture);
+ entry = fTextureCache->createAndLock(*key, texture);
+ }
+ fTextureCache->unlock(clampEntry);
+
+ } else {
+ GrTexture* texture = fGpu->createTexture(desc, srcData, rowBytes);
+ if (NULL != texture) {
+ entry = fTextureCache->createAndLock(*key, texture);
+ } else {
+ entry = NULL;
+ }
+ }
+ return entry;
+}
+
+void GrContext::unlockTexture(GrTextureEntry* entry) {
+ fTextureCache->unlock(entry);
+}
+
+void GrContext::detachCachedTexture(GrTextureEntry* entry) {
+ fTextureCache->detach(entry);
+}
+
+void GrContext::reattachAndUnlockCachedTexture(GrTextureEntry* entry) {
+ fTextureCache->reattachAndUnlock(entry);
+}
+
+GrTexture* GrContext::createUncachedTexture(const GrGpu::TextureDesc& desc,
+ void* srcData,
+ size_t rowBytes) {
+ return fGpu->createTexture(desc, srcData, rowBytes);
+}
+
+GrRenderTarget* GrContext::createPlatformRenderTarget(intptr_t platformRenderTarget,
+ int width, int height) {
+ return fGpu->createPlatformRenderTarget(platformRenderTarget,
+ width, height);
+}
+
+bool GrContext::supportsIndex8PixelConfig(const GrSamplerState& sampler,
+ int width, int height) {
+ if (!fGpu->supports8BitPalette()) {
+ return false;
+ }
+
+ bool needsRepeat = sampler.getWrapX() != GrSamplerState::kClamp_WrapMode ||
+ sampler.getWrapY() != GrSamplerState::kClamp_WrapMode;
+ bool isPow2 = GrIsPow2(width) && GrIsPow2(height);
+
+ switch (fGpu->npotTextureSupport()) {
+ case GrGpu::kNone_NPOTTextureType:
+ return isPow2;
+ case GrGpu::kNoRepeat_NPOTTextureType:
+ return isPow2 || !needsRepeat;
+ case GrGpu::kNonRendertarget_NPOTTextureType:
+ case GrGpu::kFull_NPOTTextureType:
+ return true;
+ }
+ // should never get here
+ GrAssert(!"Bad enum from fGpu->npotTextureSupport");
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void GrContext::eraseColor(GrColor color) {
+ fGpu->eraseColor(color);
+}
+
+void GrContext::drawFull(bool useTexture) {
+ // set rect to be big enough to fill the space, but not super-huge, so we
+ // don't overflow fixed-point implementations
+ GrRect r(fGpu->getClip().getBounds());
+ GrMatrix inverse;
+ if (fGpu->getViewInverse(&inverse)) {
+ inverse.mapRect(&r);
+ } else {
+ GrPrintf("---- fGpu->getViewInverse failed\n");
+ }
+
+ this->fillRect(r, useTexture);
+}
+
+/* create a triangle strip that strokes the specified triangle. There are 8
+ unique vertices, but we repreat the last 2 to close up. Alternatively we
+ could use an indices array, and then only send 8 verts, but not sure that
+ would be faster.
+ */
+static void setStrokeRectStrip(GrPoint verts[10], const GrRect& rect,
+ GrScalar width) {
+ const GrScalar rad = GrScalarHalf(width);
+
+ verts[0].set(rect.fLeft + rad, rect.fTop + rad);
+ verts[1].set(rect.fLeft - rad, rect.fTop - rad);
+ verts[2].set(rect.fRight - rad, rect.fTop + rad);
+ verts[3].set(rect.fRight + rad, rect.fTop - rad);
+ verts[4].set(rect.fRight - rad, rect.fBottom - rad);
+ verts[5].set(rect.fRight + rad, rect.fBottom + rad);
+ verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
+ verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
+ verts[8] = verts[0];
+ verts[9] = verts[1];
+}
+
+void GrContext::drawRect(const GrRect& rect, bool useTexture, GrScalar width) {
+ GrVertexLayout layout = useTexture ?
+ GrDrawTarget::kPositionAsTexCoord_VertexLayoutBit :
+ 0;
+
+ static const int worstCaseVertCount = 10;
+ GrDrawTarget::AutoReleaseGeometry geo(fGpu, layout, worstCaseVertCount, 0);
+ if (!geo.succeeded()) {
+ return;
+ }
+
+ this->flushText();
+
+ int vertCount;
+ GrGpu::PrimitiveType primType;
+ GrPoint* vertex = geo.positions();
+
+ if (width >= 0) {
+ if (width > 0) {
+ vertCount = 10;
+ primType = GrGpu::kTriangleStrip_PrimitiveType;
+ setStrokeRectStrip(vertex, rect, width);
+ } else {
+ // hairline
+ vertCount = 5;
+ primType = GrGpu::kLineStrip_PrimitiveType;
+ vertex[0].set(rect.fLeft, rect.fTop);
+ vertex[1].set(rect.fRight, rect.fTop);
+ vertex[2].set(rect.fRight, rect.fBottom);
+ vertex[3].set(rect.fLeft, rect.fBottom);
+ vertex[4].set(rect.fLeft, rect.fTop);
+ }
+ } else {
+ vertCount = 4;
+ primType = GrGpu::kTriangleFan_PrimitiveType;
+ vertex->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
+ }
+
+ fGpu->drawNonIndexed(primType, 0, vertCount);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+#define NEW_EVAL 1 // Use adaptive path tesselation
+#define STENCIL_OFF 0 // Always disable stencil (even when needed)
+#define CPU_TRANSFORM 0 // Transform path verts on CPU
+
+#if NEW_EVAL
+
+#define EVAL_TOL GR_Scalar1
+
+static uint32_t quadratic_point_count(const GrPoint points[], GrScalar tol) {
+ GrScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]);
+ // TODO: fixed points sqrt
+ if (d < tol) {
+ return 1;
+ } else {
+ // Each time we subdivide, d should be cut in 4. So we need to
+ // subdivide x = log4(d/tol) times. x subdivisions creates 2^(x)
+ // points.
+ // 2^(log4(x)) = sqrt(x);
+ d = ceilf(sqrtf(d/tol));
+ return GrNextPow2((uint32_t)d);
+ }
+}
+
+static uint32_t generate_quadratic_points(const GrPoint& p0,
+ const GrPoint& p1,
+ const GrPoint& p2,
+ GrScalar tolSqd,
+ GrPoint** points,
+ uint32_t pointsLeft) {
+ if (pointsLeft < 2 ||
+ (p1.distanceToLineSegmentBetweenSqd(p0, p2)) < tolSqd) {
+ (*points)[0] = p2;
+ *points += 1;
+ return 1;
+ }
+
+ GrPoint q[] = {
+ GrPoint(GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY)),
+ GrPoint(GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY)),
+ };
+ GrPoint r(GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY));
+
+ pointsLeft >>= 1;
+ uint32_t a = generate_quadratic_points(p0, q[0], r, tolSqd, points, pointsLeft);
+ uint32_t b = generate_quadratic_points(r, q[1], p2, tolSqd, points, pointsLeft);
+ return a + b;
+}
+
+static uint32_t cubic_point_count(const GrPoint points[], GrScalar tol) {
+ GrScalar d = GrMax(points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]),
+ points[2].distanceToLineSegmentBetweenSqd(points[0], points[3]));
+ d = sqrtf(d);
+ if (d < tol) {
+ return 1;
+ } else {
+ d = ceilf(sqrtf(d/tol));
+ return GrNextPow2((uint32_t)d);
+ }
+}
+
+static uint32_t generate_cubic_points(const GrPoint& p0,
+ const GrPoint& p1,
+ const GrPoint& p2,
+ const GrPoint& p3,
+ GrScalar tolSqd,
+ GrPoint** points,
+ uint32_t pointsLeft) {
+ if (pointsLeft < 2 ||
+ (p1.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd &&
+ p2.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd)) {
+ (*points)[0] = p3;
+ *points += 1;
+ return 1;
+ }
+ GrPoint q[] = {
+ GrPoint(GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY)),
+ GrPoint(GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY)),
+ GrPoint(GrScalarAve(p2.fX, p3.fX), GrScalarAve(p2.fY, p3.fY))
+ };
+ GrPoint r[] = {
+ GrPoint(GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY)),
+ GrPoint(GrScalarAve(q[1].fX, q[2].fX), GrScalarAve(q[1].fY, q[2].fY))
+ };
+ GrPoint s(GrScalarAve(r[0].fX, r[1].fX), GrScalarAve(r[0].fY, r[1].fY));
+ pointsLeft >>= 1;
+ uint32_t a = generate_cubic_points(p0, q[0], r[0], s, tolSqd, points, pointsLeft);
+ uint32_t b = generate_cubic_points(s, r[1], q[2], p3, tolSqd, points, pointsLeft);
+ return a + b;
+}
+
+#else // !NEW_EVAL
+
+static GrScalar gr_eval_quad(const GrScalar coord[], GrScalar t) {
+ GrScalar A = coord[0] - 2 * coord[2] + coord[4];
+ GrScalar B = 2 * (coord[2] - coord[0]);
+ GrScalar C = coord[0];
+
+ return GrMul(GrMul(A, t) + B, t) + C;
+}
+
+static void gr_eval_quad_at(const GrPoint src[3], GrScalar t, GrPoint* pt) {
+ GrAssert(src);
+ GrAssert(pt);
+ GrAssert(t >= 0 && t <= GR_Scalar1);
+ pt->set(gr_eval_quad(&src[0].fX, t), gr_eval_quad(&src[0].fY, t));
+}
+
+static GrScalar gr_eval_cubic(const GrScalar coord[], GrScalar t) {
+ GrScalar A = coord[6] - coord[0] + 3 * (coord[2] - coord[4]);
+ GrScalar B = 3 * (coord[0] - 2 * coord[2] + coord[4]);
+ GrScalar C = 3 * (coord[2] - coord[0]);
+ GrScalar D = coord[0];
+
+ return GrMul(GrMul(GrMul(A, t) + B, t) + C, t) + D;
+}
+
+static void gr_eval_cubic_at(const GrPoint src[4], GrScalar t, GrPoint* pt) {
+ GrAssert(src);
+ GrAssert(pt);
+ GrAssert(t >= 0 && t <= GR_Scalar1);
+
+ pt->set(gr_eval_cubic(&src[0].fX, t), gr_eval_cubic(&src[0].fY, t));
+}
+
+#endif // !NEW_EVAL
+
+static int worst_case_point_count(GrPathIter* path,
+ int* subpaths,
+ const GrMatrix& matrix,
+ GrScalar tol) {
+ int pointCount = 0;
+ *subpaths = 1;
+
+ bool first = true;
+
+ GrPathIter::Command cmd;
+
+ GrPoint pts[4];
+ while ((cmd = path->next(pts)) != GrPathIter::kEnd_Command) {
+
+ switch (cmd) {
+ case GrPathIter::kLine_Command:
+ pointCount += 1;
+ break;
+ case GrPathIter::kQuadratic_Command:
+#if NEW_EVAL
+ matrix.mapPoints(pts, pts, 3);
+ pointCount += quadratic_point_count(pts, tol);
+#else
+ pointCount += 9;
+#endif
+ break;
+ case GrPathIter::kCubic_Command:
+#if NEW_EVAL
+ matrix.mapPoints(pts, pts, 4);
+ pointCount += cubic_point_count(pts, tol);
+#else
+ pointCount += 17;
+#endif
+ break;
+ case GrPathIter::kMove_Command:
+ pointCount += 1;
+ if (!first) {
+ ++(*subpaths);
+ }
+ break;
+ default:
+ break;
+ }
+ first = false;
+ }
+ return pointCount;
+}
+
+static inline bool single_pass_path(const GrPathIter& path,
+ GrContext::PathFills fill,
+ bool useTex,
+ const GrGpu& gpu) {
+#if STENCIL_OFF
+ return true;
+#else
+ if (GrContext::kEvenOdd_PathFill == fill) {
+ GrPathIter::ConvexHint hint = path.hint();
+ return hint == GrPathIter::kConvex_ConvexHint ||
+ hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint;
+ } else if (GrContext::kWinding_PathFill == fill) {
+ GrPathIter::ConvexHint hint = path.hint();
+ return hint == GrPathIter::kConvex_ConvexHint ||
+ hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint ||
+ (hint == GrPathIter::kSameWindingConvexPieces_ConvexHint &&
+ gpu.canDisableBlend() && !gpu.isDitherState());
+
+ }
+ return false;
+#endif
+}
+
+void GrContext::drawPath(GrPathIter* path, PathFills fill,
+ bool useTexture, const GrPoint* translate) {
+
+ flushText();
+
+ GrGpu::AutoStateRestore asr(fGpu);
+
+#if NEW_EVAL
+ GrMatrix viewM;
+ fGpu->getViewMatrix(&viewM);
+ // In order to tesselate the path we get a bound on how much the matrix can
+ // stretch when mapping to screen coordinates.
+ GrScalar stretch = viewM.getMaxStretch();
+ bool useStretch = stretch > 0;
+ GrScalar tol = EVAL_TOL;
+ if (!useStretch) {
+ // TODO: deal with perspective in some better way.
+ tol /= 10;
+ } else {
+ // TODO: fixed point divide
+ GrScalar sinv = 1 / stretch;
+ tol = GrMul(tol, sinv);
+ viewM = GrMatrix::I();
+ }
+ GrScalar tolSqd = GrMul(tol, tol);
+#else
+ // pass to worst_case... but won't be used.
+ static const GrScalar tol = -1;
+#endif
+
+ int subpathCnt;
+ int maxPts = worst_case_point_count(path,
+ &subpathCnt,
+#if CPU_TRANSFORM
+ cpuMatrix,
+#else
+ GrMatrix::I(),
+#endif
+ tol);
+ GrVertexLayout layout = 0;
+ if (useTexture) {
+ layout = GrDrawTarget::kPositionAsTexCoord_VertexLayoutBit;
+ }
+ // add 4 to hold the bounding rect
+ GrDrawTarget::AutoReleaseGeometry arg(fGpu, layout, maxPts + 4, 0);
+
+ GrPoint* base = (GrPoint*) arg.vertices();
+ GrPoint* vert = base;
+ GrPoint* subpathBase = base;
+
+ GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
+
+ path->rewind();
+
+ // TODO: use primitve restart if available rather than multiple draws
+ GrGpu::PrimitiveType type;
+ int passCount = 0;
+ GrGpu::StencilPass passes[3];
+ bool reverse = false;
+
+ if (kHairLine_PathFill == fill) {
+ type = GrGpu::kLineStrip_PrimitiveType;
+ passCount = 1;
+ passes[0] = GrGpu::kNone_StencilPass;
+ } else {
+ type = GrGpu::kTriangleFan_PrimitiveType;
+ if (single_pass_path(*path, fill, useTexture, *fGpu)) {
+ passCount = 1;
+ passes[0] = GrGpu::kNone_StencilPass;
+ } else {
+ switch (fill) {
+ case kInverseEvenOdd_PathFill:
+ reverse = true;
+ // fallthrough
+ case kEvenOdd_PathFill:
+ passCount = 2;
+ passes[0] = GrGpu::kEvenOddStencil_StencilPass;
+ passes[1] = GrGpu::kEvenOddColor_StencilPass;
+ break;
+
+ case kInverseWinding_PathFill:
+ reverse = true;
+ // fallthrough
+ case kWinding_PathFill:
+ passes[0] = GrGpu::kWindingStencil1_StencilPass;
+ if (fGpu->supportsSingleStencilPassWinding()) {
+ passes[1] = GrGpu::kWindingColor_StencilPass;
+ passCount = 2;
+ } else {
+ passes[1] = GrGpu::kWindingStencil2_StencilPass;
+ passes[2] = GrGpu::kWindingColor_StencilPass;
+ passCount = 3;
+ }
+ break;
+ default:
+ GrAssert(!"Unknown path fill!");
+ return;
+ }
+ }
+ }
+ fGpu->setReverseFill(reverse);
+#if CPU_TRANSFORM
+ GrMatrix cpuMatrix;
+ fGpu->getViewMatrix(&cpuMatrix);
+ fGpu->setViewMatrix(GrMatrix::I());
+#endif
+
+ GrPoint pts[4];
+
+ bool first = true;
+ int subpath = 0;
+
+ for (;;) {
+ GrPathIter::Command cmd = path->next(pts);
+#if CPU_TRANSFORM
+ int numPts = GrPathIter::NumCommandPoints(cmd);
+ cpuMatrix.mapPoints(pts, pts, numPts);
+#endif
+ switch (cmd) {
+ case GrPathIter::kMove_Command:
+ if (!first) {
+ subpathVertCount[subpath] = vert-subpathBase;
+ subpathBase = vert;
+ ++subpath;
+ }
+ *vert = pts[0];
+ vert++;
+ break;
+ case GrPathIter::kLine_Command:
+ *vert = pts[1];
+ vert++;
+ break;
+ case GrPathIter::kQuadratic_Command: {
+#if NEW_EVAL
+
+ generate_quadratic_points(pts[0], pts[1], pts[2],
+ tolSqd, &vert,
+ quadratic_point_count(pts, tol));
+#else
+ const int n = 8;
+ const GrScalar dt = GR_Scalar1 / n;
+ GrScalar t = dt;
+ for (int i = 1; i < n; i++) {
+ gr_eval_quad_at(pts, t, (GrPoint*)vert);
+ t += dt;
+ vert++;
+ }
+ vert->set(pts[2].fX, pts[2].fY);
+ vert++;
+#endif
+ break;
+ }
+ case GrPathIter::kCubic_Command: {
+#if NEW_EVAL
+ generate_cubic_points(pts[0], pts[1], pts[2], pts[3],
+ tolSqd, &vert,
+ cubic_point_count(pts, tol));
+#else
+ const int n = 16;
+ const GrScalar dt = GR_Scalar1 / n;
+ GrScalar t = dt;
+ for (int i = 1; i < n; i++) {
+ gr_eval_cubic_at(pts, t, (GrPoint*)vert);
+ t += dt;
+ vert++;
+ }
+ vert->set(pts[3].fX, pts[3].fY);
+ vert++;
+#endif
+ break;
+ }
+ case GrPathIter::kClose_Command:
+ break;
+ case GrPathIter::kEnd_Command:
+ subpathVertCount[subpath] = vert-subpathBase;
+ ++subpath; // this could be only in debug
+ goto FINISHED;
+ }
+ first = false;
+ }
+FINISHED:
+ GrAssert(subpath == subpathCnt);
+ GrAssert((vert - base) <= maxPts);
+
+ if (translate) {
+ int count = vert - base;
+ for (int i = 0; i < count; i++) {
+ base[i].offset(translate->fX, translate->fY);
+ }
+ }
+
+ // arbitrary path complexity cutoff
+ bool useBounds = fill != kHairLine_PathFill &&
+ (reverse || (vert - base) > 8);
+ GrPoint* boundsVerts = base + maxPts;
+ if (useBounds) {
+ GrRect bounds;
+ if (reverse) {
+ GrAssert(NULL != fGpu->currentRenderTarget());
+ // draw over the whole world.
+ bounds.setLTRB(0, 0,
+ GrIntToScalar(fGpu->currentRenderTarget()->width()),
+ GrIntToScalar(fGpu->currentRenderTarget()->height()));
+ } else {
+ bounds.setBounds((GrPoint*)base, vert - base);
+ }
+ boundsVerts[0].setRectFan(bounds.fLeft, bounds.fTop, bounds.fRight,
+ bounds.fBottom);
+ }
+
+ for (int p = 0; p < passCount; ++p) {
+ fGpu->setStencilPass(passes[p]);
+ if (useBounds && (GrGpu::kEvenOddColor_StencilPass == passes[p] ||
+ GrGpu::kWindingColor_StencilPass == passes[p])) {
+ fGpu->drawNonIndexed(GrGpu::kTriangleFan_PrimitiveType,
+ maxPts, 4);
+ } else {
+ int baseVertex = 0;
+ for (int sp = 0; sp < subpathCnt; ++sp) {
+ fGpu->drawNonIndexed(type,
+ baseVertex,
+ subpathVertCount[sp]);
+ baseVertex += subpathVertCount[sp];
+ }
+ }
+ }
+}
+
+void GrContext::flush(bool flushRenderTarget) {
+ flushText();
+ if (flushRenderTarget) {
+ fGpu->forceRenderTargetFlush();
+ }
+}
+
+void GrContext::flushText() {
+ fTextDrawBuffer.playback(fGpu);
+ fTextDrawBuffer.reset();
+}
+
+bool GrContext::readPixels(int left, int top, int width, int height,
+ GrTexture::PixelConfig config, void* buffer) {
+ this->flush(true);
+ return fGpu->readPixels(left, top, width, height, config, buffer);
+}
+
+void GrContext::writePixels(int left, int top, int width, int height,
+ GrTexture::PixelConfig config, const void* buffer,
+ size_t stride) {
+ const GrGpu::TextureDesc desc = {
+ 0, GrGpu::kNone_AALevel, width, height, config
+ };
+ GrTexture* texture = fGpu->createTexture(desc, buffer, stride);
+ if (NULL == texture) {
+ return;
+ }
+
+ this->flush(true);
+
+ GrAutoUnref aur(texture);
+ GrDrawTarget::AutoStateRestore asr(fGpu);
+
+ GrMatrix matrix;
+ matrix.setTranslate(GrIntToScalar(left), GrIntToScalar(top));
+ fGpu->setViewMatrix(matrix);
+ matrix.setScale(GR_Scalar1 / texture->allocWidth(),
+ GR_Scalar1 / texture->allocHeight());
+ fGpu->setTextureMatrix(matrix);
+
+ fGpu->disableState(GrDrawTarget::kClip_StateBit);
+ fGpu->setAlpha(0xFF);
+ fGpu->setBlendFunc(GrDrawTarget::kOne_BlendCoeff,
+ GrDrawTarget::kZero_BlendCoeff);
+ fGpu->setTexture(texture);
+ fGpu->setSamplerState(GrSamplerState::ClampNoFilter());
+
+ this->fillRect(GrRect(0, 0, GrIntToScalar(width), GrIntToScalar(height)),
+ true);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+/* -------------------------------------------------------
+ * Mimicking the GrGpu interface for now
+ * TODO: define appropriate higher-level API for context
+ */
+
+void GrContext::resetContext() {
+ fGpu->resetContext();
+}
+
+GrVertexBuffer* GrContext::createVertexBuffer(uint32_t size, bool dynamic) {
+ return fGpu->createVertexBuffer(size, dynamic);
+}
+
+GrIndexBuffer* GrContext::createIndexBuffer(uint32_t size, bool dynamic) {
+ return fGpu->createIndexBuffer(size, dynamic);
+}
+
+void GrContext::setTexture(GrTexture* texture) {
+ fGpu->setTexture(texture);
+}
+
+void GrContext::setRenderTarget(GrRenderTarget* target) {
+ flushText();
+ fGpu->setRenderTarget(target);
+}
+
+GrRenderTarget* GrContext::currentRenderTarget() const {
+ return fGpu->currentRenderTarget();
+}
+
+void GrContext::setDefaultRenderTargetSize(uint32_t width, uint32_t height) {
+ fGpu->setDefaultRenderTargetSize(width, height);
+}
+
+void GrContext::setSamplerState(const GrSamplerState& samplerState) {
+ fGpu->setSamplerState(samplerState);
+}
+
+void GrContext::setTextureMatrix(const GrMatrix& m) {
+ fGpu->setTextureMatrix(m);
+}
+
+void GrContext::getViewMatrix(GrMatrix* m) const {
+ fGpu->getViewMatrix(m);
+}
+
+void GrContext::setViewMatrix(const GrMatrix& m) {
+ fGpu->setViewMatrix(m);
+}
+
+bool GrContext::reserveAndLockGeometry(GrVertexLayout vertexLayout,
+ uint32_t vertexCount,
+ uint32_t indexCount,
+ void** vertices,
+ void** indices) {
+ return fGpu->reserveAndLockGeometry(vertexLayout,
+ vertexCount,
+ indexCount,
+ vertices,
+ indices);
+}
+
+void GrContext::drawIndexed(GrGpu::PrimitiveType type,
+ uint32_t startVertex,
+ uint32_t startIndex,
+ uint32_t vertexCount,
+ uint32_t indexCount) {
+ flushText();
+ fGpu->drawIndexed(type,
+ startVertex,
+ startIndex,
+ vertexCount,
+ indexCount);
+}
+
+void GrContext::drawNonIndexed(GrGpu::PrimitiveType type,
+ uint32_t startVertex,
+ uint32_t vertexCount) {
+ flushText();
+ fGpu->drawNonIndexed(type,
+ startVertex,
+ vertexCount);
+}
+
+void GrContext::setVertexSourceToArray(const void* array,
+ GrVertexLayout vertexLayout) {
+ fGpu->setVertexSourceToArray(array, vertexLayout);
+}
+
+void GrContext::setIndexSourceToArray(const void* array) {
+ fGpu->setIndexSourceToArray(array);
+}
+
+void GrContext::setVertexSourceToBuffer(GrVertexBuffer* buffer,
+ GrVertexLayout vertexLayout) {
+ fGpu->setVertexSourceToBuffer(buffer, vertexLayout);
+}
+
+void GrContext::setIndexSourceToBuffer(GrIndexBuffer* buffer) {
+ fGpu->setIndexSourceToBuffer(buffer);
+}
+
+void GrContext::releaseReservedGeometry() {
+ fGpu->releaseReservedGeometry();
+}
+
+void GrContext::setClip(const GrClip& clip) {
+ fGpu->setClip(clip);
+ fGpu->enableState(GrDrawTarget::kClip_StateBit);
+}
+
+void GrContext::setAlpha(uint8_t alpha) {
+ fGpu->setAlpha(alpha);
+}
+
+void GrContext::setColor(GrColor color) {
+ fGpu->setColor(color);
+}
+
+static inline intptr_t setOrClear(intptr_t bits, int shift, intptr_t pred) {
+ intptr_t mask = 1 << shift;
+ if (pred) {
+ bits |= mask;
+ } else {
+ bits &= ~mask;
+ }
+ return bits;
+}
+
+void GrContext::setAntiAlias(bool aa) {
+ if (aa) {
+ fGpu->enableState(GrGpu::kAntialias_StateBit);
+ } else {
+ fGpu->disableState(GrGpu::kAntialias_StateBit);
+ }
+}
+
+void GrContext::setDither(bool dither) {
+ // hack for now, since iPad dither is hella-slow
+ dither = false;
+
+ if (dither) {
+ fGpu->enableState(GrGpu::kDither_StateBit);
+ } else {
+ fGpu->disableState(GrGpu::kDither_StateBit);
+ }
+}
+
+void GrContext::setPointSize(float size) {
+ fGpu->setPointSize(size);
+}
+
+void GrContext::setBlendFunc(GrGpu::BlendCoeff srcCoef,
+ GrGpu::BlendCoeff dstCoef) {
+ fGpu->setBlendFunc(srcCoef, dstCoef);
+}
+
+void GrContext::resetStats() {
+ fGpu->resetStats();
+}
+
+const GrGpu::Stats& GrContext::getStats() const {
+ return fGpu->getStats();
+}
+
+void GrContext::printStats() const {
+ fGpu->printStats();
+}
+
+GrContext::GrContext(GrGpu* gpu) :
+ fVBAllocPool(gpu,
+ gpu->supportsBufferLocking() ? POOL_VB_SIZE : 0,
+ gpu->supportsBufferLocking() ? NUM_POOL_VBS : 0),
+ fTextDrawBuffer(gpu->supportsBufferLocking() ? &fVBAllocPool : NULL) {
+ fGpu = gpu;
+ fGpu->ref();
+ fTextureCache = new GrTextureCache(MAX_TEXTURE_CACHE_COUNT,
+ MAX_TEXTURE_CACHE_BYTES);
+ fFontCache = new GrFontCache(fGpu);
+}
+
+bool GrContext::finalizeTextureKey(GrTextureKey* key,
+ const GrSamplerState& sampler) const {
+ uint32_t bits = 0;
+ uint16_t width = key->width();
+ uint16_t height = key->height();
+ if (fGpu->npotTextureSupport() < GrGpu::kNonRendertarget_NPOTTextureType) {
+ if ((sampler.getWrapX() != GrSamplerState::kClamp_WrapMode ||
+ sampler.getWrapY() != GrSamplerState::kClamp_WrapMode) &&
+ (!GrIsPow2(width) || !GrIsPow2(height))) {
+ bits |= 1;
+ bits |= sampler.isFilter() ? 2 : 0;
+ }
+ }
+ key->finalize(bits);
+ return 0 != bits;
+}
+
+GrDrawTarget* GrContext::getTextTarget() {
+#if DEFER_TEXT_RENDERING
+ fTextDrawBuffer.initializeDrawStateAndClip(*fGpu);
+ return &fTextDrawBuffer;
+#else
+ return fGpu;
+#endif
+}
+
+const GrIndexBuffer* GrContext::quadIndexBuffer() const {
+ return fGpu->quadIndexBuffer();
+}
+
+int GrContext::maxQuadsInIndexBuffer() const {
+ return fGpu->maxQuadsInIndexBuffer();
+}
+
+
+
+