RenderVideo.cpp   [plain text]


/*
 * Copyright (C) 2007 Apple Inc.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#include "config.h"

#if ENABLE(VIDEO)
#include "RenderVideo.h"

#include "Document.h"
#include "FrameView.h"
#include "GraphicsContext.h"
#include "HTMLNames.h"
#include "HTMLVideoElement.h"
#include "MediaPlayer.h"

#if ENABLE(HW_COMP)
#include "RenderLayer.h"
#include "LCLayer.h"
#endif

using namespace std;

namespace WebCore {

using namespace HTMLNames;

RenderVideo::RenderVideo(HTMLMediaElement* video)
    : RenderMedia(video, video->player() ? video->player()->naturalSize() : IntSize(300, 150))
#if ENABLE(HW_COMP)
    , m_resizeTimer(this, &RenderVideo::resizeTimerFired)
#endif
{
}

RenderVideo::~RenderVideo()
{
    if (MediaPlayer* p = player()) {
        p->setVisible(false);
        p->setParentWidget(0);
    }
}
    
void RenderVideo::videoSizeChanged()
{
    if (!player())
        return;
    IntSize size = player()->naturalSize();
    if (size != intrinsicSize()) {
        setIntrinsicSize(size);
        setPrefWidthsDirty(true);
        setNeedsLayout(true);
    }
}

#if ENABLE(HW_COMP)
void RenderVideo::videoAdvanced()
{
    IntRect repaintRect = IntRect(borderLeft() + paddingLeft(), borderTop() + paddingTop(), contentWidth(), contentHeight());
    
    // assume that we only need to repaint our parent layer so simply call repaint 
    // was: repaintRectangle(repaintRect); // this causes the entire document to be repainted
    repaint(true);
}
#endif

IntRect RenderVideo::videoBox() const 
{
    IntRect contentRect = contentBox();
    
    if (intrinsicSize().isEmpty() || contentRect.isEmpty())
        return IntRect();

    IntRect resultRect = contentRect;
    int ratio = contentRect.width() * intrinsicSize().height() - contentRect.height() * intrinsicSize().width();
    if (ratio > 0) {
        int newWidth = contentRect.height() * intrinsicSize().width() / intrinsicSize().height();
        // Just fill the whole area if the difference is one pixel or less (in both sides)
        if (resultRect.width() - newWidth > 2)
            resultRect.setWidth(newWidth);
        resultRect.move((contentRect.width() - resultRect.width()) / 2, 0);
    } else if (ratio < 0) {
        int newHeight = contentRect.width() * intrinsicSize().height() / intrinsicSize().width();
        if (resultRect.height() - newHeight > 2)
            resultRect.setHeight(newHeight);
        resultRect.move(0, (contentRect.height() - resultRect.height()) / 2);
    }
    return resultRect;
}
    
void RenderVideo::paintReplaced(PaintInfo& paintInfo, int tx, int ty)
{
    MediaPlayer* mediaPlayer = player();
    if (!mediaPlayer)
        return;
    updatePlayer();
    IntRect rect = videoBox();
    if (rect.isEmpty())
        return;
    rect.move(tx, ty);
    mediaPlayer->paint(paintInfo.context, rect);
}

void RenderVideo::layout()
{
    RenderMedia::layout();
    updatePlayer();

#if ENABLE(HW_COMP)
    updateLayerBounds();
#endif
}
    
void RenderVideo::updateFromElement()
{
    RenderMedia::updateFromElement();
    updatePlayer();
}

void RenderVideo::updatePlayer()
{
    MediaPlayer* mediaPlayer = player();
    if (!mediaPlayer)
        return;
    Document* doc = document();
    if (doc->inPageCache())
        return;
    int x;
    int y;
    absolutePosition(x, y);
    IntRect videoBounds = videoBox(); 
    videoBounds.move(x, y);
    mediaPlayer->setParentWidget(doc->view());
    mediaPlayer->setRect(videoBounds);
    mediaPlayer->setVisible(true);
}

bool RenderVideo::isWidthSpecified() const
{
    switch (style()->width().type()) {
        case Fixed:
        case Percent:
            return true;
        case Auto:
        case Relative: // FIXME: Shouldn't this case return true? It doesn't for images.
        case Static:
        case Intrinsic:
        case MinIntrinsic:
            return false;
    }
    ASSERT(false);
    return false;
}

bool RenderVideo::isHeightSpecified() const
{
    switch (style()->height().type()) {
        case Fixed:
        case Percent:
            return true;
        case Auto:
        case Relative: // FIXME: Shouldn't this case return true? It doesn't for images.
        case Static:
        case Intrinsic:
        case MinIntrinsic:
            return false;
    }
    ASSERT(false);
    return false;
}

int RenderVideo::calcReplacedWidth() const
{
    int width;
    if (isWidthSpecified())
        width = calcReplacedWidthUsing(style()->width());
    else
        width = calcAspectRatioWidth();

    int minW = calcReplacedWidthUsing(style()->minWidth());
    int maxW = style()->maxWidth().isUndefined() ? width : calcReplacedWidthUsing(style()->maxWidth());

    return max(minW, min(width, maxW));
}

int RenderVideo::calcReplacedHeight() const
{
    int height;
    if (isHeightSpecified())
        height = calcReplacedHeightUsing(style()->height());
    else
        height = calcAspectRatioHeight();

    int minH = calcReplacedHeightUsing(style()->minHeight());
    int maxH = style()->maxHeight().isUndefined() ? height : calcReplacedHeightUsing(style()->maxHeight());

    return max(minH, min(height, maxH));
}

int RenderVideo::calcAspectRatioWidth() const
{
    int intrinsicWidth = intrinsicSize().width();
    int intrinsicHeight = intrinsicSize().height();
    if (!intrinsicHeight)
        return 0;
    return RenderBox::calcReplacedHeight() * intrinsicWidth / intrinsicHeight;
}

int RenderVideo::calcAspectRatioHeight() const
{
    int intrinsicWidth = intrinsicSize().width();
    int intrinsicHeight = intrinsicSize().height();
    if (!intrinsicWidth)
        return 0;
    return RenderBox::calcReplacedWidth() * intrinsicHeight / intrinsicWidth;
}

void RenderVideo::calcPrefWidths()
{
    ASSERT(prefWidthsDirty());

    m_maxPrefWidth = calcReplacedWidth() + paddingLeft() + paddingRight() + borderLeft() + borderRight();

    if (style()->width().isPercent() || style()->height().isPercent() || 
        style()->maxWidth().isPercent() || style()->maxHeight().isPercent() ||
        style()->minWidth().isPercent() || style()->minHeight().isPercent())
        m_minPrefWidth = 0;
    else
        m_minPrefWidth = m_maxPrefWidth;

    setPrefWidthsDirty(false);
}

#if ENABLE(HW_COMP)
LCLayer* RenderVideo::compositingLayer()
{
    if (hasLayer() && layer()->hasCompositingLayer()) {
        return layer()->compositingLayer();
    } else {
        return 0;
    }
}

void RenderVideo::updateLayerBounds()
{
    // Update the bounds of the layer to match
    LCLayer *layer = compositingLayer();
    if (layer) {
        int x;
        int y;        
        absolutePosition(x, y);
        x += borderLeft() + paddingLeft();
        y += borderTop() + paddingTop();
        
        int width = m_width - borderLeft() - borderRight() - paddingLeft() - paddingRight();
        int height = m_height - borderTop() - borderBottom() - paddingTop() - paddingBottom();

        layer->setBounds(FloatRect(0.0f, 0.0f, (float)width, (float)height));
    }
}

void RenderVideo::resizeTimerFired(Timer<RenderVideo>*)
{
    MediaPlayer* p = player();

    if (p) {
        p->compositingStateChanged();
        videoSizeChanged();
    }
}

void RenderVideo::compositingStateChanged()
{
    // can't trigger a resize now because the element may not have a parent at this point, eg. in the 
    //  case of a video element that is changing poster state and is being updated with "detach() + attach()"
    m_resizeTimer.startOneShot(0);
}
#endif  // ENABLE(HW_COMP)


} // namespace WebCore

#endif