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 <androidfw/AssetManager.h>
48
49 //#define TRACE_SUBSAMPLED_BITMAPS
50 //#define TRACE_SKIPPED_BITMAPS
51
globalAssetManager()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
clear(bool clearMetadata)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
BitmapImage(SkBitmapRef * ref,ImageObserver * observer)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
initPlatformData()105 void BitmapImage::initPlatformData()
106 {
107 m_source.clearURL();
108 }
109
invalidatePlatformData()110 void BitmapImage::invalidatePlatformData()
111 {
112 }
113
checkForSolidColor()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
round(SkIRect * dst,const WebCore::FloatRect & src)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
round_scaled(SkIRect * dst,const WebCore::FloatRect & src,float sx,float sy)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
draw(GraphicsContext * gc,const FloatRect & dstRect,const FloatRect & srcRect,ColorSpace,CompositeOperator compositeOp)175 void BitmapImage::draw(GraphicsContext* gc, const FloatRect& dstRect,
176 const FloatRect& srcRect, ColorSpace,
177 CompositeOperator compositeOp)
178 {
179 startAnimation();
180
181 SkBitmapRef* image = this->nativeImageForCurrentFrame();
182 if (!image) { // If it's too early we won't have an image yet.
183 return;
184 }
185
186 // in case we get called with an incomplete bitmap
187 const SkBitmap& bitmap = image->bitmap();
188 if (bitmap.getPixels() == NULL && bitmap.pixelRef() == NULL) {
189 #ifdef TRACE_SKIPPED_BITMAPS
190 SkDebugf("----- skip bitmapimage: [%d %d] pixels %p pixelref %p\n",
191 bitmap.width(), bitmap.height(),
192 bitmap.getPixels(), bitmap.pixelRef());
193 #endif
194 return;
195 }
196
197 SkIRect srcR;
198 SkRect dstR(dstRect);
199 float invScaleX = (float)bitmap.width() / image->origWidth();
200 float invScaleY = (float)bitmap.height() / image->origHeight();
201
202 round_scaled(&srcR, srcRect, invScaleX, invScaleY);
203 if (srcR.isEmpty() || dstR.isEmpty()) {
204 #ifdef TRACE_SKIPPED_BITMAPS
205 SkDebugf("----- skip bitmapimage: [%d %d] src-empty %d dst-empty %d\n",
206 bitmap.width(), bitmap.height(),
207 srcR.isEmpty(), dstR.isEmpty());
208 #endif
209 return;
210 }
211
212 gc->platformContext()->drawBitmapRect(bitmap, &srcR, dstR, compositeOp);
213
214 #ifdef TRACE_SUBSAMPLED_BITMAPS
215 if (bitmap.width() != image->origWidth() ||
216 bitmap.height() != image->origHeight()) {
217 SkDebugf("--- BitmapImage::draw [%d %d] orig [%d %d]\n",
218 bitmap.width(), bitmap.height(),
219 image->origWidth(), image->origHeight());
220 }
221 #endif
222 }
223
setURL(const String & str)224 void BitmapImage::setURL(const String& str)
225 {
226 m_source.setURL(str);
227 }
228
229 ///////////////////////////////////////////////////////////////////////////////
230
drawPattern(GraphicsContext * gc,const FloatRect & srcRect,const AffineTransform & patternTransform,const FloatPoint & phase,ColorSpace,CompositeOperator compositeOp,const FloatRect & destRect)231 void Image::drawPattern(GraphicsContext* gc, const FloatRect& srcRect,
232 const AffineTransform& patternTransform,
233 const FloatPoint& phase, ColorSpace,
234 CompositeOperator compositeOp, const FloatRect& destRect)
235 {
236 SkBitmapRef* image = this->nativeImageForCurrentFrame();
237 if (!image || destRect.isEmpty())
238 return;
239
240 // in case we get called with an incomplete bitmap
241 const SkBitmap& origBitmap = image->bitmap();
242 if (origBitmap.getPixels() == NULL && origBitmap.pixelRef() == NULL)
243 return;
244
245 SkIRect srcR;
246 // we may have to scale if the image has been subsampled (so save RAM)
247 bool imageIsSubSampled = image->origWidth() != origBitmap.width() ||
248 image->origHeight() != origBitmap.height();
249 float scaleX = 1;
250 float scaleY = 1;
251 if (imageIsSubSampled) {
252 scaleX = (float)image->origWidth() / origBitmap.width();
253 scaleY = (float)image->origHeight() / origBitmap.height();
254 round_scaled(&srcR, srcRect, 1 / scaleX, 1 / scaleY);
255 } else
256 round(&srcR, srcRect);
257
258 // now extract the proper subset of the src image
259 SkBitmap bitmap;
260 if (!origBitmap.extractSubset(&bitmap, srcR)) {
261 SkDebugf("--- Image::drawPattern calling extractSubset failed\n");
262 return;
263 }
264
265 SkMatrix matrix(patternTransform);
266
267 if (imageIsSubSampled) {
268 matrix.preScale(SkFloatToScalar(scaleX), SkFloatToScalar(scaleY));
269 }
270 // We also need to translate it such that the origin of the pattern is the
271 // origin of the destination rect, which is what WebKit expects. Skia uses
272 // the coordinate system origin as the base for the patter. If WebKit wants
273 // a shifted image, it will shift it from there using the patternTransform.
274 float tx = phase.x() + srcRect.x() * patternTransform.a();
275 float ty = phase.y() + srcRect.y() * patternTransform.d();
276 matrix.postTranslate(SkFloatToScalar(tx), SkFloatToScalar(ty));
277
278 gc->platformContext()->drawBitmapPattern(bitmap, matrix, compositeOp, destRect);
279 }
280
281 // missingImage, textAreaResizeCorner
loadPlatformResource(const char * name)282 PassRefPtr<Image> Image::loadPlatformResource(const char *name)
283 {
284 android::AssetManager* am = globalAssetManager();
285
286 SkString path("webkit/");
287 path.append(name);
288 path.append(".png");
289
290 android::Asset* a = am->open(path.c_str(),
291 android::Asset::ACCESS_BUFFER);
292 if (a == NULL) {
293 SkDebugf("---------------- failed to open image asset %s\n", name);
294 return NULL;
295 }
296
297 SkAutoTDelete<android::Asset> ad(a);
298
299 SkBitmap bm;
300 if (SkImageDecoder::DecodeMemory(a->getBuffer(false), a->getLength(), &bm)) {
301 SkBitmapRef* ref = new SkBitmapRef(bm);
302 // create will call ref(), so we need aur() to release ours upon return
303 SkAutoUnref aur(ref);
304 return BitmapImage::create(ref, 0);
305 }
306 return Image::nullImage();
307 }
308
309 } // namespace
310