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