blob: 0cfa89532b1618ae903b59821dfe532b27e6c190 [file] [log] [blame]
/*
* 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);
}