| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 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 "effects/GrCustomXfermode.h" | 8 #include "effects/GrCustomXfermode.h" |
| 9 #include "effects/GrCustomXfermodePriv.h" | 9 #include "effects/GrCustomXfermodePriv.h" |
| 10 | 10 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 22 #include "gl/builders/GrGLProgramBuilder.h" | 22 #include "gl/builders/GrGLProgramBuilder.h" |
| 23 | 23 |
| 24 bool GrCustomXfermode::IsSupportedMode(SkXfermode::Mode mode) { | 24 bool GrCustomXfermode::IsSupportedMode(SkXfermode::Mode mode) { |
| 25 return mode > SkXfermode::kLastCoeffMode && mode <= SkXfermode::kLastMode; | 25 return mode > SkXfermode::kLastCoeffMode && mode <= SkXfermode::kLastMode; |
| 26 } | 26 } |
| 27 | 27 |
| 28 /////////////////////////////////////////////////////////////////////////////// | 28 /////////////////////////////////////////////////////////////////////////////// |
| 29 // Static helpers | 29 // Static helpers |
| 30 /////////////////////////////////////////////////////////////////////////////// | 30 /////////////////////////////////////////////////////////////////////////////// |
| 31 | 31 |
| 32 static void hard_light(GrGLFPFragmentBuilder* fsBuilder, | 32 static void hard_light(GrGLFragmentBuilder* fsBuilder, |
| 33 const char* final, | 33 const char* final, |
| 34 const char* src, | 34 const char* src, |
| 35 const char* dst) { | 35 const char* dst) { |
| 36 static const char kComponents[] = {'r', 'g', 'b'}; | 36 static const char kComponents[] = {'r', 'g', 'b'}; |
| 37 for (size_t i = 0; i < SK_ARRAY_COUNT(kComponents); ++i) { | 37 for (size_t i = 0; i < SK_ARRAY_COUNT(kComponents); ++i) { |
| 38 char component = kComponents[i]; | 38 char component = kComponents[i]; |
| 39 fsBuilder->codeAppendf("if (2.0 * %s.%c <= %s.a) {", src, component, src
); | 39 fsBuilder->codeAppendf("if (2.0 * %s.%c <= %s.a) {", src, component, src
); |
| 40 fsBuilder->codeAppendf("%s.%c = 2.0 * %s.%c * %s.%c;", | 40 fsBuilder->codeAppendf("%s.%c = 2.0 * %s.%c * %s.%c;", |
| 41 final, component, src, component, dst, component)
; | 41 final, component, src, component, dst, component)
; |
| 42 fsBuilder->codeAppend("} else {"); | 42 fsBuilder->codeAppend("} else {"); |
| 43 fsBuilder->codeAppendf("%s.%c = %s.a * %s.a - 2.0 * (%s.a - %s.%c) * (%s
.a - %s.%c);", | 43 fsBuilder->codeAppendf("%s.%c = %s.a * %s.a - 2.0 * (%s.a - %s.%c) * (%s
.a - %s.%c);", |
| 44 final, component, src, dst, dst, dst, component,
src, src, | 44 final, component, src, dst, dst, dst, component,
src, src, |
| 45 component); | 45 component); |
| 46 fsBuilder->codeAppend("}"); | 46 fsBuilder->codeAppend("}"); |
| 47 } | 47 } |
| 48 fsBuilder->codeAppendf("%s.rgb += %s.rgb * (1.0 - %s.a) + %s.rgb * (1.0 - %s
.a);", | 48 fsBuilder->codeAppendf("%s.rgb += %s.rgb * (1.0 - %s.a) + %s.rgb * (1.0 - %s
.a);", |
| 49 final, src, dst, dst, src); | 49 final, src, dst, dst, src); |
| 50 } | 50 } |
| 51 | 51 |
| 52 // Does one component of color-dodge | 52 // Does one component of color-dodge |
| 53 static void color_dodge_component(GrGLFPFragmentBuilder* fsBuilder, | 53 static void color_dodge_component(GrGLFragmentBuilder* fsBuilder, |
| 54 const char* final, | 54 const char* final, |
| 55 const char* src, | 55 const char* src, |
| 56 const char* dst, | 56 const char* dst, |
| 57 const char component) { | 57 const char component) { |
| 58 fsBuilder->codeAppendf("if (0.0 == %s.%c) {", dst, component); | 58 fsBuilder->codeAppendf("if (0.0 == %s.%c) {", dst, component); |
| 59 fsBuilder->codeAppendf("%s.%c = %s.%c * (1.0 - %s.a);", | 59 fsBuilder->codeAppendf("%s.%c = %s.%c * (1.0 - %s.a);", |
| 60 final, component, src, component, dst); | 60 final, component, src, component, dst); |
| 61 fsBuilder->codeAppend("} else {"); | 61 fsBuilder->codeAppend("} else {"); |
| 62 fsBuilder->codeAppendf("float d = %s.a - %s.%c;", src, src, component); | 62 fsBuilder->codeAppendf("float d = %s.a - %s.%c;", src, src, component); |
| 63 fsBuilder->codeAppend("if (0.0 == d) {"); | 63 fsBuilder->codeAppend("if (0.0 == d) {"); |
| 64 fsBuilder->codeAppendf("%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c *
(1.0 - %s.a);", | 64 fsBuilder->codeAppendf("%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c *
(1.0 - %s.a);", |
| 65 final, component, src, dst, src, component, dst, dst,
component, | 65 final, component, src, dst, src, component, dst, dst,
component, |
| 66 src); | 66 src); |
| 67 fsBuilder->codeAppend("} else {"); | 67 fsBuilder->codeAppend("} else {"); |
| 68 fsBuilder->codeAppendf("d = min(%s.a, %s.%c * %s.a / d);", | 68 fsBuilder->codeAppendf("d = min(%s.a, %s.%c * %s.a / d);", |
| 69 dst, dst, component, src); | 69 dst, dst, component, src); |
| 70 fsBuilder->codeAppendf("%s.%c = d * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1
.0 - %s.a);", | 70 fsBuilder->codeAppendf("%s.%c = d * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1
.0 - %s.a);", |
| 71 final, component, src, src, component, dst, dst, comp
onent, src); | 71 final, component, src, src, component, dst, dst, comp
onent, src); |
| 72 fsBuilder->codeAppend("}"); | 72 fsBuilder->codeAppend("}"); |
| 73 fsBuilder->codeAppend("}"); | 73 fsBuilder->codeAppend("}"); |
| 74 } | 74 } |
| 75 | 75 |
| 76 // Does one component of color-burn | 76 // Does one component of color-burn |
| 77 static void color_burn_component(GrGLFPFragmentBuilder* fsBuilder, | 77 static void color_burn_component(GrGLFragmentBuilder* fsBuilder, |
| 78 const char* final, | 78 const char* final, |
| 79 const char* src, | 79 const char* src, |
| 80 const char* dst, | 80 const char* dst, |
| 81 const char component) { | 81 const char component) { |
| 82 fsBuilder->codeAppendf("if (%s.a == %s.%c) {", dst, dst, component); | 82 fsBuilder->codeAppendf("if (%s.a == %s.%c) {", dst, dst, component); |
| 83 fsBuilder->codeAppendf("%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c *
(1.0 - %s.a);", | 83 fsBuilder->codeAppendf("%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c *
(1.0 - %s.a);", |
| 84 final, component, src, dst, src, component, dst, dst,
component, | 84 final, component, src, dst, src, component, dst, dst,
component, |
| 85 src); | 85 src); |
| 86 fsBuilder->codeAppendf("} else if (0.0 == %s.%c) {", src, component); | 86 fsBuilder->codeAppendf("} else if (0.0 == %s.%c) {", src, component); |
| 87 fsBuilder->codeAppendf("%s.%c = %s.%c * (1.0 - %s.a);", | 87 fsBuilder->codeAppendf("%s.%c = %s.%c * (1.0 - %s.a);", |
| 88 final, component, dst, component, src); | 88 final, component, dst, component, src); |
| 89 fsBuilder->codeAppend("} else {"); | 89 fsBuilder->codeAppend("} else {"); |
| 90 fsBuilder->codeAppendf("float d = max(0.0, %s.a - (%s.a - %s.%c) * %s.a / %s
.%c);", | 90 fsBuilder->codeAppendf("float d = max(0.0, %s.a - (%s.a - %s.%c) * %s.a / %s
.%c);", |
| 91 dst, dst, dst, component, src, src, component); | 91 dst, dst, dst, component, src, src, component); |
| 92 fsBuilder->codeAppendf("%s.%c = %s.a * d + %s.%c * (1.0 - %s.a) + %s.%c * (1
.0 - %s.a);", | 92 fsBuilder->codeAppendf("%s.%c = %s.a * d + %s.%c * (1.0 - %s.a) + %s.%c * (1
.0 - %s.a);", |
| 93 final, component, src, src, component, dst, dst, comp
onent, src); | 93 final, component, src, src, component, dst, dst, comp
onent, src); |
| 94 fsBuilder->codeAppend("}"); | 94 fsBuilder->codeAppend("}"); |
| 95 } | 95 } |
| 96 | 96 |
| 97 // Does one component of soft-light. Caller should have already checked that dst
alpha > 0. | 97 // Does one component of soft-light. Caller should have already checked that dst
alpha > 0. |
| 98 static void soft_light_component_pos_dst_alpha(GrGLFPFragmentBuilder* fsBuilder, | 98 static void soft_light_component_pos_dst_alpha(GrGLFragmentBuilder* fsBuilder, |
| 99 const char* final, | 99 const char* final, |
| 100 const char* src, | 100 const char* src, |
| 101 const char* dst, | 101 const char* dst, |
| 102 const char component) { | 102 const char component) { |
| 103 // if (2S < Sa) | 103 // if (2S < Sa) |
| 104 fsBuilder->codeAppendf("if (2.0 * %s.%c <= %s.a) {", src, component, src); | 104 fsBuilder->codeAppendf("if (2.0 * %s.%c <= %s.a) {", src, component, src); |
| 105 // (D^2 (Sa-2 S))/Da+(1-Da) S+D (-Sa+2 S+1) | 105 // (D^2 (Sa-2 S))/Da+(1-Da) S+D (-Sa+2 S+1) |
| 106 fsBuilder->codeAppendf("%s.%c = (%s.%c*%s.%c*(%s.a - 2.0*%s.%c)) / %s.a +" | 106 fsBuilder->codeAppendf("%s.%c = (%s.%c*%s.%c*(%s.a - 2.0*%s.%c)) / %s.a +" |
| 107 "(1.0 - %s.a) * %s.%c + %s.%c*(-%s.a + 2.0*%s
.%c + 1.0);", | 107 "(1.0 - %s.a) * %s.%c + %s.%c*(-%s.a + 2.0*%s
.%c + 1.0);", |
| 108 final, component, dst, component, dst, component, src
, src, | 108 final, component, dst, component, dst, component, src
, src, |
| (...skipping 22 matching lines...) Expand all Loading... |
| 131 final, component, dst, dst, component, src, src, comp
onent, dst, | 131 final, component, dst, dst, component, src, src, comp
onent, dst, |
| 132 src, component, dst, component, src, src, component,
src, | 132 src, component, dst, component, src, src, component,
src, |
| 133 component); | 133 component); |
| 134 fsBuilder->codeAppendf("}"); | 134 fsBuilder->codeAppendf("}"); |
| 135 } | 135 } |
| 136 | 136 |
| 137 // Adds a function that takes two colors and an alpha as input. It produces a co
lor with the | 137 // Adds a function that takes two colors and an alpha as input. It produces a co
lor with the |
| 138 // hue and saturation of the first color, the luminosity of the second color, an
d the input | 138 // hue and saturation of the first color, the luminosity of the second color, an
d the input |
| 139 // alpha. It has this signature: | 139 // alpha. It has this signature: |
| 140 // vec3 set_luminance(vec3 hueSatColor, float alpha, vec3 lumColor). | 140 // vec3 set_luminance(vec3 hueSatColor, float alpha, vec3 lumColor). |
| 141 static void add_lum_function(GrGLFPFragmentBuilder* fsBuilder, SkString* setLumF
unction) { | 141 static void add_lum_function(GrGLFragmentBuilder* fsBuilder, SkString* setLumFun
ction) { |
| 142 // Emit a helper that gets the luminance of a color. | 142 // Emit a helper that gets the luminance of a color. |
| 143 SkString getFunction; | 143 SkString getFunction; |
| 144 GrGLShaderVar getLumArgs[] = { | 144 GrGLShaderVar getLumArgs[] = { |
| 145 GrGLShaderVar("color", kVec3f_GrSLType), | 145 GrGLShaderVar("color", kVec3f_GrSLType), |
| 146 }; | 146 }; |
| 147 SkString getLumBody("return dot(vec3(0.3, 0.59, 0.11), color);"); | 147 SkString getLumBody("return dot(vec3(0.3, 0.59, 0.11), color);"); |
| 148 fsBuilder->emitFunction(kFloat_GrSLType, | 148 fsBuilder->emitFunction(kFloat_GrSLType, |
| 149 "luminance", | 149 "luminance", |
| 150 SK_ARRAY_COUNT(getLumArgs), getLumArgs, | 150 SK_ARRAY_COUNT(getLumArgs), getLumArgs, |
| 151 getLumBody.c_str(), | 151 getLumBody.c_str(), |
| (...skipping 24 matching lines...) Expand all Loading... |
| 176 fsBuilder->emitFunction(kVec3f_GrSLType, | 176 fsBuilder->emitFunction(kVec3f_GrSLType, |
| 177 "set_luminance", | 177 "set_luminance", |
| 178 SK_ARRAY_COUNT(setLumArgs), setLumArgs, | 178 SK_ARRAY_COUNT(setLumArgs), setLumArgs, |
| 179 setLumBody.c_str(), | 179 setLumBody.c_str(), |
| 180 setLumFunction); | 180 setLumFunction); |
| 181 } | 181 } |
| 182 | 182 |
| 183 // Adds a function that creates a color with the hue and luminosity of one input
color and | 183 // Adds a function that creates a color with the hue and luminosity of one input
color and |
| 184 // the saturation of another color. It will have this signature: | 184 // the saturation of another color. It will have this signature: |
| 185 // float set_saturation(vec3 hueLumColor, vec3 satColor) | 185 // float set_saturation(vec3 hueLumColor, vec3 satColor) |
| 186 static void add_sat_function(GrGLFPFragmentBuilder* fsBuilder, SkString* setSatF
unction) { | 186 static void add_sat_function(GrGLFragmentBuilder* fsBuilder, SkString* setSatFun
ction) { |
| 187 // Emit a helper that gets the saturation of a color | 187 // Emit a helper that gets the saturation of a color |
| 188 SkString getFunction; | 188 SkString getFunction; |
| 189 GrGLShaderVar getSatArgs[] = { GrGLShaderVar("color", kVec3f_GrSLType) }; | 189 GrGLShaderVar getSatArgs[] = { GrGLShaderVar("color", kVec3f_GrSLType) }; |
| 190 SkString getSatBody; | 190 SkString getSatBody; |
| 191 getSatBody.printf("return max(max(color.r, color.g), color.b) - " | 191 getSatBody.printf("return max(max(color.r, color.g), color.b) - " |
| 192 "min(min(color.r, color.g), color.b);"); | 192 "min(min(color.r, color.g), color.b);"); |
| 193 fsBuilder->emitFunction(kFloat_GrSLType, | 193 fsBuilder->emitFunction(kFloat_GrSLType, |
| 194 "saturation", | 194 "saturation", |
| 195 SK_ARRAY_COUNT(getSatArgs), getSatArgs, | 195 SK_ARRAY_COUNT(getSatArgs), getSatArgs, |
| 196 getSatBody.c_str(), | 196 getSatBody.c_str(), |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 249 helpFunc, helpFunc); | 249 helpFunc, helpFunc); |
| 250 fsBuilder->emitFunction(kVec3f_GrSLType, | 250 fsBuilder->emitFunction(kVec3f_GrSLType, |
| 251 "set_saturation", | 251 "set_saturation", |
| 252 SK_ARRAY_COUNT(setSatArgs), setSatArgs, | 252 SK_ARRAY_COUNT(setSatArgs), setSatArgs, |
| 253 setSatBody.c_str(), | 253 setSatBody.c_str(), |
| 254 setSatFunction); | 254 setSatFunction); |
| 255 | 255 |
| 256 } | 256 } |
| 257 | 257 |
| 258 static void emit_custom_xfermode_code(SkXfermode::Mode mode, | 258 static void emit_custom_xfermode_code(SkXfermode::Mode mode, |
| 259 GrGLFPFragmentBuilder* fsBuilder, | 259 GrGLFragmentBuilder* fsBuilder, |
| 260 const char* outputColor, | 260 const char* outputColor, |
| 261 const char* inputColor, | 261 const char* inputColor, |
| 262 const char* dstColor) { | 262 const char* dstColor) { |
| 263 // We don't try to optimize for this case at all | 263 // We don't try to optimize for this case at all |
| 264 if (NULL == inputColor) { | 264 if (NULL == inputColor) { |
| 265 fsBuilder->codeAppendf("const vec4 ones = vec4(1);"); | 265 fsBuilder->codeAppendf("const vec4 ones = vec4(1);"); |
| 266 inputColor = "ones"; | 266 inputColor = "ones"; |
| 267 } | 267 } |
| 268 fsBuilder->codeAppendf("// SkXfermode::Mode: %s\n", SkXfermode::ModeName(mod
e)); | 268 fsBuilder->codeAppendf("// SkXfermode::Mode: %s\n", SkXfermode::ModeName(mod
e)); |
| 269 | 269 |
| (...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 409 GLCustomXferFP(const GrFragmentProcessor&) {} | 409 GLCustomXferFP(const GrFragmentProcessor&) {} |
| 410 ~GLCustomXferFP() override {}; | 410 ~GLCustomXferFP() override {}; |
| 411 | 411 |
| 412 void emitCode(GrGLFPBuilder* builder, | 412 void emitCode(GrGLFPBuilder* builder, |
| 413 const GrFragmentProcessor& fp, | 413 const GrFragmentProcessor& fp, |
| 414 const char* outputColor, | 414 const char* outputColor, |
| 415 const char* inputColor, | 415 const char* inputColor, |
| 416 const TransformedCoordsArray& coords, | 416 const TransformedCoordsArray& coords, |
| 417 const TextureSamplerArray& samplers) override { | 417 const TextureSamplerArray& samplers) override { |
| 418 SkXfermode::Mode mode = fp.cast<GrCustomXferFP>().mode(); | 418 SkXfermode::Mode mode = fp.cast<GrCustomXferFP>().mode(); |
| 419 GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder(); | 419 GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder(); |
| 420 const char* dstColor = "bgColor"; | 420 const char* dstColor = "bgColor"; |
| 421 fsBuilder->codeAppendf("vec4 %s = ", dstColor); | 421 fsBuilder->codeAppendf("vec4 %s = ", dstColor); |
| 422 fsBuilder->appendTextureLookup(samplers[0], coords[0].c_str(), coords[0]
.getType()); | 422 fsBuilder->appendTextureLookup(samplers[0], coords[0].c_str(), coords[0]
.getType()); |
| 423 fsBuilder->codeAppendf(";"); | 423 fsBuilder->codeAppendf(";"); |
| 424 | 424 |
| 425 emit_custom_xfermode_code(mode, fsBuilder, outputColor, inputColor, dstC
olor); | 425 emit_custom_xfermode_code(mode, fsBuilder, outputColor, inputColor, dstC
olor); |
| 426 } | 426 } |
| 427 | 427 |
| 428 void setData(const GrGLProgramDataManager&, const GrProcessor&) override {} | 428 void setData(const GrGLProgramDataManager&, const GrProcessor&) override {} |
| 429 | 429 |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 543 static void GenKey(const GrXferProcessor& proc, const GrGLSLCaps&, GrProcess
orKeyBuilder* b) { | 543 static void GenKey(const GrXferProcessor& proc, const GrGLSLCaps&, GrProcess
orKeyBuilder* b) { |
| 544 uint32_t key = proc.numTextures(); | 544 uint32_t key = proc.numTextures(); |
| 545 SkASSERT(key <= 1); | 545 SkASSERT(key <= 1); |
| 546 key |= proc.cast<CustomXP>().mode() << 1; | 546 key |= proc.cast<CustomXP>().mode() << 1; |
| 547 b->add32(key); | 547 b->add32(key); |
| 548 } | 548 } |
| 549 | 549 |
| 550 private: | 550 private: |
| 551 void onEmitCode(const EmitArgs& args) override { | 551 void onEmitCode(const EmitArgs& args) override { |
| 552 SkXfermode::Mode mode = args.fXP.cast<CustomXP>().mode(); | 552 SkXfermode::Mode mode = args.fXP.cast<CustomXP>().mode(); |
| 553 GrGLFPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); | 553 GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); |
| 554 const char* dstColor = fsBuilder->dstColor(); | 554 const char* dstColor = fsBuilder->dstColor(); |
| 555 | 555 |
| 556 emit_custom_xfermode_code(mode, fsBuilder, args.fOutputPrimary, args.fIn
putColor, dstColor); | 556 emit_custom_xfermode_code(mode, fsBuilder, args.fOutputPrimary, args.fIn
putColor, dstColor); |
| 557 | 557 |
| 558 fsBuilder->codeAppendf("%s = %s * %s + (vec4(1.0) - %s) * %s;", | 558 fsBuilder->codeAppendf("%s = %s * %s + (vec4(1.0) - %s) * %s;", |
| 559 args.fOutputPrimary, args.fOutputPrimary, args.fI
nputCoverage, | 559 args.fOutputPrimary, args.fOutputPrimary, args.fI
nputCoverage, |
| 560 args.fInputCoverage, dstColor); | 560 args.fInputCoverage, dstColor); |
| 561 } | 561 } |
| 562 | 562 |
| 563 void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) overri
de {} | 563 void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) overri
de {} |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 620 GR_DEFINE_XP_FACTORY_TEST(GrCustomXPFactory); | 620 GR_DEFINE_XP_FACTORY_TEST(GrCustomXPFactory); |
| 621 GrXPFactory* GrCustomXPFactory::TestCreate(SkRandom* rand, | 621 GrXPFactory* GrCustomXPFactory::TestCreate(SkRandom* rand, |
| 622 GrContext*, | 622 GrContext*, |
| 623 const GrDrawTargetCaps&, | 623 const GrDrawTargetCaps&, |
| 624 GrTexture*[]) { | 624 GrTexture*[]) { |
| 625 int mode = rand->nextRangeU(SkXfermode::kLastCoeffMode + 1, SkXfermode::kLas
tSeparableMode); | 625 int mode = rand->nextRangeU(SkXfermode::kLastCoeffMode + 1, SkXfermode::kLas
tSeparableMode); |
| 626 | 626 |
| 627 return SkNEW_ARGS(GrCustomXPFactory, (static_cast<SkXfermode::Mode>(mode))); | 627 return SkNEW_ARGS(GrCustomXPFactory, (static_cast<SkXfermode::Mode>(mode))); |
| 628 } | 628 } |
| 629 | 629 |
| OLD | NEW |