RenderMultiColumnSet.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. /*
  2. * Copyright (C) 2012 Apple Inc. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. * 1. Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * 2. Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. *
  13. * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
  14. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  15. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  16. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
  17. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  20. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  21. * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  22. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  23. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24. */
  25. #include "config.h"
  26. #include "RenderMultiColumnSet.h"
  27. #include "PaintInfo.h"
  28. #include "RenderLayer.h"
  29. #include "RenderMultiColumnBlock.h"
  30. #include "RenderMultiColumnFlowThread.h"
  31. using namespace std;
  32. namespace WebCore {
  33. RenderMultiColumnSet::RenderMultiColumnSet(RenderFlowThread* flowThread)
  34. : RenderRegionSet(0, flowThread)
  35. , m_computedColumnCount(1)
  36. , m_computedColumnWidth(0)
  37. , m_computedColumnHeight(0)
  38. , m_maxColumnHeight(LayoutUnit::max())
  39. , m_minSpaceShortage(LayoutUnit::max())
  40. , m_minimumColumnHeight(0)
  41. , m_forcedBreaksCount(0)
  42. , m_maximumDistanceBetweenForcedBreaks(0)
  43. , m_forcedBreakOffset(0)
  44. {
  45. }
  46. RenderMultiColumnSet* RenderMultiColumnSet::createAnonymous(RenderFlowThread* flowThread)
  47. {
  48. Document* document = flowThread->document();
  49. RenderMultiColumnSet* renderer = new (document->renderArena()) RenderMultiColumnSet(flowThread);
  50. renderer->setDocumentForAnonymous(document);
  51. return renderer;
  52. }
  53. LayoutUnit RenderMultiColumnSet::heightAdjustedForSetOffset(LayoutUnit height) const
  54. {
  55. RenderMultiColumnBlock* multicolBlock = toRenderMultiColumnBlock(parent());
  56. LayoutUnit contentLogicalTop = logicalTop() - multicolBlock->borderAndPaddingBefore();
  57. height -= contentLogicalTop;
  58. return max(height, LayoutUnit(1)); // Let's avoid zero height, as that would probably cause an infinite amount of columns to be created.
  59. }
  60. LayoutUnit RenderMultiColumnSet::pageLogicalTopForOffset(LayoutUnit offset) const
  61. {
  62. LayoutUnit portionLogicalTop = (isHorizontalWritingMode() ? flowThreadPortionRect().y() : flowThreadPortionRect().x());
  63. unsigned columnIndex = columnIndexAtOffset(offset, AssumeNewColumns);
  64. return portionLogicalTop + columnIndex * computedColumnHeight();
  65. }
  66. void RenderMultiColumnSet::setAndConstrainColumnHeight(LayoutUnit newHeight)
  67. {
  68. m_computedColumnHeight = newHeight;
  69. if (m_computedColumnHeight > m_maxColumnHeight)
  70. m_computedColumnHeight = m_maxColumnHeight;
  71. // FIXME: the height may also be affected by the enclosing pagination context, if any.
  72. }
  73. bool RenderMultiColumnSet::calculateBalancedHeight(bool initial)
  74. {
  75. ASSERT(toRenderMultiColumnBlock(parent())->requiresBalancing());
  76. LayoutUnit oldColumnHeight = m_computedColumnHeight;
  77. LayoutUnit currentMinSpaceShortage = m_minSpaceShortage;
  78. m_minSpaceShortage = LayoutUnit::max();
  79. if (initial) {
  80. // Start with the lowest imaginable column height.
  81. LayoutUnit logicalHeightGuess = ceilf(float(flowThread()->logicalHeight()) / float(m_computedColumnCount));
  82. logicalHeightGuess = max(logicalHeightGuess, m_minimumColumnHeight);
  83. setAndConstrainColumnHeight(logicalHeightGuess);
  84. // The multicol container now typically needs at least one more layout pass with a new
  85. // column height, but if height was specified, we only need to do this if we found that we
  86. // might need less space than that. On the other hand, if we determined that the columns
  87. // need to be as tall as the specified height of the container, we have already laid it out
  88. // correctly, and there's no need for another pass.
  89. return m_computedColumnHeight != oldColumnHeight;
  90. }
  91. if (columnCount() <= computedColumnCount())
  92. // With the current column height, the content fits without creating overflowing columns. We're done.
  93. return false;
  94. // If the initial guessed column height wasn't enough, stretch it now. Stretch by the lowest
  95. // amount of space shortage found during layout.
  96. ASSERT(currentMinSpaceShortage != LayoutUnit::max()); // If this can actually happen, we probably have a bug.
  97. if (currentMinSpaceShortage == LayoutUnit::max())
  98. return false; // So bail out rather than looping infinitely.
  99. setAndConstrainColumnHeight(m_computedColumnHeight + currentMinSpaceShortage);
  100. // If we reach the maximum column height (typically set by the height or max-height property),
  101. // we may not be allowed to stretch further. Return true only if stretching
  102. // succeeded. Otherwise, we're done.
  103. ASSERT(m_computedColumnHeight >= oldColumnHeight); // We shouldn't be able to shrink the height!
  104. return m_computedColumnHeight > oldColumnHeight;
  105. }
  106. void RenderMultiColumnSet::recordSpaceShortage(LayoutUnit spaceShortage)
  107. {
  108. if (spaceShortage >= m_minSpaceShortage)
  109. return;
  110. // The space shortage is what we use as our stretch amount. We need a positive number here in
  111. // order to get anywhere.
  112. ASSERT(spaceShortage > 0);
  113. m_minSpaceShortage = spaceShortage;
  114. }
  115. void RenderMultiColumnSet::updateLogicalWidth()
  116. {
  117. RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent());
  118. setComputedColumnWidthAndCount(parentBlock->columnWidth(), parentBlock->columnCount()); // FIXME: This will eventually vary if we are contained inside regions.
  119. // FIXME: When we add regions support, we'll start it off at the width of the multi-column
  120. // block in that particular region.
  121. setLogicalWidth(parentBox()->contentLogicalWidth());
  122. // If we overflow, increase our logical width.
  123. unsigned colCount = columnCount();
  124. LayoutUnit colGap = columnGap();
  125. LayoutUnit minimumContentLogicalWidth = colCount * computedColumnWidth() + (colCount - 1) * colGap;
  126. LayoutUnit currentContentLogicalWidth = contentLogicalWidth();
  127. LayoutUnit delta = max(LayoutUnit(), minimumContentLogicalWidth - currentContentLogicalWidth);
  128. if (!delta)
  129. return;
  130. // Increase our logical width by the delta.
  131. setLogicalWidth(logicalWidth() + delta);
  132. }
  133. void RenderMultiColumnSet::prepareForLayout()
  134. {
  135. RenderMultiColumnBlock* multicolBlock = toRenderMultiColumnBlock(parent());
  136. RenderStyle* multicolStyle = multicolBlock->style();
  137. // Set box logical top.
  138. ASSERT(!previousSiblingBox() || !previousSiblingBox()->isRenderMultiColumnSet()); // FIXME: multiple set not implemented; need to examine previous set to calculate the correct logical top.
  139. setLogicalTop(multicolBlock->borderAndPaddingBefore());
  140. // Set box width.
  141. updateLogicalWidth();
  142. if (multicolBlock->requiresBalancing()) {
  143. // Set maximum column height. We will not stretch beyond this.
  144. m_maxColumnHeight = LayoutUnit::max();
  145. if (!multicolStyle->logicalHeight().isAuto())
  146. m_maxColumnHeight = multicolBlock->computeContentLogicalHeight(multicolStyle->logicalHeight());
  147. if (!multicolStyle->logicalMaxHeight().isUndefined()) {
  148. LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHeight(multicolStyle->logicalMaxHeight());
  149. if (m_maxColumnHeight > logicalMaxHeight)
  150. m_maxColumnHeight = logicalMaxHeight;
  151. }
  152. m_maxColumnHeight = heightAdjustedForSetOffset(m_maxColumnHeight);
  153. m_computedColumnHeight = 0; // Restart balancing.
  154. } else
  155. setAndConstrainColumnHeight(heightAdjustedForSetOffset(multicolBlock->columnHeightAvailable()));
  156. // Nuke previously stored minimum column height. Contents may have changed for all we know.
  157. m_minimumColumnHeight = 0;
  158. }
  159. void RenderMultiColumnSet::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
  160. {
  161. computedValues.m_extent = m_computedColumnHeight;
  162. computedValues.m_position = logicalTop;
  163. }
  164. LayoutUnit RenderMultiColumnSet::columnGap() const
  165. {
  166. // FIXME: Eventually we will cache the column gap when the widths of columns start varying, but for now we just
  167. // go to the parent block to get the gap.
  168. RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent());
  169. if (parentBlock->style()->hasNormalColumnGap())
  170. return parentBlock->style()->fontDescription().computedPixelSize(); // "1em" is recommended as the normal gap setting. Matches <p> margins.
  171. return parentBlock->style()->columnGap();
  172. }
  173. unsigned RenderMultiColumnSet::columnCount() const
  174. {
  175. // We must always return a value of 1 or greater. Column count = 0 is a meaningless situation,
  176. // and will confuse and cause problems in other parts of the code.
  177. if (!computedColumnHeight())
  178. return 1;
  179. // Our portion rect determines our column count. We have as many columns as needed to fit all the content.
  180. LayoutUnit logicalHeightInColumns = flowThread()->isHorizontalWritingMode() ? flowThreadPortionRect().height() : flowThreadPortionRect().width();
  181. unsigned count = ceil(static_cast<float>(logicalHeightInColumns) / computedColumnHeight());
  182. ASSERT(count >= 1);
  183. return count;
  184. }
  185. LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const
  186. {
  187. LayoutUnit colLogicalWidth = computedColumnWidth();
  188. LayoutUnit colLogicalHeight = computedColumnHeight();
  189. LayoutUnit colLogicalTop = borderAndPaddingBefore();
  190. LayoutUnit colLogicalLeft = borderAndPaddingLogicalLeft();
  191. LayoutUnit colGap = columnGap();
  192. if (style()->isLeftToRightDirection())
  193. colLogicalLeft += index * (colLogicalWidth + colGap);
  194. else
  195. colLogicalLeft += contentLogicalWidth() - colLogicalWidth - index * (colLogicalWidth + colGap);
  196. if (isHorizontalWritingMode())
  197. return LayoutRect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLogicalHeight);
  198. return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogicalWidth);
  199. }
  200. unsigned RenderMultiColumnSet::columnIndexAtOffset(LayoutUnit offset, ColumnIndexCalculationMode mode) const
  201. {
  202. LayoutRect portionRect(flowThreadPortionRect());
  203. // Handle the offset being out of range.
  204. LayoutUnit flowThreadLogicalTop = isHorizontalWritingMode() ? portionRect.y() : portionRect.x();
  205. if (offset < flowThreadLogicalTop)
  206. return 0;
  207. // If we're laying out right now, we cannot constrain against some logical bottom, since it
  208. // isn't known yet. Otherwise, just return the last column if we're past the logical bottom.
  209. if (mode == ClampToExistingColumns) {
  210. LayoutUnit flowThreadLogicalBottom = isHorizontalWritingMode() ? portionRect.maxY() : portionRect.maxX();
  211. if (offset >= flowThreadLogicalBottom)
  212. return columnCount() - 1;
  213. }
  214. // Just divide by the column height to determine the correct column.
  215. return static_cast<float>(offset - flowThreadLogicalTop) / computedColumnHeight();
  216. }
  217. LayoutRect RenderMultiColumnSet::flowThreadPortionRectAt(unsigned index) const
  218. {
  219. LayoutRect portionRect = flowThreadPortionRect();
  220. if (isHorizontalWritingMode())
  221. portionRect = LayoutRect(portionRect.x(), portionRect.y() + index * computedColumnHeight(), portionRect.width(), computedColumnHeight());
  222. else
  223. portionRect = LayoutRect(portionRect.x() + index * computedColumnHeight(), portionRect.y(), computedColumnHeight(), portionRect.height());
  224. return portionRect;
  225. }
  226. LayoutRect RenderMultiColumnSet::flowThreadPortionOverflowRect(const LayoutRect& portionRect, unsigned index, unsigned colCount, LayoutUnit colGap) const
  227. {
  228. // This function determines the portion of the flow thread that paints for the column. Along the inline axis, columns are
  229. // unclipped at outside edges (i.e., the first and last column in the set), and they clip to half the column
  230. // gap along interior edges.
  231. //
  232. // In the block direction, we will not clip overflow out of the top of the first column, or out of the bottom of
  233. // the last column. This applies only to the true first column and last column across all column sets.
  234. //
  235. // FIXME: Eventually we will know overflow on a per-column basis, but we can't do this until we have a painting
  236. // mode that understands not to paint contents from a previous column in the overflow area of a following column.
  237. // This problem applies to regions and pages as well and is not unique to columns.
  238. bool isFirstColumn = !index;
  239. bool isLastColumn = index == colCount - 1;
  240. bool isLeftmostColumn = style()->isLeftToRightDirection() ? isFirstColumn : isLastColumn;
  241. bool isRightmostColumn = style()->isLeftToRightDirection() ? isLastColumn : isFirstColumn;
  242. LayoutRect overflowRect(portionRect);
  243. if (isHorizontalWritingMode()) {
  244. if (isLeftmostColumn) {
  245. // Shift to the logical left overflow of the flow thread to make sure it's all covered.
  246. overflowRect.shiftXEdgeTo(min(flowThread()->visualOverflowRect().x(), portionRect.x()));
  247. } else {
  248. // Expand into half of the logical left column gap.
  249. overflowRect.shiftXEdgeTo(portionRect.x() - colGap / 2);
  250. }
  251. if (isRightmostColumn) {
  252. // Shift to the logical right overflow of the flow thread to ensure content can spill out of the column.
  253. overflowRect.shiftMaxXEdgeTo(max(flowThread()->visualOverflowRect().maxX(), portionRect.maxX()));
  254. } else {
  255. // Expand into half of the logical right column gap.
  256. overflowRect.shiftMaxXEdgeTo(portionRect.maxX() + colGap / 2);
  257. }
  258. } else {
  259. if (isLeftmostColumn) {
  260. // Shift to the logical left overflow of the flow thread to make sure it's all covered.
  261. overflowRect.shiftYEdgeTo(min(flowThread()->visualOverflowRect().y(), portionRect.y()));
  262. } else {
  263. // Expand into half of the logical left column gap.
  264. overflowRect.shiftYEdgeTo(portionRect.y() - colGap / 2);
  265. }
  266. if (isRightmostColumn) {
  267. // Shift to the logical right overflow of the flow thread to ensure content can spill out of the column.
  268. overflowRect.shiftMaxYEdgeTo(max(flowThread()->visualOverflowRect().maxY(), portionRect.maxY()));
  269. } else {
  270. // Expand into half of the logical right column gap.
  271. overflowRect.shiftMaxYEdgeTo(portionRect.maxY() + colGap / 2);
  272. }
  273. }
  274. return overflowRectForFlowThreadPortion(overflowRect, isFirstRegion() && isFirstColumn, isLastRegion() && isLastColumn);
  275. }
  276. void RenderMultiColumnSet::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
  277. {
  278. if (style()->visibility() != VISIBLE)
  279. return;
  280. RenderBlock::paintObject(paintInfo, paintOffset);
  281. // FIXME: Right now we're only painting in the foreground phase.
  282. // Columns should technically respect phases and allow for background/float/foreground overlap etc., just like
  283. // RenderBlocks do. Note this is a pretty minor issue, since the old column implementation clipped columns
  284. // anyway, thus making it impossible for them to overlap one another. It's also really unlikely that the columns
  285. // would overlap another block.
  286. if (!m_flowThread || !isValid() || (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection))
  287. return;
  288. paintColumnRules(paintInfo, paintOffset);
  289. }
  290. void RenderMultiColumnSet::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
  291. {
  292. if (paintInfo.context->paintingDisabled())
  293. return;
  294. RenderStyle* blockStyle = toRenderMultiColumnBlock(parent())->style();
  295. const Color& ruleColor = blockStyle->visitedDependentColor(CSSPropertyWebkitColumnRuleColor);
  296. bool ruleTransparent = blockStyle->columnRuleIsTransparent();
  297. EBorderStyle ruleStyle = blockStyle->columnRuleStyle();
  298. LayoutUnit ruleThickness = blockStyle->columnRuleWidth();
  299. LayoutUnit colGap = columnGap();
  300. bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent;
  301. if (!renderRule)
  302. return;
  303. unsigned colCount = columnCount();
  304. if (colCount <= 1)
  305. return;
  306. bool antialias = shouldAntialiasLines(paintInfo.context);
  307. bool leftToRight = style()->isLeftToRightDirection();
  308. LayoutUnit currLogicalLeftOffset = leftToRight ? LayoutUnit() : contentLogicalWidth();
  309. LayoutUnit ruleAdd = borderAndPaddingLogicalLeft();
  310. LayoutUnit ruleLogicalLeft = leftToRight ? LayoutUnit() : contentLogicalWidth();
  311. LayoutUnit inlineDirectionSize = computedColumnWidth();
  312. BoxSide boxSide = isHorizontalWritingMode()
  313. ? leftToRight ? BSLeft : BSRight
  314. : leftToRight ? BSTop : BSBottom;
  315. for (unsigned i = 0; i < colCount; i++) {
  316. // Move to the next position.
  317. if (leftToRight) {
  318. ruleLogicalLeft += inlineDirectionSize + colGap / 2;
  319. currLogicalLeftOffset += inlineDirectionSize + colGap;
  320. } else {
  321. ruleLogicalLeft -= (inlineDirectionSize + colGap / 2);
  322. currLogicalLeftOffset -= (inlineDirectionSize + colGap);
  323. }
  324. // Now paint the column rule.
  325. if (i < colCount - 1) {
  326. LayoutUnit ruleLeft = isHorizontalWritingMode() ? paintOffset.x() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd : paintOffset.x() + borderLeft() + paddingLeft();
  327. LayoutUnit ruleRight = isHorizontalWritingMode() ? ruleLeft + ruleThickness : ruleLeft + contentWidth();
  328. LayoutUnit ruleTop = isHorizontalWritingMode() ? paintOffset.y() + borderTop() + paddingTop() : paintOffset.y() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd;
  329. LayoutUnit ruleBottom = isHorizontalWritingMode() ? ruleTop + contentHeight() : ruleTop + ruleThickness;
  330. IntRect pixelSnappedRuleRect = pixelSnappedIntRectFromEdges(ruleLeft, ruleTop, ruleRight, ruleBottom);
  331. drawLineForBoxSide(paintInfo.context, pixelSnappedRuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias);
  332. }
  333. ruleLogicalLeft = currLogicalLeftOffset;
  334. }
  335. }
  336. void RenderMultiColumnSet::repaintFlowThreadContent(const LayoutRect& repaintRect, bool immediate) const
  337. {
  338. // Figure out the start and end columns and only check within that range so that we don't walk the
  339. // entire column set. Put the repaint rect into flow thread coordinates by flipping it first.
  340. LayoutRect flowThreadRepaintRect(repaintRect);
  341. flowThread()->flipForWritingMode(flowThreadRepaintRect);
  342. // Now we can compare this rect with the flow thread portions owned by each column. First let's
  343. // just see if the repaint rect intersects our flow thread portion at all.
  344. LayoutRect clippedRect(flowThreadRepaintRect);
  345. clippedRect.intersect(RenderRegion::flowThreadPortionOverflowRect());
  346. if (clippedRect.isEmpty())
  347. return;
  348. // Now we know we intersect at least one column. Let's figure out the logical top and logical
  349. // bottom of the area we're repainting.
  350. LayoutUnit repaintLogicalTop = isHorizontalWritingMode() ? flowThreadRepaintRect.y() : flowThreadRepaintRect.x();
  351. LayoutUnit repaintLogicalBottom = (isHorizontalWritingMode() ? flowThreadRepaintRect.maxY() : flowThreadRepaintRect.maxX()) - 1;
  352. unsigned startColumn = columnIndexAtOffset(repaintLogicalTop);
  353. unsigned endColumn = columnIndexAtOffset(repaintLogicalBottom);
  354. LayoutUnit colGap = columnGap();
  355. unsigned colCount = columnCount();
  356. for (unsigned i = startColumn; i <= endColumn; i++) {
  357. LayoutRect colRect = columnRectAt(i);
  358. // Get the portion of the flow thread that corresponds to this column.
  359. LayoutRect flowThreadPortion = flowThreadPortionRectAt(i);
  360. // Now get the overflow rect that corresponds to the column.
  361. LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap);
  362. // Do a repaint for this specific column.
  363. repaintFlowThreadContentRectangle(repaintRect, immediate, flowThreadPortion, flowThreadOverflowPortion, colRect.location());
  364. }
  365. }
  366. void RenderMultiColumnSet::collectLayerFragments(LayerFragments& fragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect)
  367. {
  368. // Put the layer bounds into flow thread-local coordinates by flipping it first.
  369. LayoutRect layerBoundsInFlowThread(layerBoundingBox);
  370. flowThread()->flipForWritingMode(layerBoundsInFlowThread);
  371. // Do the same for the dirty rect.
  372. LayoutRect dirtyRectInFlowThread(dirtyRect);
  373. flowThread()->flipForWritingMode(dirtyRectInFlowThread);
  374. // Now we can compare with the flow thread portions owned by each column. First let's
  375. // see if the rect intersects our flow thread portion at all.
  376. LayoutRect clippedRect(layerBoundsInFlowThread);
  377. clippedRect.intersect(RenderRegion::flowThreadPortionOverflowRect());
  378. if (clippedRect.isEmpty())
  379. return;
  380. // Now we know we intersect at least one column. Let's figure out the logical top and logical
  381. // bottom of the area we're checking.
  382. LayoutUnit layerLogicalTop = isHorizontalWritingMode() ? layerBoundsInFlowThread.y() : layerBoundsInFlowThread.x();
  383. LayoutUnit layerLogicalBottom = (isHorizontalWritingMode() ? layerBoundsInFlowThread.maxY() : layerBoundsInFlowThread.maxX()) - 1;
  384. // Figure out the start and end columns and only check within that range so that we don't walk the
  385. // entire column set.
  386. unsigned startColumn = columnIndexAtOffset(layerLogicalTop);
  387. unsigned endColumn = columnIndexAtOffset(layerLogicalBottom);
  388. LayoutUnit colLogicalWidth = computedColumnWidth();
  389. LayoutUnit colGap = columnGap();
  390. unsigned colCount = columnCount();
  391. for (unsigned i = startColumn; i <= endColumn; i++) {
  392. // Get the portion of the flow thread that corresponds to this column.
  393. LayoutRect flowThreadPortion = flowThreadPortionRectAt(i);
  394. // Now get the overflow rect that corresponds to the column.
  395. LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap);
  396. // In order to create a fragment we must intersect the portion painted by this column.
  397. LayoutRect clippedRect(layerBoundsInFlowThread);
  398. clippedRect.intersect(flowThreadOverflowPortion);
  399. if (clippedRect.isEmpty())
  400. continue;
  401. // We also need to intersect the dirty rect. We have to apply a translation and shift based off
  402. // our column index.
  403. LayoutPoint translationOffset;
  404. LayoutUnit inlineOffset = i * (colLogicalWidth + colGap);
  405. if (!style()->isLeftToRightDirection())
  406. inlineOffset = -inlineOffset;
  407. translationOffset.setX(inlineOffset);
  408. LayoutUnit blockOffset = isHorizontalWritingMode() ? -flowThreadPortion.y() : -flowThreadPortion.x();
  409. if (isFlippedBlocksWritingMode(style()->writingMode()))
  410. blockOffset = -blockOffset;
  411. translationOffset.setY(blockOffset);
  412. if (!isHorizontalWritingMode())
  413. translationOffset = translationOffset.transposedPoint();
  414. // FIXME: The translation needs to include the multicolumn set's content offset within the
  415. // multicolumn block as well. This won't be an issue until we start creating multiple multicolumn sets.
  416. // Shift the dirty rect to be in flow thread coordinates with this translation applied.
  417. LayoutRect translatedDirtyRect(dirtyRectInFlowThread);
  418. translatedDirtyRect.moveBy(-translationOffset);
  419. // See if we intersect the dirty rect.
  420. clippedRect = layerBoundsInFlowThread;
  421. clippedRect.intersect(translatedDirtyRect);
  422. if (clippedRect.isEmpty())
  423. continue;
  424. // Something does need to paint in this column. Make a fragment now and supply the physical translation
  425. // offset and the clip rect for the column with that offset applied.
  426. LayerFragment fragment;
  427. fragment.paginationOffset = translationOffset;
  428. LayoutRect flippedFlowThreadOverflowPortion(flowThreadOverflowPortion);
  429. flipForWritingMode(flippedFlowThreadOverflowPortion);
  430. fragment.paginationClip = flippedFlowThreadOverflowPortion;
  431. fragments.append(fragment);
  432. }
  433. }
  434. const char* RenderMultiColumnSet::renderName() const
  435. {
  436. return "RenderMultiColumnSet";
  437. }
  438. }