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