| /* |
| * Copyright 2014 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "gm/gm.h" |
| #include "include/core/SkBitmap.h" |
| #include "include/core/SkBlendMode.h" |
| #include "include/core/SkCanvas.h" |
| #include "include/core/SkColor.h" |
| #include "include/core/SkColorFilter.h" |
| #include "include/core/SkFont.h" |
| #include "include/core/SkMaskFilter.h" |
| #include "include/core/SkPaint.h" |
| #include "include/core/SkScalar.h" |
| #include "include/core/SkShader.h" |
| #include "include/core/SkSize.h" |
| #include "include/core/SkString.h" |
| #include "include/core/SkSurface.h" |
| #include "include/core/SkTypeface.h" |
| #include "include/utils/SkTextUtils.h" |
| #include "src/core/SkBlurMask.h" |
| #include "src/effects/SkEmbossMaskFilter.h" |
| #include "tools/fonts/FontToolUtils.h" |
| |
| static sk_sp<SkImage> make_bm() { |
| auto surf = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(100, 100)); |
| |
| SkPaint paint; |
| paint.setAntiAlias(true); |
| surf->getCanvas()->drawCircle(50, 50, 50, paint); |
| return surf->makeImageSnapshot(); |
| } |
| |
| class EmbossGM : public skiagm::GM { |
| public: |
| EmbossGM() { |
| } |
| |
| protected: |
| SkString getName() const override { return SkString("emboss"); } |
| |
| SkISize getISize() override { return SkISize::Make(600, 120); } |
| |
| void onDraw(SkCanvas* canvas) override { |
| SkPaint paint; |
| auto img = make_bm(); |
| canvas->drawImage(img, 10, 10); |
| canvas->translate(img->width() + SkIntToScalar(10), 0); |
| |
| paint.setMaskFilter(SkEmbossMaskFilter::Make( |
| SkBlurMask::ConvertRadiusToSigma(3), |
| { { SK_Scalar1, SK_Scalar1, SK_Scalar1 }, 0, 128, 16*2 })); |
| canvas->drawImage(img, 10, 10, SkSamplingOptions(), &paint); |
| canvas->translate(img->width() + SkIntToScalar(10), 0); |
| |
| // this combination of emboss+colorfilter used to crash -- so we exercise it to |
| // confirm that we have a fix. |
| paint.setColorFilter(SkColorFilters::Blend(0xFFFF0000, SkBlendMode::kSrcATop)); |
| canvas->drawImage(img, 10, 10, SkSamplingOptions(), &paint); |
| canvas->translate(img->width() + SkIntToScalar(10), 0); |
| |
| paint.setAntiAlias(true); |
| paint.setStyle(SkPaint::kStroke_Style); |
| paint.setStrokeWidth(SkIntToScalar(10)); |
| paint.setMaskFilter(SkEmbossMaskFilter::Make( |
| SkBlurMask::ConvertRadiusToSigma(4), |
| { { SK_Scalar1, SK_Scalar1, SK_Scalar1 }, 0, 128, 16*2 })); |
| paint.setColorFilter(nullptr); |
| paint.setShader(SkShaders::Color(SK_ColorBLUE)); |
| paint.setDither(true); |
| canvas->drawCircle(SkIntToScalar(50), SkIntToScalar(50), |
| SkIntToScalar(30), paint); |
| canvas->translate(SkIntToScalar(100), 0); |
| |
| SkFont font = SkFont(ToolUtils::DefaultPortableTypeface(), 50); |
| paint.setStyle(SkPaint::kFill_Style); |
| canvas->drawString("Hello", 0, 50, font, paint); |
| |
| paint.setShader(nullptr); |
| paint.setColor(SK_ColorGREEN); |
| canvas->drawString("World", 0, 100, font, paint); |
| } |
| |
| private: |
| using INHERITED = skiagm::GM; |
| }; |
| |
| DEF_GM(return new EmbossGM;) |
| |
| |
| void draw_emboss_example(SkCanvas* canvas, |
| const SkRect& bounds, |
| const SkEmbossMaskFilter::Light& light, |
| SkScalar blurSigma, |
| const char* label) { |
| canvas->save(); |
| canvas->clipRect(bounds); |
| canvas->translate(bounds.left(), bounds.top()); |
| |
| SkRect contentBounds = SkRect::MakeWH(bounds.width(), bounds.height()); |
| |
| sk_sp<SkMaskFilter> embossFilter = SkEmbossMaskFilter::Make(blurSigma, light); |
| if (!embossFilter) { |
| SkDebugf("Failed to create emboss filter for: %s\n", label); |
| return; |
| } |
| |
| SkPaint paint1, paint2; |
| paint1.setAntiAlias(true); |
| paint1.setColor(SK_ColorRED); |
| paint1.setMaskFilter(embossFilter); // Apply the same filter |
| |
| paint2.setAntiAlias(true); |
| paint2.setColor(SK_ColorBLUE); |
| paint2.setMaskFilter(embossFilter); // Apply the same filter |
| |
| // Draw shapes overlapping |
| SkScalar radius = contentBounds.width() * 0.25f; |
| SkScalar offset = radius * 0.3f; |
| |
| canvas->drawCircle(contentBounds.centerX() - offset, contentBounds.centerY(), radius, paint1); |
| |
| SkRect rect = SkRect::MakeXYWH(contentBounds.centerX() + offset - radius, |
| contentBounds.centerY() - radius, |
| radius * 2, radius * 2); |
| canvas->drawRect(rect, paint2); |
| |
| SkPaint textPaint; |
| textPaint.setColor(SK_ColorBLACK); |
| SkTextUtils::DrawString(canvas, label, 5, 15, ToolUtils::DefaultPortableFont(), textPaint); |
| |
| canvas->restore(); |
| } |
| |
| std::map<SkBlendMode, std::string> blendModeStrs = { |
| {SkBlendMode::kSrc, "Src"}, |
| {SkBlendMode::kDst, "Dst"}, |
| {SkBlendMode::kSrcOver, "SrcOver"}, |
| {SkBlendMode::kDstOver, "DstOver"}, |
| {SkBlendMode::kSrcIn, "SrcIn"}, |
| {SkBlendMode::kDstIn, "DstIn"}, |
| {SkBlendMode::kSrcOut, "SrcOut"}, |
| {SkBlendMode::kDstOut, "DstOut"}, |
| {SkBlendMode::kSrcATop, "SrcATop"}, |
| {SkBlendMode::kDstATop, "DstATop"}, |
| {SkBlendMode::kXor, "Xor"}, |
| {SkBlendMode::kPlus, "Plus"}, |
| {SkBlendMode::kModulate, "Modulate"}, |
| {SkBlendMode::kScreen, "Screen"}, |
| {SkBlendMode::kOverlay, "Overlay"}, |
| {SkBlendMode::kMultiply, "Multiply"}, |
| {SkBlendMode::kDarken, "Darken"}, |
| {SkBlendMode::kLighten, "Lighten"}, |
| }; |
| |
| |
| void draw_emboss_blend_example(SkCanvas* canvas, |
| const SkRect& bounds, |
| sk_sp<SkMaskFilter> embossFilter, |
| SkBlendMode blendMode, |
| const char* label) { |
| canvas->save(); |
| canvas->clipRect(bounds); |
| canvas->translate(bounds.left(), bounds.top()); |
| |
| SkRect contentBounds = SkRect::MakeWH(bounds.width(), bounds.height()); |
| |
| SkPaint paint1, paint2; |
| paint1.setAntiAlias(true); |
| paint1.setColor(SK_ColorRED); |
| |
| paint2.setAntiAlias(true); |
| paint2.setColor(SK_ColorBLUE); |
| paint2.setMaskFilter(embossFilter); |
| paint2.setBlendMode(blendMode); // Apply the specified blend mode |
| |
| // Draw shapes overlapping |
| SkScalar radius = contentBounds.width() * 0.25f; |
| SkScalar offset = radius * 0.3f; |
| |
| // Draw red circle |
| canvas->drawCircle(contentBounds.centerX() - offset, contentBounds.centerY(), radius, paint1); |
| |
| // Draw blue rect over the red circle |
| SkRect rect = SkRect::MakeXYWH(contentBounds.centerX() + offset - radius, |
| contentBounds.centerY() - radius, |
| radius * 2, radius * 2); |
| canvas->drawRect(rect, paint2); |
| |
| SkPaint textPaint; |
| textPaint.setColor(SK_ColorRED); |
| SkTextUtils::DrawString(canvas, label, 5, 15, ToolUtils::DefaultPortableFont(), textPaint); |
| |
| canvas->restore(); |
| } |
| |
| |
| |
| DEF_SIMPLE_GM(embossmaskfilter, canvas, 640, 960) { |
| const int totalWidth = 640; |
| const int topHeight = 480; |
| const int bottomHeight = 480; |
| |
| // Test different values for light: |
| { |
| const int gridCols = 2; |
| const int gridRows = 2; |
| const float cellWidth = (float)totalWidth / gridCols; |
| const float cellHeight = (float)topHeight / gridRows; |
| |
| std::vector<SkEmbossMaskFilter::Light> embossRecs; |
| std::vector<std::string> labels; |
| SkScalar blurSigma = SkDoubleToScalar(2.5); |
| |
| SkEmbossMaskFilter::Light rec; |
| |
| // Example 1: Top-left light, medium ambient, low specular |
| rec = {{-1.0f, -1.0f, 1.0f}, 0, 80, 16}; |
| embossRecs.push_back(rec); |
| labels.push_back("Light: TL, Amb: 80, Spec: 16"); |
| |
| // Example 2: Bottom-right light, high ambient, low specular |
| rec = {{1.0f, 1.0f, 0.8f}, 0, 180, 16}; |
| embossRecs.push_back(rec); |
| labels.push_back("Light: BR, Amb: 180, Spec: 16"); |
| |
| // Example 3: Direct top light, low ambient, high specular |
| rec = {{0.0f, -1.0f, 1.0f}, 0, 30, 128}; |
| embossRecs.push_back(rec); |
| labels.push_back("Light: Top, Amb: 30, Spec: 128"); |
| |
| // Example 4: Left light, medium ambient, medium specular |
| rec = {{-1.0f, 0.0f, 0.5f}, 0, 80, 64}; |
| embossRecs.push_back(rec); |
| labels.push_back("Light: Left, Amb: 80, Spec: 64"); |
| |
| size_t exampleIndex = 0; |
| for (int r = 0; r < gridRows; ++r) { |
| for (int c = 0; c < gridCols; ++c) { |
| if (exampleIndex < embossRecs.size()) { |
| // Calculate bounds within the top half |
| SkRect cellBounds = SkRect::MakeXYWH(c * cellWidth, r * cellHeight, cellWidth, cellHeight); |
| draw_emboss_example(canvas, |
| cellBounds.makeInset(10, 10), // Add padding |
| embossRecs[exampleIndex], |
| blurSigma, |
| labels[exampleIndex].c_str()); |
| exampleIndex++; |
| } |
| } |
| } |
| } |
| |
| // Test different blend modes with the mask filter |
| { |
| const int gridCols = 6; |
| const int gridRows = 3; |
| const float cellWidth = (float)totalWidth / gridCols; |
| const float cellHeight = (float)bottomHeight / gridRows; |
| |
| SkEmbossMaskFilter::Light embossRec = {{-0.707f, -0.707f, 0.707f}, 0, 60, 48}; |
| SkScalar blurSigma = SkDoubleToScalar(2.0); |
| |
| sk_sp<SkMaskFilter> embossMF = SkEmbossMaskFilter::Make(blurSigma, embossRec); |
| if (!embossMF) { |
| SkDebugf("Failed to create shared emboss filter for blend examples.\n"); |
| return; |
| } |
| |
| |
| // Blend Modes to Test |
| std::vector<SkBlendMode> blendModesToTest = { |
| SkBlendMode::kSrc, // Incorrect for Graphite |
| SkBlendMode::kDst, |
| SkBlendMode::kSrcOver, |
| SkBlendMode::kDstOver, |
| SkBlendMode::kSrcIn, // Incorrect for Graphite |
| SkBlendMode::kDstIn, // Incorrect for Graphite |
| SkBlendMode::kSrcOut, // Incorrect for Graphite |
| SkBlendMode::kDstOut, |
| SkBlendMode::kSrcATop, |
| SkBlendMode::kDstATop, // Incorrect for Graphite |
| SkBlendMode::kXor, |
| SkBlendMode::kPlus, // Incorrect for Graphite |
| SkBlendMode::kModulate, |
| SkBlendMode::kScreen, |
| SkBlendMode::kOverlay, |
| SkBlendMode::kMultiply, |
| SkBlendMode::kDarken, |
| SkBlendMode::kLighten, |
| }; |
| |
| size_t exampleIndex = 0; |
| for (int r = 0; r < gridRows; ++r) { |
| for (int c = 0; c < gridCols; ++c) { |
| if (exampleIndex < blendModesToTest.size()) { |
| SkBlendMode currentMode = blendModesToTest[exampleIndex]; |
| std::string modeName = "Unknown"; |
| auto it = blendModeStrs.find(currentMode); |
| if (it != blendModeStrs.end()) { |
| modeName = it->second; |
| } |
| |
| SkString label; |
| label.appendf("%s", modeName.c_str()); |
| |
| // Calculate bounds within the bottom half (offset by topHeight) |
| SkRect cellBounds = SkRect::MakeXYWH(c * cellWidth, |
| topHeight + r * cellHeight, // Offset Y |
| cellWidth, cellHeight); |
| draw_emboss_blend_example(canvas, |
| cellBounds.makeInset(10, 10), // Add padding |
| embossMF, // Use the same filter instance |
| currentMode, |
| label.c_str()); |
| exampleIndex++; |
| } |
| } |
| } |
| } |
| } |
| |
| DEF_SIMPLE_GM(smallemboss, canvas, 50, 50) { |
| sk_sp<SkMaskFilter> embossFilter = SkEmbossMaskFilter::Make(3, {{1, 1, 1}, 0, 0, 16}); |
| SkPaint paint; |
| paint.setAntiAlias(true); |
| paint.setColor(SK_ColorBLACK); |
| paint.setMaskFilter(embossFilter); |
| |
| auto surface = canvas->makeSurface(SkImageInfo::MakeN32Premul(50, 50)); |
| if (!surface) { |
| surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(50, 50)); |
| } |
| SkCanvas* canv = surface->getCanvas(); |
| |
| SkRect rect = SkRect::MakeXYWH(1, 1, 3, 3); |
| canv->drawRect(rect, paint); |
| |
| canvas->scale(30, 30); |
| auto img = surface->makeImageSnapshot(); |
| canvas->drawImage(img, 0, 0); |
| } |