sk_sp<SkShader> ImagePattern::createShader(const SkMatrix& localMatrix) { if (!m_tileImage) return SkShader::MakeColorShader(SK_ColorTRANSPARENT); if (isRepeatXY()) { // Fast path: for repeatXY we just return a shader from the original image. return m_tileImage->makeShader(SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix); } // Skia does not have a "draw the tile only once" option. Clamp_TileMode // repeats the last line of the image after drawing one tile. To avoid // filling the space with arbitrary pixels, this workaround forces the // image to have a line of transparent pixels on the "repeated" edge(s), // thus causing extra space to be transparent filled. SkShader::TileMode tileModeX = isRepeatX() ? SkShader::kRepeat_TileMode : SkShader::kClamp_TileMode; SkShader::TileMode tileModeY = isRepeatY() ? SkShader::kRepeat_TileMode : SkShader::kClamp_TileMode; int borderPixelX = isRepeatX() ? 0 : 1; int borderPixelY = isRepeatY() ? 0 : 1; // Create a transparent image 2 pixels wider and/or taller than the // original, then copy the orignal into the middle of it. // FIXME: Is there a better way to pad (not scale) an image in skia? sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(m_tileImage->width() + 2 * borderPixelX, m_tileImage->height() + 2 * borderPixelY); if (!surface) return SkShader::MakeColorShader(SK_ColorTRANSPARENT); SkPaint paint; paint.setBlendMode(SkBlendMode::kSrc); surface->getCanvas()->drawImage(m_tileImage, borderPixelX, borderPixelY, &paint); m_previousLocalMatrix = localMatrix; SkMatrix adjustedMatrix(localMatrix); adjustedMatrix.postTranslate(-borderPixelX, -borderPixelY); return surface->makeImageSnapshot()->makeShader(tileModeX, tileModeY, &adjustedMatrix); }
PassRefPtr<SkShader> ImagePattern::createShader() { if (!m_tileImage) return adoptRef(SkShader::CreateColorShader(SK_ColorTRANSPARENT)); SkMatrix localMatrix = affineTransformToSkMatrix(m_patternSpaceTransformation); if (isRepeatXY()) { // Fast path: for repeatXY we just return a shader from the original image. return adoptRef(m_tileImage->newShader(SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); } // Skia does not have a "draw the tile only once" option. Clamp_TileMode // repeats the last line of the image after drawing one tile. To avoid // filling the space with arbitrary pixels, this workaround forces the // image to have a line of transparent pixels on the "repeated" edge(s), // thus causing extra space to be transparent filled. SkShader::TileMode tileModeX = isRepeatX() ? SkShader::kRepeat_TileMode : SkShader::kClamp_TileMode; SkShader::TileMode tileModeY = isRepeatY() ? SkShader::kRepeat_TileMode : SkShader::kClamp_TileMode; int expandW = isRepeatX() ? 0 : 1; int expandH = isRepeatY() ? 0 : 1; // Create a transparent image 1 pixel wider and/or taller than the // original, then copy the orignal into it. // FIXME: Is there a better way to pad (not scale) an image in skia? RefPtr<SkSurface> surface = adoptRef(SkSurface::NewRasterN32Premul( m_tileImage->width() + expandW, m_tileImage->height() + expandH)); if (!surface) return adoptRef(SkShader::CreateColorShader(SK_ColorTRANSPARENT)); surface->getCanvas()->clear(SK_ColorTRANSPARENT); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); surface->getCanvas()->drawImage(m_tileImage.get(), 0, 0, &paint); RefPtr<SkImage> expandedImage = adoptRef(surface->newImageSnapshot()); return adoptRef(expandedImage->newShader(tileModeX, tileModeY, &localMatrix)); }
// TODO(ccameron): ImagePattern should not draw to a globally set color space. // https://crbug.com/672306 ImagePattern::ImagePattern(PassRefPtr<Image> image, RepeatMode repeatMode) : Pattern(repeatMode), m_tileImage(image->imageForCurrentFrame( ColorBehavior::transformToGlobalTarget())) { m_previousLocalMatrix.setIdentity(); if (m_tileImage) { // TODO(fmalita): mechanism to extract the actual SkImageInfo from an // SkImage? const SkImageInfo info = SkImageInfo::MakeN32Premul( m_tileImage->width() + (isRepeatX() ? 0 : 2), m_tileImage->height() + (isRepeatY() ? 0 : 2)); adjustExternalMemoryAllocated(info.getSafeSize(info.minRowBytes())); } }