RenderImage.cpp 13.1 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"
kocienda's avatar
kocienda committed
38

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

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

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

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

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

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

68
69
void RenderImage::setContentObject(CachedObject* co)
{
hyatt's avatar
hyatt committed
70
71
72
73
74
75
    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);
76
    }
kocienda's avatar
kocienda committed
77
78
}

hyatt's avatar
hyatt committed
79
void RenderImage::setCachedImage(CachedImage* newImage)
hyatt's avatar
hyatt committed
80
{
hyatt's avatar
hyatt committed
81
82
83
84
85
86
87
    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())
88
            imageChanged(m_cachedImage);
89
    }
hyatt's avatar
hyatt committed
90
91
}

92
void RenderImage::imageChanged(CachedImage* o)
kocienda's avatar
kocienda committed
93
{
hyatt's avatar
hyatt committed
94
    if (o != m_cachedImage) {
95
        RenderReplaced::imageChanged(o);
kocienda's avatar
kocienda committed
96
97
98
        return;
    }

99
    bool iwchanged = false;
kocienda's avatar
kocienda committed
100

hyatt's avatar
hyatt committed
101
    if (o->isErrorImage()) {
102
103
        int iw = o->image()->width() + 4;
        int ih = o->image()->height() + 4;
104
105

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

hyatt's avatar
hyatt committed
112
113
        if (iw != intrinsicWidth()) {
            setIntrinsicWidth(iw);
114
115
            iwchanged = true;
        }
hyatt's avatar
hyatt committed
116
117
        if (ih != intrinsicHeight()) {
            setIntrinsicHeight(ih);
118
119
            iwchanged = true;
        }
kocienda's avatar
kocienda committed
120
121
122
123
124
    }

    bool needlayout = false;

    // Image dimensions have been changed, see what needs to be done
125
    if ((o->imageSize().width() != intrinsicWidth() || o->imageSize().height() != intrinsicHeight() || iwchanged)) {
126
        if(!o->isErrorImage()) {
127
128
            setIntrinsicWidth(o->imageSize().width());
            setIntrinsicHeight(o->imageSize().height());
129
        }
kocienda's avatar
kocienda committed
130

131
132
133
134
        // 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()) {
135
136
137
138
139
140
141
142
143
144
145
146
            // 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
147
148
    }

149
150
151
152
153
    if (needlayout) {
        if (!selfNeedsLayout())
            setNeedsLayout(true);
        if (minMaxKnown())
            setMinMaxKnown(false);
kocienda's avatar
kocienda committed
154
    }
hyatt's avatar
hyatt committed
155
    else
156
        // FIXME: We always just do a complete repaint, since we always pass in the full image
157
        // rect at the moment anyway.
158
        repaintRectangle(IntRect(borderLeft()+paddingLeft(), borderTop()+paddingTop(), contentWidth(), contentHeight()));
kocienda's avatar
kocienda committed
159
160
}

rjw's avatar
rjw committed
161
162
void RenderImage::resetAnimation()
{
hyatt's avatar
hyatt committed
163
    if (m_cachedImage) {
164
        image()->resetAnimation();
hyatt's avatar
hyatt committed
165
166
167
        if (!needsLayout())
            repaint();
    }
rjw's avatar
rjw committed
168
169
}

170
void RenderImage::paint(PaintInfo& i, int _tx, int _ty)
kocienda's avatar
kocienda committed
171
{
172
    if (!shouldPaint(i, _tx, _ty)) return;
173
174
175
176

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

darin's avatar
darin committed
180
    GraphicsContext* p = i.p;
181
    
bdakin's avatar
bdakin committed
182
    if ((i.phase == PaintPhaseOutline || i.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE)
183
184
        paintOutline(p, _tx, _ty, width(), height(), style());
    
bdakin's avatar
bdakin committed
185
    if (i.phase != PaintPhaseForeground && i.phase != PaintPhaseSelection)
186
        return;
cblu's avatar
cblu committed
187

188
189
190
    if (!shouldPaintWithinRoot(i))
        return;
        
darin's avatar
darin committed
191
    bool isPrinting = document()->printing();
justing's avatar
justing committed
192
    bool drawSelectionTint = isSelected() && !isPrinting;
bdakin's avatar
bdakin committed
193
    if (i.phase == PaintPhaseSelection) {
194
        if (selectionState() == SelectionNone)
cblu's avatar
cblu committed
195
196
197
            return;
        drawSelectionTint = false;
    }
198
        
kocienda's avatar
kocienda committed
199
200
201
202
203
204
205
    int cWidth = contentWidth();
    int cHeight = contentHeight();
    int leftBorder = borderLeft();
    int topBorder = borderTop();
    int leftPad = paddingLeft();
    int topPad = paddingTop();

206
    if (isPrinting && !view()->printImages())
darin's avatar
darin committed
207
208
        return;

209
    if (!m_cachedImage || image()->isNull() || errorOccurred()) {
bdakin's avatar
bdakin committed
210
        if (i.phase == PaintPhaseSelection)
211
            return;
212

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

        if (drawSelectionTint)
            p->fillRect(selectionRect(), selectionColor(p));
kocienda's avatar
kocienda committed
270
271
272
273
274
    }
}

void RenderImage::layout()
{
275
    KHTMLAssert(needsLayout());
276
    KHTMLAssert( minMaxKnown() );
kocienda's avatar
kocienda committed
277

278
    IntRect oldBounds;
279
280
281
    bool checkForRepaint = checkForRepaintDuringLayout();
    if (checkForRepaint)
        oldBounds = getAbsoluteRepaintRect();
kocienda's avatar
kocienda committed
282
283

    // minimum height
hyatt's avatar
hyatt committed
284
    m_height = m_cachedImage && m_cachedImage->isErrorImage() ? intrinsicHeight() : 0;
kocienda's avatar
kocienda committed
285
286
287
288

    calcWidth();
    calcHeight();

289
290
    if (checkForRepaint)
        repaintAfterLayoutIfNeeded(oldBounds, oldBounds);
291
    
292
    setNeedsLayout(false);
kocienda's avatar
kocienda committed
293
294
}

darin's avatar
darin committed
295
HTMLMapElement* RenderImage::imageMap()
296
{
darin's avatar
darin committed
297
    HTMLImageElement* i = element() && element()->hasTagName(imgTag) ? static_cast<HTMLImageElement*>(element()) : 0;
ggaren's avatar
ggaren committed
298
    return i ? i->document()->getImageMap(i->imageMap()) : 0;
299
300
}

301
bool RenderImage::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction)
kocienda's avatar
kocienda committed
302
{
303
    bool inside = RenderReplaced::nodeAtPoint(info, _x, _y, _tx, _ty, hitTestAction);
304
305
306
307

    if (inside && element()) {
        int tx = _tx + m_x;
        int ty = _ty + m_y;
308
        
darin's avatar
darin committed
309
        HTMLMapElement* map = imageMap();
310
        if (map) {
311
            // we're a client side image map
312
            inside = map->mapMouseEvent(_x - tx, _y - ty, IntSize(contentWidth(), contentHeight()), info);
cblu's avatar
cblu committed
313
            info.setInnerNonSharedNode(element());
314
315
        }
    }
kocienda's avatar
kocienda committed
316

317
    return inside;
kocienda's avatar
kocienda committed
318
319
}

hyatt's avatar
hyatt committed
320
void RenderImage::updateAltText()
kocienda's avatar
kocienda committed
321
{
harrison's avatar
harrison committed
322
323
324
    if (!element())
        return;
        
325
    if (element()->hasTagName(inputTag))
darin's avatar
darin committed
326
        m_altText = static_cast<HTMLInputElement*>(element())->altText();
327
    else if (element()->hasTagName(imgTag))
darin's avatar
darin committed
328
        m_altText = static_cast<HTMLImageElement*>(element())->altText();
kocienda's avatar
kocienda committed
329
}
darin's avatar
darin committed
330
331
332

bool RenderImage::isWidthSpecified() const
{
darin's avatar
darin committed
333
    switch (style()->width().type()) {
darin's avatar
darin committed
334
335
336
        case Fixed:
        case Percent:
            return true;
337
        default:
darin's avatar
darin committed
338
339
340
341
342
343
344
345
            return false;
    }
    assert(false);
    return false;
}

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

357
int RenderImage::calcReplacedWidth() const
darin's avatar
darin committed
358
{
ggaren's avatar
ggaren committed
359
360
361
362
363
364
365
366
367
    int width;
    if (isWidthSpecified())
        width = calcReplacedWidthUsing(Width);
    else
        width = calcAspectRatioWidth();

    int minW = calcReplacedWidthUsing(MinWidth);
    int maxW = style()->maxWidth().value() == undefinedLength ? width : calcReplacedWidthUsing(MaxWidth);

darin's avatar
darin committed
368
    return max(minW, min(width, maxW));
darin's avatar
darin committed
369
370
371
372
}

int RenderImage::calcReplacedHeight() const
{
ggaren's avatar
ggaren committed
373
374
375
376
377
378
379
380
381
    int height;
    if (isHeightSpecified())
        height = calcReplacedHeightUsing(Height);
    else
        height = calcAspectRatioHeight();

    int minH = calcReplacedHeightUsing(MinHeight);
    int maxH = style()->maxHeight().value() == undefinedLength ? height : calcReplacedHeightUsing(MaxHeight);

darin's avatar
darin committed
382
    return max(minH, min(height, maxH));
ggaren's avatar
ggaren committed
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
}

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
417
}
darin's avatar
darin committed
418

419
Image* RenderImage::nullImage()
darin's avatar
darin committed
420
421
{
    static Image sharedNullImage;
422
    return &sharedNullImage;
darin's avatar
darin committed
423
424
}

darin's avatar
darin committed
425
}