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

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

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

Powered by Google App Engine
This is Rietveld 408576698