| |
| /* |
| * Copyright 2013 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| #include "gm.h" |
| #include "SkBitmap.h" |
| #include "SkRandom.h" |
| #include "SkShader.h" |
| #include "SkXfermode.h" |
| |
| namespace skiagm { |
| |
| /** |
| * Renders overlapping shapes with random SkXfermode::Modes against a checkerboard. |
| */ |
| class MixedXfermodesGM : public GM { |
| public: |
| MixedXfermodesGM() { |
| } |
| |
| protected: |
| enum ShapeType { |
| kShapeTypeCircle, |
| kShapeTypeRoundRect, |
| kShapeTypeRect, |
| kShapeTypeConvexPath, |
| kShapeTypeConcavePath, |
| kNumShapeTypes |
| }; |
| |
| SkString onShortName() override { |
| return SkString("mixed_xfermodes"); |
| } |
| |
| SkISize onISize() override { |
| return SkISize::Make(790, 640); |
| } |
| |
| void drawShape(SkCanvas* canvas, |
| const SkPaint& paint, |
| ShapeType type) { |
| static const SkRect kRect = SkRect::MakeXYWH(SkIntToScalar(-50), SkIntToScalar(-50), |
| SkIntToScalar(75), SkIntToScalar(105)); |
| switch (type) { |
| case kShapeTypeCircle: |
| canvas->drawCircle(0, 0, 50, paint); |
| break; |
| case kShapeTypeRoundRect: |
| canvas->drawRoundRect(kRect, SkIntToScalar(10), SkIntToScalar(20), paint); |
| break; |
| case kShapeTypeRect: |
| canvas->drawRect(kRect, paint); |
| break; |
| case kShapeTypeConvexPath: |
| if (fConvexPath.isEmpty()) { |
| SkPoint points[4]; |
| kRect.toQuad(points); |
| fConvexPath.moveTo(points[0]); |
| fConvexPath.quadTo(points[1], points[2]); |
| fConvexPath.quadTo(points[3], points[0]); |
| SkASSERT(fConvexPath.isConvex()); |
| } |
| canvas->drawPath(fConvexPath, paint); |
| break; |
| case kShapeTypeConcavePath: |
| if (fConcavePath.isEmpty()) { |
| SkPoint points[5] = {{0, SkIntToScalar(-50)} }; |
| SkMatrix rot; |
| rot.setRotate(SkIntToScalar(360) / 5); |
| for (int i = 1; i < 5; ++i) { |
| rot.mapPoints(points + i, points + i - 1, 1); |
| } |
| fConcavePath.moveTo(points[0]); |
| for (int i = 0; i < 5; ++i) { |
| fConcavePath.lineTo(points[(2 * i) % 5]); |
| } |
| fConcavePath.setFillType(SkPath::kEvenOdd_FillType); |
| SkASSERT(!fConcavePath.isConvex()); |
| } |
| canvas->drawPath(fConcavePath, paint); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void onDraw(SkCanvas* canvas) override { |
| if (NULL == fBG.get()) { |
| static uint32_t kCheckerPixelData[] = { 0xFFFFFFFF, |
| 0xFFCCCCCC, |
| 0xFFCCCCCC, |
| 0xFFFFFFFF }; |
| SkBitmap bitmap; |
| bitmap.allocN32Pixels(2, 2); |
| memcpy(bitmap.getPixels(), kCheckerPixelData, sizeof(kCheckerPixelData)); |
| SkMatrix lm; |
| lm.setScale(SkIntToScalar(20), SkIntToScalar(20)); |
| fBG.reset(SkShader::CreateBitmapShader(bitmap, |
| SkShader::kRepeat_TileMode, |
| SkShader::kRepeat_TileMode, |
| &lm)); |
| } |
| |
| SkPaint bgPaint; |
| bgPaint.setShader(fBG.get()); |
| canvas->drawPaint(bgPaint); |
| SkISize size = canvas->getDeviceSize(); |
| SkScalar maxScale = SkScalarSqrt((SkIntToScalar(size.fWidth * size.fHeight))) / 300; |
| SkRandom random; |
| for (int i = 0; i < kNumShapes; ++i) { |
| SkScalar s = random.nextRangeScalar(SK_Scalar1 / 8, SK_Scalar1) * maxScale; |
| SkScalar r = random.nextRangeScalar(0, SkIntToScalar(360)); |
| SkScalar dx = random.nextRangeScalar(0, SkIntToScalar(size.fWidth)); |
| SkScalar dy = random.nextRangeScalar(0, SkIntToScalar(size.fHeight)); |
| SkColor color = random.nextU(); |
| SkXfermode::Mode mode = |
| static_cast<SkXfermode::Mode>(random.nextULessThan(SkXfermode::kLastMode + 1)); |
| ShapeType shapeType = static_cast<ShapeType>(random.nextULessThan(kNumShapeTypes)); |
| |
| SkPaint p; |
| p.setAntiAlias(true); |
| p.setColor(color); |
| p.setXfermodeMode(mode); |
| canvas->save(); |
| canvas->translate(dx, dy); |
| canvas->scale(s, s); |
| canvas->rotate(r); |
| this->drawShape(canvas, p, shapeType); |
| canvas->restore(); |
| } |
| |
| // This draw should not affect the test's result. |
| drawWithHueOnWhite(canvas); |
| } |
| |
| /** |
| * Draws white color into a white square using the hue blend mode. |
| * The result color should be white, so it doesn't change the expectations. |
| * This will test a divide by 0 bug in shaders' setLum function, |
| * which used to output black pixels. |
| */ |
| void drawWithHueOnWhite(SkCanvas* canvas) { |
| SkColor color = SkColorSetARGBMacro(225, 255, 255, 255); |
| SkXfermode::Mode mode = SkXfermode::kHue_Mode; |
| ShapeType shapeType = kShapeTypeConvexPath; |
| |
| // Make it fit into a square. |
| SkScalar s = 0.15f; |
| // Look for a clean white square. |
| SkScalar dx = 30.f; |
| SkScalar dy = 350.f; |
| |
| SkPaint p; |
| p.setAntiAlias(true); |
| p.setColor(color); |
| p.setXfermodeMode(mode); |
| canvas->save(); |
| canvas->translate(dx, dy); |
| canvas->scale(s, s); |
| this->drawShape(canvas, p, shapeType); |
| canvas->restore(); |
| } |
| |
| private: |
| enum { |
| kNumShapes = 100, |
| }; |
| SkAutoTUnref<SkShader> fBG; |
| SkPath fConcavePath; |
| SkPath fConvexPath; |
| typedef GM INHERITED; |
| }; |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| static GM* MyFactory(void*) { return new MixedXfermodesGM; } |
| static GMRegistry reg(MyFactory); |
| |
| } |