OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright 2013 Google Inc. |
| 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. |
| 6 */ |
| 7 |
| 8 #include "GrOvalRenderer.h" |
| 9 |
| 10 #include "effects/GrCircleEdgeEffect.h" |
| 11 #include "effects/GrEllipseEdgeEffect.h" |
| 12 |
| 13 #include "GrDrawState.h" |
| 14 #include "GrDrawTarget.h" |
| 15 #include "SkStrokeRec.h" |
| 16 |
| 17 SK_DEFINE_INST_COUNT(GrOvalRenderer) |
| 18 |
| 19 namespace { |
| 20 |
| 21 struct CircleVertex { |
| 22 GrPoint fPos; |
| 23 GrPoint fCenter; |
| 24 SkScalar fOuterRadius; |
| 25 SkScalar fInnerRadius; |
| 26 }; |
| 27 |
| 28 struct EllipseVertex { |
| 29 GrPoint fPos; |
| 30 GrPoint fCenter; |
| 31 SkScalar fOuterXRadius; |
| 32 SkScalar fOuterXYRatio; |
| 33 SkScalar fInnerXRadius; |
| 34 SkScalar fInnerXYRatio; |
| 35 }; |
| 36 |
| 37 inline bool circle_stays_circle(const SkMatrix& m) { |
| 38 return m.isSimilarity(); |
| 39 } |
| 40 |
| 41 } |
| 42 |
| 43 bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, co
nst GrPaint& paint, |
| 44 const GrRect& oval, const SkStrokeRec& stroke) |
| 45 { |
| 46 if (!paint.isAntiAlias()) { |
| 47 return false; |
| 48 } |
| 49 |
| 50 const SkMatrix& vm = context->getMatrix(); |
| 51 |
| 52 // we can draw circles |
| 53 if (SkScalarNearlyEqual(oval.width(), oval.height()) |
| 54 && circle_stays_circle(vm)) { |
| 55 drawCircle(target, paint, oval, stroke); |
| 56 |
| 57 // and axis-aligned ellipses only |
| 58 } else if (vm.rectStaysRect()) { |
| 59 drawEllipse(target, paint, oval, stroke); |
| 60 |
| 61 } else { |
| 62 return false; |
| 63 } |
| 64 |
| 65 return true; |
| 66 } |
| 67 |
| 68 void GrOvalRenderer::drawCircle(GrDrawTarget* target, |
| 69 const GrPaint& paint, |
| 70 const GrRect& circle, |
| 71 const SkStrokeRec& stroke) |
| 72 { |
| 73 GrDrawState* drawState = target->drawState(); |
| 74 |
| 75 const SkMatrix& vm = drawState->getViewMatrix(); |
| 76 GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY()); |
| 77 vm.mapPoints(¢er, 1); |
| 78 SkScalar radius = vm.mapRadius(SkScalarHalf(circle.width())); |
| 79 SkScalar strokeWidth = vm.mapRadius(stroke.getWidth()); |
| 80 |
| 81 GrDrawState::AutoDeviceCoordDraw adcd(drawState); |
| 82 if (!adcd.succeeded()) { |
| 83 return; |
| 84 } |
| 85 |
| 86 // position + edge |
| 87 static const GrVertexAttrib kVertexAttribs[] = { |
| 88 {kVec2f_GrVertexAttribType, 0}, |
| 89 {kVec4f_GrVertexAttribType, sizeof(GrPoint)} |
| 90 }; |
| 91 drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs)); |
| 92 drawState->setAttribIndex(GrDrawState::kPosition_AttribIndex, 0); |
| 93 GrAssert(sizeof(CircleVertex) == drawState->getVertexSize()); |
| 94 |
| 95 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0); |
| 96 if (!geo.succeeded()) { |
| 97 GrPrintf("Failed to get space for vertices!\n"); |
| 98 return; |
| 99 } |
| 100 |
| 101 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices()); |
| 102 |
| 103 SkStrokeRec::Style style = stroke.getStyle(); |
| 104 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairl
ine_Style == style); |
| 105 enum { |
| 106 // the edge effects share this stage with glyph rendering |
| 107 // (kGlyphMaskStage in GrTextContext) && SW path rendering |
| 108 // (kPathMaskStage in GrSWMaskHelper) |
| 109 kEdgeEffectStage = GrPaint::kTotalStages, |
| 110 }; |
| 111 drawState->setAttribBindings(GrDrawState::kDefault_AttribBindings); |
| 112 |
| 113 GrEffectRef* effect = GrCircleEdgeEffect::Create(isStroked); |
| 114 static const int kCircleEdgeAttrIndex = 1; |
| 115 drawState->setEffect(kEdgeEffectStage, effect, kCircleEdgeAttrIndex)->unref(
); |
| 116 |
| 117 SkScalar innerRadius = 0.0f; |
| 118 SkScalar outerRadius = radius; |
| 119 SkScalar halfWidth = 0; |
| 120 if (style != SkStrokeRec::kFill_Style) { |
| 121 if (SkScalarNearlyZero(strokeWidth)) { |
| 122 halfWidth = SK_ScalarHalf; |
| 123 } else { |
| 124 halfWidth = SkScalarHalf(strokeWidth); |
| 125 } |
| 126 |
| 127 outerRadius += halfWidth; |
| 128 if (isStroked) { |
| 129 innerRadius = SkMaxScalar(0, radius - halfWidth); |
| 130 } |
| 131 } |
| 132 |
| 133 for (int i = 0; i < 4; ++i) { |
| 134 verts[i].fCenter = center; |
| 135 verts[i].fOuterRadius = outerRadius + 0.5f; |
| 136 verts[i].fInnerRadius = innerRadius - 0.5f; |
| 137 } |
| 138 |
| 139 SkScalar L = -outerRadius; |
| 140 SkScalar R = +outerRadius; |
| 141 SkScalar T = -outerRadius; |
| 142 SkScalar B = +outerRadius; |
| 143 |
| 144 // We've extended the outer radius out half a pixel to antialias. |
| 145 // Expand the drawn rect here so all the pixels will be captured. |
| 146 L += center.fX - SK_ScalarHalf; |
| 147 R += center.fX + SK_ScalarHalf; |
| 148 T += center.fY - SK_ScalarHalf; |
| 149 B += center.fY + SK_ScalarHalf; |
| 150 |
| 151 verts[0].fPos = SkPoint::Make(L, T); |
| 152 verts[1].fPos = SkPoint::Make(R, T); |
| 153 verts[2].fPos = SkPoint::Make(L, B); |
| 154 verts[3].fPos = SkPoint::Make(R, B); |
| 155 |
| 156 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4); |
| 157 } |
| 158 |
| 159 void GrOvalRenderer::drawEllipse(GrDrawTarget* target, |
| 160 const GrPaint& paint, |
| 161 const GrRect& ellipse, |
| 162 const SkStrokeRec& stroke) |
| 163 { |
| 164 GrDrawState* drawState = target->drawState(); |
| 165 #ifdef SK_DEBUG |
| 166 { |
| 167 // we should have checked for this previously |
| 168 bool isAxisAlignedEllipse = drawState->getViewMatrix().rectStaysRect(); |
| 169 SkASSERT(paint.isAntiAlias() && isAxisAlignedEllipse); |
| 170 } |
| 171 #endif |
| 172 |
| 173 const SkMatrix& vm = drawState->getViewMatrix(); |
| 174 GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY()); |
| 175 vm.mapPoints(¢er, 1); |
| 176 SkRect xformedRect; |
| 177 vm.mapRect(&xformedRect, ellipse); |
| 178 |
| 179 GrDrawState::AutoDeviceCoordDraw adcd(drawState); |
| 180 if (!adcd.succeeded()) { |
| 181 return; |
| 182 } |
| 183 |
| 184 // position + edge |
| 185 static const GrVertexAttrib kVertexAttribs[] = { |
| 186 {kVec2f_GrVertexAttribType, 0}, |
| 187 {kVec2f_GrVertexAttribType, sizeof(GrPoint)}, |
| 188 {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint)} |
| 189 }; |
| 190 drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs)); |
| 191 drawState->setAttribIndex(GrDrawState::kPosition_AttribIndex, 0); |
| 192 GrAssert(sizeof(EllipseVertex) == drawState->getVertexSize()); |
| 193 |
| 194 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0); |
| 195 if (!geo.succeeded()) { |
| 196 GrPrintf("Failed to get space for vertices!\n"); |
| 197 return; |
| 198 } |
| 199 |
| 200 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices()); |
| 201 |
| 202 SkStrokeRec::Style style = stroke.getStyle(); |
| 203 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairl
ine_Style == style); |
| 204 enum { |
| 205 // the edge effects share this stage with glyph rendering |
| 206 // (kGlyphMaskStage in GrTextContext) && SW path rendering |
| 207 // (kPathMaskStage in GrSWMaskHelper) |
| 208 kEdgeEffectStage = GrPaint::kTotalStages, |
| 209 }; |
| 210 drawState->setAttribBindings(GrDrawState::kDefault_AttribBindings); |
| 211 |
| 212 GrEffectRef* effect = GrEllipseEdgeEffect::Create(isStroked); |
| 213 static const int kEllipseCenterAttrIndex = 1; |
| 214 static const int kEllipseEdgeAttrIndex = 2; |
| 215 drawState->setEffect(kEdgeEffectStage, effect, |
| 216 kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref(
); |
| 217 |
| 218 SkScalar xRadius = SkScalarHalf(xformedRect.width()); |
| 219 SkScalar yRadius = SkScalarHalf(xformedRect.height()); |
| 220 SkScalar innerXRadius = 0.0f; |
| 221 SkScalar innerRatio = 1.0f; |
| 222 |
| 223 if (SkStrokeRec::kFill_Style != style) { |
| 224 SkScalar strokeWidth = stroke.getWidth(); |
| 225 |
| 226 // do (potentially) anisotropic mapping |
| 227 SkVector scaledStroke; |
| 228 scaledStroke.set(strokeWidth, strokeWidth); |
| 229 vm.mapVectors(&scaledStroke, 1); |
| 230 |
| 231 if (SkScalarNearlyZero(scaledStroke.length())) { |
| 232 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); |
| 233 } else { |
| 234 scaledStroke.scale(0.5f); |
| 235 } |
| 236 |
| 237 // this is legit only if scale & translation (which should be the case a
t the moment) |
| 238 if (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style
== style) { |
| 239 SkScalar innerYRadius = SkMaxScalar(0, yRadius - scaledStroke.fY); |
| 240 if (innerYRadius > SK_ScalarNearlyZero) { |
| 241 innerXRadius = SkMaxScalar(0, xRadius - scaledStroke.fX); |
| 242 innerRatio = innerXRadius/innerYRadius; |
| 243 } |
| 244 } |
| 245 xRadius += scaledStroke.fX; |
| 246 yRadius += scaledStroke.fY; |
| 247 } |
| 248 |
| 249 SkScalar outerRatio = SkScalarDiv(xRadius, yRadius); |
| 250 |
| 251 for (int i = 0; i < 4; ++i) { |
| 252 verts[i].fCenter = center; |
| 253 verts[i].fOuterXRadius = xRadius + 0.5f; |
| 254 verts[i].fOuterXYRatio = outerRatio; |
| 255 verts[i].fInnerXRadius = innerXRadius - 0.5f; |
| 256 verts[i].fInnerXYRatio = innerRatio; |
| 257 } |
| 258 |
| 259 SkScalar L = -xRadius; |
| 260 SkScalar R = +xRadius; |
| 261 SkScalar T = -yRadius; |
| 262 SkScalar B = +yRadius; |
| 263 |
| 264 // We've extended the outer x radius out half a pixel to antialias. |
| 265 // Expand the drawn rect here so all the pixels will be captured. |
| 266 L += center.fX - SK_ScalarHalf; |
| 267 R += center.fX + SK_ScalarHalf; |
| 268 T += center.fY - SK_ScalarHalf; |
| 269 B += center.fY + SK_ScalarHalf; |
| 270 |
| 271 verts[0].fPos = SkPoint::Make(L, T); |
| 272 verts[1].fPos = SkPoint::Make(R, T); |
| 273 verts[2].fPos = SkPoint::Make(L, B); |
| 274 verts[3].fPos = SkPoint::Make(R, B); |
| 275 |
| 276 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4); |
| 277 } |
| 278 |
OLD | NEW |