GLuint VideoLayerManager::createTextureFromImage(int buttonType) { SkRect rect = SkRect(m_buttonRect); SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height()); bitmap.allocPixels(); bitmap.eraseColor(0); SkCanvas canvas(bitmap); canvas.drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode); RenderSkinMediaButton::Draw(&canvas, m_buttonRect, buttonType, true, 0, false); GLuint texture; glGenTextures(1, &texture); GLUtils::createTextureWithBitmap(texture, bitmap); bitmap.reset(); return texture; }
static void test_inset(skiatest::Reporter* reporter) { SkRRect rr, rr2; SkRect r = { 0, 0, 100, 100 }; rr.setRect(r); rr.inset(-20, -20, &rr2); REPORTER_ASSERT(reporter, rr2.isRect()); rr.inset(20, 20, &rr2); REPORTER_ASSERT(reporter, rr2.isRect()); rr.inset(r.width()/2, r.height()/2, &rr2); REPORTER_ASSERT(reporter, rr2.isEmpty()); rr.setRectXY(r, 20, 20); rr.inset(19, 19, &rr2); REPORTER_ASSERT(reporter, rr2.isSimple()); rr.inset(20, 20, &rr2); REPORTER_ASSERT(reporter, rr2.isRect()); }
static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* metrics) { SkPaint* paint = static_cast<SkPaint*>(hbFont->userData); paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); uint16_t glyph16 = glyph; SkScalar width; SkRect bounds; paint->getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds); metrics->x = SkScalarToHBFixed(bounds.fLeft); metrics->y = SkScalarToHBFixed(bounds.fTop); metrics->width = SkScalarToHBFixed(bounds.width()); metrics->height = SkScalarToHBFixed(bounds.height()); metrics->xOffset = SkScalarToHBFixed(width); // We can't actually get the |y| correct because Skia doesn't export // the vertical advance. However, nor we do ever render vertical text at // the moment so it's unimportant. metrics->yOffset = 0; }
static void SkiaGetGlyphWidthAndExtents(SkPaint* paint, hb_codepoint_t codepoint, hb_position_t* width, hb_glyph_extents_t* extents) { if (codepoint > 0xFFFF) return; paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); SkScalar skWidth; SkRect skBounds; uint16_t glyph = codepoint; paint->getTextWidths(&glyph, sizeof(glyph), &skWidth, &skBounds); if (width) *width = SkiaScalarToHarfbuzzPosition(skWidth); if (extents) { extents->x_bearing = SkiaScalarToHarfbuzzPosition(skBounds.fLeft); // The extents are unused in harfbuzz now so we're unsure whether a negation is needed here. Revisit when we're sure. extents->y_bearing = SkiaScalarToHarfbuzzPosition(skBounds.fTop); extents->width = SkiaScalarToHarfbuzzPosition(skBounds.width()); extents->height = SkiaScalarToHarfbuzzPosition(skBounds.height()); } }
void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r, const SkPaint& paint) { if (paint.getPathEffect()) { // Create a path for the rectangle and apply the path effect to it. SkPath path; path.addRect(r); paint.getFillPath(path, &path); SkPaint noEffectPaint(paint); SkSafeUnref(noEffectPaint.setPathEffect(NULL)); drawPath(d, path, noEffectPaint, NULL, true); return; } updateGSFromPaint(paint, false); // Skia has 0,0 at top left, pdf at bottom left. Do the right thing. SkScalar bottom = r.fBottom < r.fTop ? r.fBottom : r.fTop; SkPDFUtils::AppendRectangle(r.fLeft, bottom, r.width(), r.height(), &fContent); SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType, &fContent); }
DEF_FUZZ(PathMeasure, fuzz) { uint8_t bits; fuzz->next(&bits); SkScalar distance[6]; for (auto index = 0; index < 6; ++index) { fuzz->next(&distance[index]); } SkPath path; BuildPath(fuzz, &path, SkPath::Verb::kDone_Verb); SkRect bounds = path.getBounds(); SkScalar maxDim = SkTMax(bounds.width(), bounds.height()); SkScalar resScale = maxDim / 1000; SkPathMeasure measure(path, bits & 1, resScale); SkPoint position; SkVector tangent; ignoreResult(measure.getPosTan(distance[0], &position, &tangent)); SkPath dst; ignoreResult(measure.getSegment(distance[1], distance[2], &dst, (bits >> 1) & 1)); ignoreResult(measure.nextContour()); ignoreResult(measure.getPosTan(distance[3], &position, &tangent)); ignoreResult(measure.getSegment(distance[4], distance[5], &dst, (bits >> 2) & 1)); }
std::unique_ptr<GrFragmentProcessor> GrMagnifierEffect::TestCreate(GrProcessorTestData* d) { sk_sp<GrTextureProxy> proxy = d->textureProxy(0); const int kMaxWidth = 200; const int kMaxHeight = 200; const SkScalar kMaxInset = 20.0f; uint32_t width = d->fRandom->nextULessThan(kMaxWidth); uint32_t height = d->fRandom->nextULessThan(kMaxHeight); SkScalar inset = d->fRandom->nextRangeScalar(1.0f, kMaxInset); SkIRect bounds = SkIRect::MakeWH(SkIntToScalar(kMaxWidth), SkIntToScalar(kMaxHeight)); SkRect srcRect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height)); auto effect = GrMagnifierEffect::Make(std::move(proxy), bounds, srcRect, srcRect.width() / bounds.width(), srcRect.height() / bounds.height(), bounds.width() / inset, bounds.height() / inset); SkASSERT(effect); return effect; }
void Viewer::setupCurrentSlide(int previousSlide) { if (fCurrentSlide == previousSlide) { return; // no change; do nothing } // prepare dimensions for image slides fSlides[fCurrentSlide]->load(SkIntToScalar(fWindow->width()), SkIntToScalar(fWindow->height())); fGesture.reset(); fDefaultMatrix.reset(); fDefaultMatrixInv.reset(); if (fWindow->supportsContentRect() && fWindow->scaleContentToFit()) { const SkRect contentRect = fWindow->getContentRect(); const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions(); const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height()); if (contentRect.width() > 0 && contentRect.height() > 0) { fDefaultMatrix.setRectToRect(slideBounds, contentRect, SkMatrix::kStart_ScaleToFit); SkAssertResult(fDefaultMatrix.invert(&fDefaultMatrixInv)); } } if (fWindow->supportsContentRect()) { const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions(); SkRect windowRect = fWindow->getContentRect(); fDefaultMatrixInv.mapRect(&windowRect); fGesture.setTransLimit(SkRect::MakeWH(SkIntToScalar(slideSize.width()), SkIntToScalar(slideSize.height())), windowRect); } this->updateTitle(); this->updateUIState(); if (previousSlide >= 0) { fSlides[previousSlide]->unload(); } fWindow->inval(); }
void GLWebViewState::setVisibleContentRect(const SkRect& visibleContentRect, float scale) { // allocate max possible number of tiles visible with this visibleContentRect / expandedTileBounds const float invTileContentWidth = scale / TilesManager::tileWidth(); const float invTileContentHeight = scale / TilesManager::tileHeight(); int viewMaxTileX = static_cast<int>(ceilf((visibleContentRect.width()-1) * invTileContentWidth)) + 1; int viewMaxTileY = static_cast<int>(ceilf((visibleContentRect.height()-1) * invTileContentHeight)) + 1; TilesManager* tilesManager = TilesManager::instance(); int maxTextureCount = viewMaxTileX * viewMaxTileY * (tilesManager->highEndGfx() ? 4 : 2); tilesManager->setCurrentTextureCount(maxTextureCount); // TODO: investigate whether we can move this return earlier. if ((m_visibleContentRect == visibleContentRect) && (m_scale == scale)) { // everything below will stay the same, early return. m_isVisibleContentRectScrolling = false; return; } m_scale = scale; m_goingDown = m_visibleContentRect.fTop - visibleContentRect.fTop <= 0; m_goingLeft = m_visibleContentRect.fLeft - visibleContentRect.fLeft >= 0; // detect visibleContentRect scrolling from short programmatic scrolls/jumps m_isVisibleContentRectScrolling = m_visibleContentRect != visibleContentRect && SkRect::Intersects(m_visibleContentRect, visibleContentRect); m_visibleContentRect = visibleContentRect; ALOGV("New visibleContentRect %.2f - %.2f %.2f - %.2f (w: %2.f h: %.2f scale: %.2f )", m_visibleContentRect.fLeft, m_visibleContentRect.fTop, m_visibleContentRect.fRight, m_visibleContentRect.fBottom, m_visibleContentRect.width(), m_visibleContentRect.height(), scale); }
virtual void onDraw(SkCanvas* canvas) { SkPath path; path.moveTo(0, 0); path.cubicTo(140, 150, 40, 10, 170, 150); SkPaint paint; SkRect bounds = path.getBounds(); for (SkScalar dy = -1; dy <= 1; dy += 1) { canvas->save(); for (SkScalar dx = -1; dx <= 1; dx += 1) { canvas->save(); canvas->clipRect(bounds); canvas->translate(dx, dy); canvas->drawPath(path, paint); canvas->restore(); canvas->translate(bounds.width(), 0); } canvas->restore(); canvas->translate(0, bounds.height()); } }
sk_sp<SkSpecialImage> SkImageFilter::DrawWithFP(GrContext* context, sk_sp<GrFragmentProcessor> fp, const SkIRect& bounds) { GrPaint paint; paint.addColorFragmentProcessor(fp.get()); paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); sk_sp<GrDrawContext> drawContext(context->newDrawContext(GrContext::kLoose_BackingFit, bounds.width(), bounds.height(), kRGBA_8888_GrPixelConfig)); if (!drawContext) { return nullptr; } SkRect srcRect = SkRect::Make(bounds); SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); GrClip clip(dstRect); drawContext->fillRectToRect(clip, paint, SkMatrix::I(), dstRect, srcRect); return SkSpecialImage::MakeFromGpu(SkIRect::MakeWH(bounds.width(), bounds.height()), kNeedNewImageUniqueID_SpecialImage, drawContext->asTexture()); }
static void SkiaGetGlyphWidthAndExtents(SkPaint* paint, hb_codepoint_t codepoint, hb_position_t* width, hb_glyph_extents_t* extents) { ALOG_ASSERT(codepoint <= 0xFFFF); paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); SkScalar skWidth; SkRect skBounds; uint16_t glyph = codepoint; paint->getTextWidths(&glyph, sizeof(glyph), &skWidth, &skBounds); if (kDebugGlyphs) { ALOGD("returned glyph for %i: width = %f", codepoint, skWidth); } if (width) *width = SkScalarToHBFixed(skWidth); if (extents) { // Invert y-axis because Skia is y-grows-down but we set up harfbuzz to be y-grows-up. extents->x_bearing = SkScalarToHBFixed(skBounds.fLeft); extents->y_bearing = SkScalarToHBFixed(-skBounds.fTop); extents->width = SkScalarToHBFixed(skBounds.width()); extents->height = SkScalarToHBFixed(-skBounds.height()); } }
static void toString(const SkRRect& rrect, SkString* str) { SkRect r = rrect.getBounds(); str->appendf("[%g,%g %g:%g]", SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop), SkScalarToFloat(r.width()), SkScalarToFloat(r.height())); if (rrect.isOval()) { str->append("()"); } else if (rrect.isSimple()) { const SkVector& rad = rrect.getSimpleRadii(); str->appendf("(%g,%g)", rad.x(), rad.y()); } else if (rrect.isComplex()) { SkVector radii[4] = { rrect.radii(SkRRect::kUpperLeft_Corner), rrect.radii(SkRRect::kUpperRight_Corner), rrect.radii(SkRRect::kLowerRight_Corner), rrect.radii(SkRRect::kLowerLeft_Corner), }; str->appendf("(%g,%g %g,%g %g,%g %g,%g)", radii[0].x(), radii[0].y(), radii[1].x(), radii[1].y(), radii[2].x(), radii[2].y(), radii[3].x(), radii[3].y()); } }
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()); }
bool SkImageSource::onFilterImageDeprecated(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkRect dstRect; ctx.ctm().mapRect(&dstRect, fDstRect); SkRect bounds = SkRect::MakeIWH(fImage->width(), fImage->height()); if (fSrcRect == bounds && dstRect == bounds) { // No regions cropped out or resized; return entire image. offset->fX = offset->fY = 0; return fImage->asLegacyBitmap(result, SkImage::kRO_LegacyBitmapMode); } const SkIRect dstIRect = dstRect.roundOut(); SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(dstIRect.width(), dstIRect.height())); if (nullptr == device.get()) { return false; } SkCanvas canvas(device.get()); SkPaint paint; // Subtract off the integer component of the translation (will be applied in loc, below). dstRect.offset(-SkIntToScalar(dstIRect.fLeft), -SkIntToScalar(dstIRect.fTop)); paint.setXfermodeMode(SkXfermode::kSrc_Mode); // FIXME: this probably shouldn't be necessary, but drawImageRect asserts // None filtering when it's translate-only paint.setFilterQuality( fSrcRect.width() == dstRect.width() && fSrcRect.height() == dstRect.height() ? kNone_SkFilterQuality : fFilterQuality); canvas.drawImageRect(fImage, fSrcRect, dstRect, &paint, SkCanvas::kStrict_SrcRectConstraint); *result = device.get()->accessBitmap(false); offset->fX = dstIRect.fLeft; offset->fY = dstIRect.fTop; return true; }
void bounceMe() { SkScalar width = fSrcR.width(); bounce(&fSrcR.fLeft, &fDX, fLimitR.fLeft, fLimitR.fRight - width); fSrcR.fRight = fSrcR.fLeft + width; }
static void toString(const SkRect& r, SkString* str) { str->appendf("[%g,%g %g:%g]", SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop), SkScalarToFloat(r.width()), SkScalarToFloat(r.height())); }
* * 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.
bool VideoLayerAndroid::drawGL() { // Lazily allocated the textures. if (!m_createdTexture) { m_backgroundTextureId = createBackgroundTexture(); m_spinnerOuterTextureId = createSpinnerOuterTexture(); m_spinnerInnerTextureId = createSpinnerInnerTexture(); m_posterTextureId = createPosterTexture(); m_createdTexture = true; } SkRect rect = SkRect::MakeSize(getSize()); GLfloat surfaceMatrix[16]; SkRect innerRect = SkRect(buttonRect); if (innerRect.contains(rect)) innerRect = rect; innerRect.offset((rect.width() - IMAGESIZE) / 2 , (rect.height() - IMAGESIZE) / 2); // Draw the poster image, the progressing image or the Video depending // on the player's state. if (m_playerState == PREPARING) { // Show the progressing animation, with two rotating circles // SSG if (m_surfaceTexture.get() && (m_surfaceTexture->getTimestamp() > 0)) { m_surfaceTexture->getTransformMatrix(surfaceMatrix); GLuint textureId = TilesManager::instance()->videoLayerManager()->getTextureId(uniqueId()); TilesManager::instance()->shader()->drawVideoLayerQuad(m_drawTransform, surfaceMatrix, rect, textureId); TilesManager::instance()->videoLayerManager()->updateMatrix(uniqueId(), surfaceMatrix); } else { TilesManager::instance()->shader()->drawLayerQuad(m_drawTransform, rect, m_backgroundTextureId, 1, true); } TransformationMatrix addReverseRotation; TransformationMatrix addRotation = m_drawTransform; addRotation.translate(innerRect.fLeft, innerRect.fTop); addRotation.translate(IMAGESIZE / 2, IMAGESIZE / 2); addReverseRotation = addRotation; addRotation.rotate(m_rotateDegree); addRotation.translate(-IMAGESIZE / 2, -IMAGESIZE / 2); SkRect size = SkRect::MakeWH(innerRect.width(), innerRect.height()); TilesManager::instance()->shader()->drawLayerQuad(addRotation, size, m_spinnerOuterTextureId, 1, true); addReverseRotation.rotate(-m_rotateDegree); addReverseRotation.translate(-IMAGESIZE / 2, -IMAGESIZE / 2); TilesManager::instance()->shader()->drawLayerQuad(addReverseRotation, size, m_spinnerInnerTextureId, 1, true); m_rotateDegree += ROTATESTEP; } else if (m_playerState == PLAYING && m_surfaceTexture.get()) { // Show the real video. m_surfaceTexture->updateTexImage(); m_surfaceTexture->getTransformMatrix(surfaceMatrix); GLuint textureId = TilesManager::instance()->videoLayerManager()->getTextureId(uniqueId()); TilesManager::instance()->shader()->drawVideoLayerQuad(m_drawTransform, surfaceMatrix, rect, textureId); TilesManager::instance()->videoLayerManager()->updateMatrix(uniqueId(), surfaceMatrix); } else { GLuint textureId = TilesManager::instance()->videoLayerManager()->getTextureId(uniqueId()); GLfloat* matrix = TilesManager::instance()->videoLayerManager()->getMatrix(uniqueId()); if (textureId && matrix) { // Show the screen shot for each video. TilesManager::instance()->shader()->drawVideoLayerQuad(m_drawTransform, matrix, rect, textureId); } else { // Show the static poster b/c there is no screen shot available. TilesManager::instance()->shader()->drawLayerQuad(m_drawTransform, rect, m_backgroundTextureId, 1, true); TilesManager::instance()->shader()->drawLayerQuad(m_drawTransform, innerRect, m_posterTextureId, 1, true); } } return drawChildrenGL(); }
virtual void onDrawContent(SkCanvas* canvas) { SkRect r = { 0, 0, SkIntToScalar(gWidth*2), SkIntToScalar(gHeight*2) }; static const char* gConfigNames[] = { "8888", "565", "4444" }; static const bool gFilters[] = { false, true }; static const char* gFilterNames[] = { "point", "bilinear" }; static const SkShader::TileMode gModes[] = { SkShader::kClamp_TileMode, SkShader::kRepeat_TileMode, SkShader::kMirror_TileMode }; static const char* gModeNames[] = { "C", "R", "M" }; SkScalar y = SkIntToScalar(24); SkScalar x = SkIntToScalar(10); SkPictureRecorder recorder; SkCanvas* textCanvas = NULL; if (NULL == fTextPicture) { textCanvas = recorder.beginRecording(1000, 1000, NULL, 0); } if (NULL != textCanvas) { for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) { for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) { SkPaint p; SkString str; p.setAntiAlias(true); p.setDither(true); p.setLooper(fLooper); str.printf("[%s,%s]", gModeNames[kx], gModeNames[ky]); p.setTextAlign(SkPaint::kCenter_Align); textCanvas->drawText(str.c_str(), str.size(), x + r.width()/2, y, p); x += r.width() * 4 / 3; } } } y += SkIntToScalar(16); for (size_t i = 0; i < SK_ARRAY_COUNT(gColorTypes); i++) { for (size_t j = 0; j < SK_ARRAY_COUNT(gFilters); j++) { x = SkIntToScalar(10); for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) { for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) { SkPaint paint; setup(&paint, fTexture[i], gFilters[j], gModes[kx], gModes[ky]); paint.setDither(true); canvas->save(); canvas->translate(x, y); canvas->drawRect(r, paint); canvas->restore(); x += r.width() * 4 / 3; } } if (NULL != textCanvas) { SkPaint p; SkString str; p.setAntiAlias(true); p.setLooper(fLooper); str.printf("%s, %s", gConfigNames[i], gFilterNames[j]); textCanvas->drawText(str.c_str(), str.size(), x, y + r.height() * 2 / 3, p); } y += r.height() * 4 / 3; } } if (NULL != textCanvas) { SkASSERT(NULL == fTextPicture); fTextPicture.reset(recorder.endRecording()); } SkASSERT(NULL != fTextPicture); canvas->drawPicture(*fTextPicture); }
SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { fState.get()->fImage.lockPixels(); SkMatrix finalMatrix = fState.get()->fCanvasTransform; finalMatrix.preConcat(fState.get()->fShaderTransform); SkRect surfaceBBox; surfaceBBox.set(fState.get()->fBBox); transformBBox(finalMatrix, &surfaceBBox); SkMatrix unflip; unflip.setTranslate(0, SkScalarRound(surfaceBBox.height())); unflip.preScale(SK_Scalar1, -SK_Scalar1); SkISize size = SkISize::Make(SkScalarRound(surfaceBBox.width()), SkScalarRound(surfaceBBox.height())); SkPDFDevice pattern(size, size, unflip); SkCanvas canvas(&pattern); canvas.translate(-surfaceBBox.fLeft, -surfaceBBox.fTop); finalMatrix.preTranslate(surfaceBBox.fLeft, surfaceBBox.fTop); const SkBitmap* image = &fState.get()->fImage; int width = image->width(); int height = image->height(); SkShader::TileMode tileModes[2]; tileModes[0] = fState.get()->fImageTileModes[0]; tileModes[1] = fState.get()->fImageTileModes[1]; canvas.drawBitmap(*image, 0, 0); SkRect patternBBox = SkRect::MakeXYWH(-surfaceBBox.fLeft, -surfaceBBox.fTop, width, 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); canvas.drawBitmapMatrix(*image, xMirror); patternBBox.fRight += width; } if (tileModes[1] == SkShader::kMirror_TileMode) { SkMatrix yMirror; yMirror.setScale(SK_Scalar1, -SK_Scalar1); yMirror.postTranslate(0, 2 * height); canvas.drawBitmapMatrix(*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); canvas.drawBitmapMatrix(*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(surfaceBBox.fLeft, surfaceBBox.fTop, 0, 0); if (!rect.isEmpty()) { paint.setColor(image->getColor(0, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, surfaceBBox.fTop, surfaceBBox.fRight, 0); if (!rect.isEmpty()) { paint.setColor(image->getColor(width - 1, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, height, surfaceBBox.fRight, surfaceBBox.fBottom); if (!rect.isEmpty()) { paint.setColor(image->getColor(width - 1, height - 1)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(surfaceBBox.fLeft, height, 0, surfaceBBox.fBottom); if (!rect.isEmpty()) { paint.setColor(image->getColor(0, 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, height); if (surfaceBBox.fLeft < 0) { SkBitmap left; SkAssertResult(image->extractSubset(&left, subset)); SkMatrix leftMatrix; leftMatrix.setScale(-surfaceBBox.fLeft, 1); leftMatrix.postTranslate(surfaceBBox.fLeft, 0); canvas.drawBitmapMatrix(left, leftMatrix); if (tileModes[1] == SkShader::kMirror_TileMode) { leftMatrix.postScale(SK_Scalar1, -SK_Scalar1); leftMatrix.postTranslate(0, 2 * height); canvas.drawBitmapMatrix(left, leftMatrix); } patternBBox.fLeft = 0; } if (surfaceBBox.fRight > width) { SkBitmap right; subset.offset(width - 1, 0); SkAssertResult(image->extractSubset(&right, subset)); SkMatrix rightMatrix; rightMatrix.setScale(surfaceBBox.fRight - width, 1); rightMatrix.postTranslate(width, 0); canvas.drawBitmapMatrix(right, rightMatrix); if (tileModes[1] == SkShader::kMirror_TileMode) { rightMatrix.postScale(SK_Scalar1, -SK_Scalar1); rightMatrix.postTranslate(0, 2 * height); canvas.drawBitmapMatrix(right, rightMatrix); } patternBBox.fRight = surfaceBBox.width(); } } if (tileModes[1] == SkShader::kClamp_TileMode) { SkIRect subset = SkIRect::MakeXYWH(0, 0, width, 1); if (surfaceBBox.fTop < 0) { SkBitmap top; SkAssertResult(image->extractSubset(&top, subset)); SkMatrix topMatrix; topMatrix.setScale(SK_Scalar1, -surfaceBBox.fTop); topMatrix.postTranslate(0, surfaceBBox.fTop); canvas.drawBitmapMatrix(top, topMatrix); if (tileModes[0] == SkShader::kMirror_TileMode) { topMatrix.postScale(-1, 1); topMatrix.postTranslate(2 * width, 0); canvas.drawBitmapMatrix(top, topMatrix); } patternBBox.fTop = 0; } if (surfaceBBox.fBottom > height) { SkBitmap bottom; subset.offset(0, height - 1); SkAssertResult(image->extractSubset(&bottom, subset)); SkMatrix bottomMatrix; bottomMatrix.setScale(SK_Scalar1, surfaceBBox.fBottom - height); bottomMatrix.postTranslate(0, height); canvas.drawBitmapMatrix(bottom, bottomMatrix); if (tileModes[0] == SkShader::kMirror_TileMode) { bottomMatrix.postScale(-1, 1); bottomMatrix.postTranslate(2 * width, 0); canvas.drawBitmapMatrix(bottom, bottomMatrix); } patternBBox.fBottom = surfaceBBox.height(); } } SkRefPtr<SkPDFArray> patternBBoxArray = new SkPDFArray; patternBBoxArray->unref(); // SkRefPtr and new both took a reference. patternBBoxArray->reserve(4); patternBBoxArray->appendScalar(patternBBox.fLeft); patternBBoxArray->appendScalar(patternBBox.fTop); patternBBoxArray->appendScalar(patternBBox.fRight); patternBBoxArray->appendScalar(patternBBox.fBottom); // Put the canvas into the pattern stream (fContent). SkRefPtr<SkStream> content = pattern.content(); content->unref(); // SkRefPtr and content() both took a reference. pattern.getResources(&fResources); setData(content.get()); insertName("Type", "Pattern"); insertInt("PatternType", 1); insertInt("PaintType", 1); insertInt("TilingType", 1); insert("BBox", patternBBoxArray.get()); insertScalar("XStep", patternBBox.width()); insertScalar("YStep", patternBBox.height()); insert("Resources", pattern.getResourceDict()); insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref(); fState.get()->fImage.unlockPixels(); }
virtual void onDraw(SkCanvas* canvas) { struct FillAndName { SkPath::FillType fFill; const char* fName; }; static const FillAndName gFills[] = { {SkPath::kWinding_FillType, "Winding"}, {SkPath::kEvenOdd_FillType, "Even / Odd"}, {SkPath::kInverseWinding_FillType, "Inverse Winding"}, {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"}, }; struct StyleAndName { SkPaint::Style fStyle; const char* fName; }; static const StyleAndName gStyles[] = { {SkPaint::kFill_Style, "Fill"}, {SkPaint::kStroke_Style, "Stroke"}, {SkPaint::kStrokeAndFill_Style, "Stroke And Fill"}, }; struct CapAndName { SkPaint::Cap fCap; SkPaint::Join fJoin; const char* fName; }; static const CapAndName gCaps[] = { {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"}, {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"}, {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"} }; struct PathAndName { SkPath fPath; const char* fName; }; PathAndName path; path.fPath.moveTo(25*SK_Scalar1, 10*SK_Scalar1); path.fPath.cubicTo(40*SK_Scalar1, 20*SK_Scalar1, 60*SK_Scalar1, 20*SK_Scalar1, 75*SK_Scalar1, 10*SK_Scalar1); path.fPath.close(); path.fName = "moveTo-cubic-close"; SkPaint titlePaint; titlePaint.setColor(SK_ColorBLACK); titlePaint.setAntiAlias(true); sk_tool_utils::set_portable_typeface(&titlePaint); titlePaint.setTextSize(15 * SK_Scalar1); const char title[] = "Cubic Closed Drawn Into Rectangle Clips With " "Indicated Style, Fill and Linecaps, with stroke width 10"; canvas->drawText(title, strlen(title), 20 * SK_Scalar1, 20 * SK_Scalar1, titlePaint); SkRandom rand; SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1); canvas->save(); canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1); canvas->save(); for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) { if (0 < cap) { canvas->translate((rect.width() + 40 * SK_Scalar1) * SK_ARRAY_COUNT(gStyles), 0); } canvas->save(); for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) { if (0 < fill) { canvas->translate(0, rect.height() + 40 * SK_Scalar1); } canvas->save(); for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) { if (0 < style) { canvas->translate(rect.width() + 40 * SK_Scalar1, 0); } SkColor color = 0xff007000; this->drawPath(path.fPath, canvas, color, rect, gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle, gFills[fill].fFill, SK_Scalar1*10); SkPaint rectPaint; rectPaint.setColor(SK_ColorBLACK); rectPaint.setStyle(SkPaint::kStroke_Style); rectPaint.setStrokeWidth(-1); rectPaint.setAntiAlias(true); canvas->drawRect(rect, rectPaint); SkPaint labelPaint; labelPaint.setColor(color); labelPaint.setAntiAlias(true); sk_tool_utils::set_portable_typeface(&labelPaint); labelPaint.setTextSize(10 * SK_Scalar1); canvas->drawText(gStyles[style].fName, strlen(gStyles[style].fName), 0, rect.height() + 12 * SK_Scalar1, labelPaint); canvas->drawText(gFills[fill].fName, strlen(gFills[fill].fName), 0, rect.height() + 24 * SK_Scalar1, labelPaint); canvas->drawText(gCaps[cap].fName, strlen(gCaps[cap].fName), 0, rect.height() + 36 * SK_Scalar1, labelPaint); } canvas->restore(); } canvas->restore(); } canvas->restore(); canvas->restore(); }
FloatRect::FloatRect(const SkRect& r) : m_location(r.fLeft, r.fTop), m_size(r.width(), r.height()) { }
virtual void onDraw(SkCanvas* canvas) { static const AddSegmentFunc gSegmentFunctions[] = { AddMove, AddMoveClose, AddDegenLine, AddMoveDegenLine, AddMoveDegenLineClose, AddDegenQuad, AddMoveDegenQuad, AddMoveDegenQuadClose, AddDegenCubic, AddMoveDegenCubic, AddMoveDegenCubicClose, AddClose, AddLine, AddMoveLine, AddMoveLineClose, AddQuad, AddMoveQuad, AddMoveQuadClose, AddCubic, AddMoveCubic, AddMoveCubicClose }; static const char* gSegmentNames[] = { "Move", "MoveClose", "DegenLine", "MoveDegenLine", "MoveDegenLineClose", "DegenQuad", "MoveDegenQuad", "MoveDegenQuadClose", "DegenCubic", "MoveDegenCubic", "MoveDegenCubicClose", "Close", "Line", "MoveLine", "MoveLineClose", "Quad", "MoveQuad", "MoveQuadClose", "Cubic", "MoveCubic", "MoveCubicClose" }; struct FillAndName { SkPath::FillType fFill; const char* fName; }; static const FillAndName gFills[] = { {SkPath::kWinding_FillType, "Winding"}, {SkPath::kEvenOdd_FillType, "Even / Odd"}, {SkPath::kInverseWinding_FillType, "Inverse Winding"}, {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"} }; struct StyleAndName { SkPaint::Style fStyle; const char* fName; }; static const StyleAndName gStyles[] = { {SkPaint::kFill_Style, "Fill"}, {SkPaint::kStroke_Style, "Stroke 10"}, {SkPaint::kStrokeAndFill_Style, "Stroke 10 And Fill"} }; struct CapAndName { SkPaint::Cap fCap; SkPaint::Join fJoin; const char* fName; }; static const CapAndName gCaps[] = { {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"}, {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"}, {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"} }; SkPaint titlePaint; titlePaint.setColor(SK_ColorBLACK); titlePaint.setAntiAlias(true); titlePaint.setLCDRenderText(true); titlePaint.setTextSize(15 * SK_Scalar1); const char title[] = "Random Paths Drawn Into Rectangle Clips With " "Indicated Style, Fill and Linecaps, " "with Stroke width 6"; canvas->drawText(title, strlen(title), 20 * SK_Scalar1, 20 * SK_Scalar1, titlePaint); SkRandom rand; SkRect rect = SkRect::MakeWH(220*SK_Scalar1, 50*SK_Scalar1); canvas->save(); canvas->translate(2*SK_Scalar1, 30 * SK_Scalar1); // The title canvas->save(); unsigned numSegments = SK_ARRAY_COUNT(gSegmentFunctions); unsigned numCaps = SK_ARRAY_COUNT(gCaps); unsigned numStyles = SK_ARRAY_COUNT(gStyles); unsigned numFills = SK_ARRAY_COUNT(gFills); for (size_t row = 0; row < 6; ++row) { if (0 < row) { canvas->translate(0, rect.height() + 100*SK_Scalar1); } canvas->save(); for (size_t column = 0; column < 4; ++column) { if (0 < column) { canvas->translate(rect.width() + 4*SK_Scalar1, 0); } SkColor color = 0xff007000; StyleAndName style = gStyles[(rand.nextU() >> 16) % numStyles]; CapAndName cap = gCaps[(rand.nextU() >> 16) % numCaps]; FillAndName fill = gFills[(rand.nextU() >> 16) % numFills]; SkPath path; unsigned s1 = (rand.nextU() >> 16) % numSegments; unsigned s2 = (rand.nextU() >> 16) % numSegments; unsigned s3 = (rand.nextU() >> 16) % numSegments; unsigned s4 = (rand.nextU() >> 16) % numSegments; unsigned s5 = (rand.nextU() >> 16) % numSegments; SkPoint pt = SkPoint::Make(10*SK_Scalar1, 0); pt = gSegmentFunctions[s1](path, pt); pt = gSegmentFunctions[s2](path, pt); pt = gSegmentFunctions[s3](path, pt); pt = gSegmentFunctions[s4](path, pt); pt = gSegmentFunctions[s5](path, pt); this->drawPath(path, canvas, color, rect, cap.fCap, cap.fJoin, style.fStyle, fill.fFill, SK_Scalar1*6); SkPaint rectPaint; rectPaint.setColor(SK_ColorBLACK); rectPaint.setStyle(SkPaint::kStroke_Style); rectPaint.setStrokeWidth(-1); rectPaint.setAntiAlias(true); canvas->drawRect(rect, rectPaint); SkPaint labelPaint; labelPaint.setColor(color); labelPaint.setAntiAlias(true); labelPaint.setLCDRenderText(true); labelPaint.setTextSize(10 * SK_Scalar1); canvas->drawText(style.fName, strlen(style.fName), 0, rect.height() + 12 * SK_Scalar1, labelPaint); canvas->drawText(fill.fName, strlen(fill.fName), 0, rect.height() + 24 * SK_Scalar1, labelPaint); canvas->drawText(cap.fName, strlen(cap.fName), 0, rect.height() + 36 * SK_Scalar1, labelPaint); canvas->drawText(gSegmentNames[s1], strlen(gSegmentNames[s1]), 0, rect.height() + 48 * SK_Scalar1, labelPaint); canvas->drawText(gSegmentNames[s2], strlen(gSegmentNames[s2]), 0, rect.height() + 60 * SK_Scalar1, labelPaint); canvas->drawText(gSegmentNames[s3], strlen(gSegmentNames[s3]), 0, rect.height() + 72 * SK_Scalar1, labelPaint); canvas->drawText(gSegmentNames[s4], strlen(gSegmentNames[s4]), 0, rect.height() + 84 * SK_Scalar1, labelPaint); canvas->drawText(gSegmentNames[s5], strlen(gSegmentNames[s5]), 0, rect.height() + 96 * SK_Scalar1, labelPaint); } canvas->restore(); } canvas->restore(); canvas->restore(); }
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; }
SkPDFShader::State::State(const SkShader& shader, const SkMatrix& canvasTransform, const SkIRect& bbox, SkScalar rasterScale) : fCanvasTransform(canvasTransform), fBBox(bbox), fPixelGeneration(0) { fInfo.fColorCount = 0; fInfo.fColors = NULL; fInfo.fColorOffsets = NULL; fShaderTransform = shader.getLocalMatrix(); fImageTileModes[0] = fImageTileModes[1] = SkShader::kClamp_TileMode; fType = shader.asAGradient(&fInfo); if (fType == SkShader::kNone_GradientType) { SkShader::BitmapType bitmapType; SkMatrix matrix; bitmapType = shader.asABitmap(&fImage, &matrix, fImageTileModes); if (bitmapType != SkShader::kDefault_BitmapType) { // Generic fallback for unsupported shaders: // * allocate a bbox-sized bitmap // * shade the whole area // * use the result as a bitmap shader // bbox is in device space. While that's exactly what we want for sizing our bitmap, // we need to map it into shader space for adjustments (to match // SkPDFImageShader::Create's behavior). SkRect shaderRect = SkRect::Make(bbox); if (!inverse_transform_bbox(canvasTransform, &shaderRect)) { fImage.reset(); return; } // Clamp the bitmap size to about 1M pixels static const SkScalar kMaxBitmapArea = 1024 * 1024; SkScalar bitmapArea = rasterScale * bbox.width() * rasterScale * bbox.height(); if (bitmapArea > kMaxBitmapArea) { rasterScale *= SkScalarSqrt(SkScalarDiv(kMaxBitmapArea, bitmapArea)); } SkISize size = SkISize::Make(SkScalarRoundToInt(rasterScale * bbox.width()), SkScalarRoundToInt(rasterScale * bbox.height())); SkSize scale = SkSize::Make(SkIntToScalar(size.width()) / shaderRect.width(), SkIntToScalar(size.height()) / shaderRect.height()); fImage.allocN32Pixels(size.width(), size.height()); fImage.eraseColor(SK_ColorTRANSPARENT); SkPaint p; p.setShader(const_cast<SkShader*>(&shader)); SkCanvas canvas(fImage); canvas.scale(scale.width(), scale.height()); canvas.translate(-shaderRect.x(), -shaderRect.y()); canvas.drawPaint(p); fShaderTransform.setTranslate(shaderRect.x(), shaderRect.y()); fShaderTransform.preScale(1 / scale.width(), 1 / scale.height()); } else { SkASSERT(matrix.isIdentity()); } fPixelGeneration = fImage.getGenerationID(); } else { AllocateGradientInfoStorage(); shader.asAGradient(&fInfo); } }
static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkRect& srcRect, const SkRect& destRect, const SkXfermode::Mode& compOp) { #if PLATFORM(CHROMIUM) TRACE_EVENT0("skia", "paintSkBitmap"); #endif SkPaint paint; paint.setXfermodeMode(compOp); paint.setAlpha(platformContext->getNormalizedAlpha()); paint.setLooper(platformContext->getDrawLooper()); // only antialias if we're rotated or skewed paint.setAntiAlias(hasNon90rotation(platformContext)); SkCanvas* canvas = platformContext->canvas(); ResamplingMode resampling; if (platformContext->isAccelerated()) resampling = RESAMPLE_LINEAR; else if (platformContext->printing()) resampling = RESAMPLE_NONE; else { // Take into account scale applied to the canvas when computing sampling mode (e.g. CSS scale or page scale). SkRect destRectTarget = destRect; if (!(canvas->getTotalMatrix().getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask))) canvas->getTotalMatrix().mapRect(&destRectTarget, destRect); resampling = computeResamplingMode(canvas->getTotalMatrix(), bitmap, SkScalarToFloat(srcRect.width()), SkScalarToFloat(srcRect.height()), SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTarget.height())); } if (resampling == RESAMPLE_NONE) { // FIXME: This is to not break tests (it results in the filter bitmap flag // being set to true). We need to decide if we respect RESAMPLE_NONE // being returned from computeResamplingMode. resampling = RESAMPLE_LINEAR; } resampling = limitResamplingMode(platformContext, resampling); paint.setFilterBitmap(resampling == RESAMPLE_LINEAR); if (resampling == RESAMPLE_AWESOME) drawResampledBitmap(*canvas, paint, bitmap, srcRect, destRect); else { // No resampling necessary, we can just draw the bitmap. We want to // filter it if we decided to do linear interpolation above, or if there // is something interesting going on with the matrix (like a rotation). // Note: for serialization, we will want to subset the bitmap first so // we don't send extra pixels. SkIRect enclosingSrcRect; SkRect enclosingDestRect; SkISize bitmapSize = SkISize::Make(bitmap.bitmap().width(), bitmap.bitmap().height()); bool needsClipping = computeBitmapDrawRects(bitmapSize, srcRect, destRect, &enclosingSrcRect, &enclosingDestRect); if (enclosingSrcRect.isEmpty() || enclosingDestRect.isEmpty()) return; // If destination is enlarged because source rectangle didn't align to // integer boundaries then we draw a slightly larger rectangle and clip // to the original destination rectangle. // See http://crbug.com/145540. if (needsClipping) { platformContext->save(); platformContext->canvas()->clipRect(destRect); } canvas->drawBitmapRect(bitmap.bitmap(), &enclosingSrcRect, enclosingDestRect, &paint); if (needsClipping) platformContext->restore(); } platformContext->didDrawRect(destRect, paint, &bitmap.bitmap()); }
void Image::drawPattern(GraphicsContext* context, const FloatRect& floatSrcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator compositeOp, const FloatRect& destRect) { #if PLATFORM(CHROMIUM) TRACE_EVENT0("skia", "Image::drawPattern"); #endif FloatRect normSrcRect = normalizeRect(floatSrcRect); if (destRect.isEmpty() || normSrcRect.isEmpty()) return; // nothing to draw NativeImageSkia* bitmap = nativeImageForCurrentFrame(); if (!bitmap) return; SkMatrix ctm = context->platformContext()->canvas()->getTotalMatrix(); SkMatrix totalMatrix; totalMatrix.setConcat(ctm, patternTransform); // Figure out what size the bitmap will be in the destination. The // destination rect is the bounds of the pattern, we need to use the // matrix to see how big it will be. SkRect destRectTarget; totalMatrix.mapRect(&destRectTarget, normSrcRect); float destBitmapWidth = SkScalarToFloat(destRectTarget.width()); float destBitmapHeight = SkScalarToFloat(destRectTarget.height()); // Compute the resampling mode. ResamplingMode resampling; if (context->platformContext()->isAccelerated() || context->platformContext()->printing()) resampling = RESAMPLE_LINEAR; else resampling = computeResamplingMode(totalMatrix, *bitmap, normSrcRect.width(), normSrcRect.height(), destBitmapWidth, destBitmapHeight); resampling = limitResamplingMode(context->platformContext(), resampling); // Load the transform WebKit requested. SkMatrix matrix(patternTransform); SkShader* shader; if (resampling == RESAMPLE_AWESOME) { // Do nice resampling. float scaleX = destBitmapWidth / normSrcRect.width(); float scaleY = destBitmapHeight / normSrcRect.height(); SkRect scaledSrcRect; SkIRect enclosingScaledSrcRect; // The image fragment generated here is not exactly what is // requested. The scale factor used is approximated and image // fragment is slightly larger to align to integer // boundaries. SkBitmap resampled = extractScaledImageFragment(*bitmap, normSrcRect, scaleX, scaleY, &scaledSrcRect, &enclosingScaledSrcRect); shader = SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); // Since we just resized the bitmap, we need to remove the scale // applied to the pixels in the bitmap shader. This means we need // CTM * patternTransform to have identity scale. Since we // can't modify CTM (or the rectangle will be drawn in the wrong // place), we must set patternTransform's scale to the inverse of // CTM scale. matrix.setScaleX(ctm.getScaleX() ? 1 / ctm.getScaleX() : 1); matrix.setScaleY(ctm.getScaleY() ? 1 / ctm.getScaleY() : 1); } else { // No need to do nice resampling. SkBitmap srcSubset; bitmap->bitmap().extractSubset(&srcSubset, enclosingIntRect(normSrcRect)); shader = SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); } // We also need to translate it such that the origin of the pattern is the // origin of the destination rect, which is what WebKit expects. Skia uses // the coordinate system origin as the base for the patter. If WebKit wants // a shifted image, it will shift it from there using the patternTransform. float adjustedX = phase.x() + normSrcRect.x() * narrowPrecisionToFloat(patternTransform.a()); float adjustedY = phase.y() + normSrcRect.y() * narrowPrecisionToFloat(patternTransform.d()); matrix.postTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY)); shader->setLocalMatrix(matrix); SkPaint paint; paint.setShader(shader)->unref(); paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp)); paint.setFilterBitmap(resampling == RESAMPLE_LINEAR); context->platformContext()->paintSkPaint(destRect, paint); }
virtual void onDraw(SkCanvas* canvas) { if (!fInitialized) { this->make_checkerboard(); this->make_gradient_circle(64, 64); fInitialized = true; } canvas->clear(0x00000000); SkAutoTUnref<SkImageFilter> gradient(SkBitmapSource::Create(fGradientCircle)); SkAutoTUnref<SkImageFilter> checkerboard(SkBitmapSource::Create(fCheckerboard)); SkAutoTUnref<SkShader> noise(SkPerlinNoiseShader::CreateFractalNoise( SkDoubleToScalar(0.1), SkDoubleToScalar(0.05), 1, 0)); SkMatrix resizeMatrix; resizeMatrix.setScale(RESIZE_FACTOR_X, RESIZE_FACTOR_Y); SkImageFilter* filters[] = { SkBlurImageFilter::Create(SkIntToScalar(12), SkIntToScalar(12)), SkDropShadowImageFilter::Create(SkIntToScalar(10), SkIntToScalar(10), SkIntToScalar(3), SK_ColorGREEN), SkDisplacementMapEffect::Create(SkDisplacementMapEffect::kR_ChannelSelectorType, SkDisplacementMapEffect::kR_ChannelSelectorType, SkIntToScalar(12), gradient.get(), checkerboard.get()), SkDilateImageFilter::Create(2, 2, checkerboard.get()), SkErodeImageFilter::Create(2, 2, checkerboard.get()), SkOffsetImageFilter::Create(SkIntToScalar(-16), SkIntToScalar(32)), SkMatrixImageFilter::Create(resizeMatrix, SkPaint::kNone_FilterLevel), SkRectShaderImageFilter::Create(noise), }; SkRect r = SkRect::MakeWH(SkIntToScalar(64), SkIntToScalar(64)); SkScalar margin = SkIntToScalar(16); SkRect bounds = r; bounds.outset(margin, margin); for (int xOffset = 0; xOffset < 80; xOffset += 16) { canvas->save(); bounds.fLeft = SkIntToScalar(xOffset); for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) { SkPaint paint; paint.setColor(SK_ColorWHITE); paint.setImageFilter(filters[i]); paint.setAntiAlias(true); canvas->save(); canvas->clipRect(bounds); if (5 == i) { canvas->translate(SkIntToScalar(16), SkIntToScalar(-32)); } else if (6 == i) { canvas->scale(SkScalarInvert(RESIZE_FACTOR_X), SkScalarInvert(RESIZE_FACTOR_Y)); } canvas->drawCircle(r.centerX(), r.centerY(), SkScalarDiv(r.width()*2, SkIntToScalar(5)), paint); canvas->restore(); canvas->translate(r.width() + margin, 0); } canvas->restore(); canvas->translate(0, r.height() + margin); } for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) { SkSafeUnref(filters[i]); } }
void onDraw(SkCanvas* canvas) override { SkRect dstRect = { 0, 0, SkIntToScalar(64), SkIntToScalar(64)}; static const int kMaxSrcRectSize = 1 << (SkNextLog2(gBmpSize) + 2); static const int kPadX = 30; static const int kPadY = 40; SkPaint paint; paint.setAlpha(0x20); canvas->drawBitmapRect(fLargeBitmap, SkRect::MakeIWH(gSize, gSize), &paint); canvas->translate(SK_Scalar1 * kPadX / 2, SK_Scalar1 * kPadY / 2); SkPaint blackPaint; SkScalar titleHeight = SK_Scalar1 * 24; blackPaint.setColor(SK_ColorBLACK); blackPaint.setTextSize(titleHeight); blackPaint.setAntiAlias(true); sk_tool_utils::set_portable_typeface(&blackPaint); SkString title; title.printf("Bitmap size: %d x %d", gBmpSize, gBmpSize); canvas->drawText(title.c_str(), title.size(), 0, titleHeight, blackPaint); canvas->translate(0, SK_Scalar1 * kPadY / 2 + titleHeight); int rowCount = 0; canvas->save(); for (int w = 1; w <= kMaxSrcRectSize; w *= 4) { for (int h = 1; h <= kMaxSrcRectSize; h *= 4) { SkIRect srcRect = SkIRect::MakeXYWH((gBmpSize - w) / 2, (gBmpSize - h) / 2, w, h); fProc(canvas, fImage, fLargeBitmap, srcRect, dstRect); SkString label; label.appendf("%d x %d", w, h); blackPaint.setAntiAlias(true); blackPaint.setStyle(SkPaint::kFill_Style); blackPaint.setTextSize(SK_Scalar1 * 10); SkScalar baseline = dstRect.height() + blackPaint.getTextSize() + SK_Scalar1 * 3; canvas->drawText(label.c_str(), label.size(), 0, baseline, blackPaint); blackPaint.setStyle(SkPaint::kStroke_Style); blackPaint.setStrokeWidth(SK_Scalar1); blackPaint.setAntiAlias(false); canvas->drawRect(dstRect, blackPaint); canvas->translate(dstRect.width() + SK_Scalar1 * kPadX, 0); ++rowCount; if ((dstRect.width() + kPadX) * rowCount > gSize) { canvas->restore(); canvas->translate(0, dstRect.height() + SK_Scalar1 * kPadY); canvas->save(); rowCount = 0; } } } { // test the following code path: // SkGpuDevice::drawPath() -> SkGpuDevice::drawWithMaskFilter() SkIRect srcRect; SkPaint paint; SkBitmap bm; bm = make_chessbm(5, 5); paint.setFilterQuality(kLow_SkFilterQuality); srcRect.setXYWH(1, 1, 3, 3); SkMaskFilter* mf = SkBlurMaskFilter::Create( kNormal_SkBlurStyle, SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(5)), SkBlurMaskFilter::kHighQuality_BlurFlag | SkBlurMaskFilter::kIgnoreTransform_BlurFlag); paint.setMaskFilter(mf)->unref(); canvas->drawBitmapRect(bm, srcRect, dstRect, &paint); } }