sk_tool_utils.cpp revision 483c772cfdd646fad3ae8aa187136191ae3babdc
1/*
2 * Copyright 2014 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 "sk_tool_utils.h"
9
10#include "Resources.h"
11#include "SkBitmap.h"
12#include "SkCanvas.h"
13#include "SkImage.h"
14#include "SkPixelRef.h"
15#include "SkPM4f.h"
16#include "SkPoint3.h"
17#include "SkShader.h"
18#include "SkSurface.h"
19#include "SkTestScalerContext.h"
20#include "SkTextBlob.h"
21
22namespace sk_tool_utils {
23
24const char* alphatype_name(SkAlphaType at) {
25    switch (at) {
26        case kUnknown_SkAlphaType:  return "Unknown";
27        case kOpaque_SkAlphaType:   return "Opaque";
28        case kPremul_SkAlphaType:   return "Premul";
29        case kUnpremul_SkAlphaType: return "Unpremul";
30    }
31    SkASSERT(false);
32    return "unexpected alphatype";
33}
34
35const char* colortype_name(SkColorType ct) {
36    switch (ct) {
37        case kUnknown_SkColorType:      return "Unknown";
38        case kAlpha_8_SkColorType:      return "Alpha_8";
39        case kRGB_565_SkColorType:      return "RGB_565";
40        case kARGB_4444_SkColorType:    return "ARGB_4444";
41        case kRGBA_8888_SkColorType:    return "RGBA_8888";
42        case kRGB_888x_SkColorType:     return "RGB_888x";
43        case kBGRA_8888_SkColorType:    return "BGRA_8888";
44        case kRGBA_1010102_SkColorType: return "RGBA_1010102";
45        case kRGB_101010x_SkColorType:  return "RGB_101010x";
46        case kGray_8_SkColorType:       return "Gray_8";
47        case kRGBA_F16_SkColorType:     return "RGBA_F16";
48    }
49    SkASSERT(false);
50    return "unexpected colortype";
51}
52
53SkColor color_to_565(SkColor color) {
54    SkPMColor pmColor = SkPreMultiplyColor(color);
55    U16CPU color16 = SkPixel32ToPixel16(pmColor);
56    return SkPixel16ToColor(color16);
57}
58
59void write_pixels(SkCanvas* canvas, const SkBitmap& bitmap, int x, int y,
60                  SkColorType colorType, SkAlphaType alphaType) {
61    SkBitmap tmp(bitmap);
62    const SkImageInfo info = SkImageInfo::Make(tmp.width(), tmp.height(), colorType, alphaType);
63
64    canvas->writePixels(info, tmp.getPixels(), tmp.rowBytes(), x, y);
65}
66
67void write_pixels(SkSurface* surface, const SkBitmap& src, int x, int y,
68                  SkColorType colorType, SkAlphaType alphaType) {
69    const SkImageInfo info = SkImageInfo::Make(src.width(), src.height(), colorType, alphaType);
70    surface->writePixels({info, src.getPixels(), src.rowBytes()}, x, y);
71}
72
73sk_sp<SkShader> create_checkerboard_shader(SkColor c1, SkColor c2, int size) {
74    SkBitmap bm;
75    bm.allocPixels(SkImageInfo::MakeS32(2 * size, 2 * size, kPremul_SkAlphaType));
76    bm.eraseColor(c1);
77    bm.eraseArea(SkIRect::MakeLTRB(0, 0, size, size), c2);
78    bm.eraseArea(SkIRect::MakeLTRB(size, size, 2 * size, 2 * size), c2);
79    return SkShader::MakeBitmapShader(
80            bm, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);
81}
82
83SkBitmap create_checkerboard_bitmap(int w, int h, SkColor c1, SkColor c2, int checkSize) {
84    SkBitmap bitmap;
85    bitmap.allocPixels(SkImageInfo::MakeS32(w, h, kPremul_SkAlphaType));
86    SkCanvas canvas(bitmap);
87
88    sk_tool_utils::draw_checkerboard(&canvas, c1, c2, checkSize);
89    return bitmap;
90}
91
92void draw_checkerboard(SkCanvas* canvas, SkColor c1, SkColor c2, int size) {
93    SkPaint paint;
94    paint.setShader(create_checkerboard_shader(c1, c2, size));
95    paint.setBlendMode(SkBlendMode::kSrc);
96    canvas->drawPaint(paint);
97}
98
99SkBitmap create_string_bitmap(int w, int h, SkColor c, int x, int y,
100                              int textSize, const char* str) {
101    SkBitmap bitmap;
102    bitmap.allocN32Pixels(w, h);
103    SkCanvas canvas(bitmap);
104
105    SkPaint paint;
106    paint.setAntiAlias(true);
107    sk_tool_utils::set_portable_typeface(&paint);
108    paint.setColor(c);
109    paint.setTextSize(SkIntToScalar(textSize));
110
111    canvas.clear(0x00000000);
112    canvas.drawString(str, SkIntToScalar(x), SkIntToScalar(y), paint);
113
114    // Tag data as sRGB (without doing any color space conversion). Color-space aware configs
115    // will process this correctly but legacy configs will render as if this returned N32.
116    SkBitmap result;
117    result.setInfo(SkImageInfo::MakeS32(w, h, kPremul_SkAlphaType));
118    result.setPixelRef(sk_ref_sp(bitmap.pixelRef()), 0, 0);
119    return result;
120}
121
122void add_to_text_blob_w_len(SkTextBlobBuilder* builder, const char* text, size_t len,
123                            const SkPaint& origPaint, SkScalar x, SkScalar y) {
124    SkPaint paint(origPaint);
125    SkTDArray<uint16_t> glyphs;
126
127    glyphs.append(paint.textToGlyphs(text, len, nullptr));
128    paint.textToGlyphs(text, len, glyphs.begin());
129
130    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
131    const SkTextBlobBuilder::RunBuffer& run = builder->allocRun(paint, glyphs.count(), x, y,
132                                                                nullptr);
133    memcpy(run.glyphs, glyphs.begin(), glyphs.count() * sizeof(uint16_t));
134}
135
136void add_to_text_blob(SkTextBlobBuilder* builder, const char* text,
137                      const SkPaint& origPaint, SkScalar x, SkScalar y) {
138    add_to_text_blob_w_len(builder, text, strlen(text), origPaint, x, y);
139}
140
141SkPath make_star(const SkRect& bounds, int numPts, int step) {
142    SkPath path;
143    path.setFillType(SkPath::kEvenOdd_FillType);
144    path.moveTo(0,-1);
145    for (int i = 1; i < numPts; ++i) {
146        int idx = i*step;
147        SkScalar theta = idx * 2*SK_ScalarPI/numPts + SK_ScalarPI/2;
148        SkScalar x = SkScalarCos(theta);
149        SkScalar y = -SkScalarSin(theta);
150        path.lineTo(x, y);
151    }
152    path.transform(SkMatrix::MakeRectToRect(path.getBounds(), bounds, SkMatrix::kFill_ScaleToFit));
153    return path;
154}
155
156#if !defined(__clang__) && defined(_MSC_VER)
157    // MSVC takes ~2 minutes to compile this function with optimization.
158    // We don't really care to wait that long for this function.
159    #pragma optimize("", off)
160#endif
161void make_big_path(SkPath& path) {
162    #include "BigPathBench.inc"
163}
164
165static float gaussian2d_value(int x, int y, float sigma) {
166    // don't bother with the scale term since we're just going to normalize the
167    // kernel anyways
168    float temp = expf(-(x*x + y*y)/(2*sigma*sigma));
169    return temp;
170}
171
172static float* create_2d_kernel(float sigma, int* filterSize) {
173    // We will actually take 2*halfFilterSize+1 samples (i.e., our filter kernel
174    // sizes are always odd)
175    int halfFilterSize = SkScalarCeilToInt(6*sigma)/2;
176    int wh = *filterSize = 2*halfFilterSize + 1;
177
178    float* temp = new float[wh*wh];
179
180    float filterTot = 0.0f;
181    for (int yOff = 0; yOff < wh; ++yOff) {
182        for (int xOff = 0; xOff < wh; ++xOff) {
183            temp[yOff*wh+xOff] = gaussian2d_value(xOff-halfFilterSize, yOff-halfFilterSize, sigma);
184
185            filterTot += temp[yOff*wh+xOff];
186        }
187    }
188
189    // normalize the kernel
190    for (int yOff = 0; yOff < wh; ++yOff) {
191        for (int xOff = 0; xOff < wh; ++xOff) {
192            temp[yOff*wh+xOff] /= filterTot;
193        }
194    }
195
196    return temp;
197}
198
199static SkPMColor blur_pixel(const SkBitmap& bm, int x, int y, float* kernel, int wh) {
200    SkASSERT(wh & 0x1);
201
202    int halfFilterSize = (wh-1)/2;
203
204    float r = 0.0f, g = 0.0f, b = 0.0f;
205    for (int yOff = 0; yOff < wh; ++yOff) {
206        int ySamp = y + yOff - halfFilterSize;
207
208        if (ySamp < 0) {
209            ySamp = 0;
210        } else if (ySamp > bm.height()-1) {
211            ySamp = bm.height()-1;
212        }
213
214        for (int xOff = 0; xOff < wh; ++xOff) {
215            int xSamp = x + xOff - halfFilterSize;
216
217            if (xSamp < 0) {
218                xSamp = 0;
219            } else if (xSamp > bm.width()-1) {
220                xSamp = bm.width()-1;
221            }
222
223            float filter = kernel[yOff*wh + xOff];
224
225            SkPMColor c = *bm.getAddr32(xSamp, ySamp);
226
227            r += SkGetPackedR32(c) * filter;
228            g += SkGetPackedG32(c) * filter;
229            b += SkGetPackedB32(c) * filter;
230        }
231    }
232
233    U8CPU r8, g8, b8;
234
235    r8 = (U8CPU) (r+0.5f);
236    g8 = (U8CPU) (g+0.5f);
237    b8 = (U8CPU) (b+0.5f);
238
239    return SkPackARGB32(255, r8, g8, b8);
240}
241
242SkBitmap slow_blur(const SkBitmap& src, float sigma) {
243    SkBitmap dst;
244
245    dst.allocN32Pixels(src.width(), src.height(), true);
246
247    int wh;
248    std::unique_ptr<float[]> kernel(create_2d_kernel(sigma, &wh));
249
250    for (int y = 0; y < src.height(); ++y) {
251        for (int x = 0; x < src.width(); ++x) {
252            *dst.getAddr32(x, y) = blur_pixel(src, x, y, kernel.get(), wh);
253        }
254    }
255
256    return dst;
257}
258
259// compute the intersection point between the diagonal and the ellipse in the
260// lower right corner
261static SkPoint intersection(SkScalar w, SkScalar h) {
262    SkASSERT(w > 0.0f || h > 0.0f);
263
264    return SkPoint::Make(w / SK_ScalarSqrt2, h / SK_ScalarSqrt2);
265}
266
267// Use the intersection of the corners' diagonals with their ellipses to shrink
268// the bounding rect
269SkRect compute_central_occluder(const SkRRect& rr) {
270    const SkRect r = rr.getBounds();
271
272    SkScalar newL = r.fLeft, newT = r.fTop, newR = r.fRight, newB = r.fBottom;
273
274    SkVector radii = rr.radii(SkRRect::kUpperLeft_Corner);
275    if (!radii.isZero()) {
276        SkPoint p = intersection(radii.fX, radii.fY);
277
278        newL = SkTMax(newL, r.fLeft + radii.fX - p.fX);
279        newT = SkTMax(newT, r.fTop + radii.fY - p.fY);
280    }
281
282    radii = rr.radii(SkRRect::kUpperRight_Corner);
283    if (!radii.isZero()) {
284        SkPoint p = intersection(radii.fX, radii.fY);
285
286        newR = SkTMin(newR, r.fRight + p.fX - radii.fX);
287        newT = SkTMax(newT, r.fTop + radii.fY - p.fY);
288    }
289
290    radii = rr.radii(SkRRect::kLowerRight_Corner);
291    if (!radii.isZero()) {
292        SkPoint p = intersection(radii.fX, radii.fY);
293
294        newR = SkTMin(newR, r.fRight + p.fX - radii.fX);
295        newB = SkTMin(newB, r.fBottom - radii.fY + p.fY);
296    }
297
298    radii = rr.radii(SkRRect::kLowerLeft_Corner);
299    if (!radii.isZero()) {
300        SkPoint p = intersection(radii.fX, radii.fY);
301
302        newL = SkTMax(newL, r.fLeft + radii.fX - p.fX);
303        newB = SkTMin(newB, r.fBottom - radii.fY + p.fY);
304    }
305
306    return SkRect::MakeLTRB(newL, newT, newR, newB);
307}
308
309// The widest inset rect
310SkRect compute_widest_occluder(const SkRRect& rr) {
311    const SkRect& r = rr.getBounds();
312
313    const SkVector& ul = rr.radii(SkRRect::kUpperLeft_Corner);
314    const SkVector& ur = rr.radii(SkRRect::kUpperRight_Corner);
315    const SkVector& lr = rr.radii(SkRRect::kLowerRight_Corner);
316    const SkVector& ll = rr.radii(SkRRect::kLowerLeft_Corner);
317
318    SkScalar maxT = SkTMax(ul.fY, ur.fY);
319    SkScalar maxB = SkTMax(ll.fY, lr.fY);
320
321    return SkRect::MakeLTRB(r.fLeft, r.fTop + maxT, r.fRight, r.fBottom - maxB);
322
323}
324
325// The tallest inset rect
326SkRect compute_tallest_occluder(const SkRRect& rr) {
327    const SkRect& r = rr.getBounds();
328
329    const SkVector& ul = rr.radii(SkRRect::kUpperLeft_Corner);
330    const SkVector& ur = rr.radii(SkRRect::kUpperRight_Corner);
331    const SkVector& lr = rr.radii(SkRRect::kLowerRight_Corner);
332    const SkVector& ll = rr.radii(SkRRect::kLowerLeft_Corner);
333
334    SkScalar maxL = SkTMax(ul.fX, ll.fX);
335    SkScalar maxR = SkTMax(ur.fX, lr.fX);
336
337    return SkRect::MakeLTRB(r.fLeft + maxL, r.fTop, r.fRight - maxR, r.fBottom);
338}
339
340bool copy_to(SkBitmap* dst, SkColorType dstColorType, const SkBitmap& src) {
341    SkPixmap srcPM;
342    if (!src.peekPixels(&srcPM)) {
343        return false;
344    }
345
346    SkBitmap tmpDst;
347    SkImageInfo dstInfo = srcPM.info().makeColorType(dstColorType);
348    if (!tmpDst.setInfo(dstInfo)) {
349        return false;
350    }
351
352    if (!tmpDst.tryAllocPixels()) {
353        return false;
354    }
355
356    SkPixmap dstPM;
357    if (!tmpDst.peekPixels(&dstPM)) {
358        return false;
359    }
360
361    if (!srcPM.readPixels(dstPM)) {
362        return false;
363    }
364
365    dst->swap(tmpDst);
366    return true;
367}
368
369void copy_to_g8(SkBitmap* dst, const SkBitmap& src) {
370    SkASSERT(kBGRA_8888_SkColorType == src.colorType() ||
371             kRGBA_8888_SkColorType == src.colorType());
372
373    SkImageInfo grayInfo = src.info().makeColorType(kGray_8_SkColorType);
374    dst->allocPixels(grayInfo);
375    uint8_t* dst8 = (uint8_t*)dst->getPixels();
376    const uint32_t* src32 = (const uint32_t*)src.getPixels();
377
378    const int w = src.width();
379    const int h = src.height();
380    const bool isBGRA = (kBGRA_8888_SkColorType == src.colorType());
381    for (int y = 0; y < h; ++y) {
382        if (isBGRA) {
383            // BGRA
384            for (int x = 0; x < w; ++x) {
385                uint32_t s = src32[x];
386                dst8[x] = SkComputeLuminance((s >> 16) & 0xFF, (s >> 8) & 0xFF, s & 0xFF);
387            }
388        } else {
389            // RGBA
390            for (int x = 0; x < w; ++x) {
391                uint32_t s = src32[x];
392                dst8[x] = SkComputeLuminance(s & 0xFF, (s >> 8) & 0xFF, (s >> 16) & 0xFF);
393            }
394        }
395        src32 = (const uint32_t*)((const char*)src32 + src.rowBytes());
396        dst8 += dst->rowBytes();
397    }
398}
399
400    //////////////////////////////////////////////////////////////////////////////////////////////
401
402    static int scale255(float x) {
403        return sk_float_round2int(x * 255);
404    }
405
406    static unsigned diff(const SkColorType ct, const void* a, const void* b) {
407        int dr = 0,
408            dg = 0,
409            db = 0,
410            da = 0;
411        switch (ct) {
412            case kRGBA_8888_SkColorType:
413            case kBGRA_8888_SkColorType: {
414                SkPMColor c0 = *(const SkPMColor*)a;
415                SkPMColor c1 = *(const SkPMColor*)b;
416                dr = SkGetPackedR32(c0) - SkGetPackedR32(c1);
417                dg = SkGetPackedG32(c0) - SkGetPackedG32(c1);
418                db = SkGetPackedB32(c0) - SkGetPackedB32(c1);
419                da = SkGetPackedA32(c0) - SkGetPackedA32(c1);
420            } break;
421            case kRGB_565_SkColorType: {
422                uint16_t c0 = *(const uint16_t*)a;
423                uint16_t c1 = *(const uint16_t*)b;
424                dr = SkGetPackedR16(c0) - SkGetPackedR16(c1);
425                dg = SkGetPackedG16(c0) - SkGetPackedG16(c1);
426                db = SkGetPackedB16(c0) - SkGetPackedB16(c1);
427            } break;
428            case kARGB_4444_SkColorType: {
429                uint16_t c0 = *(const uint16_t*)a;
430                uint16_t c1 = *(const uint16_t*)b;
431                dr = SkGetPackedR4444(c0) - SkGetPackedR4444(c1);
432                dg = SkGetPackedG4444(c0) - SkGetPackedG4444(c1);
433                db = SkGetPackedB4444(c0) - SkGetPackedB4444(c1);
434                da = SkGetPackedA4444(c0) - SkGetPackedA4444(c1);
435            } break;
436            case kAlpha_8_SkColorType:
437            case kGray_8_SkColorType:
438                da = (const uint8_t*)a - (const uint8_t*)b;
439                break;
440            case kRGBA_F16_SkColorType: {
441                const SkPM4f* c0 = (const SkPM4f*)a;
442                const SkPM4f* c1 = (const SkPM4f*)b;
443                dr = scale255(c0->r() - c1->r());
444                dg = scale255(c0->g() - c1->g());
445                db = scale255(c0->b() - c1->b());
446                da = scale255(c0->a() - c1->a());
447            } break;
448            default:
449                return 0;
450        }
451        dr = SkAbs32(dr);
452        dg = SkAbs32(dg);
453        db = SkAbs32(db);
454        da = SkAbs32(da);
455        return SkMax32(dr, SkMax32(dg, SkMax32(db, da)));
456    }
457
458    bool equal_pixels(const SkPixmap& a, const SkPixmap& b, unsigned maxDiff,
459                      bool respectColorSpace) {
460        if (a.width() != b.width() ||
461            a.height() != b.height() ||
462            a.colorType() != b.colorType() ||
463            (respectColorSpace && (a.colorSpace() != b.colorSpace())))
464        {
465            return false;
466        }
467
468        for (int y = 0; y < a.height(); ++y) {
469            const char* aptr = (const char*)a.addr(0, y);
470            const char* bptr = (const char*)b.addr(0, y);
471            if (memcmp(aptr, bptr, a.width() * a.info().bytesPerPixel())) {
472                for (int x = 0; x < a.width(); ++x) {
473                    if (diff(a.colorType(), a.addr(x, y), b.addr(x, y)) > maxDiff) {
474                        return false;
475                    }
476                }
477            }
478            aptr += a.rowBytes();
479            bptr += b.rowBytes();
480        }
481        return true;
482    }
483
484    bool equal_pixels(const SkBitmap& bm0, const SkBitmap& bm1, unsigned maxDiff,
485                      bool respectColorSpaces) {
486        SkPixmap pm0, pm1;
487        return bm0.peekPixels(&pm0) && bm1.peekPixels(&pm1) &&
488               equal_pixels(pm0, pm1, maxDiff, respectColorSpaces);
489    }
490
491    bool equal_pixels(const SkImage* a, const SkImage* b, unsigned maxDiff,
492                      bool respectColorSpaces) {
493        // ensure that peekPixels will succeed
494        auto imga = a->makeRasterImage();
495        auto imgb = b->makeRasterImage();
496        a = imga.get();
497        b = imgb.get();
498
499        SkPixmap pm0, pm1;
500        return a->peekPixels(&pm0) && b->peekPixels(&pm1) &&
501               equal_pixels(pm0, pm1, maxDiff, respectColorSpaces);
502    }
503
504    sk_sp<SkSurface> makeSurface(SkCanvas* canvas, const SkImageInfo& info,
505                                 const SkSurfaceProps* props) {
506        auto surf = canvas->makeSurface(info, props);
507        if (!surf) {
508            surf = SkSurface::MakeRaster(info, props);
509        }
510        return surf;
511    }
512}  // namespace sk_tool_utils
513