Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 1999 Antti Koivisto (koivisto (at) kde.org)
      4  *           (C) 2000 Dirk Mueller (mueller (at) kde.org)
      5  *           (C) 2006 Allan Sandfeld Jensen (kde (at) carewolf.com)
      6  *           (C) 2006 Samuel Weinig (sam.weinig (at) gmail.com)
      7  * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved.
      8  *
      9  * This library is free software; you can redistribute it and/or
     10  * modify it under the terms of the GNU Library General Public
     11  * License as published by the Free Software Foundation; either
     12  * version 2 of the License, or (at your option) any later version.
     13  *
     14  * This library is distributed in the hope that it will be useful,
     15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     17  * Library General Public License for more details.
     18  *
     19  * You should have received a copy of the GNU Library General Public License
     20  * along with this library; see the file COPYING.LIB.  If not, write to
     21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     22  * Boston, MA 02110-1301, USA.
     23  *
     24  */
     25 
     26 #include "config.h"
     27 #include "RenderImage.h"
     28 
     29 #include "Frame.h"
     30 #include "GraphicsContext.h"
     31 #include "HTMLAreaElement.h"
     32 #include "HTMLCollection.h"
     33 #include "HTMLImageElement.h"
     34 #include "HTMLInputElement.h"
     35 #include "HTMLMapElement.h"
     36 #include "HTMLNames.h"
     37 #include "HitTestResult.h"
     38 #include "Page.h"
     39 #include "RenderTheme.h"
     40 #include "RenderView.h"
     41 #include "SelectionController.h"
     42 #include <wtf/CurrentTime.h>
     43 #include <wtf/UnusedParam.h>
     44 
     45 #ifdef ANDROID_LAYOUT
     46 #include "Settings.h"
     47 #endif
     48 
     49 #if ENABLE(WML)
     50 #include "WMLImageElement.h"
     51 #include "WMLNames.h"
     52 #endif
     53 
     54 using namespace std;
     55 
     56 namespace WebCore {
     57 
     58 static const double cInterpolationCutoff = 800. * 800.;
     59 static const double cLowQualityTimeThreshold = 0.050; // 50 ms
     60 
     61 class RenderImageScaleData : public Noncopyable {
     62 public:
     63     RenderImageScaleData(RenderImage* image, const IntSize& size, double time, bool lowQualityScale)
     64         : m_size(size)
     65         , m_time(time)
     66         , m_lowQualityScale(lowQualityScale)
     67         , m_highQualityRepaintTimer(image, &RenderImage::highQualityRepaintTimerFired)
     68     {
     69     }
     70 
     71     ~RenderImageScaleData()
     72     {
     73         m_highQualityRepaintTimer.stop();
     74     }
     75 
     76     const IntSize& size() const { return m_size; }
     77     double time() const { return m_time; }
     78     bool useLowQualityScale() const { return m_lowQualityScale; }
     79     Timer<RenderImage>& hiqhQualityRepaintTimer() { return m_highQualityRepaintTimer; }
     80 
     81     void setSize(const IntSize& s) { m_size = s; }
     82     void setTime(double t) { m_time = t; }
     83     void setUseLowQualityScale(bool b)
     84     {
     85         m_highQualityRepaintTimer.stop();
     86         m_lowQualityScale = b;
     87         if (b)
     88             m_highQualityRepaintTimer.startOneShot(cLowQualityTimeThreshold);
     89     }
     90 
     91 private:
     92     IntSize m_size;
     93     double m_time;
     94     bool m_lowQualityScale;
     95     Timer<RenderImage> m_highQualityRepaintTimer;
     96 };
     97 
     98 class RenderImageScaleObserver {
     99 public:
    100     static bool shouldImagePaintAtLowQuality(RenderImage*, const IntSize&);
    101 
    102     static void imageDestroyed(RenderImage* image)
    103     {
    104         if (gImages) {
    105             RenderImageScaleData* data = gImages->take(image);
    106             delete data;
    107             if (gImages->size() == 0) {
    108                 delete gImages;
    109                 gImages = 0;
    110             }
    111         }
    112     }
    113 
    114     static void highQualityRepaintTimerFired(RenderImage* image)
    115     {
    116         RenderImageScaleObserver::imageDestroyed(image);
    117         image->repaint();
    118     }
    119 
    120     static HashMap<RenderImage*, RenderImageScaleData*>* gImages;
    121 };
    122 
    123 bool RenderImageScaleObserver::shouldImagePaintAtLowQuality(RenderImage* image, const IntSize& size)
    124 {
    125     // If the image is not a bitmap image, then none of this is relevant and we just paint at high
    126     // quality.
    127     if (!image->image() || !image->image()->isBitmapImage())
    128         return false;
    129 
    130     // Make sure to use the unzoomed image size, since if a full page zoom is in effect, the image
    131     // is actually being scaled.
    132     IntSize imageSize(image->image()->width(), image->image()->height());
    133 
    134     // Look ourselves up in the hashtable.
    135     RenderImageScaleData* data = 0;
    136     if (gImages)
    137         data = gImages->get(image);
    138 
    139     if (imageSize == size) {
    140         // There is no scale in effect.  If we had a scale in effect before, we can just delete this data.
    141         if (data) {
    142             gImages->remove(image);
    143             delete data;
    144         }
    145         return false;
    146     }
    147 
    148     // There is no need to hash scaled images that always use low quality mode when the page demands it.  This is the iChat case.
    149     if (image->document()->page()->inLowQualityImageInterpolationMode()) {
    150         double totalPixels = static_cast<double>(image->image()->width()) * static_cast<double>(image->image()->height());
    151         if (totalPixels > cInterpolationCutoff)
    152             return true;
    153     }
    154 
    155     // If there is no data yet, we will paint the first scale at high quality and record the paint time in case a second scale happens
    156     // very soon.
    157     if (!data) {
    158         data = new RenderImageScaleData(image, size, currentTime(), false);
    159         if (!gImages)
    160             gImages = new HashMap<RenderImage*, RenderImageScaleData*>;
    161         gImages->set(image, data);
    162         return false;
    163     }
    164 
    165     // We are scaled, but we painted already at this size, so just keep using whatever mode we last painted with.
    166     if (data->size() == size)
    167         return data->useLowQualityScale();
    168 
    169     // We have data and our size just changed.  If this change happened quickly, go into low quality mode and then set a repaint
    170     // timer to paint in high quality mode.  Otherwise it is ok to just paint in high quality mode.
    171     double newTime = currentTime();
    172     data->setUseLowQualityScale(newTime - data->time() < cLowQualityTimeThreshold);
    173     data->setTime(newTime);
    174     data->setSize(size);
    175     return data->useLowQualityScale();
    176 }
    177 
    178 HashMap<RenderImage*, RenderImageScaleData*>* RenderImageScaleObserver::gImages = 0;
    179 
    180 void RenderImage::highQualityRepaintTimerFired(Timer<RenderImage>*)
    181 {
    182     RenderImageScaleObserver::highQualityRepaintTimerFired(this);
    183 }
    184 
    185 using namespace HTMLNames;
    186 
    187 RenderImage::RenderImage(Node* node)
    188     : RenderReplaced(node, IntSize(0, 0))
    189     , m_cachedImage(0)
    190 {
    191     updateAltText();
    192 
    193     view()->frameView()->setIsVisuallyNonEmpty();
    194 }
    195 
    196 RenderImage::~RenderImage()
    197 {
    198     if (m_cachedImage)
    199         m_cachedImage->removeClient(this);
    200     RenderImageScaleObserver::imageDestroyed(this);
    201 }
    202 
    203 void RenderImage::setCachedImage(CachedImage* newImage)
    204 {
    205     if (m_cachedImage == newImage)
    206         return;
    207     if (m_cachedImage)
    208         m_cachedImage->removeClient(this);
    209     m_cachedImage = newImage;
    210     if (m_cachedImage) {
    211         m_cachedImage->addClient(this);
    212         if (m_cachedImage->errorOccurred())
    213             imageChanged(m_cachedImage.get());
    214     }
    215 }
    216 
    217 // If we'll be displaying either alt text or an image, add some padding.
    218 static const unsigned short paddingWidth = 4;
    219 static const unsigned short paddingHeight = 4;
    220 
    221 // Alt text is restricted to this maximum size, in pixels.  These are
    222 // signed integers because they are compared with other signed values.
    223 static const int maxAltTextWidth = 1024;
    224 static const int maxAltTextHeight = 256;
    225 
    226 // Sets the image height and width to fit the alt text.  Returns true if the
    227 // image size changed.
    228 bool RenderImage::setImageSizeForAltText(CachedImage* newImage /* = 0 */)
    229 {
    230     int imageWidth = 0;
    231     int imageHeight = 0;
    232 
    233     // If we'll be displaying either text or an image, add a little padding.
    234     if (!m_altText.isEmpty() || newImage) {
    235         imageWidth = paddingWidth;
    236         imageHeight = paddingHeight;
    237     }
    238 
    239     if (newImage && newImage->image()) {
    240         // imageSize() returns 0 for the error image.  We need the true size of the
    241         // error image, so we have to get it by grabbing image() directly.
    242         imageWidth += newImage->image()->width() * style()->effectiveZoom();
    243         imageHeight += newImage->image()->height() * style()->effectiveZoom();
    244     }
    245 
    246     // we have an alt and the user meant it (its not a text we invented)
    247     if (!m_altText.isEmpty()) {
    248         const Font& font = style()->font();
    249         imageWidth = max(imageWidth, min(font.width(TextRun(m_altText.characters(), m_altText.length())), maxAltTextWidth));
    250         imageHeight = max(imageHeight, min(font.height(), maxAltTextHeight));
    251     }
    252 
    253     IntSize imageSize = IntSize(imageWidth, imageHeight);
    254     if (imageSize == intrinsicSize())
    255         return false;
    256 
    257     setIntrinsicSize(imageSize);
    258     return true;
    259 }
    260 
    261 void RenderImage::imageChanged(WrappedImagePtr newImage, const IntRect* rect)
    262 {
    263     if (documentBeingDestroyed())
    264         return;
    265 
    266     if (hasBoxDecorations() || hasMask())
    267         RenderReplaced::imageChanged(newImage, rect);
    268 
    269     if (newImage != imagePtr() || !newImage)
    270         return;
    271 
    272     bool imageSizeChanged = false;
    273 
    274     // Set image dimensions, taking into account the size of the alt text.
    275     if (errorOccurred())
    276         imageSizeChanged = setImageSizeForAltText(m_cachedImage.get());
    277 
    278     bool shouldRepaint = true;
    279 
    280     // Image dimensions have been changed, see what needs to be done
    281     if (imageSize(style()->effectiveZoom()) != intrinsicSize() || imageSizeChanged) {
    282         if (!errorOccurred())
    283             setIntrinsicSize(imageSize(style()->effectiveZoom()));
    284 
    285         // In the case of generated image content using :before/:after, we might not be in the
    286         // render tree yet.  In that case, we don't need to worry about check for layout, since we'll get a
    287         // layout when we get added in to the render tree hierarchy later.
    288         if (containingBlock()) {
    289             // lets see if we need to relayout at all..
    290             int oldwidth = width();
    291             int oldheight = height();
    292             if (!prefWidthsDirty())
    293                 setPrefWidthsDirty(true);
    294             calcWidth();
    295             calcHeight();
    296 
    297             if (imageSizeChanged || width() != oldwidth || height() != oldheight) {
    298                 shouldRepaint = false;
    299                 if (!selfNeedsLayout())
    300                     setNeedsLayout(true);
    301             }
    302 
    303             setWidth(oldwidth);
    304             setHeight(oldheight);
    305         }
    306     }
    307 
    308     if (shouldRepaint) {
    309         IntRect repaintRect;
    310         if (rect) {
    311             // The image changed rect is in source image coordinates (pre-zooming),
    312             // so map from the bounds of the image to the contentsBox.
    313             repaintRect = enclosingIntRect(mapRect(*rect, FloatRect(FloatPoint(), imageSize(1.0f)), contentBoxRect()));
    314             // Guard against too-large changed rects.
    315             repaintRect.intersect(contentBoxRect());
    316         } else
    317             repaintRect = contentBoxRect();
    318 
    319         repaintRectangle(repaintRect);
    320 
    321 #if USE(ACCELERATED_COMPOSITING)
    322         if (hasLayer()) {
    323             // Tell any potential compositing layers that the image needs updating.
    324             layer()->rendererContentChanged();
    325         }
    326 #endif
    327     }
    328 }
    329 
    330 void RenderImage::notifyFinished(CachedResource* newImage)
    331 {
    332     if (documentBeingDestroyed())
    333         return;
    334 
    335 #if USE(ACCELERATED_COMPOSITING)
    336     if ((newImage == m_cachedImage) && hasLayer()) {
    337         // tell any potential compositing layers
    338         // that the image is done and they can reference it directly.
    339         layer()->rendererContentChanged();
    340     }
    341 #else
    342     UNUSED_PARAM(newImage);
    343 #endif
    344 }
    345 
    346 void RenderImage::resetAnimation()
    347 {
    348     if (m_cachedImage) {
    349         image()->resetAnimation();
    350         if (!needsLayout())
    351             repaint();
    352     }
    353 }
    354 
    355 void RenderImage::paintReplaced(PaintInfo& paintInfo, int tx, int ty)
    356 {
    357     int cWidth = contentWidth();
    358     int cHeight = contentHeight();
    359     int leftBorder = borderLeft();
    360     int topBorder = borderTop();
    361     int leftPad = paddingLeft();
    362     int topPad = paddingTop();
    363 
    364     if (document()->printing() && !view()->printImages())
    365         return;
    366 
    367     GraphicsContext* context = paintInfo.context;
    368 
    369     if (!hasImage() || errorOccurred()) {
    370         if (paintInfo.phase == PaintPhaseSelection)
    371             return;
    372 
    373         if (cWidth > 2 && cHeight > 2) {
    374             // Draw an outline rect where the image should be.
    375 #ifdef ANDROID_FIX // see http://b/issue?id=2052757
    376             context->save();
    377 #endif
    378             context->setStrokeStyle(SolidStroke);
    379             context->setStrokeColor(Color::lightGray, style()->colorSpace());
    380             context->setFillColor(Color::transparent, style()->colorSpace());
    381             context->drawRect(IntRect(tx + leftBorder + leftPad, ty + topBorder + topPad, cWidth, cHeight));
    382 #ifdef ANDROID_FIX // see http://b/issue?id=2052757
    383             context->restore();
    384 #endif
    385 
    386             bool errorPictureDrawn = false;
    387             int imageX = 0;
    388             int imageY = 0;
    389             // When calculating the usable dimensions, exclude the pixels of
    390             // the ouline rect so the error image/alt text doesn't draw on it.
    391             int usableWidth = cWidth - 2;
    392             int usableHeight = cHeight - 2;
    393 
    394             if (errorOccurred() && !image()->isNull() && (usableWidth >= image()->width()) && (usableHeight >= image()->height())) {
    395                 // Center the error image, accounting for border and padding.
    396                 int centerX = (usableWidth - image()->width()) / 2;
    397                 if (centerX < 0)
    398                     centerX = 0;
    399                 int centerY = (usableHeight - image()->height()) / 2;
    400                 if (centerY < 0)
    401                     centerY = 0;
    402                 imageX = leftBorder + leftPad + centerX + 1;
    403                 imageY = topBorder + topPad + centerY + 1;
    404                 context->drawImage(image(), style()->colorSpace(), IntPoint(tx + imageX, ty + imageY));
    405                 errorPictureDrawn = true;
    406             }
    407 
    408             if (!m_altText.isEmpty()) {
    409                 String text = document()->displayStringModifiedByEncoding(m_altText);
    410                 context->setFillColor(style()->color(), style()->colorSpace());
    411                 int ax = tx + leftBorder + leftPad;
    412                 int ay = ty + topBorder + topPad;
    413                 const Font& font = style()->font();
    414                 int ascent = font.ascent();
    415 
    416                 // Only draw the alt text if it'll fit within the content box,
    417                 // and only if it fits above the error image.
    418                 TextRun textRun(text.characters(), text.length());
    419                 int textWidth = font.width(textRun);
    420                 if (errorPictureDrawn) {
    421                     if (usableWidth >= textWidth && font.height() <= imageY)
    422                         context->drawText(style()->font(), textRun, IntPoint(ax, ay + ascent));
    423                 } else if (usableWidth >= textWidth && cHeight >= font.height())
    424                     context->drawText(style()->font(), textRun, IntPoint(ax, ay + ascent));
    425             }
    426         }
    427     } else if (hasImage() && cWidth > 0 && cHeight > 0) {
    428         Image* img = image(cWidth, cHeight);
    429         if (!img || img->isNull())
    430             return;
    431 
    432 #if PLATFORM(MAC)
    433         if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
    434             paintCustomHighlight(tx - x(), ty - y(), style()->highlight(), true);
    435 #endif
    436 
    437         IntSize contentSize(cWidth, cHeight);
    438         IntRect rect(IntPoint(tx + leftBorder + leftPad, ty + topBorder + topPad), contentSize);
    439         paintIntoRect(context, rect);
    440     }
    441 }
    442 
    443 void RenderImage::paint(PaintInfo& paintInfo, int tx, int ty)
    444 {
    445     RenderReplaced::paint(paintInfo, tx, ty);
    446 
    447     if (paintInfo.phase == PaintPhaseOutline)
    448         paintFocusRings(paintInfo, style());
    449 }
    450 
    451 void RenderImage::paintFocusRings(PaintInfo& paintInfo, const RenderStyle* style)
    452 {
    453     // Don't draw focus rings if printing.
    454     if (document()->printing() || !document()->frame()->selection()->isFocusedAndActive())
    455         return;
    456 
    457     if (paintInfo.context->paintingDisabled() && !paintInfo.context->updatingControlTints())
    458         return;
    459 
    460     HTMLMapElement* mapElement = imageMap();
    461     if (!mapElement)
    462         return;
    463 
    464     Document* document = mapElement->document();
    465     if (!document)
    466         return;
    467 
    468     Node* focusedNode = document->focusedNode();
    469     if (!focusedNode)
    470         return;
    471 
    472     RefPtr<HTMLCollection> areas = mapElement->areas();
    473     unsigned numAreas = areas->length();
    474 
    475     // FIXME: Clip the paths to the image bounding box.
    476     for (unsigned k = 0; k < numAreas; ++k) {
    477         HTMLAreaElement* areaElement = static_cast<HTMLAreaElement*>(areas->item(k));
    478         if (focusedNode != areaElement)
    479             continue;
    480 
    481         Vector<Path> focusRingPaths;
    482         focusRingPaths.append(areaElement->getPath(this));
    483         paintInfo.context->drawFocusRing(focusRingPaths, style->outlineWidth(), style->outlineOffset(), style->outlineColor());
    484         break;
    485     }
    486 }
    487 
    488 void RenderImage::paintIntoRect(GraphicsContext* context, const IntRect& rect)
    489 {
    490     if (!hasImage() || errorOccurred() || rect.width() <= 0 || rect.height() <= 0)
    491         return;
    492 
    493     Image* img = image(rect.width(), rect.height());
    494     if (!img || img->isNull())
    495         return;
    496 
    497     HTMLImageElement* imageElt = (node() && node()->hasTagName(imgTag)) ? static_cast<HTMLImageElement*>(node()) : 0;
    498     CompositeOperator compositeOperator = imageElt ? imageElt->compositeOperator() : CompositeSourceOver;
    499     bool useLowQualityScaling = RenderImageScaleObserver::shouldImagePaintAtLowQuality(this, rect.size());
    500     context->drawImage(image(rect.width(), rect.height()), style()->colorSpace(), rect, compositeOperator, useLowQualityScaling);
    501 }
    502 
    503 int RenderImage::minimumReplacedHeight() const
    504 {
    505     return errorOccurred() ? intrinsicSize().height() : 0;
    506 }
    507 
    508 HTMLMapElement* RenderImage::imageMap() const
    509 {
    510     HTMLImageElement* i = node() && node()->hasTagName(imgTag) ? static_cast<HTMLImageElement*>(node()) : 0;
    511     return i ? i->document()->getImageMap(i->getAttribute(usemapAttr)) : 0;
    512 }
    513 
    514 bool RenderImage::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction)
    515 {
    516     HitTestResult tempResult(result.point());
    517     bool inside = RenderReplaced::nodeAtPoint(request, tempResult, x, y, tx, ty, hitTestAction);
    518 
    519     if (inside && node()) {
    520         if (HTMLMapElement* map = imageMap()) {
    521             IntRect contentBox = contentBoxRect();
    522             float zoom = style()->effectiveZoom();
    523             int mapX = lroundf((x - tx - this->x() - contentBox.x()) / zoom);
    524             int mapY = lroundf((y - ty - this->y() - contentBox.y()) / zoom);
    525             if (map->mapMouseEvent(mapX, mapY, contentBox.size(), tempResult))
    526                 tempResult.setInnerNonSharedNode(node());
    527         }
    528     }
    529 
    530     if (inside)
    531         result = tempResult;
    532     return inside;
    533 }
    534 
    535 void RenderImage::updateAltText()
    536 {
    537     if (!node())
    538         return;
    539 
    540     if (node()->hasTagName(inputTag))
    541         m_altText = static_cast<HTMLInputElement*>(node())->altText();
    542     else if (node()->hasTagName(imgTag))
    543         m_altText = static_cast<HTMLImageElement*>(node())->altText();
    544 #if ENABLE(WML)
    545     else if (node()->hasTagName(WMLNames::imgTag))
    546         m_altText = static_cast<WMLImageElement*>(node())->altText();
    547 #endif
    548 }
    549 
    550 bool RenderImage::isWidthSpecified() const
    551 {
    552     switch (style()->width().type()) {
    553         case Fixed:
    554         case Percent:
    555             return true;
    556         case Auto:
    557         case Relative: // FIXME: Shouldn't this case return true?
    558         case Static:
    559         case Intrinsic:
    560         case MinIntrinsic:
    561             return false;
    562     }
    563     ASSERT(false);
    564     return false;
    565 }
    566 
    567 bool RenderImage::isHeightSpecified() const
    568 {
    569     switch (style()->height().type()) {
    570         case Fixed:
    571         case Percent:
    572             return true;
    573         case Auto:
    574         case Relative: // FIXME: Shouldn't this case return true?
    575         case Static:
    576         case Intrinsic:
    577         case MinIntrinsic:
    578             return false;
    579     }
    580     ASSERT(false);
    581     return false;
    582 }
    583 
    584 int RenderImage::calcReplacedWidth(bool includeMaxWidth) const
    585 {
    586     if (imageHasRelativeWidth())
    587         if (RenderObject* cb = isPositioned() ? container() : containingBlock()) {
    588             if (cb->isBox())
    589                 setImageContainerSize(IntSize(toRenderBox(cb)->availableWidth(), toRenderBox(cb)->availableHeight()));
    590         }
    591 
    592     int width;
    593     if (isWidthSpecified())
    594         width = calcReplacedWidthUsing(style()->width());
    595     else if (usesImageContainerSize())
    596         width = imageSize(style()->effectiveZoom()).width();
    597     else if (imageHasRelativeWidth())
    598         width = 0; // If the image is relatively-sized, set the width to 0 until there is a set container size.
    599     else
    600         width = calcAspectRatioWidth();
    601 
    602     int minW = calcReplacedWidthUsing(style()->minWidth());
    603     int maxW = !includeMaxWidth || style()->maxWidth().isUndefined() ? width : calcReplacedWidthUsing(style()->maxWidth());
    604 
    605 #ifdef ANDROID_LAYOUT
    606     width = max(minW, min(width, maxW));
    607     // in SSR mode, we will fit the image to its container width
    608     if (document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) {
    609         int cw = containingBlockWidthForContent();
    610         if (cw && width>cw)
    611             width = cw;
    612     }
    613     return width;
    614 #else
    615     return max(minW, min(width, maxW));
    616 #endif
    617 }
    618 
    619 int RenderImage::calcReplacedHeight() const
    620 {
    621     int height;
    622     if (isHeightSpecified())
    623         height = calcReplacedHeightUsing(style()->height());
    624     else if (usesImageContainerSize())
    625         height = imageSize(style()->effectiveZoom()).height();
    626     else if (imageHasRelativeHeight())
    627         height = 0; // If the image is relatively-sized, set the height to 0 until there is a set container size.
    628     else
    629         height = calcAspectRatioHeight();
    630 
    631     int minH = calcReplacedHeightUsing(style()->minHeight());
    632     int maxH = style()->maxHeight().isUndefined() ? height : calcReplacedHeightUsing(style()->maxHeight());
    633 
    634 #ifdef ANDROID_LAYOUT
    635     height = max(minH, min(height, maxH));
    636     // in SSR mode, we will fit the image to its container width
    637     if (height && document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) {
    638         int width;
    639         if (isWidthSpecified())
    640             width = calcReplacedWidthUsing(style()->width());
    641         else
    642             width = calcAspectRatioWidth();
    643         int minW = calcReplacedWidthUsing(style()->minWidth());
    644         int maxW = style()->maxWidth().value() == undefinedLength ? width :
    645             calcReplacedWidthUsing(style()->maxWidth());
    646         width = max(minW, min(width, maxW));
    647 
    648         int cw = containingBlockWidthForContent();
    649         if (cw && width && width > cw)
    650             height = cw * height / width;   // preserve aspect ratio
    651     }
    652     return height;
    653 #else
    654     return max(minH, min(height, maxH));
    655 #endif
    656 }
    657 
    658 int RenderImage::calcAspectRatioWidth() const
    659 {
    660     IntSize size = intrinsicSize();
    661     if (!size.height())
    662         return 0;
    663     if (!hasImage() || errorOccurred())
    664         return size.width(); // Don't bother scaling.
    665     return RenderReplaced::calcReplacedHeight() * size.width() / size.height();
    666 }
    667 
    668 int RenderImage::calcAspectRatioHeight() const
    669 {
    670     IntSize size = intrinsicSize();
    671     if (!size.width())
    672         return 0;
    673     if (!hasImage() || errorOccurred())
    674         return size.height(); // Don't bother scaling.
    675     return RenderReplaced::calcReplacedWidth() * size.height() / size.width();
    676 }
    677 
    678 void RenderImage::calcPrefWidths()
    679 {
    680     ASSERT(prefWidthsDirty());
    681 
    682     int paddingAndBorders = paddingLeft() + paddingRight() + borderLeft() + borderRight();
    683     m_maxPrefWidth = calcReplacedWidth(false) + paddingAndBorders;
    684 
    685     if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength)
    686         m_maxPrefWidth = min(m_maxPrefWidth, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? paddingAndBorders : 0));
    687 
    688     if (style()->width().isPercent() || style()->height().isPercent() ||
    689         style()->maxWidth().isPercent() || style()->maxHeight().isPercent() ||
    690         style()->minWidth().isPercent() || style()->minHeight().isPercent())
    691         m_minPrefWidth = 0;
    692     else
    693         m_minPrefWidth = m_maxPrefWidth;
    694 
    695     setPrefWidthsDirty(false);
    696 }
    697 
    698 Image* RenderImage::nullImage()
    699 {
    700     return Image::nullImage();
    701 }
    702 
    703 } // namespace WebCore
    704