webengine/osswebengine/WebCore/rendering/RenderImage.cpp
changeset 0 dd21522fd290
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 /**
       
     2  * This file is part of the DOM implementation for KDE.
       
     3  *
       
     4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
       
     5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
       
     6  *           (C) 2000 Dirk Mueller (mueller@kde.org)
       
     7  *           (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com)
       
     8  *           (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
       
     9  * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc.
       
    10  *
       
    11  * This library is free software; you can redistribute it and/or
       
    12  * modify it under the terms of the GNU Library General Public
       
    13  * License as published by the Free Software Foundation; either
       
    14  * version 2 of the License, or (at your option) any later version.
       
    15  *
       
    16  * This library is distributed in the hope that it will be useful,
       
    17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    19  * Library General Public License for more details.
       
    20  *
       
    21  * You should have received a copy of the GNU Library General Public License
       
    22  * along with this library; see the file COPYING.LIB.  If not, write to
       
    23  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
       
    24  * Boston, MA 02110-1301, USA.
       
    25  *
       
    26  */
       
    27 
       
    28 #include "config.h"
       
    29 #include "RenderImage.h"
       
    30 
       
    31 #include "BitmapImage.h"
       
    32 #include "Document.h"
       
    33 #include "GraphicsContext.h"
       
    34 #include "HTMLImageElement.h"
       
    35 #include "HTMLInputElement.h"
       
    36 #include "HTMLMapElement.h"
       
    37 #include "HTMLNames.h"
       
    38 #include "HitTestResult.h"
       
    39 #include "Page.h"
       
    40 #include "RenderView.h"
       
    41 #include "TextStyle.h"
       
    42 
       
    43 using namespace std;
       
    44 
       
    45 namespace WebCore {
       
    46 
       
    47 using namespace HTMLNames;
       
    48 
       
    49 RenderImage::RenderImage(Node* node)
       
    50     : RenderReplaced(node, IntSize(0, 0))
       
    51     , m_cachedImage(0)
       
    52     , m_isAnonymousImage(false)
       
    53 {
       
    54     updateAltText();
       
    55 }
       
    56 
       
    57 RenderImage::~RenderImage()
       
    58 {
       
    59     if (m_cachedImage)
       
    60         m_cachedImage->deref(this);
       
    61 }
       
    62 
       
    63 void RenderImage::setCachedImage(CachedImage* newImage)
       
    64 {
       
    65     if (m_isAnonymousImage || m_cachedImage == newImage)
       
    66         return;
       
    67     if (m_cachedImage)
       
    68         m_cachedImage->deref(this);
       
    69     m_cachedImage = newImage;
       
    70     if (m_cachedImage) {
       
    71         m_cachedImage->ref(this);
       
    72         if (m_cachedImage->errorOccurred())
       
    73             imageChanged(m_cachedImage);
       
    74     }
       
    75 }
       
    76 
       
    77 // If we'll be displaying either alt text or an image, add some padding.
       
    78 static const unsigned short paddingWidth = 4;
       
    79 static const unsigned short paddingHeight = 4;
       
    80 
       
    81 // Alt text is restricted to this maximum size, in pixels.  These are
       
    82 // signed integers because they are compared with other signed values.
       
    83 static const int maxAltTextWidth = 1024;
       
    84 static const int maxAltTextHeight = 256;
       
    85 
       
    86 // Sets the image height and width to fit the alt text.  Returns true if the
       
    87 // image size changed.
       
    88 bool RenderImage::setImageSizeForAltText(CachedImage* newImage /* = 0 */)
       
    89 {
       
    90     int imageWidth = 0;
       
    91     int imageHeight = 0;
       
    92   
       
    93     // If we'll be displaying either text or an image, add a little padding.
       
    94     if (!m_altText.isEmpty() || newImage) {
       
    95         imageWidth = paddingWidth;
       
    96         imageHeight = paddingHeight;
       
    97     }
       
    98   
       
    99     if (newImage) {
       
   100         imageWidth += newImage->image()->width();
       
   101         imageHeight += newImage->image()->height();
       
   102     }
       
   103   
       
   104     // we have an alt and the user meant it (its not a text we invented)
       
   105     if (!m_altText.isEmpty()) {
       
   106         const Font& font = style()->font();
       
   107         imageWidth = max(imageWidth, min(font.width(TextRun(m_altText.characters(), m_altText.length())), maxAltTextWidth));
       
   108 #if PLATFORM(SYMBIAN)
       
   109         // display both alt text and broken image
       
   110         imageHeight+= min(font.height(), maxAltTextHeight);
       
   111 #else
       
   112         imageHeight = max(imageHeight, min(font.height(), maxAltTextHeight));
       
   113 #endif
       
   114     }
       
   115   
       
   116     IntSize imageSize = IntSize(imageWidth, imageHeight);
       
   117     if (imageSize == intrinsicSize())
       
   118         return false;
       
   119 
       
   120     setIntrinsicSize(imageSize);
       
   121     return true;
       
   122 }
       
   123 
       
   124 void RenderImage::imageChanged(CachedImage* newImage)
       
   125 {
       
   126     if (documentBeingDestroyed())
       
   127         return;
       
   128 
       
   129     if (hasBoxDecorations())
       
   130         RenderReplaced::imageChanged(newImage);
       
   131     
       
   132     if (newImage != m_cachedImage)
       
   133         return;
       
   134 
       
   135     bool imageSizeChanged = false;
       
   136 
       
   137     // Set image dimensions, taking into account the size of the alt text.
       
   138     if (newImage->errorOccurred())
       
   139         imageSizeChanged = setImageSizeForAltText(newImage);
       
   140     
       
   141     bool shouldRepaint = true;
       
   142 
       
   143     // Image dimensions have been changed, see what needs to be done
       
   144     if (newImage->imageSize() != intrinsicSize() || imageSizeChanged) {
       
   145         if (!newImage->errorOccurred())
       
   146             setIntrinsicSize(newImage->imageSize());
       
   147 
       
   148         // In the case of generated image content using :before/:after, we might not be in the
       
   149         // render tree yet.  In that case, we don't need to worry about check for layout, since we'll get a
       
   150         // layout when we get added in to the render tree hierarchy later.
       
   151         if (containingBlock()) {
       
   152             // lets see if we need to relayout at all..
       
   153             int oldwidth = m_width;
       
   154             int oldheight = m_height;
       
   155             if (!prefWidthsDirty())
       
   156                 setPrefWidthsDirty(true);
       
   157             calcWidth();
       
   158             calcHeight();
       
   159 
       
   160             if (imageSizeChanged || m_width != oldwidth || m_height != oldheight) {
       
   161                 shouldRepaint = false;
       
   162                 if (!selfNeedsLayout())
       
   163                     setNeedsLayout(true);
       
   164             }
       
   165 
       
   166             m_width = oldwidth;
       
   167             m_height = oldheight;
       
   168         }
       
   169     }
       
   170 
       
   171     if (shouldRepaint)
       
   172         // FIXME: We always just do a complete repaint, since we always pass in the full image
       
   173         // rect at the moment anyway.
       
   174         repaintRectangle(contentBox());
       
   175 }
       
   176 
       
   177 void RenderImage::resetAnimation()
       
   178 {
       
   179     if (m_cachedImage) {
       
   180         image()->resetAnimation();
       
   181         if (!needsLayout())
       
   182             repaint();
       
   183     }
       
   184 }
       
   185 
       
   186 void RenderImage::paint(PaintInfo& paintInfo, int tx, int ty)
       
   187 {
       
   188     if (!shouldPaint(paintInfo, tx, ty))
       
   189         return;
       
   190 
       
   191     tx += m_x;
       
   192     ty += m_y;
       
   193         
       
   194     if (hasBoxDecorations() && paintInfo.phase != PaintPhaseOutline && paintInfo.phase != PaintPhaseSelfOutline) 
       
   195         paintBoxDecorations(paintInfo, tx, ty);
       
   196 
       
   197     GraphicsContext* context = paintInfo.context;
       
   198 
       
   199     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE)
       
   200         paintOutline(context, tx, ty, width(), height(), style());
       
   201 
       
   202     if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)
       
   203         return;
       
   204 
       
   205     if (!shouldPaintWithinRoot(paintInfo))
       
   206         return;
       
   207         
       
   208     bool isPrinting = document()->printing();
       
   209     bool drawSelectionTint = isSelected() && !isPrinting;
       
   210     if (paintInfo.phase == PaintPhaseSelection) {
       
   211         if (selectionState() == SelectionNone)
       
   212             return;
       
   213         drawSelectionTint = false;
       
   214     }
       
   215         
       
   216     int cWidth = contentWidth();
       
   217     int cHeight = contentHeight();
       
   218     int leftBorder = borderLeft();
       
   219     int topBorder = borderTop();
       
   220     int leftPad = paddingLeft();
       
   221     int topPad = paddingTop();
       
   222 
       
   223     if (isPrinting && !view()->printImages())
       
   224         return;
       
   225 
       
   226     if (!m_cachedImage || errorOccurred()) {
       
   227         if (paintInfo.phase == PaintPhaseSelection)
       
   228             return;
       
   229 
       
   230         if (cWidth > 2 && cHeight > 2) {
       
   231             if (!errorOccurred()) {
       
   232                 context->setStrokeStyle(SolidStroke);
       
   233                 context->setStrokeColor(Color::lightGray);
       
   234                 context->setFillColor(Color::transparent);
       
   235                 context->drawRect(IntRect(tx + leftBorder + leftPad, ty + topBorder + topPad, cWidth, cHeight));
       
   236             }
       
   237 
       
   238             bool errorPictureDrawn = false;
       
   239             int imageX = 0;
       
   240             int imageY = 0;
       
   241             int usableWidth = cWidth;
       
   242             int usableHeight = cHeight;
       
   243 
       
   244             if (errorOccurred() && !image()->isNull() && (usableWidth >= image()->width()) && (usableHeight >= image()->height())) {
       
   245                 // Center the error image, accounting for border and padding.
       
   246                 int centerX = (usableWidth - image()->width()) / 2;
       
   247                 if (centerX < 0)
       
   248                     centerX = 0;
       
   249 #if PLATFORM(SYMBIAN)
       
   250                 // push image to the bottom to make space for alt text. centerY is a little bit misleading here
       
   251                 int centerY = usableHeight - image()->height();
       
   252 #else
       
   253                 int centerY = (usableHeight - image()->height()) / 2;                
       
   254 #endif                
       
   255                 if (centerY < 0)
       
   256                     centerY = 0;
       
   257                 imageX = leftBorder + leftPad + centerX;
       
   258                 imageY = topBorder + topPad + centerY;
       
   259                 context->drawImage(image(), IntPoint(tx + imageX, ty + imageY));
       
   260                 errorPictureDrawn = true;
       
   261             }
       
   262 
       
   263             if (!m_altText.isEmpty()) {
       
   264                 DeprecatedString text = m_altText.deprecatedString();
       
   265                 text.replace('\\', backslashAsCurrencySymbol());
       
   266                 context->setFont(style()->font());
       
   267                 context->setFillColor(style()->color());
       
   268                 int ax = tx + leftBorder + leftPad;
       
   269                 int ay = ty + topBorder + topPad;
       
   270                 const Font& font = style()->font();
       
   271                 int ascent = font.ascent();
       
   272 
       
   273                 // Only draw the alt text if it'll fit within the content box,
       
   274                 // and only if it fits above the error image.
       
   275                 TextRun textRun(reinterpret_cast<const UChar*>(text.unicode()), text.length());
       
   276                 int textWidth = font.width(textRun);
       
   277                 if (errorPictureDrawn) {
       
   278                     if (usableWidth >= textWidth && font.height() <= imageY)
       
   279                         context->drawText(textRun, IntPoint(ax, ay + ascent));
       
   280                 } else if (usableWidth >= textWidth && cHeight >= font.height())
       
   281                     context->drawText(textRun, IntPoint(ax, ay + ascent));
       
   282             }
       
   283         }
       
   284     } else if (m_cachedImage && !image()->isNull()) {
       
   285 #if PLATFORM(MAC)
       
   286         if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
       
   287             paintCustomHighlight(tx - m_x, ty - m_y, style()->highlight(), true);
       
   288 #endif
       
   289 
       
   290         IntRect rect(IntPoint(tx + leftBorder + leftPad, ty + topBorder + topPad), IntSize(cWidth, cHeight));
       
   291 
       
   292         HTMLImageElement* imageElt = (element() && element()->hasTagName(imgTag)) ? static_cast<HTMLImageElement*>(element()) : 0;
       
   293         CompositeOperator compositeOperator = imageElt ? imageElt->compositeOperator() : CompositeSourceOver;
       
   294         context->drawImage(image(), rect, compositeOperator, document()->page()->inLowQualityImageInterpolationMode());
       
   295     }
       
   296 
       
   297     // draw the selection tint even if the image itself is not available
       
   298     if (drawSelectionTint)
       
   299         context->fillRect(selectionRect(), selectionBackgroundColor());
       
   300 }
       
   301 
       
   302 void RenderImage::layout()
       
   303 {
       
   304     ASSERT(needsLayout());
       
   305 
       
   306     IntRect oldBounds;
       
   307     IntRect oldOutlineBox;
       
   308     bool checkForRepaint = checkForRepaintDuringLayout();
       
   309     if (checkForRepaint) {
       
   310         oldBounds = absoluteClippedOverflowRect();
       
   311         oldOutlineBox = absoluteOutlineBox();
       
   312     }
       
   313 
       
   314     // minimum height
       
   315     m_height = m_cachedImage && m_cachedImage->errorOccurred() ? intrinsicSize().height() : 0;
       
   316 
       
   317     calcWidth();
       
   318     calcHeight();
       
   319     adjustOverflowForBoxShadow();
       
   320 
       
   321     if (checkForRepaint)
       
   322         repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox);
       
   323     
       
   324     setNeedsLayout(false);
       
   325 }
       
   326 
       
   327 HTMLMapElement* RenderImage::imageMap()
       
   328 {
       
   329     HTMLImageElement* i = element() && element()->hasTagName(imgTag) ? static_cast<HTMLImageElement*>(element()) : 0;
       
   330     return i ? i->document()->getImageMap(i->imageMap()) : 0;
       
   331 }
       
   332 
       
   333 bool RenderImage::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction)
       
   334 {
       
   335     bool inside = RenderReplaced::nodeAtPoint(request, result, _x, _y, _tx, _ty, hitTestAction);
       
   336 
       
   337     if (inside && element()) {
       
   338         int tx = _tx + m_x;
       
   339         int ty = _ty + m_y;
       
   340         
       
   341         HTMLMapElement* map = imageMap();
       
   342         if (map) {
       
   343             // we're a client side image map
       
   344             inside = map->mapMouseEvent(_x - tx, _y - ty, IntSize(contentWidth(), contentHeight()), result);
       
   345             result.setInnerNonSharedNode(element());
       
   346         }
       
   347     }
       
   348 
       
   349     return inside;
       
   350 }
       
   351 
       
   352 void RenderImage::updateAltText()
       
   353 {
       
   354     if (!element())
       
   355         return;
       
   356 
       
   357     if (element()->hasTagName(inputTag))
       
   358         m_altText = static_cast<HTMLInputElement*>(element())->altText();
       
   359     else if (element()->hasTagName(imgTag))
       
   360         m_altText = static_cast<HTMLImageElement*>(element())->altText();
       
   361 }
       
   362 
       
   363 bool RenderImage::isWidthSpecified() const
       
   364 {
       
   365     switch (style()->width().type()) {
       
   366         case Fixed:
       
   367         case Percent:
       
   368             return true;
       
   369         default:
       
   370             return false;
       
   371     }
       
   372 }
       
   373 
       
   374 bool RenderImage::isHeightSpecified() const
       
   375 {
       
   376     switch (style()->height().type()) {
       
   377         case Fixed:
       
   378         case Percent:
       
   379             return true;
       
   380         default:
       
   381             return false;
       
   382     }
       
   383 }
       
   384 
       
   385 int RenderImage::calcReplacedWidth() const
       
   386 {
       
   387     int width;
       
   388     if (isWidthSpecified())
       
   389         width = calcReplacedWidthUsing(style()->width());
       
   390     else
       
   391         width = calcAspectRatioWidth();
       
   392 
       
   393     int minW = calcReplacedWidthUsing(style()->minWidth());
       
   394     int maxW = style()->maxWidth().isUndefined() ? width : calcReplacedWidthUsing(style()->maxWidth());
       
   395 
       
   396     return max(minW, min(width, maxW));
       
   397 }
       
   398 
       
   399 int RenderImage::calcReplacedHeight() const
       
   400 {
       
   401     int height;
       
   402     if (isHeightSpecified())
       
   403         height = calcReplacedHeightUsing(style()->height());
       
   404     else
       
   405         height = calcAspectRatioHeight();
       
   406 
       
   407     int minH = calcReplacedHeightUsing(style()->minHeight());
       
   408     int maxH = style()->maxHeight().isUndefined() ? height : calcReplacedHeightUsing(style()->maxHeight());
       
   409 
       
   410     return max(minH, min(height, maxH));
       
   411 }
       
   412 
       
   413 int RenderImage::calcAspectRatioWidth() const
       
   414 {
       
   415     IntSize size = intrinsicSize();
       
   416     if (!size.height())
       
   417         return 0;
       
   418     if (!m_cachedImage || m_cachedImage->errorOccurred())
       
   419         return size.width(); // Don't bother scaling.
       
   420     return RenderReplaced::calcReplacedHeight() * size.width() / size.height();
       
   421 }
       
   422 
       
   423 int RenderImage::calcAspectRatioHeight() const
       
   424 {
       
   425     IntSize size = intrinsicSize();
       
   426     if (!size.width())
       
   427         return 0;
       
   428     if (!m_cachedImage || m_cachedImage->errorOccurred())
       
   429         return size.height(); // Don't bother scaling.
       
   430     return RenderReplaced::calcReplacedWidth() * size.height() / size.width();
       
   431 }
       
   432 
       
   433 void RenderImage::calcPrefWidths()
       
   434 {
       
   435     ASSERT(prefWidthsDirty());
       
   436 
       
   437     m_maxPrefWidth = calcReplacedWidth() + paddingLeft() + paddingRight() + borderLeft() + borderRight();
       
   438 
       
   439     if (style()->width().isPercent() || style()->height().isPercent() || 
       
   440         style()->maxWidth().isPercent() || style()->maxHeight().isPercent() ||
       
   441         style()->minWidth().isPercent() || style()->minHeight().isPercent())
       
   442         m_minPrefWidth = 0;
       
   443     else
       
   444         m_minPrefWidth = m_maxPrefWidth;
       
   445 
       
   446     setPrefWidthsDirty(false);
       
   447 }
       
   448 
       
   449 Image* RenderImage::nullImage()
       
   450 {
       
   451     static BitmapImage sharedNullImage;
       
   452     return &sharedNullImage;
       
   453 }
       
   454 
       
   455 } // namespace WebCore