/** * Called on a background thread. Here we can only modify fBackMatrices. */ virtual void runAnimationTask(double t, double dt, int w, int h) { for (int idx = 0; idx < kNumPaths; ++idx) { Velocity* v = &fVelocities[idx]; Glyph* glyph = &fGlyphs[idx]; SkMatrix* backMatrix = &fBackMatrices[idx]; glyph->fPosition.fX += v->fDx * dt; if (glyph->fPosition.x() < 0) { glyph->fPosition.fX -= 2 * glyph->fPosition.x(); v->fDx = -v->fDx; } else if (glyph->fPosition.x() > w) { glyph->fPosition.fX -= 2 * (glyph->fPosition.x() - w); v->fDx = -v->fDx; } glyph->fPosition.fY += v->fDy * dt; if (glyph->fPosition.y() < 0) { glyph->fPosition.fY -= 2 * glyph->fPosition.y(); v->fDy = -v->fDy; } else if (glyph->fPosition.y() > h) { glyph->fPosition.fY -= 2 * (glyph->fPosition.y() - h); v->fDy = -v->fDy; } glyph->fSpin += v->fDSpin * dt; backMatrix->setTranslate(glyph->fPosition.x(), glyph->fPosition.y()); backMatrix->preScale(glyph->fZoom, glyph->fZoom); backMatrix->preRotate(glyph->fSpin); backMatrix->preTranslate(-glyph->fMidpt.x(), -glyph->fMidpt.y()); } }
virtual void onDraw(SkCanvas* canvas) { this->drawBG(canvas); SkMatrix matrix; SkGroupShape* gs = new SkGroupShape; SkAutoUnref aur(gs); gs->appendShape(&fGroup); matrix.setScale(-SK_Scalar1, SK_Scalar1); matrix.postTranslate(SkIntToScalar(220), SkIntToScalar(240)); gs->appendShape(&fGroup, matrix); matrix.setTranslate(SkIntToScalar(240), 0); matrix.preScale(SK_Scalar1*2, SK_Scalar1*2); gs->appendShape(&fGroup, matrix); #if 1 SkPicture* pict = new SkPicture; SkCanvas* cv = pict->beginRecording(1000, 1000); cv->scale(SK_ScalarHalf, SK_ScalarHalf); gs->draw(cv); cv->translate(SkIntToScalar(680), SkIntToScalar(480)); cv->scale(-SK_Scalar1, SK_Scalar1); gs->draw(cv); pict->endRecording(); canvas->drawPicture(*pict); pict->unref(); #endif }
void GeneratedImage::drawPattern(GraphicsContext& destContext, const FloatRect& srcRect, const FloatSize& scale, const FloatPoint& phase, SkBlendMode compositeOp, const FloatRect& destRect, const FloatSize& repeatSpacing) { FloatRect tileRect = srcRect; tileRect.expand(FloatSize(repeatSpacing)); std::unique_ptr<PaintController> paintController = PaintController::create(); GraphicsContext context(*paintController); context.beginRecording(tileRect); drawTile(context, srcRect); sk_sp<SkPicture> tilePicture = context.endRecording(); SkMatrix patternMatrix = SkMatrix::MakeTrans(phase.x(), phase.y()); patternMatrix.preScale(scale.width(), scale.height()); patternMatrix.preTranslate(tileRect.x(), tileRect.y()); RefPtr<Pattern> picturePattern = Pattern::createPicturePattern(std::move(tilePicture)); SkPaint fillPaint = destContext.fillPaint(); picturePattern->applyToPaint(fillPaint, patternMatrix); fillPaint.setColor(SK_ColorBLACK); fillPaint.setBlendMode(compositeOp); destContext.drawRect(destRect, fillPaint); }
static void scaleMatrix(const SkPath& one, const SkPath& two, SkMatrix& scale) { SkRect larger = one.getBounds(); larger.join(two.getBounds()); SkScalar largerWidth = larger.width(); if (largerWidth < 4) { largerWidth = 4; } SkScalar largerHeight = larger.height(); if (largerHeight < 4) { largerHeight = 4; } SkScalar hScale = (bitWidth - 2) / largerWidth; SkScalar vScale = (bitHeight - 2) / largerHeight; scale.reset(); scale.preScale(hScale, vScale); larger.fLeft *= hScale; larger.fRight *= hScale; larger.fTop *= vScale; larger.fBottom *= vScale; SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft : 16000 < larger.fRight ? 16000 - larger.fRight : 0; SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0; scale.postTranslate(dx, dy); }
virtual void onDraw(SkCanvas* canvas) { this->drawBG(canvas); SkMatrix saveM = *fMatrixRefs[3]; SkScalar c = SkIntToScalar(50); fMatrixRefs[3]->preRotate(SkIntToScalar(30), c, c); SkMatrix matrix; SkGroupShape* gs = new SkGroupShape; SkAutoUnref aur(gs); gs->appendShape(&fGroup); matrix.setScale(-SK_Scalar1, SK_Scalar1); matrix.postTranslate(SkIntToScalar(220), SkIntToScalar(240)); gs->appendShape(&fGroup, matrix); matrix.setTranslate(SkIntToScalar(240), 0); matrix.preScale(SK_Scalar1*2, SK_Scalar1*2); gs->appendShape(&fGroup, matrix); #if 0 canvas->drawShape(gs); #else SkPicture pict; SkCanvas* cv = pict.beginRecording(1000, 1000); cv->scale(SK_ScalarHalf, SK_ScalarHalf); cv->drawShape(gs); cv->translate(SkIntToScalar(680), SkIntToScalar(480)); cv->scale(-SK_Scalar1, SK_Scalar1); cv->drawShape(gs); pict.endRecording(); canvas->drawPicture(pict); #endif *fMatrixRefs[3] = saveM; }
void Image::drawPattern(GraphicsContext* context, const FloatRect& floatSrcRect, const FloatSize& scale, const FloatPoint& phase, SkXfermode::Mode compositeOp, const FloatRect& destRect, const IntSize& repeatSpacing) { TRACE_EVENT0("skia", "Image::drawPattern"); SkBitmap bitmap; if (!bitmapForCurrentFrame(&bitmap)) return; FloatRect normSrcRect = floatSrcRect; normSrcRect.intersect(FloatRect(0, 0, bitmap.width(), bitmap.height())); if (destRect.isEmpty() || normSrcRect.isEmpty()) return; // nothing to draw SkMatrix localMatrix; // 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 pattern. If WebKit wants // a shifted image, it will shift it from there using the localMatrix. const float adjustedX = phase.x() + normSrcRect.x() * scale.width(); const float adjustedY = phase.y() + normSrcRect.y() * scale.height(); localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY)); // Because no resizing occurred, the shader transform should be // set to the pattern's transform, which just includes scale. localMatrix.preScale(scale.width(), scale.height()); SkBitmap bitmapToPaint; bitmap.extractSubset(&bitmapToPaint, enclosingIntRect(normSrcRect)); if (!repeatSpacing.isZero()) { SkScalar ctmScaleX = 1.0; SkScalar ctmScaleY = 1.0; if (!RuntimeEnabledFeatures::slimmingPaintEnabled()) { AffineTransform ctm = context->getCTM(); ctmScaleX = ctm.xScale(); ctmScaleY = ctm.yScale(); } bitmapToPaint = createBitmapWithSpace( bitmapToPaint, repeatSpacing.width() * ctmScaleX / scale.width(), repeatSpacing.height() * ctmScaleY / scale.height()); } RefPtr<SkShader> shader = adoptRef(SkShader::CreateBitmapShader(bitmapToPaint, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap); { SkPaint paint; int initialSaveCount = context->preparePaintForDrawRectToRect(&paint, floatSrcRect, destRect, compositeOp, !bitmap.isOpaque(), isLazyDecoded, bitmap.isImmutable()); paint.setShader(shader.get()); context->drawRect(destRect, paint); context->canvas()->restoreToCount(initialSaveCount); } if (isLazyDecoded) PlatformInstrumentation::didDrawLazyPixelRef(bitmap.getGenerationID()); }
void Matrix::NativePreScale( /* [in] */ Int64 nObj, /* [in] */ Float sx, /* [in] */ Float sy) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(nObj); obj->preScale(sx, sy); }
void draw(SkCanvas* canvas) { SkMatrix matrix; SkPoint bitmapBounds[4], perspect[4] = {{50, 10}, {180, 40}, {236, 176}, {10, 206}}; SkRect::Make(source.bounds()).toQuad(bitmapBounds); matrix.setPolyToPoly(bitmapBounds, perspect, 4); matrix.preScale(.75f, 1.5f, source.width() / 2, source.height() / 2); canvas->concat(matrix); canvas->drawBitmap(source, 0, 0); }
void Image::drawPattern(GraphicsContext& context, const FloatRect& floatSrcRect, const FloatSize& scale, const FloatPoint& phase, SkBlendMode compositeOp, const FloatRect& destRect, const FloatSize& repeatSpacing) { TRACE_EVENT0("skia", "Image::drawPattern"); sk_sp<SkImage> image = imageForCurrentFrame(); if (!image) return; FloatRect normSrcRect = floatSrcRect; normSrcRect.intersect(FloatRect(0, 0, image->width(), image->height())); if (destRect.isEmpty() || normSrcRect.isEmpty()) return; // nothing to draw SkMatrix localMatrix; // 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 pattern. If WebKit wants // a shifted image, it will shift it from there using the localMatrix. const float adjustedX = phase.x() + normSrcRect.x() * scale.width(); const float adjustedY = phase.y() + normSrcRect.y() * scale.height(); localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY)); // Because no resizing occurred, the shader transform should be // set to the pattern's transform, which just includes scale. localMatrix.preScale(scale.width(), scale.height()); // Fetch this now as subsetting may swap the image. auto imageID = image->uniqueID(); image = image->makeSubset(enclosingIntRect(normSrcRect)); if (!image) return; { SkPaint paint = context.fillPaint(); paint.setColor(SK_ColorBLACK); paint.setBlendMode(static_cast<SkBlendMode>(compositeOp)); paint.setFilterQuality( context.computeFilterQuality(this, destRect, normSrcRect)); paint.setAntiAlias(context.shouldAntialias()); paint.setShader(createPatternShader( image.get(), localMatrix, paint, FloatSize(repeatSpacing.width() / scale.width(), repeatSpacing.height() / scale.height()))); context.drawRect(destRect, paint); } if (currentFrameIsLazyDecoded()) PlatformInstrumentation::didDrawLazyPixelRef(imageID); }
SkShader* SkPictureShader::refBitmapShader(const SkMatrix& matrix, const SkMatrix* localM) const { SkASSERT(fPicture && fPicture->width() > 0 && fPicture->height() > 0); SkMatrix m; m.setConcat(matrix, this->getLocalMatrix()); if (localM) { m.preConcat(*localM); } // Use a rotation-invariant scale SkPoint scale; if (!SkDecomposeUpper2x2(m, NULL, &scale, NULL)) { // Decomposition failed, use an approximation. scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()), SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY())); } SkSize scaledSize = SkSize::Make(scale.x() * fPicture->width(), scale.y() * fPicture->height()); SkISize tileSize = scaledSize.toRound(); if (tileSize.isEmpty()) { return NULL; } // The actual scale, compensating for rounding. SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fPicture->width(), SkIntToScalar(tileSize.height()) / fPicture->height()); SkAutoMutexAcquire ama(fCachedBitmapShaderMutex); if (!fCachedBitmapShader || tileScale != fCachedTileScale) { SkBitmap bm; if (!bm.allocN32Pixels(tileSize.width(), tileSize.height())) { return NULL; } bm.eraseColor(SK_ColorTRANSPARENT); SkCanvas canvas(bm); canvas.scale(tileScale.width(), tileScale.height()); canvas.drawPicture(fPicture); fCachedTileScale = tileScale; SkMatrix shaderMatrix = this->getLocalMatrix(); shaderMatrix.preScale(1 / tileScale.width(), 1 / tileScale.height()); fCachedBitmapShader.reset(CreateBitmapShader(bm, fTmx, fTmy, &shaderMatrix)); } // Increment the ref counter inside the mutex to ensure the returned pointer is still valid. // Otherwise, the pointer may have been overwritten on a different thread before the object's // ref count was incremented. fCachedBitmapShader.get()->ref(); return fCachedBitmapShader; }
static bool check_decompScale(const SkMatrix& matrix) { SkSize scale; SkMatrix remaining; if (!matrix.decomposeScale(&scale, &remaining)) { return false; } if (scale.width() <= 0 || scale.height() <= 0) { return false; } remaining.preScale(scale.width(), scale.height()); return nearly_equal(matrix, remaining); }
static int scaledDrawTheSame(const SkPath& one, const SkPath& two, int a, int b, bool drawPaths, SkBitmap& bitmap, SkCanvas* canvas) { SkMatrix scale; scale.reset(); float aScale = 1.21f; float bScale = 1.11f; scale.preScale(a * aScale, b * bScale); SkPath scaledOne, scaledTwo; one.transform(scale, &scaledOne); two.transform(scale, &scaledTwo); int errors = pathsDrawTheSame(scaledOne, scaledTwo, bitmap, canvas); if (errors == 0) { return 0; } while (!drawAsciiPaths(scaledOne, scaledTwo, drawPaths)) { scale.reset(); aScale *= 0.5f; bScale *= 0.5f; scale.preScale(a * aScale, b * bScale); one.transform(scale, &scaledOne); two.transform(scale, &scaledTwo); } return errors; }
static void scaleMatrix(const SkPath& one, const SkPath& two, SkMatrix& scale) { SkRect larger = one.getBounds(); larger.join(two.getBounds()); SkScalar largerWidth = larger.width(); if (largerWidth < 4) { largerWidth = 4; } SkScalar largerHeight = larger.height(); if (largerHeight < 4) { largerHeight = 4; } SkScalar hScale = (bitWidth - 2) / largerWidth; SkScalar vScale = (bitHeight - 2) / largerHeight; scale.reset(); scale.preScale(hScale, vScale); }
void SkGlyphCache::dump() const { const SkTypeface* face = fScalerContext->getTypeface(); const SkScalerContextRec& rec = fScalerContext->getRec(); SkMatrix matrix; rec.getSingleMatrix(&matrix); matrix.preScale(SkScalarInvert(rec.fTextSize), SkScalarInvert(rec.fTextSize)); SkString name; face->getFamilyName(&name); SkString msg; SkFontStyle style = face->fontStyle(); msg.printf("cache typeface:%x %25s:(%d,%d,%d)\n %s glyphs:%3d", face->uniqueID(), name.c_str(), style.weight(), style.width(), style.slant(), rec.dump().c_str(), fGlyphMap.count()); SkDebugf("%s\n", msg.c_str()); }
virtual void onDrawContent(SkCanvas* canvas) { SkScalar angle = SampleCode::GetAnimScalar(SkIntToScalar(180), SkIntToScalar(360)); SkMatrix saveM = *fMatrixRefs[3]; SkScalar c = SkIntToScalar(50); fMatrixRefs[3]->preRotate(angle, c, c); const SkScalar dx = 350; const SkScalar dy = 500; const int N = 1; for (int v = -N; v <= N; v++) { for (int h = -N; h <= N; h++) { SkAutoCanvasRestore acr(canvas, true); canvas->translate(h * dx, v * dy); SkMatrix matrix; SkGroupShape* gs = new SkGroupShape; SkAutoUnref aur(gs); gs->appendShape(&fGroup); matrix.setScale(-SK_Scalar1, SK_Scalar1); matrix.postTranslate(SkIntToScalar(220), SkIntToScalar(240)); gs->appendShape(&fGroup, matrix); matrix.setTranslate(SkIntToScalar(240), 0); matrix.preScale(SK_Scalar1*2, SK_Scalar1*2); gs->appendShape(&fGroup, matrix); #if 1 SkPicture* pict = new SkPicture; SkCanvas* cv = pict->beginRecording(1000, 1000); cv->scale(SK_ScalarHalf, SK_ScalarHalf); gs->draw(cv); cv->translate(SkIntToScalar(680), SkIntToScalar(480)); cv->scale(-SK_Scalar1, SK_Scalar1); gs->draw(cv); pict->endRecording(); drawpicture(canvas, *pict); pict->unref(); #endif }} *fMatrixRefs[3] = saveM; this->inval(NULL); }
void SkGlyphCache::dump() const { const SkTypeface* face = fScalerContext->getTypeface(); const SkScalerContextRec& rec = fScalerContext->getRec(); SkMatrix matrix; rec.getSingleMatrix(&matrix); matrix.preScale(SkScalarInvert(rec.fTextSize), SkScalarInvert(rec.fTextSize)); SkString name; face->getFamilyName(&name); SkString msg; msg.printf("cache typeface:%x %25s:%d size:%2g [%g %g %g %g] lum:%02X devG:%d pntG:%d cntr:%d glyphs:%3d", face->uniqueID(), name.c_str(), face->style(), rec.fTextSize, matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewX], matrix[SkMatrix::kMSkewY], matrix[SkMatrix::kMScaleY], rec.fLumBits & 0xFF, rec.fDeviceGamma, rec.fPaintGamma, rec.fContrast, fGlyphMap.count()); SkDebugf("%s\n", msg.c_str()); }
void LayerAndroid::updatePositions() { // apply the viewport to us SkMatrix matrix; if (!m_isFixed) { // turn our fields into a matrix. // // TODO: this should happen in the caller, and we should remove these // fields from our subclass matrix.setTranslate(m_translation.fX, m_translation.fY); if (m_doRotation) { matrix.preRotate(m_angleTransform); } matrix.preScale(m_scale.fX, m_scale.fY); this->setMatrix(matrix); } // now apply it to our children int count = this->countChildren(); for (int i = 0; i < count; i++) { this->getChild(i)->updatePositions(); } }
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(); }
void NativeImageSkia::drawPattern( GraphicsContext* context, const FloatRect& floatSrcRect, const FloatSize& scale, const FloatPoint& phase, CompositeOperator compositeOp, const FloatRect& destRect, WebBlendMode blendMode, const IntSize& repeatSpacing) const { FloatRect normSrcRect = floatSrcRect; normSrcRect.intersect(FloatRect(0, 0, bitmap().width(), bitmap().height())); if (destRect.isEmpty() || normSrcRect.isEmpty()) return; // nothing to draw SkMatrix totalMatrix = context->getTotalMatrix(); AffineTransform ctm = context->getCTM(); SkScalar ctmScaleX = ctm.xScale(); SkScalar ctmScaleY = ctm.yScale(); totalMatrix.preScale(scale.width(), scale.height()); // 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()); bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap()); // Compute the resampling mode. InterpolationQuality resampling; if (context->isAccelerated()) resampling = InterpolationLow; else if (isLazyDecoded) resampling = InterpolationHigh; else resampling = computeInterpolationQuality(totalMatrix, normSrcRect.width(), normSrcRect.height(), destBitmapWidth, destBitmapHeight, isDataComplete()); resampling = limitInterpolationQuality(context, resampling); SkMatrix localMatrix; // 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 pattern. If WebKit wants // a shifted image, it will shift it from there using the localMatrix. const float adjustedX = phase.x() + normSrcRect.x() * scale.width(); const float adjustedY = phase.y() + normSrcRect.y() * scale.height(); localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY)); RefPtr<SkShader> shader; SkFilterQuality filterLevel = static_cast<SkFilterQuality>(resampling); // Bicubic filter is only applied to defer-decoded images, see // NativeImageSkia::draw for details. if (resampling == InterpolationHigh && !isLazyDecoded) { // Do nice resampling. filterLevel = kNone_SkFilterQuality; float scaleX = destBitmapWidth / normSrcRect.width(); float scaleY = destBitmapHeight / normSrcRect.height(); SkRect scaledSrcRect; // Since we are resizing the bitmap, we need to remove the scale // applied to the pixels in the bitmap shader. This means we need // CTM * localMatrix to have identity scale. Since we // can't modify CTM (or the rectangle will be drawn in the wrong // place), we must set localMatrix's scale to the inverse of // CTM scale. localMatrix.preScale(ctmScaleX ? 1 / ctmScaleX : 1, ctmScaleY ? 1 / ctmScaleY : 1); // 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(normSrcRect, scaleX, scaleY, &scaledSrcRect); if (repeatSpacing.isZero()) { shader = adoptRef(SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); } else { shader = adoptRef(SkShader::CreateBitmapShader( createBitmapWithSpace(resampled, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY), SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); } } else { // Because no resizing occurred, the shader transform should be // set to the pattern's transform, which just includes scale. localMatrix.preScale(scale.width(), scale.height()); // No need to resample before drawing. SkBitmap srcSubset; bitmap().extractSubset(&srcSubset, enclosingIntRect(normSrcRect)); if (repeatSpacing.isZero()) { shader = adoptRef(SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); } else { shader = adoptRef(SkShader::CreateBitmapShader( createBitmapWithSpace(srcSubset, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY), SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); } } SkPaint paint; paint.setShader(shader.get()); paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode)); paint.setColorFilter(context->colorFilter()); paint.setFilterQuality(filterLevel); context->drawRect(destRect, paint); }
static int pathsDrawTheSame(const SkPath& one, const SkPath& two, SkBitmap& bits, SkPath& scaledOne, SkPath& scaledTwo, int& error2x2) { const int bitWidth = 64; const int bitHeight = 64; if (bits.width() == 0) { bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight); bits.allocPixels(); } SkRect larger = one.getBounds(); larger.join(two.getBounds()); SkScalar largerWidth = larger.width(); if (largerWidth < 4) { largerWidth = 4; } SkScalar largerHeight = larger.height(); if (largerHeight < 4) { largerHeight = 4; } SkScalar hScale = (bitWidth - 2) / largerWidth; SkScalar vScale = (bitHeight - 2) / largerHeight; SkMatrix scale; scale.reset(); scale.preScale(hScale, vScale); one.transform(scale, &scaledOne); two.transform(scale, &scaledTwo); const SkRect& bounds1 = scaledOne.getBounds(); SkCanvas canvas(bits); canvas.drawColor(SK_ColorWHITE); SkPaint paint; canvas.save(); canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1); canvas.drawPath(scaledOne, paint); canvas.restore(); canvas.save(); canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1); canvas.drawPath(scaledTwo, paint); canvas.restore(); int errors2 = 0; int errors = 0; for (int y = 0; y < bitHeight - 1; ++y) { uint32_t* addr1 = bits.getAddr32(0, y); uint32_t* addr2 = bits.getAddr32(0, y + 1); uint32_t* addr3 = bits.getAddr32(bitWidth, y); uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1); for (int x = 0; x < bitWidth - 1; ++x) { // count 2x2 blocks bool err = addr1[x] != addr3[x]; if (err) { errors2 += addr1[x + 1] != addr3[x + 1] && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1]; errors++; } } } if (errors2 >= 6 || errors > 160) { SkDebugf("%s errors2=%d errors=%d\n", __FUNCTION__, errors2, errors); } error2x2 = errors2; return errors; }
void onDraw(int loops, SkCanvas* canvas) override { SkRandom scaleRand; SkRandom transRand; SkRandom rotRand; int width, height; if (fUseAtlas) { width = kAtlasCellWidth; height = kAtlasCellHeight; } else { width = kCheckerboardWidth; height = kCheckerboardHeight; } SkPaint clearPaint; clearPaint.setColor(0xFF000000); clearPaint.setAntiAlias(true); SkISize size = canvas->getDeviceSize(); SkScalar maxTransX, maxTransY; if (kScale_Type == fType) { maxTransX = size.fWidth - (1.5f * width); maxTransY = size.fHeight - (1.5f * height); } else if (kTranslate_Type == fType) { maxTransX = SkIntToScalar(size.fWidth - width); maxTransY = SkIntToScalar(size.fHeight - height); } else { SkASSERT(kRotate_Type == fType); // Yes, some rotations will be off the top and left sides maxTransX = size.fWidth - SK_ScalarSqrt2 * height; maxTransY = size.fHeight - SK_ScalarSqrt2 * height; } SkMatrix mat; SkRect dst = { 0, 0, SkIntToScalar(width), SkIntToScalar(height) }; SkRect clearRect = { -1.0f, -1.0f, width+1.0f, height+1.0f }; SkPoint verts[4] = { // for drawVertices path { 0, 0 }, { 0, SkIntToScalar(height) }, { SkIntToScalar(width), SkIntToScalar(height) }, { SkIntToScalar(width), 0 } }; uint16_t indices[6] = { 0, 1, 2, 0, 2, 3 }; SkPaint p; p.setColor(0xFF000000); p.setFilterQuality(kLow_SkFilterQuality); SkPaint p2; // for drawVertices path p2.setColor(0xFF000000); p2.setFilterQuality(kLow_SkFilterQuality); p2.setShader(SkShader::MakeBitmapShader(fAtlas, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)); for (int i = 0; i < loops; ++i, ++fNumSaved) { if (0 == i % kNumBeforeClear) { if (kPartial_Clear == fClear) { for (int j = 0; j < fNumSaved; ++j) { canvas->setMatrix(SkMatrix::I()); mat.setTranslate(fSaved[j][0], fSaved[j][1]); if (kScale_Type == fType) { mat.preScale(fSaved[j][2], fSaved[j][2]); } else if (kRotate_Type == fType) { mat.preRotate(fSaved[j][2]); } canvas->concat(mat); canvas->drawRect(clearRect, clearPaint); } } else { canvas->clear(0xFF000000); } fNumSaved = 0; } SkASSERT(fNumSaved < kNumBeforeClear); canvas->setMatrix(SkMatrix::I()); fSaved[fNumSaved][0] = transRand.nextRangeScalar(0.0f, maxTransX); fSaved[fNumSaved][1] = transRand.nextRangeScalar(0.0f, maxTransY); if (fAligned) { // make the translations integer aligned fSaved[fNumSaved][0] = SkScalarFloorToScalar(fSaved[fNumSaved][0]); fSaved[fNumSaved][1] = SkScalarFloorToScalar(fSaved[fNumSaved][1]); } mat.setTranslate(fSaved[fNumSaved][0], fSaved[fNumSaved][1]); if (kScale_Type == fType) { fSaved[fNumSaved][2] = scaleRand.nextRangeScalar(0.5f, 1.5f); mat.preScale(fSaved[fNumSaved][2], fSaved[fNumSaved][2]); } else if (kRotate_Type == fType) { fSaved[fNumSaved][2] = rotRand.nextRangeScalar(0.0f, 360.0f); mat.preRotate(fSaved[fNumSaved][2]); } canvas->concat(mat); if (fUseAtlas) { const int curCell = i % (kNumAtlasedX * kNumAtlasedY); SkIRect src = fAtlasRects[curCell % (kNumAtlasedX)][curCell / (kNumAtlasedX)]; if (fUseDrawVertices) { SkPoint uvs[4] = { { SkIntToScalar(src.fLeft), SkIntToScalar(src.fBottom) }, { SkIntToScalar(src.fLeft), SkIntToScalar(src.fTop) }, { SkIntToScalar(src.fRight), SkIntToScalar(src.fTop) }, { SkIntToScalar(src.fRight), SkIntToScalar(src.fBottom) }, }; canvas->drawVertices(SkCanvas::kTriangles_VertexMode, 4, verts, uvs, nullptr, nullptr, indices, 6, p2); } else { canvas->drawBitmapRect(fAtlas, src, dst, &p, SkCanvas::kFast_SrcRectConstraint); } } else { canvas->drawBitmapRect(fCheckerboard, dst, &p); } } }
static void preScale__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); obj->preScale(sx, sy); }
virtual void performTest() { SkMatrix m; m = fM0; m.preScale(fSX, fSY); m = fM1; m.preScale(fSX, fSY); m = fM2; m.preScale(fSX, fSY); }
void onDraw(SkCanvas* canvas) override { fShader = gBleedRec[fBT].fShaderMaker(); canvas->clear(SK_ColorGRAY); SkTDArray<SkMatrix> matrices; // Draw with identity *matrices.append() = SkMatrix::I(); // Draw with rotation and scale down in x, up in y. SkMatrix m; constexpr SkScalar kBottom = SkIntToScalar(kRow4Y + kBlockSize + kBlockSpacing); m.setTranslate(0, kBottom); m.preRotate(15.f, 0, kBottom + kBlockSpacing); m.preScale(0.71f, 1.22f); *matrices.append() = m; // Align the next set with the middle of the previous in y, translated to the right in x. SkPoint corners[] = {{0, 0}, { 0, kBottom }, { kWidth, kBottom }, {kWidth, 0} }; matrices[matrices.count()-1].mapPoints(corners, 4); SkScalar y = (corners[0].fY + corners[1].fY + corners[2].fY + corners[3].fY) / 4; SkScalar x = SkTMax(SkTMax(corners[0].fX, corners[1].fX), SkTMax(corners[2].fX, corners[3].fX)); m.setTranslate(x, y); m.preScale(0.2f, 0.2f); *matrices.append() = m; SkScalar maxX = 0; for (int antiAlias = 0; antiAlias < 2; ++antiAlias) { canvas->save(); canvas->translate(maxX, 0); for (int m = 0; m < matrices.count(); ++m) { canvas->save(); canvas->concat(matrices[m]); bool aa = SkToBool(antiAlias); // First draw a column with no bleeding and no filtering this->drawCase1(canvas, kCol0X, kRow0Y, aa, SkCanvas::kStrict_SrcRectConstraint, kNone_SkFilterQuality); this->drawCase2(canvas, kCol0X, kRow1Y, aa, SkCanvas::kStrict_SrcRectConstraint, kNone_SkFilterQuality); this->drawCase3(canvas, kCol0X, kRow2Y, aa, SkCanvas::kStrict_SrcRectConstraint, kNone_SkFilterQuality); this->drawCase4(canvas, kCol0X, kRow3Y, aa, SkCanvas::kStrict_SrcRectConstraint, kNone_SkFilterQuality); this->drawCase5(canvas, kCol0X, kRow4Y, aa, SkCanvas::kStrict_SrcRectConstraint, kNone_SkFilterQuality); // Then draw a column with no bleeding and low filtering this->drawCase1(canvas, kCol1X, kRow0Y, aa, SkCanvas::kStrict_SrcRectConstraint, kLow_SkFilterQuality); this->drawCase2(canvas, kCol1X, kRow1Y, aa, SkCanvas::kStrict_SrcRectConstraint, kLow_SkFilterQuality); this->drawCase3(canvas, kCol1X, kRow2Y, aa, SkCanvas::kStrict_SrcRectConstraint, kLow_SkFilterQuality); this->drawCase4(canvas, kCol1X, kRow3Y, aa, SkCanvas::kStrict_SrcRectConstraint, kLow_SkFilterQuality); this->drawCase5(canvas, kCol1X, kRow4Y, aa, SkCanvas::kStrict_SrcRectConstraint, kLow_SkFilterQuality); // Then draw a column with no bleeding and high filtering this->drawCase1(canvas, kCol2X, kRow0Y, aa, SkCanvas::kStrict_SrcRectConstraint, kHigh_SkFilterQuality); this->drawCase2(canvas, kCol2X, kRow1Y, aa, SkCanvas::kStrict_SrcRectConstraint, kHigh_SkFilterQuality); this->drawCase3(canvas, kCol2X, kRow2Y, aa, SkCanvas::kStrict_SrcRectConstraint, kHigh_SkFilterQuality); this->drawCase4(canvas, kCol2X, kRow3Y, aa, SkCanvas::kStrict_SrcRectConstraint, kHigh_SkFilterQuality); this->drawCase5(canvas, kCol2X, kRow4Y, aa, SkCanvas::kStrict_SrcRectConstraint, kHigh_SkFilterQuality); // Then draw a column with bleeding and no filtering (bleed should have no effect w/out blur) this->drawCase1(canvas, kCol3X, kRow0Y, aa, SkCanvas::kFast_SrcRectConstraint, kNone_SkFilterQuality); this->drawCase2(canvas, kCol3X, kRow1Y, aa, SkCanvas::kFast_SrcRectConstraint, kNone_SkFilterQuality); this->drawCase3(canvas, kCol3X, kRow2Y, aa, SkCanvas::kFast_SrcRectConstraint, kNone_SkFilterQuality); this->drawCase4(canvas, kCol3X, kRow3Y, aa, SkCanvas::kFast_SrcRectConstraint, kNone_SkFilterQuality); this->drawCase5(canvas, kCol3X, kRow4Y, aa, SkCanvas::kFast_SrcRectConstraint, kNone_SkFilterQuality); // Then draw a column with bleeding and low filtering this->drawCase1(canvas, kCol4X, kRow0Y, aa, SkCanvas::kFast_SrcRectConstraint, kLow_SkFilterQuality); this->drawCase2(canvas, kCol4X, kRow1Y, aa, SkCanvas::kFast_SrcRectConstraint, kLow_SkFilterQuality); this->drawCase3(canvas, kCol4X, kRow2Y, aa, SkCanvas::kFast_SrcRectConstraint, kLow_SkFilterQuality); this->drawCase4(canvas, kCol4X, kRow3Y, aa, SkCanvas::kFast_SrcRectConstraint, kLow_SkFilterQuality); this->drawCase5(canvas, kCol4X, kRow4Y, aa, SkCanvas::kFast_SrcRectConstraint, kLow_SkFilterQuality); // Finally draw a column with bleeding and high filtering this->drawCase1(canvas, kCol5X, kRow0Y, aa, SkCanvas::kFast_SrcRectConstraint, kHigh_SkFilterQuality); this->drawCase2(canvas, kCol5X, kRow1Y, aa, SkCanvas::kFast_SrcRectConstraint, kHigh_SkFilterQuality); this->drawCase3(canvas, kCol5X, kRow2Y, aa, SkCanvas::kFast_SrcRectConstraint, kHigh_SkFilterQuality); this->drawCase4(canvas, kCol5X, kRow3Y, aa, SkCanvas::kFast_SrcRectConstraint, kHigh_SkFilterQuality); this->drawCase5(canvas, kCol5X, kRow4Y, aa, SkCanvas::kFast_SrcRectConstraint, kHigh_SkFilterQuality); SkPoint corners[] = { { 0, 0 },{ 0, kBottom },{ kWidth, kBottom },{ kWidth, 0 } }; matrices[m].mapPoints(corners, 4); SkScalar x = kBlockSize + SkTMax(SkTMax(corners[0].fX, corners[1].fX), SkTMax(corners[2].fX, corners[3].fX)); maxX = SkTMax(maxX, x); canvas->restore(); } canvas->restore(); } }
void SkScalerContext_CairoFT::generateMetrics(SkGlyph* glyph) { SkASSERT(fScaledFont != nullptr); glyph->zeroMetrics(); CairoLockedFTFace faceLock(fScaledFont); FT_Face face = faceLock.getFace(); FT_Error err = FT_Load_Glyph( face, glyph->getGlyphID(), fLoadGlyphFlags ); if (err != 0) { return; } prepareGlyph(face->glyph); switch (face->glyph->format) { case FT_GLYPH_FORMAT_OUTLINE: if (!face->glyph->outline.n_contours) { break; } FT_BBox bbox; FT_Outline_Get_CBox(&face->glyph->outline, &bbox); bbox.xMin &= ~63; bbox.yMin &= ~63; bbox.xMax = (bbox.xMax + 63) & ~63; bbox.yMax = (bbox.yMax + 63) & ~63; glyph->fWidth = SkToU16(SkFDot6Floor(bbox.xMax - bbox.xMin)); glyph->fHeight = SkToU16(SkFDot6Floor(bbox.yMax - bbox.yMin)); glyph->fTop = -SkToS16(SkFDot6Floor(bbox.yMax)); glyph->fLeft = SkToS16(SkFDot6Floor(bbox.xMin)); if (isLCD(fRec) && gSetLcdFilter && (fLcdFilter == FT_LCD_FILTER_DEFAULT || fLcdFilter == FT_LCD_FILTER_LIGHT)) { if (fRec.fFlags & kLCD_Vertical_Flag) { glyph->fTop -= 1; glyph->fHeight += 2; } else { glyph->fLeft -= 1; glyph->fWidth += 2; } } break; case FT_GLYPH_FORMAT_BITMAP: #ifdef FT_LOAD_COLOR if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) { glyph->fMaskFormat = SkMask::kARGB32_Format; } #endif if (isLCD(fRec)) { fRec.fMaskFormat = SkMask::kA8_Format; } if (fHaveShape) { // Apply the shape matrix to the glyph's bounding box. SkMatrix matrix; fRec.getSingleMatrix(&matrix); matrix.preScale(SkScalarInvert(fScaleX), SkScalarInvert(fScaleY)); SkRect srcRect = SkRect::MakeXYWH( SkIntToScalar(face->glyph->bitmap_left), -SkIntToScalar(face->glyph->bitmap_top), SkIntToScalar(face->glyph->bitmap.width), SkIntToScalar(face->glyph->bitmap.rows)); SkRect destRect; matrix.mapRect(&destRect, srcRect); SkIRect glyphRect = destRect.roundOut(); glyph->fWidth = SkToU16(glyphRect.width()); glyph->fHeight = SkToU16(glyphRect.height()); glyph->fTop = SkToS16(SkScalarRoundToInt(destRect.fTop)); glyph->fLeft = SkToS16(SkScalarRoundToInt(destRect.fLeft)); } else { glyph->fWidth = SkToU16(face->glyph->bitmap.width); glyph->fHeight = SkToU16(face->glyph->bitmap.rows); glyph->fTop = -SkToS16(face->glyph->bitmap_top); glyph->fLeft = SkToS16(face->glyph->bitmap_left); } break; default: SkDEBUGFAIL("unknown glyph format"); return; } if (fRec.fFlags & SkScalerContext::kVertical_Flag) { glyph->fAdvanceX = -SkFDot6ToFloat(face->glyph->advance.x); glyph->fAdvanceY = SkFDot6ToFloat(face->glyph->advance.y); } else { glyph->fAdvanceX = SkFDot6ToFloat(face->glyph->advance.x); glyph->fAdvanceY = -SkFDot6ToFloat(face->glyph->advance.y); } }
int SkBuildQuadArc(const SkVector& uStart, const SkVector& uStop, SkRotationDirection dir, const SkMatrix* userMatrix, SkPoint quadPoints[]) { // rotate by x,y so that uStart is (1.0) SkScalar x = SkPoint::DotProduct(uStart, uStop); SkScalar y = SkPoint::CrossProduct(uStart, uStop); SkScalar absX = SkScalarAbs(x); SkScalar absY = SkScalarAbs(y); int pointCount; // check for (effectively) coincident vectors // this can happen if our angle is nearly 0 or nearly 180 (y == 0) // ... we use the dot-prod to distinguish between 0 and 180 (x > 0) if (absY <= SK_ScalarNearlyZero && x > 0 && ((y >= 0 && kCW_SkRotationDirection == dir) || (y <= 0 && kCCW_SkRotationDirection == dir))) { // just return the start-point quadPoints[0].set(SK_Scalar1, 0); pointCount = 1; } else { if (dir == kCCW_SkRotationDirection) y = -y; // what octant (quadratic curve) is [xy] in? int oct = 0; bool sameSign = true; if (0 == y) { oct = 4; // 180 SkASSERT(SkScalarAbs(x + SK_Scalar1) <= SK_ScalarNearlyZero); } else if (0 == x) { SkASSERT(absY - SK_Scalar1 <= SK_ScalarNearlyZero); if (y > 0) oct = 2; // 90 else oct = 6; // 270 } else { if (y < 0) oct += 4; if ((x < 0) != (y < 0)) { oct += 2; sameSign = false; } if ((absX < absY) == sameSign) oct += 1; } int wholeCount = oct << 1; memcpy(quadPoints, gQuadCirclePts, (wholeCount + 1) * sizeof(SkPoint)); const SkPoint* arc = &gQuadCirclePts[wholeCount]; if (quad_pt2OffCurve(arc, x, y, &quadPoints[wholeCount + 1])) { quadPoints[wholeCount + 2].set(x, y); wholeCount += 2; } pointCount = wholeCount + 1; } // now handle counter-clockwise and the initial unitStart rotation SkMatrix matrix; matrix.setSinCos(uStart.fY, uStart.fX); if (dir == kCCW_SkRotationDirection) { matrix.preScale(SK_Scalar1, -SK_Scalar1); } if (userMatrix) { matrix.postConcat(*userMatrix); } matrix.mapPoints(quadPoints, pointCount); return pointCount; }
SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { fState.get()->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 = fState.get()->fCanvasTransform; finalMatrix.preConcat(fState.get()->fShaderTransform); SkRect deviceBounds; deviceBounds.set(fState.get()->fBBox); if (!inverseTransformBBox(finalMatrix, &deviceBounds)) { return; } const SkBitmap* image = &fState.get()->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] = fState.get()->fImageTileModes[0]; tileModes[1] = fState.get()->fImageTileModes[1]; if (tileModes[0] != SkShader::kClamp_TileMode || tileModes[1] != SkShader::kClamp_TileMode) { deviceBounds.join(bitmapBounds); } SkMatrix unflip; unflip.setTranslate(0, SkScalarRoundToScalar(deviceBounds.height())); unflip.preScale(SK_Scalar1, -SK_Scalar1); SkISize size = SkISize::Make(SkScalarRound(deviceBounds.width()), SkScalarRound(deviceBounds.height())); SkPDFDevice pattern(size, size, unflip); SkCanvas canvas(&pattern); 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); 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(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); 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 (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); 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 = 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()); 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 (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); 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 = deviceBounds.height(); } } // Put the canvas into the pattern stream (fContent). SkAutoTUnref<SkStream> content(pattern.content()); setData(content.get()); SkPDFResourceDict* resourceDict = pattern.getResourceDict(); resourceDict->getReferencedResources(fResources, &fResources, false); populate_tiling_pattern_dict(this, patternBBox, pattern.getResourceDict(), finalMatrix); fState.get()->fImage.unlockPixels(); }
bool SVGPaintServerGradient::setup(GraphicsContext*& context, const RenderObject* object, SVGPaintTargetType type, bool isPaintingText) const { m_ownerElement->buildGradient(); RenderStyle* style = object->style(); bool isFilled = (type & ApplyToFillTargetType) && style->svgStyle()->hasFill(); bool isStroked = (type & ApplyToStrokeTargetType) && style->svgStyle()->hasStroke(); if(!gradientStops().size()) return false; if(gradientStops().size()==1) { context->setFillColor(gradientStops()[0].second); return true; } // Create a gradient builder helper to generate the data // we'll need to provide Skia SkiaGradientBuilder builder(gradientStops(), isFilled ? style->svgStyle()->fillOpacity() : style->svgStyle()->strokeOpacity()); SkShader::TileMode tile_mode; // Convert SVG spread modes to Skia tile modes switch(spreadMethod()) { default: case SPREADMETHOD_PAD: tile_mode = SkShader::kClamp_TileMode; break; case SPREADMETHOD_REFLECT: tile_mode = SkShader::kMirror_TileMode; break; case SPREADMETHOD_REPEAT: tile_mode = SkShader::kRepeat_TileMode; break; } SkShader* shader = NULL; SkMatrix matrix; // Calculate a matrix to transform a gradient to fit the bounding box if (boundingBoxMode()) { matrix.reset(); SkRect rc = context->getBoundingBoxForCurrentPath(true); matrix.preTranslate(rc.fLeft, rc.fTop); matrix.preScale(rc.width(), rc.height()); matrix.preConcat(gradientTransform()); } else matrix = gradientTransform(); if (this->type() == LinearGradientPaintServer) { const SVGPaintServerLinearGradient* linear = static_cast<const SVGPaintServerLinearGradient*>(this); SkPoint pts[2]; pts[0].fX = linear->gradientStart().x(); pts[0].fY = linear->gradientStart().y(); pts[1].fX = linear->gradientEnd().x(); pts[1].fY = linear->gradientEnd().y(); shader = SkGradientShader::CreateLinear(pts, builder.colors(), builder.pos(), builder.count(), tile_mode); } else if (this->type() == RadialGradientPaintServer) { const SVGPaintServerRadialGradient* radial = static_cast<const SVGPaintServerRadialGradient*>(this); SkPoint center; SkScalar radius; center.fX = radial->gradientCenter().x(); center.fY = radial->gradientCenter().y(); radius = radial->gradientRadius(); shader = SkGradientShader::CreateRadial( center, radius, builder.colors(), builder.pos(), builder.count(), tile_mode); } else { return false; } if (isPaintingText) { if (isFilled) { context->setTextDrawingMode(cTextFill); } if (isStroked) { context->setTextDrawingMode(cTextStroke); } } if (isStroked) { applyStrokeStyleToContext(context, style, object); } if (shader) { shader->setLocalMatrix(matrix); context->platformContext()->setGradient(shader); return true; } return false; }
sk_sp<SkShader> Gradient::createShader(const SkMatrix& localMatrix) { sortStopsIfNecessary(); ASSERT(m_stopsSorted); size_t countUsed = totalStopsNeeded(m_stops.data(), m_stops.size()); ASSERT(countUsed >= 2); ASSERT(countUsed >= m_stops.size()); ColorStopOffsetVector pos(countUsed); ColorStopColorVector colors(countUsed); fillStops(m_stops.data(), m_stops.size(), pos, colors); SkShader::TileMode tile = SkShader::kClamp_TileMode; switch (m_spreadMethod) { case SpreadMethodReflect: tile = SkShader::kMirror_TileMode; break; case SpreadMethodRepeat: tile = SkShader::kRepeat_TileMode; break; case SpreadMethodPad: tile = SkShader::kClamp_TileMode; break; } sk_sp<SkShader> shader; uint32_t shouldDrawInPMColorSpace = m_drawInPMColorSpace ? SkGradientShader::kInterpolateColorsInPremul_Flag : 0; if (m_radial) { SkMatrix adjustedLocalMatrix = localMatrix; if (m_aspectRatio != 1) { // CSS3 elliptical gradients: apply the elliptical scaling at the // gradient center point. adjustedLocalMatrix.preTranslate(m_p0.x(), m_p0.y()); adjustedLocalMatrix.preScale(1, 1 / m_aspectRatio); adjustedLocalMatrix.preTranslate(-m_p0.x(), -m_p0.y()); ASSERT(m_p0 == m_p1); } // Since the two-point radial gradient is slower than the plain radial, // only use it if we have to. if (m_p0 == m_p1 && m_r0 <= 0.0f) { shader = SkGradientShader::MakeRadial( m_p1.data(), m_r1, colors.data(), pos.data(), static_cast<int>(countUsed), tile, shouldDrawInPMColorSpace, &adjustedLocalMatrix); } else { // The radii we give to Skia must be positive. If we're given a // negative radius, ask for zero instead. SkScalar radius0 = m_r0 >= 0.0f ? WebCoreFloatToSkScalar(m_r0) : 0; SkScalar radius1 = m_r1 >= 0.0f ? WebCoreFloatToSkScalar(m_r1) : 0; shader = SkGradientShader::MakeTwoPointConical( m_p0.data(), radius0, m_p1.data(), radius1, colors.data(), pos.data(), static_cast<int>(countUsed), tile, shouldDrawInPMColorSpace, &adjustedLocalMatrix); } } else { SkPoint pts[2] = {m_p0.data(), m_p1.data()}; shader = SkGradientShader::MakeLinear( pts, colors.data(), pos.data(), static_cast<int>(countUsed), tile, shouldDrawInPMColorSpace, &localMatrix); } if (!shader) { // use last color, since our "geometry" was degenerate (e.g. radius==0) shader = SkShader::MakeColorShader(colors[countUsed - 1]); } return shader; }
void performTest() override { SkMatrix m; m = fM0; m.preScale(fSX, fSY); m = fM1; m.preScale(fSX, fSY); m = fM2; m.preScale(fSX, fSY); }