Home | History | Annotate | Download | only in cairo
      1 /*
      2  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.  All rights reserved.
      3  * Copyright (C) 2007 Alp Toker <alp (at) atoker.com>
      4  * Copyright (C) 2009 Dirk Schulze <krit (at) webkit.org>
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include "config.h"
     29 #include "BitmapImage.h"
     30 
     31 #if PLATFORM(CAIRO)
     32 
     33 #include "AffineTransform.h"
     34 #include "Color.h"
     35 #include "FloatRect.h"
     36 #include "GraphicsContext.h"
     37 #include "ImageBuffer.h"
     38 #include "ImageObserver.h"
     39 #include <cairo.h>
     40 #include <math.h>
     41 #include <wtf/OwnPtr.h>
     42 
     43 namespace WebCore {
     44 
     45 bool FrameData::clear(bool clearMetadata)
     46 {
     47     if (clearMetadata)
     48         m_haveMetadata = false;
     49 
     50     if (m_frame) {
     51         cairo_surface_destroy(m_frame);
     52         m_frame = 0;
     53         return true;
     54     }
     55     return false;
     56 }
     57 
     58 BitmapImage::BitmapImage(cairo_surface_t* surface, ImageObserver* observer)
     59     : Image(observer)
     60     , m_currentFrame(0)
     61     , m_frames(0)
     62     , m_frameTimer(0)
     63     , m_repetitionCount(cAnimationNone)
     64     , m_repetitionCountStatus(Unknown)
     65     , m_repetitionsComplete(0)
     66     , m_isSolidColor(false)
     67     , m_checkedForSolidColor(false)
     68     , m_animationFinished(true)
     69     , m_allDataReceived(true)
     70     , m_haveSize(true)
     71     , m_sizeAvailable(true)
     72     , m_decodedSize(0)
     73     , m_haveFrameCount(true)
     74     , m_frameCount(1)
     75 {
     76     initPlatformData();
     77 
     78     // TODO: check to be sure this is an image surface
     79 
     80     int width = cairo_image_surface_get_width(surface);
     81     int height = cairo_image_surface_get_height(surface);
     82     m_decodedSize = width * height * 4;
     83     m_size = IntSize(width, height);
     84 
     85     m_frames.grow(1);
     86     m_frames[0].m_frame = surface;
     87     m_frames[0].m_hasAlpha = cairo_surface_get_content(surface) != CAIRO_CONTENT_COLOR;
     88     m_frames[0].m_haveMetadata = true;
     89     checkForSolidColor();
     90 }
     91 
     92 void BitmapImage::draw(GraphicsContext* context, const FloatRect& dst, const FloatRect& src, ColorSpace styleColorSpace, CompositeOperator op)
     93 {
     94     FloatRect srcRect(src);
     95     FloatRect dstRect(dst);
     96 
     97     if (dstRect.width() == 0.0f || dstRect.height() == 0.0f ||
     98         srcRect.width() == 0.0f || srcRect.height() == 0.0f)
     99         return;
    100 
    101     startAnimation();
    102 
    103     cairo_surface_t* image = frameAtIndex(m_currentFrame);
    104     if (!image) // If it's too early we won't have an image yet.
    105         return;
    106 
    107     if (mayFillWithSolidColor()) {
    108         fillWithSolidColor(context, dstRect, solidColor(), styleColorSpace, op);
    109         return;
    110     }
    111 
    112     IntSize selfSize = size();
    113 
    114     cairo_t* cr = context->platformContext();
    115     context->save();
    116 
    117     // Set the compositing operation.
    118     if (op == CompositeSourceOver && !frameHasAlphaAtIndex(m_currentFrame))
    119         context->setCompositeOperation(CompositeCopy);
    120     else
    121         context->setCompositeOperation(op);
    122 
    123     // If we're drawing a sub portion of the image or scaling then create
    124     // a pattern transformation on the image and draw the transformed pattern.
    125     // Test using example site at http://www.meyerweb.com/eric/css/edge/complexspiral/demo.html
    126     cairo_pattern_t* pattern = cairo_pattern_create_for_surface(image);
    127 
    128     cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
    129 
    130     float scaleX = srcRect.width() / dstRect.width();
    131     float scaleY = srcRect.height() / dstRect.height();
    132     cairo_matrix_t matrix = { scaleX, 0, 0, scaleY, srcRect.x(), srcRect.y() };
    133     cairo_pattern_set_matrix(pattern, &matrix);
    134 
    135     // Draw the shadow
    136 #if ENABLE(FILTERS)
    137     IntSize shadowSize;
    138     int shadowBlur;
    139     Color shadowColor;
    140     if (context->getShadow(shadowSize, shadowBlur, shadowColor)) {
    141         IntSize shadowBufferSize;
    142         FloatRect shadowRect;
    143         float kernelSize (0.0);
    144         GraphicsContext::calculateShadowBufferDimensions(shadowBufferSize, shadowRect, kernelSize, dstRect, shadowSize, shadowBlur);
    145         shadowColor = colorWithOverrideAlpha(shadowColor.rgb(), (shadowColor.alpha() *  context->getAlpha()) / 255.f);
    146 
    147         //draw shadow into a new ImageBuffer
    148         OwnPtr<ImageBuffer> shadowBuffer = ImageBuffer::create(shadowBufferSize);
    149         cairo_t* shadowContext = shadowBuffer->context()->platformContext();
    150         cairo_set_source(shadowContext, pattern);
    151         cairo_translate(shadowContext, -dstRect.x(), -dstRect.y());
    152         cairo_rectangle(shadowContext, 0, 0, dstRect.width(), dstRect.height());
    153         cairo_fill(shadowContext);
    154 
    155         context->createPlatformShadow(shadowBuffer.release(), shadowColor, shadowRect, kernelSize);
    156     }
    157 #endif
    158 
    159     // Draw the image.
    160     cairo_translate(cr, dstRect.x(), dstRect.y());
    161     cairo_set_source(cr, pattern);
    162     cairo_pattern_destroy(pattern);
    163     cairo_rectangle(cr, 0, 0, dstRect.width(), dstRect.height());
    164     cairo_clip(cr);
    165     cairo_paint_with_alpha(cr, context->getAlpha());
    166 
    167     context->restore();
    168 
    169     if (imageObserver())
    170         imageObserver()->didDraw(this);
    171 }
    172 
    173 void Image::drawPattern(GraphicsContext* context, const FloatRect& tileRect, const AffineTransform& patternTransform,
    174                         const FloatPoint& phase, ColorSpace, CompositeOperator op, const FloatRect& destRect)
    175 {
    176     cairo_surface_t* image = nativeImageForCurrentFrame();
    177     if (!image) // If it's too early we won't have an image yet.
    178         return;
    179 
    180     // Avoid NaN
    181     if (!isfinite(phase.x()) || !isfinite(phase.y()))
    182        return;
    183 
    184     cairo_t* cr = context->platformContext();
    185     context->save();
    186 
    187     IntRect imageSize = enclosingIntRect(tileRect);
    188     OwnPtr<ImageBuffer> imageSurface = ImageBuffer::create(imageSize.size());
    189 
    190     if (!imageSurface)
    191         return;
    192 
    193     if (tileRect.size() != size()) {
    194         cairo_t* clippedImageContext = imageSurface->context()->platformContext();
    195         cairo_set_source_surface(clippedImageContext, image, -tileRect.x(), -tileRect.y());
    196         cairo_paint(clippedImageContext);
    197         image = imageSurface->image()->nativeImageForCurrentFrame();
    198     }
    199 
    200     cairo_pattern_t* pattern = cairo_pattern_create_for_surface(image);
    201     cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
    202 
    203     cairo_matrix_t pattern_matrix = cairo_matrix_t(patternTransform);
    204     cairo_matrix_t phase_matrix = {1, 0, 0, 1, phase.x() + tileRect.x() * patternTransform.a(), phase.y() + tileRect.y() * patternTransform.d()};
    205     cairo_matrix_t combined;
    206     cairo_matrix_multiply(&combined, &pattern_matrix, &phase_matrix);
    207     cairo_matrix_invert(&combined);
    208     cairo_pattern_set_matrix(pattern, &combined);
    209 
    210     context->setCompositeOperation(op);
    211     cairo_set_source(cr, pattern);
    212     cairo_pattern_destroy(pattern);
    213     cairo_rectangle(cr, destRect.x(), destRect.y(), destRect.width(), destRect.height());
    214     cairo_fill(cr);
    215 
    216     context->restore();
    217 
    218     if (imageObserver())
    219         imageObserver()->didDraw(this);
    220 }
    221 
    222 void BitmapImage::checkForSolidColor()
    223 {
    224     m_isSolidColor = false;
    225     m_checkedForSolidColor = true;
    226 
    227     if (frameCount() > 1)
    228         return;
    229 
    230     cairo_surface_t* frameSurface = frameAtIndex(0);
    231     if (!frameSurface)
    232         return;
    233 
    234     ASSERT(cairo_surface_get_type(frameSurface) == CAIRO_SURFACE_TYPE_IMAGE);
    235 
    236     int width = cairo_image_surface_get_width(frameSurface);
    237     int height = cairo_image_surface_get_height(frameSurface);
    238 
    239     if (width != 1 || height != 1)
    240         return;
    241 
    242     unsigned* pixelColor = reinterpret_cast<unsigned*>(cairo_image_surface_get_data(frameSurface));
    243     m_solidColor = colorFromPremultipliedARGB(*pixelColor);
    244 
    245     m_isSolidColor = true;
    246 }
    247 
    248 }
    249 
    250 #endif // PLATFORM(CAIRO)
    251