Home | History | Annotate | Download | only in android
      1 /*
      2  * Copyright 2009, The Android Open Source Project
      3  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.  All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *  * Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  *  * Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
     15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #include "config.h"
     28 #include "TransformationMatrix.h"
     29 #include "BitmapImage.h"
     30 #include "Image.h"
     31 #include "FloatRect.h"
     32 #include "GraphicsContext.h"
     33 #include "PlatformGraphicsContext.h"
     34 #include "PlatformString.h"
     35 #include "SharedBuffer.h"
     36 
     37 #include "android_graphics.h"
     38 #include "SkBitmapRef.h"
     39 #include "SkCanvas.h"
     40 #include "SkColorPriv.h"
     41 #include "SkImageDecoder.h"
     42 #include "SkShader.h"
     43 #include "SkString.h"
     44 #include "SkTemplates.h"
     45 #include "SkiaUtils.h"
     46 
     47 #include <utils/AssetManager.h>
     48 
     49 //#define TRACE_SUBSAMPLED_BITMAPS
     50 //#define TRACE_SKIPPED_BITMAPS
     51 
     52 android::AssetManager* globalAssetManager() {
     53     static android::AssetManager* gGlobalAssetMgr;
     54     if (!gGlobalAssetMgr) {
     55         gGlobalAssetMgr = new android::AssetManager();
     56         gGlobalAssetMgr->addDefaultAssets();
     57     }
     58     return gGlobalAssetMgr;
     59 }
     60 
     61 namespace WebCore {
     62 
     63 bool FrameData::clear(bool clearMetadata)
     64 {
     65     if (clearMetadata)
     66         m_haveMetadata = false;
     67 
     68     if (m_frame) {
     69         m_frame->unref();
     70         m_frame = 0;
     71         return true;
     72     }
     73     return false;
     74 }
     75 
     76 BitmapImage::BitmapImage(SkBitmapRef* ref, ImageObserver* observer)
     77     : Image(observer)
     78     , m_currentFrame(0)
     79     , m_frames(0)
     80     , m_frameTimer(0)
     81     , m_repetitionCount(0)
     82     , m_repetitionCountStatus(Unknown)
     83     , m_repetitionsComplete(0)
     84     , m_isSolidColor(false)
     85     , m_animationFinished(true)
     86     , m_allDataReceived(true)
     87     , m_haveSize(true)
     88     , m_sizeAvailable(true)
     89     , m_decodedSize(0)
     90     , m_haveFrameCount(true)
     91     , m_frameCount(1)
     92 {
     93     initPlatformData();
     94 
     95     m_size = IntSize(ref->bitmap().width(), ref->bitmap().height());
     96 
     97     m_frames.grow(1);
     98     m_frames[0].m_frame = ref;
     99     m_frames[0].m_hasAlpha = !ref->bitmap().isOpaque();
    100     checkForSolidColor();
    101     ref->ref();
    102 }
    103 
    104 
    105 void BitmapImage::initPlatformData()
    106 {
    107     m_source.clearURL();
    108 }
    109 
    110 void BitmapImage::invalidatePlatformData()
    111 {
    112 }
    113 
    114 void BitmapImage::checkForSolidColor()
    115 {
    116     m_checkedForSolidColor = true;
    117     m_isSolidColor = false;
    118     if (frameCount() == 1) {
    119         SkBitmapRef* ref = frameAtIndex(0);
    120         if (!ref) {
    121             return; // keep solid == false
    122         }
    123 
    124         const SkBitmap& bm = ref->bitmap();
    125         if (bm.width() != 1 || bm.height() != 1) {
    126             return;  // keep solid == false
    127         }
    128 
    129         SkAutoLockPixels alp(bm);
    130         if (!bm.readyToDraw()) {
    131             return;  // keep solid == false
    132         }
    133 
    134         SkPMColor color;
    135         switch (bm.getConfig()) {
    136             case SkBitmap::kARGB_8888_Config:
    137                 color = *bm.getAddr32(0, 0);
    138                 break;
    139             case SkBitmap::kRGB_565_Config:
    140                 color = SkPixel16ToPixel32(*bm.getAddr16(0, 0));
    141                 break;
    142             case SkBitmap::kIndex8_Config: {
    143                 SkColorTable* ctable = bm.getColorTable();
    144                 if (!ctable) {
    145                 return;
    146                 }
    147                 color = (*ctable)[*bm.getAddr8(0, 0)];
    148                 break;
    149             }
    150             default:
    151                 return;  // keep solid == false
    152         }
    153         m_isSolidColor = true;
    154         m_solidColor = SkPMColorToWebCoreColor(color);
    155     }
    156 }
    157 
    158 static void round(SkIRect* dst, const WebCore::FloatRect& src)
    159 {
    160     dst->set(SkScalarRound(SkFloatToScalar(src.x())),
    161              SkScalarRound(SkFloatToScalar(src.y())),
    162              SkScalarRound(SkFloatToScalar((src.x() + src.width()))),
    163              SkScalarRound(SkFloatToScalar((src.y() + src.height()))));
    164 }
    165 
    166 static void round_scaled(SkIRect* dst, const WebCore::FloatRect& src,
    167                          float sx, float sy)
    168 {
    169     dst->set(SkScalarRound(SkFloatToScalar(src.x() * sx)),
    170              SkScalarRound(SkFloatToScalar(src.y() * sy)),
    171              SkScalarRound(SkFloatToScalar((src.x() + src.width()) * sx)),
    172              SkScalarRound(SkFloatToScalar((src.y() + src.height()) * sy)));
    173 }
    174 
    175 static inline void fixPaintForBitmapsThatMaySeam(SkPaint* paint) {
    176     /*  Bitmaps may be drawn to seem next to other images. If we are drawn
    177         zoomed, or at fractional coordinates, we may see cracks/edges if
    178         we antialias, because that will cause us to draw the same pixels
    179         more than once (e.g. from the left and right bitmaps that share
    180         an edge).
    181 
    182         Disabling antialiasing fixes this, and since so far we are never
    183         rotated at non-multiple-of-90 angles, this seems to do no harm
    184      */
    185     paint->setAntiAlias(false);
    186 }
    187 
    188 void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect,
    189                    const FloatRect& srcRect, ColorSpace,
    190                    CompositeOperator compositeOp)
    191 {
    192     startAnimation();
    193 
    194     SkBitmapRef* image = this->nativeImageForCurrentFrame();
    195     if (!image) { // If it's too early we won't have an image yet.
    196         return;
    197     }
    198 
    199     // in case we get called with an incomplete bitmap
    200     const SkBitmap& bitmap = image->bitmap();
    201     if (bitmap.getPixels() == NULL && bitmap.pixelRef() == NULL) {
    202 #ifdef TRACE_SKIPPED_BITMAPS
    203         SkDebugf("----- skip bitmapimage: [%d %d] pixels %p pixelref %p\n",
    204                  bitmap.width(), bitmap.height(),
    205                  bitmap.getPixels(), bitmap.pixelRef());
    206 #endif
    207         return;
    208     }
    209 
    210     SkIRect srcR;
    211     SkRect  dstR(dstRect);
    212     float invScaleX = (float)bitmap.width() / image->origWidth();
    213     float invScaleY = (float)bitmap.height() / image->origHeight();
    214 
    215     round_scaled(&srcR, srcRect, invScaleX, invScaleY);
    216     if (srcR.isEmpty() || dstR.isEmpty()) {
    217 #ifdef TRACE_SKIPPED_BITMAPS
    218         SkDebugf("----- skip bitmapimage: [%d %d] src-empty %d dst-empty %d\n",
    219                  bitmap.width(), bitmap.height(),
    220                  srcR.isEmpty(), dstR.isEmpty());
    221 #endif
    222         return;
    223     }
    224 
    225     SkCanvas*   canvas = ctxt->platformContext()->mCanvas;
    226     SkPaint     paint;
    227 
    228     ctxt->setupBitmapPaint(&paint);   // need global alpha among other things
    229     paint.setFilterBitmap(true);
    230     paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp));
    231     fixPaintForBitmapsThatMaySeam(&paint);
    232     canvas->drawBitmapRect(bitmap, &srcR, dstR, &paint);
    233 
    234 #ifdef TRACE_SUBSAMPLED_BITMAPS
    235     if (bitmap.width() != image->origWidth() ||
    236         bitmap.height() != image->origHeight()) {
    237         SkDebugf("--- BitmapImage::draw [%d %d] orig [%d %d]\n",
    238                  bitmap.width(), bitmap.height(),
    239                  image->origWidth(), image->origHeight());
    240     }
    241 #endif
    242 }
    243 
    244 void BitmapImage::setURL(const String& str)
    245 {
    246     m_source.setURL(str);
    247 }
    248 
    249 ///////////////////////////////////////////////////////////////////////////////
    250 
    251 void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& srcRect,
    252                         const AffineTransform& patternTransform,
    253                         const FloatPoint& phase, ColorSpace,
    254                         CompositeOperator compositeOp, const FloatRect& destRect)
    255 {
    256     SkBitmapRef* image = this->nativeImageForCurrentFrame();
    257     if (!image) { // If it's too early we won't have an image yet.
    258         return;
    259     }
    260 
    261     // in case we get called with an incomplete bitmap
    262     const SkBitmap& origBitmap = image->bitmap();
    263     if (origBitmap.getPixels() == NULL && origBitmap.pixelRef() == NULL) {
    264         return;
    265     }
    266 
    267     SkRect  dstR(destRect);
    268     if (dstR.isEmpty()) {
    269         return;
    270     }
    271 
    272     SkIRect srcR;
    273     // we may have to scale if the image has been subsampled (so save RAM)
    274     bool imageIsSubSampled = image->origWidth() != origBitmap.width() ||
    275                              image->origHeight() != origBitmap.height();
    276     float scaleX = 1;
    277     float scaleY = 1;
    278     if (imageIsSubSampled) {
    279         scaleX = (float)image->origWidth() / origBitmap.width();
    280         scaleY = (float)image->origHeight() / origBitmap.height();
    281 //        SkDebugf("----- subsampled %g %g\n", scaleX, scaleY);
    282         round_scaled(&srcR, srcRect, 1 / scaleX, 1 / scaleY);
    283     } else {
    284         round(&srcR, srcRect);
    285     }
    286 
    287     // now extract the proper subset of the src image
    288     SkBitmap bitmap;
    289     if (!origBitmap.extractSubset(&bitmap, srcR)) {
    290         SkDebugf("--- Image::drawPattern calling extractSubset failed\n");
    291         return;
    292     }
    293 
    294     SkCanvas*   canvas = ctxt->platformContext()->mCanvas;
    295     SkPaint     paint;
    296     ctxt->setupBitmapPaint(&paint);   // need global alpha among other things
    297 
    298     SkShader* shader = SkShader::CreateBitmapShader(bitmap,
    299                                                     SkShader::kRepeat_TileMode,
    300                                                     SkShader::kRepeat_TileMode);
    301     paint.setShader(shader)->unref();
    302     // now paint is the only owner of shader
    303     paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp));
    304     paint.setFilterBitmap(true);
    305     fixPaintForBitmapsThatMaySeam(&paint);
    306 
    307     SkMatrix matrix(patternTransform);
    308 
    309     if (imageIsSubSampled) {
    310         matrix.preScale(SkFloatToScalar(scaleX), SkFloatToScalar(scaleY));
    311     }
    312     // We also need to translate it such that the origin of the pattern is the
    313     // origin of the destination rect, which is what WebKit expects. Skia uses
    314     // the coordinate system origin as the base for the patter. If WebKit wants
    315     // a shifted image, it will shift it from there using the patternTransform.
    316     float tx = phase.x() + srcRect.x() * patternTransform.a();
    317     float ty = phase.y() + srcRect.y() * patternTransform.d();
    318     matrix.postTranslate(SkFloatToScalar(tx), SkFloatToScalar(ty));
    319     shader->setLocalMatrix(matrix);
    320 #if 0
    321     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",
    322              srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height(),
    323              destRect.x(), destRect.y(), destRect.width(), destRect.height(),
    324              patternTransform.a(), patternTransform.b(), patternTransform.c(),
    325              patternTransform.d(), patternTransform.e(), patternTransform.f(),
    326              matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
    327 #endif
    328     canvas->drawRect(dstR, paint);
    329 
    330 #ifdef TRACE_SUBSAMPLED_BITMAPS
    331     if (bitmap.width() != image->origWidth() ||
    332         bitmap.height() != image->origHeight()) {
    333         SkDebugf("--- Image::drawPattern [%d %d] orig [%d %d] dst [%g %g]\n",
    334                  bitmap.width(), bitmap.height(),
    335                  image->origWidth(), image->origHeight(),
    336                  SkScalarToFloat(dstR.width()), SkScalarToFloat(dstR.height()));
    337     }
    338 #endif
    339 }
    340 
    341 // missingImage, textAreaResizeCorner
    342 PassRefPtr<Image> Image::loadPlatformResource(const char *name)
    343 {
    344     android::AssetManager* am = globalAssetManager();
    345 
    346     SkString path("webkit/");
    347     path.append(name);
    348     path.append(".png");
    349 
    350     android::Asset* a = am->open(path.c_str(),
    351                                  android::Asset::ACCESS_BUFFER);
    352     if (a == NULL) {
    353         SkDebugf("---------------- failed to open image asset %s\n", name);
    354         return NULL;
    355     }
    356 
    357     SkAutoTDelete<android::Asset> ad(a);
    358 
    359     SkBitmap bm;
    360     if (SkImageDecoder::DecodeMemory(a->getBuffer(false), a->getLength(), &bm)) {
    361         SkBitmapRef* ref = new SkBitmapRef(bm);
    362         // create will call ref(), so we need aur() to release ours upon return
    363         SkAutoUnref aur(ref);
    364         return BitmapImage::create(ref, 0);
    365     }
    366     return Image::nullImage();
    367 }
    368 
    369 }   // namespace
    370