Home | History | Annotate | Download | only in gm
      1 /*
      2  * Copyright 2015 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 
     10 #include "SkAutoMalloc.h"
     11 #include "SkCanvas.h"
     12 #include "SkRSXform.h"
     13 #include "SkSurface.h"
     14 #include "SkTextBlob.h"
     15 #include "sk_tool_utils.h"
     16 
     17 class DrawAtlasGM : public skiagm::GM {
     18     static sk_sp<SkImage> MakeAtlas(SkCanvas* caller, const SkRect& target) {
     19         SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
     20         auto surface(sk_tool_utils::makeSurface(caller, info));
     21         SkCanvas* canvas = surface->getCanvas();
     22         // draw red everywhere, but we don't expect to see it in the draw, testing the notion
     23         // that drawAtlas draws a subset-region of the atlas.
     24         canvas->clear(SK_ColorRED);
     25 
     26         SkPaint paint;
     27         paint.setBlendMode(SkBlendMode::kClear);
     28         SkRect r(target);
     29         r.inset(-1, -1);
     30         // zero out a place (with a 1-pixel border) to land our drawing.
     31         canvas->drawRect(r, paint);
     32         paint.setBlendMode(SkBlendMode::kSrcOver);
     33         paint.setColor(SK_ColorBLUE);
     34         paint.setAntiAlias(true);
     35         canvas->drawOval(target, paint);
     36         return surface->makeImageSnapshot();
     37     }
     38 
     39 public:
     40     DrawAtlasGM() {}
     41 
     42 protected:
     43 
     44     SkString onShortName() override {
     45         return SkString("draw-atlas");
     46     }
     47 
     48     SkISize onISize() override {
     49         return SkISize::Make(640, 480);
     50     }
     51 
     52     void onDraw(SkCanvas* canvas) override {
     53         const SkRect target = { 50, 50, 80, 90 };
     54         auto atlas = MakeAtlas(canvas, target);
     55 
     56         const struct {
     57             SkScalar fScale;
     58             SkScalar fDegrees;
     59             SkScalar fTx;
     60             SkScalar fTy;
     61 
     62             void apply(SkRSXform* xform) const {
     63                 const SkScalar rad = SkDegreesToRadians(fDegrees);
     64                 xform->fSCos = fScale * SkScalarCos(rad);
     65                 xform->fSSin = fScale * SkScalarSin(rad);
     66                 xform->fTx   = fTx;
     67                 xform->fTy   = fTy;
     68             }
     69         } rec[] = {
     70             { 1, 0, 10, 10 },       // just translate
     71             { 2, 0, 110, 10 },      // scale + translate
     72             { 1, 30, 210, 10 },     // rotate + translate
     73             { 2, -30, 310, 30 },    // scale + rotate + translate
     74         };
     75 
     76         const int N = SK_ARRAY_COUNT(rec);
     77         SkRSXform xform[N];
     78         SkRect tex[N];
     79         SkColor colors[N];
     80 
     81         for (int i = 0; i < N; ++i) {
     82             rec[i].apply(&xform[i]);
     83             tex[i] = target;
     84             colors[i] = 0x80FF0000 + (i * 40 * 256);
     85         }
     86 
     87         SkPaint paint;
     88         paint.setFilterQuality(kLow_SkFilterQuality);
     89         paint.setAntiAlias(true);
     90 
     91         canvas->drawAtlas(atlas.get(), xform, tex, N, nullptr, &paint);
     92         canvas->translate(0, 100);
     93         canvas->drawAtlas(atlas.get(), xform, tex, colors, N, SkBlendMode::kSrcIn, nullptr, &paint);
     94     }
     95 
     96 private:
     97     typedef GM INHERITED;
     98 };
     99 DEF_GM( return new DrawAtlasGM; )
    100 
    101 ///////////////////////////////////////////////////////////////////////////////////////////////////
    102 #include "SkFont.h"
    103 #include "SkFontPriv.h"
    104 #include "SkPath.h"
    105 #include "SkPathMeasure.h"
    106 
    107 static void draw_text_on_path(SkCanvas* canvas, const void* text, size_t length,
    108                               const SkPoint xy[], const SkPath& path, const SkFont& font, const SkPaint& paint,
    109                               float baseline_offset) {
    110     SkPathMeasure meas(path, false);
    111 
    112     int count = font.countText(text, length, kUTF8_SkTextEncoding);
    113     size_t size = count * (sizeof(SkRSXform) + sizeof(SkScalar));
    114     SkAutoSMalloc<512> storage(size);
    115     SkRSXform* xform = (SkRSXform*)storage.get();
    116     SkScalar* widths = (SkScalar*)(xform + count);
    117 
    118     // Compute a conservative bounds so we can cull the draw
    119     const SkRect fontb = SkFontPriv::GetFontBounds(font);
    120     const SkScalar max = SkTMax(SkTMax(SkScalarAbs(fontb.fLeft), SkScalarAbs(fontb.fRight)),
    121                                 SkTMax(SkScalarAbs(fontb.fTop), SkScalarAbs(fontb.fBottom)));
    122     const SkRect bounds = path.getBounds().makeOutset(max, max);
    123 
    124     SkAutoTArray<SkGlyphID> glyphs(count);
    125     font.textToGlyphs(text, length, kUTF8_SkTextEncoding, glyphs.get(), count);
    126     font.getWidths(glyphs.get(), count, widths);
    127 
    128     for (int i = 0; i < count; ++i) {
    129         // we want to position each character on the center of its advance
    130         const SkScalar offset = SkScalarHalf(widths[i]);
    131         SkPoint pos;
    132         SkVector tan;
    133         if (!meas.getPosTan(xy[i].x() + offset, &pos, &tan)) {
    134             pos = xy[i];
    135             tan.set(1, 0);
    136         }
    137         pos += SkVector::Make(-tan.fY, tan.fX) * baseline_offset;
    138 
    139         xform[i].fSCos = tan.x();
    140         xform[i].fSSin = tan.y();
    141         xform[i].fTx   = pos.x() - tan.y() * xy[i].y() - tan.x() * offset;
    142         xform[i].fTy   = pos.y() + tan.x() * xy[i].y() - tan.y() * offset;
    143     }
    144 
    145     canvas->drawTextBlob(SkTextBlob::MakeFromRSXform(glyphs.get(), count * sizeof(SkGlyphID),
    146                                          &xform[0], font, kGlyphID_SkTextEncoding),
    147                          0, 0, paint);
    148 
    149     if (true) {
    150         SkPaint p;
    151         p.setStyle(SkPaint::kStroke_Style);
    152         canvas->drawRect(bounds, p);
    153     }
    154 }
    155 
    156 #include "SkGradientShader.h"
    157 static sk_sp<SkShader> make_shader() {
    158     SkPoint pts[2] = {{0, 0}, {220, 0}};
    159     SkColor colors[2] = {SK_ColorRED, SK_ColorBLUE};
    160     return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkShader::kMirror_TileMode);
    161 }
    162 
    163 static void drawTextPath(SkCanvas* canvas, bool doStroke) {
    164     const char text0[] = "ABCDFGHJKLMNOPQRSTUVWXYZ";
    165     const int N = sizeof(text0) - 1;
    166     SkPoint pos[N];
    167 
    168     SkFont font;
    169     font.setSize(100);
    170 
    171     SkPaint paint;
    172     paint.setShader(make_shader());
    173     paint.setAntiAlias(true);
    174     if (doStroke) {
    175         paint.setStyle(SkPaint::kStroke_Style);
    176         paint.setStrokeWidth(2.25f);
    177         paint.setStrokeJoin(SkPaint::kRound_Join);
    178     }
    179 
    180     SkScalar x = 0;
    181     for (int i = 0; i < N; ++i) {
    182         pos[i].set(x, 0);
    183         x += font.measureText(&text0[i], 1, kUTF8_SkTextEncoding, nullptr, &paint);
    184     }
    185 
    186     SkPath path;
    187     const float baseline_offset = -5;
    188 
    189     const SkPath::Direction dirs[] = {
    190         SkPath::kCW_Direction, SkPath::kCCW_Direction,
    191     };
    192     for (auto d : dirs) {
    193         path.reset();
    194         path.addOval(SkRect::MakeXYWH(160, 160, 540, 540), d);
    195         draw_text_on_path(canvas, text0, N, pos, path, font, paint, baseline_offset);
    196     }
    197 
    198     paint.reset();
    199     paint.setStyle(SkPaint::kStroke_Style);
    200     canvas->drawPath(path, paint);
    201 }
    202 
    203 DEF_SIMPLE_GM(drawTextRSXform, canvas, 430, 860) {
    204     canvas->scale(0.5f, 0.5f);
    205     const bool doStroke[] = { false, true };
    206     for (auto st : doStroke) {
    207         drawTextPath(canvas, st);
    208         canvas->translate(0, 860);
    209     }
    210 }
    211 
    212 // Exercise xform blob and its bounds
    213 DEF_SIMPLE_GM(blob_rsxform, canvas, 500, 100) {
    214     SkFont font;
    215     font.setTypeface(sk_tool_utils::create_portable_typeface());
    216     font.setSize(50);
    217 
    218     const char text[] = "CrazyXform";
    219     constexpr size_t len = sizeof(text) - 1;
    220 
    221     SkRSXform xforms[len];
    222     SkScalar scale = 1;
    223     SkScalar x = 0, y = 0;
    224     for (size_t i = 0; i < len; ++i) {
    225         scale = SkScalarSin(i * SK_ScalarPI / (len-1)) * 0.75f + 0.5f;
    226         xforms[i] = SkRSXform::Make(scale, 0, x, y);
    227         x += 50 * scale;
    228     }
    229 
    230     auto blob = SkTextBlob::MakeFromRSXform(text, len, xforms, font);
    231 
    232     SkPoint offset = { 20, 70 };
    233     SkPaint paint;
    234     paint.setColor(0xFFCCCCCC);
    235     canvas->drawRect(blob->bounds().makeOffset(offset.fX, offset.fY), paint);
    236     paint.setColor(SK_ColorBLACK);
    237     canvas->drawTextBlob(blob, offset.fX, offset.fY, paint);
    238 }
    239 
    240 #include "Resources.h"
    241 #include "SkColorFilter.h"
    242 #include "SkVertices.h"
    243 
    244 static sk_sp<SkVertices> make_vertices(sk_sp<SkImage> image, const SkRect& r,
    245                                        SkColor color) {
    246     SkPoint pos[4];
    247     r.toQuad(pos);
    248     SkColor colors[4] = { color, color, color, color };
    249     return SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, 4,
    250                                 pos, pos, colors);
    251 }
    252 
    253 /*
    254  *  drawAtlas and drawVertices have several things in common:
    255  *  - can create compound "shaders", combining texture and colors
    256  *      - these are combined via an explicit blendmode
    257  *  - like drawImage, they only respect parts of the paint
    258  *      - colorfilter, imagefilter, blendmode, alpha
    259  *
    260  *  This GM produces a series of pairs of images (atlas | vertices).
    261  *  Each pair should look the same, and each set shows a different combination
    262  *  of alpha | colorFilter | mode
    263  */
    264 DEF_SIMPLE_GM(compare_atlas_vertices, canvas, 560, 585) {
    265     const SkRect tex = SkRect::MakeWH(128, 128);
    266     const SkRSXform xform = SkRSXform::Make(1, 0, 0, 0);
    267     const SkColor color = 0x884488CC;
    268 
    269     auto image = GetResourceAsImage("images/mandrill_128.png");
    270     auto verts = make_vertices(image, tex, color);
    271     const sk_sp<SkColorFilter> filters[] = {
    272         nullptr,
    273         SkColorFilter::MakeModeFilter(0xFF00FF88, SkBlendMode::kModulate),
    274     };
    275     const SkBlendMode modes[] = {
    276         SkBlendMode::kSrcOver,
    277         SkBlendMode::kPlus,
    278     };
    279 
    280     canvas->translate(10, 10);
    281     SkPaint paint;
    282     for (SkBlendMode mode : modes) {
    283         for (float alpha : { 1.0f, 0.5f }) {
    284             paint.setAlphaf(alpha);
    285             canvas->save();
    286             for (auto cf : filters) {
    287                 paint.setColorFilter(cf);
    288                 canvas->drawAtlas(image, &xform, &tex, &color, 1,
    289                                   mode, &tex, &paint);
    290                 canvas->translate(128, 0);
    291                 paint.setShader(image->makeShader());
    292                 canvas->drawVertices(verts, mode, paint);
    293                 paint.setShader(nullptr);
    294                 canvas->translate(145, 0);
    295             }
    296             canvas->restore();
    297             canvas->translate(0, 145);
    298         }
    299     }
    300 }
    301