Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright 2013 Google Inc. | 2 * Copyright 2013 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include "GrDistanceFieldTextureEffect.h" | 8 #include "GrDistanceFieldTextureEffect.h" |
| 9 #include "GrFontAtlasSizes.h" | 9 #include "GrFontAtlasSizes.h" |
| 10 #include "GrInvariantOutput.h" | 10 #include "GrInvariantOutput.h" |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 77 fsBuilder->codeAppendf("vec2 uv = %s;\n", uv.fsIn()); | 77 fsBuilder->codeAppendf("vec2 uv = %s;\n", uv.fsIn()); |
| 78 | 78 |
| 79 fsBuilder->codeAppend("\tfloat texColor = "); | 79 fsBuilder->codeAppend("\tfloat texColor = "); |
| 80 fsBuilder->appendTextureLookup(args.fSamplers[0], | 80 fsBuilder->appendTextureLookup(args.fSamplers[0], |
| 81 "uv", | 81 "uv", |
| 82 kVec2f_GrSLType); | 82 kVec2f_GrSLType); |
| 83 fsBuilder->codeAppend(".r;\n"); | 83 fsBuilder->codeAppend(".r;\n"); |
| 84 fsBuilder->codeAppend("\tfloat distance = " | 84 fsBuilder->codeAppend("\tfloat distance = " |
| 85 SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFie ldThreshold ");"); | 85 SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFie ldThreshold ");"); |
| 86 | 86 |
| 87 // we adjust for the effect of the transformation on the distance by usi ng | |
| 88 // the length of the gradient of the texture coordinates. We use st coor dinates | |
| 89 // to ensure we're mapping 1:1 from texel space to pixel space. | |
| 90 fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision , | 87 fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision , |
| 91 pb->ctxInfo().stand ard())); | 88 pb->ctxInfo().stand ard())); |
| 92 fsBuilder->codeAppendf("vec2 st = %s;\n", st.fsIn()); | 89 fsBuilder->codeAppendf("vec2 st = %s;", st.fsIn()); |
| 93 fsBuilder->codeAppend("\tfloat afwidth;\n"); | 90 fsBuilder->codeAppend("float afwidth;"); |
| 94 if (dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag) { | 91 if (dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag) { |
| 92 // For uniform scale, we adjust for the effect of the transformation on the distance | |
| 93 // by using the length of the gradient of the texture coordinates. W e use st coordinates | |
| 94 // to ensure we're mapping 1:1 from texel space to pixel space. | |
| 95 | |
| 95 // this gives us a smooth step across approximately one fragment | 96 // this gives us a smooth step across approximately one fragment |
| 96 fsBuilder->codeAppend("\tafwidth = abs(" SK_DistanceFieldAAFactor "* dFdx(st.x));\n"); | 97 fsBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*dF dx(st.x));"); |
| 97 } else { | 98 } else { |
| 98 fsBuilder->codeAppend("\tvec2 Jdx = dFdx(st);\n"); | 99 // For general transforms, to determine the amount of correction we multiply a unit |
| 99 fsBuilder->codeAppend("\tvec2 Jdy = dFdy(st);\n"); | 100 // vector pointing along the SDF gradient direction by the Jacobian of the st coords |
| 101 // (which is the inverse transform for this fragment) and take the l ength of the result. | |
|
robertphillips
2015/03/16 18:11:35
Move this computation down to where it is used?
jvanverth1
2015/03/16 18:17:40
Done.
| |
| 102 fsBuilder->codeAppend("vec2 Jdx = dFdx(st);"); | |
| 103 fsBuilder->codeAppend("vec2 Jdy = dFdy(st);"); | |
| 100 | 104 |
| 101 fsBuilder->codeAppend("\tvec2 uv_grad;\n"); | 105 fsBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance), dFdy(di stance));"); |
| 102 if (args.fPB->ctxInfo().caps()->dropsTileOnZeroDivide()) { | 106 if (args.fPB->ctxInfo().caps()->dropsTileOnZeroDivide()) { |
| 103 // this is to compensate for the Adreno, which likes to drop til es on division by 0 | 107 // this is to compensate for the Adreno, which likes to drop til es on division by 0 |
| 104 fsBuilder->codeAppend("\tfloat uv_len2 = dot(uv, uv);\n"); | 108 fsBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad) ;"); |
| 105 fsBuilder->codeAppend("\tif (uv_len2 < 0.0001) {\n"); | 109 fsBuilder->codeAppend("if (dg_len2 < 0.0001) {"); |
| 106 fsBuilder->codeAppend("\t\tuv_grad = vec2(0.7071, 0.7071);\n"); | 110 fsBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);"); |
| 107 fsBuilder->codeAppend("\t} else {\n"); | 111 fsBuilder->codeAppend("} else {"); |
| 108 fsBuilder->codeAppend("\t\tuv_grad = uv*inversesqrt(uv_len2);\n" ); | 112 fsBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2 );"); |
| 109 fsBuilder->codeAppend("\t}\n"); | 113 fsBuilder->codeAppend("}"); |
| 110 } else { | 114 } else { |
| 111 fsBuilder->codeAppend("\tuv_grad = normalize(uv);\n"); | 115 fsBuilder->codeAppend("dist_grad = normalize(dist_grad);\n"); |
| 112 } | 116 } |
| 113 fsBuilder->codeAppend("\tvec2 grad = vec2(uv_grad.x*Jdx.x + uv_grad. y*Jdy.x,\n"); | 117 fsBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_gra d.y*Jdy.x,"); |
| 114 fsBuilder->codeAppend("\t uv_grad.x*Jdx.y + uv_grad. y*Jdy.y);\n"); | 118 fsBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_gra d.y*Jdy.y);"); |
| 115 | 119 |
| 116 // this gives us a smooth step across approximately one fragment | 120 // this gives us a smooth step across approximately one fragment |
| 117 fsBuilder->codeAppend("\tafwidth = " SK_DistanceFieldAAFactor "*leng th(grad);\n"); | 121 fsBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length (grad);"); |
| 118 } | 122 } |
| 119 fsBuilder->codeAppend("\tfloat val = smoothstep(-afwidth, afwidth, dista nce);\n"); | 123 fsBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distanc e);"); |
| 120 | 124 |
| 121 #ifdef SK_GAMMA_APPLY_TO_A8 | 125 #ifdef SK_GAMMA_APPLY_TO_A8 |
| 122 // adjust based on gamma | 126 // adjust based on gamma |
| 123 const char* luminanceUniName = NULL; | 127 const char* luminanceUniName = NULL; |
| 124 // width, height, 1/(3*width) | 128 // width, height, 1/(3*width) |
| 125 fLuminanceUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visib ility, | 129 fLuminanceUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visib ility, |
| 126 kFloat_GrSLType, kDefault_GrSLPreci sion, | 130 kFloat_GrSLType, kDefault_GrSLPreci sion, |
| 127 "Luminance", &luminanceUniName); | 131 "Luminance", &luminanceUniName); |
| 128 | 132 |
| 129 fsBuilder->codeAppendf("\tuv = vec2(val, %s);\n", luminanceUniName); | 133 fsBuilder->codeAppendf("\tuv = vec2(val, %s);\n", luminanceUniName); |
| (...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 361 fsBuilder->codeAppendf("vec2 uv = %s;", v.fsIn()); | 365 fsBuilder->codeAppendf("vec2 uv = %s;", v.fsIn()); |
| 362 | 366 |
| 363 fsBuilder->codeAppend("float texColor = "); | 367 fsBuilder->codeAppend("float texColor = "); |
| 364 fsBuilder->appendTextureLookup(args.fSamplers[0], | 368 fsBuilder->appendTextureLookup(args.fSamplers[0], |
| 365 "uv", | 369 "uv", |
| 366 kVec2f_GrSLType); | 370 kVec2f_GrSLType); |
| 367 fsBuilder->codeAppend(".r;"); | 371 fsBuilder->codeAppend(".r;"); |
| 368 fsBuilder->codeAppend("float distance = " | 372 fsBuilder->codeAppend("float distance = " |
| 369 SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFieldThreshold ");"); | 373 SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFieldThreshold ");"); |
| 370 | 374 |
| 371 // we adjust for the effect of the transformation on the distance by usi ng | |
| 372 // the length of the gradient of the texture coordinates. We use st coor dinates | |
| 373 // to ensure we're mapping 1:1 from texel space to pixel space. | |
| 374 fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision , | 375 fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision , |
| 375 pb->ctxInfo().stand ard())); | 376 pb->ctxInfo().stand ard())); |
| 376 fsBuilder->codeAppendf("vec2 st = uv*%s;", textureSizeUniName); | 377 fsBuilder->codeAppendf("vec2 st = uv*%s;", textureSizeUniName); |
| 377 fsBuilder->codeAppend("float afwidth;"); | 378 fsBuilder->codeAppend("float afwidth;"); |
| 378 if (dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag) { | 379 if (dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag) { |
| 380 // For uniform scale, we adjust for the effect of the transformation on the distance | |
| 381 // by using the length of the gradient of the texture coordinates. W e use st coordinates | |
| 382 // to ensure we're mapping 1:1 from texel space to pixel space. | |
| 383 | |
| 379 // this gives us a smooth step across approximately one fragment | 384 // this gives us a smooth step across approximately one fragment |
| 380 fsBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*dF dx(st.x));"); | 385 fsBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*dF dx(st.x));"); |
| 381 } else { | 386 } else { |
| 387 // For general transforms, to determine the amount of correction we multiply a unit | |
| 388 // vector pointing along the SDF gradient direction by the Jacobian of the st coords | |
| 389 // (which is the inverse transform for this fragment) and take the l ength of the result. | |
|
robertphillips
2015/03/16 18:11:35
here too ?
jvanverth1
2015/03/16 18:17:41
Done.
| |
| 382 fsBuilder->codeAppend("vec2 Jdx = dFdx(st);"); | 390 fsBuilder->codeAppend("vec2 Jdx = dFdx(st);"); |
| 383 fsBuilder->codeAppend("vec2 Jdy = dFdy(st);"); | 391 fsBuilder->codeAppend("vec2 Jdy = dFdy(st);"); |
| 384 | 392 |
| 385 fsBuilder->codeAppend("vec2 uv_grad;"); | 393 fsBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance), dFdy(di stance));"); |
| 386 if (args.fPB->ctxInfo().caps()->dropsTileOnZeroDivide()) { | 394 if (args.fPB->ctxInfo().caps()->dropsTileOnZeroDivide()) { |
| 387 // this is to compensate for the Adreno, which likes to drop til es on division by 0 | 395 // this is to compensate for the Adreno, which likes to drop til es on division by 0 |
| 388 fsBuilder->codeAppend("float uv_len2 = dot(uv, uv);"); | 396 fsBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad) ;"); |
| 389 fsBuilder->codeAppend("if (uv_len2 < 0.0001) {"); | 397 fsBuilder->codeAppend("if (dg_len2 < 0.0001) {"); |
| 390 fsBuilder->codeAppend("uv_grad = vec2(0.7071, 0.7071);"); | 398 fsBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);"); |
| 391 fsBuilder->codeAppend("} else {"); | 399 fsBuilder->codeAppend("} else {"); |
| 392 fsBuilder->codeAppend("uv_grad = uv*inversesqrt(uv_len2);"); | 400 fsBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2 );"); |
| 393 fsBuilder->codeAppend("}"); | 401 fsBuilder->codeAppend("}"); |
| 394 } else { | 402 } else { |
| 395 fsBuilder->codeAppend("uv_grad = normalize(uv);"); | 403 fsBuilder->codeAppend("dist_grad = normalize(dist_grad);"); |
| 396 } | 404 } |
| 397 fsBuilder->codeAppend("vec2 grad = vec2(uv_grad.x*Jdx.x + uv_grad.y* Jdy.x,"); | 405 fsBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_gra d.y*Jdy.x,"); |
| 398 fsBuilder->codeAppend(" uv_grad.x*Jdx.y + uv_grad.y* Jdy.y);"); | 406 fsBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_gra d.y*Jdy.y);"); |
| 399 | 407 |
| 400 // this gives us a smooth step across approximately one fragment | 408 // this gives us a smooth step across approximately one fragment |
| 401 fsBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length (grad);"); | 409 fsBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length (grad);"); |
| 402 } | 410 } |
| 403 fsBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distanc e);"); | 411 fsBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distanc e);"); |
| 404 | 412 |
| 405 fsBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage); | 413 fsBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage); |
| 406 } | 414 } |
| 407 | 415 |
| 408 virtual void setData(const GrGLProgramDataManager& pdman, | 416 virtual void setData(const GrGLProgramDataManager& pdman, |
| (...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 641 // blue is distance to right offset | 649 // blue is distance to right offset |
| 642 fsBuilder->codeAppend("\tuv_adjusted = uv + offset;\n"); | 650 fsBuilder->codeAppend("\tuv_adjusted = uv + offset;\n"); |
| 643 fsBuilder->codeAppend("\ttexColor = "); | 651 fsBuilder->codeAppend("\ttexColor = "); |
| 644 fsBuilder->appendTextureLookup(args.fSamplers[0], "uv_adjusted", kVec2f_ GrSLType); | 652 fsBuilder->appendTextureLookup(args.fSamplers[0], "uv_adjusted", kVec2f_ GrSLType); |
| 645 fsBuilder->codeAppend(";\n"); | 653 fsBuilder->codeAppend(";\n"); |
| 646 fsBuilder->codeAppend("\tdistance.z = texColor.r;\n"); | 654 fsBuilder->codeAppend("\tdistance.z = texColor.r;\n"); |
| 647 | 655 |
| 648 fsBuilder->codeAppend("\tdistance = " | 656 fsBuilder->codeAppend("\tdistance = " |
| 649 "vec3(" SK_DistanceFieldMultiplier ")*(distance - vec3(" SK_DistanceF ieldThreshold"));"); | 657 "vec3(" SK_DistanceFieldMultiplier ")*(distance - vec3(" SK_DistanceF ieldThreshold"));"); |
| 650 | 658 |
| 651 // we adjust for the effect of the transformation on the distance by usi ng | |
| 652 // the length of the gradient of the texture coordinates. We use st coor dinates | |
| 653 // to ensure we're mapping 1:1 from texel space to pixel space. | |
| 654 | |
| 655 // To be strictly correct, we should compute the anti-aliasing factor se parately | 659 // To be strictly correct, we should compute the anti-aliasing factor se parately |
| 656 // for each color component. However, this is only important when using perspective | 660 // for each color component. However, this is only important when using perspective |
| 657 // transformations, and even then using a single factor seems like a rea sonable | 661 // transformations, and even then using a single factor seems like a rea sonable |
| 658 // trade-off between quality and speed. | 662 // trade-off between quality and speed. |
| 659 fsBuilder->codeAppend("\tfloat afwidth;\n"); | 663 fsBuilder->codeAppend("float afwidth;"); |
| 660 if (isUniformScale) { | 664 if (isUniformScale) { |
| 665 // For uniform scale, we adjust for the effect of the transformation on the distance | |
| 666 // by using the length of the gradient of the texture coordinates. W e use st coordinates | |
| 667 // to ensure we're mapping 1:1 from texel space to pixel space. | |
| 668 | |
| 661 // this gives us a smooth step across approximately one fragment | 669 // this gives us a smooth step across approximately one fragment |
| 662 fsBuilder->codeAppend("\tafwidth = abs(" SK_DistanceFieldAAFactor "* dx);\n"); | 670 fsBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*dx );"); |
| 663 } else { | 671 } else { |
| 664 fsBuilder->codeAppend("\tvec2 uv_grad;\n"); | 672 // For general transforms, to determine the amount of correction we multiply a unit |
| 673 // vector pointing along the SDF gradient direction by the Jacobian of the st coords | |
| 674 // (which is the inverse transform for this fragment) and take the l ength of the result. | |
| 675 fsBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance.r), dFdy( distance.r));"); | |
| 665 if (args.fPB->ctxInfo().caps()->dropsTileOnZeroDivide()) { | 676 if (args.fPB->ctxInfo().caps()->dropsTileOnZeroDivide()) { |
| 666 // this is to compensate for the Adreno, which likes to drop til es on division by 0 | 677 // this is to compensate for the Adreno, which likes to drop til es on division by 0 |
| 667 fsBuilder->codeAppend("\tfloat uv_len2 = dot(uv, uv);\n"); | 678 fsBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad) ;"); |
| 668 fsBuilder->codeAppend("\tif (uv_len2 < 0.0001) {\n"); | 679 fsBuilder->codeAppend("if (dg_len2 < 0.0001) {"); |
| 669 fsBuilder->codeAppend("\t\tuv_grad = vec2(0.7071, 0.7071);\n"); | 680 fsBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);"); |
| 670 fsBuilder->codeAppend("\t} else {\n"); | 681 fsBuilder->codeAppend("} else {"); |
| 671 fsBuilder->codeAppend("\t\tuv_grad = uv*inversesqrt(uv_len2);\n" ); | 682 fsBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2 );"); |
| 672 fsBuilder->codeAppend("\t}\n"); | 683 fsBuilder->codeAppend("}"); |
| 673 } else { | 684 } else { |
| 674 fsBuilder->codeAppend("\tuv_grad = normalize(uv);\n"); | 685 fsBuilder->codeAppend("dist_grad = normalize(dist_grad);\n"); |
| 675 } | 686 } |
| 676 fsBuilder->codeAppend("\tvec2 grad = vec2(uv_grad.x*Jdx.x + uv_grad. y*Jdy.x,\n"); | 687 fsBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_gra d.y*Jdy.x,"); |
| 677 fsBuilder->codeAppend("\t uv_grad.x*Jdx.y + uv_grad. y*Jdy.y);\n"); | 688 fsBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_gra d.y*Jdy.y);"); |
| 678 | 689 |
| 679 // this gives us a smooth step across approximately one fragment | 690 // this gives us a smooth step across approximately one fragment |
| 680 fsBuilder->codeAppend("\tafwidth = " SK_DistanceFieldAAFactor "*leng th(grad);\n"); | 691 fsBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length (grad);"); |
| 681 } | 692 } |
| 682 | 693 |
| 683 fsBuilder->codeAppend("\tvec4 val = vec4(smoothstep(vec3(-afwidth), vec3 (afwidth), distance), 1.0);\n"); | 694 fsBuilder->codeAppend("vec4 val = vec4(smoothstep(vec3(-afwidth), vec3(a fwidth), distance), 1.0);"); |
| 684 | 695 |
| 685 // adjust based on gamma | 696 // adjust based on gamma |
| 686 const char* textColorUniName = NULL; | 697 const char* textColorUniName = NULL; |
| 687 fTextColorUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visib ility, | 698 fTextColorUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visib ility, |
| 688 kVec3f_GrSLType, kDefault_GrSLPreci sion, | 699 kVec3f_GrSLType, kDefault_GrSLPreci sion, |
| 689 "TextColor", &textColorUniName); | 700 "TextColor", &textColorUniName); |
| 690 | 701 |
| 691 fsBuilder->codeAppendf("\tuv = vec2(val.x, %s.x);\n", textColorUniName); | 702 fsBuilder->codeAppendf("\tuv = vec2(val.x, %s.x);\n", textColorUniName); |
| 692 fsBuilder->codeAppend("float gammaColor = "); | 703 fsBuilder->codeAppend("float gammaColor = "); |
| 693 fsBuilder->appendTextureLookup(args.fSamplers[1], "uv", kVec2f_GrSLType) ; | 704 fsBuilder->appendTextureLookup(args.fSamplers[1], "uv", kVec2f_GrSLType) ; |
| (...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 856 uint32_t flags = kUseLCD_DistanceFieldEffectFlag; | 867 uint32_t flags = kUseLCD_DistanceFieldEffectFlag; |
| 857 flags |= random->nextBool() ? kUniformScale_DistanceFieldEffectMask : 0; | 868 flags |= random->nextBool() ? kUniformScale_DistanceFieldEffectMask : 0; |
| 858 flags |= random->nextBool() ? kBGR_DistanceFieldEffectFlag : 0; | 869 flags |= random->nextBool() ? kBGR_DistanceFieldEffectFlag : 0; |
| 859 return GrDistanceFieldLCDTextureEffect::Create(GrRandomColor(random), | 870 return GrDistanceFieldLCDTextureEffect::Create(GrRandomColor(random), |
| 860 GrProcessorUnitTest::TestMatr ix(random), | 871 GrProcessorUnitTest::TestMatr ix(random), |
| 861 textures[texIdx], params, | 872 textures[texIdx], params, |
| 862 textures[texIdx2], params2, | 873 textures[texIdx2], params2, |
| 863 textColor, | 874 textColor, |
| 864 flags); | 875 flags); |
| 865 } | 876 } |
| OLD | NEW |