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