// Verify that associated bitmap cache entries are purged on SkImage destruction. DEF_TEST(BitmapCache_discarded_image, reporter) { // Cache entries associated with SkImages fall into two categories: // // 1) generated image bitmaps (managed by the image cacherator) // 2) scaled/resampled bitmaps (cached when HQ filters are used) // // To exercise the first cache type, we use generated/picture-backed SkImages. // To exercise the latter, we draw scaled bitmap images using HQ filters. const SkMatrix xforms[] = { SkMatrix::MakeScale(1, 1), SkMatrix::MakeScale(1.7f, 0.5f), }; for (size_t i = 0; i < SK_ARRAY_COUNT(xforms); ++i) { test_discarded_image(reporter, xforms[i], []() { auto surface(SkSurface::MakeRasterN32Premul(10, 10)); surface->getCanvas()->clear(SK_ColorCYAN); return surface->makeImageSnapshot(); }); test_discarded_image(reporter, xforms[i], []() { SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(10, 10); canvas->clear(SK_ColorCYAN); return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(), SkISize::Make(10, 10), nullptr, nullptr, SkImage::BitDepth::kU8, SkColorSpace::MakeSRGB()); }); } }
void onDraw(SkCanvas* canvas) override { SkPaint blurPaint; SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(5.0f, 5.0f)); blurPaint.setImageFilter(blur); const SkScalar tile_size = SkIntToScalar(128); SkRect bounds; if (!canvas->getClipBounds(&bounds)) { bounds.setEmpty(); } int ts = SkScalarCeilToInt(tile_size); SkImageInfo info = SkImageInfo::MakeN32Premul(ts, ts); auto tileSurface(canvas->makeSurface(info)); if (!tileSurface) { tileSurface = SkSurface::MakeRaster(info); } SkCanvas* tileCanvas = tileSurface->getCanvas(); for (SkScalar y = bounds.top(); y < bounds.bottom(); y += tile_size) { for (SkScalar x = bounds.left(); x < bounds.right(); x += tile_size) { tileCanvas->save(); tileCanvas->clear(0); tileCanvas->translate(-x, -y); SkRect rect = SkRect::MakeWH(WIDTH, HEIGHT); tileCanvas->saveLayer(&rect, &blurPaint); SkRRect rrect = SkRRect::MakeRectXY(rect.makeInset(20, 20), 25, 25); tileCanvas->clipRRect(rrect, SkRegion::kDifference_Op, true); SkPaint paint; tileCanvas->drawRect(rect, paint); tileCanvas->restore(); tileCanvas->restore(); canvas->drawImage(tileSurface->makeImageSnapshot().get(), x, y); } } }
static sk_sp<SkImage> render(const SkTextBlob* blob) { auto surf = SkSurface::MakeRasterN32Premul(SkScalarRoundToInt(blob->bounds().width()), SkScalarRoundToInt(blob->bounds().height())); if (!surf) { return nullptr; // bounds are empty? } surf->getCanvas()->clear(SK_ColorWHITE); surf->getCanvas()->drawTextBlob(blob, -blob->bounds().left(), -blob->bounds().top(), SkPaint()); return surf->makeImageSnapshot(); }
void draw(SkCanvas* canvas) { SkDebugf("SkSurface::MakeNull(0, 0) %c= nullptr\n", SkSurface::MakeNull(0, 0) == nullptr ? '=' : '!'); const int w = 37; const int h = 1000; auto surf = SkSurface::MakeNull(w, h); auto nullCanvas = surf->getCanvas(); nullCanvas->drawPaint(SkPaint()); // does not crash, nothing draws SkDebugf("surf->makeImageSnapshot() %c= nullptr\n", surf->makeImageSnapshot() == nullptr ? '=' : '!'); }
static sk_sp<SkImage> make_image(SkCanvas* rootCanvas) { SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100); auto surface(rootCanvas->makeSurface(info)); if (!surface) { surface = SkSurface::MakeRaster(info); } SkPaint paint; paint.setAntiAlias(true); paint.setColor(SK_ColorRED); surface->getCanvas()->drawCircle(50, 50, 50, paint); return surface->makeImageSnapshot(); }
static void draw_zero_length_capped_paths(SkCanvas* canvas, bool aa) { canvas->translate(kCellPad, kCellPad); SkImageInfo info = canvas->imageInfo().makeWH(kCellWidth, kCellHeight); auto surface = canvas->makeSurface(info); if (!surface) { surface = SkSurface::MakeRasterN32Premul(kCellWidth, kCellHeight); } SkPaint paint; paint.setColor(SK_ColorWHITE); paint.setAntiAlias(aa); paint.setStyle(SkPaint::kStroke_Style); int numFailedTests = 0; for (auto cap : kCaps) { for (auto width : kWidths) { paint.setStrokeCap(cap); paint.setStrokeWidth(width); canvas->save(); for (auto verb : kAllVerbs) { SkString pathStr; pathStr.appendf("M %f %f ", (kCellWidth - 1) * 0.5f, (kCellHeight - 1) * 0.5f); if (verb) { pathStr.append(verb); } SkPath path; SkParsePath::FromSVGString(pathStr.c_str(), &path); surface->getCanvas()->clear(SK_ColorTRANSPARENT); surface->getCanvas()->drawPath(path, paint); auto img = surface->makeImageSnapshot(); // All cases should draw one cap, except for butt capped, and dangling moves // (without a verb or close), which shouldn't draw anything. int expectedCaps = ((SkPaint::kButt_Cap == cap) || !verb) ? 0 : 1; if (!draw_path_cell(canvas, img.get(), expectedCaps)) { ++numFailedTests; } canvas->translate(kCellWidth + kCellPad, 0); } canvas->restore(); canvas->translate(0, kCellHeight + kCellPad); } } canvas->drawColor(numFailedTests > 0 ? kFailureRed : kSuccessGreen); }
DEF_TEST(RecordDraw_drawImage, r){ class SkCanvasMock : public SkCanvas { public: SkCanvasMock(int width, int height) : SkCanvas(width, height) { this->resetTestValues(); } void onDrawImage(const SkImage* image, SkScalar left, SkScalar top, const SkPaint* paint) override { fDrawImageCalled = true; } void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, const SkPaint* paint, SrcRectConstraint) override { fDrawImageRectCalled = true; } void resetTestValues() { fDrawImageCalled = fDrawImageRectCalled = false; } bool fDrawImageCalled; bool fDrawImageRectCalled; }; auto surface(SkSurface::MakeRasterN32Premul(10, 10)); surface->getCanvas()->clear(SK_ColorGREEN); sk_sp<SkImage> image(surface->makeImageSnapshot()); SkCanvasMock canvas(10, 10); { SkRecord record; SkRecorder recorder(&record, 10, 10); recorder.drawImage(image, 0, 0); SkRecordDraw(record, &canvas, nullptr, nullptr, 0, nullptr, 0); } REPORTER_ASSERT(r, canvas.fDrawImageCalled); canvas.resetTestValues(); { SkRecord record; SkRecorder recorder(&record, 10, 10); recorder.drawImageRect(image, SkRect::MakeWH(10, 10), nullptr); SkRecordDraw(record, &canvas, nullptr, nullptr, 0, nullptr, 0); } REPORTER_ASSERT(r, canvas.fDrawImageRectCalled); }
void onPerCanvasPreDraw(SkCanvas* canvas) override { auto ii = SkImageInfo::Make(fImageSize, fImageSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr); SkRandom random; for (int i = 0; i < kNumImages; ++i) { auto surf = canvas->makeSurface(ii); SkColor color = random.nextU(); surf->getCanvas()->clear(color); SkPaint paint; paint.setColor(~color); paint.setBlendMode(SkBlendMode::kSrc); surf->getCanvas()->drawRect(SkRect::MakeLTRB(3, 3, fImageSize - 3, fImageSize - 3), paint); fImages[i] = surf->makeImageSnapshot(); } }
// Tests that MIP maps are created and invalidated as expected when drawing to and from GrTextures. DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrTextureMipMapInvalidationTest, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); if (!context->priv().caps()->mipMapSupport()) { return; } auto isMipped = [] (SkSurface* surf) { const GrTexture* texture = surf->makeImageSnapshot()->getTexture(); return GrMipMapped::kYes == texture->texturePriv().mipMapped(); }; auto mipsAreDirty = [] (SkSurface* surf) { return surf->makeImageSnapshot()->getTexture()->texturePriv().mipMapsAreDirty(); }; auto info = SkImageInfo::MakeN32Premul(256, 256); for (auto allocateMips : {false, true}) { auto surf1 = SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info, 0, kBottomLeft_GrSurfaceOrigin, nullptr, allocateMips); auto surf2 = SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info); // Draw something just in case we ever had a solid color optimization surf1->getCanvas()->drawCircle(128, 128, 50, SkPaint()); surf1->flush(); // No mipmaps initially REPORTER_ASSERT(reporter, isMipped(surf1.get()) == allocateMips); // Painting with downscale and medium filter quality should result in mipmap creation // Flush the context rather than the canvas as flushing the canvas triggers MIP level // generation. SkPaint paint; paint.setFilterQuality(kMedium_SkFilterQuality); surf2->getCanvas()->scale(0.2f, 0.2f); surf2->getCanvas()->drawImage(surf1->makeImageSnapshot(), 0, 0, &paint); context->flush(); REPORTER_ASSERT(reporter, isMipped(surf1.get()) == allocateMips); REPORTER_ASSERT(reporter, !allocateMips || !mipsAreDirty(surf1.get())); // Changing the contents of the surface should invalidate the mipmap, but not de-allocate surf1->getCanvas()->drawCircle(128, 128, 100, SkPaint()); context->flush(); REPORTER_ASSERT(reporter, isMipped(surf1.get()) == allocateMips); REPORTER_ASSERT(reporter, mipsAreDirty(surf1.get())); } }
static sk_sp<SkImage> make_image(SkCanvas* rootCanvas) { static constexpr SkScalar kSize = 50; SkImageInfo info = SkImageInfo::MakeN32Premul(kSize, kSize); auto surface = ToolUtils::makeSurface(rootCanvas, info); SkPaint p; p.setAntiAlias(true); p.setColor(SK_ColorGREEN); surface->getCanvas()->drawCircle(kSize / 2, kSize / 2, kSize / 2, p); p.setStyle(SkPaint::kStroke_Style); p.setColor(SK_ColorRED); surface->getCanvas()->drawLine(kSize * .25f, kSize * .50f, kSize * .75f, kSize * .50f, p); surface->getCanvas()->drawLine(kSize * .50f, kSize * .25f, kSize * .50f, kSize * .75f, p); return surface->makeImageSnapshot(); }
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ReimportImageTextureWithMipLevels, reporter, ctxInfo) { auto* ctx = ctxInfo.grContext(); if (!ctx->priv().caps()->mipMapSupport()) { return; } static constexpr auto kCreateWithMipMaps = true; auto surf = SkSurface::MakeRenderTarget( ctx, SkBudgeted::kYes, SkImageInfo::Make(100, 100, kRGBA_8888_SkColorType, kPremul_SkAlphaType), 1, kTopLeft_GrSurfaceOrigin, nullptr, kCreateWithMipMaps); if (!surf) { return; } surf->getCanvas()->drawColor(SK_ColorDKGRAY); auto img = surf->makeImageSnapshot(); if (!img) { return; } surf.reset(); GrBackendTexture btex; SkImage::BackendTextureReleaseProc texRelease; if (!SkImage::MakeBackendTextureFromSkImage(ctx, std::move(img), &btex, &texRelease)) { // Not all backends support stealing textures yet. // ERRORF(reporter, "Could not turn image into texture"); return; } REPORTER_ASSERT(reporter, btex.hasMipMaps()); // Reimport the texture as an image and perform a downsampling draw with medium quality which // should use the upper MIP levels. img = SkImage::MakeFromTexture(ctx, btex, kTopLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr); const auto singlePixelInfo = SkImageInfo::Make(1, 1, kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr); surf = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kYes, singlePixelInfo, 1, kTopLeft_GrSurfaceOrigin, nullptr); SkPaint paint; paint.setFilterQuality(kMedium_SkFilterQuality); surf->getCanvas()->drawImageRect(img, SkRect::MakeWH(1, 1), &paint); uint32_t pixel; surf->readPixels(singlePixelInfo, &pixel, sizeof(uint32_t), 0, 0); REPORTER_ASSERT(reporter, pixel == SkPreMultiplyColor(SK_ColorDKGRAY)); img.reset(); texRelease(btex); }
DEF_TEST(Recorder_drawImage_takeReference, reporter) { sk_sp<SkImage> image; { auto surface(SkSurface::MakeRasterN32Premul(100, 100)); surface->getCanvas()->clear(SK_ColorGREEN); image = surface->makeImageSnapshot(); } { SkRecord record; SkRecorder recorder(&record, 100, 100); // DrawImage is supposed to take a reference recorder.drawImage(image, 0, 0); REPORTER_ASSERT(reporter, !image->unique()); Tally tally; tally.apply(record); REPORTER_ASSERT(reporter, 1 == tally.count<SkRecords::DrawImage>()); } REPORTER_ASSERT(reporter, image->unique()); { SkRecord record; SkRecorder recorder(&record, 100, 100); // DrawImageRect is supposed to take a reference recorder.drawImageRect(image, SkRect::MakeWH(100, 100), nullptr); REPORTER_ASSERT(reporter, !image->unique()); Tally tally; tally.apply(record); REPORTER_ASSERT(reporter, 1 == tally.count<SkRecords::DrawImageRect>()); } REPORTER_ASSERT(reporter, image->unique()); }
static sk_sp<SkImage> MakeAtlas(SkCanvas* caller, const SkRect& target) { SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100); auto surface(caller->makeSurface(info)); if (nullptr == surface) { surface = SkSurface::MakeRaster(info); } SkCanvas* canvas = surface->getCanvas(); // draw red everywhere, but we don't expect to see it in the draw, testing the notion // that drawAtlas draws a subset-region of the atlas. canvas->clear(SK_ColorRED); SkPaint paint; paint.setBlendMode(SkBlendMode::kClear); SkRect r(target); r.inset(-1, -1); // zero out a place (with a 1-pixel border) to land our drawing. canvas->drawRect(r, paint); paint.setBlendMode(SkBlendMode::kSrcOver); paint.setColor(SK_ColorBLUE); paint.setAntiAlias(true); canvas->drawOval(target, paint); return surface->makeImageSnapshot(); }
// Tests that MIP maps are created and invalidated as expected when drawing to and from GrTextures. DEF_GPUTEST_FOR_NULLGL_CONTEXT(GrTextureMipMapInvalidationTest, reporter, ctxInfo) { auto isMipped = [] (SkSurface* surf) { const GrTexture* texture = surf->makeImageSnapshot()->getTexture(); return GrMipMapped::kYes == texture->texturePriv().mipMapped(); }; auto mipsAreDirty = [] (SkSurface* surf) { return surf->makeImageSnapshot()->getTexture()->texturePriv().mipMapsAreDirty(); }; GrContext* context = ctxInfo.grContext(); auto info = SkImageInfo::MakeN32Premul(256, 256); auto surf1 = SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info); auto surf2 = SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info); // Draw something just in case we ever had a solid color optimization surf1->getCanvas()->drawCircle(128, 128, 50, SkPaint()); surf1->getCanvas()->flush(); // No mipmaps initially REPORTER_ASSERT(reporter, !isMipped(surf1.get())); // Painting with downscale and medium filter quality should result in mipmap creation SkPaint paint; paint.setFilterQuality(kMedium_SkFilterQuality); surf2->getCanvas()->scale(0.2f, 0.2f); surf2->getCanvas()->drawImage(surf1->makeImageSnapshot(), 0, 0, &paint); surf2->getCanvas()->flush(); REPORTER_ASSERT(reporter, isMipped(surf1.get())); REPORTER_ASSERT(reporter, !mipsAreDirty(surf1.get())); // Changing the contents of the surface should invalidate the mipmap, but not de-allocate surf1->getCanvas()->drawCircle(128, 128, 100, SkPaint()); surf1->getCanvas()->flush(); REPORTER_ASSERT(reporter, isMipped(surf1.get())); REPORTER_ASSERT(reporter, mipsAreDirty(surf1.get())); }
static void draw_zero_length_capped_paths_dbl_contour(SkCanvas* canvas, bool aa) { canvas->translate(kCellPad, kCellPad); SkImageInfo info = canvas->imageInfo().makeWH(kCellWidth, kCellHeight); auto surface = canvas->makeSurface(info); if (!surface) { surface = SkSurface::MakeRasterN32Premul(kCellWidth, kCellHeight); } SkPaint paint; paint.setColor(SK_ColorWHITE); paint.setAntiAlias(aa); paint.setStyle(SkPaint::kStroke_Style); int numFailedTests = 0; for (auto cap : kCaps) { for (auto width : kWidths) { paint.setStrokeCap(cap); paint.setStrokeWidth(width); canvas->save(); for (auto firstVerb : kSomeVerbs) { for (auto secondVerb : kSomeVerbs) { int expectedCaps = 0; SkString pathStr; pathStr.append("M 9.5 9.5 "); if (firstVerb) { pathStr.append(firstVerb); ++expectedCaps; } pathStr.append("M 40.5 9.5 "); if (secondVerb) { pathStr.append(secondVerb); ++expectedCaps; } SkPath path; SkParsePath::FromSVGString(pathStr.c_str(), &path); surface->getCanvas()->clear(SK_ColorTRANSPARENT); surface->getCanvas()->drawPath(path, paint); auto img = surface->makeImageSnapshot(); if (SkPaint::kButt_Cap == cap) { expectedCaps = 0; } if (!draw_path_cell(canvas, img.get(), expectedCaps)) { ++numFailedTests; } canvas->translate(kCellWidth + kCellPad, 0); } } canvas->restore(); canvas->translate(0, kCellHeight + kCellPad); } } canvas->drawColor(numFailedTests > 0 ? kFailureRed : kSuccessGreen); }
static sk_sp<SkImage> picture_to_image(sk_sp<SkPicture> pic) { SkIRect r = pic->cullRect().round(); auto surf = SkSurface::MakeRasterN32Premul(r.width(), r.height()); surf->getCanvas()->drawPicture(pic); return surf->makeImageSnapshot(); }
static sk_sp<SkImage> makebm(SkCanvas* origCanvas, SkBitmap* resultBM, int w, int h) { SkImageInfo info = SkImageInfo::MakeN32Premul(w, h); auto surface(origCanvas->makeSurface(info)); if (nullptr == surface) { // picture canvas will return null, so fall-back to raster surface = SkSurface::MakeRaster(info); } SkCanvas* canvas = surface->getCanvas(); canvas->clear(SK_ColorTRANSPARENT); SkScalar wScalar = SkIntToScalar(w); SkScalar hScalar = SkIntToScalar(h); SkPoint pt = { wScalar / 2, hScalar / 2 }; SkScalar radius = 4 * SkMaxScalar(wScalar, hScalar); SkColor colors[] = { SK_ColorRED, SK_ColorYELLOW, SK_ColorGREEN, SK_ColorMAGENTA, SK_ColorBLUE, SK_ColorCYAN, SK_ColorRED}; SkScalar pos[] = {0, SK_Scalar1 / 6, 2 * SK_Scalar1 / 6, 3 * SK_Scalar1 / 6, 4 * SK_Scalar1 / 6, 5 * SK_Scalar1 / 6, SK_Scalar1}; SkPaint paint; SkRect rect = SkRect::MakeWH(wScalar, hScalar); SkMatrix mat = SkMatrix::I(); for (int i = 0; i < 4; ++i) { paint.setShader(SkGradientShader::MakeRadial( pt, radius, colors, pos, SK_ARRAY_COUNT(colors), SkShader::kRepeat_TileMode, 0, &mat)); canvas->drawRect(rect, paint); rect.inset(wScalar / 8, hScalar / 8); mat.postScale(SK_Scalar1 / 4, SK_Scalar1 / 4); } auto image = surface->makeImageSnapshot(); SkBitmap tempBM; #if SK_SUPPORT_GPU if (GrTexture* texture = as_IB(image)->peekTexture()) { GrWrapTextureInBitmap(texture, image->width(), image->height(), image->isOpaque(), &tempBM); } else #endif { image->asLegacyBitmap(&tempBM, SkImage::kRO_LegacyBitmapMode); } // Let backends know we won't change this, so they don't have to deep copy it defensively. tempBM.setImmutable(); *resultBM = tempBM; return image; }
void onDraw(SkCanvas* canvas) override{ SkPaint blackFill; //----------- // Normal paints (no source) SkTArray<SkPaint> paints; create_paints(nullptr, &paints); //----------- // Paints with a PictureImageFilter as a source sk_sp<SkPicture> pic; { SkPictureRecorder rec; SkCanvas* c = rec.beginRecording(10, 10); c->drawRect(SkRect::MakeWH(10, 10), blackFill); pic = rec.finishRecordingAsPicture(); } SkAutoTUnref<SkImageFilter> pif(SkPictureImageFilter::Create(pic.get())); SkTArray<SkPaint> pifPaints; create_paints(pif, &pifPaints); //----------- // Paints with a SkImageSource as a source auto surface(SkSurface::MakeRasterN32Premul(10, 10)); { SkPaint p; SkCanvas* temp = surface->getCanvas(); temp->clear(SK_ColorYELLOW); p.setColor(SK_ColorBLUE); temp->drawRect(SkRect::MakeLTRB(5, 5, 10, 10), p); p.setColor(SK_ColorGREEN); temp->drawRect(SkRect::MakeLTRB(5, 0, 10, 5), p); } sk_sp<SkImage> image(surface->makeImageSnapshot()); SkAutoTUnref<SkImageFilter> imageSource(SkImageSource::Create(image.get())); SkTArray<SkPaint> bmsPaints; create_paints(imageSource, &bmsPaints); //----------- SkASSERT(paints.count() == kNumVertTiles); SkASSERT(paints.count() == pifPaints.count()); SkASSERT(paints.count() == bmsPaints.count()); // horizontal separators for (int i = 1; i < paints.count(); ++i) { canvas->drawLine(0, i*SkIntToScalar(kTileHeight), SkIntToScalar((SK_ARRAY_COUNT(gDrawMthds) + kNumXtraCols)*kTileWidth), i*SkIntToScalar(kTileHeight), blackFill); } // vertical separators for (int i = 0; i < (int)SK_ARRAY_COUNT(gDrawMthds) + kNumXtraCols; ++i) { canvas->drawLine(SkIntToScalar(i * kTileWidth), 0, SkIntToScalar(i * kTileWidth), SkIntToScalar(paints.count() * kTileWidth), blackFill); } // A column of saveLayers with PictureImageFilters for (int i = 0; i < pifPaints.count(); ++i) { draw_savelayer_with_paint(SkIPoint::Make(0, i*kTileHeight), canvas, pifPaints[i]); } // A column of saveLayers with BitmapSources for (int i = 0; i < pifPaints.count(); ++i) { draw_savelayer_with_paint(SkIPoint::Make(kTileWidth, i*kTileHeight), canvas, bmsPaints[i]); } // Multiple columns with different geometry for (int i = 0; i < (int)SK_ARRAY_COUNT(gDrawMthds); ++i) { for (int j = 0; j < paints.count(); ++j) { draw_geom_with_paint(*gDrawMthds[i], SkIPoint::Make((i+kNumXtraCols) * kTileWidth, j*kTileHeight), canvas, paints[j]); } } }
virtual void onDraw(SkCanvas* inputCanvas) override { SkScalar textSizes[] = { 9.0f, 9.0f*2.0f, 9.0f*5.0f, 9.0f*2.0f*5.0f }; SkScalar scales[] = { 2.0f*5.0f, 5.0f, 2.0f, 1.0f }; // set up offscreen rendering with distance field text GrContext* ctx = inputCanvas->getGrContext(); SkISize size = onISize(); SkImageInfo info = SkImageInfo::MakeN32(size.width(), size.height(), kPremul_SkAlphaType, inputCanvas->imageInfo().refColorSpace()); SkSurfaceProps props(SkSurfaceProps::kUseDeviceIndependentFonts_Flag, SkSurfaceProps::kLegacyFontHost_InitType); auto surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info, 0, &props)); SkCanvas* canvas = surface ? surface->getCanvas() : inputCanvas; // init our new canvas with the old canvas's matrix canvas->setMatrix(inputCanvas->getTotalMatrix()); // apply global scale to test glyph positioning canvas->scale(1.05f, 1.05f); canvas->clear(0xffffffff); SkPaint paint; paint.setAntiAlias(true); SkFont font(ToolUtils::create_portable_typeface("serif", SkFontStyle())); font.setSubpixel(true); const char* text = "Hamburgefons"; const size_t textLen = strlen(text); // check scaling up SkScalar x = SkIntToScalar(0); SkScalar y = SkIntToScalar(78); for (size_t i = 0; i < SK_ARRAY_COUNT(textSizes); ++i) { SkAutoCanvasRestore acr(canvas, true); canvas->translate(x, y); canvas->scale(scales[i], scales[i]); font.setSize(textSizes[i]); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 0, 0, font, paint); y += font.getMetrics(nullptr)*scales[i]; } // check rotation for (size_t i = 0; i < 5; ++i) { SkScalar rotX = SkIntToScalar(10); SkScalar rotY = y; SkAutoCanvasRestore acr(canvas, true); canvas->translate(SkIntToScalar(10 + i * 200), -80); canvas->rotate(SkIntToScalar(i * 5), rotX, rotY); for (int ps = 6; ps <= 32; ps += 3) { font.setSize(SkIntToScalar(ps)); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, rotX, rotY, font, paint); rotY += font.getMetrics(nullptr); } } // check scaling down font.setEdging(SkFont::Edging::kSubpixelAntiAlias); x = SkIntToScalar(680); y = SkIntToScalar(20); size_t arraySize = SK_ARRAY_COUNT(textSizes); for (size_t i = 0; i < arraySize; ++i) { SkAutoCanvasRestore acr(canvas, true); canvas->translate(x, y); SkScalar scaleFactor = SkScalarInvert(scales[arraySize - i - 1]); canvas->scale(scaleFactor, scaleFactor); font.setSize(textSizes[i]); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 0, 0, font, paint); y += font.getMetrics(nullptr)*scaleFactor; } // check pos text { SkAutoCanvasRestore acr(canvas, true); canvas->scale(2.0f, 2.0f); SkAutoTArray<SkGlyphID> glyphs(SkToInt(textLen)); int count = font.textToGlyphs(text, textLen, SkTextEncoding::kUTF8, glyphs.get(), textLen); SkAutoTArray<SkPoint> pos(count); font.setSize(textSizes[0]); font.getPos(glyphs.get(), count, pos.get(), {340, 75}); auto blob = SkTextBlob::MakeFromPosText(glyphs.get(), count * sizeof(SkGlyphID), pos.get(), font, SkTextEncoding::kGlyphID); canvas->drawTextBlob(blob, 0, 0, paint); } // check gamma-corrected blending const SkColor fg[] = { 0xFFFFFFFF, 0xFFFFFF00, 0xFFFF00FF, 0xFF00FFFF, 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFF000000, }; paint.setColor(0xFFF7F3F7); SkRect r = SkRect::MakeLTRB(670, 215, 820, 397); canvas->drawRect(r, paint); x = SkIntToScalar(680); y = SkIntToScalar(235); font.setSize(SkIntToScalar(19)); for (size_t i = 0; i < SK_ARRAY_COUNT(fg); ++i) { paint.setColor(fg[i]); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, x, y, font, paint); y += font.getMetrics(nullptr); } paint.setColor(0xFF181C18); r = SkRect::MakeLTRB(820, 215, 970, 397); canvas->drawRect(r, paint); x = SkIntToScalar(830); y = SkIntToScalar(235); font.setSize(SkIntToScalar(19)); for (size_t i = 0; i < SK_ARRAY_COUNT(fg); ++i) { paint.setColor(fg[i]); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, x, y, font, paint); y += font.getMetrics(nullptr); } // check skew { font.setEdging(SkFont::Edging::kAntiAlias); SkAutoCanvasRestore acr(canvas, true); canvas->skew(0.0f, 0.151515f); font.setSize(SkIntToScalar(32)); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 745, 70, font, paint); } { font.setEdging(SkFont::Edging::kSubpixelAntiAlias); SkAutoCanvasRestore acr(canvas, true); canvas->skew(0.5f, 0.0f); font.setSize(SkIntToScalar(32)); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 580, 125, font, paint); } // check perspective { font.setEdging(SkFont::Edging::kAntiAlias); SkAutoCanvasRestore acr(canvas, true); SkMatrix persp; persp.setAll(0.9839f, 0, 0, 0.2246f, 0.6829f, 0, 0.0002352f, -0.0003844f, 1); canvas->concat(persp); canvas->translate(1100, -295); font.setSize(37.5f); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 0, 0, font, paint); } { font.setSubpixel(false); font.setEdging(SkFont::Edging::kAlias); SkAutoCanvasRestore acr(canvas, true); SkMatrix persp; persp.setAll(0.9839f, 0, 0, 0.2246f, 0.6829f, 0, 0.0002352f, -0.0003844f, 1); canvas->concat(persp); canvas->translate(1075, -245); canvas->scale(375, 375); font.setSize(0.1f); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 0, 0, font, paint); } // check color emoji if (fEmojiTypeface) { SkFont emoiFont; emoiFont.setSubpixel(true); emoiFont.setTypeface(fEmojiTypeface); emoiFont.setSize(SkIntToScalar(19)); canvas->drawSimpleText(fEmojiText, strlen(fEmojiText), SkTextEncoding::kUTF8, 670, 90, emoiFont, paint); } // render offscreen buffer if (surface) { SkAutoCanvasRestore acr(inputCanvas, true); // since we prepended this matrix already, we blit using identity inputCanvas->resetMatrix(); inputCanvas->drawImage(surface->makeImageSnapshot().get(), 0, 0, nullptr); } }
void SetImageShader(SkPaint* paint, int imageWidth, int imageHeight, SkShader::TileMode xTile, SkShader::TileMode yTile) { auto surface = SkSurface::MakeRasterN32Premul(imageWidth, imageHeight); paint->setShader(SkImageShader::Make(surface->makeImageSnapshot(), xTile, yTile, nullptr)); }