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 "GrAARectRenderer.h" michael@0: #include "GrGpu.h" michael@0: #include "gl/GrGLEffect.h" michael@0: #include "gl/GrGLVertexEffect.h" michael@0: #include "GrTBackendEffectFactory.h" michael@0: #include "SkColorPriv.h" michael@0: #include "effects/GrVertexEffect.h" michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: class GrGLAlignedRectEffect; michael@0: michael@0: // Axis Aligned special case michael@0: class GrAlignedRectEffect : public GrVertexEffect { michael@0: public: michael@0: static GrEffectRef* Create() { michael@0: GR_CREATE_STATIC_EFFECT(gAlignedRectEffect, GrAlignedRectEffect, ()); michael@0: gAlignedRectEffect->ref(); michael@0: return gAlignedRectEffect; michael@0: } michael@0: michael@0: virtual ~GrAlignedRectEffect() {} michael@0: michael@0: static const char* Name() { return "AlignedRectEdge"; } michael@0: michael@0: virtual void getConstantColorComponents(GrColor* color, michael@0: uint32_t* validFlags) const SK_OVERRIDE { michael@0: *validFlags = 0; michael@0: } michael@0: michael@0: virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { michael@0: return GrTBackendEffectFactory::getInstance(); michael@0: } michael@0: michael@0: class GLEffect : public GrGLVertexEffect { michael@0: public: michael@0: GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) michael@0: : INHERITED (factory) {} michael@0: michael@0: virtual void emitCode(GrGLFullShaderBuilder* builder, michael@0: const GrDrawEffect& drawEffect, michael@0: EffectKey key, michael@0: const char* outputColor, michael@0: const char* inputColor, michael@0: const TransformedCoordsArray&, michael@0: const TextureSamplerArray& samplers) SK_OVERRIDE { michael@0: // setup the varying for the Axis aligned rect effect michael@0: // xy -> interpolated offset michael@0: // zw -> w/2+0.5, h/2+0.5 michael@0: const char *vsRectName, *fsRectName; michael@0: builder->addVarying(kVec4f_GrSLType, "Rect", &vsRectName, &fsRectName); michael@0: const SkString* attr0Name = michael@0: builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]); michael@0: builder->vsCodeAppendf("\t%s = %s;\n", vsRectName, attr0Name->c_str()); michael@0: michael@0: // TODO: compute all these offsets, spans, and scales in the VS michael@0: builder->fsCodeAppendf("\tfloat insetW = min(1.0, %s.z) - 0.5;\n", fsRectName); michael@0: builder->fsCodeAppendf("\tfloat insetH = min(1.0, %s.w) - 0.5;\n", fsRectName); michael@0: builder->fsCodeAppend("\tfloat outset = 0.5;\n"); michael@0: // For rects > 1 pixel wide and tall the span's are noops (i.e., 1.0). For rects michael@0: // < 1 pixel wide or tall they serve to normalize the < 1 ramp to a 0 .. 1 range. michael@0: builder->fsCodeAppend("\tfloat spanW = insetW + outset;\n"); michael@0: builder->fsCodeAppend("\tfloat spanH = insetH + outset;\n"); michael@0: // For rects < 1 pixel wide or tall, these scale factors are used to cap the maximum michael@0: // value of coverage that is used. In other words it is the coverage that is michael@0: // used in the interior of the rect after the ramp. michael@0: builder->fsCodeAppend("\tfloat scaleW = min(1.0, 2.0*insetW/spanW);\n"); michael@0: builder->fsCodeAppend("\tfloat scaleH = min(1.0, 2.0*insetH/spanH);\n"); michael@0: michael@0: // Compute the coverage for the rect's width michael@0: builder->fsCodeAppendf( michael@0: "\tfloat coverage = scaleW*clamp((%s.z-abs(%s.x))/spanW, 0.0, 1.0);\n", fsRectName, michael@0: fsRectName); michael@0: // Compute the coverage for the rect's height and merge with the width michael@0: builder->fsCodeAppendf( michael@0: "\tcoverage = coverage*scaleH*clamp((%s.w-abs(%s.y))/spanH, 0.0, 1.0);\n", michael@0: fsRectName, fsRectName); michael@0: michael@0: michael@0: builder->fsCodeAppendf("\t%s = %s;\n", outputColor, michael@0: (GrGLSLExpr4(inputColor) * GrGLSLExpr1("coverage")).c_str()); michael@0: } michael@0: michael@0: static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { michael@0: return 0; michael@0: } michael@0: michael@0: virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect&) SK_OVERRIDE {} michael@0: michael@0: private: michael@0: typedef GrGLVertexEffect INHERITED; michael@0: }; michael@0: michael@0: michael@0: private: michael@0: GrAlignedRectEffect() : GrVertexEffect() { michael@0: this->addVertexAttrib(kVec4f_GrSLType); michael@0: } michael@0: michael@0: virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE { return true; } michael@0: michael@0: GR_DECLARE_EFFECT_TEST; michael@0: michael@0: typedef GrVertexEffect INHERITED; michael@0: }; michael@0: michael@0: michael@0: GR_DEFINE_EFFECT_TEST(GrAlignedRectEffect); michael@0: michael@0: GrEffectRef* GrAlignedRectEffect::TestCreate(SkRandom* random, michael@0: GrContext* context, michael@0: const GrDrawTargetCaps&, michael@0: GrTexture* textures[]) { michael@0: return GrAlignedRectEffect::Create(); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: class GrGLRectEffect; michael@0: michael@0: /** michael@0: * The output of this effect is a modulation of the input color and coverage michael@0: * for an arbitrarily oriented rect. The rect is specified as: michael@0: * Center of the rect michael@0: * Unit vector point down the height of the rect michael@0: * Half width + 0.5 michael@0: * Half height + 0.5 michael@0: * The center and vector are stored in a vec4 varying ("RectEdge") with the michael@0: * center in the xy components and the vector in the zw components. michael@0: * The munged width and height are stored in a vec2 varying ("WidthHeight") michael@0: * with the width in x and the height in y. michael@0: */ michael@0: class GrRectEffect : public GrVertexEffect { michael@0: public: michael@0: static GrEffectRef* Create() { michael@0: GR_CREATE_STATIC_EFFECT(gRectEffect, GrRectEffect, ()); michael@0: gRectEffect->ref(); michael@0: return gRectEffect; michael@0: } michael@0: michael@0: virtual ~GrRectEffect() {} michael@0: michael@0: static const char* Name() { return "RectEdge"; } michael@0: michael@0: virtual void getConstantColorComponents(GrColor* color, michael@0: uint32_t* validFlags) const SK_OVERRIDE { michael@0: *validFlags = 0; michael@0: } michael@0: michael@0: virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { michael@0: return GrTBackendEffectFactory::getInstance(); michael@0: } michael@0: michael@0: class GLEffect : public GrGLVertexEffect { michael@0: public: michael@0: GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) michael@0: : INHERITED (factory) {} michael@0: michael@0: virtual void emitCode(GrGLFullShaderBuilder* builder, michael@0: const GrDrawEffect& drawEffect, michael@0: EffectKey key, michael@0: const char* outputColor, michael@0: const char* inputColor, michael@0: const TransformedCoordsArray&, michael@0: const TextureSamplerArray& samplers) SK_OVERRIDE { michael@0: // setup the varying for the center point and the unit vector michael@0: // that points down the height of the rect michael@0: const char *vsRectEdgeName, *fsRectEdgeName; michael@0: builder->addVarying(kVec4f_GrSLType, "RectEdge", michael@0: &vsRectEdgeName, &fsRectEdgeName); michael@0: const SkString* attr0Name = michael@0: builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]); michael@0: builder->vsCodeAppendf("\t%s = %s;\n", vsRectEdgeName, attr0Name->c_str()); michael@0: michael@0: // setup the varying for width/2+.5 and height/2+.5 michael@0: const char *vsWidthHeightName, *fsWidthHeightName; michael@0: builder->addVarying(kVec2f_GrSLType, "WidthHeight", michael@0: &vsWidthHeightName, &fsWidthHeightName); michael@0: const SkString* attr1Name = michael@0: builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]); michael@0: builder->vsCodeAppendf("\t%s = %s;\n", vsWidthHeightName, attr1Name->c_str()); michael@0: michael@0: // TODO: compute all these offsets, spans, and scales in the VS michael@0: builder->fsCodeAppendf("\tfloat insetW = min(1.0, %s.x) - 0.5;\n", fsWidthHeightName); michael@0: builder->fsCodeAppendf("\tfloat insetH = min(1.0, %s.y) - 0.5;\n", fsWidthHeightName); michael@0: builder->fsCodeAppend("\tfloat outset = 0.5;\n"); michael@0: // For rects > 1 pixel wide and tall the span's are noops (i.e., 1.0). For rects michael@0: // < 1 pixel wide or tall they serve to normalize the < 1 ramp to a 0 .. 1 range. michael@0: builder->fsCodeAppend("\tfloat spanW = insetW + outset;\n"); michael@0: builder->fsCodeAppend("\tfloat spanH = insetH + outset;\n"); michael@0: // For rects < 1 pixel wide or tall, these scale factors are used to cap the maximum michael@0: // value of coverage that is used. In other words it is the coverage that is michael@0: // used in the interior of the rect after the ramp. michael@0: builder->fsCodeAppend("\tfloat scaleW = min(1.0, 2.0*insetW/spanW);\n"); michael@0: builder->fsCodeAppend("\tfloat scaleH = min(1.0, 2.0*insetH/spanH);\n"); michael@0: michael@0: // Compute the coverage for the rect's width michael@0: builder->fsCodeAppendf("\tvec2 offset = %s.xy - %s.xy;\n", michael@0: builder->fragmentPosition(), fsRectEdgeName); michael@0: builder->fsCodeAppendf("\tfloat perpDot = abs(offset.x * %s.w - offset.y * %s.z);\n", michael@0: fsRectEdgeName, fsRectEdgeName); michael@0: builder->fsCodeAppendf( michael@0: "\tfloat coverage = scaleW*clamp((%s.x-perpDot)/spanW, 0.0, 1.0);\n", michael@0: fsWidthHeightName); michael@0: michael@0: // Compute the coverage for the rect's height and merge with the width michael@0: builder->fsCodeAppendf("\tperpDot = abs(dot(offset, %s.zw));\n", michael@0: fsRectEdgeName); michael@0: builder->fsCodeAppendf( michael@0: "\tcoverage = coverage*scaleH*clamp((%s.y-perpDot)/spanH, 0.0, 1.0);\n", michael@0: fsWidthHeightName); michael@0: michael@0: michael@0: builder->fsCodeAppendf("\t%s = %s;\n", outputColor, michael@0: (GrGLSLExpr4(inputColor) * GrGLSLExpr1("coverage")).c_str()); michael@0: } michael@0: michael@0: static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { michael@0: return 0; michael@0: } michael@0: michael@0: virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect&) SK_OVERRIDE {} michael@0: michael@0: private: michael@0: typedef GrGLVertexEffect INHERITED; michael@0: }; michael@0: michael@0: michael@0: private: michael@0: GrRectEffect() : GrVertexEffect() { michael@0: this->addVertexAttrib(kVec4f_GrSLType); michael@0: this->addVertexAttrib(kVec2f_GrSLType); michael@0: this->setWillReadFragmentPosition(); michael@0: } michael@0: michael@0: virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE { return true; } michael@0: michael@0: GR_DECLARE_EFFECT_TEST; michael@0: michael@0: typedef GrVertexEffect INHERITED; michael@0: }; michael@0: michael@0: michael@0: GR_DEFINE_EFFECT_TEST(GrRectEffect); michael@0: michael@0: GrEffectRef* GrRectEffect::TestCreate(SkRandom* random, michael@0: GrContext* context, michael@0: const GrDrawTargetCaps&, michael@0: GrTexture* textures[]) { michael@0: return GrRectEffect::Create(); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: namespace { michael@0: michael@0: extern const GrVertexAttrib gAARectCoverageAttribs[] = { michael@0: {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, michael@0: {kVec4ub_GrVertexAttribType, sizeof(GrPoint), kCoverage_GrVertexAttribBinding}, michael@0: }; michael@0: michael@0: extern const GrVertexAttrib gAARectColorAttribs[] = { michael@0: {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, michael@0: {kVec4ub_GrVertexAttribType, sizeof(GrPoint), kColor_GrVertexAttribBinding}, michael@0: }; michael@0: michael@0: static void set_aa_rect_vertex_attributes(GrDrawState* drawState, bool useCoverage) { michael@0: if (useCoverage) { michael@0: drawState->setVertexAttribs(SK_ARRAY_COUNT(gAARectCoverageAttribs)); michael@0: } else { michael@0: drawState->setVertexAttribs(SK_ARRAY_COUNT(gAARectColorAttribs)); michael@0: } michael@0: } michael@0: michael@0: static void set_inset_fan(GrPoint* pts, size_t stride, michael@0: const SkRect& r, SkScalar dx, SkScalar dy) { michael@0: pts->setRectFan(r.fLeft + dx, r.fTop + dy, michael@0: r.fRight - dx, r.fBottom - dy, stride); michael@0: } michael@0: michael@0: }; michael@0: michael@0: void GrAARectRenderer::reset() { michael@0: SkSafeSetNull(fAAFillRectIndexBuffer); michael@0: SkSafeSetNull(fAAMiterStrokeRectIndexBuffer); michael@0: SkSafeSetNull(fAABevelStrokeRectIndexBuffer); michael@0: } michael@0: michael@0: static const uint16_t gFillAARectIdx[] = { michael@0: 0, 1, 5, 5, 4, 0, michael@0: 1, 2, 6, 6, 5, 1, michael@0: 2, 3, 7, 7, 6, 2, michael@0: 3, 0, 4, 4, 7, 3, michael@0: 4, 5, 6, 6, 7, 4, michael@0: }; michael@0: michael@0: static const int kIndicesPerAAFillRect = GR_ARRAY_COUNT(gFillAARectIdx); michael@0: static const int kVertsPerAAFillRect = 8; michael@0: static const int kNumAAFillRectsInIndexBuffer = 256; michael@0: michael@0: GrIndexBuffer* GrAARectRenderer::aaFillRectIndexBuffer(GrGpu* gpu) { michael@0: static const size_t kAAFillRectIndexBufferSize = kIndicesPerAAFillRect * michael@0: sizeof(uint16_t) * michael@0: kNumAAFillRectsInIndexBuffer; michael@0: michael@0: if (NULL == fAAFillRectIndexBuffer) { michael@0: fAAFillRectIndexBuffer = gpu->createIndexBuffer(kAAFillRectIndexBufferSize, false); michael@0: if (NULL != fAAFillRectIndexBuffer) { michael@0: uint16_t* data = (uint16_t*) fAAFillRectIndexBuffer->lock(); michael@0: bool useTempData = (NULL == data); michael@0: if (useTempData) { michael@0: data = SkNEW_ARRAY(uint16_t, kNumAAFillRectsInIndexBuffer * kIndicesPerAAFillRect); michael@0: } michael@0: for (int i = 0; i < kNumAAFillRectsInIndexBuffer; ++i) { michael@0: // Each AA filled rect is drawn with 8 vertices and 10 triangles (8 around michael@0: // the inner rect (for AA) and 2 for the inner rect. michael@0: int baseIdx = i * kIndicesPerAAFillRect; michael@0: uint16_t baseVert = (uint16_t)(i * kVertsPerAAFillRect); michael@0: for (int j = 0; j < kIndicesPerAAFillRect; ++j) { michael@0: data[baseIdx+j] = baseVert + gFillAARectIdx[j]; michael@0: } michael@0: } michael@0: if (useTempData) { michael@0: if (!fAAFillRectIndexBuffer->updateData(data, kAAFillRectIndexBufferSize)) { michael@0: GrCrash("Can't get AA Fill Rect indices into buffer!"); michael@0: } michael@0: SkDELETE_ARRAY(data); michael@0: } else { michael@0: fAAFillRectIndexBuffer->unlock(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return fAAFillRectIndexBuffer; michael@0: } michael@0: michael@0: static const uint16_t gMiterStrokeAARectIdx[] = { michael@0: 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0, michael@0: 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0, michael@0: 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0, michael@0: 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0, michael@0: michael@0: 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4, michael@0: 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4, michael@0: 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4, michael@0: 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4, michael@0: michael@0: 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8, michael@0: 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8, michael@0: 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8, michael@0: 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8, michael@0: }; michael@0: michael@0: /** michael@0: * As in miter-stroke, index = a + b, and a is the current index, b is the shift michael@0: * from the first index. The index layout: michael@0: * outer AA line: 0~3, 4~7 michael@0: * outer edge: 8~11, 12~15 michael@0: * inner edge: 16~19 michael@0: * inner AA line: 20~23 michael@0: * Following comes a bevel-stroke rect and its indices: michael@0: * michael@0: * 4 7 michael@0: * ********************************* michael@0: * * ______________________________ * michael@0: * * / 12 15 \ * michael@0: * * / \ * michael@0: * 0 * |8 16_____________________19 11 | * 3 michael@0: * * | | | | * michael@0: * * | | **************** | | * michael@0: * * | | * 20 23 * | | * michael@0: * * | | * * | | * michael@0: * * | | * 21 22 * | | * michael@0: * * | | **************** | | * michael@0: * * | |____________________| | * michael@0: * 1 * |9 17 18 10| * 2 michael@0: * * \ / * michael@0: * * \13 __________________________14/ * michael@0: * * * michael@0: * ********************************** michael@0: * 5 6 michael@0: */ michael@0: static const uint16_t gBevelStrokeAARectIdx[] = { michael@0: // Draw outer AA, from outer AA line to outer edge, shift is 0. michael@0: 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0, michael@0: 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0, michael@0: 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0, michael@0: 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0, michael@0: 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0, michael@0: 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0, michael@0: 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0, michael@0: 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0, michael@0: michael@0: // Draw the stroke, from outer edge to inner edge, shift is 8. michael@0: 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8, michael@0: 1 + 8, 5 + 8, 9 + 8, michael@0: 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8, michael@0: 6 + 8, 2 + 8, 10 + 8, michael@0: 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8, michael@0: 3 + 8, 7 + 8, 11 + 8, michael@0: 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8, michael@0: 4 + 8, 0 + 8, 8 + 8, michael@0: michael@0: // Draw the inner AA, from inner edge to inner AA line, shift is 16. michael@0: 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16, michael@0: 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16, michael@0: 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16, michael@0: 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16, michael@0: }; michael@0: michael@0: int GrAARectRenderer::aaStrokeRectIndexCount(bool miterStroke) { michael@0: return miterStroke ? GR_ARRAY_COUNT(gMiterStrokeAARectIdx) : michael@0: GR_ARRAY_COUNT(gBevelStrokeAARectIdx); michael@0: } michael@0: michael@0: GrIndexBuffer* GrAARectRenderer::aaStrokeRectIndexBuffer(GrGpu* gpu, bool miterStroke) { michael@0: if (miterStroke) { michael@0: if (NULL == fAAMiterStrokeRectIndexBuffer) { michael@0: fAAMiterStrokeRectIndexBuffer = michael@0: gpu->createIndexBuffer(sizeof(gMiterStrokeAARectIdx), false); michael@0: if (NULL != fAAMiterStrokeRectIndexBuffer) { michael@0: #ifdef SK_DEBUG michael@0: bool updated = michael@0: #endif michael@0: fAAMiterStrokeRectIndexBuffer->updateData(gMiterStrokeAARectIdx, michael@0: sizeof(gMiterStrokeAARectIdx)); michael@0: GR_DEBUGASSERT(updated); michael@0: } michael@0: } michael@0: return fAAMiterStrokeRectIndexBuffer; michael@0: } else { michael@0: if (NULL == fAABevelStrokeRectIndexBuffer) { michael@0: fAABevelStrokeRectIndexBuffer = michael@0: gpu->createIndexBuffer(sizeof(gBevelStrokeAARectIdx), false); michael@0: if (NULL != fAABevelStrokeRectIndexBuffer) { michael@0: #ifdef SK_DEBUG michael@0: bool updated = michael@0: #endif michael@0: fAABevelStrokeRectIndexBuffer->updateData(gBevelStrokeAARectIdx, michael@0: sizeof(gBevelStrokeAARectIdx)); michael@0: GR_DEBUGASSERT(updated); michael@0: } michael@0: } michael@0: return fAABevelStrokeRectIndexBuffer; michael@0: } michael@0: } michael@0: michael@0: void GrAARectRenderer::geometryFillAARect(GrGpu* gpu, michael@0: GrDrawTarget* target, michael@0: const SkRect& rect, michael@0: const SkMatrix& combinedMatrix, michael@0: const SkRect& devRect, michael@0: bool useVertexCoverage) { michael@0: GrDrawState* drawState = target->drawState(); michael@0: michael@0: set_aa_rect_vertex_attributes(drawState, useVertexCoverage); michael@0: michael@0: GrDrawTarget::AutoReleaseGeometry geo(target, 8, 0); michael@0: if (!geo.succeeded()) { michael@0: GrPrintf("Failed to get space for vertices!\n"); michael@0: return; michael@0: } michael@0: michael@0: GrIndexBuffer* indexBuffer = this->aaFillRectIndexBuffer(gpu); michael@0: if (NULL == indexBuffer) { michael@0: GrPrintf("Failed to create index buffer!\n"); michael@0: return; michael@0: } michael@0: michael@0: intptr_t verts = reinterpret_cast(geo.vertices()); michael@0: size_t vsize = drawState->getVertexSize(); michael@0: SkASSERT(sizeof(GrPoint) + sizeof(GrColor) == vsize); michael@0: michael@0: GrPoint* fan0Pos = reinterpret_cast(verts); michael@0: GrPoint* fan1Pos = reinterpret_cast(verts + 4 * vsize); michael@0: michael@0: SkScalar inset = SkMinScalar(devRect.width(), SK_Scalar1); michael@0: inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height()); michael@0: michael@0: if (combinedMatrix.rectStaysRect()) { michael@0: // Temporarily #if'ed out. We don't want to pass in the devRect but michael@0: // right now it is computed in GrContext::apply_aa_to_rect and we don't michael@0: // want to throw away the work michael@0: #if 0 michael@0: SkRect devRect; michael@0: combinedMatrix.mapRect(&devRect, rect); michael@0: #endif michael@0: michael@0: set_inset_fan(fan0Pos, vsize, devRect, -SK_ScalarHalf, -SK_ScalarHalf); michael@0: set_inset_fan(fan1Pos, vsize, devRect, inset, inset); michael@0: } else { michael@0: // compute transformed (1, 0) and (0, 1) vectors michael@0: SkVector vec[2] = { michael@0: { combinedMatrix[SkMatrix::kMScaleX], combinedMatrix[SkMatrix::kMSkewY] }, michael@0: { combinedMatrix[SkMatrix::kMSkewX], combinedMatrix[SkMatrix::kMScaleY] } michael@0: }; michael@0: michael@0: vec[0].normalize(); michael@0: vec[0].scale(SK_ScalarHalf); michael@0: vec[1].normalize(); michael@0: vec[1].scale(SK_ScalarHalf); michael@0: michael@0: // create the rotated rect michael@0: fan0Pos->setRectFan(rect.fLeft, rect.fTop, michael@0: rect.fRight, rect.fBottom, vsize); michael@0: combinedMatrix.mapPointsWithStride(fan0Pos, vsize, 4); michael@0: michael@0: // Now create the inset points and then outset the original michael@0: // rotated points michael@0: michael@0: // TL michael@0: *((SkPoint*)((intptr_t)fan1Pos + 0 * vsize)) = michael@0: *((SkPoint*)((intptr_t)fan0Pos + 0 * vsize)) + vec[0] + vec[1]; michael@0: *((SkPoint*)((intptr_t)fan0Pos + 0 * vsize)) -= vec[0] + vec[1]; michael@0: // BL michael@0: *((SkPoint*)((intptr_t)fan1Pos + 1 * vsize)) = michael@0: *((SkPoint*)((intptr_t)fan0Pos + 1 * vsize)) + vec[0] - vec[1]; michael@0: *((SkPoint*)((intptr_t)fan0Pos + 1 * vsize)) -= vec[0] - vec[1]; michael@0: // BR michael@0: *((SkPoint*)((intptr_t)fan1Pos + 2 * vsize)) = michael@0: *((SkPoint*)((intptr_t)fan0Pos + 2 * vsize)) - vec[0] - vec[1]; michael@0: *((SkPoint*)((intptr_t)fan0Pos + 2 * vsize)) += vec[0] + vec[1]; michael@0: // TR michael@0: *((SkPoint*)((intptr_t)fan1Pos + 3 * vsize)) = michael@0: *((SkPoint*)((intptr_t)fan0Pos + 3 * vsize)) - vec[0] + vec[1]; michael@0: *((SkPoint*)((intptr_t)fan0Pos + 3 * vsize)) += vec[0] - vec[1]; michael@0: } michael@0: michael@0: verts += sizeof(GrPoint); michael@0: for (int i = 0; i < 4; ++i) { michael@0: *reinterpret_cast(verts + i * vsize) = 0; michael@0: } michael@0: michael@0: int scale; michael@0: if (inset < SK_ScalarHalf) { michael@0: scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf)); michael@0: SkASSERT(scale >= 0 && scale <= 255); michael@0: } else { michael@0: scale = 0xff; michael@0: } michael@0: michael@0: GrColor innerColor; michael@0: if (useVertexCoverage) { michael@0: innerColor = GrColorPackRGBA(scale, scale, scale, scale); michael@0: } else { michael@0: if (0xff == scale) { michael@0: innerColor = target->getDrawState().getColor(); michael@0: } else { michael@0: innerColor = SkAlphaMulQ(target->getDrawState().getColor(), scale); michael@0: } michael@0: } michael@0: michael@0: verts += 4 * vsize; michael@0: for (int i = 0; i < 4; ++i) { michael@0: *reinterpret_cast(verts + i * vsize) = innerColor; michael@0: } michael@0: michael@0: target->setIndexSourceToBuffer(indexBuffer); michael@0: target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, michael@0: kVertsPerAAFillRect, michael@0: kIndicesPerAAFillRect); michael@0: target->resetIndexSource(); michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: // Rotated michael@0: struct RectVertex { michael@0: GrPoint fPos; michael@0: GrPoint fCenter; michael@0: GrPoint fDir; michael@0: GrPoint fWidthHeight; michael@0: }; michael@0: michael@0: // Rotated michael@0: extern const GrVertexAttrib gAARectVertexAttribs[] = { michael@0: { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding }, michael@0: { kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding }, michael@0: { kVec2f_GrVertexAttribType, 3*sizeof(GrPoint), kEffect_GrVertexAttribBinding } michael@0: }; michael@0: michael@0: // Axis Aligned michael@0: struct AARectVertex { michael@0: GrPoint fPos; michael@0: GrPoint fOffset; michael@0: GrPoint fWidthHeight; michael@0: }; michael@0: michael@0: // Axis Aligned michael@0: extern const GrVertexAttrib gAAAARectVertexAttribs[] = { michael@0: { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding }, michael@0: { kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding }, michael@0: }; michael@0: michael@0: }; michael@0: michael@0: void GrAARectRenderer::shaderFillAARect(GrGpu* gpu, michael@0: GrDrawTarget* target, michael@0: const SkRect& rect, michael@0: const SkMatrix& combinedMatrix) { michael@0: GrDrawState* drawState = target->drawState(); michael@0: michael@0: SkPoint center = SkPoint::Make(rect.centerX(), rect.centerY()); michael@0: combinedMatrix.mapPoints(¢er, 1); michael@0: michael@0: // compute transformed (0, 1) vector michael@0: SkVector dir = { combinedMatrix[SkMatrix::kMSkewX], combinedMatrix[SkMatrix::kMScaleY] }; michael@0: dir.normalize(); michael@0: michael@0: // compute transformed (width, 0) and (0, height) vectors michael@0: SkVector vec[2] = { michael@0: { combinedMatrix[SkMatrix::kMScaleX], combinedMatrix[SkMatrix::kMSkewY] }, michael@0: { combinedMatrix[SkMatrix::kMSkewX], combinedMatrix[SkMatrix::kMScaleY] } michael@0: }; michael@0: michael@0: SkScalar newWidth = SkScalarHalf(rect.width() * vec[0].length()) + SK_ScalarHalf; michael@0: SkScalar newHeight = SkScalarHalf(rect.height() * vec[1].length()) + SK_ScalarHalf; michael@0: drawState->setVertexAttribs(SK_ARRAY_COUNT(gAARectVertexAttribs)); michael@0: SkASSERT(sizeof(RectVertex) == drawState->getVertexSize()); michael@0: michael@0: GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0); michael@0: if (!geo.succeeded()) { michael@0: GrPrintf("Failed to get space for vertices!\n"); michael@0: return; michael@0: } michael@0: michael@0: RectVertex* verts = reinterpret_cast(geo.vertices()); michael@0: michael@0: GrEffectRef* effect = GrRectEffect::Create(); michael@0: static const int kRectAttrIndex = 1; michael@0: static const int kWidthIndex = 2; michael@0: drawState->addCoverageEffect(effect, kRectAttrIndex, kWidthIndex)->unref(); michael@0: michael@0: for (int i = 0; i < 4; ++i) { michael@0: verts[i].fCenter = center; michael@0: verts[i].fDir = dir; michael@0: verts[i].fWidthHeight.fX = newWidth; michael@0: verts[i].fWidthHeight.fY = newHeight; michael@0: } michael@0: michael@0: SkRect devRect; michael@0: combinedMatrix.mapRect(&devRect, rect); michael@0: michael@0: SkRect devBounds = { michael@0: devRect.fLeft - SK_ScalarHalf, michael@0: devRect.fTop - SK_ScalarHalf, michael@0: devRect.fRight + SK_ScalarHalf, michael@0: devRect.fBottom + SK_ScalarHalf michael@0: }; michael@0: michael@0: verts[0].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fTop); michael@0: verts[1].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fBottom); michael@0: verts[2].fPos = SkPoint::Make(devBounds.fRight, devBounds.fBottom); michael@0: verts[3].fPos = SkPoint::Make(devBounds.fRight, devBounds.fTop); michael@0: michael@0: target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer()); michael@0: target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6); michael@0: target->resetIndexSource(); michael@0: } michael@0: michael@0: void GrAARectRenderer::shaderFillAlignedAARect(GrGpu* gpu, michael@0: GrDrawTarget* target, michael@0: const SkRect& rect, michael@0: const SkMatrix& combinedMatrix) { michael@0: GrDrawState* drawState = target->drawState(); michael@0: SkASSERT(combinedMatrix.rectStaysRect()); michael@0: michael@0: drawState->setVertexAttribs(SK_ARRAY_COUNT(gAAAARectVertexAttribs)); michael@0: SkASSERT(sizeof(AARectVertex) == drawState->getVertexSize()); michael@0: michael@0: GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0); michael@0: if (!geo.succeeded()) { michael@0: GrPrintf("Failed to get space for vertices!\n"); michael@0: return; michael@0: } michael@0: michael@0: AARectVertex* verts = reinterpret_cast(geo.vertices()); michael@0: michael@0: GrEffectRef* effect = GrAlignedRectEffect::Create(); michael@0: static const int kOffsetIndex = 1; michael@0: drawState->addCoverageEffect(effect, kOffsetIndex)->unref(); michael@0: michael@0: SkRect devRect; michael@0: combinedMatrix.mapRect(&devRect, rect); michael@0: michael@0: SkRect devBounds = { michael@0: devRect.fLeft - SK_ScalarHalf, michael@0: devRect.fTop - SK_ScalarHalf, michael@0: devRect.fRight + SK_ScalarHalf, michael@0: devRect.fBottom + SK_ScalarHalf michael@0: }; michael@0: michael@0: GrPoint widthHeight = { michael@0: SkScalarHalf(devRect.width()) + SK_ScalarHalf, michael@0: SkScalarHalf(devRect.height()) + SK_ScalarHalf michael@0: }; michael@0: michael@0: verts[0].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fTop); michael@0: verts[0].fOffset = SkPoint::Make(-widthHeight.fX, -widthHeight.fY); michael@0: verts[0].fWidthHeight = widthHeight; michael@0: michael@0: verts[1].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fBottom); michael@0: verts[1].fOffset = SkPoint::Make(-widthHeight.fX, widthHeight.fY); michael@0: verts[1].fWidthHeight = widthHeight; michael@0: michael@0: verts[2].fPos = SkPoint::Make(devBounds.fRight, devBounds.fBottom); michael@0: verts[2].fOffset = widthHeight; michael@0: verts[2].fWidthHeight = widthHeight; michael@0: michael@0: verts[3].fPos = SkPoint::Make(devBounds.fRight, devBounds.fTop); michael@0: verts[3].fOffset = SkPoint::Make(widthHeight.fX, -widthHeight.fY); michael@0: verts[3].fWidthHeight = widthHeight; michael@0: michael@0: target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer()); michael@0: target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6); michael@0: target->resetIndexSource(); michael@0: } michael@0: michael@0: void GrAARectRenderer::strokeAARect(GrGpu* gpu, michael@0: GrDrawTarget* target, michael@0: const SkRect& rect, michael@0: const SkMatrix& combinedMatrix, michael@0: const SkRect& devRect, michael@0: const SkStrokeRec* stroke, michael@0: bool useVertexCoverage) { michael@0: GrVec devStrokeSize; michael@0: SkScalar width = stroke->getWidth(); michael@0: if (width > 0) { michael@0: devStrokeSize.set(width, width); michael@0: combinedMatrix.mapVectors(&devStrokeSize, 1); michael@0: devStrokeSize.setAbs(devStrokeSize); michael@0: } else { michael@0: devStrokeSize.set(SK_Scalar1, SK_Scalar1); michael@0: } michael@0: michael@0: const SkScalar dx = devStrokeSize.fX; michael@0: const SkScalar dy = devStrokeSize.fY; michael@0: const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf); michael@0: const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf); michael@0: michael@0: // Temporarily #if'ed out. We don't want to pass in the devRect but michael@0: // right now it is computed in GrContext::apply_aa_to_rect and we don't michael@0: // want to throw away the work michael@0: #if 0 michael@0: SkRect devRect; michael@0: combinedMatrix.mapRect(&devRect, rect); michael@0: #endif michael@0: michael@0: SkScalar spare; michael@0: { michael@0: SkScalar w = devRect.width() - dx; michael@0: SkScalar h = devRect.height() - dy; michael@0: spare = GrMin(w, h); michael@0: } michael@0: michael@0: SkRect devOutside(devRect); michael@0: devOutside.outset(rx, ry); michael@0: michael@0: bool miterStroke = true; michael@0: // small miter limit means right angles show bevel... michael@0: if (stroke->getJoin() != SkPaint::kMiter_Join || stroke->getMiter() < SK_ScalarSqrt2) { michael@0: miterStroke = false; michael@0: } michael@0: michael@0: if (spare <= 0 && miterStroke) { michael@0: this->fillAARect(gpu, target, devOutside, SkMatrix::I(), michael@0: devOutside, useVertexCoverage); michael@0: return; michael@0: } michael@0: michael@0: SkRect devInside(devRect); michael@0: devInside.inset(rx, ry); michael@0: michael@0: SkRect devOutsideAssist(devRect); michael@0: michael@0: // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist) michael@0: // to draw the outer of the rect. Because there are 8 vertices on the outer michael@0: // edge, while vertex number of inner edge is 4, the same as miter-stroke. michael@0: if (!miterStroke) { michael@0: devOutside.inset(0, ry); michael@0: devOutsideAssist.outset(0, ry); michael@0: } michael@0: michael@0: this->geometryStrokeAARect(gpu, target, devOutside, devOutsideAssist, michael@0: devInside, useVertexCoverage, miterStroke); michael@0: } michael@0: michael@0: void GrAARectRenderer::geometryStrokeAARect(GrGpu* gpu, michael@0: GrDrawTarget* target, michael@0: const SkRect& devOutside, michael@0: const SkRect& devOutsideAssist, michael@0: const SkRect& devInside, michael@0: bool useVertexCoverage, michael@0: bool miterStroke) { michael@0: GrDrawState* drawState = target->drawState(); michael@0: michael@0: set_aa_rect_vertex_attributes(drawState, useVertexCoverage); michael@0: michael@0: int innerVertexNum = 4; michael@0: int outerVertexNum = miterStroke ? 4 : 8; michael@0: int totalVertexNum = (outerVertexNum + innerVertexNum) * 2; michael@0: michael@0: GrDrawTarget::AutoReleaseGeometry geo(target, totalVertexNum, 0); michael@0: if (!geo.succeeded()) { michael@0: GrPrintf("Failed to get space for vertices!\n"); michael@0: return; michael@0: } michael@0: GrIndexBuffer* indexBuffer = this->aaStrokeRectIndexBuffer(gpu, miterStroke); michael@0: if (NULL == indexBuffer) { michael@0: GrPrintf("Failed to create index buffer!\n"); michael@0: return; michael@0: } michael@0: michael@0: intptr_t verts = reinterpret_cast(geo.vertices()); michael@0: size_t vsize = drawState->getVertexSize(); michael@0: SkASSERT(sizeof(GrPoint) + sizeof(GrColor) == vsize); michael@0: michael@0: // We create vertices for four nested rectangles. There are two ramps from 0 to full michael@0: // coverage, one on the exterior of the stroke and the other on the interior. michael@0: // The following pointers refer to the four rects, from outermost to innermost. michael@0: GrPoint* fan0Pos = reinterpret_cast(verts); michael@0: GrPoint* fan1Pos = reinterpret_cast(verts + outerVertexNum * vsize); michael@0: GrPoint* fan2Pos = reinterpret_cast(verts + 2 * outerVertexNum * vsize); michael@0: GrPoint* fan3Pos = reinterpret_cast(verts + (2 * outerVertexNum + innerVertexNum) * vsize); michael@0: michael@0: #ifndef SK_IGNORE_THIN_STROKED_RECT_FIX michael@0: // TODO: this only really works if the X & Y margins are the same all around michael@0: // the rect michael@0: SkScalar inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight); michael@0: inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft); michael@0: inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop); michael@0: if (miterStroke) { michael@0: inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom); michael@0: } else { michael@0: inset = SK_ScalarHalf * SkMinScalar(inset, devOutsideAssist.fBottom - devInside.fBottom); michael@0: } michael@0: SkASSERT(inset >= 0); michael@0: #else michael@0: SkScalar inset = SK_ScalarHalf; michael@0: #endif michael@0: michael@0: if (miterStroke) { michael@0: // outermost michael@0: set_inset_fan(fan0Pos, vsize, devOutside, -SK_ScalarHalf, -SK_ScalarHalf); michael@0: // inner two michael@0: set_inset_fan(fan1Pos, vsize, devOutside, inset, inset); michael@0: set_inset_fan(fan2Pos, vsize, devInside, -inset, -inset); michael@0: // innermost michael@0: set_inset_fan(fan3Pos, vsize, devInside, SK_ScalarHalf, SK_ScalarHalf); michael@0: } else { michael@0: GrPoint* fan0AssistPos = reinterpret_cast(verts + 4 * vsize); michael@0: GrPoint* fan1AssistPos = reinterpret_cast(verts + (outerVertexNum + 4) * vsize); michael@0: // outermost michael@0: set_inset_fan(fan0Pos, vsize, devOutside, -SK_ScalarHalf, -SK_ScalarHalf); michael@0: set_inset_fan(fan0AssistPos, vsize, devOutsideAssist, -SK_ScalarHalf, -SK_ScalarHalf); michael@0: // outer one of the inner two michael@0: set_inset_fan(fan1Pos, vsize, devOutside, inset, inset); michael@0: set_inset_fan(fan1AssistPos, vsize, devOutsideAssist, inset, inset); michael@0: // inner one of the inner two michael@0: set_inset_fan(fan2Pos, vsize, devInside, -inset, -inset); michael@0: // innermost michael@0: set_inset_fan(fan3Pos, vsize, devInside, SK_ScalarHalf, SK_ScalarHalf); michael@0: } michael@0: michael@0: // The outermost rect has 0 coverage michael@0: verts += sizeof(GrPoint); michael@0: for (int i = 0; i < outerVertexNum; ++i) { michael@0: *reinterpret_cast(verts + i * vsize) = 0; michael@0: } michael@0: michael@0: int scale; michael@0: if (inset < SK_ScalarHalf) { michael@0: scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf)); michael@0: SkASSERT(scale >= 0 && scale <= 255); michael@0: } else { michael@0: scale = 0xff; michael@0: } michael@0: michael@0: // The inner two rects have full coverage michael@0: GrColor innerColor; michael@0: if (useVertexCoverage) { michael@0: innerColor = GrColorPackRGBA(scale, scale, scale, scale); michael@0: } else { michael@0: if (0xff == scale) { michael@0: innerColor = target->getDrawState().getColor(); michael@0: } else { michael@0: innerColor = SkAlphaMulQ(target->getDrawState().getColor(), scale); michael@0: } michael@0: } michael@0: michael@0: verts += outerVertexNum * vsize; michael@0: for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) { michael@0: *reinterpret_cast(verts + i * vsize) = innerColor; michael@0: } michael@0: michael@0: // The innermost rect has 0 coverage michael@0: verts += (outerVertexNum + innerVertexNum) * vsize; michael@0: for (int i = 0; i < innerVertexNum; ++i) { michael@0: *reinterpret_cast(verts + i * vsize) = 0; michael@0: } michael@0: michael@0: target->setIndexSourceToBuffer(indexBuffer); michael@0: target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, michael@0: totalVertexNum, aaStrokeRectIndexCount(miterStroke)); michael@0: } michael@0: michael@0: void GrAARectRenderer::fillAANestedRects(GrGpu* gpu, michael@0: GrDrawTarget* target, michael@0: const SkRect rects[2], michael@0: const SkMatrix& combinedMatrix, michael@0: bool useVertexCoverage) { michael@0: SkASSERT(combinedMatrix.rectStaysRect()); michael@0: SkASSERT(!rects[1].isEmpty()); michael@0: michael@0: SkRect devOutside, devOutsideAssist, devInside; michael@0: combinedMatrix.mapRect(&devOutside, rects[0]); michael@0: // can't call mapRect for devInside since it calls sort michael@0: combinedMatrix.mapPoints((SkPoint*)&devInside, (const SkPoint*)&rects[1], 2); michael@0: michael@0: if (devInside.isEmpty()) { michael@0: this->fillAARect(gpu, target, devOutside, SkMatrix::I(), devOutside, useVertexCoverage); michael@0: return; michael@0: } michael@0: michael@0: this->geometryStrokeAARect(gpu, target, devOutside, devOutsideAssist, michael@0: devInside, useVertexCoverage, true); michael@0: }