#include "config.h"
#include "AffineTransform.h"
#include "BitmapImage.h"
#include "BitmapImageSingleFrameSkia.h"
#include "FloatConversion.h"
#include "FloatRect.h"
#include "GraphicsContext.h"
#include "GraphicsContextGPU.h"
#include "Logging.h"
#include "NativeImageSkia.h"
#include "PlatformContextSkia.h"
#include "PlatformString.h"
#include "SkPixelRef.h"
#include "SkRect.h"
#include "SkShader.h"
#include "SkiaUtils.h"
#include "Texture.h"
#include "skia/ext/image_operations.h"
#include "skia/ext/platform_canvas.h"
namespace WebCore {
enum ResamplingMode {
RESAMPLE_NONE,
RESAMPLE_LINEAR,
RESAMPLE_AWESOME,
};
static ResamplingMode computeResamplingMode(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, int srcWidth, int srcHeight, float destWidth, float destHeight)
{
if (platformContext->hasImageResamplingHint()) {
IntSize srcSize;
FloatSize dstSize;
platformContext->getImageResamplingHint(&srcSize, &dstSize);
srcWidth = srcSize.width();
srcHeight = srcSize.height();
destWidth = dstSize.width();
destHeight = dstSize.height();
}
int destIWidth = static_cast<int>(destWidth);
int destIHeight = static_cast<int>(destHeight);
const float kFractionalChangeThreshold = 0.025f;
const int kSmallImageSizeThreshold = 8;
const float kLargeStretch = 3.0f;
if (srcWidth == destIWidth && srcHeight == destIHeight) {
return RESAMPLE_NONE;
}
if (srcWidth <= kSmallImageSizeThreshold
|| srcHeight <= kSmallImageSizeThreshold
|| destWidth <= kSmallImageSizeThreshold
|| destHeight <= kSmallImageSizeThreshold) {
return RESAMPLE_NONE;
}
if (srcHeight * kLargeStretch <= destHeight || srcWidth * kLargeStretch <= destWidth) {
if (srcWidth == destWidth || srcHeight == destHeight)
return RESAMPLE_NONE;
return RESAMPLE_LINEAR;
}
if ((fabs(destWidth - srcWidth) / srcWidth < kFractionalChangeThreshold)
&& (fabs(destHeight - srcHeight) / srcHeight < kFractionalChangeThreshold)) {
return RESAMPLE_NONE;
}
if (!bitmap.isDataComplete())
return RESAMPLE_LINEAR;
if (platformContext->interpolationQuality() == InterpolationHigh
&& !(platformContext->canvas()->getTotalMatrix().getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)))
return RESAMPLE_AWESOME;
return RESAMPLE_LINEAR;
}
static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeImageSkia& bitmap, const SkIRect& srcIRect, const SkRect& destRect)
{
SkBitmap subset;
bitmap.extractSubset(&subset, srcIRect);
SkRect srcRect;
srcRect.set(srcIRect);
bool srcIsFull = srcIRect.fLeft == 0 && srcIRect.fTop == 0
&& srcIRect.width() == bitmap.width()
&& srcIRect.height() == bitmap.height();
SkIRect destRectRounded;
destRect.round(&destRectRounded);
SkIRect resizedImageRect = { 0, 0, destRectRounded.width(), destRectRounded.height() };
SkRect destRectTransformed;
canvas.getTotalMatrix().mapRect(&destRectTransformed, destRect);
SkIRect destRectTransformedRounded;
destRectTransformed.round(&destRectTransformedRounded);
if (srcIsFull && bitmap.hasResizedBitmap(destRectTransformedRounded.width(), destRectTransformedRounded.height())) {
SkBitmap resampled = bitmap.resizedBitmap(destRectTransformedRounded.width(), destRectTransformedRounded.height());
canvas.drawBitmapRect(resampled, 0, destRect, &paint);
return;
}
SkRect destBitmapSubsetSk;
ClipRectToCanvas(canvas, destRect, &destBitmapSubsetSk);
SkRect destBitmapSubsetTransformed;
canvas.getTotalMatrix().mapRect(&destBitmapSubsetTransformed, destBitmapSubsetSk);
destBitmapSubsetSk.offset(-destRect.fLeft, -destRect.fTop);
SkIRect destBitmapSubsetTransformedRounded;
destBitmapSubsetTransformed.round(&destBitmapSubsetTransformedRounded);
destBitmapSubsetTransformedRounded.offset(-destRectTransformedRounded.fLeft, -destRectTransformedRounded.fTop);
SkIRect destBitmapSubsetSkI;
destBitmapSubsetSk.roundOut(&destBitmapSubsetSkI);
if (!destBitmapSubsetSkI.intersect(resizedImageRect))
return;
if (srcIsFull && bitmap.shouldCacheResampling(
resizedImageRect.width(),
resizedImageRect.height(),
destBitmapSubsetSkI.width(),
destBitmapSubsetSkI.height())) {
SkBitmap resampled = bitmap.resizedBitmap(destRectTransformedRounded.width(),
destRectTransformedRounded.height());
canvas.drawBitmapRect(resampled, 0, destRect, &paint);
} else {
if (destBitmapSubsetTransformedRounded.intersect(0, 0,
destRectTransformedRounded.width(), destRectTransformedRounded.height())) {
SkBitmap resampled = skia::ImageOperations::Resize(subset,
skia::ImageOperations::RESIZE_LANCZOS3,
destRectTransformedRounded.width(), destRectTransformedRounded.height(),
destBitmapSubsetTransformedRounded);
destBitmapSubsetSkI.offset(destRect.fLeft, destRect.fTop);
SkRect offsetDestRect;
offsetDestRect.set(destBitmapSubsetSkI);
canvas.drawBitmapRect(resampled, 0, offsetDestRect, &paint);
}
}
}
static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkIRect& srcRect, const SkRect& destRect, const SkXfermode::Mode& compOp)
{
SkPaint paint;
paint.setXfermodeMode(compOp);
paint.setFilterBitmap(true);
paint.setAlpha(platformContext->getNormalizedAlpha());
paint.setLooper(platformContext->getDrawLooper());
SkCanvas* canvas = platformContext->canvas();
ResamplingMode resampling;
if (platformContext->useSkiaGPU())
resampling = RESAMPLE_LINEAR;
else
resampling = platformContext->printing() ? RESAMPLE_NONE :
computeResamplingMode(platformContext, bitmap, srcRect.width(), srcRect.height(), SkScalarToFloat(destRect.width()), SkScalarToFloat(destRect.height()));
if (resampling == RESAMPLE_AWESOME) {
drawResampledBitmap(*canvas, paint, bitmap, srcRect, destRect);
} else {
canvas->drawBitmapRect(bitmap, &srcRect, destRect, &paint);
}
}
static void TransformDimensions(const SkMatrix& matrix, float srcWidth, float srcHeight, float* destWidth, float* destHeight) {
SkPoint src_points[3]; src_points[0].set(0, 0);
src_points[1].set(SkFloatToScalar(srcWidth), 0);
src_points[2].set(0, SkFloatToScalar(srcHeight));
SkPoint dest_points[3];
matrix.mapPoints(dest_points, src_points, 3);
*destWidth = SkScalarToFloat((dest_points[1] - dest_points[0]).length());
*destHeight = SkScalarToFloat((dest_points[2] - dest_points[0]).length());
}
FloatRect normalizeRect(const FloatRect& rect)
{
FloatRect norm = rect;
if (norm.width() < 0) {
norm.setX(norm.x() + norm.width());
norm.setWidth(-norm.width());
}
if (norm.height() < 0) {
norm.setY(norm.y() + norm.height());
norm.setHeight(-norm.height());
}
return norm;
}
bool FrameData::clear(bool clearMetadata)
{
if (clearMetadata)
m_haveMetadata = false;
if (m_frame) {
delete m_frame;
m_frame = 0;
return true;
}
return false;
}
void Image::drawPattern(GraphicsContext* context,
const FloatRect& floatSrcRect,
const AffineTransform& patternTransform,
const FloatPoint& phase,
ColorSpace styleColorSpace,
CompositeOperator compositeOp,
const FloatRect& destRect)
{
FloatRect normSrcRect = normalizeRect(floatSrcRect);
if (destRect.isEmpty() || normSrcRect.isEmpty())
return;
NativeImageSkia* bitmap = nativeImageForCurrentFrame();
if (!bitmap)
return;
SkBitmap srcSubset;
SkIRect srcRect = enclosingIntRect(normSrcRect);
bitmap->extractSubset(&srcSubset, srcRect);
SkBitmap resampled;
SkShader* shader;
float destBitmapWidth, destBitmapHeight;
TransformDimensions(patternTransform, srcRect.width(), srcRect.height(),
&destBitmapWidth, &destBitmapHeight);
ResamplingMode resampling;
if (context->platformContext()->useSkiaGPU())
resampling = RESAMPLE_LINEAR;
else {
if (context->platformContext()->printing())
resampling = RESAMPLE_LINEAR;
else
resampling = computeResamplingMode(context->platformContext(), *bitmap, srcRect.width(), srcRect.height(), destBitmapWidth, destBitmapHeight);
}
SkMatrix matrix(patternTransform);
if (resampling == RESAMPLE_AWESOME) {
SkBitmap resampled;
int width = static_cast<int>(destBitmapWidth);
int height = static_cast<int>(destBitmapHeight);
if (!srcRect.fLeft && !srcRect.fTop
&& srcRect.fRight == bitmap->width() && srcRect.fBottom == bitmap->height()
&& (bitmap->hasResizedBitmap(width, height)
|| bitmap->shouldCacheResampling(width, height, width, height))) {
resampled = bitmap->resizedBitmap(width, height);
} else {
resampled = skia::ImageOperations::Resize(srcSubset,
skia::ImageOperations::RESIZE_LANCZOS3, width, height);
}
shader = SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);
matrix.setScaleX(SkIntToScalar(1));
matrix.setScaleY(SkIntToScalar(1));
} else {
shader = SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);
}
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);
}
static void drawBitmapGLES2(GraphicsContext* ctxt, NativeImageSkia* bitmap, const FloatRect& srcRect, const FloatRect& dstRect, ColorSpace styleColorSpace, CompositeOperator compositeOp)
{
ctxt->platformContext()->prepareForHardwareDraw();
GraphicsContextGPU* gpuCanvas = ctxt->platformContext()->gpuCanvas();
Texture* texture = gpuCanvas->getTexture(bitmap);
if (!texture) {
ASSERT(bitmap->config() == SkBitmap::kARGB_8888_Config);
ASSERT(bitmap->rowBytes() == bitmap->width() * 4);
texture = gpuCanvas->createTexture(bitmap, Texture::BGRA8, bitmap->width(), bitmap->height());
SkAutoLockPixels lock(*bitmap);
ASSERT(bitmap->getPixels());
texture->load(bitmap->getPixels());
}
gpuCanvas->drawTexturedRect(texture, srcRect, dstRect, styleColorSpace, compositeOp);
}
void BitmapImage::initPlatformData()
{
}
void BitmapImage::invalidatePlatformData()
{
}
void BitmapImage::checkForSolidColor()
{
m_isSolidColor = false;
m_checkedForSolidColor = true;
if (frameCount() > 1)
return;
WebCore::NativeImageSkia* frame = frameAtIndex(0);
if (frame && size().width() == 1 && size().height() == 1) {
SkAutoLockPixels lock(*frame);
if (!frame->getPixels())
return;
m_isSolidColor = true;
m_solidColor = Color(frame->getColor(0, 0));
}
}
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect,
const FloatRect& srcRect, ColorSpace colorSpace, CompositeOperator compositeOp)
{
if (!m_source.initialized())
return;
startAnimation();
NativeImageSkia* bm = nativeImageForCurrentFrame();
if (!bm)
return;
FloatRect normDstRect = normalizeRect(dstRect);
FloatRect normSrcRect = normalizeRect(srcRect);
if (normSrcRect.isEmpty() || normDstRect.isEmpty())
return;
if (ctxt->platformContext()->useGPU() && ctxt->platformContext()->canAccelerate()) {
drawBitmapGLES2(ctxt, bm, normSrcRect, normDstRect, colorSpace, compositeOp);
return;
}
ctxt->platformContext()->prepareForSoftwareDraw();
paintSkBitmap(ctxt->platformContext(),
*bm,
enclosingIntRect(normSrcRect),
normDstRect,
WebCoreCompositeToSkiaComposite(compositeOp));
}
void BitmapImageSingleFrameSkia::draw(GraphicsContext* ctxt,
const FloatRect& dstRect,
const FloatRect& srcRect,
ColorSpace styleColorSpace,
CompositeOperator compositeOp)
{
FloatRect normDstRect = normalizeRect(dstRect);
FloatRect normSrcRect = normalizeRect(srcRect);
if (normSrcRect.isEmpty() || normDstRect.isEmpty())
return;
if (ctxt->platformContext()->useGPU() && ctxt->platformContext()->canAccelerate()) {
drawBitmapGLES2(ctxt, &m_nativeImage, srcRect, dstRect, styleColorSpace, compositeOp);
return;
}
ctxt->platformContext()->prepareForSoftwareDraw();
paintSkBitmap(ctxt->platformContext(),
m_nativeImage,
enclosingIntRect(normSrcRect),
normDstRect,
WebCoreCompositeToSkiaComposite(compositeOp));
}
BitmapImageSingleFrameSkia::BitmapImageSingleFrameSkia(const SkBitmap& bitmap)
: m_nativeImage(bitmap)
{
}
PassRefPtr<BitmapImageSingleFrameSkia> BitmapImageSingleFrameSkia::create(const SkBitmap& bitmap, bool copyPixels)
{
if (copyPixels) {
SkBitmap temp;
bitmap.copyTo(&temp, bitmap.config());
return adoptRef(new BitmapImageSingleFrameSkia(temp));
}
return adoptRef(new BitmapImageSingleFrameSkia(bitmap));
}
}