Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 2007, 2008, 2009, 2010 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 
     28 #if ENABLE(VIDEO)
     29 #include "RenderVideo.h"
     30 
     31 #include "Document.h"
     32 #include "FrameView.h"
     33 #include "GraphicsContext.h"
     34 #include "HTMLNames.h"
     35 #include "HTMLVideoElement.h"
     36 #include "MediaPlayer.h"
     37 #include "RenderView.h"
     38 
     39 #if USE(ACCELERATED_COMPOSITING)
     40 #include "RenderLayer.h"
     41 #include "RenderLayerBacking.h"
     42 #endif
     43 
     44 using namespace std;
     45 
     46 namespace WebCore {
     47 
     48 using namespace HTMLNames;
     49 
     50 static const int cDefaultWidth = 300;
     51 static const int cDefaultHeight = 150;
     52 
     53 RenderVideo::RenderVideo(HTMLVideoElement* video)
     54     : RenderMedia(video)
     55 {
     56     if (video->player())
     57         setIntrinsicSize(video->player()->naturalSize());
     58     else {
     59         // When the natural size of the video is unavailable, we use the provided
     60         // width and height attributes of the video element as the intrinsic size until
     61         // better values become available. If these attributes are not set, we fall back
     62         // to a default video size (300x150).
     63         if (video->hasAttribute(widthAttr) && video->hasAttribute(heightAttr))
     64             setIntrinsicSize(IntSize(video->width(), video->height()));
     65         else if (video->ownerDocument() && video->ownerDocument()->isMediaDocument()) {
     66             // Video in standalone media documents should not use the default 300x150
     67             // size since they also have audio thrown at them. By setting the intrinsic
     68             // size to 300x1 the video will resize itself in these cases, and audio will
     69             // have the correct height (it needs to be > 0 for controls to render properly).
     70             setIntrinsicSize(IntSize(cDefaultWidth, 1));
     71         }
     72         else
     73             setIntrinsicSize(IntSize(cDefaultWidth, cDefaultHeight));
     74     }
     75 }
     76 
     77 RenderVideo::~RenderVideo()
     78 {
     79     if (MediaPlayer* p = player()) {
     80         p->setVisible(false);
     81         p->setFrameView(0);
     82     }
     83 }
     84 
     85 void RenderVideo::intrinsicSizeChanged()
     86 {
     87     if (videoElement()->shouldDisplayPosterImage())
     88         RenderMedia::intrinsicSizeChanged();
     89     videoSizeChanged();
     90 }
     91 
     92 
     93 void RenderVideo::videoSizeChanged()
     94 {
     95     if (!player())
     96         return;
     97     IntSize size = player()->naturalSize();
     98     if (!size.isEmpty() && size != intrinsicSize()) {
     99         setIntrinsicSize(size);
    100         setPrefWidthsDirty(true);
    101         setNeedsLayout(true);
    102     }
    103 }
    104 
    105 void RenderVideo::imageChanged(WrappedImagePtr newImage, const IntRect* rect)
    106 {
    107     RenderMedia::imageChanged(newImage, rect);
    108 
    109     // Cache the image intrinsic size so we can continue to use it to draw the image correctly
    110     // even after we know the video intrisic size but aren't able to draw video frames yet
    111     // (we don't want to scale the poster to the video size).
    112     if (videoElement()->shouldDisplayPosterImage())
    113         m_cachedImageSize = intrinsicSize();
    114 }
    115 
    116 IntRect RenderVideo::videoBox() const
    117 {
    118     if (m_cachedImageSize.isEmpty() && videoElement()->shouldDisplayPosterImage())
    119         return IntRect();
    120 
    121     IntSize elementSize;
    122     if (videoElement()->shouldDisplayPosterImage())
    123         elementSize = m_cachedImageSize;
    124     else
    125         elementSize = intrinsicSize();
    126 
    127     IntRect contentRect = contentBoxRect();
    128     if (elementSize.isEmpty() || contentRect.isEmpty())
    129         return IntRect();
    130 
    131     IntRect renderBox = contentRect;
    132     int ratio = renderBox.width() * elementSize.height() - renderBox.height() * elementSize.width();
    133     if (ratio > 0) {
    134         int newWidth = renderBox.height() * elementSize.width() / elementSize.height();
    135         // Just fill the whole area if the difference is one pixel or less (in both sides)
    136         if (renderBox.width() - newWidth > 2)
    137             renderBox.setWidth(newWidth);
    138         renderBox.move((contentRect.width() - renderBox.width()) / 2, 0);
    139     } else if (ratio < 0) {
    140         int newHeight = renderBox.width() * elementSize.height() / elementSize.width();
    141         if (renderBox.height() - newHeight > 2)
    142             renderBox.setHeight(newHeight);
    143         renderBox.move(0, (contentRect.height() - renderBox.height()) / 2);
    144     }
    145 
    146     return renderBox;
    147 }
    148 
    149 void RenderVideo::paintReplaced(PaintInfo& paintInfo, int tx, int ty)
    150 {
    151     MediaPlayer* mediaPlayer = player();
    152     bool displayingPoster = videoElement()->shouldDisplayPosterImage();
    153 
    154     if (displayingPoster && document()->printing() && !view()->printImages())
    155         return;
    156 
    157     if (!displayingPoster) {
    158         if (!mediaPlayer)
    159             return;
    160         updatePlayer();
    161     }
    162 
    163     IntRect rect = videoBox();
    164     if (rect.isEmpty())
    165         return;
    166     rect.move(tx, ty);
    167     if (displayingPoster)
    168         paintIntoRect(paintInfo.context, rect);
    169     else
    170         mediaPlayer->paint(paintInfo.context, rect);
    171 }
    172 
    173 void RenderVideo::layout()
    174 {
    175     RenderMedia::layout();
    176     updatePlayer();
    177 }
    178 
    179 HTMLVideoElement* RenderVideo::videoElement() const
    180 {
    181     ASSERT(node()->hasTagName(videoTag));
    182     return static_cast<HTMLVideoElement*>(node());
    183 }
    184 
    185 void RenderVideo::updateFromElement()
    186 {
    187     RenderMedia::updateFromElement();
    188     updatePlayer();
    189 }
    190 
    191 void RenderVideo::updatePlayer()
    192 {
    193     MediaPlayer* mediaPlayer = player();
    194     if (!mediaPlayer)
    195         return;
    196     if (!videoElement()->inActiveDocument()) {
    197         mediaPlayer->setVisible(false);
    198         return;
    199     }
    200 
    201 #if USE(ACCELERATED_COMPOSITING)
    202     layer()->rendererContentChanged();
    203 #endif
    204 
    205     IntRect videoBounds = videoBox();
    206     mediaPlayer->setFrameView(document()->view());
    207     mediaPlayer->setSize(IntSize(videoBounds.width(), videoBounds.height()));
    208     mediaPlayer->setVisible(true);
    209 }
    210 
    211 int RenderVideo::calcReplacedWidth(bool includeMaxWidth) const
    212 {
    213     int width;
    214     if (isWidthSpecified())
    215         width = calcReplacedWidthUsing(style()->width());
    216     else
    217         width = calcAspectRatioWidth() * style()->effectiveZoom();
    218 
    219     int minW = calcReplacedWidthUsing(style()->minWidth());
    220     int maxW = !includeMaxWidth || style()->maxWidth().isUndefined() ? width : calcReplacedWidthUsing(style()->maxWidth());
    221 
    222     return max(minW, min(width, maxW));
    223 }
    224 
    225 int RenderVideo::calcReplacedHeight() const
    226 {
    227     int height;
    228     if (isHeightSpecified())
    229         height = calcReplacedHeightUsing(style()->height());
    230     else
    231         height = calcAspectRatioHeight() * style()->effectiveZoom();
    232 
    233     int minH = calcReplacedHeightUsing(style()->minHeight());
    234     int maxH = style()->maxHeight().isUndefined() ? height : calcReplacedHeightUsing(style()->maxHeight());
    235 
    236     return max(minH, min(height, maxH));
    237 }
    238 
    239 int RenderVideo::calcAspectRatioWidth() const
    240 {
    241     int intrinsicWidth = intrinsicSize().width();
    242     int intrinsicHeight = intrinsicSize().height();
    243     if (!intrinsicHeight)
    244         return 0;
    245     return RenderBox::calcReplacedHeight() * intrinsicWidth / intrinsicHeight;
    246 }
    247 
    248 int RenderVideo::calcAspectRatioHeight() const
    249 {
    250     int intrinsicWidth = intrinsicSize().width();
    251     int intrinsicHeight = intrinsicSize().height();
    252     if (!intrinsicWidth)
    253         return 0;
    254     return RenderBox::calcReplacedWidth() * intrinsicHeight / intrinsicWidth;
    255 }
    256 
    257 int RenderVideo::minimumReplacedHeight() const
    258 {
    259     return 0;
    260 }
    261 
    262 #if USE(ACCELERATED_COMPOSITING)
    263 bool RenderVideo::supportsAcceleratedRendering() const
    264 {
    265     MediaPlayer* p = player();
    266     if (p)
    267         return p->supportsAcceleratedRendering();
    268 
    269     return false;
    270 }
    271 
    272 void RenderVideo::acceleratedRenderingStateChanged()
    273 {
    274     MediaPlayer* p = player();
    275     if (p)
    276         p->acceleratedRenderingStateChanged();
    277 }
    278 
    279 GraphicsLayer* RenderVideo::videoGraphicsLayer() const
    280 {
    281     if (hasLayer() && layer()->isComposited())
    282         return layer()->backing()->graphicsLayer();
    283 
    284     return 0;
    285 }
    286 #endif  // USE(ACCELERATED_COMPOSITING)
    287 
    288 } // namespace WebCore
    289 
    290 #endif
    291