Home | History | Annotate | Download | only in cg
      1 /*
      2  * Copyright (C) 2004, 2005, 2006, 2008 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 COMPUTER, 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 COMPUTER, 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 "ImageSource.h"
     28 
     29 #if PLATFORM(CG)
     30 #include "ImageSourceCG.h"
     31 
     32 #include "IntSize.h"
     33 #include "MIMETypeRegistry.h"
     34 #include "SharedBuffer.h"
     35 #include <ApplicationServices/ApplicationServices.h>
     36 #include <wtf/UnusedParam.h>
     37 
     38 using namespace std;
     39 
     40 namespace WebCore {
     41 
     42 static const CFStringRef kCGImageSourceShouldPreferRGB32 = CFSTR("kCGImageSourceShouldPreferRGB32");
     43 
     44 #if !PLATFORM(MAC)
     45 size_t sharedBufferGetBytesAtPosition(void* info, void* buffer, off_t position, size_t count)
     46 {
     47     SharedBuffer* sharedBuffer = static_cast<SharedBuffer*>(info);
     48     size_t sourceSize = sharedBuffer->size();
     49     if (position >= sourceSize)
     50         return 0;
     51 
     52     const char* source = sharedBuffer->data() + position;
     53     size_t amount = min<size_t>(count, sourceSize - position);
     54     memcpy(buffer, source, amount);
     55     return amount;
     56 }
     57 
     58 void sharedBufferRelease(void* info)
     59 {
     60     SharedBuffer* sharedBuffer = static_cast<SharedBuffer*>(info);
     61     sharedBuffer->deref();
     62 }
     63 #endif
     64 
     65 ImageSource::ImageSource()
     66     : m_decoder(0)
     67 {
     68 }
     69 
     70 ImageSource::~ImageSource()
     71 {
     72     clear(true);
     73 }
     74 
     75 void ImageSource::clear(bool destroyAllFrames, size_t, SharedBuffer* data, bool allDataReceived)
     76 {
     77 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
     78     // Recent versions of ImageIO discard previously decoded image frames if the client
     79     // application no longer holds references to them, so there's no need to throw away
     80     // the decoder unless we're explicitly asked to destroy all of the frames.
     81 
     82     if (!destroyAllFrames)
     83         return;
     84 #else
     85     // Older versions of ImageIO hold references to previously decoded image frames.
     86     // There is no API to selectively release some of the frames it is holding, and
     87     // if we don't release the frames we use too much memory on large images.
     88     // Destroying the decoder is the only way to release previous frames.
     89 
     90     UNUSED_PARAM(destroyAllFrames);
     91 #endif
     92 
     93     if (m_decoder) {
     94         CFRelease(m_decoder);
     95         m_decoder = 0;
     96     }
     97     if (data)
     98         setData(data, allDataReceived);
     99 }
    100 
    101 static CFDictionaryRef imageSourceOptions()
    102 {
    103     static CFDictionaryRef options;
    104 
    105     if (!options) {
    106         const unsigned numOptions = 2;
    107         const void* keys[numOptions] = { kCGImageSourceShouldCache, kCGImageSourceShouldPreferRGB32 };
    108         const void* values[numOptions] = { kCFBooleanTrue, kCFBooleanTrue };
    109         options = CFDictionaryCreate(NULL, keys, values, numOptions,
    110             &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    111     }
    112     return options;
    113 }
    114 
    115 bool ImageSource::initialized() const
    116 {
    117     return m_decoder;
    118 }
    119 
    120 void ImageSource::setData(SharedBuffer* data, bool allDataReceived)
    121 {
    122     if (!m_decoder)
    123         m_decoder = CGImageSourceCreateIncremental(NULL);
    124 #if PLATFORM(MAC)
    125     // On Mac the NSData inside the SharedBuffer can be secretly appended to without the SharedBuffer's knowledge.  We use SharedBuffer's ability
    126     // to wrap itself inside CFData to get around this, ensuring that ImageIO is really looking at the SharedBuffer.
    127     RetainPtr<CFDataRef> cfData(AdoptCF, data->createCFData());
    128     CGImageSourceUpdateData(m_decoder, cfData.get(), allDataReceived);
    129 #else
    130     // Create a CGDataProvider to wrap the SharedBuffer.
    131     data->ref();
    132     // We use the GetBytesAtPosition callback rather than the GetBytePointer one because SharedBuffer
    133     // does not provide a way to lock down the byte pointer and guarantee that it won't move, which
    134     // is a requirement for using the GetBytePointer callback.
    135     CGDataProviderDirectCallbacks providerCallbacks = { 0, 0, 0, sharedBufferGetBytesAtPosition, sharedBufferRelease };
    136     RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateDirect(data, data->size(), &providerCallbacks));
    137     CGImageSourceUpdateDataProvider(m_decoder, dataProvider.get(), allDataReceived);
    138 #endif
    139 }
    140 
    141 String ImageSource::filenameExtension() const
    142 {
    143     if (!m_decoder)
    144         return String();
    145     CFStringRef imageSourceType = CGImageSourceGetType(m_decoder);
    146     return WebCore::preferredExtensionForImageSourceType(imageSourceType);
    147 }
    148 
    149 bool ImageSource::isSizeAvailable()
    150 {
    151     bool result = false;
    152     CGImageSourceStatus imageSourceStatus = CGImageSourceGetStatus(m_decoder);
    153 
    154     // Ragnaros yells: TOO SOON! You have awakened me TOO SOON, Executus!
    155     if (imageSourceStatus >= kCGImageStatusIncomplete) {
    156         RetainPtr<CFDictionaryRef> image0Properties(AdoptCF, CGImageSourceCopyPropertiesAtIndex(m_decoder, 0, imageSourceOptions()));
    157         if (image0Properties) {
    158             CFNumberRef widthNumber = (CFNumberRef)CFDictionaryGetValue(image0Properties.get(), kCGImagePropertyPixelWidth);
    159             CFNumberRef heightNumber = (CFNumberRef)CFDictionaryGetValue(image0Properties.get(), kCGImagePropertyPixelHeight);
    160             result = widthNumber && heightNumber;
    161         }
    162     }
    163 
    164     return result;
    165 }
    166 
    167 IntSize ImageSource::frameSizeAtIndex(size_t index) const
    168 {
    169     IntSize result;
    170     RetainPtr<CFDictionaryRef> properties(AdoptCF, CGImageSourceCopyPropertiesAtIndex(m_decoder, index, imageSourceOptions()));
    171     if (properties) {
    172         int w = 0, h = 0;
    173         CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyPixelWidth);
    174         if (num)
    175             CFNumberGetValue(num, kCFNumberIntType, &w);
    176         num = (CFNumberRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyPixelHeight);
    177         if (num)
    178             CFNumberGetValue(num, kCFNumberIntType, &h);
    179         result = IntSize(w, h);
    180     }
    181     return result;
    182 }
    183 
    184 IntSize ImageSource::size() const
    185 {
    186     return frameSizeAtIndex(0);
    187 }
    188 
    189 int ImageSource::repetitionCount()
    190 {
    191     int result = cAnimationLoopOnce; // No property means loop once.
    192     if (!initialized())
    193         return result;
    194 
    195     // A property with value 0 means loop forever.
    196     RetainPtr<CFDictionaryRef> properties(AdoptCF, CGImageSourceCopyProperties(m_decoder, imageSourceOptions()));
    197     if (properties) {
    198         CFDictionaryRef gifProperties = (CFDictionaryRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyGIFDictionary);
    199         if (gifProperties) {
    200             CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFLoopCount);
    201             if (num)
    202                 CFNumberGetValue(num, kCFNumberIntType, &result);
    203         } else
    204             result = cAnimationNone; // Turns out we're not a GIF after all, so we don't animate.
    205     }
    206 
    207     return result;
    208 }
    209 
    210 size_t ImageSource::frameCount() const
    211 {
    212     return m_decoder ? CGImageSourceGetCount(m_decoder) : 0;
    213 }
    214 
    215 CGImageRef ImageSource::createFrameAtIndex(size_t index)
    216 {
    217     if (!initialized())
    218         return 0;
    219 
    220     RetainPtr<CGImageRef> image(AdoptCF, CGImageSourceCreateImageAtIndex(m_decoder, index, imageSourceOptions()));
    221     CFStringRef imageUTI = CGImageSourceGetType(m_decoder);
    222     static const CFStringRef xbmUTI = CFSTR("public.xbitmap-image");
    223     if (!imageUTI || !CFEqual(imageUTI, xbmUTI))
    224         return image.releaseRef();
    225 
    226     // If it is an xbm image, mask out all the white areas to render them transparent.
    227     const CGFloat maskingColors[6] = {255, 255,  255, 255, 255, 255};
    228     RetainPtr<CGImageRef> maskedImage(AdoptCF, CGImageCreateWithMaskingColors(image.get(), maskingColors));
    229     if (!maskedImage)
    230         return image.releaseRef();
    231 
    232     return maskedImage.releaseRef();
    233 }
    234 
    235 bool ImageSource::frameIsCompleteAtIndex(size_t index)
    236 {
    237     return CGImageSourceGetStatusAtIndex(m_decoder, index) == kCGImageStatusComplete;
    238 }
    239 
    240 float ImageSource::frameDurationAtIndex(size_t index)
    241 {
    242     if (!initialized())
    243         return 0;
    244 
    245     float duration = 0;
    246     RetainPtr<CFDictionaryRef> properties(AdoptCF, CGImageSourceCopyPropertiesAtIndex(m_decoder, index, imageSourceOptions()));
    247     if (properties) {
    248         CFDictionaryRef typeProperties = (CFDictionaryRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyGIFDictionary);
    249         if (typeProperties) {
    250             CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(typeProperties, kCGImagePropertyGIFDelayTime);
    251             if (num)
    252                 CFNumberGetValue(num, kCFNumberFloatType, &duration);
    253         }
    254     }
    255 
    256     // Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
    257     // We follow WinIE's behavior and use a duration of 100 ms for any frames that specify
    258     // a duration of <= 50 ms. See <http://bugs.webkit.org/show_bug.cgi?id=14413> or Radar 4051389 for more.
    259     if (duration < 0.051f)
    260         return 0.100f;
    261     return duration;
    262 }
    263 
    264 bool ImageSource::frameHasAlphaAtIndex(size_t)
    265 {
    266     if (!m_decoder)
    267         return false;
    268 
    269     CFStringRef imageType = CGImageSourceGetType(m_decoder);
    270 
    271     // Return false if there is no image type or the image type is JPEG, because
    272     // JPEG does not support alpha transparency.
    273     if (!imageType || CFEqual(imageType, CFSTR("public.jpeg")))
    274         return false;
    275 
    276     // FIXME: Could return false for other non-transparent image formats.
    277     // FIXME: Could maybe return false for a GIF Frame if we have enough info in the GIF properties dictionary
    278     // to determine whether or not a transparent color was defined.
    279     return true;
    280 }
    281 
    282 }
    283 
    284 #endif // PLATFORM(CG)
    285