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); }
void draw(SkCanvas* canvas) { SkRect rect = SkRect::MakeLTRB(5, 35, 15, 25); SkDebugf("rect: %g, %g, %g, %g isEmpty: %s\n", rect.left(), rect.top(), rect.right(), rect.bottom(), rect.isEmpty() ? "true" : "false"); rect.sort(); SkDebugf("rect: %g, %g, %g, %g isEmpty: %s\n", rect.left(), rect.top(), rect.right(), rect.bottom(), rect.isEmpty() ? "true" : "false"); }
static void convolve_gaussian(GrDrawContext* drawContext, GrRenderTarget* rt, const GrClip& clip, 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_1d(drawContext, rt, clip, 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_1d(drawContext, rt, clip, srcRect, dstRect, texture, direction, radius, sigma, true, bounds); } else { // Draw upper and lower margins with bounds; middle without. convolve_gaussian_1d(drawContext, rt, clip, lowerSrcRect, lowerDstRect, texture, direction, radius, sigma, true, bounds); convolve_gaussian_1d(drawContext, rt, clip, upperSrcRect, upperDstRect, texture, direction, radius, sigma, true, bounds); convolve_gaussian_1d(drawContext, rt, clip, middleSrcRect, middleDstRect, texture, direction, radius, sigma, false, bounds); } }
static bool has_aligned_samples(const SkRect& srcRect, const SkRect& transformedRect) { // detect pixel disalignment if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - transformedRect.left()) < kColorBleedTolerance && SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) - transformedRect.top()) < kColorBleedTolerance && SkScalarAbs(transformedRect.width() - srcRect.width()) < kColorBleedTolerance && SkScalarAbs(transformedRect.height() - srcRect.height()) < kColorBleedTolerance) { return true; } return false; }
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); }
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(); } } }
static bool quick_reject_in_y(const SkPoint pts[4], const SkRect& clip) { Sk4s ys(pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY); Sk4s t(clip.top()); Sk4s b(clip.bottom()); return (ys < t).allTrue() || (ys > b).allTrue(); }
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(); }
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); } } }
// 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"); } }
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)); } }
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); }
bool WebGLLayer::drawGL(bool layerTilesDisabled) { LOGWEBGL("WebGLLayer::drawGL() [+]"); bool askScreenUpdate = false; askScreenUpdate |= LayerAndroid::drawGL(layerTilesDisabled); if (m_proxy.get()) { EGLImageKHR eglImage; int width; int height; SkRect localBounds; bool requestUpdate; bool locked = m_proxy->lockFrontBuffer(eglImage, width, height, localBounds, requestUpdate); if (locked) { GLuint texture; LOGWEBGL("WebGLLayer::drawGL(), this = %p, m_proxy = %p, eglImage = %d", this, m_proxy.get(), eglImage); if (m_eglImages[0] == eglImage) { texture = m_textures[0]; } else if (m_eglImages[1] == eglImage) { texture = m_textures[1]; } else { // New buffer int idx = 0; if (m_eglImages[idx] != 0) idx++; if (m_eglImages[idx] != 0) return false; m_eglImages[idx] = eglImage; m_textures[idx] = createTexture(eglImage, width, height); texture = m_textures[idx]; } // Flip the y-coordinate TransformationMatrix transform = m_drawTransform; transform = transform.translate(0, 2 * localBounds.top() + localBounds.height()); transform = transform.scale3d(1.0, -1.0, 1.0); TextureQuadData data(texture, GL_TEXTURE_2D, GL_LINEAR, LayerQuad, &transform, &localBounds, 1.0f, true); TilesManager::instance()->shader()->drawQuad(&data); m_proxy->releaseFrontBuffer(); askScreenUpdate |= requestUpdate; } } else { LOGWEBGL("m_proxy.get() returned NULL"); } return askScreenUpdate; }
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); }
bool WebGLLayer::drawGL() { bool askScreenUpdate = false; askScreenUpdate |= LayerAndroid::drawGL(); if (m_proxy.get()) { GLuint texture; SkRect localBounds; bool locked = m_proxy->lockFrontBuffer(texture, localBounds); if (locked) { // Flip the y-coordinate TransformationMatrix transform = m_drawTransform; transform = transform.translate(0, 2 * localBounds.top() + localBounds.height()); transform = transform.scale3d(1.0, -1.0, 1.0); TilesManager::instance()->shader()->drawLayerQuad(transform, localBounds, texture, 1.0f, true, GL_TEXTURE_EXTERNAL_OES); m_proxy->releaseFrontBuffer(); } } return askScreenUpdate; }
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()); }
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(); }
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; }
// 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)); } }
static void make_path_line(SkPath* path, const SkRect& bounds) { path->moveTo(bounds.left(), bounds.top()); path->lineTo(bounds.right(), bounds.bottom()); }
* * 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.
void draw(SkCanvas* canvas) { SkRect unsorted = { 15, 25, 10, 5 }; SkDebugf("unsorted.fTop: %g unsorted.top(): %g\n", unsorted.fTop, unsorted.top()); SkRect sorted = unsorted.makeSorted(); SkDebugf("sorted.fTop: %g sorted.top(): %g\n", sorted.fTop, sorted.top()); }
// 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); }