1/* 2 * Copyright (C) 2004, 2005, 2006 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "BitmapImage.h" 28 29#if USE(CG) 30 31#include "FloatConversion.h" 32#include "GraphicsContextCG.h" 33#include "ImageObserver.h" 34#include "SubimageCacheWithTimer.h" 35#include <wtf/RetainPtr.h> 36 37#if USE(APPKIT) 38#include <ApplicationServices/ApplicationServices.h> 39#endif 40 41#if PLATFORM(IOS) 42#include <CoreGraphics/CGContextPrivate.h> 43#endif 44 45#if PLATFORM(COCOA) 46#include "WebCoreSystemInterface.h" 47#endif 48 49#if PLATFORM(WIN) 50#include <WebKitSystemInterface/WebKitSystemInterface.h> 51#endif 52 53namespace WebCore { 54 55bool FrameData::clear(bool clearMetadata) 56{ 57 if (clearMetadata) 58 m_haveMetadata = false; 59 60 m_orientation = DefaultImageOrientation; 61 62#if PLATFORM(IOS) 63 m_frameBytes = 0; 64 m_subsamplingScale = 1; 65 m_haveInfo = false; 66#endif 67 68 if (m_frame) { 69#if CACHE_SUBIMAGES 70 subimageCache().clearImage(m_frame); 71#endif 72 CGImageRelease(m_frame); 73 m_frame = 0; 74 return true; 75 } 76 return false; 77} 78 79BitmapImage::BitmapImage(CGImageRef cgImage, ImageObserver* observer) 80 : Image(observer) 81 , m_currentFrame(0) 82 , m_frames(0) 83 , m_repetitionCount(cAnimationNone) 84 , m_repetitionCountStatus(Unknown) 85 , m_repetitionsComplete(0) 86 , m_decodedSize(0) 87 , m_decodedPropertiesSize(0) 88 , m_frameCount(1) 89 , m_isSolidColor(false) 90 , m_checkedForSolidColor(false) 91 , m_animationFinished(true) 92 , m_allDataReceived(true) 93 , m_haveSize(true) 94 , m_sizeAvailable(true) 95 , m_haveFrameCount(true) 96{ 97 CGFloat width = CGImageGetWidth(cgImage); 98 CGFloat height = CGImageGetHeight(cgImage); 99 m_decodedSize = width * height * 4; 100 m_size = IntSize(width, height); 101 102 // Since we don't have a decoder, we can't figure out the image orientation. 103 // Set m_sizeRespectingOrientation to be the same as m_size so it's not 0x0. 104 m_sizeRespectingOrientation = m_size; 105 106#if PLATFORM(IOS) 107 m_originalSize = m_size; 108 m_originalSizeRespectingOrientation = m_size; 109#endif 110 111 m_frames.grow(1); 112 m_frames[0].m_frame = CGImageRetain(cgImage); 113 m_frames[0].m_hasAlpha = true; 114 m_frames[0].m_haveMetadata = true; 115 116#if PLATFORM(IOS) 117 m_frames[0].m_subsamplingScale = 1; 118#endif 119 120 checkForSolidColor(); 121} 122 123// Drawing Routines 124 125void BitmapImage::checkForSolidColor() 126{ 127 m_checkedForSolidColor = true; 128 if (frameCount() > 1) { 129 m_isSolidColor = false; 130 return; 131 } 132 133#if !PLATFORM(IOS) 134 CGImageRef image = frameAtIndex(0); 135#else 136 // Note, checkForSolidColor() may be called from frameAtIndex(). On iOS frameAtIndex() gets passed a scaleHint 137 // argument which it uses to tell CG to create a scaled down image. Since we don't know the scaleHint here, if 138 // we call frameAtIndex() again, we would pass it the default scale of 1 and would end up recreating the image. 139 // So we do a quick check and call frameAtIndex(0) only if we haven't yet created an image. 140 CGImageRef image = nullptr; 141 if (m_frames.size()) 142 image = m_frames[0].m_frame; 143 144 if (!image) 145 image = frameAtIndex(0); 146#endif 147 148 // Currently we only check for solid color in the important special case of a 1x1 image. 149 if (image && CGImageGetWidth(image) == 1 && CGImageGetHeight(image) == 1) { 150 unsigned char pixel[4]; // RGBA 151 RetainPtr<CGContextRef> bitmapContext = adoptCF(CGBitmapContextCreate(pixel, 1, 1, 8, sizeof(pixel), deviceRGBColorSpaceRef(), 152 kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big)); 153 if (!bitmapContext) 154 return; 155 GraphicsContext(bitmapContext.get()).setCompositeOperation(CompositeCopy); 156 CGRect destinationRect = CGRectMake(0, 0, 1, 1); 157 CGContextDrawImage(bitmapContext.get(), destinationRect, image); 158 if (!pixel[3]) 159 m_solidColor = Color(0, 0, 0, 0); 160 else 161 m_solidColor = Color(pixel[0] * 255 / pixel[3], pixel[1] * 255 / pixel[3], pixel[2] * 255 / pixel[3], pixel[3]); 162 163 m_isSolidColor = true; 164 } 165} 166 167CGImageRef BitmapImage::getCGImageRef() 168{ 169 return frameAtIndex(0); 170} 171 172CGImageRef BitmapImage::getFirstCGImageRefOfSize(const IntSize& size) 173{ 174 size_t count = frameCount(); 175 for (size_t i = 0; i < count; ++i) { 176 CGImageRef cgImage = frameAtIndex(i); 177 if (cgImage && IntSize(CGImageGetWidth(cgImage), CGImageGetHeight(cgImage)) == size) 178 return cgImage; 179 } 180 181 // Fallback to the default CGImageRef if we can't find the right size 182 return getCGImageRef(); 183} 184 185RetainPtr<CFArrayRef> BitmapImage::getCGImageArray() 186{ 187 size_t count = frameCount(); 188 if (!count) 189 return 0; 190 191 CFMutableArrayRef array = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks); 192 for (size_t i = 0; i < count; ++i) { 193 if (CGImageRef currFrame = frameAtIndex(i)) 194 CFArrayAppendValue(array, currFrame); 195 } 196 return adoptCF(array); 197} 198 199void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription description) 200{ 201 CGImageRef image; 202 FloatRect srcRectForCurrentFrame = srcRect; 203 204#if PLATFORM(IOS) 205 if (m_originalSize != m_size && !m_originalSize.isEmpty()) 206 srcRectForCurrentFrame.scale(m_size.width() / static_cast<float>(m_originalSize.width()), m_size.height() / static_cast<float>(m_originalSize.height())); 207 208 startAnimation(DoNotCatchUp); 209 210 CGRect transformedDestinationRect = CGRectApplyAffineTransform(destRect, CGContextGetCTM(ctxt->platformContext())); 211 RetainPtr<CGImageRef> imagePossiblyCopied; 212 // Never use subsampled images for drawing into PDF contexts. 213 if (CGContextGetType(ctxt->platformContext()) == kCGContextTypePDF) 214 imagePossiblyCopied = adoptCF(copyUnscaledFrameAtIndex(m_currentFrame)); 215 else 216 imagePossiblyCopied = frameAtIndex(m_currentFrame, std::min<float>(1.0f, std::max(transformedDestinationRect.size.width / srcRectForCurrentFrame.width(), transformedDestinationRect.size.height / srcRectForCurrentFrame.height()))); 217 218 image = imagePossiblyCopied.get(); 219#else 220 startAnimation(); 221 222 image = frameAtIndex(m_currentFrame); 223#endif 224 225 if (!image) // If it's too early we won't have an image yet. 226 return; 227 228 if (mayFillWithSolidColor()) { 229 fillWithSolidColor(ctxt, destRect, solidColor(), styleColorSpace, compositeOp); 230 return; 231 } 232 233 float scale = 1; 234#if PLATFORM(IOS) 235 scale = m_frames[m_currentFrame].m_subsamplingScale; 236#endif 237 FloatSize selfSize = currentFrameSize(); 238 ImageOrientation orientation; 239 240 if (description.respectImageOrientation() == RespectImageOrientation) 241 orientation = frameOrientationAtIndex(m_currentFrame); 242 243 ctxt->drawNativeImage(image, selfSize, styleColorSpace, destRect, srcRectForCurrentFrame, scale, compositeOp, blendMode, orientation); 244 245 if (imageObserver()) 246 imageObserver()->didDraw(this); 247} 248 249#if PLATFORM(IOS) 250PassNativeImagePtr BitmapImage::copyUnscaledFrameAtIndex(size_t index) 251{ 252 if (index >= frameCount()) 253 return nullptr; 254 255 if (index >= m_frames.size() || !m_frames[index].m_frame) 256 cacheFrame(index, 1); 257 258 if (m_frames[index].m_subsamplingScale == 1 && !m_source.isSubsampled()) 259 return CGImageRetain(m_frames[index].m_frame); 260 261 return m_source.createFrameAtIndex(index); 262} 263#endif 264 265} 266 267#endif // USE(CG) 268