static void convolve_gaussian(GrContext* context, const SkRect& srcRect, const SkRect& dstRect, GrTexture* texture, Gr1DKernelEffect::Direction direction, int radius, float sigma, bool cropToSrcRect) { float bounds[2] = { 0.0f, 1.0f }; if (!cropToSrcRect) { convolve_gaussian_pass(context, srcRect, dstRect, texture, direction, radius, sigma, false, bounds); return; } SkRect lowerSrcRect = srcRect, lowerDstRect = dstRect; SkRect middleSrcRect = srcRect, middleDstRect = dstRect; SkRect upperSrcRect = srcRect, upperDstRect = dstRect; SkScalar size; SkScalar rad = SkIntToScalar(radius); if (direction == Gr1DKernelEffect::kX_Direction) { bounds[0] = SkScalarToFloat(srcRect.left()) / texture->width(); bounds[1] = SkScalarToFloat(srcRect.right()) / texture->width(); size = srcRect.width(); lowerSrcRect.fRight = srcRect.left() + rad; lowerDstRect.fRight = dstRect.left() + rad; upperSrcRect.fLeft = srcRect.right() - rad; upperDstRect.fLeft = dstRect.right() - rad; middleSrcRect.inset(rad, 0); middleDstRect.inset(rad, 0); } else { bounds[0] = SkScalarToFloat(srcRect.top()) / texture->height(); bounds[1] = SkScalarToFloat(srcRect.bottom()) / texture->height(); size = srcRect.height(); lowerSrcRect.fBottom = srcRect.top() + rad; lowerDstRect.fBottom = dstRect.top() + rad; upperSrcRect.fTop = srcRect.bottom() - rad; upperDstRect.fTop = dstRect.bottom() - rad; middleSrcRect.inset(0, rad); middleDstRect.inset(0, rad); } if (radius >= size * SK_ScalarHalf) { // Blur radius covers srcRect; use bounds over entire draw convolve_gaussian_pass(context, srcRect, dstRect, texture, direction, radius, sigma, true, bounds); } else { // Draw upper and lower margins with bounds; middle without. convolve_gaussian_pass(context, lowerSrcRect, lowerDstRect, texture, direction, radius, sigma, true, bounds); convolve_gaussian_pass(context, upperSrcRect, upperDstRect, texture, direction, radius, sigma, true, bounds); convolve_gaussian_pass(context, middleSrcRect, middleDstRect, texture, direction, radius, sigma, false, bounds); } }
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); SkAutoTUnref<SkSurface> tileSurface(canvas->newSurface(info)); if (!tileSurface.get()) { tileSurface.reset(SkSurface::NewRaster(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); } } }
virtual void onDraw(SkCanvas* canvas) { SkPaint paint; paint.setImageFilter(SkBlurImageFilter::Create(fSigmaX, fSigmaY))->unref(); const SkScalar tile_size = SkIntToScalar(128); SkRect bounds; if (!canvas->getClipBounds(&bounds)) { bounds.setEmpty(); } for (SkScalar y = bounds.top(); y < bounds.bottom(); y += tile_size) { for (SkScalar x = bounds.left(); x < bounds.right(); x += tile_size) { canvas->save(); canvas->clipRect(SkRect::MakeXYWH(x, y, tile_size, tile_size)); canvas->saveLayer(NULL, &paint); const char* str[] = { "The quick", "brown fox", "jumped over", "the lazy dog.", }; SkPaint textPaint; textPaint.setAntiAlias(true); textPaint.setTextSize(SkIntToScalar(100)); int posY = 0; for (unsigned i = 0; i < SK_ARRAY_COUNT(str); i++) { posY += 100; canvas->drawText(str[i], strlen(str[i]), SkIntToScalar(0), SkIntToScalar(posY), textPaint); } canvas->restore(); canvas->restore(); } } }
void onDraw(const int loops, SkCanvas* canvas) override { SkPaint paint; paint.setAntiAlias(true); paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(2); if (fRound) { paint.setStrokeJoin(SkPaint::kRound_Join); } this->setupPaint(&paint); const SkRect r = fPath.getBounds(); switch (fAlign) { case kLeft_Align: canvas->translate(-r.left(), 0); break; case kMiddle_Align: break; case kRight_Align: canvas->translate(640 - r.right(), 0); break; } for (int i = 0; i < loops; i++) { canvas->drawPath(fPath, paint); } }
// test that we can draw an aa-rect at coordinates > 32K (bigger than fixedpoint) static void test_big_aa_rect(skiatest::Reporter* reporter) { SkBitmap output; SkPMColor pixel[1]; output.installPixels(SkImageInfo::MakeN32Premul(1, 1), pixel, 4); auto surf = SkSurface::MakeRasterN32Premul(300, 33300); SkCanvas* canvas = surf->getCanvas(); SkRect r = { 0, 33000, 300, 33300 }; int x = SkScalarRoundToInt(r.left()); int y = SkScalarRoundToInt(r.top()); // check that the pixel in question starts as transparent (by the surface) if (surf->readPixels(output, x, y)) { REPORTER_ASSERT(reporter, 0 == pixel[0]); } else { REPORTER_ASSERT(reporter, false, "readPixels failed"); } SkPaint paint; paint.setAntiAlias(true); paint.setColor(SK_ColorWHITE); canvas->drawRect(r, paint); // Now check that it is BLACK if (surf->readPixels(output, x, y)) { // don't know what swizzling PMColor did, but white should always // appear the same. REPORTER_ASSERT(reporter, 0xFFFFFFFF == pixel[0]); } else { REPORTER_ASSERT(reporter, false, "readPixels failed"); } }
static void draw_gradients(SkCanvas* canvas, sk_sp<SkShader> (*makeShader)(const SkPoint[2], const SkMatrix&), const SkPoint ptsArray[][2], int numImages) { // Use some nice prime numbers for the rectangle and matrix with // different scaling along the x and y axes (which is the bug this // test addresses, where incorrect order of operations mixed up the axes) SkRect rectGrad = { SkIntToScalar(43), SkIntToScalar(61), SkIntToScalar(181), SkIntToScalar(167) }; SkMatrix shaderMat; shaderMat.setScale(rectGrad.width(), rectGrad.height()); shaderMat.postTranslate(rectGrad.left(), rectGrad.top()); canvas->save(); for (int i = 0; i < numImages; i++) { // Advance line downwards if necessary. if (i % IMAGES_X == 0 && i != 0) { canvas->restore(); canvas->translate(0, TESTGRID_Y); canvas->save(); } SkPaint paint; paint.setShader(makeShader(*ptsArray, shaderMat)); canvas->drawRect(rectGrad, paint); // Advance to next position. canvas->translate(TESTGRID_X, 0); ptsArray++; } canvas->restore(); }
static void draw_bbox(SkCanvas* canvas, const SkRect& r) { SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); paint.setColor(0xFFFF7088); canvas->drawRect(r, paint); canvas->drawLine(r.left(), r.top(), r.right(), r.bottom(), paint); canvas->drawLine(r.left(), r.bottom(), r.right(), r.top(), paint); }
Json::Value SkJSONCanvas::makeRect(const SkRect& rect) { Json::Value result(Json::arrayValue); result.append(Json::Value(rect.left())); result.append(Json::Value(rect.top())); result.append(Json::Value(rect.right())); result.append(Json::Value(rect.bottom())); return result; }
PassRefPtr<JSONObject> LoggingCanvas::objectForSkRect(const SkRect& rect) { RefPtr<JSONObject> rectItem = JSONObject::create(); rectItem->setNumber("left", rect.left()); rectItem->setNumber("top", rect.top()); rectItem->setNumber("right", rect.right()); rectItem->setNumber("bottom", rect.bottom()); return rectItem.release(); }
virtual void onDraw(SkCanvas* canvas) { canvas->drawColor(SK_ColorGRAY); SkPaint paint; paint.setTypeface(fTypeface); const char* text = "hamburgerfons"; // draw text at different point sizes const int textSize[] = { 10, 30, 50 }; const int textYOffset[] = { 10, 40, 100}; SkASSERT(sizeof(textSize) == sizeof(textYOffset)); for (size_t y = 0; y < sizeof(textSize) / sizeof(int); ++y) { paint.setTextSize(SkIntToScalar(textSize[y])); canvas->drawText(text, strlen(text), 10, SkIntToScalar(textYOffset[y]), paint); } // setup work needed to draw text with different clips canvas->translate(10, 160); paint.setTextSize(40); // compute the bounds of the text SkRect bounds; paint.measureText(text, strlen(text), &bounds); const SkScalar boundsHalfWidth = bounds.width() * SK_ScalarHalf; const SkScalar boundsHalfHeight = bounds.height() * SK_ScalarHalf; const SkScalar boundsQuarterWidth = boundsHalfWidth * SK_ScalarHalf; const SkScalar boundsQuarterHeight = boundsHalfHeight * SK_ScalarHalf; SkRect upperLeftClip = SkRect::MakeXYWH(bounds.left(), bounds.top(), boundsHalfWidth, boundsHalfHeight); SkRect lowerRightClip = SkRect::MakeXYWH(bounds.centerX(), bounds.centerY(), boundsHalfWidth, boundsHalfHeight); SkRect interiorClip = bounds; interiorClip.inset(boundsQuarterWidth, boundsQuarterHeight); const SkRect clipRects[] = { bounds, upperLeftClip, lowerRightClip, interiorClip }; SkPaint clipHairline; clipHairline.setColor(SK_ColorWHITE); clipHairline.setStyle(SkPaint::kStroke_Style); for (size_t x = 0; x < sizeof(clipRects) / sizeof(SkRect); ++x) { canvas->save(); canvas->drawRect(clipRects[x], clipHairline); paint.setAlpha(0x20); canvas->drawText(text, strlen(text), 0, 0, paint); canvas->clipRect(clipRects[x]); paint.setAlpha(0xFF); canvas->drawText(text, strlen(text), 0, 0, paint); canvas->restore(); canvas->translate(0, bounds.height() + SkIntToScalar(25)); } }
static void draw_box_frame(SkCanvas* canvas, int width, int height) { SkPaint p; p.setStyle(SkPaint::kStroke_Style); p.setColor(SK_ColorRED); SkRect r = SkRect::MakeIWH(width, height); r.inset(0.5f, 0.5f); canvas->drawRect(r, p); canvas->drawLine(r.left(), r.top(), r.right(), r.bottom(), p); canvas->drawLine(r.left(), r.bottom(), r.right(), r.top(), p); }
void drawBorders(SkCanvas* canvas) { SkPaint p; p.setStyle(SkPaint::kStroke_Style); p.setColor(SK_ColorBLUE); SkRect r = SkRect::MakeWH(fCell.width() * 2, fCell.height() * 2); r.inset(SK_ScalarHalf, SK_ScalarHalf); canvas->drawRect(r, p); canvas->drawLine(r.left(), r.centerY(), r.right(), r.centerY(), p); canvas->drawLine(r.centerX(), r.top(), r.centerX(), r.bottom(), p); }
static sk_sp<SkShader> make_shader(const SkRect& bounds) { const SkPoint pts[] = { { bounds.left(), bounds.top() }, { bounds.right(), bounds.bottom() }, }; const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorBLACK, SK_ColorCYAN, SK_ColorMAGENTA, SK_ColorYELLOW, }; return SkGradientShader::MakeLinear(pts, colors, nullptr, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode); }
void writePathVertices(GrDrawBatch::Target* target, GrBatchAtlas* atlas, intptr_t offset, GrColor color, size_t vertexStride, const SkMatrix& viewMatrix, const ShapeData* shapeData) const { GrTexture* texture = atlas->getTexture(); SkScalar dx = shapeData->fBounds.fLeft; SkScalar dy = shapeData->fBounds.fTop; SkScalar width = shapeData->fBounds.width(); SkScalar height = shapeData->fBounds.height(); SkScalar invScale = 1.0f / shapeData->fScale; dx *= invScale; dy *= invScale; width *= invScale; height *= invScale; SkPoint* positions = reinterpret_cast<SkPoint*>(offset); // vertex positions // TODO make the vertex attributes a struct SkRect r = SkRect::MakeXYWH(dx, dy, width, height); positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertexStride); // colors for (int i = 0; i < kVerticesPerQuad; i++) { GrColor* colorPtr = (GrColor*)(offset + sizeof(SkPoint) + i * vertexStride); *colorPtr = color; } const SkScalar tx = SkIntToScalar(shapeData->fAtlasLocation.fX); const SkScalar ty = SkIntToScalar(shapeData->fAtlasLocation.fY); // vertex texture coords SkPoint* textureCoords = (SkPoint*)(offset + sizeof(SkPoint) + sizeof(GrColor)); textureCoords->setRectFan(tx / texture->width(), ty / texture->height(), (tx + shapeData->fBounds.width()) / texture->width(), (ty + shapeData->fBounds.height()) / texture->height(), vertexStride); }
static void draw_rect(SkCanvas* canvas, const SkRect& r, SkColor c, SkColorProfileType profile, const SkAlpha aa[]) { const SkIRect ir = r.round(); SkBitmap bm; bm.allocN32Pixels(ir.width(), ir.height()); bm.eraseColor(0xFFFFFFFF); SkPixmap pm; bm.peekPixels(&pm); uint32_t flags = 0; if (SkColorGetA(c) == 0xFF) { flags |= SkXfermode::kSrcIsOpaque_PM4fFlag; } if (kSRGB_SkColorProfileType == profile) { flags |= SkXfermode::kDstIsSRGB_PM4fFlag; } const SkXfermode::PM4fState state { nullptr, flags }; const SkPM4f src = SkColor4f::FromColor(c).premul(); auto proc1 = SkXfermode::GetPM4fProc1(SkXfermode::kSrcOver_Mode, flags); for (int y = 0; y < ir.height()/2; ++y) { proc1(state, pm.writable_addr32(0, y), src, ir.width(), aa); } SkPM4f srcRow[1000]; for (int i = 0; i < ir.width(); ++i) { srcRow[i] = src; } auto procN = SkXfermode::GetPM4fProcN(SkXfermode::kSrcOver_Mode, flags); // +1 to skip a row, so we can see the boundary between proc1 and procN for (int y = ir.height()/2 + 1; y < ir.height(); ++y) { procN(state, pm.writable_addr32(0, y), srcRow, ir.width(), aa); } canvas->drawBitmap(bm, r.left(), r.top(), nullptr); }
void advance(const SkRect& bounds) { fCenter += fVelocity; if (fCenter.fX > bounds.right()) { SkASSERT(fVelocity.fX > 0); fVelocity.fX = -fVelocity.fX; } else if (fCenter.fX < bounds.left()) { SkASSERT(fVelocity.fX < 0); fVelocity.fX = -fVelocity.fX; } if (fCenter.fY > bounds.bottom()) { if (fVelocity.fY > 0) { fVelocity.fY = -fVelocity.fY; } } else if (fCenter.fY < bounds.top()) { if (fVelocity.fY < 0) { fVelocity.fY = -fVelocity.fY; } } fScale += fDScale; if (fScale > 2 || fScale < SK_Scalar1/2) { fDScale = -fDScale; } fRadian += fDRadian; fRadian = SkScalarMod(fRadian, 2 * SK_ScalarPI); fAlpha += fDAlpha; if (fAlpha > 1) { fAlpha = 1; fDAlpha = -fDAlpha; } else if (fAlpha < 0) { fAlpha = 0; fDAlpha = -fDAlpha; } }
void TransparencyWin::computeLayerSize() { if (m_transformMode == Untransform) { // The meaning of the "transformed" source rect is a little ambigous // here. The rest of the code doesn't care about it in the Untransform // case since we're using our own happy coordinate system. So we set it // to be the source rect since that matches how the code below actually // uses the variable: to determine how to translate things to account // for the offset of the layer. m_transformedSourceRect = m_sourceRect; } else if (m_transformMode == KeepTransform && m_layerMode != TextComposite) { // FIXME: support clipping for other modes IntRect clippedSourceRect = m_sourceRect; SkRect clipBounds; if (m_destContext->getClipBounds(&clipBounds)) { FloatRect clipRect(clipBounds.left(), clipBounds.top(), clipBounds.width(), clipBounds.height()); clippedSourceRect.intersect(enclosingIntRect(clipRect)); } m_transformedSourceRect = m_orgTransform.mapRect(clippedSourceRect); } else m_transformedSourceRect = m_orgTransform.mapRect(m_sourceRect); m_layerSize = IntSize(m_transformedSourceRect.width(), m_transformedSourceRect.height()); }
// Returns true if this method handled the glyph, false if needs to be passed to fallback // bool GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed, SkScalar sx, SkScalar sy, GrFontScaler* scaler) { if (NULL == fDrawTarget) { return true; } if (NULL == fStrike) { fStrike = fContext->getFontCache()->getStrike(scaler, true); } GrGlyph* glyph = fStrike->getGlyph(packed, scaler); if (NULL == glyph || glyph->fBounds.isEmpty()) { return true; } // fallback to color glyph support if (kA8_GrMaskFormat != glyph->fMaskFormat) { return false; } SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset); SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset); SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset); SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset); SkScalar scale = fTextRatio; dx *= scale; dy *= scale; sx += dx; sy += dy; width *= scale; height *= scale; SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height); // check if we clipped out SkRect dstRect; const SkMatrix& ctm = fContext->getMatrix(); (void) ctm.mapRect(&dstRect, glyphRect); if (fClipRect.quickReject(SkScalarTruncToInt(dstRect.left()), SkScalarTruncToInt(dstRect.top()), SkScalarTruncToInt(dstRect.right()), SkScalarTruncToInt(dstRect.bottom()))) { // SkCLZ(3); // so we can set a break-point in the debugger return true; } if (NULL == glyph->fPlot) { if (!fStrike->glyphTooLargeForAtlas(glyph)) { if (fStrike->addGlyphToAtlas(glyph, scaler)) { goto HAS_ATLAS; } // try to clear out an unused plot before we flush if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && fStrike->addGlyphToAtlas(glyph, scaler)) { goto HAS_ATLAS; } if (c_DumpFontCache) { #ifdef SK_DEVELOPER fContext->getFontCache()->dump(); #endif } // before we purge the cache, we must flush any accumulated draws this->flush(); fContext->flush(); // we should have an unused plot now if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && fStrike->addGlyphToAtlas(glyph, scaler)) { goto HAS_ATLAS; } } if (NULL == glyph->fPath) { SkPath* path = SkNEW(SkPath); if (!scaler->getGlyphPath(glyph->glyphID(), path)) { // flag the glyph as being dead? delete path; return true; } glyph->fPath = path; } // flush any accumulated draws before drawing this glyph as a path. this->flush(); GrContext::AutoMatrix am; SkMatrix ctm; ctm.setScale(fTextRatio, fTextRatio); ctm.postTranslate(sx - dx, sy - dy); GrPaint tmpPaint(fPaint); am.setPreConcat(fContext, ctm, &tmpPaint); GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle); fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo); // remove this glyph from the vertices we need to allocate fTotalVertexCount -= kVerticesPerGlyph; return true; } HAS_ATLAS: SkASSERT(glyph->fPlot); GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken(); glyph->fPlot->setDrawToken(drawToken); GrTexture* texture = glyph->fPlot->texture(); SkASSERT(texture); if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fTotalVertexCount) { this->flush(); fCurrTexture = texture; fCurrTexture->ref(); } bool useColorVerts = !fUseLCDText; if (NULL == fVertices) { int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads(); fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices); fVertices = alloc_vertices(fDrawTarget, fAllocVertexCount, useColorVerts); } SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX + SK_DistanceFieldInset); SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY + SK_DistanceFieldInset); SkFixed tw = SkIntToFixed(glyph->fBounds.width() - 2*SK_DistanceFieldInset); SkFixed th = SkIntToFixed(glyph->fBounds.height() - 2*SK_DistanceFieldInset); fVertexBounds.joinNonEmptyArg(glyphRect); size_t vertSize = get_vertex_stride(useColorVerts); SkPoint* positions = reinterpret_cast<SkPoint*>( reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex); positions->setRectFan(glyphRect.fLeft, glyphRect.fTop, glyphRect.fRight, glyphRect.fBottom, vertSize); // The texture coords are last in both the with and without color vertex layouts. SkPoint* textureCoords = reinterpret_cast<SkPoint*>( reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint)); textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)), SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)), SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)), SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)), vertSize); if (useColorVerts) { // color comes after position. GrColor* colors = reinterpret_cast<GrColor*>(positions + 1); for (int i = 0; i < 4; ++i) { *colors = fPaint.getColor(); colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize); } } fCurrVertex += 4; return true; }
* * The portion in the layer, will end up SRC_OVERing the 0x80 layer pixels onto the canvas' red * pixels, making magenta. * * Thus the expected result is the upper half to be magenta 0xFF7F0080, and the lower half to be * light blue 0xFF7F7FFF. */ DEF_SIMPLE_GM(dont_clip_to_layer, canvas, 120, 120) { const SkRect r { 10, 10, 110, 110 }; // Wrap the entire test inside a red layer, so we don't punch the actual gm's alpha with // kSrc_Mode, which makes it hard to view (we like our GMs to have opaque pixels). canvas->saveLayer(&r, nullptr); canvas->drawColor(SK_ColorRED); SkRect r0 = SkRect::MakeXYWH(r.left(), r.top(), r.width(), r.height()/2); SkCanvas::SaveLayerRec rec; rec.fPaint = nullptr; rec.fBounds = &r0; rec.fBackdrop = nullptr; rec.fSaveLayerFlags = 1 << 31;//SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag; canvas->saveLayer(rec); do_draw(canvas, r); canvas->restore(); canvas->restore(); // red-layer } /** Draw a 2px border around the target, then red behind the target; set the clip to match the target, then draw >> the target in blue.
SkPDFImageShader* SkPDFImageShader::Create( SkPDFCanon* canon, SkScalar dpi, SkAutoTDelete<SkPDFShader::State>* autoState) { const SkPDFShader::State& state = **autoState; state.fImage.lockPixels(); // The image shader pattern cell will be drawn into a separate device // in pattern cell space (no scaling on the bitmap, though there may be // translations so that all content is in the device, coordinates > 0). // Map clip bounds to shader space to ensure the device is large enough // to handle fake clamping. SkMatrix finalMatrix = state.fCanvasTransform; finalMatrix.preConcat(state.fShaderTransform); SkRect deviceBounds; deviceBounds.set(state.fBBox); if (!inverse_transform_bbox(finalMatrix, &deviceBounds)) { return NULL; } const SkBitmap* image = &state.fImage; SkRect bitmapBounds; image->getBounds(&bitmapBounds); // For tiling modes, the bounds should be extended to include the bitmap, // otherwise the bitmap gets clipped out and the shader is empty and awful. // For clamp modes, we're only interested in the clip region, whether // or not the main bitmap is in it. SkShader::TileMode tileModes[2]; tileModes[0] = state.fImageTileModes[0]; tileModes[1] = state.fImageTileModes[1]; if (tileModes[0] != SkShader::kClamp_TileMode || tileModes[1] != SkShader::kClamp_TileMode) { deviceBounds.join(bitmapBounds); } SkISize size = SkISize::Make(SkScalarRoundToInt(deviceBounds.width()), SkScalarRoundToInt(deviceBounds.height())); SkAutoTUnref<SkPDFDevice> patternDevice( SkPDFDevice::CreateUnflipped(size, dpi, canon)); SkCanvas canvas(patternDevice.get()); SkRect patternBBox; image->getBounds(&patternBBox); // Translate the canvas so that the bitmap origin is at (0, 0). canvas.translate(-deviceBounds.left(), -deviceBounds.top()); patternBBox.offset(-deviceBounds.left(), -deviceBounds.top()); // Undo the translation in the final matrix finalMatrix.preTranslate(deviceBounds.left(), deviceBounds.top()); // If the bitmap is out of bounds (i.e. clamp mode where we only see the // stretched sides), canvas will clip this out and the extraneous data // won't be saved to the PDF. canvas.drawBitmap(*image, 0, 0); SkScalar width = SkIntToScalar(image->width()); SkScalar height = SkIntToScalar(image->height()); // Tiling is implied. First we handle mirroring. if (tileModes[0] == SkShader::kMirror_TileMode) { SkMatrix xMirror; xMirror.setScale(-1, 1); xMirror.postTranslate(2 * width, 0); drawBitmapMatrix(&canvas, *image, xMirror); patternBBox.fRight += width; } if (tileModes[1] == SkShader::kMirror_TileMode) { SkMatrix yMirror; yMirror.setScale(SK_Scalar1, -SK_Scalar1); yMirror.postTranslate(0, 2 * height); drawBitmapMatrix(&canvas, *image, yMirror); patternBBox.fBottom += height; } if (tileModes[0] == SkShader::kMirror_TileMode && tileModes[1] == SkShader::kMirror_TileMode) { SkMatrix mirror; mirror.setScale(-1, -1); mirror.postTranslate(2 * width, 2 * height); drawBitmapMatrix(&canvas, *image, mirror); } // Then handle Clamping, which requires expanding the pattern canvas to // cover the entire surfaceBBox. // If both x and y are in clamp mode, we start by filling in the corners. // (Which are just a rectangles of the corner colors.) if (tileModes[0] == SkShader::kClamp_TileMode && tileModes[1] == SkShader::kClamp_TileMode) { SkPaint paint; SkRect rect; rect = SkRect::MakeLTRB(deviceBounds.left(), deviceBounds.top(), 0, 0); if (!rect.isEmpty()) { paint.setColor(image->getColor(0, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, deviceBounds.top(), deviceBounds.right(), 0); if (!rect.isEmpty()) { paint.setColor(image->getColor(image->width() - 1, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, height, deviceBounds.right(), deviceBounds.bottom()); if (!rect.isEmpty()) { paint.setColor(image->getColor(image->width() - 1, image->height() - 1)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(deviceBounds.left(), height, 0, deviceBounds.bottom()); if (!rect.isEmpty()) { paint.setColor(image->getColor(0, image->height() - 1)); canvas.drawRect(rect, paint); } } // Then expand the left, right, top, then bottom. if (tileModes[0] == SkShader::kClamp_TileMode) { SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, image->height()); if (deviceBounds.left() < 0) { SkBitmap left; SkAssertResult(image->extractSubset(&left, subset)); SkMatrix leftMatrix; leftMatrix.setScale(-deviceBounds.left(), 1); leftMatrix.postTranslate(deviceBounds.left(), 0); drawBitmapMatrix(&canvas, left, leftMatrix); if (tileModes[1] == SkShader::kMirror_TileMode) { leftMatrix.postScale(SK_Scalar1, -SK_Scalar1); leftMatrix.postTranslate(0, 2 * height); drawBitmapMatrix(&canvas, left, leftMatrix); } patternBBox.fLeft = 0; } if (deviceBounds.right() > width) { SkBitmap right; subset.offset(image->width() - 1, 0); SkAssertResult(image->extractSubset(&right, subset)); SkMatrix rightMatrix; rightMatrix.setScale(deviceBounds.right() - width, 1); rightMatrix.postTranslate(width, 0); drawBitmapMatrix(&canvas, right, rightMatrix); if (tileModes[1] == SkShader::kMirror_TileMode) { rightMatrix.postScale(SK_Scalar1, -SK_Scalar1); rightMatrix.postTranslate(0, 2 * height); drawBitmapMatrix(&canvas, right, rightMatrix); } patternBBox.fRight = deviceBounds.width(); } } if (tileModes[1] == SkShader::kClamp_TileMode) { SkIRect subset = SkIRect::MakeXYWH(0, 0, image->width(), 1); if (deviceBounds.top() < 0) { SkBitmap top; SkAssertResult(image->extractSubset(&top, subset)); SkMatrix topMatrix; topMatrix.setScale(SK_Scalar1, -deviceBounds.top()); topMatrix.postTranslate(0, deviceBounds.top()); drawBitmapMatrix(&canvas, top, topMatrix); if (tileModes[0] == SkShader::kMirror_TileMode) { topMatrix.postScale(-1, 1); topMatrix.postTranslate(2 * width, 0); drawBitmapMatrix(&canvas, top, topMatrix); } patternBBox.fTop = 0; } if (deviceBounds.bottom() > height) { SkBitmap bottom; subset.offset(0, image->height() - 1); SkAssertResult(image->extractSubset(&bottom, subset)); SkMatrix bottomMatrix; bottomMatrix.setScale(SK_Scalar1, deviceBounds.bottom() - height); bottomMatrix.postTranslate(0, height); drawBitmapMatrix(&canvas, bottom, bottomMatrix); if (tileModes[0] == SkShader::kMirror_TileMode) { bottomMatrix.postScale(-1, 1); bottomMatrix.postTranslate(2 * width, 0); drawBitmapMatrix(&canvas, bottom, bottomMatrix); } patternBBox.fBottom = deviceBounds.height(); } } // Put the canvas into the pattern stream (fContent). SkAutoTDelete<SkStreamAsset> content(patternDevice->content()); SkPDFImageShader* imageShader = SkNEW_ARGS(SkPDFImageShader, (autoState->detach())); imageShader->setData(content.get()); SkAutoTUnref<SkPDFDict> resourceDict( patternDevice->createResourceDict()); populate_tiling_pattern_dict(imageShader, patternBBox, resourceDict.get(), finalMatrix); imageShader->fShaderState->fImage.unlockPixels(); canon->addImageShader(imageShader); return imageShader; }
void onOnceBeforeDraw() override { const SkRect fieldBounds = kBounds.makeOutset(kBallSize / 2, kBallSize / 2); const SkRRect ball = SkRRect::MakeOval(SkRect::MakeWH(kBallSize, kBallSize)); const SkRRect paddle = SkRRect::MakeRectXY(SkRect::MakeWH(kPaddleSize.width(), kPaddleSize.height()), kPaddleSize.width() / 2, kPaddleSize.width() / 2); fBall.initialize(ball, SkPoint::Make(kBounds.centerX(), kBounds.centerY()), SkVector::Make(fRand.nextRangeScalar(kBallSpeedMin, kBallSpeedMax), fRand.nextRangeScalar(kBallSpeedMin, kBallSpeedMax))); fPaddle0.initialize(paddle, SkPoint::Make(fieldBounds.left() - kPaddleSize.width() / 2, fieldBounds.centerY()), SkVector::Make(0, 0)); fPaddle1.initialize(paddle, SkPoint::Make(fieldBounds.right() + kPaddleSize.width() / 2, fieldBounds.centerY()), SkVector::Make(0, 0)); // Background decoration. SkPath bgPath; bgPath.moveTo(kBounds.left() , fieldBounds.top()); bgPath.lineTo(kBounds.right(), fieldBounds.top()); bgPath.moveTo(kBounds.left() , fieldBounds.bottom()); bgPath.lineTo(kBounds.right(), fieldBounds.bottom()); // TODO: stroke-dash support would come in handy right about now. for (uint32_t i = 0; i < kBackgroundDashCount; ++i) { bgPath.moveTo(kBounds.centerX(), kBounds.top() + (i + 0.25f) * kBounds.height() / kBackgroundDashCount); bgPath.lineTo(kBounds.centerX(), kBounds.top() + (i + 0.75f) * kBounds.height() / kBackgroundDashCount); } auto bg_path = sksg::Path::Make(bgPath); auto bg_paint = sksg::Color::Make(SK_ColorBLACK); bg_paint->setStyle(SkPaint::kStroke_Style); bg_paint->setStrokeWidth(kBackgroundStroke); auto ball_paint = sksg::Color::Make(SK_ColorGREEN), paddle0_paint = sksg::Color::Make(SK_ColorBLUE), paddle1_paint = sksg::Color::Make(SK_ColorRED), shadow_paint = sksg::Color::Make(SK_ColorBLACK); ball_paint->setAntiAlias(true); paddle0_paint->setAntiAlias(true); paddle1_paint->setAntiAlias(true); shadow_paint->setAntiAlias(true); shadow_paint->setOpacity(kShadowOpacity); // Build the scene graph. auto group = sksg::Group::Make(); group->addChild(sksg::Draw::Make(std::move(bg_path), std::move(bg_paint))); group->addChild(sksg::Draw::Make(fPaddle0.shadowNode, shadow_paint)); group->addChild(sksg::Draw::Make(fPaddle1.shadowNode, shadow_paint)); group->addChild(sksg::Draw::Make(fBall.shadowNode, shadow_paint)); group->addChild(sksg::Draw::Make(fPaddle0.objectNode, paddle0_paint)); group->addChild(sksg::Draw::Make(fPaddle1.objectNode, paddle1_paint)); group->addChild(sksg::Draw::Make(fBall.objectNode, ball_paint)); // Handle everything in a normalized 1x1 space. fContentMatrix = sksg::Matrix<SkMatrix>::Make( SkMatrix::MakeRectToRect(SkRect::MakeWH(1, 1), SkRect::MakeIWH(this->width(), this->height()), SkMatrix::kFill_ScaleToFit)); auto root = sksg::TransformEffect::Make(std::move(group), fContentMatrix); fScene = sksg::Scene::Make(std::move(root), sksg::AnimatorList()); // Off we go. this->updatePaddleStrategy(); }
// Convert user-space bounds to grid tiles they cover (LT and RB both inclusive). void SkTileGrid::userToGrid(const SkRect& user, SkIRect* grid) const { grid->fLeft = SkPin32(user.left() * fInvWidth , 0, fXTiles - 1); grid->fTop = SkPin32(user.top() * fInvHeight, 0, fYTiles - 1); grid->fRight = SkPin32(user.right() * fInvWidth , 0, fXTiles - 1); grid->fBottom = SkPin32(user.bottom() * fInvHeight, 0, fYTiles - 1); }
static void convolve_gaussian(GrDrawContext* drawContext, const GrClip& clip, const SkRect& srcRect, GrTexture* texture, Gr1DKernelEffect::Direction direction, int radius, float sigma, const SkRect* srcBounds, const SkPoint& srcOffset) { float bounds[2] = { 0.0f, 1.0f }; SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); if (!srcBounds) { convolve_gaussian_1d(drawContext, clip, dstRect, srcOffset, texture, direction, radius, sigma, false, bounds); return; } SkRect midRect = *srcBounds, leftRect, rightRect; midRect.offset(srcOffset); SkIRect topRect, bottomRect; SkScalar rad = SkIntToScalar(radius); if (direction == Gr1DKernelEffect::kX_Direction) { bounds[0] = SkScalarToFloat(srcBounds->left()) / texture->width(); bounds[1] = SkScalarToFloat(srcBounds->right()) / texture->width(); SkRect::MakeLTRB(0, 0, dstRect.right(), midRect.top()).roundOut(&topRect); SkRect::MakeLTRB(0, midRect.bottom(), dstRect.right(), dstRect.bottom()) .roundOut(&bottomRect); midRect.inset(rad, 0); leftRect = SkRect::MakeLTRB(0, midRect.top(), midRect.left(), midRect.bottom()); rightRect = SkRect::MakeLTRB(midRect.right(), midRect.top(), dstRect.width(), midRect.bottom()); dstRect.fTop = midRect.top(); dstRect.fBottom = midRect.bottom(); } else { bounds[0] = SkScalarToFloat(srcBounds->top()) / texture->height(); bounds[1] = SkScalarToFloat(srcBounds->bottom()) / texture->height(); SkRect::MakeLTRB(0, 0, midRect.left(), dstRect.bottom()).roundOut(&topRect); SkRect::MakeLTRB(midRect.right(), 0, dstRect.right(), dstRect.bottom()) .roundOut(&bottomRect);; midRect.inset(0, rad); leftRect = SkRect::MakeLTRB(midRect.left(), 0, midRect.right(), midRect.top()); rightRect = SkRect::MakeLTRB(midRect.left(), midRect.bottom(), midRect.right(), dstRect.height()); dstRect.fLeft = midRect.left(); dstRect.fRight = midRect.right(); } if (!topRect.isEmpty()) { drawContext->clear(&topRect, 0, false); } if (!bottomRect.isEmpty()) { drawContext->clear(&bottomRect, 0, false); } if (midRect.isEmpty()) { // Blur radius covers srcBounds; use bounds over entire draw convolve_gaussian_1d(drawContext, clip, dstRect, srcOffset, texture, direction, radius, sigma, true, bounds); } else { // Draw right and left margins with bounds; middle without. convolve_gaussian_1d(drawContext, clip, leftRect, srcOffset, texture, direction, radius, sigma, true, bounds); convolve_gaussian_1d(drawContext, clip, rightRect, srcOffset, texture, direction, radius, sigma, true, bounds); convolve_gaussian_1d(drawContext, clip, midRect, srcOffset, texture, direction, radius, sigma, false, bounds); } }
void onOnceBeforeDraw() override { const SkRect fieldBounds = kBounds.makeOutset(kBallSize / 2, kBallSize / 2); const SkRRect ball = SkRRect::MakeOval(SkRect::MakeWH(kBallSize, kBallSize)); const SkRRect paddle = SkRRect::MakeRectXY(SkRect::MakeWH(kPaddleSize.width(), kPaddleSize.height()), kPaddleSize.width() / 2, kPaddleSize.width() / 2); fBall.initialize(ball, SK_ColorGREEN, SkPoint::Make(kBounds.centerX(), kBounds.centerY()), SkVector::Make(fRand.nextRangeScalar(kBallSpeedMin, kBallSpeedMax), fRand.nextRangeScalar(kBallSpeedMin, kBallSpeedMax))); fPaddle0.initialize(paddle, SK_ColorBLUE, SkPoint::Make(fieldBounds.left() - kPaddleSize.width() / 2, fieldBounds.centerY()), SkVector::Make(0, 0)); fPaddle1.initialize(paddle, SK_ColorRED, SkPoint::Make(fieldBounds.right() + kPaddleSize.width() / 2, fieldBounds.centerY()), SkVector::Make(0, 0)); // Background decoration. SkPath bgPath; bgPath.moveTo(kBounds.left() , fieldBounds.top()); bgPath.lineTo(kBounds.right(), fieldBounds.top()); bgPath.moveTo(kBounds.left() , fieldBounds.bottom()); bgPath.lineTo(kBounds.right(), fieldBounds.bottom()); // TODO: stroke-dash support would come in handy right about now. for (uint32_t i = 0; i < kBackgroundDashCount; ++i) { bgPath.moveTo(kBounds.centerX(), kBounds.top() + (i + 0.25f) * kBounds.height() / kBackgroundDashCount); bgPath.lineTo(kBounds.centerX(), kBounds.top() + (i + 0.75f) * kBounds.height() / kBackgroundDashCount); } sk_sp<SkSVGPath> bg = SkSVGPath::Make(); bg->setPath(bgPath); bg->setFill(SkSVGPaint(SkSVGPaint::Type::kNone)); bg->setStroke(SkSVGPaint(SkSVGColorType(SK_ColorBLACK))); bg->setStrokeWidth(SkSVGLength(kBackgroundStroke)); // Build the SVG DOM tree. sk_sp<SkSVGSVG> root = SkSVGSVG::Make(); root->appendChild(std::move(bg)); root->appendChild(fPaddle0.shadowNode); root->appendChild(fPaddle1.shadowNode); root->appendChild(fBall.shadowNode); root->appendChild(fPaddle0.objectNode); root->appendChild(fPaddle1.objectNode); root->appendChild(fBall.objectNode); // Handle everything in a normalized 1x1 space. root->setViewBox(SkSVGViewBoxType(SkRect::MakeWH(1, 1))); fDom = sk_sp<SkSVGDOM>(new SkSVGDOM()); fDom->setContainerSize(SkSize::Make(this->width(), this->height())); fDom->setRoot(std::move(root)); // Off we go. this->updatePaddleStrategy(); }
static void make_path_line(SkPath* path, const SkRect& bounds) { path->moveTo(bounds.left(), bounds.top()); path->lineTo(bounds.right(), bounds.bottom()); }
// Returns true if this method handled the glyph, false if needs to be passed to fallback // bool GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed, SkScalar sx, SkScalar sy, GrFontScaler* scaler) { if (NULL == fDrawTarget) { return true; } if (NULL == fStrike) { fStrike = fContext->getFontCache()->getStrike(scaler, true); } GrGlyph* glyph = fStrike->getGlyph(packed, scaler); if (NULL == glyph || glyph->fBounds.isEmpty()) { return true; } // fallback to color glyph support if (kA8_GrMaskFormat != glyph->fMaskFormat) { return false; } SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset); SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset); SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset); SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset); SkScalar scale = fTextRatio; dx *= scale; dy *= scale; sx += dx; sy += dy; width *= scale; height *= scale; SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height); // check if we clipped out SkRect dstRect; const SkMatrix& ctm = fViewMatrix; (void) ctm.mapRect(&dstRect, glyphRect); if (fClipRect.quickReject(SkScalarTruncToInt(dstRect.left()), SkScalarTruncToInt(dstRect.top()), SkScalarTruncToInt(dstRect.right()), SkScalarTruncToInt(dstRect.bottom()))) { return true; } if (NULL == glyph->fPlot) { // needs to be a separate conditional to avoid over-optimization // on Nexus 7 and Nexus 10 // If the glyph is too large we fall back to paths if (!uploadGlyph(glyph, scaler)) { if (NULL == glyph->fPath) { SkPath* path = SkNEW(SkPath); if (!scaler->getGlyphPath(glyph->glyphID(), path)) { // flag the glyph as being dead? delete path; return true; } glyph->fPath = path; } // flush any accumulated draws before drawing this glyph as a path. this->flush(); SkMatrix ctm; ctm.setScale(fTextRatio, fTextRatio); ctm.postTranslate(sx - dx, sy - dy); SkPath tmpPath(*glyph->fPath); tmpPath.transform(ctm); GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle); fContext->drawPath(fRenderTarget, fClip, fPaint, fViewMatrix, tmpPath, strokeInfo); // remove this glyph from the vertices we need to allocate fTotalVertexCount -= kVerticesPerGlyph; return true; } } SkASSERT(glyph->fPlot); GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken(); glyph->fPlot->setDrawToken(drawToken); GrTexture* texture = glyph->fPlot->texture(); SkASSERT(texture); if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fTotalVertexCount) { this->flush(); fCurrTexture = texture; fCurrTexture->ref(); } bool useColorVerts = !fUseLCDText; if (NULL == fVertices) { int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads(); fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices); fVertices = alloc_vertices(fDrawTarget, fAllocVertexCount, useColorVerts); } fVertexBounds.joinNonEmptyArg(glyphRect); int u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset; int v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset; int u1 = u0 + glyph->fBounds.width() - 2*SK_DistanceFieldInset; int v1 = v0 + glyph->fBounds.height() - 2*SK_DistanceFieldInset; size_t vertSize = get_vertex_stride(useColorVerts); intptr_t vertex = reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex; // V0 SkPoint* position = reinterpret_cast<SkPoint*>(vertex); position->set(glyphRect.fLeft, glyphRect.fTop); if (useColorVerts) { SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); *color = fPaint.getColor(); } SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16)); textureCoords->set(u0, v0); vertex += vertSize; // V1 position = reinterpret_cast<SkPoint*>(vertex); position->set(glyphRect.fLeft, glyphRect.fBottom); if (useColorVerts) { SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); *color = fPaint.getColor(); } textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16)); textureCoords->set(u0, v1); vertex += vertSize; // V2 position = reinterpret_cast<SkPoint*>(vertex); position->set(glyphRect.fRight, glyphRect.fBottom); if (useColorVerts) { SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); *color = fPaint.getColor(); } textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16)); textureCoords->set(u1, v1); vertex += vertSize; // V3 position = reinterpret_cast<SkPoint*>(vertex); position->set(glyphRect.fRight, glyphRect.fTop); if (useColorVerts) { SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); *color = fPaint.getColor(); } textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16)); textureCoords->set(u1, v0); fCurrVertex += 4; return true; }
static bool equal(const SkRect& a, const SkRect& b) { return SkScalarNearlyEqual(a.left(), b.left()) && SkScalarNearlyEqual(a.top(), b.top()) && SkScalarNearlyEqual(a.right(), b.right()) && SkScalarNearlyEqual(a.bottom(), b.bottom()); }
void onDraw(SkCanvas* canvas) override { canvas->drawColor(sk_tool_utils::color_to_565(SK_ColorGRAY)); SkPaint paint; paint.setTypeface(emojiFont.typeface); const char* text = emojiFont.text; // draw text at different point sizes const int textSize[] = { 10, 30, 50, }; const int textYOffset[] = { 10, 40, 100, }; SkASSERT(sizeof(textSize) == sizeof(textYOffset)); size_t y_offset = 0; for (size_t y = 0; y < sizeof(textSize) / sizeof(int); y++) { paint.setTextSize(SkIntToScalar(textSize[y])); canvas->drawText(text, strlen(text), 10, SkIntToScalar(textYOffset[y]), paint); y_offset += textYOffset[y]; } // draw with shaders and image filters for (int makeLinear = 0; makeLinear < 2; makeLinear++) { for (int makeBlur = 0; makeBlur < 2; makeBlur++) { for (int makeGray = 0; makeGray < 2; makeGray++) { SkPaint shaderPaint; shaderPaint.setTypeface(paint.getTypeface()); if (SkToBool(makeLinear)) { shaderPaint.setShader(MakeLinear())->unref(); } if (SkToBool(makeBlur) && SkToBool(makeGray)) { SkAutoTUnref<SkImageFilter> grayScale(make_grayscale(nullptr)); SkAutoTUnref<SkImageFilter> blur(make_blur(3.0f, grayScale)); shaderPaint.setImageFilter(blur); } else if (SkToBool(makeBlur)) { SkAutoTUnref<SkImageFilter> blur(make_blur(3.0f, nullptr)); shaderPaint.setImageFilter(blur); } else if (SkToBool(makeGray)) { SkAutoTUnref<SkImageFilter> grayScale(make_grayscale(nullptr)); shaderPaint.setImageFilter(grayScale); } shaderPaint.setTextSize(30); canvas->drawText(text, strlen(text), 380, SkIntToScalar(y_offset), shaderPaint); y_offset += 32; } } } // setup work needed to draw text with different clips canvas->translate(10, 160); paint.setTextSize(40); // compute the bounds of the text SkRect bounds; paint.measureText(text, strlen(text), &bounds); const SkScalar boundsHalfWidth = bounds.width() * SK_ScalarHalf; const SkScalar boundsHalfHeight = bounds.height() * SK_ScalarHalf; const SkScalar boundsQuarterWidth = boundsHalfWidth * SK_ScalarHalf; const SkScalar boundsQuarterHeight = boundsHalfHeight * SK_ScalarHalf; SkRect upperLeftClip = SkRect::MakeXYWH(bounds.left(), bounds.top(), boundsHalfWidth, boundsHalfHeight); SkRect lowerRightClip = SkRect::MakeXYWH(bounds.centerX(), bounds.centerY(), boundsHalfWidth, boundsHalfHeight); SkRect interiorClip = bounds; interiorClip.inset(boundsQuarterWidth, boundsQuarterHeight); const SkRect clipRects[] = { bounds, upperLeftClip, lowerRightClip, interiorClip }; SkPaint clipHairline; clipHairline.setColor(SK_ColorWHITE); clipHairline.setStyle(SkPaint::kStroke_Style); for (size_t x = 0; x < sizeof(clipRects) / sizeof(SkRect); ++x) { canvas->save(); canvas->drawRect(clipRects[x], clipHairline); paint.setAlpha(0x20); canvas->drawText(text, strlen(text), 0, 0, paint); canvas->clipRect(clipRects[x]); paint.setAlpha(0xFF); canvas->drawText(text, strlen(text), 0, 0, paint); canvas->restore(); canvas->translate(0, bounds.height() + SkIntToScalar(25)); } }
void draw(SkCanvas* canvas) { SkRect result; bool intersected = result.intersect({ 10, 40, 50, 80 }, { 30, 60, 70, 90 }); SkDebugf("%s intersection: %g, %g, %g, %g\n", intersected ? "" : "no ", result.left(), result.top(), result.right(), result.bottom()); }
static SkPDFIndirectReference make_function_shader(SkPDFDocument* doc, const SkPDFGradientShader::Key& state) { SkPoint transformPoints[2]; const SkShader::GradientInfo& info = state.fInfo; SkMatrix finalMatrix = state.fCanvasTransform; finalMatrix.preConcat(state.fShaderTransform); bool doStitchFunctions = (state.fType == SkShader::kLinear_GradientType || state.fType == SkShader::kRadial_GradientType || state.fType == SkShader::kConical_GradientType) && (SkTileMode)info.fTileMode == SkTileMode::kClamp && !finalMatrix.hasPerspective(); int32_t shadingType = 1; auto pdfShader = SkPDFMakeDict(); // The two point radial gradient further references // state.fInfo // in translating from x, y coordinates to the t parameter. So, we have // to transform the points and radii according to the calculated matrix. if (doStitchFunctions) { pdfShader->insertObject("Function", gradientStitchCode(info)); shadingType = (state.fType == SkShader::kLinear_GradientType) ? 2 : 3; auto extend = SkPDFMakeArray(); extend->reserve(2); extend->appendBool(true); extend->appendBool(true); pdfShader->insertObject("Extend", std::move(extend)); std::unique_ptr<SkPDFArray> coords; if (state.fType == SkShader::kConical_GradientType) { SkScalar r1 = info.fRadius[0]; SkScalar r2 = info.fRadius[1]; SkPoint pt1 = info.fPoint[0]; SkPoint pt2 = info.fPoint[1]; FixUpRadius(pt1, r1, pt2, r2); coords = SkPDFMakeArray(pt1.x(), pt1.y(), r1, pt2.x(), pt2.y(), r2); } else if (state.fType == SkShader::kRadial_GradientType) { const SkPoint& pt1 = info.fPoint[0]; coords = SkPDFMakeArray(pt1.x(), pt1.y(), 0, pt1.x(), pt1.y(), info.fRadius[0]); } else { const SkPoint& pt1 = info.fPoint[0]; const SkPoint& pt2 = info.fPoint[1]; coords = SkPDFMakeArray(pt1.x(), pt1.y(), pt2.x(), pt2.y()); } pdfShader->insertObject("Coords", std::move(coords)); } else { // Depending on the type of the gradient, we want to transform the // coordinate space in different ways. transformPoints[0] = info.fPoint[0]; transformPoints[1] = info.fPoint[1]; switch (state.fType) { case SkShader::kLinear_GradientType: break; case SkShader::kRadial_GradientType: transformPoints[1] = transformPoints[0]; transformPoints[1].fX += info.fRadius[0]; break; case SkShader::kConical_GradientType: { transformPoints[1] = transformPoints[0]; transformPoints[1].fX += SK_Scalar1; break; } case SkShader::kSweep_GradientType: transformPoints[1] = transformPoints[0]; transformPoints[1].fX += SK_Scalar1; break; case SkShader::kColor_GradientType: case SkShader::kNone_GradientType: default: return SkPDFIndirectReference(); } // Move any scaling (assuming a unit gradient) or translation // (and rotation for linear gradient), of the final gradient from // info.fPoints to the matrix (updating bbox appropriately). Now // the gradient can be drawn on on the unit segment. SkMatrix mapperMatrix; unit_to_points_matrix(transformPoints, &mapperMatrix); finalMatrix.preConcat(mapperMatrix); // Preserves as much as possible in the final matrix, and only removes // the perspective. The inverse of the perspective is stored in // perspectiveInverseOnly matrix and has 3 useful numbers // (p0, p1, p2), while everything else is either 0 or 1. // In this way the shader will handle it eficiently, with minimal code. SkMatrix perspectiveInverseOnly = SkMatrix::I(); if (finalMatrix.hasPerspective()) { if (!split_perspective(finalMatrix, &finalMatrix, &perspectiveInverseOnly)) { return SkPDFIndirectReference(); } } SkRect bbox; bbox.set(state.fBBox); if (!SkPDFUtils::InverseTransformBBox(finalMatrix, &bbox)) { return SkPDFIndirectReference(); } SkDynamicMemoryWStream functionCode; SkShader::GradientInfo infoCopy = info; if (state.fType == SkShader::kConical_GradientType) { SkMatrix inverseMapperMatrix; if (!mapperMatrix.invert(&inverseMapperMatrix)) { return SkPDFIndirectReference(); } inverseMapperMatrix.mapPoints(infoCopy.fPoint, 2); infoCopy.fRadius[0] = inverseMapperMatrix.mapRadius(info.fRadius[0]); infoCopy.fRadius[1] = inverseMapperMatrix.mapRadius(info.fRadius[1]); } switch (state.fType) { case SkShader::kLinear_GradientType: linearCode(infoCopy, perspectiveInverseOnly, &functionCode); break; case SkShader::kRadial_GradientType: radialCode(infoCopy, perspectiveInverseOnly, &functionCode); break; case SkShader::kConical_GradientType: twoPointConicalCode(infoCopy, perspectiveInverseOnly, &functionCode); break; case SkShader::kSweep_GradientType: sweepCode(infoCopy, perspectiveInverseOnly, &functionCode); break; default: SkASSERT(false); } pdfShader->insertObject( "Domain", SkPDFMakeArray(bbox.left(), bbox.right(), bbox.top(), bbox.bottom())); auto domain = SkPDFMakeArray(bbox.left(), bbox.right(), bbox.top(), bbox.bottom()); std::unique_ptr<SkPDFArray> rangeObject = SkPDFMakeArray(0, 1, 0, 1, 0, 1); pdfShader->insertRef("Function", make_ps_function(functionCode.detachAsStream(), std::move(domain), std::move(rangeObject), doc)); } pdfShader->insertInt("ShadingType", shadingType); pdfShader->insertName("ColorSpace", "DeviceRGB"); SkPDFDict pdfFunctionShader("Pattern"); pdfFunctionShader.insertInt("PatternType", 2); pdfFunctionShader.insertObject("Matrix", SkPDFUtils::MatrixToArray(finalMatrix)); pdfFunctionShader.insertObject("Shading", std::move(pdfShader)); return doc->emit(pdfFunctionShader); }