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