LayoutTests:
[WebKit-https.git] / WebCore / rendering / RenderImage.cpp
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., 59 Temple Place - Suite 330,
24  * Boston, MA 02111-1307, 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 "RenderView.h"
40 #include "TextStyle.h"
41
42 using namespace std;
43
44 namespace WebCore {
45
46 using namespace HTMLNames;
47
48 RenderImage::RenderImage(Node* node)
49     : RenderReplaced(node)
50     , m_cachedImage(0)
51     , m_isAnonymousImage(false)
52 {
53     setIntrinsicWidth(0);
54     setIntrinsicHeight(0);
55     updateAltText();
56 }
57
58 RenderImage::~RenderImage()
59 {
60     if (m_cachedImage)
61         m_cachedImage->deref(this);
62 }
63
64 void RenderImage::setCachedImage(CachedImage* newImage)
65 {
66     if (m_isAnonymousImage || m_cachedImage == newImage)
67         return;
68     if (m_cachedImage)
69         m_cachedImage->deref(this);
70     m_cachedImage = newImage;
71     if (m_cachedImage) {
72         m_cachedImage->ref(this);
73         if (m_cachedImage->isErrorImage())
74             imageChanged(m_cachedImage);
75     }
76 }
77
78 // If we'll be displaying either alt text or an image, add some padding.
79 static const unsigned short paddingWidth = 4;
80 static const unsigned short paddingHeight = 4;
81
82 // Alt text is restricted to this maximum size, in pixels.  These are
83 // signed integers because they are compared with other signed values.
84 static const int maxAltTextWidth = 1024;
85 static const int maxAltTextHeight = 256;
86
87 // Sets the image height and width to fit the alt text.  Returns true if the
88 // image size changed.
89 bool RenderImage::setImageSizeForAltText(CachedImage* newImage /* = 0 */)
90 {
91     int imageWidth = 0;
92     int imageHeight = 0;
93   
94     // If we'll be displaying either text or an image, add a little padding.
95     if (!m_altText.isEmpty() || newImage) {
96         imageWidth = paddingWidth;
97         imageHeight = paddingHeight;
98     }
99   
100     if (newImage) {
101         imageWidth += newImage->image()->width();
102         imageHeight += newImage->image()->height();
103     }
104   
105     // we have an alt and the user meant it (its not a text we invented)
106     if (!m_altText.isEmpty()) {
107         const Font& font = style()->font();
108         imageWidth = max(imageWidth, min(font.width(TextRun(m_altText.characters(), m_altText.length())), maxAltTextWidth));
109         imageHeight = max(imageHeight, min(font.height(), maxAltTextHeight));
110     }
111   
112     bool imageSizeChanged = false;
113   
114     if (imageWidth != intrinsicWidth()) {
115         setIntrinsicWidth(imageWidth);
116         imageSizeChanged = true;
117     }
118     if (imageHeight != intrinsicHeight()) {
119         setIntrinsicHeight(imageHeight);
120         imageSizeChanged = true;
121     }
122   
123     return imageSizeChanged;
124 }
125
126 void RenderImage::imageChanged(CachedImage* newImage)
127 {
128     if (documentBeingDestroyed())
129         return;
130
131     if (newImage != m_cachedImage) {
132         RenderReplaced::imageChanged(newImage);
133         return;
134     }
135
136     bool imageSizeChanged = false;
137
138     // Set image dimensions, taking into account the size of the alt text.
139     if (newImage->isErrorImage())
140         imageSizeChanged = setImageSizeForAltText(newImage);
141     
142     bool ensureLayout = false;
143
144     // Image dimensions have been changed, see what needs to be done
145     if (newImage->imageSize().width() != intrinsicWidth() || newImage->imageSize().height() != intrinsicHeight() || imageSizeChanged) {
146         if (!newImage->isErrorImage()) {
147             setIntrinsicWidth(newImage->imageSize().width());
148             setIntrinsicHeight(newImage->imageSize().height());
149         }
150
151         // In the case of generated image content using :before/:after, we might not be in the
152         // render tree yet.  In that case, we don't need to worry about check for layout, since we'll get a
153         // layout when we get added in to the render tree hierarchy later.
154         if (containingBlock()) {
155             // lets see if we need to relayout at all..
156             int oldwidth = m_width;
157             int oldheight = m_height;
158             calcWidth();
159             calcHeight();
160     
161             if (imageSizeChanged || m_width != oldwidth || m_height != oldheight)
162                 ensureLayout = true;
163
164             m_width = oldwidth;
165             m_height = oldheight;
166         }
167     }
168
169     if (ensureLayout) {
170         if (!selfNeedsLayout())
171             setNeedsLayout(true);
172         if (minMaxKnown())
173             setMinMaxKnown(false);
174     } else
175         // FIXME: We always just do a complete repaint, since we always pass in the full image
176         // rect at the moment anyway.
177         repaintRectangle(IntRect(borderLeft() + paddingLeft(), borderTop() + paddingTop(), contentWidth(), contentHeight()));
178 }
179
180 void RenderImage::resetAnimation()
181 {
182     if (m_cachedImage) {
183         image()->resetAnimation();
184         if (!needsLayout())
185             repaint();
186     }
187 }
188
189 void RenderImage::paint(PaintInfo& paintInfo, int tx, int ty)
190 {
191     if (!shouldPaint(paintInfo, tx, ty))
192         return;
193
194     tx += m_x;
195     ty += m_y;
196         
197     if (hasBoxDecorations() && paintInfo.phase != PaintPhaseOutline && paintInfo.phase != PaintPhaseSelfOutline) 
198         paintBoxDecorations(paintInfo, tx, ty);
199
200     GraphicsContext* context = paintInfo.context;
201
202     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE)
203         paintOutline(context, tx, ty, width(), height(), style());
204
205     if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)
206         return;
207
208     if (!shouldPaintWithinRoot(paintInfo))
209         return;
210         
211     bool isPrinting = document()->printing();
212     bool drawSelectionTint = isSelected() && !isPrinting;
213     if (paintInfo.phase == PaintPhaseSelection) {
214         if (selectionState() == SelectionNone)
215             return;
216         drawSelectionTint = false;
217     }
218         
219     int cWidth = contentWidth();
220     int cHeight = contentHeight();
221     int leftBorder = borderLeft();
222     int topBorder = borderTop();
223     int leftPad = paddingLeft();
224     int topPad = paddingTop();
225
226     if (isPrinting && !view()->printImages())
227         return;
228
229     if (!m_cachedImage || image()->isNull() || isErrorImage()) {
230         if (paintInfo.phase == PaintPhaseSelection)
231             return;
232
233         if (cWidth > 2 && cHeight > 2) {
234             if (!isErrorImage()) {
235                 context->setStrokeStyle(SolidStroke);
236                 context->setStrokeColor(Color::lightGray);
237                 context->setFillColor(Color::transparent);
238                 context->drawRect(IntRect(tx + leftBorder + leftPad, ty + topBorder + topPad, cWidth, cHeight));
239             }
240
241             bool errorPictureDrawn = false;
242             int imageX = 0;
243             int imageY = 0;
244             int usableWidth = cWidth;
245             int usableHeight = cHeight;
246
247             if (isErrorImage() && !image()->isNull() && (usableWidth >= image()->width()) && (usableHeight >= image()->height())) {
248                 // Center the error image, accounting for border and padding.
249                 int centerX = (usableWidth - image()->width()) / 2;
250                 if (centerX < 0)
251                     centerX = 0;
252                 int centerY = (usableHeight - image()->height()) / 2;
253                 if (centerY < 0)
254                     centerY = 0;
255                 imageX = leftBorder + leftPad + centerX;
256                 imageY = topBorder + topPad + centerY;
257                 context->drawImage(image(), IntPoint(tx + imageX, ty + imageY));
258                 errorPictureDrawn = true;
259             }
260
261             if (!m_altText.isEmpty()) {
262                 DeprecatedString text = m_altText.deprecatedString();
263                 text.replace('\\', backslashAsCurrencySymbol());
264                 context->setFont(style()->font());
265                 context->setFillColor(style()->color());
266                 int ax = tx + leftBorder + leftPad;
267                 int ay = ty + topBorder + topPad;
268                 const Font& font = style()->font();
269                 int ascent = font.ascent();
270
271                 // Only draw the alt text if it'll fit within the content box,
272                 // and only if it fits above the error image.
273                 TextRun textRun(reinterpret_cast<const UChar*>(text.unicode()), text.length());
274                 int textWidth = font.width(textRun);
275                 if (errorPictureDrawn) {
276                     if (usableWidth >= textWidth && font.height() <= imageY)
277                         context->drawText(textRun, IntPoint(ax, ay + ascent));
278                 } else if (usableWidth >= textWidth && cHeight >= font.height())
279                     context->drawText(textRun, IntPoint(ax, ay + ascent));
280             }
281         }
282     } else if (m_cachedImage) {
283 #if PLATFORM(MAC)
284         if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
285             paintCustomHighlight(tx - m_x, ty - m_y, style()->highlight(), true);
286 #endif
287
288         IntRect rect(IntPoint(tx + leftBorder + leftPad, ty + topBorder + topPad), IntSize(cWidth, cHeight));
289
290         HTMLImageElement* imageElt = (element() && element()->hasTagName(imgTag)) ? static_cast<HTMLImageElement*>(element()) : 0;
291         CompositeOperator compositeOperator = imageElt ? imageElt->compositeOperator() : CompositeSourceOver;
292         context->drawImage(image(), rect, compositeOperator);
293     }
294
295     // draw the selection tint even if the image itself is not available
296     if (drawSelectionTint)
297         context->fillRect(selectionRect(), selectionBackgroundColor());
298 }
299
300 void RenderImage::layout()
301 {
302     ASSERT(needsLayout());
303     ASSERT(minMaxKnown());
304
305     IntRect oldBounds;
306     bool checkForRepaint = checkForRepaintDuringLayout();
307     if (checkForRepaint) {
308         oldBounds = getAbsoluteRepaintRect();
309         oldBounds.move(view()->layoutDelta());
310     }
311
312     // minimum height
313     m_height = m_cachedImage && m_cachedImage->isErrorImage() ? intrinsicHeight() : 0;
314
315     calcWidth();
316     calcHeight();
317
318     if (checkForRepaint)
319         repaintAfterLayoutIfNeeded(oldBounds);
320     
321     setNeedsLayout(false);
322 }
323
324 HTMLMapElement* RenderImage::imageMap()
325 {
326     HTMLImageElement* i = element() && element()->hasTagName(imgTag) ? static_cast<HTMLImageElement*>(element()) : 0;
327     return i ? i->document()->getImageMap(i->imageMap()) : 0;
328 }
329
330 bool RenderImage::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction)
331 {
332     bool inside = RenderReplaced::nodeAtPoint(request, result, _x, _y, _tx, _ty, hitTestAction);
333
334     if (inside && element()) {
335         int tx = _tx + m_x;
336         int ty = _ty + m_y;
337         
338         HTMLMapElement* map = imageMap();
339         if (map) {
340             // we're a client side image map
341             inside = map->mapMouseEvent(_x - tx, _y - ty, IntSize(contentWidth(), contentHeight()), result);
342             result.setInnerNonSharedNode(element());
343         }
344     }
345
346     return inside;
347 }
348
349 void RenderImage::updateAltText()
350 {
351     if (!element())
352         return;
353
354     if (element()->hasTagName(inputTag))
355         m_altText = static_cast<HTMLInputElement*>(element())->altText();
356     else if (element()->hasTagName(imgTag))
357         m_altText = static_cast<HTMLImageElement*>(element())->altText();
358 }
359
360 bool RenderImage::isWidthSpecified() const
361 {
362     switch (style()->width().type()) {
363         case Fixed:
364         case Percent:
365             return true;
366         default:
367             return false;
368     }
369     assert(false);
370     return false;
371 }
372
373 bool RenderImage::isHeightSpecified() const
374 {
375     switch (style()->height().type()) {
376         case Fixed:
377         case Percent:
378             return true;
379         default:
380             return false;
381     }
382     assert(false);
383     return false;
384 }
385
386 int RenderImage::calcReplacedWidth() const
387 {
388     int width;
389     if (isWidthSpecified())
390         width = calcReplacedWidthUsing(style()->width());
391     else
392         width = calcAspectRatioWidth();
393
394     int minW = calcReplacedWidthUsing(style()->minWidth());
395     int maxW = style()->maxWidth().isUndefined() ? width : calcReplacedWidthUsing(style()->maxWidth());
396
397     return max(minW, min(width, maxW));
398 }
399
400 int RenderImage::calcReplacedHeight() const
401 {
402     int height;
403     if (isHeightSpecified())
404         height = calcReplacedHeightUsing(style()->height());
405     else
406         height = calcAspectRatioHeight();
407
408     int minH = calcReplacedHeightUsing(style()->minHeight());
409     int maxH = style()->maxHeight().isUndefined() ? height : calcReplacedHeightUsing(style()->maxHeight());
410
411     return max(minH, min(height, maxH));
412 }
413
414 int RenderImage::calcAspectRatioWidth() const
415 {
416     if (!intrinsicHeight())
417         return 0;
418     if (!m_cachedImage || m_cachedImage->isErrorImage())
419         return intrinsicWidth(); // Don't bother scaling.
420     return RenderReplaced::calcReplacedHeight() * intrinsicWidth() / intrinsicHeight();
421 }
422
423 int RenderImage::calcAspectRatioHeight() const
424 {
425     if (!intrinsicWidth())
426         return 0;
427     if (!m_cachedImage || m_cachedImage->isErrorImage())
428         return intrinsicHeight(); // Don't bother scaling.
429     return RenderReplaced::calcReplacedWidth() * intrinsicHeight() / intrinsicWidth();
430 }
431
432 void RenderImage::calcMinMaxWidth()
433 {
434     ASSERT(!minMaxKnown());
435
436     m_maxWidth = calcReplacedWidth() + paddingLeft() + paddingRight() + borderLeft() + borderRight();
437
438     if (style()->width().isPercent() || style()->height().isPercent() || 
439         style()->maxWidth().isPercent() || style()->maxHeight().isPercent() ||
440         style()->minWidth().isPercent() || style()->minHeight().isPercent())
441         m_minWidth = 0;
442     else
443         m_minWidth = m_maxWidth;
444
445     setMinMaxKnown();
446 }
447
448 Image* RenderImage::nullImage()
449 {
450     static BitmapImage sharedNullImage;
451     return &sharedNullImage;
452 }
453
454 } // namespace WebCore