Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(811)

Side by Side Diff: src/effects/SkLightingShader.cpp

Issue 1291783003: Update SkLightingShader to support rotation (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Fix gyp file Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/effects/SkLightingShader.h ('k') | src/effects/SkPoint3.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1
2 /*
3 * Copyright 2015 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9 #include "SkBitmapProcState.h"
10 #include "SkColor.h"
11 #include "SkEmptyShader.h"
12 #include "SkErrorInternals.h"
13 #include "SkLightingShader.h"
14 #include "SkMathPriv.h"
15 #include "SkReadBuffer.h"
16 #include "SkWriteBuffer.h"
17
18 ////////////////////////////////////////////////////////////////////////////
19
20 /*
21 SkLightingShader TODOs:
22 support other than clamp mode
23 allow 'diffuse' & 'normal' to be of different dimensions?
24 support different light types
25 support multiple lights
26 enforce normal map is 4 channel
27 use SkImages instead if SkBitmaps
28
29 To Test:
30 non-opaque diffuse textures
31 A8 diffuse textures
32 down & upsampled draws
33 */
34
35
36
37 /** \class SkLightingShaderImpl
38 This subclass of shader applies lighting.
39 */
40 class SK_API SkLightingShaderImpl : public SkShader {
41 public:
42
43 /** Create a new lighting shader that use the provided normal map, light
44 and ambient color to light the diffuse bitmap.
45 @param diffuse the diffuse bitmap
46 @param normal the normal map
47 @param light the light applied to the normal map
48 @param ambient the linear (unpremul) ambient light color
49 */
50 SkLightingShaderImpl(const SkBitmap& diffuse, const SkBitmap& normal,
51 const SkLightingShader::Light& light,
52 const SkColor3f& ambient, const SkMatrix* localMatrix)
53 : INHERITED(localMatrix)
54 , fDiffuseMap(diffuse)
55 , fNormalMap(normal)
56 , fLight(light)
57 , fAmbientColor(ambient) {
58 if (!fLight.fDirection.normalize()) {
59 fLight.fDirection = SkPoint3::Make(0.0f, 0.0f, 1.0f);
60 }
61 }
62
63 bool isOpaque() const override;
64
65 bool asFragmentProcessor(GrContext*, const SkPaint& paint, const SkMatrix& v iewM,
66 const SkMatrix* localMatrix, GrColor* color,
67 GrProcessorDataManager*, GrFragmentProcessor** fp) const override;
68
69 size_t contextSize() const override;
70
71 class LightingShaderContext : public SkShader::Context {
72 public:
73 // The context takes ownership of the states. It will call their destruc tors
74 // but will NOT free the memory.
75 LightingShaderContext(const SkLightingShaderImpl&, const ContextRec&,
76 SkBitmapProcState* diffuseState, SkBitmapProcState * normalState);
77 ~LightingShaderContext() override;
78
79 void shadeSpan(int x, int y, SkPMColor[], int count) override;
80
81 uint32_t getFlags() const override { return fFlags; }
82
83 private:
84 SkBitmapProcState* fDiffuseState;
85 SkBitmapProcState* fNormalState;
86 uint32_t fFlags;
87
88 typedef SkShader::Context INHERITED;
89 };
90
91 SK_TO_STRING_OVERRIDE()
92 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLightingShaderImpl)
93
94 protected:
95 void flatten(SkWriteBuffer&) const override;
96 Context* onCreateContext(const ContextRec&, void*) const override;
97
98 private:
99 SkBitmap fDiffuseMap;
100 SkBitmap fNormalMap;
101 SkLightingShader::Light fLight;
102 SkColor3f fAmbientColor; // linear (unpremul) color. Range is 0..1/channel.
103
104 friend class SkLightingShader;
105
106 typedef SkShader INHERITED;
107 };
108
109 ////////////////////////////////////////////////////////////////////////////
110
111 #if SK_SUPPORT_GPU
112
113 #include "GrCoordTransform.h"
114 #include "GrFragmentProcessor.h"
115 #include "GrTextureAccess.h"
116 #include "gl/GrGLProcessor.h"
117 #include "gl/builders/GrGLProgramBuilder.h"
118 #include "SkGr.h"
119
120 class LightingFP : public GrFragmentProcessor {
121 public:
122 LightingFP(GrTexture* diffuse, GrTexture* normal, const SkMatrix& matrix,
123 const SkVector3& lightDir, const SkColor3f& lightColor,
124 const SkColor3f& ambientColor)
125 : fDeviceTransform(kDevice_GrCoordSet, matrix)
126 , fDiffuseTextureAccess(diffuse)
127 , fNormalTextureAccess(normal)
128 , fLightDir(lightDir)
129 , fLightColor(lightColor)
130 , fAmbientColor(ambientColor) {
131 this->addCoordTransform(&fDeviceTransform);
132 this->addTextureAccess(&fDiffuseTextureAccess);
133 this->addTextureAccess(&fNormalTextureAccess);
134
135 this->initClassID<LightingFP>();
136 }
137
138 class LightingGLFP : public GrGLFragmentProcessor {
139 public:
140 LightingGLFP() {
141 fLightDir.fX = 10000.0f;
142 fLightColor.fX = 0.0f;
143 fAmbientColor.fX = 0.0f;
144 }
145
146 void emitCode(EmitArgs& args) override {
147
148 GrGLFragmentBuilder* fpb = args.fBuilder->getFragmentShaderBuilder() ;
149
150 // add uniforms
151 const char* lightDirUniName = NULL;
152 fLightDirUni = args.fBuilder->addUniform(GrGLProgramBuilder::kFragme nt_Visibility,
153 kVec3f_GrSLType, kDefault_G rSLPrecision,
154 "LightDir", &lightDirUniNam e);
155
156 const char* lightColorUniName = NULL;
157 fLightColorUni = args.fBuilder->addUniform(GrGLProgramBuilder::kFrag ment_Visibility,
158 kVec3f_GrSLType, kDefault _GrSLPrecision,
159 "LightColor", &lightColor UniName);
160
161 const char* ambientColorUniName = NULL;
162 fAmbientColorUni = args.fBuilder->addUniform(GrGLProgramBuilder::kFr agment_Visibility,
163 kVec3f_GrSLType, kDefau lt_GrSLPrecision,
164 "AmbientColor", &ambien tColorUniName);
165
166 fpb->codeAppend("vec4 diffuseColor = ");
167 fpb->appendTextureLookupAndModulate(args.fInputColor, args.fSamplers [0],
168 args.fCoords[0].c_str(),
169 args.fCoords[0].getType());
170 fpb->codeAppend(";");
171
172 fpb->codeAppend("vec4 normalColor = ");
173 fpb->appendTextureLookup(args.fSamplers[1],
174 args.fCoords[0].c_str(),
175 args.fCoords[0].getType());
176 fpb->codeAppend(";");
177
178 fpb->codeAppend("vec3 normal = normalize(normalColor.rgb - vec3(0.5) );");
179 fpb->codeAppendf("vec3 lightDir = normalize(%s);", lightDirUniName);
180 fpb->codeAppend("float NdotL = dot(normal, lightDir);");
181 // diffuse light
182 fpb->codeAppendf("vec3 result = %s*diffuseColor.rgb*NdotL;", lightCo lorUniName);
183 // ambient light
184 fpb->codeAppendf("result += %s;", ambientColorUniName);
185 fpb->codeAppendf("%s = vec4(result.rgb, diffuseColor.a);", args.fOut putColor);
186 }
187
188 static void GenKey(const GrProcessor& proc, const GrGLSLCaps&,
189 GrProcessorKeyBuilder* b) {
190 // const LightingFP& lightingFP = proc.cast<LightingFP>();
191 // only one shader generated currently
192 b->add32(0x0);
193 }
194
195 protected:
196 void onSetData(const GrGLProgramDataManager& pdman, const GrProcessor& p roc) override {
197 const LightingFP& lightingFP = proc.cast<LightingFP>();
198
199 const SkVector3& lightDir = lightingFP.lightDir();
200 if (lightDir != fLightDir) {
201 pdman.set3fv(fLightDirUni, 1, &lightDir.fX);
202 fLightDir = lightDir;
203 }
204
205 const SkColor3f& lightColor = lightingFP.lightColor();
206 if (lightColor != fLightColor) {
207 pdman.set3fv(fLightColorUni, 1, &lightColor.fX);
208 fLightColor = lightColor;
209 }
210
211 const SkColor3f& ambientColor = lightingFP.ambientColor();
212 if (ambientColor != fAmbientColor) {
213 pdman.set3fv(fAmbientColorUni, 1, &ambientColor.fX);
214 fAmbientColor = ambientColor;
215 }
216 }
217
218 private:
219 SkVector3 fLightDir;
220 GrGLProgramDataManager::UniformHandle fLightDirUni;
221
222 SkColor3f fLightColor;
223 GrGLProgramDataManager::UniformHandle fLightColorUni;
224
225 SkColor3f fAmbientColor;
226 GrGLProgramDataManager::UniformHandle fAmbientColorUni;
227 };
228
229 void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) c onst override {
230 LightingGLFP::GenKey(*this, caps, b);
231 }
232
233 const char* name() const override { return "LightingFP"; }
234
235 void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
236 inout->mulByUnknownFourComponents();
237 }
238
239 const SkVector3& lightDir() const { return fLightDir; }
240 const SkColor3f& lightColor() const { return fLightColor; }
241 const SkColor3f& ambientColor() const { return fAmbientColor; }
242
243 private:
244 GrGLFragmentProcessor* onCreateGLInstance() const override { return SkNEW(Li ghtingGLFP); }
245
246 bool onIsEqual(const GrFragmentProcessor& proc) const override {
247 const LightingFP& lightingFP = proc.cast<LightingFP>();
248 return fDeviceTransform == lightingFP.fDeviceTransform &&
249 fDiffuseTextureAccess == lightingFP.fDiffuseTextureAccess &&
250 fNormalTextureAccess == lightingFP.fNormalTextureAccess &&
251 fLightDir == lightingFP.fLightDir &&
252 fLightColor == lightingFP.fLightColor &&
253 fAmbientColor == lightingFP.fAmbientColor;
254 }
255
256 GrCoordTransform fDeviceTransform;
257 GrTextureAccess fDiffuseTextureAccess;
258 GrTextureAccess fNormalTextureAccess;
259 SkVector3 fLightDir;
260 SkColor3f fLightColor;
261 SkColor3f fAmbientColor;
262 };
263
264 ////////////////////////////////////////////////////////////////////////////
265
266 bool SkLightingShaderImpl::asFragmentProcessor(GrContext* context, const SkPaint & paint,
267 const SkMatrix& viewM, const SkMa trix* localMatrix,
268 GrColor* color, GrProcessorDataMa nager*,
269 GrFragmentProcessor** fp) const {
270 // we assume diffuse and normal maps have same width and height
271 // TODO: support different sizes
272 SkASSERT(fDiffuseMap.width() == fNormalMap.width() &&
273 fDiffuseMap.height() == fNormalMap.height());
274 SkMatrix matrix;
275 matrix.setIDiv(fDiffuseMap.width(), fDiffuseMap.height());
276
277 SkMatrix lmInverse;
278 if (!this->getLocalMatrix().invert(&lmInverse)) {
279 return false;
280 }
281 if (localMatrix) {
282 SkMatrix inv;
283 if (!localMatrix->invert(&inv)) {
284 return false;
285 }
286 lmInverse.postConcat(inv);
287 }
288 matrix.preConcat(lmInverse);
289
290 // Must set wrap and filter on the sampler before requesting a texture. In t wo places below
291 // we check the matrix scale factors to determine how to interpret the filte r quality setting.
292 // This completely ignores the complexity of the drawVertices case where exp licit local coords
293 // are provided by the caller.
294 GrTextureParams::FilterMode textureFilterMode = GrTextureParams::kBilerp_Fil terMode;
295 switch (paint.getFilterQuality()) {
296 case kNone_SkFilterQuality:
297 textureFilterMode = GrTextureParams::kNone_FilterMode;
298 break;
299 case kLow_SkFilterQuality:
300 textureFilterMode = GrTextureParams::kBilerp_FilterMode;
301 break;
302 case kMedium_SkFilterQuality:{
303 SkMatrix matrix;
304 matrix.setConcat(viewM, this->getLocalMatrix());
305 if (matrix.getMinScale() < SK_Scalar1) {
306 textureFilterMode = GrTextureParams::kMipMap_FilterMode;
307 } else {
308 // Don't trigger MIP level generation unnecessarily.
309 textureFilterMode = GrTextureParams::kBilerp_FilterMode;
310 }
311 break;
312 }
313 case kHigh_SkFilterQuality:
314 default:
315 SkErrorInternals::SetError(kInvalidPaint_SkError,
316 "Sorry, I don't understand the filtering "
317 "mode you asked for. Falling back to "
318 "MIPMaps.");
319 textureFilterMode = GrTextureParams::kMipMap_FilterMode;
320 break;
321
322 }
323
324 // TODO: support other tile modes
325 GrTextureParams params(kClamp_TileMode, textureFilterMode);
326 SkAutoTUnref<GrTexture> diffuseTexture(GrRefCachedBitmapTexture(context, fDi ffuseMap, &params));
327 if (!diffuseTexture) {
328 SkErrorInternals::SetError(kInternalError_SkError,
329 "Couldn't convert bitmap to texture.");
330 return false;
331 }
332
333 SkAutoTUnref<GrTexture> normalTexture(GrRefCachedBitmapTexture(context, fNor malMap, &params));
334 if (!normalTexture) {
335 SkErrorInternals::SetError(kInternalError_SkError,
336 "Couldn't convert bitmap to texture.");
337 return false;
338 }
339
340 *fp = SkNEW_ARGS(LightingFP, (diffuseTexture, normalTexture, matrix,
341 fLight.fDirection, fLight.fColor, fAmbientColo r));
342 *color = GrColorPackA4(paint.getAlpha());
343 return true;
344 }
345 #else
346
347 bool SkLightingShaderImpl::asFragmentProcessor(GrContext* context, const SkPaint & paint,
348 const SkMatrix& viewM, const SkMa trix* localMatrix,
349 GrColor* color, GrProcessorDataMa nager*,
350 GrFragmentProcessor** fp) const {
351 SkDEBUGFAIL("Should not call in GPU-less build");
352 return false;
353 }
354
355 #endif
356
357 ////////////////////////////////////////////////////////////////////////////
358
359 bool SkLightingShaderImpl::isOpaque() const {
360 return fDiffuseMap.isOpaque();
361 }
362
363 size_t SkLightingShaderImpl::contextSize() const {
364 return 2 * sizeof(SkBitmapProcState) + sizeof(LightingShaderContext);
365 }
366
367 SkLightingShaderImpl::LightingShaderContext::LightingShaderContext(const SkLight ingShaderImpl& shader,
368 const Context Rec& rec,
369 SkBitmapProcS tate* diffuseState,
370 SkBitmapProcS tate* normalState)
371 : INHERITED(shader, rec)
372 , fDiffuseState(diffuseState)
373 , fNormalState(normalState)
374 {
375 const SkPixmap& pixmap = fDiffuseState->fPixmap;
376 bool isOpaque = pixmap.isOpaque();
377
378 // update fFlags
379 uint32_t flags = 0;
380 if (isOpaque && (255 == this->getPaintAlpha())) {
381 flags |= kOpaqueAlpha_Flag;
382 }
383
384 fFlags = flags;
385 }
386
387 SkLightingShaderImpl::LightingShaderContext::~LightingShaderContext() {
388 // The bitmap proc states have been created outside of the context on memory that will be freed
389 // elsewhere. Call the destructors but leave the freeing of the memory to th e caller.
390 fDiffuseState->~SkBitmapProcState();
391 fNormalState->~SkBitmapProcState();
392 }
393
394 static inline int light(SkScalar light, int diff, SkScalar NdotL, SkScalar ambie nt) {
395 SkScalar color = light * diff * NdotL + 255 * ambient;
396 if (color <= 0.0f) {
397 return 0;
398 } else if (color >= 255.0f) {
399 return 255;
400 } else {
401 return (int) color;
402 }
403 }
404
405 // larger is better (fewer times we have to loop), but we shouldn't
406 // take up too much stack-space (each could here costs 16 bytes)
407 #define TMP_COUNT 16
408
409 void SkLightingShaderImpl::LightingShaderContext::shadeSpan(int x, int y,
410 SkPMColor result[], int count) {
411 const SkLightingShaderImpl& lightShader = static_cast<const SkLightingShader Impl&>(fShader);
412
413 SkPMColor tmpColor[TMP_COUNT], tmpColor2[TMP_COUNT];
414 SkPMColor tmpNormal[TMP_COUNT], tmpNormal2[TMP_COUNT];
415
416 SkBitmapProcState::MatrixProc diffMProc = fDiffuseState->getMatrixProc();
417 SkBitmapProcState::SampleProc32 diffSProc = fDiffuseState->getSampleProc32() ;
418
419 SkBitmapProcState::MatrixProc normalMProc = fNormalState->getMatrixProc();
420 SkBitmapProcState::SampleProc32 normalSProc = fNormalState->getSampleProc32( );
421
422 SkASSERT(fDiffuseState->fPixmap.addr());
423 SkASSERT(fNormalState->fPixmap.addr());
424
425 SkPoint3 norm;
426 SkScalar NdotL;
427 int r, g, b;
428
429 do {
430 int n = count;
431 if (n > TMP_COUNT) {
432 n = TMP_COUNT;
433 }
434
435 diffMProc(*fDiffuseState, tmpColor, n, x, y);
436 diffSProc(*fDiffuseState, tmpColor, n, tmpColor2);
437
438 normalMProc(*fNormalState, tmpNormal, n, x, y);
439 normalSProc(*fNormalState, tmpNormal, n, tmpNormal2);
440
441 for (int i = 0; i < n; ++i) {
442 SkASSERT(0xFF == SkColorGetA(tmpNormal2[i])); // opaque -> unpremul
443 norm.set(SkIntToScalar(SkGetPackedR32(tmpNormal2[i]))-127.0f,
444 SkIntToScalar(SkGetPackedG32(tmpNormal2[i]))-127.0f,
445 SkIntToScalar(SkGetPackedB32(tmpNormal2[i]))-127.0f);
446 norm.normalize();
447
448 SkColor diffColor = SkUnPreMultiply::PMColorToColor(tmpColor2[i]);
449 NdotL = norm.dot(lightShader.fLight.fDirection);
450
451 // This is all done in linear unpremul color space
452 r = light(lightShader.fLight.fColor.fX, SkColorGetR(diffColor), Ndot L,
453 lightShader.fAmbientColor.fX);
454 g = light(lightShader.fLight.fColor.fY, SkColorGetG(diffColor), Ndot L,
455 lightShader.fAmbientColor.fY);
456 b = light(lightShader.fLight.fColor.fZ, SkColorGetB(diffColor), Ndot L,
457 lightShader.fAmbientColor.fZ);
458
459 result[i] = SkPreMultiplyARGB(SkColorGetA(diffColor), r, g, b);
460 }
461
462 result += n;
463 x += n;
464 count -= n;
465 } while (count > 0);
466 }
467
468 ////////////////////////////////////////////////////////////////////////////
469
470 #ifndef SK_IGNORE_TO_STRING
471 void SkLightingShaderImpl::toString(SkString* str) const {
472 str->appendf("LightingShader: ()");
473 }
474 #endif
475
476 SkFlattenable* SkLightingShaderImpl::CreateProc(SkReadBuffer& buf) {
477 SkMatrix localMatrix;
478 buf.readMatrix(&localMatrix);
479
480 SkBitmap diffuse;
481 if (!buf.readBitmap(&diffuse)) {
482 return NULL;
483 }
484 diffuse.setImmutable();
485
486 SkBitmap normal;
487 if (!buf.readBitmap(&normal)) {
488 return NULL;
489 }
490 normal.setImmutable();
491
492 SkLightingShader::Light light;
493 if (!buf.readScalarArray(&light.fDirection.fX, 3)) {
494 return NULL;
495 }
496 if (!buf.readScalarArray(&light.fColor.fX, 3)) {
497 return NULL;
498 }
499
500 SkColor3f ambient;
501 if (!buf.readScalarArray(&ambient.fX, 3)) {
502 return NULL;
503 }
504
505 return SkNEW_ARGS(SkLightingShaderImpl, (diffuse, normal, light, ambient, &l ocalMatrix));
506 }
507
508 void SkLightingShaderImpl::flatten(SkWriteBuffer& buf) const {
509 buf.writeMatrix(this->getLocalMatrix());
510
511 buf.writeBitmap(fDiffuseMap);
512 buf.writeBitmap(fNormalMap);
513 buf.writeScalarArray(&fLight.fDirection.fX, 3);
514 buf.writeScalarArray(&fLight.fColor.fX, 3);
515 buf.writeScalarArray(&fAmbientColor.fX, 3);
516 }
517
518 SkShader::Context* SkLightingShaderImpl::onCreateContext(const ContextRec& rec,
519 void* storage) const {
520
521 SkMatrix totalInverse;
522 // Do this first, so we know the matrix can be inverted.
523 if (!this->computeTotalInverse(rec, &totalInverse)) {
524 return NULL;
525 }
526
527 void* diffuseStateStorage = (char*)storage + sizeof(LightingShaderContext);
528 SkBitmapProcState* diffuseState = SkNEW_PLACEMENT(diffuseStateStorage, SkBit mapProcState);
529 SkASSERT(diffuseState);
530
531 diffuseState->fTileModeX = SkShader::kClamp_TileMode;
532 diffuseState->fTileModeY = SkShader::kClamp_TileMode;
533 diffuseState->fOrigBitmap = fDiffuseMap;
534 if (!diffuseState->chooseProcs(totalInverse, *rec.fPaint)) {
535 diffuseState->~SkBitmapProcState();
536 return NULL;
537 }
538
539 void* normalStateStorage = (char*)storage + sizeof(LightingShaderContext) + sizeof(SkBitmapProcState);
540 SkBitmapProcState* normalState = SkNEW_PLACEMENT(normalStateStorage, SkBitma pProcState);
541 SkASSERT(normalState);
542
543 normalState->fTileModeX = SkShader::kClamp_TileMode;
544 normalState->fTileModeY = SkShader::kClamp_TileMode;
545 normalState->fOrigBitmap = fNormalMap;
546 if (!normalState->chooseProcs(totalInverse, *rec.fPaint)) {
547 diffuseState->~SkBitmapProcState();
548 normalState->~SkBitmapProcState();
549 return NULL;
550 }
551
552 return SkNEW_PLACEMENT_ARGS(storage, LightingShaderContext, (*this, rec,
553 diffuseState, n ormalState));
554 }
555
556 ///////////////////////////////////////////////////////////////////////////////
557
558 static bool bitmap_is_too_big(const SkBitmap& bm) {
559 // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer, as it
560 // communicates between its matrix-proc and its sampler-proc. Until we can
561 // widen that, we have to reject bitmaps that are larger.
562 //
563 static const int kMaxSize = 65535;
564
565 return bm.width() > kMaxSize || bm.height() > kMaxSize;
566 }
567
568 SkShader* SkLightingShader::Create(const SkBitmap& diffuse, const SkBitmap& norm al,
569 const SkLightingShader::Light& light,
570 const SkColor3f& ambient,
571 const SkMatrix* localMatrix) {
572 if (diffuse.isNull() || bitmap_is_too_big(diffuse) ||
573 normal.isNull() || bitmap_is_too_big(normal) ||
574 diffuse.width() != normal.width() ||
575 diffuse.height() != normal.height()) {
576 return nullptr;
577 }
578
579 return SkNEW_ARGS(SkLightingShaderImpl, (diffuse, normal, light, ambient, lo calMatrix));
580 }
581
582 ///////////////////////////////////////////////////////////////////////////////
583
584 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkLightingShader)
585 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLightingShaderImpl)
586 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
587
588 ///////////////////////////////////////////////////////////////////////////////
OLDNEW
« no previous file with comments | « src/effects/SkLightingShader.h ('k') | src/effects/SkPoint3.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698