blob: 831c6f3398c02ae458aa23b7fa97d2900fdceb55 [file] [log] [blame]
/*
* Copyright 2009, The Android Open Source Project
* Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "TransformationMatrix.h"
#include "BitmapImage.h"
#include "Image.h"
#include "FloatRect.h"
#include "GraphicsContext.h"
#include "PlatformGraphicsContext.h"
#include "PlatformString.h"
#include "SharedBuffer.h"
#include "android_graphics.h"
#include "SkBitmapRef.h"
#include "SkCanvas.h"
#include "SkColorPriv.h"
#include "SkImageDecoder.h"
#include "SkShader.h"
#include "SkString.h"
#include "SkTemplates.h"
#include "SkiaUtils.h"
#include <utils/AssetManager.h>
//#define TRACE_SUBSAMPLED_BITMAPS
//#define TRACE_SKIPPED_BITMAPS
android::AssetManager* globalAssetManager() {
static android::AssetManager* gGlobalAssetMgr;
if (!gGlobalAssetMgr) {
gGlobalAssetMgr = new android::AssetManager();
gGlobalAssetMgr->addDefaultAssets();
}
return gGlobalAssetMgr;
}
namespace WebCore {
bool FrameData::clear(bool clearMetadata)
{
if (clearMetadata)
m_haveMetadata = false;
if (m_frame) {
m_frame->unref();
m_frame = 0;
return true;
}
return false;
}
BitmapImage::BitmapImage(SkBitmapRef* ref, ImageObserver* observer)
: Image(observer)
, m_currentFrame(0)
, m_frames(0)
, m_frameTimer(0)
, m_repetitionCount(0)
, m_repetitionCountStatus(Unknown)
, m_repetitionsComplete(0)
, m_isSolidColor(false)
, m_animationFinished(true)
, m_allDataReceived(true)
, m_haveSize(true)
, m_sizeAvailable(true)
, m_decodedSize(0)
, m_haveFrameCount(true)
, m_frameCount(1)
{
initPlatformData();
m_size = IntSize(ref->bitmap().width(), ref->bitmap().height());
m_frames.grow(1);
m_frames[0].m_frame = ref;
m_frames[0].m_hasAlpha = !ref->bitmap().isOpaque();
checkForSolidColor();
ref->ref();
}
void BitmapImage::initPlatformData()
{
m_source.clearURL();
}
void BitmapImage::invalidatePlatformData()
{
}
void BitmapImage::checkForSolidColor()
{
m_checkedForSolidColor = true;
m_isSolidColor = false;
if (frameCount() == 1) {
SkBitmapRef* ref = frameAtIndex(0);
if (!ref) {
return; // keep solid == false
}
const SkBitmap& bm = ref->bitmap();
if (bm.width() != 1 || bm.height() != 1) {
return; // keep solid == false
}
SkAutoLockPixels alp(bm);
if (!bm.readyToDraw()) {
return; // keep solid == false
}
SkPMColor color;
switch (bm.getConfig()) {
case SkBitmap::kARGB_8888_Config:
color = *bm.getAddr32(0, 0);
break;
case SkBitmap::kRGB_565_Config:
color = SkPixel16ToPixel32(*bm.getAddr16(0, 0));
break;
case SkBitmap::kIndex8_Config: {
SkColorTable* ctable = bm.getColorTable();
if (!ctable) {
return;
}
color = (*ctable)[*bm.getAddr8(0, 0)];
break;
}
default:
return; // keep solid == false
}
m_isSolidColor = true;
m_solidColor = SkPMColorToWebCoreColor(color);
}
}
static void round(SkIRect* dst, const WebCore::FloatRect& src)
{
dst->set(SkScalarRound(SkFloatToScalar(src.x())),
SkScalarRound(SkFloatToScalar(src.y())),
SkScalarRound(SkFloatToScalar((src.x() + src.width()))),
SkScalarRound(SkFloatToScalar((src.y() + src.height()))));
}
static void round_scaled(SkIRect* dst, const WebCore::FloatRect& src,
float sx, float sy)
{
dst->set(SkScalarRound(SkFloatToScalar(src.x() * sx)),
SkScalarRound(SkFloatToScalar(src.y() * sy)),
SkScalarRound(SkFloatToScalar((src.x() + src.width()) * sx)),
SkScalarRound(SkFloatToScalar((src.y() + src.height()) * sy)));
}
static inline void fixPaintForBitmapsThatMaySeam(SkPaint* paint) {
/* Bitmaps may be drawn to seem next to other images. If we are drawn
zoomed, or at fractional coordinates, we may see cracks/edges if
we antialias, because that will cause us to draw the same pixels
more than once (e.g. from the left and right bitmaps that share
an edge).
Disabling antialiasing fixes this, and since so far we are never
rotated at non-multiple-of-90 angles, this seems to do no harm
*/
paint->setAntiAlias(false);
}
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect,
const FloatRect& srcRect, CompositeOperator compositeOp)
{
startAnimation();
SkBitmapRef* image = this->nativeImageForCurrentFrame();
if (!image) { // If it's too early we won't have an image yet.
return;
}
// in case we get called with an incomplete bitmap
const SkBitmap& bitmap = image->bitmap();
if (bitmap.getPixels() == NULL && bitmap.pixelRef() == NULL) {
#ifdef TRACE_SKIPPED_BITMAPS
SkDebugf("----- skip bitmapimage: [%d %d] pixels %p pixelref %p\n",
bitmap.width(), bitmap.height(),
bitmap.getPixels(), bitmap.pixelRef());
#endif
return;
}
SkIRect srcR;
SkRect dstR(dstRect);
float invScaleX = (float)bitmap.width() / image->origWidth();
float invScaleY = (float)bitmap.height() / image->origHeight();
round_scaled(&srcR, srcRect, invScaleX, invScaleY);
if (srcR.isEmpty() || dstR.isEmpty()) {
#ifdef TRACE_SKIPPED_BITMAPS
SkDebugf("----- skip bitmapimage: [%d %d] src-empty %d dst-empty %d\n",
bitmap.width(), bitmap.height(),
srcR.isEmpty(), dstR.isEmpty());
#endif
return;
}
SkCanvas* canvas = ctxt->platformContext()->mCanvas;
SkPaint paint;
ctxt->setupBitmapPaint(&paint); // need global alpha among other things
paint.setFilterBitmap(true);
paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp));
fixPaintForBitmapsThatMaySeam(&paint);
canvas->drawBitmapRect(bitmap, &srcR, dstR, &paint);
#ifdef TRACE_SUBSAMPLED_BITMAPS
if (bitmap.width() != image->origWidth() ||
bitmap.height() != image->origHeight()) {
SkDebugf("--- BitmapImage::draw [%d %d] orig [%d %d]\n",
bitmap.width(), bitmap.height(),
image->origWidth(), image->origHeight());
}
#endif
}
void BitmapImage::setURL(const String& str)
{
m_source.setURL(str);
}
///////////////////////////////////////////////////////////////////////////////
void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& srcRect,
const TransformationMatrix& patternTransform,
const FloatPoint& phase, CompositeOperator compositeOp,
const FloatRect& destRect)
{
SkBitmapRef* image = this->nativeImageForCurrentFrame();
if (!image) { // If it's too early we won't have an image yet.
return;
}
// in case we get called with an incomplete bitmap
const SkBitmap& origBitmap = image->bitmap();
if (origBitmap.getPixels() == NULL && origBitmap.pixelRef() == NULL) {
return;
}
SkRect dstR(destRect);
if (dstR.isEmpty()) {
return;
}
SkIRect srcR;
// we may have to scale if the image has been subsampled (so save RAM)
bool imageIsSubSampled = image->origWidth() != origBitmap.width() ||
image->origHeight() != origBitmap.height();
float scaleX = 1;
float scaleY = 1;
if (imageIsSubSampled) {
scaleX = (float)image->origWidth() / origBitmap.width();
scaleY = (float)image->origHeight() / origBitmap.height();
// SkDebugf("----- subsampled %g %g\n", scaleX, scaleY);
round_scaled(&srcR, srcRect, 1 / scaleX, 1 / scaleY);
} else {
round(&srcR, srcRect);
}
// now extract the proper subset of the src image
SkBitmap bitmap;
if (!origBitmap.extractSubset(&bitmap, srcR)) {
SkDebugf("--- Image::drawPattern calling extractSubset failed\n");
return;
}
SkCanvas* canvas = ctxt->platformContext()->mCanvas;
SkPaint paint;
ctxt->setupBitmapPaint(&paint); // need global alpha among other things
SkShader* shader = SkShader::CreateBitmapShader(bitmap,
SkShader::kRepeat_TileMode,
SkShader::kRepeat_TileMode);
paint.setShader(shader)->unref();
// now paint is the only owner of shader
paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp));
paint.setFilterBitmap(true);
fixPaintForBitmapsThatMaySeam(&paint);
SkMatrix matrix(patternTransform);
if (imageIsSubSampled) {
matrix.preScale(SkFloatToScalar(scaleX), SkFloatToScalar(scaleY));
}
// 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 tx = phase.x() + srcRect.x() * patternTransform.a();
float ty = phase.y() + srcRect.y() * patternTransform.d();
matrix.postTranslate(SkFloatToScalar(tx), SkFloatToScalar(ty));
shader->setLocalMatrix(matrix);
#if 0
SkDebugf("--- drawPattern: src [%g %g %g %g] dst [%g %g %g %g] transform [%g %g %g %g %g %g] matrix [%g %g %g %g %g %g]\n",
srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height(),
destRect.x(), destRect.y(), destRect.width(), destRect.height(),
patternTransform.a(), patternTransform.b(), patternTransform.c(),
patternTransform.d(), patternTransform.e(), patternTransform.f(),
matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
#endif
canvas->drawRect(dstR, paint);
#ifdef TRACE_SUBSAMPLED_BITMAPS
if (bitmap.width() != image->origWidth() ||
bitmap.height() != image->origHeight()) {
SkDebugf("--- Image::drawPattern [%d %d] orig [%d %d] dst [%g %g]\n",
bitmap.width(), bitmap.height(),
image->origWidth(), image->origHeight(),
SkScalarToFloat(dstR.width()), SkScalarToFloat(dstR.height()));
}
#endif
}
// missingImage, textAreaResizeCorner
PassRefPtr<Image> Image::loadPlatformResource(const char *name)
{
android::AssetManager* am = globalAssetManager();
SkString path("webkit/");
path.append(name);
path.append(".png");
android::Asset* a = am->open(path.c_str(),
android::Asset::ACCESS_BUFFER);
if (a == NULL) {
SkDebugf("---------------- failed to open image asset %s\n", name);
return NULL;
}
SkAutoTDelete<android::Asset> ad(a);
SkBitmap bm;
if (SkImageDecoder::DecodeMemory(a->getBuffer(false), a->getLength(), &bm)) {
SkBitmapRef* ref = new SkBitmapRef(bm);
// create will call ref(), so we need aur() to release ours upon return
SkAutoUnref aur(ref);
return BitmapImage::create(ref, 0);
}
return Image::nullImage();
}
} // namespace