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 51 android::AssetManager* globalAssetManager() { 52 static android::AssetManager* gGlobalAssetMgr; 53 if (!gGlobalAssetMgr) { 54 gGlobalAssetMgr = new android::AssetManager(); 55 gGlobalAssetMgr->addDefaultAssets(); 56 } 57 return gGlobalAssetMgr; 58 } 59 60 namespace WebCore { 61 62 bool 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 75 BitmapImage::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 104 void BitmapImage::initPlatformData() 105 { 106 m_source.clearURL(); 107 } 108 109 void BitmapImage::invalidatePlatformData() 110 { 111 } 112 113 void 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 157 static 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 165 static 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 174 void 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 223 void BitmapImage::setURL(const String& str) 224 { 225 m_source.setURL(str); 226 } 227 228 /////////////////////////////////////////////////////////////////////////////// 229 230 void 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 281 PassRefPtr<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