// The following rules apply for resolving conflicts and figuring out which border // to use. // (1) Borders with the 'border-style' of 'hidden' take precedence over all other conflicting // borders. Any border with this value suppresses all borders at this location. // (2) Borders with a style of 'none' have the lowest priority. Only if the border properties of all // the elements meeting at this edge are 'none' will the border be omitted (but note that 'none' is // the default value for the border style.) // (3) If none of the styles are 'hidden' and at least one of them is not 'none', then narrow borders // are discarded in favor of wider ones. If several have the same 'border-width' then styles are preferred // in this order: 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove', and the lowest: 'inset'. // (4) If border styles differ only in color, then a style set on a cell wins over one on a row, // which wins over a row group, column, column group and, lastly, table. It is undefined which color // is used when two elements of the same type disagree. static CollapsedBorderValue compareBorders(const CollapsedBorderValue& border1, const CollapsedBorderValue& border2) { // Sanity check the values passed in. If either is null, return the other. if (!border2.exists()) return border1; if (!border1.exists()) return border2; // Rule #1 above. if (border1.style() == BHIDDEN || border2.style() == BHIDDEN) return CollapsedBorderValue(); // No border should exist at this location. // Rule #2 above. A style of 'none' has lowest priority and always loses to any other border. if (border2.style() == BNONE) return border1; if (border1.style() == BNONE) return border2; // The first part of rule #3 above. Wider borders win. if (border1.width() != border2.width()) return border1.width() > border2.width() ? border1 : border2; // The borders have equal width. Sort by border style. if (border1.style() != border2.style()) return border1.style() > border2.style() ? border1 : border2; // The border have the same width and style. Rely on precedence (cell over row over row group, etc.) return border1.precedence >= border2.precedence ? border1 : border2; }
void RenderTableCell::paintCollapsedBorder(GraphicsContext* graphicsContext, const IntRect& paintRect) { if (!table()->currentBorderStyle() || graphicsContext->paintingDisabled()) return; CollapsedBorderValue leftVal = collapsedLeftBorder(); CollapsedBorderValue rightVal = collapsedRightBorder(); CollapsedBorderValue topVal = collapsedTopBorder(); CollapsedBorderValue bottomVal = collapsedBottomBorder(); // Adjust our x/y/width/height so that we paint the collapsed borders at the correct location. int topWidth = topVal.width(); int bottomWidth = bottomVal.width(); int leftWidth = leftVal.width(); int rightWidth = rightVal.width(); int x = paintRect.x() - leftWidth / 2; int y = paintRect.y() - topWidth / 2; int w = paintRect.width() + leftWidth / 2 + (rightWidth + 1) / 2; int h = paintRect.height() + topWidth / 2 + (bottomWidth + 1) / 2; EBorderStyle topStyle = collapsedBorderStyle(topVal.style()); EBorderStyle bottomStyle = collapsedBorderStyle(bottomVal.style()); EBorderStyle leftStyle = collapsedBorderStyle(leftVal.style()); EBorderStyle rightStyle = collapsedBorderStyle(rightVal.style()); bool renderTop = topStyle > BHIDDEN && !topVal.isTransparent(); bool renderBottom = bottomStyle > BHIDDEN && !bottomVal.isTransparent(); bool renderLeft = leftStyle > BHIDDEN && !leftVal.isTransparent(); bool renderRight = rightStyle > BHIDDEN && !rightVal.isTransparent(); // We never paint diagonals at the joins. We simply let the border with the highest // precedence paint on top of borders with lower precedence. CollapsedBorders borders; borders.addBorder(topVal, BSTop, renderTop, x, y, x + w, y + topWidth, topStyle); borders.addBorder(bottomVal, BSBottom, renderBottom, x, y + h - bottomWidth, x + w, y + h, bottomStyle); borders.addBorder(leftVal, BSLeft, renderLeft, x, y, x + leftWidth, y + h, leftStyle); borders.addBorder(rightVal, BSRight, renderRight, x + w - rightWidth, y, x + w, y + h, rightStyle); const AffineTransform& currentCTM = graphicsContext->getCTM(); bool antialias = !currentCTM.isIdentityOrTranslationOrFlipped(); for (CollapsedBorder* border = borders.nextBorder(); border; border = borders.nextBorder()) { if (border->borderValue == *table()->currentBorderStyle()) drawLineForBoxSide(graphicsContext, border->x1, border->y1, border->x2, border->y2, border->side, border->borderValue.color(), border->style, 0, 0, antialias); } }
void RenderTableCell::paintCollapsedBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h) { if (!table()->currentBorderStyle()) return; bool rtl = table()->style()->direction() == RTL; CollapsedBorderValue leftVal = collapsedLeftBorder(rtl); CollapsedBorderValue rightVal = collapsedRightBorder(rtl); CollapsedBorderValue topVal = collapsedTopBorder(); CollapsedBorderValue bottomVal = collapsedBottomBorder(); // Adjust our x/y/width/height so that we paint the collapsed borders at the correct location. int topWidth = topVal.width(); int bottomWidth = bottomVal.width(); int leftWidth = leftVal.width(); int rightWidth = rightVal.width(); tx -= leftWidth / 2; ty -= topWidth / 2; w += leftWidth / 2 + (rightWidth + 1) / 2; h += topWidth / 2 + (bottomWidth + 1) / 2; EBorderStyle topStyle = collapsedBorderStyle(topVal.style()); EBorderStyle bottomStyle = collapsedBorderStyle(bottomVal.style()); EBorderStyle leftStyle = collapsedBorderStyle(leftVal.style()); EBorderStyle rightStyle = collapsedBorderStyle(rightVal.style()); bool renderTop = topStyle > BHIDDEN && !topVal.isTransparent(); bool renderBottom = bottomStyle > BHIDDEN && !bottomVal.isTransparent(); bool renderLeft = leftStyle > BHIDDEN && !leftVal.isTransparent(); bool renderRight = rightStyle > BHIDDEN && !rightVal.isTransparent(); // We never paint diagonals at the joins. We simply let the border with the highest // precedence paint on top of borders with lower precedence. CollapsedBorders borders; borders.addBorder(topVal, BSTop, renderTop, tx, ty, tx + w, ty + topWidth, topStyle); borders.addBorder(bottomVal, BSBottom, renderBottom, tx, ty + h - bottomWidth, tx + w, ty + h, bottomStyle); borders.addBorder(leftVal, BSLeft, renderLeft, tx, ty, tx + leftWidth, ty + h, leftStyle); borders.addBorder(rightVal, BSRight, renderRight, tx + w - rightWidth, ty, tx + w, ty + h, rightStyle); for (CollapsedBorder* border = borders.nextBorder(); border; border = borders.nextBorder()) { if (border->borderValue == *table()->currentBorderStyle()) drawLineForBoxSide(graphicsContext, border->x1, border->y1, border->x2, border->y2, border->side, border->borderValue.color(), style()->color(), border->style, 0, 0); } }
// The following rules apply for resolving conflicts and figuring out which border // to use. // (1) Borders with the 'border-style' of 'hidden' take precedence over all other conflicting // borders. Any border with this value suppresses all borders at this location. // (2) Borders with a style of 'none' have the lowest priority. Only if the border properties of all // the elements meeting at this edge are 'none' will the border be omitted (but note that 'none' is // the default value for the border style.) // (3) If none of the styles are 'hidden' and at least one of them is not 'none', then narrow borders // are discarded in favor of wider ones. If several have the same 'border-width' then styles are preferred // in this order: 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove', and the lowest: 'inset'. // (4) If border styles differ only in color, then a style set on a cell wins over one on a row, // which wins over a row group, column, column group and, lastly, table. It is undefined which color // is used when two elements of the same type disagree. static int compareBorders(const CollapsedBorderValue& border1, const CollapsedBorderValue& border2) { // Sanity check the values passed in. The null border have lowest priority. if (!border2.exists()) { if (!border1.exists()) return 0; return 1; } if (!border1.exists()) return -1; // Rule #1 above. if (border2.style() == BHIDDEN) { if (border1.style() == BHIDDEN) return 0; return -1; } if (border1.style() == BHIDDEN) return 1; // Rule #2 above. A style of 'none' has lowest priority and always loses to any other border. if (border2.style() == BNONE) { if (border1.style() == BNONE) return 0; return 1; } if (border1.style() == BNONE) return -1; // The first part of rule #3 above. Wider borders win. if (border1.width() != border2.width()) return border1.width() < border2.width() ? -1 : 1; // The borders have equal width. Sort by border style. if (border1.style() != border2.style()) return border1.style() < border2.style() ? -1 : 1; // The border have the same width and style. Rely on precedence (cell over row over row group, etc.) if (border1.precedence() == border2.precedence()) return 0; return border1.precedence() < border2.precedence() ? -1 : 1; }
void RenderTableCell::readyWRATHWidgetCollapsedBorder(PaintedWidgetsOfWRATHHandle& handle, ContextOfWRATH *wrath_context, int tx, int ty, int w, int h) { RenderTableCell_ReadyWRATHWidgetCollapsedBorder *d(RenderTableCell_ReadyWRATHWidgetCollapsedBorder::object(this, handle)); ContextOfWRATH::AutoPushNode autoPushRoot(wrath_context, d->m_root_node); ContextOfWRATH::AutoPushNode autoPushSkip(wrath_context, d->m_skip_node); if (!table()->currentBorderStyle()) { d->m_skip_node.widget()->visible(false); return; } d->m_skip_node.widget()->visible(true); CollapsedBorderValue leftVal = collapsedLeftBorder(); CollapsedBorderValue rightVal = collapsedRightBorder(); CollapsedBorderValue topVal = collapsedTopBorder(); CollapsedBorderValue bottomVal = collapsedBottomBorder(); // Adjust our x/y/width/height so that we paint the collapsed borders at the correct location. int topWidth = topVal.width(); int bottomWidth = bottomVal.width(); int leftWidth = leftVal.width(); int rightWidth = rightVal.width(); tx -= leftWidth / 2; ty -= topWidth / 2; w += leftWidth / 2 + (rightWidth + 1) / 2; h += topWidth / 2 + (bottomWidth + 1) / 2; EBorderStyle topStyle = collapsedBorderStyle(topVal.style()); EBorderStyle bottomStyle = collapsedBorderStyle(bottomVal.style()); EBorderStyle leftStyle = collapsedBorderStyle(leftVal.style()); EBorderStyle rightStyle = collapsedBorderStyle(rightVal.style()); bool renderTop = topStyle > BHIDDEN && !topVal.isTransparent(); bool renderBottom = bottomStyle > BHIDDEN && !bottomVal.isTransparent(); bool renderLeft = leftStyle > BHIDDEN && !leftVal.isTransparent(); bool renderRight = rightStyle > BHIDDEN && !rightVal.isTransparent(); // We never paint diagonals at the joins. We simply let the border with the highest // precedence paint on top of borders with lower precedence. CollapsedBorders borders; borders.addBorder(topVal, BSTop, renderTop, tx, ty, tx + w, ty + topWidth, topStyle); borders.addBorder(bottomVal, BSBottom, renderBottom, tx, ty + h - bottomWidth, tx + w, ty + h, bottomStyle); borders.addBorder(leftVal, BSLeft, renderLeft, tx, ty, tx + leftWidth, ty + h, leftStyle); borders.addBorder(rightVal, BSRight, renderRight, tx + w - rightWidth, ty, tx + w, ty + h, rightStyle); /* [WRATH-DANGER]: These are not used at the moment const AffineTransform& currentCTM = graphicsContext->getCTM(); bool antialias = !currentCTM.isIdentityOrTranslationOrFlipped(); */ d->m_top.visible(false); d->m_bottom.visible(false); d->m_left.visible(false); d->m_right.visible(false); for (CollapsedBorder* border = borders.nextBorder(); border; border = borders.nextBorder()) { if (border->borderValue == *table()->currentBorderStyle()) { PaintedWidgetsOfWRATHHandle* bh = NULL; if (border->borderValue == topVal) bh = &d->m_top; else if (border->borderValue == bottomVal) bh = &d->m_bottom; else if (border->borderValue == leftVal) bh = &d->m_left; else if (border->borderValue == rightVal) bh = &d->m_right; else ASSERT_NOT_REACHED(); bh->visible(true); readyWRATHWidgetDrawLineForBoxSide(*bh, wrath_context, border->x1, border->y1, border->x2, border->y2, border->side, border->borderValue.color(), border->style, 0, 0, false /*antialias*/); } } }
void TableCellPainter::paintCollapsedBorders(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { ASSERT(paintInfo.phase == PaintPhaseCollapsedTableBorders); if (!paintInfo.shouldPaintWithinRoot(&m_renderTableCell) || m_renderTableCell.style()->visibility() != VISIBLE) return; LayoutRect paintRect = paintBounds(paintOffset, AddOffsetFromParent); if (paintRect.y() - m_renderTableCell.table()->outerBorderTop() >= paintInfo.rect.maxY()) return; if (paintRect.maxY() + m_renderTableCell.table()->outerBorderBottom() <= paintInfo.rect.y()) return; if (!m_renderTableCell.table()->currentBorderValue()) return; RenderDrawingRecorder recorder(paintInfo.context, m_renderTableCell, paintInfo.phase, paintRect); if (recorder.canUseCachedDrawing()) return; const RenderStyle* styleForCellFlow = m_renderTableCell.styleForCellFlow(); CollapsedBorderValue leftVal = cachedCollapsedLeftBorder(styleForCellFlow); CollapsedBorderValue rightVal = cachedCollapsedRightBorder(styleForCellFlow); CollapsedBorderValue topVal = cachedCollapsedTopBorder(styleForCellFlow); CollapsedBorderValue bottomVal = cachedCollapsedBottomBorder(styleForCellFlow); // Adjust our x/y/width/height so that we paint the collapsed borders at the correct location. int topWidth = topVal.width(); int bottomWidth = bottomVal.width(); int leftWidth = leftVal.width(); int rightWidth = rightVal.width(); IntRect borderRect = pixelSnappedIntRect(paintRect.x() - leftWidth / 2, paintRect.y() - topWidth / 2, paintRect.width() + leftWidth / 2 + (rightWidth + 1) / 2, paintRect.height() + topWidth / 2 + (bottomWidth + 1) / 2); EBorderStyle topStyle = collapsedBorderStyle(topVal.style()); EBorderStyle bottomStyle = collapsedBorderStyle(bottomVal.style()); EBorderStyle leftStyle = collapsedBorderStyle(leftVal.style()); EBorderStyle rightStyle = collapsedBorderStyle(rightVal.style()); bool renderTop = topStyle > BHIDDEN && !topVal.isTransparent(); bool renderBottom = bottomStyle > BHIDDEN && !bottomVal.isTransparent(); bool renderLeft = leftStyle > BHIDDEN && !leftVal.isTransparent(); bool renderRight = rightStyle > BHIDDEN && !rightVal.isTransparent(); // We never paint diagonals at the joins. We simply let the border with the highest // precedence paint on top of borders with lower precedence. CollapsedBorders borders; borders.addBorder(topVal, BSTop, renderTop, borderRect.x(), borderRect.y(), borderRect.maxX(), borderRect.y() + topWidth, topStyle); borders.addBorder(bottomVal, BSBottom, renderBottom, borderRect.x(), borderRect.maxY() - bottomWidth, borderRect.maxX(), borderRect.maxY(), bottomStyle); borders.addBorder(leftVal, BSLeft, renderLeft, borderRect.x(), borderRect.y(), borderRect.x() + leftWidth, borderRect.maxY(), leftStyle); borders.addBorder(rightVal, BSRight, renderRight, borderRect.maxX() - rightWidth, borderRect.y(), borderRect.maxX(), borderRect.maxY(), rightStyle); GraphicsContext* graphicsContext = paintInfo.context; bool antialias = BoxPainter::shouldAntialiasLines(graphicsContext); for (CollapsedBorder* border = borders.nextBorder(); border; border = borders.nextBorder()) { if (border->borderValue.isSameIgnoringColor(*m_renderTableCell.table()->currentBorderValue())) { ObjectPainter::drawLineForBoxSide(graphicsContext, border->x1, border->y1, border->x2, border->y2, border->side, border->borderValue.color().resolve(m_renderTableCell.resolveColor(CSSPropertyColor)), border->style, 0, 0, antialias); } } }