OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2012 Google Inc. | 2 * Copyright 2012 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 "gl/GrGLShaderBuilder.h" | 8 #include "gl/GrGLShaderBuilder.h" |
9 #include "gl/GrGLProgram.h" | 9 #include "gl/GrGLProgram.h" |
10 #include "gl/GrGLUniformHandle.h" | 10 #include "gl/GrGLUniformHandle.h" |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
131 const char* dstCopyTopLeftName; | 131 const char* dstCopyTopLeftName; |
132 const char* dstCopyCoordScaleName; | 132 const char* dstCopyCoordScaleName; |
133 uint32_t configMask; | 133 uint32_t configMask; |
134 if (SkToBool(kUseAlphaConfig_DstReadKeyBit & header.fDstReadKey)) { | 134 if (SkToBool(kUseAlphaConfig_DstReadKeyBit & header.fDstReadKey)) { |
135 configMask = kA_GrColorComponentFlag; | 135 configMask = kA_GrColorComponentFlag; |
136 } else { | 136 } else { |
137 configMask = kRGBA_GrColorComponentFlags; | 137 configMask = kRGBA_GrColorComponentFlags; |
138 } | 138 } |
139 fDstCopySampler.init(this, configMask, "rgba", 0); | 139 fDstCopySampler.init(this, configMask, "rgba", 0); |
140 | 140 |
141 fDstCopyTopLeftUniform = this->addUniform(kFragment_ShaderType, | 141 fDstCopyTopLeftUniform = this->addUniform(kFragment_Visibility, |
142 kVec2f_GrSLType, | 142 kVec2f_GrSLType, |
143 "DstCopyUpperLeft", | 143 "DstCopyUpperLeft", |
144 &dstCopyTopLeftName); | 144 &dstCopyTopLeftName); |
145 fDstCopyScaleUniform = this->addUniform(kFragment_ShaderType, | 145 fDstCopyScaleUniform = this->addUniform(kFragment_Visibility, |
146 kVec2f_GrSLType, | 146 kVec2f_GrSLType, |
147 "DstCopyCoordScale", | 147 "DstCopyCoordScale", |
148 &dstCopyCoordScaleName); | 148 &dstCopyCoordScaleName); |
149 const char* fragPos = this->fragmentPosition(); | 149 const char* fragPos = this->fragmentPosition(); |
150 this->fsCodeAppend("\t// Read color from copy of the destination.\n"); | 150 this->fsCodeAppend("\t// Read color from copy of the destination.\n"); |
151 this->fsCodeAppendf("\tvec2 _dstTexCoord = (%s.xy - %s) * %s;\n", | 151 this->fsCodeAppendf("\tvec2 _dstTexCoord = (%s.xy - %s) * %s;\n", |
152 fragPos, dstCopyTopLeftName, dstCopyCoordScaleName); | 152 fragPos, dstCopyTopLeftName, dstCopyCoordScaleName); |
153 if (!topDown) { | 153 if (!topDown) { |
154 this->fsCodeAppend("\t_dstTexCoord.y = 1.0 - _dstTexCoord.y;\n"); | 154 this->fsCodeAppend("\t_dstTexCoord.y = 1.0 - _dstTexCoord.y;\n"); |
155 } | 155 } |
156 this->fsCodeAppendf("\tvec4 %s = ", kDstCopyColorName); | 156 this->fsCodeAppendf("\tvec4 %s = ", kDstCopyColorName); |
157 this->appendTextureLookup(kFragment_ShaderType, fDstCopySampler, "_dstTe
xCoord"); | 157 this->fsAppendTextureLookup(fDstCopySampler, "_dstTexCoord"); |
158 this->fsCodeAppend(";\n\n"); | 158 this->fsCodeAppend(";\n\n"); |
159 } | 159 } |
160 } | 160 } |
161 | 161 |
162 bool GrGLShaderBuilder::enableFeature(GLSLFeature feature) { | 162 bool GrGLShaderBuilder::enableFeature(GLSLFeature feature) { |
163 switch (feature) { | 163 switch (feature) { |
164 case kStandardDerivatives_GLSLFeature: | 164 case kStandardDerivatives_GLSLFeature: |
165 if (!fCtxInfo.caps()->shaderDerivativeSupport()) { | 165 if (!fCtxInfo.caps()->shaderDerivativeSupport()) { |
166 return false; | 166 return false; |
167 } | 167 } |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
246 } else if (GrGLCaps::kNV_FBFetchType == fetchType) { | 246 } else if (GrGLCaps::kNV_FBFetchType == fetchType) { |
247 SkAssertResult(this->enablePrivateFeature(kNVShaderFramebufferFetch_GLSL
PrivateFeature)); | 247 SkAssertResult(this->enablePrivateFeature(kNVShaderFramebufferFetch_GLSL
PrivateFeature)); |
248 return kFBFetchColorName; | 248 return kFBFetchColorName; |
249 } else if (fDstCopySampler.isInitialized()) { | 249 } else if (fDstCopySampler.isInitialized()) { |
250 return kDstCopyColorName; | 250 return kDstCopyColorName; |
251 } else { | 251 } else { |
252 return ""; | 252 return ""; |
253 } | 253 } |
254 } | 254 } |
255 | 255 |
256 void GrGLShaderBuilder::codeAppendf(ShaderType type, const char format[], va_lis
t args) { | |
257 SkString* string = NULL; | |
258 switch (type) { | |
259 case kVertex_ShaderType: | |
260 string = &fVSCode; | |
261 break; | |
262 case kGeometry_ShaderType: | |
263 string = &fGSCode; | |
264 break; | |
265 case kFragment_ShaderType: | |
266 string = &fFSCode; | |
267 break; | |
268 default: | |
269 GrCrash("Invalid shader type"); | |
270 } | |
271 string->appendf(format, args); | |
272 } | |
273 | |
274 void GrGLShaderBuilder::codeAppend(ShaderType type, const char* str) { | |
275 SkString* string = NULL; | |
276 switch (type) { | |
277 case kVertex_ShaderType: | |
278 string = &fVSCode; | |
279 break; | |
280 case kGeometry_ShaderType: | |
281 string = &fGSCode; | |
282 break; | |
283 case kFragment_ShaderType: | |
284 string = &fFSCode; | |
285 break; | |
286 default: | |
287 GrCrash("Invalid shader type"); | |
288 } | |
289 string->append(str); | |
290 } | |
291 | |
292 void GrGLShaderBuilder::appendTextureLookup(SkString* out, | 256 void GrGLShaderBuilder::appendTextureLookup(SkString* out, |
293 const GrGLShaderBuilder::TextureSamp
ler& sampler, | 257 const GrGLShaderBuilder::TextureSamp
ler& sampler, |
294 const char* coordName, | 258 const char* coordName, |
295 GrSLType varyingType) const { | 259 GrSLType varyingType) const { |
296 SkASSERT(NULL != coordName); | 260 SkASSERT(NULL != coordName); |
297 | 261 |
298 out->appendf("%s(%s, %s)", | 262 out->appendf("%s(%s, %s)", |
299 sample_function_name(varyingType, fCtxInfo.glslGeneration()), | 263 sample_function_name(varyingType, fCtxInfo.glslGeneration()), |
300 this->getUniformCStr(sampler.fSamplerUniform), | 264 this->getUniformCStr(sampler.fSamplerUniform), |
301 coordName); | 265 coordName); |
302 append_swizzle(out, sampler, *fCtxInfo.caps()); | 266 append_swizzle(out, sampler, *fCtxInfo.caps()); |
303 } | 267 } |
304 | 268 |
305 void GrGLShaderBuilder::appendTextureLookup(ShaderType type, | 269 void GrGLShaderBuilder::fsAppendTextureLookup(const GrGLShaderBuilder::TextureSa
mpler& sampler, |
306 const GrGLShaderBuilder::TextureSamp
ler& sampler, | 270 const char* coordName, |
307 const char* coordName, | 271 GrSLType varyingType) { |
308 GrSLType varyingType) { | |
309 SkASSERT(kFragment_ShaderType == type); | |
310 this->appendTextureLookup(&fFSCode, sampler, coordName, varyingType); | 272 this->appendTextureLookup(&fFSCode, sampler, coordName, varyingType); |
311 } | 273 } |
312 | 274 |
313 void GrGLShaderBuilder::appendTextureLookupAndModulate( | 275 void GrGLShaderBuilder::fsAppendTextureLookupAndModulate( |
314 ShaderType type, | |
315 const char* modulation, | 276 const char* modulation, |
316 const GrGLShaderBuilder::TextureSamp
ler& sampler, | 277 const GrGLShaderBuilder::TextureSamp
ler& sampler, |
317 const char* coordName, | 278 const char* coordName, |
318 GrSLType varyingType) { | 279 GrSLType varyingType) { |
319 SkASSERT(kFragment_ShaderType == type); | |
320 SkString lookup; | 280 SkString lookup; |
321 this->appendTextureLookup(&lookup, sampler, coordName, varyingType); | 281 this->appendTextureLookup(&lookup, sampler, coordName, varyingType); |
322 GrGLSLModulatef<4>(&fFSCode, modulation, lookup.c_str()); | 282 GrGLSLModulatef<4>(&fFSCode, modulation, lookup.c_str()); |
323 } | 283 } |
324 | 284 |
325 GrBackendEffectFactory::EffectKey GrGLShaderBuilder::KeyForTextureAccess( | 285 GrBackendEffectFactory::EffectKey GrGLShaderBuilder::KeyForTextureAccess( |
326 const GrTextureAcces
s& access, | 286 const GrTextureAcces
s& access, |
327 const GrGLCaps& caps
) { | 287 const GrGLCaps& caps
) { |
328 uint32_t configComponentMask = GrPixelConfigComponentMask(access.getTexture(
)->config()); | 288 uint32_t configComponentMask = GrPixelConfigComponentMask(access.getTexture(
)->config()); |
329 if (swizzle_requires_alpha_remapping(caps, configComponentMask, access.swizz
leMask())) { | 289 if (swizzle_requires_alpha_remapping(caps, configComponentMask, access.swizz
leMask())) { |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
376 return gStraight; | 336 return gStraight; |
377 } | 337 } |
378 } | 338 } |
379 | 339 |
380 GrGLUniformManager::UniformHandle GrGLShaderBuilder::addUniformArray(uint32_t vi
sibility, | 340 GrGLUniformManager::UniformHandle GrGLShaderBuilder::addUniformArray(uint32_t vi
sibility, |
381 GrSLType ty
pe, | 341 GrSLType ty
pe, |
382 const char*
name, | 342 const char*
name, |
383 int count, | 343 int count, |
384 const char*
* outName) { | 344 const char*
* outName) { |
385 SkASSERT(name && strlen(name)); | 345 SkASSERT(name && strlen(name)); |
386 SkDEBUGCODE(static const uint32_t kVisibilityMask = kVertex_ShaderType | kFr
agment_ShaderType); | 346 SkDEBUGCODE(static const uint32_t kVisibilityMask = kVertex_Visibility | kFr
agment_Visibility); |
387 SkASSERT(0 == (~kVisibilityMask & visibility)); | 347 SkASSERT(0 == (~kVisibilityMask & visibility)); |
388 SkASSERT(0 != visibility); | 348 SkASSERT(0 != visibility); |
389 | 349 |
390 BuilderUniform& uni = fUniforms.push_back(); | 350 BuilderUniform& uni = fUniforms.push_back(); |
391 UniformHandle h = GrGLUniformManager::UniformHandle::CreateFromUniformIndex(
fUniforms.count() - 1); | 351 UniformHandle h = GrGLUniformManager::UniformHandle::CreateFromUniformIndex(
fUniforms.count() - 1); |
392 GR_DEBUGCODE(UniformHandle h2 =) | 352 GR_DEBUGCODE(UniformHandle h2 =) |
393 fUniformManager.appendUniform(type, count); | 353 fUniformManager.appendUniform(type, count); |
394 // We expect the uniform manager to initially have no uniforms and that all
uniforms are added | 354 // We expect the uniform manager to initially have no uniforms and that all
uniforms are added |
395 // by this function. Therefore, the handles should match. | 355 // by this function. Therefore, the handles should match. |
396 SkASSERT(h2 == h); | 356 SkASSERT(h2 == h); |
397 uni.fVariable.setType(type); | 357 uni.fVariable.setType(type); |
398 uni.fVariable.setTypeModifier(GrGLShaderVar::kUniform_TypeModifier); | 358 uni.fVariable.setTypeModifier(GrGLShaderVar::kUniform_TypeModifier); |
399 this->nameVariable(uni.fVariable.accessName(), 'u', name); | 359 this->nameVariable(uni.fVariable.accessName(), 'u', name); |
400 uni.fVariable.setArrayCount(count); | 360 uni.fVariable.setArrayCount(count); |
401 uni.fVisibility = visibility; | 361 uni.fVisibility = visibility; |
402 | 362 |
403 // If it is visible in both the VS and FS, the precision must match. | 363 // If it is visible in both the VS and FS, the precision must match. |
404 // We declare a default FS precision, but not a default VS. So set the var | 364 // We declare a default FS precision, but not a default VS. So set the var |
405 // to use the default FS precision. | 365 // to use the default FS precision. |
406 if ((kVertex_ShaderType | kFragment_ShaderType) == visibility) { | 366 if ((kVertex_Visibility | kFragment_Visibility) == visibility) { |
407 // the fragment and vertex precisions must match | 367 // the fragment and vertex precisions must match |
408 uni.fVariable.setPrecision(kDefaultFragmentPrecision); | 368 uni.fVariable.setPrecision(kDefaultFragmentPrecision); |
409 } | 369 } |
410 | 370 |
411 if (NULL != outName) { | 371 if (NULL != outName) { |
412 *outName = uni.fVariable.c_str(); | 372 *outName = uni.fVariable.c_str(); |
413 } | 373 } |
414 | 374 |
415 return h; | 375 return h; |
416 } | 376 } |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
501 return "gl_FragCoord"; | 461 return "gl_FragCoord"; |
502 } else { | 462 } else { |
503 static const char* kCoordName = "fragCoordYDown"; | 463 static const char* kCoordName = "fragCoordYDown"; |
504 if (!fSetupFragPosition) { | 464 if (!fSetupFragPosition) { |
505 // temporarily change the stage index because we're inserting non-st
age code. | 465 // temporarily change the stage index because we're inserting non-st
age code. |
506 CodeStage::AutoStageRestore csar(&fCodeStage, NULL); | 466 CodeStage::AutoStageRestore csar(&fCodeStage, NULL); |
507 | 467 |
508 SkASSERT(!fRTHeightUniform.isValid()); | 468 SkASSERT(!fRTHeightUniform.isValid()); |
509 const char* rtHeightName; | 469 const char* rtHeightName; |
510 | 470 |
511 fRTHeightUniform = this->addUniform(kFragment_ShaderType, | 471 fRTHeightUniform = this->addUniform(kFragment_Visibility, |
512 kFloat_GrSLType, | 472 kFloat_GrSLType, |
513 "RTHeight", | 473 "RTHeight", |
514 &rtHeightName); | 474 &rtHeightName); |
515 | 475 |
516 this->fFSCode.prependf("\tvec4 %s = vec4(gl_FragCoord.x, %s - gl_Fra
gCoord.y, gl_FragCoord.zw);\n", | 476 this->fFSCode.prependf("\tvec4 %s = vec4(gl_FragCoord.x, %s - gl_Fra
gCoord.y, gl_FragCoord.zw);\n", |
517 kCoordName, rtHeightName); | 477 kCoordName, rtHeightName); |
518 fSetupFragPosition = true; | 478 fSetupFragPosition = true; |
519 } | 479 } |
520 SkASSERT(fRTHeightUniform.isValid()); | 480 SkASSERT(fRTHeightUniform.isValid()); |
521 return kCoordName; | 481 return kCoordName; |
522 } | 482 } |
523 } | 483 } |
524 | 484 |
525 | 485 |
526 void GrGLShaderBuilder::emitFunction(ShaderType shader, | 486 void GrGLShaderBuilder::fsEmitFunction(GrSLType returnType, |
527 GrSLType returnType, | 487 const char* name, |
528 const char* name, | 488 int argCnt, |
529 int argCnt, | 489 const GrGLShaderVar* args, |
530 const GrGLShaderVar* args, | 490 const char* body, |
531 const char* body, | 491 SkString* outName) { |
532 SkString* outName) { | |
533 SkASSERT(kFragment_ShaderType == shader); | |
534 fFSFunctions.append(GrGLSLTypeString(returnType)); | 492 fFSFunctions.append(GrGLSLTypeString(returnType)); |
535 this->nameVariable(outName, '\0', name); | 493 this->nameVariable(outName, '\0', name); |
536 fFSFunctions.appendf(" %s", outName->c_str()); | 494 fFSFunctions.appendf(" %s", outName->c_str()); |
537 fFSFunctions.append("("); | 495 fFSFunctions.append("("); |
538 for (int i = 0; i < argCnt; ++i) { | 496 for (int i = 0; i < argCnt; ++i) { |
539 args[i].appendDecl(fCtxInfo, &fFSFunctions); | 497 args[i].appendDecl(fCtxInfo, &fFSFunctions); |
540 if (i < argCnt - 1) { | 498 if (i < argCnt - 1) { |
541 fFSFunctions.append(", "); | 499 fFSFunctions.append(", "); |
542 } | 500 } |
543 } | 501 } |
(...skipping 28 matching lines...) Expand all Loading... |
572 } | 530 } |
573 } | 531 } |
574 | 532 |
575 void GrGLShaderBuilder::appendDecls(const VarArray& vars, SkString* out) const { | 533 void GrGLShaderBuilder::appendDecls(const VarArray& vars, SkString* out) const { |
576 for (int i = 0; i < vars.count(); ++i) { | 534 for (int i = 0; i < vars.count(); ++i) { |
577 vars[i].appendDecl(fCtxInfo, out); | 535 vars[i].appendDecl(fCtxInfo, out); |
578 out->append(";\n"); | 536 out->append(";\n"); |
579 } | 537 } |
580 } | 538 } |
581 | 539 |
582 void GrGLShaderBuilder::appendUniformDecls(ShaderType stype, SkString* out) cons
t { | 540 void GrGLShaderBuilder::appendUniformDecls(ShaderVisibility visibility, |
| 541 SkString* out) const { |
583 for (int i = 0; i < fUniforms.count(); ++i) { | 542 for (int i = 0; i < fUniforms.count(); ++i) { |
584 if (fUniforms[i].fVisibility & stype) { | 543 if (fUniforms[i].fVisibility & visibility) { |
585 fUniforms[i].fVariable.appendDecl(fCtxInfo, out); | 544 fUniforms[i].fVariable.appendDecl(fCtxInfo, out); |
586 out->append(";\n"); | 545 out->append(";\n"); |
587 } | 546 } |
588 } | 547 } |
589 } | 548 } |
590 | 549 |
591 void GrGLShaderBuilder::getShader(ShaderType type, SkString* shaderStr) const { | 550 void GrGLShaderBuilder::vsGetShader(SkString* shaderStr) const { |
592 const char* version = GrGetGLSLVersionDecl(fCtxInfo); | 551 *shaderStr = GrGetGLSLVersionDecl(fCtxInfo); |
| 552 this->appendUniformDecls(kVertex_Visibility, shaderStr); |
| 553 this->appendDecls(fVSAttrs, shaderStr); |
| 554 this->appendDecls(fVSOutputs, shaderStr); |
| 555 shaderStr->append("void main() {\n"); |
| 556 shaderStr->append(fVSCode); |
| 557 shaderStr->append("}\n"); |
| 558 } |
593 | 559 |
594 switch (type) { | 560 void GrGLShaderBuilder::gsGetShader(SkString* shaderStr) const { |
595 case kVertex_ShaderType: | 561 if (!fUsesGS) { |
596 *shaderStr = version; | 562 shaderStr->reset(); |
597 this->appendUniformDecls(kVertex_ShaderType, shaderStr); | 563 return; |
598 this->appendDecls(fVSAttrs, shaderStr); | |
599 this->appendDecls(fVSOutputs, shaderStr); | |
600 shaderStr->append("void main() {\n"); | |
601 shaderStr->append(fVSCode); | |
602 shaderStr->append("}\n"); | |
603 break; | |
604 case kGeometry_ShaderType: | |
605 if (fUsesGS) { | |
606 *shaderStr = version; | |
607 shaderStr->append(fGSHeader); | |
608 this->appendDecls(fGSInputs, shaderStr); | |
609 this->appendDecls(fGSOutputs, shaderStr); | |
610 shaderStr->append("void main() {\n"); | |
611 shaderStr->append(fGSCode); | |
612 shaderStr->append("}\n"); | |
613 } else { | |
614 shaderStr->reset(); | |
615 } | |
616 break; | |
617 case kFragment_ShaderType: | |
618 *shaderStr = version; | |
619 shaderStr->append(fFSExtensions); | |
620 append_default_precision_qualifier(kDefaultFragmentPrecision, | |
621 fCtxInfo.binding(), | |
622 shaderStr); | |
623 this->appendUniformDecls(kFragment_ShaderType, shaderStr); | |
624 this->appendDecls(fFSInputs, shaderStr); | |
625 // We shouldn't have declared outputs on 1.10 | |
626 SkASSERT(k110_GrGLSLGeneration != fCtxInfo.glslGeneration() || fFSOu
tputs.empty()); | |
627 this->appendDecls(fFSOutputs, shaderStr); | |
628 shaderStr->append(fFSFunctions); | |
629 shaderStr->append("void main() {\n"); | |
630 shaderStr->append(fFSCode); | |
631 shaderStr->append("}\n"); | |
632 break; | |
633 } | 564 } |
634 } | 565 |
| 566 *shaderStr = GrGetGLSLVersionDecl(fCtxInfo); |
| 567 shaderStr->append(fGSHeader); |
| 568 this->appendDecls(fGSInputs, shaderStr); |
| 569 this->appendDecls(fGSOutputs, shaderStr); |
| 570 shaderStr->append("void main() {\n"); |
| 571 shaderStr->append(fGSCode); |
| 572 shaderStr->append("}\n"); |
| 573 } |
| 574 |
| 575 void GrGLShaderBuilder::fsGetShader(SkString* shaderStr) const { |
| 576 *shaderStr = GrGetGLSLVersionDecl(fCtxInfo); |
| 577 shaderStr->append(fFSExtensions); |
| 578 append_default_precision_qualifier(kDefaultFragmentPrecision, |
| 579 fCtxInfo.binding(), |
| 580 shaderStr); |
| 581 this->appendUniformDecls(kFragment_Visibility, shaderStr); |
| 582 this->appendDecls(fFSInputs, shaderStr); |
| 583 // We shouldn't have declared outputs on 1.10 |
| 584 SkASSERT(k110_GrGLSLGeneration != fCtxInfo.glslGeneration() || fFSOutputs.em
pty()); |
| 585 this->appendDecls(fFSOutputs, shaderStr); |
| 586 shaderStr->append(fFSFunctions); |
| 587 shaderStr->append("void main() {\n"); |
| 588 shaderStr->append(fFSCode); |
| 589 shaderStr->append("}\n"); |
| 590 } |
635 | 591 |
636 void GrGLShaderBuilder::finished(GrGLuint programID) { | 592 void GrGLShaderBuilder::finished(GrGLuint programID) { |
637 fUniformManager.getUniformLocations(programID, fUniforms); | 593 fUniformManager.getUniformLocations(programID, fUniforms); |
638 } | 594 } |
639 | 595 |
640 void GrGLShaderBuilder::emitEffects( | 596 void GrGLShaderBuilder::emitEffects( |
641 const GrEffectStage* effectStages[], | 597 const GrEffectStage* effectStages[], |
642 const GrBackendEffectFactory::EffectKey effectKeys[], | 598 const GrBackendEffectFactory::EffectKey effectKeys[], |
643 int effectCnt, | 599 int effectCnt, |
644 SkString* fsInOutColor, | 600 SkString* fsInOutColor, |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
720 for (const AttributePair* attrib = this->getEffectAttributes().begin(); | 676 for (const AttributePair* attrib = this->getEffectAttributes().begin(); |
721 attrib != attribEnd; | 677 attrib != attribEnd; |
722 ++attrib) { | 678 ++attrib) { |
723 if (attrib->fIndex == attributeIndex) { | 679 if (attrib->fIndex == attributeIndex) { |
724 return &attrib->fName; | 680 return &attrib->fName; |
725 } | 681 } |
726 } | 682 } |
727 | 683 |
728 return NULL; | 684 return NULL; |
729 } | 685 } |
OLD | NEW |