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