1 /*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "gm.h"
9 #include "sk_tool_utils.h"
10
11 #include "SkGradientShader.h"
12 #include "SkImagePriv.h"
13 #include "SkPM4fPriv.h"
14 #include "SkSurface.h"
15 #include "SkVertices.h"
16
17 static const int gRectSize = 50;
18 static const SkScalar gScalarSize = SkIntToScalar(gRectSize);
19 static const int gTestWidth = 700;
20 static const int gTestHeight = 300;
21
22 struct CellRenderer {
23 virtual void draw(SkCanvas* canvas) = 0;
24 virtual const char* label() = 0;
~CellRendererCellRenderer25 virtual ~CellRenderer() {}
26 };
27
28 struct PaintColorCellRenderer : public CellRenderer {
PaintColorCellRendererPaintColorCellRenderer29 PaintColorCellRenderer(SkColor color) : fColor(color) {}
drawPaintColorCellRenderer30 void draw(SkCanvas* canvas) override {
31 canvas->drawColor(fColor);
32 }
labelPaintColorCellRenderer33 const char* label() override {
34 return "Paint Color";
35 }
36 protected:
37 SkColor fColor;
38 };
39
40 struct BitmapCellRenderer : public CellRenderer {
BitmapCellRendererBitmapCellRenderer41 BitmapCellRenderer(SkColor color, SkFilterQuality quality, float scale = 1.0f)
42 : fQuality(quality) {
43 int scaledSize = sk_float_round2int(scale * gRectSize);
44 fBitmap.allocPixels(SkImageInfo::MakeS32(scaledSize, scaledSize, kPremul_SkAlphaType));
45 fBitmap.eraseColor(color);
46 const char* qualityNames[] = { "None", "Low", "Medium", "High" };
47 fLabel = SkStringPrintf("Bitmap (%s)", qualityNames[quality]);
48 }
drawBitmapCellRenderer49 void draw(SkCanvas* canvas) override {
50 SkPaint paint;
51 paint.setFilterQuality(fQuality);
52 canvas->drawBitmapRect(fBitmap, SkRect::MakeIWH(gRectSize, gRectSize), &paint);
53 }
labelBitmapCellRenderer54 const char* label() override {
55 return fLabel.c_str();
56 }
57 protected:
58 SkFilterQuality fQuality;
59 SkBitmap fBitmap;
60 SkString fLabel;
61 };
62
63 struct GradientCellRenderer : public CellRenderer {
GradientCellRendererGradientCellRenderer64 GradientCellRenderer(SkColor colorOne, SkColor colorTwo, bool manyStops) {
65 fColors[0] = colorOne;
66 fColors[1] = colorTwo;
67 fManyStops = manyStops;
68 }
drawGradientCellRenderer69 void draw(SkCanvas* canvas) override {
70 SkPoint points[2] = {
71 SkPoint::Make(0, 0),
72 SkPoint::Make(0, gScalarSize)
73 };
74 SkPaint paint;
75 if (fManyStops) {
76 SkColor colors[4] ={
77 fColors[0], fColors[0], fColors[1], fColors[1]
78 };
79 paint.setShader(SkGradientShader::MakeLinear(points, colors, nullptr, 4,
80 SkShader::kClamp_TileMode));
81 } else {
82 paint.setShader(SkGradientShader::MakeLinear(points, fColors, nullptr, 2,
83 SkShader::kClamp_TileMode));
84 }
85 canvas->drawPaint(paint);
86 }
labelGradientCellRenderer87 const char* label() override {
88 return "Linear Gradient";
89 }
90 protected:
91 SkColor fColors[2];
92 bool fManyStops;
93 };
94
95 struct VerticesCellRenderer : public CellRenderer {
VerticesCellRendererVerticesCellRenderer96 VerticesCellRenderer(SkColor colorOne, SkColor colorTwo) {
97 fColors[0] = fColors[1] = colorOne;
98 fColors[2] = fColors[3] = colorTwo;
99 }
drawVerticesCellRenderer100 void draw(SkCanvas* canvas) override {
101 SkPaint paint;
102 SkPoint vertices[4] = {
103 SkPoint::Make(0, 0),
104 SkPoint::Make(gScalarSize, 0),
105 SkPoint::Make(gScalarSize, gScalarSize),
106 SkPoint::Make(0, gScalarSize)
107 };
108 canvas->drawVertices(SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, 4, vertices,
109 nullptr, fColors),
110 SkBlendMode::kModulate, paint);
111 }
labelVerticesCellRenderer112 const char* label() override {
113 return "Vertices";
114 }
115 protected:
116 SkColor fColors[4];
117 };
118
draw_gamut_grid(SkCanvas * canvas,SkTArray<std::unique_ptr<CellRenderer>> & renderers)119 static void draw_gamut_grid(SkCanvas* canvas, SkTArray<std::unique_ptr<CellRenderer>>& renderers) {
120 // We want our colors in our wide gamut to be obviously visibly distorted from sRGB, so we use
121 // Wide Gamut RGB (with sRGB gamma, for HW acceleration) as the working space for this test:
122 const float gWideGamutRGB_toXYZD50[]{
123 0.7161046f, 0.1009296f, 0.1471858f, // -> X
124 0.2581874f, 0.7249378f, 0.0168748f, // -> Y
125 0.0000000f, 0.0517813f, 0.7734287f, // -> Z
126 };
127
128 SkMatrix44 wideGamutRGB_toXYZD50(SkMatrix44::kUninitialized_Constructor);
129 wideGamutRGB_toXYZD50.set3x3RowMajorf(gWideGamutRGB_toXYZD50);
130
131 // Use the original canvas' color type, but account for gamma requirements
132 SkImageInfo origInfo = canvas->imageInfo();
133 sk_sp<SkColorSpace> srgbCS;
134 sk_sp<SkColorSpace> wideCS;
135 switch (origInfo.colorType()) {
136 case kRGBA_8888_SkColorType:
137 case kBGRA_8888_SkColorType:
138 srgbCS = SkColorSpace::MakeSRGB();
139 wideCS = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
140 wideGamutRGB_toXYZD50);
141 break;
142 case kRGBA_F16_SkColorType:
143 srgbCS = SkColorSpace::MakeSRGBLinear();
144 wideCS = SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma,
145 wideGamutRGB_toXYZD50);
146 break;
147 default:
148 return;
149 }
150 SkASSERT(srgbCS);
151 SkASSERT(wideCS);
152
153 // Make our two working surfaces (one sRGB, one Wide)
154 SkImageInfo srgbGamutInfo = SkImageInfo::Make(gRectSize, gRectSize, origInfo.colorType(),
155 kPremul_SkAlphaType, srgbCS);
156 SkImageInfo wideGamutInfo = SkImageInfo::Make(gRectSize, gRectSize, origInfo.colorType(),
157 kPremul_SkAlphaType, wideCS);
158
159 sk_sp<SkSurface> srgbGamutSurface = canvas->makeSurface(srgbGamutInfo);
160 sk_sp<SkSurface> wideGamutSurface = canvas->makeSurface(wideGamutInfo);
161 if (!srgbGamutSurface || !wideGamutSurface) {
162 return;
163 }
164 SkCanvas* srgbGamutCanvas = srgbGamutSurface->getCanvas();
165 SkCanvas* wideGamutCanvas = wideGamutSurface->getCanvas();
166
167 SkPaint textPaint;
168 textPaint.setAntiAlias(true);
169 textPaint.setColor(SK_ColorWHITE);
170 sk_tool_utils::set_portable_typeface(&textPaint);
171
172 SkScalar x = 0, y = 0;
173 SkScalar textHeight = textPaint.getFontSpacing();
174
175 for (const auto& renderer : renderers) {
176 srgbGamutCanvas->clear(SK_ColorBLACK);
177 renderer->draw(srgbGamutCanvas);
178 wideGamutCanvas->clear(SK_ColorBLACK);
179 renderer->draw(wideGamutCanvas);
180
181 canvas->drawString(renderer->label(), x, y + textHeight, textPaint);
182
183 // Re-interpret the off-screen images, so we can see the raw data (eg, Wide gamut squares
184 // will look desaturated, relative to sRGB).
185 auto srgbImage = srgbGamutSurface->makeImageSnapshot();
186 srgbImage = SkImageMakeRasterCopyAndAssignColorSpace(srgbImage.get(),
187 origInfo.colorSpace());
188 canvas->drawImage(srgbImage, x, y + textHeight + 5);
189 x += (gScalarSize + 1);
190
191 auto wideImage = wideGamutSurface->makeImageSnapshot();
192 wideImage = SkImageMakeRasterCopyAndAssignColorSpace(wideImage.get(),
193 origInfo.colorSpace());
194 canvas->drawImage(wideImage, x, y + textHeight + 5);
195 x += (gScalarSize + 10);
196
197 if (x + (2 * gScalarSize + 1) > gTestWidth) {
198 x = 0;
199 y += (textHeight + gScalarSize + 10);
200 }
201 }
202 }
203
DEF_SIMPLE_GM_BG(gamut,canvas,gTestWidth,gTestHeight,SK_ColorBLACK)204 DEF_SIMPLE_GM_BG(gamut, canvas, gTestWidth, gTestHeight, SK_ColorBLACK) {
205 SkTArray<std::unique_ptr<CellRenderer>> renderers;
206
207 // sRGB primaries, rendered as paint color
208 renderers.emplace_back(new PaintColorCellRenderer(SK_ColorRED));
209 renderers.emplace_back(new PaintColorCellRenderer(SK_ColorGREEN));
210
211 // sRGB primaries, rendered as bitmaps
212 renderers.emplace_back(new BitmapCellRenderer(SK_ColorRED, kNone_SkFilterQuality));
213 renderers.emplace_back(new BitmapCellRenderer(SK_ColorGREEN, kLow_SkFilterQuality));
214 // Larger bitmap to trigger mipmaps
215 renderers.emplace_back(new BitmapCellRenderer(SK_ColorRED, kMedium_SkFilterQuality, 2.0f));
216 // Smaller bitmap to trigger bicubic
217 renderers.emplace_back(new BitmapCellRenderer(SK_ColorGREEN, kHigh_SkFilterQuality, 0.5f));
218
219 // Various gradients involving sRGB primaries and white/black
220
221 // First with just two stops (implemented with uniforms on GPU)
222 renderers.emplace_back(new GradientCellRenderer(SK_ColorRED, SK_ColorGREEN, false));
223 renderers.emplace_back(new GradientCellRenderer(SK_ColorGREEN, SK_ColorBLACK, false));
224 renderers.emplace_back(new GradientCellRenderer(SK_ColorGREEN, SK_ColorWHITE, false));
225
226 // ... and then with four stops (implemented with textures on GPU)
227 renderers.emplace_back(new GradientCellRenderer(SK_ColorRED, SK_ColorGREEN, true));
228 renderers.emplace_back(new GradientCellRenderer(SK_ColorGREEN, SK_ColorBLACK, true));
229 renderers.emplace_back(new GradientCellRenderer(SK_ColorGREEN, SK_ColorWHITE, true));
230
231 // Vertex colors
232 renderers.emplace_back(new VerticesCellRenderer(SK_ColorRED, SK_ColorRED));
233 renderers.emplace_back(new VerticesCellRenderer(SK_ColorRED, SK_ColorGREEN));
234
235 draw_gamut_grid(canvas, renderers);
236 }
237