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 "SkBitmapRef.h"
38#include "SkCanvas.h"
39#include "SkColorPriv.h"
40#include "SkImageDecoder.h"
41#include "SkShader.h"
42#include "SkString.h"
43#include "SkTemplates.h"
44#include "SkiaUtils.h"
45
46#include <androidfw/AssetManager.h>
47
48//#define TRACE_SUBSAMPLED_BITMAPS
49//#define TRACE_SKIPPED_BITMAPS
50
51android::AssetManager* globalAssetManager() {
52    static android::AssetManager* gGlobalAssetMgr;
53    if (!gGlobalAssetMgr) {
54        gGlobalAssetMgr = new android::AssetManager();
55        gGlobalAssetMgr->addDefaultAssets();
56    }
57    return gGlobalAssetMgr;
58}
59
60namespace WebCore {
61
62bool FrameData::clear(bool clearMetadata)
63{
64    if (clearMetadata)
65        m_haveMetadata = false;
66
67    if (m_frame) {
68        m_frame->unref();
69        m_frame = 0;
70        return true;
71    }
72    return false;
73}
74
75BitmapImage::BitmapImage(SkBitmapRef* ref, ImageObserver* observer)
76    : Image(observer)
77    , m_currentFrame(0)
78    , m_frames(0)
79    , m_frameTimer(0)
80    , m_repetitionCount(0)
81    , m_repetitionCountStatus(Unknown)
82    , m_repetitionsComplete(0)
83    , m_isSolidColor(false)
84    , m_animationFinished(true)
85    , m_allDataReceived(true)
86    , m_haveSize(true)
87    , m_sizeAvailable(true)
88    , m_decodedSize(0)
89    , m_haveFrameCount(true)
90    , m_frameCount(1)
91{
92    initPlatformData();
93
94    m_size = IntSize(ref->bitmap().width(), ref->bitmap().height());
95
96    m_frames.grow(1);
97    m_frames[0].m_frame = ref;
98    m_frames[0].m_hasAlpha = !ref->bitmap().isOpaque();
99    checkForSolidColor();
100    ref->ref();
101}
102
103
104void BitmapImage::initPlatformData()
105{
106    m_source.clearURL();
107}
108
109void BitmapImage::invalidatePlatformData()
110{
111}
112
113void BitmapImage::checkForSolidColor()
114{
115    m_checkedForSolidColor = true;
116    m_isSolidColor = false;
117    if (frameCount() == 1) {
118        SkBitmapRef* ref = frameAtIndex(0);
119        if (!ref) {
120            return; // keep solid == false
121        }
122
123        const SkBitmap& bm = ref->bitmap();
124        if (bm.width() != 1 || bm.height() != 1) {
125            return;  // keep solid == false
126        }
127
128        SkAutoLockPixels alp(bm);
129        if (!bm.readyToDraw()) {
130            return;  // keep solid == false
131        }
132
133        SkPMColor color;
134        switch (bm.getConfig()) {
135            case SkBitmap::kARGB_8888_Config:
136                color = *bm.getAddr32(0, 0);
137                break;
138            case SkBitmap::kRGB_565_Config:
139                color = SkPixel16ToPixel32(*bm.getAddr16(0, 0));
140                break;
141            case SkBitmap::kIndex8_Config: {
142                SkColorTable* ctable = bm.getColorTable();
143                if (!ctable) {
144                return;
145                }
146                color = (*ctable)[*bm.getAddr8(0, 0)];
147                break;
148            }
149            default:
150                return;  // keep solid == false
151        }
152        m_isSolidColor = true;
153        m_solidColor = SkPMColorToWebCoreColor(color);
154    }
155}
156
157static void round(SkIRect* dst, const WebCore::FloatRect& src)
158{
159    dst->set(SkScalarRound(SkFloatToScalar(src.x())),
160             SkScalarRound(SkFloatToScalar(src.y())),
161             SkScalarRound(SkFloatToScalar((src.x() + src.width()))),
162             SkScalarRound(SkFloatToScalar((src.y() + src.height()))));
163}
164
165static void round_scaled(SkIRect* dst, const WebCore::FloatRect& src,
166                         float sx, float sy)
167{
168    dst->set(SkScalarRound(SkFloatToScalar(src.x() * sx)),
169             SkScalarRound(SkFloatToScalar(src.y() * sy)),
170             SkScalarRound(SkFloatToScalar((src.x() + src.width()) * sx)),
171             SkScalarRound(SkFloatToScalar((src.y() + src.height()) * sy)));
172}
173
174void BitmapImage::draw(GraphicsContext* gc, const FloatRect& dstRect,
175                   const FloatRect& srcRect, ColorSpace,
176                   CompositeOperator compositeOp)
177{
178    startAnimation();
179
180    SkBitmapRef* image = this->nativeImageForCurrentFrame();
181    if (!image) { // If it's too early we won't have an image yet.
182        return;
183    }
184
185    // in case we get called with an incomplete bitmap
186    const SkBitmap& bitmap = image->bitmap();
187    if (bitmap.getPixels() == NULL && bitmap.pixelRef() == NULL) {
188#ifdef TRACE_SKIPPED_BITMAPS
189        SkDebugf("----- skip bitmapimage: [%d %d] pixels %p pixelref %p\n",
190                 bitmap.width(), bitmap.height(),
191                 bitmap.getPixels(), bitmap.pixelRef());
192#endif
193        return;
194    }
195
196    SkIRect srcR;
197    SkRect  dstR(dstRect);
198    float invScaleX = (float)bitmap.width() / image->origWidth();
199    float invScaleY = (float)bitmap.height() / image->origHeight();
200
201    round_scaled(&srcR, srcRect, invScaleX, invScaleY);
202    if (srcR.isEmpty() || dstR.isEmpty()) {
203#ifdef TRACE_SKIPPED_BITMAPS
204        SkDebugf("----- skip bitmapimage: [%d %d] src-empty %d dst-empty %d\n",
205                 bitmap.width(), bitmap.height(),
206                 srcR.isEmpty(), dstR.isEmpty());
207#endif
208        return;
209    }
210
211    gc->platformContext()->drawBitmapRect(bitmap, &srcR, dstR, compositeOp);
212
213#ifdef TRACE_SUBSAMPLED_BITMAPS
214    if (bitmap.width() != image->origWidth() ||
215        bitmap.height() != image->origHeight()) {
216        SkDebugf("--- BitmapImage::draw [%d %d] orig [%d %d]\n",
217                 bitmap.width(), bitmap.height(),
218                 image->origWidth(), image->origHeight());
219    }
220#endif
221}
222
223void BitmapImage::setURL(const String& str)
224{
225    m_source.setURL(str);
226}
227
228///////////////////////////////////////////////////////////////////////////////
229
230void Image::drawPattern(GraphicsContext* gc, const FloatRect& srcRect,
231                        const AffineTransform& patternTransform,
232                        const FloatPoint& phase, ColorSpace,
233                        CompositeOperator compositeOp, const FloatRect& destRect)
234{
235    SkBitmapRef* image = this->nativeImageForCurrentFrame();
236    if (!image || destRect.isEmpty())
237        return;
238
239    // in case we get called with an incomplete bitmap
240    const SkBitmap& origBitmap = image->bitmap();
241    if (origBitmap.getPixels() == NULL && origBitmap.pixelRef() == NULL)
242        return;
243
244    SkIRect srcR;
245    // we may have to scale if the image has been subsampled (so save RAM)
246    bool imageIsSubSampled = image->origWidth() != origBitmap.width() ||
247                             image->origHeight() != origBitmap.height();
248    float scaleX = 1;
249    float scaleY = 1;
250    if (imageIsSubSampled) {
251        scaleX = (float)image->origWidth() / origBitmap.width();
252        scaleY = (float)image->origHeight() / origBitmap.height();
253        round_scaled(&srcR, srcRect, 1 / scaleX, 1 / scaleY);
254    } else
255        round(&srcR, srcRect);
256
257    // now extract the proper subset of the src image
258    SkBitmap bitmap;
259    if (!origBitmap.extractSubset(&bitmap, srcR)) {
260        SkDebugf("--- Image::drawPattern calling extractSubset failed\n");
261        return;
262    }
263
264    SkMatrix matrix(patternTransform);
265
266    if (imageIsSubSampled) {
267        matrix.preScale(SkFloatToScalar(scaleX), SkFloatToScalar(scaleY));
268    }
269    // We also need to translate it such that the origin of the pattern is the
270    // origin of the destination rect, which is what WebKit expects. Skia uses
271    // the coordinate system origin as the base for the patter. If WebKit wants
272    // a shifted image, it will shift it from there using the patternTransform.
273    float tx = phase.x() + srcRect.x() * patternTransform.a();
274    float ty = phase.y() + srcRect.y() * patternTransform.d();
275    matrix.postTranslate(SkFloatToScalar(tx), SkFloatToScalar(ty));
276
277    gc->platformContext()->drawBitmapPattern(bitmap, matrix, compositeOp, destRect);
278}
279
280// missingImage, textAreaResizeCorner
281PassRefPtr<Image> Image::loadPlatformResource(const char *name)
282{
283    android::AssetManager* am = globalAssetManager();
284
285    SkString path("webkit/");
286    path.append(name);
287    path.append(".png");
288
289    android::Asset* a = am->open(path.c_str(),
290                                 android::Asset::ACCESS_BUFFER);
291    if (a == NULL) {
292        SkDebugf("---------------- failed to open image asset %s\n", name);
293        return NULL;
294    }
295
296    SkAutoTDelete<android::Asset> ad(a);
297
298    SkBitmap bm;
299    if (SkImageDecoder::DecodeMemory(a->getBuffer(false), a->getLength(), &bm)) {
300        SkBitmapRef* ref = new SkBitmapRef(bm);
301        // create will call ref(), so we need aur() to release ours upon return
302        SkAutoUnref aur(ref);
303        return BitmapImage::create(ref, 0);
304    }
305    return Image::nullImage();
306}
307
308}   // namespace
309