Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2006 Eric Seidel <eric (at) webkit.org>
      3  * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
      4  * Copyright (C) Research In Motion Limited 2011. All rights reserved.
      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 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 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 
     30 #include "core/svg/graphics/SVGImage.h"
     31 
     32 #include "core/loader/DocumentLoader.h"
     33 #include "core/page/FrameView.h"
     34 #include "core/page/Settings.h"
     35 #include "core/platform/graphics/GraphicsContextStateSaver.h"
     36 #include "core/platform/graphics/ImageBuffer.h"
     37 #include "core/platform/graphics/ImageObserver.h"
     38 #include "core/platform/graphics/IntRect.h"
     39 #include "core/rendering/style/RenderStyle.h"
     40 #include "core/rendering/svg/RenderSVGRoot.h"
     41 #include "core/svg/SVGDocument.h"
     42 #include "core/svg/SVGSVGElement.h"
     43 #include "core/svg/graphics/SVGImageChromeClient.h"
     44 #include "wtf/PassRefPtr.h"
     45 
     46 namespace WebCore {
     47 
     48 SVGImage::SVGImage(ImageObserver* observer)
     49     : Image(observer)
     50 {
     51 }
     52 
     53 SVGImage::~SVGImage()
     54 {
     55     if (m_page) {
     56         // Store m_page in a local variable, clearing m_page, so that SVGImageChromeClient knows we're destructed.
     57         OwnPtr<Page> currentPage = m_page.release();
     58         currentPage->mainFrame()->loader()->frameDetached(); // Break both the loader and view references to the frame
     59     }
     60 
     61     // Verify that page teardown destroyed the Chrome
     62     ASSERT(!m_chromeClient || !m_chromeClient->image());
     63 }
     64 
     65 void SVGImage::setContainerSize(const IntSize& size)
     66 {
     67     if (!m_page || !usesContainerSize())
     68         return;
     69 
     70     Frame* frame = m_page->mainFrame();
     71     SVGSVGElement* rootElement = toSVGDocument(frame->document())->rootElement();
     72     if (!rootElement)
     73         return;
     74     RenderSVGRoot* renderer = toRenderSVGRoot(rootElement->renderer());
     75     if (!renderer)
     76         return;
     77 
     78     FrameView* view = frameView();
     79     view->resize(this->containerSize());
     80 
     81     renderer->setContainerSize(size);
     82 }
     83 
     84 IntSize SVGImage::containerSize() const
     85 {
     86     if (!m_page)
     87         return IntSize();
     88     Frame* frame = m_page->mainFrame();
     89     SVGSVGElement* rootElement = toSVGDocument(frame->document())->rootElement();
     90     if (!rootElement)
     91         return IntSize();
     92 
     93     RenderSVGRoot* renderer = toRenderSVGRoot(rootElement->renderer());
     94     if (!renderer)
     95         return IntSize();
     96 
     97     // If a container size is available it has precedence.
     98     IntSize containerSize = renderer->containerSize();
     99     if (!containerSize.isEmpty())
    100         return containerSize;
    101 
    102     // Assure that a container size is always given for a non-identity zoom level.
    103     ASSERT(renderer->style()->effectiveZoom() == 1);
    104 
    105     FloatSize currentSize;
    106     if (rootElement->intrinsicWidth().isFixed() && rootElement->intrinsicHeight().isFixed())
    107         currentSize = rootElement->currentViewportSize();
    108     else
    109         currentSize = rootElement->currentViewBoxRect().size();
    110 
    111     if (!currentSize.isEmpty())
    112         return IntSize(static_cast<int>(ceilf(currentSize.width())), static_cast<int>(ceilf(currentSize.height())));
    113 
    114     // As last resort, use CSS default intrinsic size.
    115     return IntSize(300, 150);
    116 }
    117 
    118 void SVGImage::drawForContainer(GraphicsContext* context, const FloatSize containerSize, float zoom, const FloatRect& dstRect,
    119     const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode)
    120 {
    121     if (!m_page)
    122         return;
    123 
    124     ImageObserver* observer = imageObserver();
    125     ASSERT(observer);
    126 
    127     // Temporarily reset image observer, we don't want to receive any changeInRect() calls due to this relayout.
    128     setImageObserver(0);
    129 
    130     IntSize roundedContainerSize = roundedIntSize(containerSize);
    131     setContainerSize(roundedContainerSize);
    132 
    133     FloatRect scaledSrc = srcRect;
    134     scaledSrc.scale(1 / zoom);
    135 
    136     // Compensate for the container size rounding by adjusting the source rect.
    137     FloatSize adjustedSrcSize = scaledSrc.size();
    138     adjustedSrcSize.scale(roundedContainerSize.width() / containerSize.width(), roundedContainerSize.height() / containerSize.height());
    139     scaledSrc.setSize(adjustedSrcSize);
    140 
    141     draw(context, dstRect, scaledSrc, compositeOp, blendMode);
    142 
    143     setImageObserver(observer);
    144 }
    145 
    146 PassRefPtr<NativeImageSkia> SVGImage::nativeImageForCurrentFrame()
    147 {
    148     if (!m_page)
    149         return 0;
    150 
    151     OwnPtr<ImageBuffer> buffer = ImageBuffer::create(size(), 1);
    152     if (!buffer) // failed to allocate image
    153         return 0;
    154 
    155     drawForContainer(buffer->context(), size(), 1, rect(), rect(), CompositeSourceOver, BlendModeNormal);
    156 
    157     // FIXME: WK(Bug 113657): We should use DontCopyBackingStore here.
    158     return buffer->copyImage(CopyBackingStore)->nativeImageForCurrentFrame();
    159 }
    160 
    161 void SVGImage::drawPatternForContainer(GraphicsContext* context, const FloatSize containerSize, float zoom, const FloatRect& srcRect,
    162     const FloatSize& scale, const FloatPoint& phase, CompositeOperator compositeOp, const FloatRect& dstRect, BlendMode blendMode)
    163 {
    164     FloatRect zoomedContainerRect = FloatRect(FloatPoint(), containerSize);
    165     zoomedContainerRect.scale(zoom);
    166 
    167     // The ImageBuffer size needs to be scaled to match the final resolution.
    168     // FIXME: No need to get the full CTM here, we just need the scale.
    169     AffineTransform transform = context->getCTM();
    170     FloatSize imageBufferScale = FloatSize(transform.xScale(), transform.yScale());
    171     ASSERT(imageBufferScale.width());
    172     ASSERT(imageBufferScale.height());
    173 
    174     FloatSize scaleWithoutCTM(scale.width() / imageBufferScale.width(), scale.height() / imageBufferScale.height());
    175 
    176     FloatRect imageBufferSize = zoomedContainerRect;
    177     imageBufferSize.scale(imageBufferScale.width(), imageBufferScale.height());
    178 
    179     OwnPtr<ImageBuffer> buffer = ImageBuffer::create(expandedIntSize(imageBufferSize.size()), 1);
    180     if (!buffer) // Failed to allocate buffer.
    181         return;
    182     drawForContainer(buffer->context(), containerSize, zoom, imageBufferSize, zoomedContainerRect, CompositeSourceOver, BlendModeNormal);
    183     RefPtr<Image> image = buffer->copyImage(DontCopyBackingStore, Unscaled);
    184 
    185     // Adjust the source rect and transform due to the image buffer's scaling.
    186     FloatRect scaledSrcRect = srcRect;
    187     scaledSrcRect.scale(imageBufferScale.width(), imageBufferScale.height());
    188 
    189     image->drawPattern(context, scaledSrcRect, scaleWithoutCTM, phase, compositeOp, dstRect, blendMode);
    190 }
    191 
    192 void SVGImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode)
    193 {
    194     if (!m_page)
    195         return;
    196 
    197     FrameView* view = frameView();
    198 
    199     GraphicsContextStateSaver stateSaver(*context);
    200     context->setCompositeOperation(compositeOp, blendMode);
    201     context->clip(enclosingIntRect(dstRect));
    202     if (compositeOp != CompositeSourceOver)
    203         context->beginTransparencyLayer(1);
    204 
    205     FloatSize scale(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height());
    206 
    207     // We can only draw the entire frame, clipped to the rect we want. So compute where the top left
    208     // of the image would be if we were drawing without clipping, and translate accordingly.
    209     FloatSize topLeftOffset(srcRect.location().x() * scale.width(), srcRect.location().y() * scale.height());
    210     FloatPoint destOffset = dstRect.location() - topLeftOffset;
    211 
    212     context->translate(destOffset.x(), destOffset.y());
    213     context->scale(scale);
    214 
    215     view->resize(containerSize());
    216 
    217     if (view->needsLayout())
    218         view->layout();
    219 
    220     view->paint(context, enclosingIntRect(srcRect));
    221 
    222     if (compositeOp != CompositeSourceOver)
    223         context->endTransparencyLayer();
    224 
    225     stateSaver.restore();
    226 
    227     if (imageObserver())
    228         imageObserver()->didDraw(this);
    229 }
    230 
    231 RenderBox* SVGImage::embeddedContentBox() const
    232 {
    233     if (!m_page)
    234         return 0;
    235     Frame* frame = m_page->mainFrame();
    236     SVGSVGElement* rootElement = toSVGDocument(frame->document())->rootElement();
    237     if (!rootElement)
    238         return 0;
    239     return toRenderBox(rootElement->renderer());
    240 }
    241 
    242 FrameView* SVGImage::frameView() const
    243 {
    244     if (!m_page)
    245         return 0;
    246 
    247     return m_page->mainFrame()->view();
    248 }
    249 
    250 bool SVGImage::hasRelativeWidth() const
    251 {
    252     if (!m_page)
    253         return false;
    254     Frame* frame = m_page->mainFrame();
    255     SVGSVGElement* rootElement = toSVGDocument(frame->document())->rootElement();
    256     if (!rootElement)
    257         return false;
    258     return rootElement->intrinsicWidth().isPercent();
    259 }
    260 
    261 bool SVGImage::hasRelativeHeight() const
    262 {
    263     if (!m_page)
    264         return false;
    265     Frame* frame = m_page->mainFrame();
    266     SVGSVGElement* rootElement = toSVGDocument(frame->document())->rootElement();
    267     if (!rootElement)
    268         return false;
    269     return rootElement->intrinsicHeight().isPercent();
    270 }
    271 
    272 void SVGImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
    273 {
    274     if (!m_page)
    275         return;
    276     Frame* frame = m_page->mainFrame();
    277     SVGSVGElement* rootElement = toSVGDocument(frame->document())->rootElement();
    278     if (!rootElement)
    279         return;
    280 
    281     intrinsicWidth = rootElement->intrinsicWidth();
    282     intrinsicHeight = rootElement->intrinsicHeight();
    283     if (rootElement->preserveAspectRatioCurrentValue().align() == SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE)
    284         return;
    285 
    286     intrinsicRatio = rootElement->viewBoxCurrentValue().size();
    287     if (intrinsicRatio.isEmpty() && intrinsicWidth.isFixed() && intrinsicHeight.isFixed())
    288         intrinsicRatio = FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0));
    289 }
    290 
    291 // FIXME: support catchUpIfNecessary.
    292 void SVGImage::startAnimation(bool /* catchUpIfNecessary */)
    293 {
    294     if (!m_page)
    295         return;
    296     Frame* frame = m_page->mainFrame();
    297     SVGSVGElement* rootElement = toSVGDocument(frame->document())->rootElement();
    298     if (!rootElement)
    299         return;
    300     rootElement->unpauseAnimations();
    301     rootElement->setCurrentTime(0);
    302 }
    303 
    304 void SVGImage::stopAnimation()
    305 {
    306     if (!m_page)
    307         return;
    308     Frame* frame = m_page->mainFrame();
    309     SVGSVGElement* rootElement = toSVGDocument(frame->document())->rootElement();
    310     if (!rootElement)
    311         return;
    312     rootElement->pauseAnimations();
    313 }
    314 
    315 void SVGImage::resetAnimation()
    316 {
    317     stopAnimation();
    318 }
    319 
    320 bool SVGImage::dataChanged(bool allDataReceived)
    321 {
    322     // Don't do anything if is an empty image.
    323     if (!data()->size())
    324         return true;
    325 
    326     if (allDataReceived) {
    327         static FrameLoaderClient* dummyFrameLoaderClient =  new EmptyFrameLoaderClient;
    328 
    329         Page::PageClients pageClients;
    330         fillWithEmptyClients(pageClients);
    331         m_chromeClient = adoptPtr(new SVGImageChromeClient(this));
    332         pageClients.chromeClient = m_chromeClient.get();
    333 
    334         // FIXME: If this SVG ends up loading itself, we might leak the world.
    335         // The Cache code does not know about ImageResources holding Frames and
    336         // won't know to break the cycle.
    337         // This will become an issue when SVGImage will be able to load other
    338         // SVGImage objects, but we're safe now, because SVGImage can only be
    339         // loaded by a top-level document.
    340         m_page = adoptPtr(new Page(pageClients));
    341         m_page->settings()->setMediaEnabled(false);
    342         m_page->settings()->setScriptEnabled(false);
    343         m_page->settings()->setPluginsEnabled(false);
    344         m_page->settings()->setAcceleratedCompositingEnabled(false);
    345 
    346         RefPtr<Frame> frame = Frame::create(m_page.get(), 0, dummyFrameLoaderClient);
    347         frame->setView(FrameView::create(frame.get()));
    348         frame->init();
    349         FrameLoader* loader = frame->loader();
    350         loader->forceSandboxFlags(SandboxAll);
    351 
    352         frame->view()->setScrollbarsSuppressed(true);
    353         frame->view()->setCanHaveScrollbars(false); // SVG Images will always synthesize a viewBox, if it's not available, and thus never see scrollbars.
    354         frame->view()->setTransparent(true); // SVG Images are transparent.
    355 
    356         ASSERT(loader->activeDocumentLoader()); // DocumentLoader should have been created by frame->init().
    357         DocumentWriter* writer = loader->activeDocumentLoader()->beginWriting("image/svg+xml", "UTF-8");
    358         writer->addData(data()->data(), data()->size());
    359         loader->activeDocumentLoader()->endWriting(writer);
    360         // Set the intrinsic size before a container size is available.
    361         m_intrinsicSize = containerSize();
    362     }
    363 
    364     return m_page;
    365 }
    366 
    367 String SVGImage::filenameExtension() const
    368 {
    369     return "svg";
    370 }
    371 
    372 }
    373 
    374