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 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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
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
fixPaintForBitmapsThatMaySeam(SkPaint * paint)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
draw(GraphicsContext * ctxt,const FloatRect & dstRect,const FloatRect & srcRect,CompositeOperator compositeOp)188 void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect,
189 const FloatRect& srcRect, CompositeOperator compositeOp)
190 {
191 startAnimation();
192
193 SkBitmapRef* image = this->nativeImageForCurrentFrame();
194 if (!image) { // If it's too early we won't have an image yet.
195 return;
196 }
197
198 // in case we get called with an incomplete bitmap
199 const SkBitmap& bitmap = image->bitmap();
200 if (bitmap.getPixels() == NULL && bitmap.pixelRef() == NULL) {
201 #ifdef TRACE_SKIPPED_BITMAPS
202 SkDebugf("----- skip bitmapimage: [%d %d] pixels %p pixelref %p\n",
203 bitmap.width(), bitmap.height(),
204 bitmap.getPixels(), bitmap.pixelRef());
205 #endif
206 return;
207 }
208
209 SkIRect srcR;
210 SkRect dstR(dstRect);
211 float invScaleX = (float)bitmap.width() / image->origWidth();
212 float invScaleY = (float)bitmap.height() / image->origHeight();
213
214 round_scaled(&srcR, srcRect, invScaleX, invScaleY);
215 if (srcR.isEmpty() || dstR.isEmpty()) {
216 #ifdef TRACE_SKIPPED_BITMAPS
217 SkDebugf("----- skip bitmapimage: [%d %d] src-empty %d dst-empty %d\n",
218 bitmap.width(), bitmap.height(),
219 srcR.isEmpty(), dstR.isEmpty());
220 #endif
221 return;
222 }
223
224 SkCanvas* canvas = ctxt->platformContext()->mCanvas;
225 SkPaint paint;
226
227 ctxt->setupFillPaint(&paint); // need global alpha among other things
228 paint.setFilterBitmap(true);
229 paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp));
230 fixPaintForBitmapsThatMaySeam(&paint);
231 canvas->drawBitmapRect(bitmap, &srcR, dstR, &paint);
232
233 #ifdef TRACE_SUBSAMPLED_BITMAPS
234 if (bitmap.width() != image->origWidth() ||
235 bitmap.height() != image->origHeight()) {
236 SkDebugf("--- BitmapImage::draw [%d %d] orig [%d %d]\n",
237 bitmap.width(), bitmap.height(),
238 image->origWidth(), image->origHeight());
239 }
240 #endif
241 }
242
setURL(const String & str)243 void BitmapImage::setURL(const String& str)
244 {
245 m_source.setURL(str);
246 }
247
248 ///////////////////////////////////////////////////////////////////////////////
249
drawPattern(GraphicsContext * ctxt,const FloatRect & srcRect,const TransformationMatrix & patternTransform,const FloatPoint & phase,CompositeOperator compositeOp,const FloatRect & destRect)250 void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& srcRect,
251 const TransformationMatrix& patternTransform,
252 const FloatPoint& phase, CompositeOperator compositeOp,
253 const FloatRect& destRect)
254 {
255 SkBitmapRef* image = this->nativeImageForCurrentFrame();
256 if (!image) { // If it's too early we won't have an image yet.
257 return;
258 }
259
260 // in case we get called with an incomplete bitmap
261 const SkBitmap& origBitmap = image->bitmap();
262 if (origBitmap.getPixels() == NULL && origBitmap.pixelRef() == NULL) {
263 return;
264 }
265
266 SkRect dstR(destRect);
267 if (dstR.isEmpty()) {
268 return;
269 }
270
271 SkIRect srcR;
272 // we may have to scale if the image has been subsampled (so save RAM)
273 bool imageIsSubSampled = image->origWidth() != origBitmap.width() ||
274 image->origHeight() != origBitmap.height();
275 float scaleX = 1;
276 float scaleY = 1;
277 if (imageIsSubSampled) {
278 scaleX = (float)image->origWidth() / origBitmap.width();
279 scaleY = (float)image->origHeight() / origBitmap.height();
280 // SkDebugf("----- subsampled %g %g\n", scaleX, scaleY);
281 round_scaled(&srcR, srcRect, 1 / scaleX, 1 / scaleY);
282 } else {
283 round(&srcR, srcRect);
284 }
285
286 // now extract the proper subset of the src image
287 SkBitmap bitmap;
288 if (!origBitmap.extractSubset(&bitmap, srcR)) {
289 SkDebugf("--- Image::drawPattern calling extractSubset failed\n");
290 return;
291 }
292
293 SkCanvas* canvas = ctxt->platformContext()->mCanvas;
294 SkPaint paint;
295 ctxt->setupFillPaint(&paint); // need global alpha among other things
296
297 SkShader* shader = SkShader::CreateBitmapShader(bitmap,
298 SkShader::kRepeat_TileMode,
299 SkShader::kRepeat_TileMode);
300 paint.setShader(shader)->unref();
301 // now paint is the only owner of shader
302 paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp));
303 paint.setFilterBitmap(true);
304 fixPaintForBitmapsThatMaySeam(&paint);
305
306 SkMatrix matrix(patternTransform);
307
308 if (imageIsSubSampled) {
309 matrix.preScale(SkFloatToScalar(scaleX), SkFloatToScalar(scaleY));
310 }
311 // We also need to translate it such that the origin of the pattern is the
312 // origin of the destination rect, which is what WebKit expects. Skia uses
313 // the coordinate system origin as the base for the patter. If WebKit wants
314 // a shifted image, it will shift it from there using the patternTransform.
315 float tx = phase.x() + srcRect.x() * patternTransform.a();
316 float ty = phase.y() + srcRect.y() * patternTransform.d();
317 matrix.postTranslate(SkFloatToScalar(tx), SkFloatToScalar(ty));
318 shader->setLocalMatrix(matrix);
319 #if 0
320 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",
321 srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height(),
322 destRect.x(), destRect.y(), destRect.width(), destRect.height(),
323 patternTransform.a(), patternTransform.b(), patternTransform.c(),
324 patternTransform.d(), patternTransform.e(), patternTransform.f(),
325 matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
326 #endif
327 canvas->drawRect(dstR, paint);
328
329 #ifdef TRACE_SUBSAMPLED_BITMAPS
330 if (bitmap.width() != image->origWidth() ||
331 bitmap.height() != image->origHeight()) {
332 SkDebugf("--- Image::drawPattern [%d %d] orig [%d %d] dst [%g %g]\n",
333 bitmap.width(), bitmap.height(),
334 image->origWidth(), image->origHeight(),
335 SkScalarToFloat(dstR.width()), SkScalarToFloat(dstR.height()));
336 }
337 #endif
338 }
339
340 // missingImage, textAreaResizeCorner
loadPlatformResource(const char * name)341 PassRefPtr<Image> Image::loadPlatformResource(const char *name)
342 {
343 android::AssetManager* am = globalAssetManager();
344
345 SkString path("webkit/");
346 path.append(name);
347 path.append(".png");
348
349 android::Asset* a = am->open(path.c_str(),
350 android::Asset::ACCESS_BUFFER);
351 if (a == NULL) {
352 SkDebugf("---------------- failed to open image asset %s\n", name);
353 return NULL;
354 }
355
356 SkAutoTDelete<android::Asset> ad(a);
357
358 SkBitmap bm;
359 if (SkImageDecoder::DecodeMemory(a->getBuffer(false), a->getLength(), &bm)) {
360 SkBitmapRef* ref = new SkBitmapRef(bm);
361 // create will call ref(), so we need aur() to release ours upon return
362 SkAutoUnref aur(ref);
363 return BitmapImage::create(ref, 0);
364 }
365 return Image::nullImage();
366 }
367
368 } // namespace
369
370