michael@0: michael@0: /* michael@0: * Copyright 2012 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "GrSoftwarePathRenderer.h" michael@0: #include "GrContext.h" michael@0: #include "GrSWMaskHelper.h" michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: bool GrSoftwarePathRenderer::canDrawPath(const SkPath&, michael@0: const SkStrokeRec&, michael@0: const GrDrawTarget*, michael@0: bool antiAlias) const { michael@0: if (!antiAlias || NULL == fContext) { michael@0: // TODO: We could allow the SW path to also handle non-AA paths but michael@0: // this would mean that GrDefaultPathRenderer would never be called michael@0: // (since it appears after the SW renderer in the path renderer michael@0: // chain). Some testing would need to be done r.e. performance michael@0: // and consistency of the resulting images before removing michael@0: // the "!antiAlias" clause from the above test michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: GrPathRenderer::StencilSupport GrSoftwarePathRenderer::onGetStencilSupport( michael@0: const SkPath&, michael@0: const SkStrokeRec&, michael@0: const GrDrawTarget*) const { michael@0: return GrPathRenderer::kNoSupport_StencilSupport; michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // gets device coord bounds of path (not considering the fill) and clip. The michael@0: // path bounds will be a subset of the clip bounds. returns false if michael@0: // path bounds would be empty. michael@0: bool get_path_and_clip_bounds(const GrDrawTarget* target, michael@0: const SkPath& path, michael@0: const SkMatrix& matrix, michael@0: SkIRect* devPathBounds, michael@0: SkIRect* devClipBounds) { michael@0: // compute bounds as intersection of rt size, clip, and path michael@0: const GrRenderTarget* rt = target->getDrawState().getRenderTarget(); michael@0: if (NULL == rt) { michael@0: return false; michael@0: } michael@0: *devPathBounds = SkIRect::MakeWH(rt->width(), rt->height()); michael@0: michael@0: target->getClip()->getConservativeBounds(rt, devClipBounds); michael@0: michael@0: // TODO: getConservativeBounds already intersects with the michael@0: // render target's bounding box. Remove this next line michael@0: if (!devPathBounds->intersect(*devClipBounds)) { michael@0: return false; michael@0: } michael@0: michael@0: if (!path.getBounds().isEmpty()) { michael@0: SkRect pathSBounds; michael@0: matrix.mapRect(&pathSBounds, path.getBounds()); michael@0: SkIRect pathIBounds; michael@0: pathSBounds.roundOut(&pathIBounds); michael@0: if (!devPathBounds->intersect(pathIBounds)) { michael@0: // set the correct path bounds, as this would be used later. michael@0: *devPathBounds = pathIBounds; michael@0: return false; michael@0: } michael@0: } else { michael@0: *devPathBounds = SkIRect::EmptyIRect(); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: void draw_around_inv_path(GrDrawTarget* target, michael@0: const SkIRect& devClipBounds, michael@0: const SkIRect& devPathBounds) { michael@0: GrDrawState::AutoViewMatrixRestore avmr; michael@0: if (!avmr.setIdentity(target->drawState())) { michael@0: return; michael@0: } michael@0: SkRect rect; michael@0: if (devClipBounds.fTop < devPathBounds.fTop) { michael@0: rect.iset(devClipBounds.fLeft, devClipBounds.fTop, michael@0: devClipBounds.fRight, devPathBounds.fTop); michael@0: target->drawSimpleRect(rect, NULL); michael@0: } michael@0: if (devClipBounds.fLeft < devPathBounds.fLeft) { michael@0: rect.iset(devClipBounds.fLeft, devPathBounds.fTop, michael@0: devPathBounds.fLeft, devPathBounds.fBottom); michael@0: target->drawSimpleRect(rect, NULL); michael@0: } michael@0: if (devClipBounds.fRight > devPathBounds.fRight) { michael@0: rect.iset(devPathBounds.fRight, devPathBounds.fTop, michael@0: devClipBounds.fRight, devPathBounds.fBottom); michael@0: target->drawSimpleRect(rect, NULL); michael@0: } michael@0: if (devClipBounds.fBottom > devPathBounds.fBottom) { michael@0: rect.iset(devClipBounds.fLeft, devPathBounds.fBottom, michael@0: devClipBounds.fRight, devClipBounds.fBottom); michael@0: target->drawSimpleRect(rect, NULL); michael@0: } michael@0: } michael@0: michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // return true on success; false on failure michael@0: bool GrSoftwarePathRenderer::onDrawPath(const SkPath& path, michael@0: const SkStrokeRec& stroke, michael@0: GrDrawTarget* target, michael@0: bool antiAlias) { michael@0: michael@0: if (NULL == fContext) { michael@0: return false; michael@0: } michael@0: michael@0: GrDrawState* drawState = target->drawState(); michael@0: michael@0: SkMatrix vm = drawState->getViewMatrix(); michael@0: michael@0: SkIRect devPathBounds, devClipBounds; michael@0: if (!get_path_and_clip_bounds(target, path, vm, michael@0: &devPathBounds, &devClipBounds)) { michael@0: if (path.isInverseFillType()) { michael@0: draw_around_inv_path(target, devClipBounds, devPathBounds); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: SkAutoTUnref texture( michael@0: GrSWMaskHelper::DrawPathMaskToTexture(fContext, path, stroke, michael@0: devPathBounds, michael@0: antiAlias, &vm)); michael@0: if (NULL == texture) { michael@0: return false; michael@0: } michael@0: michael@0: GrSWMaskHelper::DrawToTargetWithPathMask(texture, target, devPathBounds); michael@0: michael@0: if (path.isInverseFillType()) { michael@0: draw_around_inv_path(target, devClipBounds, devPathBounds); michael@0: } michael@0: michael@0: return true; michael@0: }