Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(86)

Side by Side Diff: Source/core/paint/BoxPainter.cpp

Issue 559733005: Move painting code from RenderBoxModelObject into BoxPainter. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "config.h" 5 #include "config.h"
6 #include "core/paint/BoxPainter.h" 6 #include "core/paint/BoxPainter.h"
7 7
8 #include "core/HTMLNames.h"
9 #include "core/frame/Settings.h"
10 #include "core/html/HTMLFrameOwnerElement.h"
11 #include "core/paint/BackgroundImageGeometry.h"
8 #include "core/paint/BoxDecorationData.h" 12 #include "core/paint/BoxDecorationData.h"
13 #include "core/rendering/ImageQualityController.h"
9 #include "core/rendering/PaintInfo.h" 14 #include "core/rendering/PaintInfo.h"
10 #include "core/rendering/RenderBox.h" 15 #include "core/rendering/RenderBox.h"
16 #include "core/rendering/RenderBoxModelObject.h"
11 #include "core/rendering/RenderLayer.h" 17 #include "core/rendering/RenderLayer.h"
12 #include "core/rendering/RenderTable.h" 18 #include "core/rendering/RenderTable.h"
13 #include "core/rendering/RenderTheme.h" 19 #include "core/rendering/RenderTheme.h"
14 #include "core/rendering/RenderView.h" 20 #include "core/rendering/RenderView.h"
21 #include "core/rendering/compositing/CompositedLayerMapping.h"
22 #include "core/rendering/style/ShadowList.h"
23 #include "platform/LengthFunctions.h"
15 #include "platform/geometry/LayoutPoint.h" 24 #include "platform/geometry/LayoutPoint.h"
16 #include "platform/graphics/GraphicsContextStateSaver.h" 25 #include "platform/graphics/GraphicsContextStateSaver.h"
17 26
18 namespace blink { 27 namespace blink {
19 28
20 void BoxPainter::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 29 void BoxPainter::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
21 { 30 {
22 LayoutPoint adjustedPaintOffset = paintOffset + m_renderBox.location(); 31 LayoutPoint adjustedPaintOffset = paintOffset + m_renderBox.location();
23 // default implementation. Just pass paint through to the children 32 // default implementation. Just pass paint through to the children
24 PaintInfo childInfo(paintInfo); 33 PaintInfo childInfo(paintInfo);
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
150 GraphicsContext* context = paintInfo.context; 159 GraphicsContext* context = paintInfo.context;
151 if (!context) 160 if (!context)
152 shouldDrawBackgroundInSeparateBuffer = false; 161 shouldDrawBackgroundInSeparateBuffer = false;
153 162
154 bool skipBaseColor = false; 163 bool skipBaseColor = false;
155 if (shouldDrawBackgroundInSeparateBuffer) { 164 if (shouldDrawBackgroundInSeparateBuffer) {
156 bool isBaseColorVisible = !isBottomLayerOccluded && c.hasAlpha(); 165 bool isBaseColorVisible = !isBottomLayerOccluded && c.hasAlpha();
157 166
158 // Paint the document's base background color outside the transparency l ayer, 167 // Paint the document's base background color outside the transparency l ayer,
159 // so that the background images don't blend with this color: http://crb ug.com/389039. 168 // so that the background images don't blend with this color: http://crb ug.com/389039.
160 if (isBaseColorVisible && m_renderBox.isDocumentElementWithOpaqueBackgro und()) { 169 if (isBaseColorVisible && isDocumentElementWithOpaqueBackground()) {
161 m_renderBox.paintRootBackgroundColor(paintInfo, rect, Color()); 170 paintRootBackgroundColor(paintInfo, rect, Color());
162 skipBaseColor = true; 171 skipBaseColor = true;
163 } 172 }
164 context->beginTransparencyLayer(1); 173 context->beginTransparencyLayer(1);
165 } 174 }
166 175
167 Vector<const FillLayer*>::const_reverse_iterator topLayer = layers.rend(); 176 Vector<const FillLayer*>::const_reverse_iterator topLayer = layers.rend();
168 for (Vector<const FillLayer*>::const_reverse_iterator it = layers.rbegin(); it != topLayer; ++it) 177 for (Vector<const FillLayer*>::const_reverse_iterator it = layers.rbegin(); it != topLayer; ++it)
169 paintFillLayer(paintInfo, c, **it, rect, bleedAvoidance, op, backgroundO bject, skipBaseColor); 178 paintFillLayer(paintInfo, c, **it, rect, bleedAvoidance, op, backgroundO bject, skipBaseColor);
170 179
171 if (shouldDrawBackgroundInSeparateBuffer) 180 if (shouldDrawBackgroundInSeparateBuffer)
172 context->endLayer(); 181 context->endLayer();
173 } 182 }
174 183
175 void BoxPainter::paintFillLayer(const PaintInfo& paintInfo, const Color& c, cons t FillLayer& fillLayer, const LayoutRect& rect, 184 void BoxPainter::paintFillLayer(const PaintInfo& paintInfo, const Color& c, cons t FillLayer& fillLayer, const LayoutRect& rect,
176 BackgroundBleedAvoidance bleedAvoidance, CompositeOperator op, RenderObject* backgroundObject, bool skipBaseColor) 185 BackgroundBleedAvoidance bleedAvoidance, CompositeOperator op, RenderObject* backgroundObject, bool skipBaseColor)
177 { 186 {
178 m_renderBox.paintFillLayerExtended(paintInfo, c, fillLayer, rect, bleedAvoid ance, 0, LayoutSize(), op, backgroundObject, skipBaseColor); 187 paintFillLayerExtended(paintInfo, c, fillLayer, rect, bleedAvoidance, 0, Lay outSize(), op, backgroundObject, skipBaseColor);
188 }
189
190 void BoxPainter::applyBoxShadowForBackground(GraphicsContext* context)
191 {
192 const ShadowList* shadowList = m_renderBox.style()->boxShadow();
193 ASSERT(shadowList);
194 for (size_t i = shadowList->shadows().size(); i--; ) {
195 const ShadowData& boxShadow = shadowList->shadows()[i];
196 if (boxShadow.style() != Normal)
197 continue;
198 FloatSize shadowOffset(boxShadow.x(), boxShadow.y());
199 context->setShadow(shadowOffset, boxShadow.blur(), boxShadow.color(),
200 DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::Shad owIgnoresAlpha);
201 return;
202 }
203 }
204
205 // FIXME: See crbug.com/382491. The use of getCTM in this context is incorrect b ecause the matrix returned does not
206 // include scales applied at raster time, such as the device zoom.
207 static LayoutRect shrinkRectByOnePixel(GraphicsContext* context, const LayoutRec t& rect)
208 {
209 LayoutRect shrunkRect = rect;
210 AffineTransform transform = context->getCTM();
211 shrunkRect.inflateX(-static_cast<LayoutUnit>(ceil(1 / transform.xScale())));
212 shrunkRect.inflateY(-static_cast<LayoutUnit>(ceil(1 / transform.yScale())));
213 return shrunkRect;
214 }
215
216 RoundedRect BoxPainter::getBackgroundRoundedRect(const LayoutRect& borderRect, I nlineFlowBox* box, LayoutUnit inlineBoxWidth, LayoutUnit inlineBoxHeight,
217 bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const
218 {
219 RoundedRect border = m_renderBox.style()->getRoundedBorderFor(borderRect, in cludeLogicalLeftEdge, includeLogicalRightEdge);
220 if (box && (box->nextLineBox() || box->prevLineBox())) {
221 RoundedRect segmentBorder = m_renderBox.style()->getRoundedBorderFor(Lay outRect(0, 0, inlineBoxWidth, inlineBoxHeight), includeLogicalLeftEdge, includeL ogicalRightEdge);
222 border.setRadii(segmentBorder.radii());
223 }
224
225 return border;
226 }
227
228 RoundedRect BoxPainter::backgroundRoundedRectAdjustedForBleedAvoidance(GraphicsC ontext* context, const LayoutRect& borderRect, BackgroundBleedAvoidance bleedAvo idance, InlineFlowBox* box, const LayoutSize& boxSize, bool includeLogicalLeftEd ge, bool includeLogicalRightEdge) const
229 {
230 if (bleedAvoidance == BackgroundBleedShrinkBackground) {
231 // We shrink the rectangle by one pixel on each side because the bleed i s one pixel maximum.
232 return getBackgroundRoundedRect(shrinkRectByOnePixel(context, borderRect ), box, boxSize.width(), boxSize.height(), includeLogicalLeftEdge, includeLogica lRightEdge);
233 }
234 if (bleedAvoidance == BackgroundBleedBackgroundOverBorder)
235 return m_renderBox.style()->getRoundedInnerBorderFor(borderRect, include LogicalLeftEdge, includeLogicalRightEdge);
236
237 return getBackgroundRoundedRect(borderRect, box, boxSize.width(), boxSize.he ight(), includeLogicalLeftEdge, includeLogicalRightEdge);
238 }
239
240 void BoxPainter::clipRoundedInnerRect(GraphicsContext * context, const LayoutRec t& rect, const RoundedRect& clipRect)
241 {
242 if (clipRect.isRenderable()) {
243 context->clipRoundedRect(clipRect);
244 } else {
245 // We create a rounded rect for each of the corners and clip it, while m aking sure we clip opposing corners together.
246 if (!clipRect.radii().topLeft().isEmpty() || !clipRect.radii().bottomRig ht().isEmpty()) {
247 IntRect topCorner(clipRect.rect().x(), clipRect.rect().y(), rect.max X() - clipRect.rect().x(), rect.maxY() - clipRect.rect().y());
248 RoundedRect::Radii topCornerRadii;
249 topCornerRadii.setTopLeft(clipRect.radii().topLeft());
250 context->clipRoundedRect(RoundedRect(topCorner, topCornerRadii));
251
252 IntRect bottomCorner(rect.x(), rect.y(), clipRect.rect().maxX() - re ct.x(), clipRect.rect().maxY() - rect.y());
253 RoundedRect::Radii bottomCornerRadii;
254 bottomCornerRadii.setBottomRight(clipRect.radii().bottomRight());
255 context->clipRoundedRect(RoundedRect(bottomCorner, bottomCornerRadii ));
256 }
257
258 if (!clipRect.radii().topRight().isEmpty() || !clipRect.radii().bottomLe ft().isEmpty()) {
259 IntRect topCorner(rect.x(), clipRect.rect().y(), clipRect.rect().max X() - rect.x(), rect.maxY() - clipRect.rect().y());
260 RoundedRect::Radii topCornerRadii;
261 topCornerRadii.setTopRight(clipRect.radii().topRight());
262 context->clipRoundedRect(RoundedRect(topCorner, topCornerRadii));
263
264 IntRect bottomCorner(clipRect.rect().x(), rect.y(), rect.maxX() - cl ipRect.rect().x(), clipRect.rect().maxY() - rect.y());
265 RoundedRect::Radii bottomCornerRadii;
266 bottomCornerRadii.setBottomLeft(clipRect.radii().bottomLeft());
267 context->clipRoundedRect(RoundedRect(bottomCorner, bottomCornerRadii ));
268 }
269 }
270 }
271
272 void BoxPainter::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& color, const FillLayer& bgLayer, const LayoutRect& rect,
273 BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* box, const LayoutSiz e& boxSize, CompositeOperator op, RenderObject* backgroundObject, bool skipBaseC olor)
274 {
275 GraphicsContext* context = paintInfo.context;
276 if (rect.isEmpty())
277 return;
278
279 bool includeLeftEdge = box ? box->includeLogicalLeftEdge() : true;
280 bool includeRightEdge = box ? box->includeLogicalRightEdge() : true;
281
282 bool hasRoundedBorder = m_renderBox.style()->hasBorderRadius() && (includeLe ftEdge || includeRightEdge);
283 bool clippedWithLocalScrolling = m_renderBox.hasOverflowClip() && bgLayer.at tachment() == LocalBackgroundAttachment;
284 bool isBorderFill = bgLayer.clip() == BorderFillBox;
285 bool isDocumentElementRenderer = m_renderBox.isDocumentElement();
286 bool isBottomLayer = !bgLayer.next();
287
288 Color bgColor = color;
289 StyleImage* bgImage = bgLayer.image();
290 bool shouldPaintBackgroundImage = bgImage && bgImage->canRender(m_renderBox, m_renderBox.style()->effectiveZoom());
291
292 bool forceBackgroundToWhite = false;
293 if (m_renderBox.document().printing()) {
294 if (m_renderBox.style()->printColorAdjust() == PrintColorAdjustEconomy)
295 forceBackgroundToWhite = true;
296 if (m_renderBox.document().settings() && m_renderBox.document().settings ()->shouldPrintBackgrounds())
297 forceBackgroundToWhite = false;
298 }
299
300 // When printing backgrounds is disabled or using economy mode,
301 // change existing background colors and images to a solid white background.
302 // If there's no bg color or image, leave it untouched to avoid affecting tr ansparency.
303 // We don't try to avoid loading the background images, because this style f lag is only set
304 // when printing, and at that point we've already loaded the background imag es anyway. (To avoid
305 // loading the background images we'd have to do this check when applying st yles rather than
306 // while rendering.)
307 if (forceBackgroundToWhite) {
308 // Note that we can't reuse this variable below because the bgColor migh t be changed
309 bool shouldPaintBackgroundColor = isBottomLayer && bgColor.alpha();
310 if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) {
311 bgColor = Color::white;
312 shouldPaintBackgroundImage = false;
313 }
314 }
315
316 bool colorVisible = bgColor.alpha();
317
318 // Fast path for drawing simple color backgrounds.
319 if (!isDocumentElementRenderer && !clippedWithLocalScrolling && !shouldPaint BackgroundImage && isBorderFill && isBottomLayer) {
320 if (!colorVisible)
321 return;
322
323 bool boxShadowShouldBeAppliedToBackground = m_renderBox.boxShadowShouldB eAppliedToBackground(bleedAvoidance, box);
324 GraphicsContextStateSaver shadowStateSaver(*context, boxShadowShouldBeAp pliedToBackground);
325 if (boxShadowShouldBeAppliedToBackground)
326 applyBoxShadowForBackground(context);
327
328 if (hasRoundedBorder && bleedAvoidance != BackgroundBleedClipBackground) {
329 RoundedRect border = backgroundRoundedRectAdjustedForBleedAvoidance( context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, includeRightEdge);
330 if (border.isRenderable()) {
331 context->fillRoundedRect(border, bgColor);
332 } else {
333 context->save();
334 clipRoundedInnerRect(context, rect, border);
335 context->fillRect(border.rect(), bgColor);
336 context->restore();
337 }
338 } else {
339 context->fillRect(pixelSnappedIntRect(rect), bgColor);
340 }
341
342 return;
343 }
344
345 // BorderFillBox radius clipping is taken care of by BackgroundBleedClipBack ground
346 bool clipToBorderRadius = hasRoundedBorder && !(isBorderFill && bleedAvoidan ce == BackgroundBleedClipBackground);
347 GraphicsContextStateSaver clipToBorderStateSaver(*context, clipToBorderRadiu s);
348 if (clipToBorderRadius) {
349 RoundedRect border = isBorderFill ? backgroundRoundedRectAdjustedForBlee dAvoidance(context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, include RightEdge) : getBackgroundRoundedRect(rect, box, boxSize.width(), boxSize.height (), includeLeftEdge, includeRightEdge);
350
351 // Clip to the padding or content boxes as necessary.
352 if (bgLayer.clip() == ContentFillBox) {
353 border = m_renderBox.style()->getRoundedInnerBorderFor(border.rect() ,
354 m_renderBox.paddingTop() + m_renderBox.borderTop(), m_renderBox. paddingBottom() + m_renderBox.borderBottom(),
355 m_renderBox.paddingLeft() + m_renderBox.borderLeft(), m_renderBo x.paddingRight() + m_renderBox.borderRight(), includeLeftEdge, includeRightEdge) ;
356 } else if (bgLayer.clip() == PaddingFillBox) {
357 border = m_renderBox.style()->getRoundedInnerBorderFor(border.rect() , includeLeftEdge, includeRightEdge);
358 }
359
360 clipRoundedInnerRect(context, rect, border);
361 }
362
363 int bLeft = includeLeftEdge ? m_renderBox.borderLeft() : 0;
364 int bRight = includeRightEdge ? m_renderBox.borderRight() : 0;
365 LayoutUnit pLeft = includeLeftEdge ? m_renderBox.paddingLeft() : LayoutUnit( );
366 LayoutUnit pRight = includeRightEdge ? m_renderBox.paddingRight() : LayoutUn it();
367
368 GraphicsContextStateSaver clipWithScrollingStateSaver(*context, clippedWithL ocalScrolling);
369 LayoutRect scrolledPaintRect = rect;
370 if (clippedWithLocalScrolling) {
371 // Clip to the overflow area.
372 context->clip(m_renderBox.overflowClipRect(rect.location()));
373
374 // Adjust the paint rect to reflect a scrolled content box with borders at the ends.
375 IntSize offset = m_renderBox.scrolledContentOffset();
376 scrolledPaintRect.move(-offset);
377 scrolledPaintRect.setWidth(bLeft + m_renderBox.scrollWidth() + bRight);
378 scrolledPaintRect.setHeight(m_renderBox.borderTop() + m_renderBox.scroll Height() + m_renderBox.borderBottom());
379 }
380
381 GraphicsContextStateSaver backgroundClipStateSaver(*context, false);
382 IntRect maskRect;
383
384 switch (bgLayer.clip()) {
385 case PaddingFillBox:
386 case ContentFillBox: {
387 if (clipToBorderRadius)
388 break;
389
390 // Clip to the padding or content boxes as necessary.
391 bool includePadding = bgLayer.clip() == ContentFillBox;
392 LayoutRect clipRect = LayoutRect(scrolledPaintRect.x() + bLeft + (includ ePadding ? pLeft : LayoutUnit()),
393 scrolledPaintRect.y() + m_renderBox.borderTop() + (includePadding ? m_renderBox.paddingTop() : LayoutUnit()),
394 scrolledPaintRect.width() - bLeft - bRight - (includePadding ? pLeft + pRight : LayoutUnit()),
395 scrolledPaintRect.height() - m_renderBox.borderTop() - m_renderBox.b orderBottom() - (includePadding ? m_renderBox.paddingTop() + m_renderBox.padding Bottom() : LayoutUnit()));
396 backgroundClipStateSaver.save();
397 context->clip(clipRect);
398
399 break;
400 }
401 case TextFillBox: {
402 // First figure out how big the mask has to be. It should be no bigger t han what we need
403 // to actually render, so we should intersect the dirty rect with the bo rder box of the background.
404 maskRect = pixelSnappedIntRect(rect);
405 maskRect.intersect(paintInfo.rect);
406
407 // We draw the background into a separate layer, to be later masked with yet another layer
408 // holding the text content.
409 backgroundClipStateSaver.save();
410 context->clip(maskRect);
411 context->beginTransparencyLayer(1);
412
413 break;
414 }
415 case BorderFillBox:
416 break;
417 default:
418 ASSERT_NOT_REACHED();
419 break;
420 }
421
422 // Paint the color first underneath all images, culled if background image o ccludes it.
423 // FIXME: In the bgLayer->hasFiniteBounds() case, we could improve the culli ng test
424 // by verifying whether the background image covers the entire layout rect.
425 if (isBottomLayer) {
426 IntRect backgroundRect(pixelSnappedIntRect(scrolledPaintRect));
427 bool boxShadowShouldBeAppliedToBackground = m_renderBox.boxShadowShouldB eAppliedToBackground(bleedAvoidance, box);
428 bool isOpaqueRoot = (isDocumentElementRenderer && !bgColor.hasAlpha()) | | isDocumentElementWithOpaqueBackground();
429 if (boxShadowShouldBeAppliedToBackground || !shouldPaintBackgroundImage || !bgLayer.hasOpaqueImage(&m_renderBox) || !bgLayer.hasRepeatXY() || (isOpaqueR oot && m_renderBox.height())) {
430 if (!boxShadowShouldBeAppliedToBackground)
431 backgroundRect.intersect(paintInfo.rect);
432
433 GraphicsContextStateSaver shadowStateSaver(*context, boxShadowShould BeAppliedToBackground);
434 if (boxShadowShouldBeAppliedToBackground)
435 applyBoxShadowForBackground(context);
436
437 if (isOpaqueRoot && !skipBaseColor) {
438 paintRootBackgroundColor(paintInfo, rect, bgColor);
439 } else if (bgColor.alpha()) {
440 context->fillRect(backgroundRect, bgColor, context->compositeOpe ration());
441 }
442 }
443 }
444
445 // no progressive loading of the background image
446 if (shouldPaintBackgroundImage) {
447 BackgroundImageGeometry geometry;
448 calculateBackgroundImageGeometry(paintInfo.paintContainer(), bgLayer, sc rolledPaintRect, geometry, backgroundObject);
449 geometry.clip(paintInfo.rect);
450 if (!geometry.destRect().isEmpty()) {
451 CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer. composite() : op;
452 RenderObject* clientForBackgroundImage = backgroundObject ? backgrou ndObject : &m_renderBox;
453 RefPtr<Image> image = bgImage->image(clientForBackgroundImage, geome try.tileSize());
454 InterpolationQuality interpolationQuality = chooseInterpolationQuali ty(context, image.get(), &bgLayer, geometry.tileSize());
455 if (bgLayer.maskSourceType() == MaskLuminance)
456 context->setColorFilter(ColorFilterLuminanceToAlpha);
457 InterpolationQuality previousInterpolationQuality = context->imageIn terpolationQuality();
458 context->setImageInterpolationQuality(interpolationQuality);
459 context->drawTiledImage(image.get(), geometry.destRect(), geometry.r elativePhase(), geometry.tileSize(),
460 compositeOp, bgLayer.blendMode(), geometry.spaceSize());
461 context->setImageInterpolationQuality(previousInterpolationQuality);
462 }
463 }
464
465 if (bgLayer.clip() == TextFillBox) {
466 // Create the text mask layer.
467 context->setCompositeOperation(CompositeDestinationIn);
468 context->beginTransparencyLayer(1);
469
470 // FIXME: Workaround for https://code.google.com/p/skia/issues/detail?id =1291.
471 context->clearRect(maskRect);
472
473 // Now draw the text into the mask. We do this by painting using a speci al paint phase that signals to
474 // InlineTextBoxes that they should just add their contents to the clip.
475 PaintInfo info(context, maskRect, PaintPhaseTextClip, PaintBehaviorForce BlackText, 0);
476 context->setCompositeOperation(CompositeSourceOver);
477 if (box) {
478 RootInlineBox& root = box->root();
479 box->paint(info, LayoutPoint(scrolledPaintRect.x() - box->x(), scrol ledPaintRect.y() - box->y()), root.lineTop(), root.lineBottom());
480 } else {
481 LayoutSize localOffset = m_renderBox.locationOffset();
482 paint(info, scrolledPaintRect.location() - localOffset);
483 }
484
485 context->endLayer();
486 context->endLayer();
487 }
179 } 488 }
180 489
181 void BoxPainter::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 490 void BoxPainter::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
182 { 491 {
183 if (!paintInfo.shouldPaintWithinRoot(&m_renderBox) || m_renderBox.style()->v isibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) 492 if (!paintInfo.shouldPaintWithinRoot(&m_renderBox) || m_renderBox.style()->v isibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
184 return; 493 return;
185 494
186 LayoutRect paintRect = LayoutRect(paintOffset, m_renderBox.size()); 495 LayoutRect paintRect = LayoutRect(paintOffset, m_renderBox.size());
187 paintMaskImages(paintInfo, paintRect); 496 paintMaskImages(paintInfo, paintRect);
188 } 497 }
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
231 return; 540 return;
232 541
233 // We should never have this state in this function. A layer with a mask 542 // We should never have this state in this function. A layer with a mask
234 // should have always created its own backing if it became composited. 543 // should have always created its own backing if it became composited.
235 ASSERT(m_renderBox.layer()->compositingState() != HasOwnBackingButPaintsInto Ancestor); 544 ASSERT(m_renderBox.layer()->compositingState() != HasOwnBackingButPaintsInto Ancestor);
236 545
237 LayoutRect paintRect = LayoutRect(paintOffset, m_renderBox.size()); 546 LayoutRect paintRect = LayoutRect(paintOffset, m_renderBox.size());
238 paintInfo.context->fillRect(pixelSnappedIntRect(paintRect), Color::black); 547 paintInfo.context->fillRect(pixelSnappedIntRect(paintRect), Color::black);
239 } 548 }
240 549
550 void BoxPainter::paintRootBackgroundColor(const PaintInfo& paintInfo, const Layo utRect& rect, const Color& bgColor)
551 {
552 GraphicsContext* context = paintInfo.context;
553 if (rect.isEmpty())
554 return;
555
556 ASSERT(isDocumentElement());
557
558 IntRect backgroundRect(pixelSnappedIntRect(rect));
559 backgroundRect.intersect(paintInfo.rect);
560
561 Color baseColor = m_renderBox.view()->frameView()->baseBackgroundColor();
562 bool shouldClearDocumentBackground = m_renderBox.document().settings() && m_ renderBox.document().settings()->shouldClearDocumentBackground();
563 CompositeOperator operation = shouldClearDocumentBackground ? CompositeCopy : context->compositeOperation();
564
565 // If we have an alpha go ahead and blend with the base background color.
566 if (baseColor.alpha()) {
567 if (bgColor.alpha())
568 baseColor = baseColor.blend(bgColor);
569 context->fillRect(backgroundRect, baseColor, operation);
570 } else if (bgColor.alpha()) {
571 context->fillRect(backgroundRect, bgColor, operation);
572 } else if (shouldClearDocumentBackground) {
573 context->clearRect(backgroundRect);
574 }
575 }
576
577 bool BoxPainter::isDocumentElementWithOpaqueBackground() const
578 {
579 if (!m_renderBox.isDocumentElement())
580 return false;
581
582 // The background is opaque only if we're the root document, since iframes w ith
583 // no background in the child document should show the parent's background.
584 bool isOpaque = true;
585 Element* ownerElement = m_renderBox.document().ownerElement();
586 if (ownerElement) {
587 if (!isHTMLFrameElement(*ownerElement)) {
588 // Locate the <body> element using the DOM. This is easier than tryi ng
589 // to crawl around a render tree with potential :before/:after conte nt and
590 // anonymous blocks created by inline <body> tags etc. We can locate the <body>
591 // render object very easily via the DOM.
592 HTMLElement* body = m_renderBox.document().body();
593 if (body) {
594 // Can't scroll a frameset document anyway.
595 isOpaque = body->hasTagName(HTMLNames::framesetTag);
596 } else {
597 // FIXME: SVG specific behavior should be in the SVG code.
598 // SVG documents and XML documents with SVG root nodes are trans parent.
599 isOpaque = !m_renderBox.document().hasSVGRootNode();
600 }
601 }
602 } else if (m_renderBox.view()->frameView()) {
603 isOpaque = !m_renderBox.view()->frameView()->isTransparent();
604 }
605
606 return isOpaque;
607 }
608
609 static inline int getSpace(int areaSize, int tileSize)
610 {
611 int numberOfTiles = areaSize / tileSize;
612 int space = -1;
613
614 if (numberOfTiles > 1)
615 space = lroundf((float)(areaSize - numberOfTiles * tileSize) / (numberOf Tiles - 1));
616
617 return space;
618 }
619
620 void BoxPainter::calculateBackgroundImageGeometry(const RenderLayerModelObject* paintContainer, const FillLayer& fillLayer, const LayoutRect& paintRect,
621 BackgroundImageGeometry& geometry, RenderObject* backgroundObject) const
622 {
623 LayoutUnit left = 0;
624 LayoutUnit top = 0;
625 IntSize positioningAreaSize;
626 IntRect snappedPaintRect = pixelSnappedIntRect(paintRect);
627
628 // Determine the background positioning area and set destRect to the backgro und painting area.
629 // destRect will be adjusted later if the background is non-repeating.
630 // FIXME: transforms spec says that fixed backgrounds behave like scroll ins ide transforms.
631 bool fixedAttachment = fillLayer.attachment() == FixedBackgroundAttachment;
632
633 if (RuntimeEnabledFeatures::fastMobileScrollingEnabled()) {
634 // As a side effect of an optimization to blit on scroll, we do not hono r the CSS
635 // property "background-attachment: fixed" because it may result in rend ering
636 // artifacts. Note, these artifacts only appear if we are blitting on sc roll of
637 // a page that has fixed background images.
638 fixedAttachment = false;
639 }
640
641 if (!fixedAttachment) {
642 geometry.setDestRect(snappedPaintRect);
643
644 LayoutUnit right = 0;
645 LayoutUnit bottom = 0;
646 // Scroll and Local.
647 if (fillLayer.origin() != BorderFillBox) {
648 left = m_renderBox.borderLeft();
649 right = m_renderBox.borderRight();
650 top = m_renderBox.borderTop();
651 bottom = m_renderBox.borderBottom();
652 if (fillLayer.origin() == ContentFillBox) {
653 left += m_renderBox.paddingLeft();
654 right += m_renderBox.paddingRight();
655 top += m_renderBox.paddingTop();
656 bottom += m_renderBox.paddingBottom();
657 }
658 }
659
660 // The background of the box generated by the root element covers the en tire canvas including
661 // its margins. Since those were added in already, we have to factor the m out when computing
662 // the background positioning area.
663 if (m_renderBox.isDocumentElement()) {
664 positioningAreaSize = pixelSnappedIntSize(m_renderBox.size() - Layou tSize(left + right, top + bottom), m_renderBox.location());
665 left += m_renderBox.marginLeft();
666 top += m_renderBox.marginTop();
667 } else {
668 positioningAreaSize = pixelSnappedIntSize(paintRect.size() - LayoutS ize(left + right, top + bottom), paintRect.location());
669 }
670 } else {
671 geometry.setHasNonLocalGeometry();
672
673 IntRect viewportRect = pixelSnappedIntRect(m_renderBox.viewRect());
674 if (fixedBackgroundPaintsInLocalCoordinates())
675 viewportRect.setLocation(IntPoint());
676 else if (FrameView* frameView = m_renderBox.view()->frameView())
677 viewportRect.setLocation(IntPoint(frameView->scrollOffsetForFixedPos ition()));
678
679 if (paintContainer) {
680 IntPoint absoluteContainerOffset = roundedIntPoint(paintContainer->l ocalToAbsolute(FloatPoint()));
681 viewportRect.moveBy(-absoluteContainerOffset);
682 }
683
684 geometry.setDestRect(pixelSnappedIntRect(viewportRect));
685 positioningAreaSize = geometry.destRect().size();
686 }
687
688 const RenderObject* clientForBackgroundImage = backgroundObject ? background Object : &m_renderBox;
689 IntSize fillTileSize = calculateFillTileSize(fillLayer, positioningAreaSize) ;
690 fillLayer.image()->setContainerSizeForRenderer(clientForBackgroundImage, fil lTileSize, m_renderBox.style()->effectiveZoom());
691 geometry.setTileSize(fillTileSize);
692
693 EFillRepeat backgroundRepeatX = fillLayer.repeatX();
694 EFillRepeat backgroundRepeatY = fillLayer.repeatY();
695 int availableWidth = positioningAreaSize.width() - geometry.tileSize().width ();
696 int availableHeight = positioningAreaSize.height() - geometry.tileSize().hei ght();
697
698 LayoutUnit computedXPosition = roundedMinimumValueForLength(fillLayer.xPosit ion(), availableWidth);
699 if (backgroundRepeatX == RoundFill && positioningAreaSize.width() > 0 && fil lTileSize.width() > 0) {
700 long nrTiles = std::max(1l, lroundf((float)positioningAreaSize.width() / fillTileSize.width()));
701
702 if (fillLayer.size().size.height().isAuto() && backgroundRepeatY != Roun dFill) {
703 fillTileSize.setHeight(fillTileSize.height() * positioningAreaSize.w idth() / (nrTiles * fillTileSize.width()));
704 }
705
706 fillTileSize.setWidth(positioningAreaSize.width() / nrTiles);
707 geometry.setTileSize(fillTileSize);
708 geometry.setPhaseX(geometry.tileSize().width() ? geometry.tileSize().wid th() - roundToInt(computedXPosition + left) % geometry.tileSize().width() : 0);
709 geometry.setSpaceSize(IntSize());
710 }
711
712 LayoutUnit computedYPosition = roundedMinimumValueForLength(fillLayer.yPosit ion(), availableHeight);
713 if (backgroundRepeatY == RoundFill && positioningAreaSize.height() > 0 && fi llTileSize.height() > 0) {
714 long nrTiles = std::max(1l, lroundf((float)positioningAreaSize.height() / fillTileSize.height()));
715
716 if (fillLayer.size().size.width().isAuto() && backgroundRepeatX != Round Fill) {
717 fillTileSize.setWidth(fillTileSize.width() * positioningAreaSize.hei ght() / (nrTiles * fillTileSize.height()));
718 }
719
720 fillTileSize.setHeight(positioningAreaSize.height() / nrTiles);
721 geometry.setTileSize(fillTileSize);
722 geometry.setPhaseY(geometry.tileSize().height() ? geometry.tileSize().he ight() - roundToInt(computedYPosition + top) % geometry.tileSize().height() : 0) ;
723 geometry.setSpaceSize(IntSize());
724 }
725
726 if (backgroundRepeatX == RepeatFill) {
727 geometry.setPhaseX(geometry.tileSize().width() ? geometry.tileSize().wid th() - roundToInt(computedXPosition + left) % geometry.tileSize().width() : 0);
728 geometry.setSpaceSize(IntSize());
729 } else if (backgroundRepeatX == SpaceFill && fillTileSize.width() > 0) {
730 int space = getSpace(positioningAreaSize.width(), geometry.tileSize().wi dth());
731 int actualWidth = geometry.tileSize().width() + space;
732
733 if (space >= 0) {
734 computedXPosition = roundedMinimumValueForLength(Length(), available Width);
735 geometry.setSpaceSize(IntSize(space, 0));
736 geometry.setPhaseX(actualWidth ? actualWidth - roundToInt(computedXP osition + left) % actualWidth : 0);
737 } else {
738 backgroundRepeatX = NoRepeatFill;
739 }
740 }
741 if (backgroundRepeatX == NoRepeatFill) {
742 int xOffset = fillLayer.backgroundXOrigin() == RightEdge ? availableWidt h - computedXPosition : computedXPosition;
743 geometry.setNoRepeatX(left + xOffset);
744 geometry.setSpaceSize(IntSize(0, geometry.spaceSize().height()));
745 }
746
747 if (backgroundRepeatY == RepeatFill) {
748 geometry.setPhaseY(geometry.tileSize().height() ? geometry.tileSize().he ight() - roundToInt(computedYPosition + top) % geometry.tileSize().height() : 0) ;
749 geometry.setSpaceSize(IntSize(geometry.spaceSize().width(), 0));
750 } else if (backgroundRepeatY == SpaceFill && fillTileSize.height() > 0) {
751 int space = getSpace(positioningAreaSize.height(), geometry.tileSize().h eight());
752 int actualHeight = geometry.tileSize().height() + space;
753
754 if (space >= 0) {
755 computedYPosition = roundedMinimumValueForLength(Length(), available Height);
756 geometry.setSpaceSize(IntSize(geometry.spaceSize().width(), space));
757 geometry.setPhaseY(actualHeight ? actualHeight - roundToInt(computed YPosition + top) % actualHeight : 0);
758 } else {
759 backgroundRepeatY = NoRepeatFill;
760 }
761 }
762 if (backgroundRepeatY == NoRepeatFill) {
763 int yOffset = fillLayer.backgroundYOrigin() == BottomEdge ? availableHei ght - computedYPosition : computedYPosition;
764 geometry.setNoRepeatY(top + yOffset);
765 geometry.setSpaceSize(IntSize(geometry.spaceSize().width(), 0));
766 }
767
768 if (fixedAttachment)
769 geometry.useFixedAttachment(snappedPaintRect.location());
770
771 geometry.clip(snappedPaintRect);
772 geometry.setDestOrigin(geometry.destRect().location());
773 }
774
775
776 InterpolationQuality BoxPainter::chooseInterpolationQuality(GraphicsContext* con text, Image* image, const void* layer, const LayoutSize& size)
777 {
778 return ImageQualityController::imageQualityController()->chooseInterpolation Quality(context, &m_renderBox, image, layer, size);
779 }
780
781 bool BoxPainter::fixedBackgroundPaintsInLocalCoordinates() const
782 {
783 if (!m_renderBox.isDocumentElement())
784 return false;
785
786 if (m_renderBox.view()->frameView() && m_renderBox.view()->frameView()->pain tBehavior() & PaintBehaviorFlattenCompositingLayers)
787 return false;
788
789 RenderLayer* rootLayer = m_renderBox.view()->layer();
790 if (!rootLayer || rootLayer->compositingState() == NotComposited)
791 return false;
792
793 return rootLayer->compositedLayerMapping()->backgroundLayerPaintsFixedRootBa ckground();
794 }
795
796 static inline void applySubPixelHeuristicForTileSize(LayoutSize& tileSize, const IntSize& positioningAreaSize)
797 {
798 tileSize.setWidth(positioningAreaSize.width() - tileSize.width() <= 1 ? tile Size.width().ceil() : tileSize.width().floor());
799 tileSize.setHeight(positioningAreaSize.height() - tileSize.height() <= 1 ? t ileSize.height().ceil() : tileSize.height().floor());
800 }
801
802 IntSize BoxPainter::calculateFillTileSize(const FillLayer& fillLayer, const IntS ize& positioningAreaSize) const
803 {
804 StyleImage* image = fillLayer.image();
805 EFillSizeType type = fillLayer.size().type;
806
807 IntSize imageIntrinsicSize = m_renderBox.calculateImageIntrinsicDimensions(i mage, positioningAreaSize, RenderBoxModelObject::ScaleByEffectiveZoom);
808 imageIntrinsicSize.scale(1 / image->imageScaleFactor(), 1 / image->imageScal eFactor());
809 switch (type) {
810 case SizeLength: {
811 LayoutSize tileSize = positioningAreaSize;
812
813 Length layerWidth = fillLayer.size().size.width();
814 Length layerHeight = fillLayer.size().size.height();
815
816 if (layerWidth.isFixed())
817 tileSize.setWidth(layerWidth.value());
818 else if (layerWidth.isPercent())
819 tileSize.setWidth(valueForLength(layerWidth, positioningAreaSize.wid th()));
820
821 if (layerHeight.isFixed())
822 tileSize.setHeight(layerHeight.value());
823 else if (layerHeight.isPercent())
824 tileSize.setHeight(valueForLength(layerHeight, positioningAreaSize.h eight()));
825
826 applySubPixelHeuristicForTileSize(tileSize, positioningAreaSize);
827
828 // If one of the values is auto we have to use the appropriate
829 // scale to maintain our aspect ratio.
830 if (layerWidth.isAuto() && !layerHeight.isAuto()) {
831 if (imageIntrinsicSize.height())
832 tileSize.setWidth(imageIntrinsicSize.width() * tileSize.height() / imageIntrinsicSize.height());
833 } else if (!layerWidth.isAuto() && layerHeight.isAuto()) {
834 if (imageIntrinsicSize.width())
835 tileSize.setHeight(imageIntrinsicSize.height() * tileSize.width( ) / imageIntrinsicSize.width());
836 } else if (layerWidth.isAuto() && layerHeight.isAuto()) {
837 // If both width and height are auto, use the image's intrinsic size .
838 tileSize = imageIntrinsicSize;
839 }
840
841 tileSize.clampNegativeToZero();
842 return flooredIntSize(tileSize);
843 }
844 case SizeNone: {
845 // If both values are ‘auto’ then the intrinsic width and/or height of t he image should be used, if any.
846 if (!imageIntrinsicSize.isEmpty())
847 return imageIntrinsicSize;
848
849 // If the image has neither an intrinsic width nor an intrinsic height, its size is determined as for ‘contain’.
850 type = Contain;
851 }
852 case Contain:
853 case Cover: {
854 float horizontalScaleFactor = imageIntrinsicSize.width()
855 ? static_cast<float>(positioningAreaSize.width()) / imageIntrinsicSi ze.width() : 1;
856 float verticalScaleFactor = imageIntrinsicSize.height()
857 ? static_cast<float>(positioningAreaSize.height()) / imageIntrinsicS ize.height() : 1;
858 float scaleFactor = type == Contain ? std::min(horizontalScaleFactor, ve rticalScaleFactor) : std::max(horizontalScaleFactor, verticalScaleFactor);
859 return IntSize(std::max(1l, lround(imageIntrinsicSize.width() * scaleFac tor)), std::max(1l, lround(imageIntrinsicSize.height() * scaleFactor)));
860 }
861 }
862
863 ASSERT_NOT_REACHED();
864 return IntSize();
865 }
866
241 } // namespace blink 867 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698