RenderImage.cpp 13.5 KB
Newer Older
kocienda's avatar
kocienda committed
1
2
3
4
5
6
/**
 * This file is part of the DOM implementation for KDE.
 *
 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
 *           (C) 2000 Dirk Mueller (mueller@kde.org)
ggaren's avatar
ggaren committed
7
8
 *           (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com)
 *           (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
darin's avatar
darin committed
9
 * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc.
kocienda's avatar
kocienda committed
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 */

mjs's avatar
mjs committed
28
#include "config.h"
darin's avatar
darin committed
29
#include "RenderImage.h"
kocienda's avatar
kocienda committed
30

darin's avatar
darin committed
31
#include "Document.h"
darin's avatar
darin committed
32
#include "GraphicsContext.h"
33
#include "HTMLImageElement.h"
darin's avatar
darin committed
34
#include "HTMLInputElement.h"
35
#include "HTMLMapElement.h"
darin's avatar
darin committed
36
#include "HTMLNames.h"
37
#include "RenderView.h"
38
#include "TextStyle.h"
kocienda's avatar
kocienda committed
39

darin's avatar
darin committed
40
41
using namespace std;

darin's avatar
darin committed
42
namespace WebCore {
eseidel's avatar
eseidel committed
43

44
using namespace HTMLNames;
kocienda's avatar
kocienda committed
45

46
47
48
RenderImage::RenderImage(Node* n)
    : RenderReplaced(n)
    , m_cachedImage(0)
kocienda's avatar
kocienda committed
49
{
cblu's avatar
cblu committed
50
    m_selectionState = SelectionNone;
hyatt's avatar
hyatt committed
51
52
    setIntrinsicWidth(0);
    setIntrinsicHeight(0);
bdakin's avatar
bdakin committed
53
    m_isAnonymousImage = 0;
harrison's avatar
harrison committed
54
    updateAltText();
kocienda's avatar
kocienda committed
55
56
57
58
}

RenderImage::~RenderImage()
{
hyatt's avatar
hyatt committed
59
60
    if (m_cachedImage)
        m_cachedImage->deref(this);
kocienda's avatar
kocienda committed
61
62
63
64
65
}

void RenderImage::setStyle(RenderStyle* _style)
{
    RenderReplaced::setStyle(_style);
66
    
67
    setShouldPaintBackgroundOrBorder(true);
68
}
69

70
void RenderImage::setContentObject(CachedResource* co)
71
{
hyatt's avatar
hyatt committed
72
73
74
75
76
77
    if (co && m_cachedImage != co) {
        if (m_cachedImage)
            m_cachedImage->deref(this);
        m_cachedImage = static_cast<CachedImage*>(co);
        if (m_cachedImage)
            m_cachedImage->ref(this);
78
    }
kocienda's avatar
kocienda committed
79
80
}

hyatt's avatar
hyatt committed
81
void RenderImage::setCachedImage(CachedImage* newImage)
hyatt's avatar
hyatt committed
82
{
bdakin's avatar
bdakin committed
83
84
85
    if (isAnonymousImage())
        return;
    
hyatt's avatar
hyatt committed
86
87
88
89
90
91
92
    if (m_cachedImage != newImage) {
        if (m_cachedImage)
            m_cachedImage->deref(this);
        m_cachedImage = newImage;
        if (m_cachedImage)
            m_cachedImage->ref(this);
        if (m_cachedImage->isErrorImage())
93
            imageChanged(m_cachedImage);
94
    }
hyatt's avatar
hyatt committed
95
96
}

97
void RenderImage::imageChanged(CachedImage* o)
kocienda's avatar
kocienda committed
98
{
99
100
101
    if (documentBeingDestroyed())
        return;

hyatt's avatar
hyatt committed
102
    if (o != m_cachedImage) {
103
        RenderReplaced::imageChanged(o);
kocienda's avatar
kocienda committed
104
105
106
        return;
    }

107
    bool iwchanged = false;
kocienda's avatar
kocienda committed
108

hyatt's avatar
hyatt committed
109
    if (o->isErrorImage()) {
110
111
        int iw = o->image()->width() + 4;
        int ih = o->image()->height() + 4;
112
113

        // we have an alt and the user meant it (its not a text we invented)
hyatt's avatar
hyatt committed
114
        if (!m_altText.isEmpty()) {
115
            const Font& font = style()->font();
116
            iw = max(iw, min(font.width(TextRun(m_altText.characters(), m_altText.length())), 1024));
darin's avatar
darin committed
117
            ih = max(ih, min(font.height(), 256));
118
119
        }

hyatt's avatar
hyatt committed
120
121
        if (iw != intrinsicWidth()) {
            setIntrinsicWidth(iw);
122
123
            iwchanged = true;
        }
hyatt's avatar
hyatt committed
124
125
        if (ih != intrinsicHeight()) {
            setIntrinsicHeight(ih);
126
127
            iwchanged = true;
        }
kocienda's avatar
kocienda committed
128
129
130
131
132
    }

    bool needlayout = false;

    // Image dimensions have been changed, see what needs to be done
133
    if ((o->imageSize().width() != intrinsicWidth() || o->imageSize().height() != intrinsicHeight() || iwchanged)) {
134
        if(!o->isErrorImage()) {
135
136
            setIntrinsicWidth(o->imageSize().width());
            setIntrinsicHeight(o->imageSize().height());
137
        }
kocienda's avatar
kocienda committed
138

139
140
141
142
        // In the case of generated image content using :before/:after, we might not be in the
        // render tree yet.  In that case, we don't need to worry about check for layout, since we'll get a
        // layout when we get added in to the render tree hierarchy later.
        if (containingBlock()) {
143
144
145
146
147
148
149
150
151
152
153
154
            // lets see if we need to relayout at all..
            int oldwidth = m_width;
            int oldheight = m_height;
            calcWidth();
            calcHeight();
    
            if(iwchanged || m_width != oldwidth || m_height != oldheight)
                needlayout = true;
    
            m_width = oldwidth;
            m_height = oldheight;
        }
kocienda's avatar
kocienda committed
155
156
    }

157
158
159
160
161
    if (needlayout) {
        if (!selfNeedsLayout())
            setNeedsLayout(true);
        if (minMaxKnown())
            setMinMaxKnown(false);
kocienda's avatar
kocienda committed
162
    }
hyatt's avatar
hyatt committed
163
    else
164
        // FIXME: We always just do a complete repaint, since we always pass in the full image
165
        // rect at the moment anyway.
166
        repaintRectangle(IntRect(borderLeft()+paddingLeft(), borderTop()+paddingTop(), contentWidth(), contentHeight()));
kocienda's avatar
kocienda committed
167
168
}

rjw's avatar
rjw committed
169
170
void RenderImage::resetAnimation()
{
hyatt's avatar
hyatt committed
171
    if (m_cachedImage) {
172
        image()->resetAnimation();
hyatt's avatar
hyatt committed
173
174
175
        if (!needsLayout())
            repaint();
    }
rjw's avatar
rjw committed
176
177
}

178
void RenderImage::paint(PaintInfo& i, int _tx, int _ty)
kocienda's avatar
kocienda committed
179
{
180
    if (!shouldPaint(i, _tx, _ty)) return;
181
182
183
184

    _tx += m_x;
    _ty += m_y;
        
bdakin's avatar
bdakin committed
185
    if (shouldPaintBackgroundOrBorder() && i.phase != PaintPhaseOutline && i.phase != PaintPhaseSelfOutline) 
186
187
        paintBoxDecorations(i, _tx, _ty);

darin's avatar
darin committed
188
    GraphicsContext* p = i.p;
189
    
bdakin's avatar
bdakin committed
190
    if ((i.phase == PaintPhaseOutline || i.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE)
191
192
        paintOutline(p, _tx, _ty, width(), height(), style());
    
bdakin's avatar
bdakin committed
193
    if (i.phase != PaintPhaseForeground && i.phase != PaintPhaseSelection)
194
        return;
cblu's avatar
cblu committed
195

196
197
198
    if (!shouldPaintWithinRoot(i))
        return;
        
darin's avatar
darin committed
199
    bool isPrinting = document()->printing();
justing's avatar
justing committed
200
    bool drawSelectionTint = isSelected() && !isPrinting;
bdakin's avatar
bdakin committed
201
    if (i.phase == PaintPhaseSelection) {
202
        if (selectionState() == SelectionNone)
cblu's avatar
cblu committed
203
204
205
            return;
        drawSelectionTint = false;
    }
206
        
kocienda's avatar
kocienda committed
207
208
209
210
211
212
213
    int cWidth = contentWidth();
    int cHeight = contentHeight();
    int leftBorder = borderLeft();
    int topBorder = borderTop();
    int leftPad = paddingLeft();
    int topPad = paddingTop();

214
    if (isPrinting && !view()->printImages())
darin's avatar
darin committed
215
216
        return;

217
    if (!m_cachedImage || image()->isNull() || errorOccurred()) {
bdakin's avatar
bdakin committed
218
        if (i.phase == PaintPhaseSelection)
219
            return;
220

hyatt's avatar
hyatt committed
221
222
        if (cWidth > 2 && cHeight > 2) {
            if (!errorOccurred()) {
darin's avatar
darin committed
223
224
225
                p->setPen(Color::lightGray);
                p->setFillColor(Color::transparent);
                p->drawRect(IntRect(_tx + leftBorder + leftPad, _ty + topBorder + topPad, cWidth, cHeight));
226
            }
cblu's avatar
cblu committed
227
            
rjw's avatar
WebKit:    
rjw committed
228
229
            bool errorPictureDrawn = false;
            int imageX = 0, imageY = 0;
230
231
            int usableWidth = cWidth;
            int usableHeight = cHeight;
rjw's avatar
WebKit:    
rjw committed
232
            
233
            if (errorOccurred() && !image()->isNull() && (usableWidth >= image()->width()) && (usableHeight >= image()->height())) {
rjw's avatar
WebKit:    
rjw committed
234
                // Center the error image, accounting for border and padding.
235
                int centerX = (usableWidth - image()->width())/2;
rjw's avatar
WebKit:    
rjw committed
236
237
                if (centerX < 0)
                    centerX = 0;
238
                int centerY = (usableHeight - image()->height())/2;
rjw's avatar
WebKit:    
rjw committed
239
240
241
242
                if (centerY < 0)
                    centerY = 0;
                imageX = leftBorder + leftPad + centerX;
                imageY = topBorder + topPad + centerY;
darin's avatar
darin committed
243
                p->drawImage(image(), IntPoint(_tx + imageX, _ty + imageY));
rjw's avatar
WebKit:    
rjw committed
244
245
246
                errorPictureDrawn = true;
            }
            
hyatt's avatar
hyatt committed
247
            if (!m_altText.isEmpty()) {
darin's avatar
darin committed
248
                DeprecatedString text = m_altText.deprecatedString();
darin's avatar
darin committed
249
                text.replace('\\', backslashAsCurrencySymbol());
250
                p->setFont(style()->font());
251
                p->setPen(style()->color());
rjw's avatar
WebKit:    
rjw committed
252
253
                int ax = _tx + leftBorder + leftPad;
                int ay = _ty + topBorder + topPad;
254
255
                const Font& font = style()->font();
                int ascent = font.ascent();
rjw's avatar
WebKit:    
rjw committed
256
257
258
                
                // Only draw the alt text if it'll fit within the content box,
                // and only if it fits above the error image.
259
260
                TextRun textRun(reinterpret_cast<const UChar*>(text.unicode()), text.length());
                int textWidth = font.width(textRun);
261
                if (errorPictureDrawn) {
262
                    if (usableWidth >= textWidth && font.height() <= imageY)
263
                        p->drawText(textRun, IntPoint(ax, ay + ascent));
264
                } else if (usableWidth >= textWidth && cHeight >= font.height())
265
                    p->drawText(textRun, IntPoint(ax, ay + ascent));
rjw's avatar
WebKit:    
rjw committed
266
            }
kocienda's avatar
kocienda committed
267
        }
harrison's avatar
harrison committed
268
    } else if (m_cachedImage) {
harrison's avatar
harrison committed
269
270
271
272
273
#if PLATFORM(MAC)
        if (style()->highlight() != nullAtom && !i.p->paintingDisabled())
            paintCustomHighlight(_tx - m_x, _ty - m_y, style()->highlight(), true);
#endif

hyatt's avatar
hyatt committed
274
275
        IntRect rect(IntPoint(_tx + leftBorder + leftPad, _ty + topBorder + topPad), IntSize(cWidth, cHeight));
        
darin's avatar
darin committed
276
        HTMLImageElement* imageElt = (element() && element()->hasTagName(imgTag)) ? static_cast<HTMLImageElement*>(element()) : 0;
darin's avatar
darin committed
277
278
        CompositeOperator compositeOperator = imageElt ? imageElt->compositeOperator() : CompositeSourceOver;
        p->drawImage(image(), rect, compositeOperator);
hyatt's avatar
hyatt committed
279
280

        if (drawSelectionTint)
281
            p->fillRect(selectionRect(), selectionBackgroundColor());
kocienda's avatar
kocienda committed
282
283
284
285
286
    }
}

void RenderImage::layout()
{
darin's avatar
darin committed
287
288
    ASSERT(needsLayout());
    ASSERT( minMaxKnown() );
kocienda's avatar
kocienda committed
289

290
    IntRect oldBounds;
291
292
293
    bool checkForRepaint = checkForRepaintDuringLayout();
    if (checkForRepaint)
        oldBounds = getAbsoluteRepaintRect();
kocienda's avatar
kocienda committed
294
295

    // minimum height
hyatt's avatar
hyatt committed
296
    m_height = m_cachedImage && m_cachedImage->isErrorImage() ? intrinsicHeight() : 0;
kocienda's avatar
kocienda committed
297
298
299
300

    calcWidth();
    calcHeight();

301
302
    if (checkForRepaint)
        repaintAfterLayoutIfNeeded(oldBounds, oldBounds);
303
    
304
    setNeedsLayout(false);
kocienda's avatar
kocienda committed
305
306
}

darin's avatar
darin committed
307
HTMLMapElement* RenderImage::imageMap()
308
{
darin's avatar
darin committed
309
    HTMLImageElement* i = element() && element()->hasTagName(imgTag) ? static_cast<HTMLImageElement*>(element()) : 0;
ggaren's avatar
ggaren committed
310
    return i ? i->document()->getImageMap(i->imageMap()) : 0;
311
312
}

313
bool RenderImage::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction)
kocienda's avatar
kocienda committed
314
{
315
    bool inside = RenderReplaced::nodeAtPoint(info, _x, _y, _tx, _ty, hitTestAction);
316
317
318
319

    if (inside && element()) {
        int tx = _tx + m_x;
        int ty = _ty + m_y;
320
        
darin's avatar
darin committed
321
        HTMLMapElement* map = imageMap();
322
        if (map) {
323
            // we're a client side image map
324
            inside = map->mapMouseEvent(_x - tx, _y - ty, IntSize(contentWidth(), contentHeight()), info);
cblu's avatar
cblu committed
325
            info.setInnerNonSharedNode(element());
326
327
        }
    }
kocienda's avatar
kocienda committed
328

329
    return inside;
kocienda's avatar
kocienda committed
330
331
}

hyatt's avatar
hyatt committed
332
void RenderImage::updateAltText()
kocienda's avatar
kocienda committed
333
{
harrison's avatar
harrison committed
334
335
336
    if (!element())
        return;
        
337
    if (element()->hasTagName(inputTag))
darin's avatar
darin committed
338
        m_altText = static_cast<HTMLInputElement*>(element())->altText();
339
    else if (element()->hasTagName(imgTag))
darin's avatar
darin committed
340
        m_altText = static_cast<HTMLImageElement*>(element())->altText();
kocienda's avatar
kocienda committed
341
}
darin's avatar
darin committed
342
343
344

bool RenderImage::isWidthSpecified() const
{
darin's avatar
darin committed
345
    switch (style()->width().type()) {
darin's avatar
darin committed
346
347
348
        case Fixed:
        case Percent:
            return true;
349
        default:
darin's avatar
darin committed
350
351
352
353
354
355
356
357
            return false;
    }
    assert(false);
    return false;
}

bool RenderImage::isHeightSpecified() const
{
darin's avatar
darin committed
358
    switch (style()->height().type()) {
darin's avatar
darin committed
359
360
361
        case Fixed:
        case Percent:
            return true;
362
        default:
darin's avatar
darin committed
363
364
365
366
367
368
            return false;
    }
    assert(false);
    return false;
}

369
int RenderImage::calcReplacedWidth() const
darin's avatar
darin committed
370
{
ggaren's avatar
ggaren committed
371
372
    int width;
    if (isWidthSpecified())
adele's avatar
adele committed
373
        width = calcReplacedWidthUsing(style()->width());
ggaren's avatar
ggaren committed
374
375
376
    else
        width = calcAspectRatioWidth();

adele's avatar
adele committed
377
378
    int minW = calcReplacedWidthUsing(style()->minWidth());
    int maxW = style()->maxWidth().value() == undefinedLength ? width : calcReplacedWidthUsing(style()->maxWidth());
ggaren's avatar
ggaren committed
379

darin's avatar
darin committed
380
    return max(minW, min(width, maxW));
darin's avatar
darin committed
381
382
383
384
}

int RenderImage::calcReplacedHeight() const
{
ggaren's avatar
ggaren committed
385
386
    int height;
    if (isHeightSpecified())
adele's avatar
adele committed
387
        height = calcReplacedHeightUsing(style()->height());
ggaren's avatar
ggaren committed
388
389
390
    else
        height = calcAspectRatioHeight();

adele's avatar
adele committed
391
392
    int minH = calcReplacedHeightUsing(style()->minHeight());
    int maxH = style()->maxHeight().value() == undefinedLength ? height : calcReplacedHeightUsing(style()->maxHeight());
ggaren's avatar
ggaren committed
393

darin's avatar
darin committed
394
    return max(minH, min(height, maxH));
ggaren's avatar
ggaren committed
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
}

int RenderImage::calcAspectRatioWidth() const
{
    if (!intrinsicHeight())
        return 0;
    if (!m_cachedImage || m_cachedImage->isErrorImage())
        return intrinsicWidth(); // Don't bother scaling.
    return RenderReplaced::calcReplacedHeight() * intrinsicWidth() / intrinsicHeight();
}

int RenderImage::calcAspectRatioHeight() const
{
    if (!intrinsicWidth())
        return 0;
    if (!m_cachedImage || m_cachedImage->isErrorImage())
        return intrinsicHeight(); // Don't bother scaling.
    return RenderReplaced::calcReplacedWidth() * intrinsicHeight() / intrinsicWidth();
}

void RenderImage::calcMinMaxWidth()
{
    ASSERT(!minMaxKnown());

    m_maxWidth = 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_minWidth = 0;
    else
        m_minWidth = m_maxWidth;

    setMinMaxKnown();
darin's avatar
darin committed
429
}
darin's avatar
darin committed
430

431
Image* RenderImage::nullImage()
darin's avatar
darin committed
432
433
{
    static Image sharedNullImage;
434
    return &sharedNullImage;
darin's avatar
darin committed
435
436
}

darin's avatar
darin committed
437
}