b0427650f3c10c8699a1b9fa108aa1d8d6efb992
[WebKit-https.git] / Source / WebCore / platform / graphics / cg / BitmapImageCG.cpp
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
53 namespace WebCore {
54
55 bool 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
79 BitmapImage::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
125 void 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
167 CGImageRef BitmapImage::getCGImageRef()
168 {
169     return frameAtIndex(0);
170 }
171
172 CGImageRef 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
185 RetainPtr<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
199 void 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)
250 PassNativeImagePtr 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)