From a519244d2606e24e25bbcd0adfd5c9a9d8568a3e Mon Sep 17 00:00:00 2001 From: Bernd Date: Thu, 5 Nov 2009 10:26:00 +0100 Subject: [PATCH] reorganize border collapse painting code to make a little bit more readable bug 452319 r=fantasai sr=bz --- layout/tables/nsCellMap.h | 4 +- layout/tables/nsTableFrame.cpp | 1782 +++++++++++++++++++------------- 2 files changed, 1069 insertions(+), 717 deletions(-) diff --git a/layout/tables/nsCellMap.h b/layout/tables/nsCellMap.h index 65daaf6bdb98..5481c6caaa25 100644 --- a/layout/tables/nsCellMap.h +++ b/layout/tables/nsCellMap.h @@ -268,7 +268,7 @@ protected: friend class nsCellMap; friend class BCMapCellIterator; - friend class BCMapBorderIterator; + friend class BCPaintBorderIterator; friend class nsCellMapColumnIterator; /** Insert a row group cellmap after aPrevMap, if aPrefMap is null insert it @@ -474,7 +474,7 @@ public: protected: friend class nsTableCellMap; friend class BCMapCellIterator; - friend class BCMapBorderIterator; + friend class BCPaintBorderIterator; friend class nsTableFrame; friend class nsCellMapColumnIterator; diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index 3480382faf6c..e23da75f27ed 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -2367,9 +2367,9 @@ nsTableFrame::GetUsedPadding() const } static void -DivideBCBorderSize(nscoord aPixelSize, - nscoord& aSmallHalf, - nscoord& aLargeHalf) +DivideBCBorderSize(BCPixelSize aPixelSize, + BCPixelSize& aSmallHalf, + BCPixelSize& aLargeHalf) { aSmallHalf = aPixelSize / 2; aLargeHalf = aPixelSize - aSmallHalf; @@ -5047,7 +5047,8 @@ BCCellBorders::BCCellBorders(PRInt32 aNumBorders, } // this function sets the new border properties and returns true if the border -// segment will start a new segment and not prolong the existing segment. +// segment will start a new segment and not be accumulated into the previous +// segment. static PRBool SetBorder(const BCCellBorder& aNewBorder, BCCellBorder& aBorder) @@ -6082,7 +6083,6 @@ nsTableFrame::CalcBCBorders() } } } // for (iter.First(info); info.mCell; iter.Next(info)) { - // reset the bc flag and damage area SetNeedToCalcBCBorders(PR_FALSE); propData->mDamageArea.x = propData->mDamageArea.y = propData->mDamageArea.width = propData->mDamageArea.height = 0; @@ -6091,264 +6091,557 @@ nsTableFrame::CalcBCBorders() #endif } +class BCPaintBorderIterator; + +struct BCVerticalSeg +{ + BCVerticalSeg(); + + void Start(BCPaintBorderIterator& aIter, + BCBorderOwner aBorderOwner, + BCPixelSize aVerSegWidth, + BCPixelSize aHorSegHeight); + + void Initialize(BCPaintBorderIterator& aIter); + void GetBottomCorner(BCPaintBorderIterator& aIter, + BCPixelSize aHorSegHeight); + + + void Paint(BCPaintBorderIterator& aIter, + nsIRenderingContext& aRenderingContext, + BCPixelSize aHorSegHeight); + void AdvanceOffsetY(); + void IncludeCurrentBorder(BCPaintBorderIterator& aIter); + + + union { + nsTableColFrame* mCol; + PRInt32 mColWidth; + }; + nscoord mOffsetX; // x-offset with respect to the table edge + nscoord mOffsetY; // y-offset with respect to the table edge + nscoord mLength; // vertical length including corners + BCPixelSize mWidth; // width in pixels + + nsTableCellFrame* mAjaCell; // previous sibling to the first cell + // where the segment starts, it can be + // the owner of a segment + nsTableCellFrame* mFirstCell; // cell at the start of the segment + nsTableRowGroupFrame* mFirstRowGroup; // row group at the start of the segment + nsTableRowFrame* mFirstRow; // row at the start of the segment + nsTableCellFrame* mLastCell; // cell at the current end of the + // segment + + + PRUint8 mOwner; // owner of the border, defines the + // style + PRUint8 mTopBevelSide; // direction to bevel at the top + nscoord mTopBevelOffset; // how much to bevel at the top + BCPixelSize mBottomHorSegHeight; // height of the crossing + //horizontal border + nscoord mBottomOffset; // how much longer is the segment due + // to the horizontal border, by this + // amount the next segment needs to be + // shifted. + PRBool mIsBottomBevel; // should we bevel at the bottom +}; + +struct BCHorizontalSeg +{ + BCHorizontalSeg(); + + void Start(BCPaintBorderIterator& aIter, + BCBorderOwner aBorderOwner, + BCPixelSize aBottomVerSegWidth, + BCPixelSize aHorSegHeight); + void GetRightCorner(BCPaintBorderIterator& aIter, + BCPixelSize aLeftSegWidth); + void AdvanceOffsetX(PRInt32 aIncrement); + void IncludeCurrentBorder(BCPaintBorderIterator& aIter); + void Paint(BCPaintBorderIterator& aIter, + nsIRenderingContext& aRenderingContext); + + nscoord mOffsetX; // x-offset with respect to the table edge + nscoord mOffsetY; // y-offset with respect to the table edge + nscoord mLength; // horizontal length including corners + BCPixelSize mWidth; // border width in pixels + nscoord mLeftBevelOffset; // how much to bevel at the left + PRUint8 mLeftBevelSide; // direction to bevel at the left + PRBool mIsRightBevel; // should we bevel at the right end + nscoord mRightBevelOffset; // how much to bevel at the right + PRUint8 mRightBevelSide; // direction to bevel at the right + nscoord mEndOffset; // how much longer is the segment due + // to the vertical border, by this + // amount the next segment needs to be + // shifted. + PRUint8 mOwner; // owner of the border, defines the + // style + nsTableCellFrame* mFirstCell; // cell at the start of the segment + nsTableCellFrame* mAjaCell; // neighboring cell to the first cell + // where the segment starts, it can be + // the owner of a segment +}; + // Iterates over borders (left border, corner, top border) in the cell map within a damage area // from left to right, top to bottom. All members are in terms of the 1st in flow frames, except // where suffixed by InFlow. -class BCMapBorderIterator +class BCPaintBorderIterator { public: - BCMapBorderIterator(nsTableFrame& aTableFrame, - nsTableRowGroupFrame& aRowGroupFrame, - nsTableRowFrame& aRowFrame, - const nsRect& aDamageArea); - void Reset(nsTableFrame& aTableFrame, - nsTableRowGroupFrame& aRowGroupFrame, - nsTableRowFrame& aRowFrame, - const nsRect& aDamageArea); + + + BCPaintBorderIterator(nsTableFrame* aTable); + ~BCPaintBorderIterator() { if (mVerInfo) { + delete [] mVerInfo; + }} + void Reset(); + + PRBool SetDamageArea(nsRect aDirtyRect); void First(); void Next(); + void AccumulateOrPaintHorizontalSegment(nsIRenderingContext& aRenderingContext); + void AccumulateOrPaintVerticalSegment(nsIRenderingContext& aRenderingContext); + void ResetVerInfo(); + void StoreColumnWidth(PRInt32 aIndex); + PRBool VerticalSegmentOwnsCorner(); - nsTableFrame* table; - nsTableCellMap* tableCellMap; - nsCellMap* cellMap; + nsTableFrame* mTable; + nsTableFrame* mTableFirstInFlow; + nsTableCellMap* mTableCellMap; + nsCellMap* mCellMap; + PRBool mTableIsLTR; + PRInt32 mColInc; // +1 for ltr -1 for rtl + const nsStyleBackground* mTableBgColor; + nsTableFrame::RowGroupArray mRowGroups; - nsTableFrame::RowGroupArray rowGroups; - nsTableRowGroupFrame* prevRg; - nsTableRowGroupFrame* rg; - PRInt32 rowGroupIndex; - PRInt32 fifRowGroupStart; - PRInt32 rowGroupStart; - PRInt32 rowGroupEnd; - PRInt32 numRows; // number of rows in the table and all continuations + nsTableRowGroupFrame* mPrevRg; + nsTableRowGroupFrame* mRg; + PRBool mIsRepeatedHeader; + PRBool mIsRepeatedFooter; + nsTableRowGroupFrame* mStartRg; // first row group in the damagearea + PRInt32 mRgIndex; // current row group index in the + // mRowgroups array + PRInt32 mFifRgFirstRowIndex; // start row index of the first in + // flow of the row group + PRInt32 mRgFirstRowIndex; // row index of the first row in the + // row group + PRInt32 mRgLastRowIndex; // row index of the last row in the row + // group + PRInt32 mNumTableRows; // number of rows in the table and all + // continuations + PRInt32 mNumTableCols; // number of columns in the table + PRInt32 mColIndex; // with respect to the table + PRInt32 mRowIndex; // with respect to the table + PRInt32 mRepeatedHeaderRowIndex; // row index in a repeated + //header, it's equivalent to + // mRowIndex when we're in a repeated + // header, and set to the last row + // index of a repeated header when + // we're not + PRBool mIsNewRow; + PRBool mAtEnd; // the iterator cycled over all + // borders + nsTableRowFrame* mPrevRow; + nsTableRowFrame* mRow; + nsTableRowFrame* mStartRow; //first row in a inside the damagearea - nsTableRowFrame* prevRow; - nsTableRowFrame* row; - PRInt32 numCols; - PRInt32 x; - PRInt32 y; - nsTableCellFrame* prevCell; - nsTableCellFrame* cell; - BCCellData* prevCellData; - BCCellData* cellData; - BCData* bcData; + // cell properties + nsTableCellFrame* mPrevCell; + nsTableCellFrame* mCell; + BCCellData* mPrevCellData; + BCCellData* mCellData; + BCData* mBCData; - PRBool IsTopMostTable() { return (y == 0) && !table->GetPrevInFlow(); } - PRBool IsRightMostTable() { return (x >= numCols); } - PRBool IsBottomMostTable() { return (y >= numRows) && !table->GetNextInFlow(); } - PRBool IsLeftMostTable() { return (x == 0); } - PRBool IsTopMost() { return (y == startY); } - PRBool IsRightMost() { return (x >= endX); } - PRBool IsBottomMost() { return (y >= endY); } - PRBool IsLeftMost() { return (x == startX); } - PRBool isNewRow; + PRBool IsTableTopMost() {return (mRowIndex == 0) && !mTable->GetPrevInFlow();} + PRBool IsTableRightMost() {return (mColIndex >= mNumTableCols);} + PRBool IsTableBottomMost() {return (mRowIndex >= mNumTableRows) && !mTable->GetNextInFlow();} + PRBool IsTableLeftMost() {return (mColIndex == 0);} + PRBool IsDamageAreaTopMost() {return (mRowIndex == mDamageArea.y);} + PRBool IsDamageAreaRightMost() {return (mColIndex >= mDamageArea.XMost());} + PRBool IsDamageAreaBottomMost() {return (mRowIndex >= mDamageArea.YMost());} + PRBool IsDamageAreaLeftMost() {return (mColIndex == mDamageArea.x);} + PRInt32 GetRelativeColIndex() {return (mColIndex - mDamageArea.x);} - PRInt32 startX; - PRInt32 startY; - PRInt32 endX; - PRInt32 endY; - PRBool isRepeatedHeader; - PRBool isRepeatedFooter; - PRBool atEnd; + nsRect mDamageArea; // damageArea in cellmap coordinates + PRBool IsAfterRepeatedHeader() { return !mIsRepeatedHeader && (mRowIndex == (mRepeatedHeaderRowIndex + 1));} + PRBool StartRepeatedFooter() {return mIsRepeatedFooter && (mRowIndex == mRgFirstRowIndex) && (mRowIndex != mDamageArea.y);} + nscoord mInitialOffsetX; // offsetX of the first border with + // respect to the table + nscoord mInitialOffsetY; // offsetY of the first border with + // respect to the table + nscoord mNextOffsetY; // offsetY of the next segment + BCVerticalSeg* mVerInfo; // this array is used differently when + // horizontal and vertical borders are drawn + // When horizontal border are drawn we cache + // the column widths and the width of the + // vertical borders that arrive from top + // When we draw vertical borders we store + // lengths and width for vertical borders + // before they are drawn while we move over + // the columns in the damage area + // It has one more elements than columns are + //in the table. + BCHorizontalSeg mHorSeg; // the horizontal segment while we + // move over the colums + BCPixelSize mPrevHorSegHeight; // the height of the previous + // horizontal border private: PRBool SetNewRow(nsTableRowFrame* aRow = nsnull); PRBool SetNewRowGroup(); - void SetNewData(PRInt32 aY, PRInt32 aX); + void SetNewData(PRInt32 aRowIndex, PRInt32 aColIndex); }; -BCMapBorderIterator::BCMapBorderIterator(nsTableFrame& aTable, - nsTableRowGroupFrame& aRowGroup, - nsTableRowFrame& aRow, - const nsRect& aDamageArea) + + +BCPaintBorderIterator::BCPaintBorderIterator(nsTableFrame* aTable) { - Reset(aTable, aRowGroup, aRow, aDamageArea); + mTable = aTable; + mVerInfo = nsnull; + nsMargin childAreaOffset = mTable->GetChildAreaOffset(nsnull); + mTableFirstInFlow = (nsTableFrame*) mTable->GetFirstInFlow(); + mTableCellMap = mTable->GetCellMap(); + // y position of first row in damage area + mInitialOffsetY = (mTable->GetPrevInFlow()) ? 0 : childAreaOffset.top; + mNumTableRows = mTable->GetRowCount(); + mNumTableCols = mTable->GetColCount(); + + // Get the ordered row groups + mTable->OrderRowGroups(mRowGroups); + + mTableIsLTR = mTable->GetStyleVisibility()->mDirection == + NS_STYLE_DIRECTION_LTR; + mColInc = (mTableIsLTR) ? 1 : -1; + + nsStyleContext* bgContext = + nsCSSRendering::FindNonTransparentBackground(mTable->GetStyleContext()); + mTableBgColor = bgContext->GetStyleBackground(); +} + +/** + * determine the damage area in terms of rows and columns and finalize + mInitialOffsetY and mInitialOffsetY + @param aDirtyRect - dirty rect in table coordinates + @return - do we need to paint at all + */ +PRBool +BCPaintBorderIterator::SetDamageArea(nsRect aDirtyRect) +{ + + PRUint32 startRowIndex, endRowIndex, startColIndex, endColIndex; + startRowIndex = endRowIndex = startColIndex = endColIndex = 0; + PRBool done = PR_FALSE; + PRBool haveIntersect = PR_FALSE; + // find startRowIndex, endRowIndex, startRowY + PRInt32 rowY = mInitialOffsetY; + for (PRUint32 rgX = 0; rgX < mRowGroups.Length() && !done; rgX++) { + nsTableRowGroupFrame* rgFrame = mRowGroups[rgX]; + for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); rowFrame; + rowFrame = rowFrame->GetNextRow()) { + // conservatively estimate the half border widths outside the row + nscoord topBorderHalf = (mTable->GetPrevInFlow()) ? 0 : + nsPresContext::CSSPixelsToAppUnits(rowFrame->GetTopBCBorderWidth() + 1); + nscoord bottomBorderHalf = (mTable->GetNextInFlow()) ? 0 : + nsPresContext::CSSPixelsToAppUnits(rowFrame->GetBottomBCBorderWidth() + 1); + // get the row rect relative to the table rather than the row group + nsSize rowSize = rowFrame->GetSize(); + if (haveIntersect) { + if (aDirtyRect.YMost() >= (rowY - topBorderHalf)) { + nsTableRowFrame* fifRow = + (nsTableRowFrame*)rowFrame->GetFirstInFlow(); + if (!fifRow) ABORT1(PR_FALSE); + endRowIndex = fifRow->GetRowIndex(); + } + else done = PR_TRUE; + } + else { + if ((rowY + rowSize.height + bottomBorderHalf) >= aDirtyRect.y) { + mStartRg = rgFrame; + mStartRow = rowFrame; + nsTableRowFrame* fifRow = + (nsTableRowFrame*)rowFrame->GetFirstInFlow(); + if (!fifRow) ABORT1(PR_FALSE); + startRowIndex = endRowIndex = fifRow->GetRowIndex(); + haveIntersect = PR_TRUE; + } + else { + mInitialOffsetY += rowSize.height; + } + } + rowY += rowSize.height; + } + } + mNextOffsetY = mInitialOffsetY; + + // XXX comment refers to the obsolete NS_FRAME_OUTSIDE_CHILDREN flag + // XXX but I don't understand it, so not changing it for now + // outer table borders overflow the table, so the table might be + // target to other areas as the NS_FRAME_OUTSIDE_CHILDREN is set + // on the table + if (!haveIntersect) + return PR_FALSE; + // find startColIndex, endColIndex, startColX + haveIntersect = PR_FALSE; + if (0 == mNumTableCols) + return PR_FALSE; + PRInt32 leftCol, rightCol; // columns are in the range [leftCol, rightCol) + + nsMargin childAreaOffset = mTable->GetChildAreaOffset(nsnull); + if (mTableIsLTR) { + mInitialOffsetX = childAreaOffset.left; // x position of first col in + // damage area + leftCol = 0; + rightCol = mNumTableCols; + } else { + // x position of first col in damage area + mInitialOffsetX = mTable->GetRect().width - childAreaOffset.right; + leftCol = mNumTableCols-1; + rightCol = -1; + } + nscoord x = 0; + PRInt32 colX; + for (colX = leftCol; colX != rightCol; colX += mColInc) { + nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(colX); + if (!colFrame) ABORT1(PR_FALSE); + // conservatively estimate the half border widths outside the col + nscoord leftBorderHalf = + nsPresContext::CSSPixelsToAppUnits(colFrame->GetLeftBorderWidth() + 1); + nscoord rightBorderHalf = + nsPresContext::CSSPixelsToAppUnits(colFrame->GetRightBorderWidth() + 1); + // get the col rect relative to the table rather than the col group + nsSize size = colFrame->GetSize(); + if (haveIntersect) { + if (aDirtyRect.XMost() >= (x - leftBorderHalf)) { + endColIndex = colX; + } + else break; + } + else { + if ((x + size.width + rightBorderHalf) >= aDirtyRect.x) { + startColIndex = endColIndex = colX; + haveIntersect = PR_TRUE; + } + else { + mInitialOffsetX += mColInc * size.width; + } + } + x += size.width; + } + if (!mTableIsLTR) { + PRUint32 temp; + mInitialOffsetX = mTable->GetRect().width - childAreaOffset.right; + temp = startColIndex; startColIndex = endColIndex; endColIndex = temp; + for (PRUint32 column = 0; column < startColIndex; column++) { + nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(column); + if (!colFrame) ABORT1(PR_FALSE); + nsSize size = colFrame->GetSize(); + mInitialOffsetX += mColInc * size.width; + } + } + if (!haveIntersect) + return PR_FALSE; + mDamageArea = nsRect(startColIndex, startRowIndex, + 1 + PR_ABS(PRInt32(endColIndex - startColIndex)), + 1 + endRowIndex - startRowIndex); + + Reset(); + mVerInfo = new BCVerticalSeg[mDamageArea.width + 1]; + if (!mVerInfo) + return PR_FALSE; + return PR_TRUE; } void -BCMapBorderIterator::Reset(nsTableFrame& aTable, - nsTableRowGroupFrame& aRowGroup, - nsTableRowFrame& aRow, - const nsRect& aDamageArea) +BCPaintBorderIterator::Reset() { - atEnd = PR_TRUE; // gets reset when First() is called + mAtEnd = PR_TRUE; // gets reset when First() is called + mRg = mStartRg; + mPrevRow = nsnull; + mRow = mStartRow; + mRowIndex = 0; + mColIndex = 0; + mRgIndex = -1; + mPrevCell = nsnull; + mCell = nsnull; + mPrevCellData = nsnull; + mCellData = nsnull; + mBCData = nsnull; + ResetVerInfo(); + } - table = &aTable; - rg = &aRowGroup; - prevRow = nsnull; - row = &aRow; - - nsTableFrame* tableFif = (nsTableFrame*)table->GetFirstInFlow(); if (!tableFif) ABORT0(); - tableCellMap = tableFif->GetCellMap(); - - startX = aDamageArea.x; - startY = aDamageArea.y; - endY = aDamageArea.y + aDamageArea.height; - endX = aDamageArea.x + aDamageArea.width; - - numRows = tableFif->GetRowCount(); - y = 0; - numCols = tableFif->GetColCount(); - x = 0; - rowGroupIndex = -1; - prevCell = nsnull; - cell = nsnull; - prevCellData = nsnull; - cellData = nsnull; - bcData = nsnull; - - // Get the ordered row groups - table->OrderRowGroups(rowGroups); -} - -void -BCMapBorderIterator::SetNewData(PRInt32 aY, +/** + * Set the iterator data to a new cellmap coordinate + * @param aRowIndex - the row index + * @param aColIndex - the col index + */ +void +BCPaintBorderIterator::SetNewData(PRInt32 aY, PRInt32 aX) { - if (!tableCellMap || !tableCellMap->mBCInfo) ABORT0(); + if (!mTableCellMap || !mTableCellMap->mBCInfo) ABORT0(); - x = aX; - y = aY; - prevCellData = cellData; - if (IsRightMost() && IsBottomMost()) { - cell = nsnull; - bcData = &tableCellMap->mBCInfo->mLowerRightCorner; + mColIndex = aX; + mRowIndex = aY; + mPrevCellData = mCellData; + if (IsTableRightMost() && IsTableBottomMost()) { + mCell = nsnull; + mBCData = &mTableCellMap->mBCInfo->mLowerRightCorner; } - else if (IsRightMost()) { - cellData = nsnull; - bcData = &tableCellMap->mBCInfo->mRightBorders.ElementAt(aY); + else if (IsTableRightMost()) { + mCellData = nsnull; + mBCData = &mTableCellMap->mBCInfo->mRightBorders.ElementAt(aY); } - else if (IsBottomMost()) { - cellData = nsnull; - bcData = &tableCellMap->mBCInfo->mBottomBorders.ElementAt(aX); + else if (IsTableBottomMost()) { + mCellData = nsnull; + mBCData = &mTableCellMap->mBCInfo->mBottomBorders.ElementAt(aX); } else { - if (PRUint32(y - fifRowGroupStart) < cellMap->mRows.Length()) { - bcData = nsnull; - cellData = - (BCCellData*)cellMap->mRows[y - fifRowGroupStart].SafeElementAt(x); - if (cellData) { - bcData = &cellData->mData; - if (!cellData->IsOrig()) { - if (cellData->IsRowSpan()) { - aY -= cellData->GetRowSpanOffset(); + if (PRUint32(mRowIndex - mFifRgFirstRowIndex) < mCellMap->mRows.Length()) { + mBCData = nsnull; + mCellData = + (BCCellData*)mCellMap->mRows[mRowIndex - mFifRgFirstRowIndex].SafeElementAt(mColIndex); + if (mCellData) { + mBCData = &mCellData->mData; + if (!mCellData->IsOrig()) { + if (mCellData->IsRowSpan()) { + aY -= mCellData->GetRowSpanOffset(); } - if (cellData->IsColSpan()) { - aX -= cellData->GetColSpanOffset(); + if (mCellData->IsColSpan()) { + aX -= mCellData->GetColSpanOffset(); } if ((aX >= 0) && (aY >= 0)) { - cellData = (BCCellData*)cellMap->mRows[aY - fifRowGroupStart][aX]; + mCellData = (BCCellData*)mCellMap->mRows[aY - mFifRgFirstRowIndex][aX]; } } - if (cellData->IsOrig()) { - prevCell = cell; - cell = cellData->GetCellFrame(); + if (mCellData->IsOrig()) { + mPrevCell = mCell; + mCell = mCellData->GetCellFrame(); } } } } } +/** + * Set the iterator to a new row + * @param aRow - the new row frame, if null the iterator will advance to the + * next row + */ PRBool -BCMapBorderIterator::SetNewRow(nsTableRowFrame* aRow) +BCPaintBorderIterator::SetNewRow(nsTableRowFrame* aRow) { - prevRow = row; - row = (aRow) ? aRow : row->GetNextRow(); - - if (row) { - isNewRow = PR_TRUE; - y = row->GetRowIndex(); - x = startX; + mPrevRow = mRow; + mRow = (aRow) ? aRow : mRow->GetNextRow(); + if (mRow) { + mIsNewRow = PR_TRUE; + mRowIndex = mRow->GetRowIndex(); + mColIndex = mDamageArea.x; + mPrevHorSegHeight = 0; + if (mIsRepeatedHeader) { + mRepeatedHeaderRowIndex = mRowIndex; + } } else { - atEnd = PR_TRUE; + mAtEnd = PR_TRUE; } - return !atEnd; + return !mAtEnd; } - +/** + * Advance the iterator to the next row group + */ PRBool -BCMapBorderIterator::SetNewRowGroup() +BCPaintBorderIterator::SetNewRowGroup() { - rowGroupIndex++; - isRepeatedHeader = PR_FALSE; - isRepeatedFooter = PR_FALSE; + mRgIndex++; - if (PRUint32(rowGroupIndex) < rowGroups.Length()) { - prevRg = rg; - rg = rowGroups[rowGroupIndex]; - fifRowGroupStart = ((nsTableRowGroupFrame*)rg->GetFirstInFlow())->GetStartRowIndex(); - rowGroupStart = rg->GetStartRowIndex(); - rowGroupEnd = rowGroupStart + rg->GetRowCount() - 1; + mIsRepeatedHeader = PR_FALSE; + mIsRepeatedFooter = PR_FALSE; - if (SetNewRow(rg->GetFirstRow())) { - cellMap = - tableCellMap->GetMapFor((nsTableRowGroupFrame*)rg->GetFirstInFlow(), - nsnull); - if (!cellMap) ABORT1(PR_FALSE); + if (mRgIndex < mRowGroups.Length()) { + mPrevRg = mRg; + mRg = mRowGroups[mRgIndex]; + mFifRgFirstRowIndex = ((nsTableRowGroupFrame*)mRg->GetFirstInFlow())->GetStartRowIndex(); + mRgFirstRowIndex = mRg->GetStartRowIndex(); + mRgLastRowIndex = mRgFirstRowIndex + mRg->GetRowCount() - 1; + + if (SetNewRow(mRg->GetFirstRow())) { + mCellMap = + mTableCellMap->GetMapFor((nsTableRowGroupFrame*)mRg->GetFirstInFlow(), + nsnull); + if (!mCellMap) ABORT1(PR_FALSE); } - if (rg && table->GetPrevInFlow() && !rg->GetPrevInFlow()) { - // if rg doesn't have a prev in flow, then it may be a repeated header or footer - const nsStyleDisplay* display = rg->GetStyleDisplay(); - if (y == startY) { - isRepeatedHeader = (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == display->mDisplay); + if (mRg && mTable->GetPrevInFlow() && !mRg->GetPrevInFlow()) { + // if mRowGroup doesn't have a prev in flow, then it may be a repeated + // header or footer + const nsStyleDisplay* display = mRg->GetStyleDisplay(); + if (mRowIndex == mDamageArea.y) { + mIsRepeatedHeader = (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == display->mDisplay); } else { - isRepeatedFooter = (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == display->mDisplay); + mIsRepeatedFooter = (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == display->mDisplay); } } } else { - atEnd = PR_TRUE; + mAtEnd = PR_TRUE; } - return !atEnd; + return !mAtEnd; } -void -BCMapBorderIterator::First() +/** + * Move the iterator to the first position in the damageArea + */ +void +BCPaintBorderIterator::First() { - if (!table || (startX >= numCols) || (startY >= numRows)) ABORT0(); + if (!mTable || (mDamageArea.x >= mNumTableCols) || + (mDamageArea.y >= mNumTableRows)) ABORT0(); - atEnd = PR_FALSE; + mAtEnd = PR_FALSE; - PRUint32 numRowGroups = rowGroups.Length(); - for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) { - nsTableRowGroupFrame* rowG = rowGroups[rgX]; + PRUint32 numRowGroups = mRowGroups.Length(); + for (PRUint32 rgY = 0; rgY < numRowGroups; rgY++) { + nsTableRowGroupFrame* rowG = mRowGroups[rgY]; PRInt32 start = rowG->GetStartRowIndex(); PRInt32 end = start + rowG->GetRowCount() - 1; - if ((startY >= start) && (startY <= end)) { - rowGroupIndex = rgX - 1; // SetNewRowGroup increments rowGroupIndex - if (SetNewRowGroup()) { - while ((y < startY) && !atEnd) { + if ((mDamageArea.y >= start) && (mDamageArea.y <= end)) { + mRgIndex = rgY - 1; // SetNewRowGroup increments rowGroupIndex + if (SetNewRowGroup()) { + while ((mRowIndex < mDamageArea.y) && !mAtEnd) { SetNewRow(); } - if (!atEnd) { - SetNewData(startY, startX); + if (!mAtEnd) { + SetNewData(mDamageArea.y, mDamageArea.x); } } return; } } - atEnd = PR_TRUE; + mAtEnd = PR_TRUE; } -void -BCMapBorderIterator::Next() +/** + * Advance the iterator to the next position + */ +void +BCPaintBorderIterator::Next() { - if (atEnd) ABORT0(); - isNewRow = PR_FALSE; + if (mAtEnd) ABORT0(); + mIsNewRow = PR_FALSE; - x++; - if (x > endX) { - y++; - if (y == endY) { - x = startX; + mColIndex++; + if (mColIndex > mDamageArea.XMost()) { + mRowIndex++; + if (mRowIndex == mDamageArea.YMost()) { + mColIndex = mDamageArea.x; } - else if (y < endY) { - if (y <= rowGroupEnd) { + else if (mRowIndex < mDamageArea.YMost()) { + if (mRowIndex <= mRgLastRowIndex) { SetNewRow(); } else { @@ -6356,26 +6649,36 @@ BCMapBorderIterator::Next() } } else { - atEnd = PR_TRUE; + mAtEnd = PR_TRUE; } } - if (!atEnd) { - SetNewData(y, x); + if (!mAtEnd) { + SetNewData(mRowIndex, mColIndex); } } -// XXX if CalcVerCornerOffset and CalcHorCornerOffset remain similar, combine them +// XXX if CalcVerCornerOffset and CalcHorCornerOffset remain similar, combine +// them +/** Compute the vertical offset of a vertical border segment + * @param aCornerOwnerSide - which side owns the corner + * @param aCornerSubWidth - how wide is the nonwinning side of the corner + * @param aHorWidth - how wide is the horizontal edge of the corner + * @param aIsStartOfSeg - does this corner start a new segment + * @param aIsBevel - is this corner beveled + * @return - offset in twips + */ static nscoord -CalcVerCornerOffset(PRUint8 aCornerOwnerSide, - nscoord aCornerSubWidth, - nscoord aHorWidth, - PRBool aIsStartOfSeg, - PRBool aIsBevel) +CalcVerCornerOffset(PRUint8 aCornerOwnerSide, + BCPixelSize aCornerSubWidth, + BCPixelSize aHorWidth, + PRBool aIsStartOfSeg, + PRBool aIsBevel) { nscoord offset = 0; - // XXX These should be replaced with appropriate side-specific macros (which?). - nscoord smallHalf, largeHalf; - if ((NS_SIDE_TOP == aCornerOwnerSide) || (NS_SIDE_BOTTOM == aCornerOwnerSide)) { + // XXX These should be replaced with appropriate side-specific macros (which?) + BCPixelSize smallHalf, largeHalf; + if ((NS_SIDE_TOP == aCornerOwnerSide) || + (NS_SIDE_BOTTOM == aCornerOwnerSide)) { DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf); if (aIsBevel) { offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; @@ -6402,22 +6705,22 @@ CalcVerCornerOffset(PRUint8 aCornerOwnerSide, * @param aVerWidth - how wide is the vertical edge of the corner * @param aIsStartOfSeg - does this corner start a new segment * @param aIsBevel - is this corner beveled - * @param aPixelsToTwips - conversion factor * @param aTableIsLTR - direction, the computation depends on ltr or rtl - * @return - offset in pixel + * @return - offset in twips */ static nscoord -CalcHorCornerOffset(PRUint8 aCornerOwnerSide, - nscoord aCornerSubWidth, - nscoord aVerWidth, - PRBool aIsStartOfSeg, - PRBool aIsBevel, - PRBool aTableIsLTR) +CalcHorCornerOffset(PRUint8 aCornerOwnerSide, + BCPixelSize aCornerSubWidth, + BCPixelSize aVerWidth, + PRBool aIsStartOfSeg, + PRBool aIsBevel, + PRBool aTableIsLTR) { nscoord offset = 0; - // XXX These should be replaced with appropriate side-specific macros (which?). - nscoord smallHalf, largeHalf; - if ((NS_SIDE_LEFT == aCornerOwnerSide) || (NS_SIDE_RIGHT == aCornerOwnerSide)) { + // XXX These should be replaced with appropriate side-specific macros (which?) + BCPixelSize smallHalf, largeHalf; + if ((NS_SIDE_LEFT == aCornerOwnerSide) || + (NS_SIDE_RIGHT == aCornerOwnerSide)) { if (aTableIsLTR) { DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf); } @@ -6448,553 +6751,602 @@ CalcHorCornerOffset(PRUint8 aCornerOwnerSide, return nsPresContext::CSSPixelsToAppUnits(offset); } -struct BCVerticalSeg +BCVerticalSeg::BCVerticalSeg() { - BCVerticalSeg(); - - void Start(BCMapBorderIterator& aIter, - BCBorderOwner aBorderOwner, - nscoord aVerSegWidth, - nscoord aPrevHorSegHeight, - nscoord aHorSegHeight, - BCVerticalSeg* aVerInfoArray); - - union { - nsTableColFrame* col; - PRInt32 colWidth; - }; - PRInt32 colX; - nsTableCellFrame* ajaCell; - nsTableCellFrame* firstCell; // cell at the start of the segment - nsTableRowGroupFrame* firstRowGroup; // row group at the start of the segment - nsTableRowFrame* firstRow; // row at the start of the segment - nsTableCellFrame* lastCell; // cell at the current end of the segment - PRInt32 segY; - PRInt32 segHeight; - PRInt16 segWidth; // width in pixels - PRUint8 owner; - PRUint8 bevelSide; - PRUint16 bevelOffset; -}; - -BCVerticalSeg::BCVerticalSeg() -{ - col = nsnull; firstCell = lastCell = ajaCell = nsnull; colX = segY = segHeight = 0; - segWidth = bevelOffset = 0; bevelSide = 0; owner = eCellOwner; + mCol = nsnull; + mFirstCell = mLastCell = mAjaCell = nsnull; + mOffsetX = mOffsetY = mLength = mWidth = mTopBevelOffset = mTopBevelSide = 0; + mOwner = eCellOwner; } - + +/** + * Start a new vertical segment + * @param aIter - iterator containing the structural information + * @param aBorderOwner - determines the border style + * @param aVerSegWidth - the width of segment in pixel + * @param aHorSegHeight - the width of the horizontal segment joining the corner + * at the start + */ void -BCVerticalSeg::Start(BCMapBorderIterator& aIter, - BCBorderOwner aBorderOwner, - nscoord aVerSegWidth, - nscoord aPrevHorSegHeight, - nscoord aHorSegHeight, - BCVerticalSeg* aVerInfoArray) +BCVerticalSeg::Start(BCPaintBorderIterator& aIter, + BCBorderOwner aBorderOwner, + BCPixelSize aVerSegWidth, + BCPixelSize aHorSegHeight) { - PRUint8 ownerSide = 0; - PRPackedBool bevel = PR_FALSE; - PRInt32 xAdj = aIter.x - aIter.startX; + PRUint8 ownerSide = 0; + PRPackedBool bevel = PR_FALSE; + + + nscoord cornerSubWidth = (aIter.mBCData) ? + aIter.mBCData->GetCorner(ownerSide, bevel) : 0; - nscoord cornerSubWidth = (aIter.bcData) ? aIter.bcData->GetCorner(ownerSide, bevel) : 0; PRBool topBevel = (aVerSegWidth > 0) ? bevel : PR_FALSE; - nscoord maxHorSegHeight = NS_MAX(aPrevHorSegHeight, aHorSegHeight); - nscoord offset = CalcVerCornerOffset(ownerSide, cornerSubWidth, maxHorSegHeight, - PR_TRUE, topBevel); + BCPixelSize maxHorSegHeight = PR_MAX(aIter.mPrevHorSegHeight, aHorSegHeight); + nscoord offset = CalcVerCornerOffset(ownerSide, cornerSubWidth, + maxHorSegHeight, PR_TRUE, + topBevel); - bevelOffset = (topBevel) ? maxHorSegHeight : 0; - bevelSide = (aHorSegHeight > 0) ? NS_SIDE_RIGHT : NS_SIDE_LEFT; - segY += offset; - segHeight = -offset; - segWidth = aVerSegWidth; - owner = aBorderOwner; - firstCell = aIter.cell; - firstRowGroup = aIter.rg; - firstRow = aIter.row; - if (xAdj > 0) { - ajaCell = aVerInfoArray[xAdj - 1].lastCell; + mTopBevelOffset = (topBevel) ? + nsPresContext::CSSPixelsToAppUnits(maxHorSegHeight): 0; + // XXX this assumes that only corners where 2 segments join can be beveled + mTopBevelSide = (aHorSegHeight > 0) ? NS_SIDE_RIGHT : NS_SIDE_LEFT; + mOffsetY += offset; + mLength = -offset; + mWidth = aVerSegWidth; + mOwner = aBorderOwner; + mFirstCell = aIter.mCell; + mFirstRowGroup = aIter.mRg; + mFirstRow = aIter.mRow; + if (aIter.GetRelativeColIndex() > 0) { + mAjaCell = aIter.mVerInfo[aIter.GetRelativeColIndex() - 1].mLastCell; } } -struct BCHorizontalSeg +/** + * Initialize the vertical segments with information that will persist for any + * vertical segment in this column + * @param aIter - iterator containing the structural information + */ +void +BCVerticalSeg::Initialize(BCPaintBorderIterator& aIter) { - BCHorizontalSeg(); - - void Start(BCMapBorderIterator& aIter, - BCBorderOwner aBorderOwner, - PRUint8 aCornerOwnerSide, - nscoord aSubWidth, - PRBool aBevel, - nscoord aTopVerSegWidth, - nscoord aBottomVerSegWidth, - nscoord aHorSegHeight, - nsTableCellFrame* aLastCell, - PRBool aTableIsLTR); - - nscoord x; - nscoord y; - nscoord width; - nscoord height; - PRBool leftBevel; - nscoord leftBevelOffset; - PRUint8 leftBevelSide; - PRUint8 owner; - nsTableCellFrame* firstCell; // cell at the start of the segment - nsTableCellFrame* ajaCell; -}; - -BCHorizontalSeg::BCHorizontalSeg() -{ - x = y = width = height = leftBevel = leftBevelOffset = leftBevelSide = 0; - firstCell = ajaCell = nsnull; + PRInt32 relColIndex = aIter.GetRelativeColIndex(); + mCol = aIter.IsTableRightMost() ? aIter.mVerInfo[relColIndex - 1].mCol : + aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex); + if (!mCol) ABORT0(); + if (0 == relColIndex) { + mOffsetX = aIter.mInitialOffsetX; + } + // set colX for the next column + if (!aIter.IsDamageAreaRightMost()) { + aIter.mVerInfo[relColIndex + 1].mOffsetX = mOffsetX + + aIter.mColInc * mCol->GetSize().width; + } + mOffsetY = aIter.mInitialOffsetY; + mLastCell = aIter.mCell; } - + +/** + * Compute the offsets for the bottom corner of a vertical segment + * @param aIter - iterator containing the structural information + * @param aHorSegHeight - the width of the horizontal segment joining the corner + * at the start + */ +void +BCVerticalSeg::GetBottomCorner(BCPaintBorderIterator& aIter, + BCPixelSize aHorSegHeight) +{ + PRUint8 ownerSide = 0; + nscoord cornerSubWidth = 0; + PRPackedBool bevel = PR_FALSE; + if (aIter.mBCData) { + cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel); + } + mIsBottomBevel = (mWidth > 0) ? bevel : PR_FALSE; + mBottomHorSegHeight = PR_MAX(aIter.mPrevHorSegHeight, aHorSegHeight); + mBottomOffset = CalcVerCornerOffset(ownerSide, cornerSubWidth, + mBottomHorSegHeight, + PR_FALSE, mIsBottomBevel); + mLength += mBottomOffset; +} + +/** + * Paint the vertical segment + * @param aIter - iterator containing the structural information + * @param aRenderingContext - the rendering context + * @param aHorSegHeight - the width of the horizontal segment joining the corner + * at the start + */ +void +BCVerticalSeg::Paint(BCPaintBorderIterator& aIter, + nsIRenderingContext& aRenderingContext, + BCPixelSize aHorSegHeight) +{ + // get the border style, color and paint the segment + PRUint8 side = (aIter.IsDamageAreaRightMost()) ? NS_SIDE_RIGHT : + NS_SIDE_LEFT; + PRInt32 relColIndex = aIter.GetRelativeColIndex(); + nsTableColFrame* col = mCol; if (!col) ABORT0(); + nsTableCellFrame* cell = mFirstCell; // ??? + PRUint8 style = NS_STYLE_BORDER_STYLE_SOLID; + nscolor color = 0xFFFFFFFF; + PRBool ignoreIfRules = (aIter.IsTableRightMost() || + aIter.IsTableLeftMost()); + + switch (mOwner) { + case eTableOwner: + ::GetPaintStyleInfo(aIter.mTable, side, style, color, aIter.mTableIsLTR, + PR_FALSE); + break; + case eAjaColGroupOwner: + side = NS_SIDE_RIGHT; + if (!aIter.IsTableRightMost() && (relColIndex > 0)) { + col = aIter.mVerInfo[relColIndex - 1].mCol; + } // and fall through + case eColGroupOwner: + if (col) { + nsIFrame* cg = col->GetParent(); + if (cg) { + ::GetPaintStyleInfo(cg, side, style, color, aIter.mTableIsLTR, + ignoreIfRules); + } + } + break; + case eAjaColOwner: + side = NS_SIDE_RIGHT; + if (!aIter.IsTableRightMost() && (relColIndex > 0)) { + col = aIter.mVerInfo[relColIndex - 1].mCol; + } // and fall through + case eColOwner: + if (col) { + ::GetPaintStyleInfo(col, side, style, color, aIter.mTableIsLTR, + ignoreIfRules); + } + break; + case eAjaRowGroupOwner: + NS_ERROR("a neighboring rowgroup can never own a vertical border"); + // and fall through + case eRowGroupOwner: + NS_ASSERTION(aIter.IsTableLeftMost() || aIter.IsTableRightMost(), + "row group can own border only at table edge"); + if (mFirstRowGroup) { + ::GetPaintStyleInfo(mFirstRowGroup, side, style, color, + aIter.mTableIsLTR, ignoreIfRules); + } + break; + case eAjaRowOwner: + NS_ASSERTION(PR_FALSE, "program error"); // and fall through + case eRowOwner: + NS_ASSERTION(aIter.IsTableLeftMost() || aIter.IsTableRightMost(), + "row can own border only at table edge"); + if (mFirstRow) { + ::GetPaintStyleInfo(mFirstRow, side, style, color, aIter.mTableIsLTR, + ignoreIfRules); + } + break; + case eAjaCellOwner: + side = NS_SIDE_RIGHT; + cell = mAjaCell; // and fall through + case eCellOwner: + if (cell) { + ::GetPaintStyleInfo(cell, side, style, color, aIter.mTableIsLTR, + PR_FALSE); + } + break; + } + BCPixelSize smallHalf, largeHalf; + DivideBCBorderSize(mWidth, smallHalf, largeHalf); + nsRect segRect(mOffsetX - nsPresContext::CSSPixelsToAppUnits(largeHalf), + mOffsetY, + nsPresContext::CSSPixelsToAppUnits(mWidth), mLength); + nscoord bottomBevelOffset = (mIsBottomBevel) ? + nsPresContext::CSSPixelsToAppUnits(mBottomHorSegHeight) : 0; + PRUint8 bottomBevelSide = ((aHorSegHeight > 0) ^ !aIter.mTableIsLTR) ? + NS_SIDE_RIGHT : NS_SIDE_LEFT; + PRUint8 topBevelSide = ((mTopBevelSide == NS_SIDE_RIGHT) ^ !aIter.mTableIsLTR)? + NS_SIDE_RIGHT : NS_SIDE_LEFT; + nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, + aIter.mTableBgColor, segRect, + nsPresContext::AppUnitsPerCSSPixel(), + topBevelSide, mTopBevelOffset, + bottomBevelSide, bottomBevelOffset); +} + +/** + * Advance the start point of a segment + */ +void +BCVerticalSeg::AdvanceOffsetY() +{ + mOffsetY += mLength - mBottomOffset; +} + +/** + * Accumulate the current segment + */ +void +BCVerticalSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter) +{ + mLastCell = aIter.mCell; + mLength += aIter.mRow->GetRect().height; +} + +BCHorizontalSeg::BCHorizontalSeg() +{ + mOffsetX = mOffsetY = mLength = mWidth = mLeftBevelOffset = 0; + mLeftBevelSide = 0; + mFirstCell = mAjaCell = nsnull; +} + /** Initialize a horizontal border segment for painting * @param aIter - iterator storing the current and adjacent frames * @param aBorderOwner - which frame owns the border - * @param aCornerOwnerSide - which side owns the starting corner - * @param aSubWidth - how wide is the nonowning width of the corner - * @param aBevel - is the corner beveled - * @param aTopVerSegWidth - vertical segment width going down * @param aBottomVerSegWidth - vertical segment width coming from up * @param aHorSegHeight - the height of the segment - * @param aLastCell - cell frame above this segment - * @param aPixelsToTwips - conversion factor - * @param aTableIsLTR - direction, the computation depends on ltr or rtl - */ + + */ void -BCHorizontalSeg::Start(BCMapBorderIterator& aIter, +BCHorizontalSeg::Start(BCPaintBorderIterator& aIter, BCBorderOwner aBorderOwner, - PRUint8 aCornerOwnerSide, - nscoord aSubWidth, - PRBool aBevel, - nscoord aTopVerSegWidth, - nscoord aBottomVerSegWidth, - nscoord aHorSegHeight, - nsTableCellFrame* aLastCell, - PRBool aTableIsLTR) + BCPixelSize aBottomVerSegWidth, + BCPixelSize aHorSegHeight) { - owner = aBorderOwner; - leftBevel = (aHorSegHeight > 0) ? aBevel : PR_FALSE; - nscoord maxVerSegWidth = NS_MAX(aTopVerSegWidth, aBottomVerSegWidth); - nscoord offset = CalcHorCornerOffset(aCornerOwnerSide, aSubWidth, maxVerSegWidth, - PR_TRUE, leftBevel, aTableIsLTR); - leftBevelOffset = (leftBevel && (aHorSegHeight > 0)) ? maxVerSegWidth : 0; - leftBevelSide = (aBottomVerSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP; - if (aTableIsLTR) { - x += offset; + PRUint8 cornerOwnerSide = 0; + PRPackedBool bevel = PR_FALSE; + + mOwner = aBorderOwner; + nscoord cornerSubWidth = (aIter.mBCData) ? + aIter.mBCData->GetCorner(cornerOwnerSide, + bevel) : 0; + + PRBool leftBevel = (aHorSegHeight > 0) ? bevel : PR_FALSE; + PRInt32 relColIndex = aIter.GetRelativeColIndex(); + nscoord maxVerSegWidth = PR_MAX(aIter.mVerInfo[relColIndex].mWidth, + aBottomVerSegWidth); + nscoord offset = CalcHorCornerOffset(cornerOwnerSide, cornerSubWidth, + maxVerSegWidth, PR_TRUE, leftBevel, + aIter.mTableIsLTR); + mLeftBevelOffset = (leftBevel && (aHorSegHeight > 0)) ? maxVerSegWidth : 0; + // XXX this assumes that only corners where 2 segments join can be beveled + mLeftBevelSide = (aBottomVerSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP; + if (aIter.mTableIsLTR) { + mOffsetX += offset; } else { - x -= offset; + mOffsetX -= offset; } - width = -offset; - height = aHorSegHeight; - firstCell = aIter.cell; - ajaCell = (aIter.IsTopMost()) ? nsnull : aLastCell; + mLength = -offset; + mWidth = aHorSegHeight; + mFirstCell = aIter.mCell; + mAjaCell = (aIter.IsDamageAreaTopMost()) ? nsnull : + aIter.mVerInfo[relColIndex].mLastCell; } +/** + * Compute the offsets for the right corner of a horizontal segment + * @param aIter - iterator containing the structural information + * @param aLeftSegWidth - the width of the vertical segment joining the corner + * at the start + */ +void +BCHorizontalSeg::GetRightCorner(BCPaintBorderIterator& aIter, + BCPixelSize aLeftSegWidth) +{ + PRUint8 ownerSide = 0; + nscoord cornerSubWidth = 0; + PRPackedBool bevel = PR_FALSE; + if (aIter.mBCData) { + cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel); + } + + mIsRightBevel = (mWidth > 0) ? bevel : 0; + PRInt32 relColIndex = aIter.GetRelativeColIndex(); + nscoord verWidth = PR_MAX(aIter.mVerInfo[relColIndex].mWidth, aLeftSegWidth); + mEndOffset = CalcHorCornerOffset(ownerSide, cornerSubWidth, verWidth, + PR_FALSE, mIsRightBevel, aIter.mTableIsLTR); + mLength += mEndOffset; + mRightBevelOffset = (mIsRightBevel) ? + nsPresContext::CSSPixelsToAppUnits(verWidth) : 0; + mRightBevelSide = (aLeftSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP; +} + +/** + * Paint the horizontal segment + * @param aIter - iterator containing the structural information + * @param aRenderingContext - the rendering context + */ +void +BCHorizontalSeg::Paint(BCPaintBorderIterator& aIter, + nsIRenderingContext& aRenderingContext) +{ + // get the border style, color and paint the segment + PRUint8 side = (aIter.IsDamageAreaBottomMost()) ? NS_SIDE_BOTTOM : + NS_SIDE_TOP; + nsIFrame* rg = aIter.mRg; if (!rg) ABORT0(); + nsIFrame* row = aIter.mRow; if (!row) ABORT0(); + nsIFrame* cell = mFirstCell; if (!cell) ABORT0(); // ???? + nsIFrame* col; + + PRUint8 style = NS_STYLE_BORDER_STYLE_SOLID; + nscolor color = 0xFFFFFFFF; + PRBool ignoreIfRules = (aIter.IsTableTopMost() || + aIter.IsTableBottomMost()); + + switch (mOwner) { + case eTableOwner: + ::GetPaintStyleInfo(aIter.mTable, side, style, color, aIter.mTableIsLTR, + PR_FALSE); + break; + case eAjaColGroupOwner: + NS_ERROR("neighboring colgroups can never own a horizontal border"); + // and fall through + case eColGroupOwner: { + NS_ASSERTION(aIter.IsTableTopMost() || aIter.IsTableBottomMost(), + "col group can own border only at the table edge"); + col = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1); + if (!col) ABORT0(); + nsIFrame* cg = col->GetParent(); if (!cg) ABORT0(); + ::GetPaintStyleInfo(cg, side, style, color, aIter.mTableIsLTR, + ignoreIfRules); + break; + } + case eAjaColOwner: + NS_ERROR("neighboring column can never own a horizontal border"); + // and fall through + case eColOwner: + NS_ASSERTION(aIter.IsTableTopMost() || aIter.IsTableBottomMost(), + "col can own border only at the table edge"); + col = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1); + if (!col) ABORT0(); + ::GetPaintStyleInfo(col, side, style, color, aIter.mTableIsLTR, + ignoreIfRules); + break; + case eAjaRowGroupOwner: + side = NS_SIDE_BOTTOM; + rg = (aIter.IsTableBottomMost()) ? aIter.mRg : aIter.mPrevRg; + // and fall through + case eRowGroupOwner: + if (rg) { + ::GetPaintStyleInfo(rg, side, style, color, aIter.mTableIsLTR, + ignoreIfRules); + } + break; + case eAjaRowOwner: + side = NS_SIDE_BOTTOM; + row = (aIter.IsTableBottomMost()) ? aIter.mRow : aIter.mPrevRow; + // and fall through + case eRowOwner: + if (row) { + ::GetPaintStyleInfo(row, side, style, color, aIter.mTableIsLTR, + aIter.IsTableBottomMost()); + } + break; + case eAjaCellOwner: + side = NS_SIDE_BOTTOM; + // if this is null due to the damage area origin-y > 0, then the border + // won't show up anyway + cell = mAjaCell; + // and fall through + case eCellOwner: + if (cell) { + ::GetPaintStyleInfo(cell, side, style, color, aIter.mTableIsLTR, + PR_FALSE); + } + break; + } + BCPixelSize smallHalf, largeHalf; + DivideBCBorderSize(mWidth, smallHalf, largeHalf); + nsRect segRect(mOffsetX, + mOffsetY - nsPresContext::CSSPixelsToAppUnits(largeHalf), + mLength, + nsPresContext::CSSPixelsToAppUnits(mWidth)); + if (aIter.mTableIsLTR) { + nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, + aIter.mTableBgColor, segRect, + nsPresContext::AppUnitsPerCSSPixel(), + mLeftBevelSide, + nsPresContext::CSSPixelsToAppUnits(mLeftBevelOffset), + mRightBevelSide, mRightBevelOffset); + } + else { + segRect.x -= segRect.width; + nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, + aIter.mTableBgColor, segRect, + nsPresContext::AppUnitsPerCSSPixel(), + mRightBevelSide, mRightBevelOffset, + mLeftBevelSide, + nsPresContext::CSSPixelsToAppUnits(mLeftBevelOffset)); + } +} + +/** + * Advance the start point of a segment + */ +void +BCHorizontalSeg::AdvanceOffsetX(PRInt32 aIncrement) +{ + mOffsetX += aIncrement * (mLength - mEndOffset); +} + +/** + * Accumulate the current segment + */ +void +BCHorizontalSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter) +{ + mLength += aIter.mVerInfo[aIter.GetRelativeColIndex()].mColWidth; +} + +/** + * store the column width information while painting horizontal segment + */ +void +BCPaintBorderIterator::StoreColumnWidth(PRInt32 aIndex) +{ + if (IsTableRightMost()) { + mVerInfo[aIndex].mColWidth = mVerInfo[aIndex - 1].mColWidth; + } + else { + nsTableColFrame* col = mTableFirstInFlow->GetColFrame(mColIndex); + if (!col) ABORT0(); + mVerInfo[aIndex].mColWidth = col->GetSize().width; + } +} +/** + * Determine if a vertical segment owns the corder + */ +PRBool +BCPaintBorderIterator::VerticalSegmentOwnsCorner() +{ + PRUint8 cornerOwnerSide = 0; + PRPackedBool bevel = PR_FALSE; + nscoord cornerSubWidth; + cornerSubWidth = (mBCData) ? mBCData->GetCorner(cornerOwnerSide, bevel) : 0; + // unitialized ownerside, bevel + return (NS_SIDE_TOP == cornerOwnerSide) || + (NS_SIDE_BOTTOM == cornerOwnerSide); +} + +/** + * Paint if necessary a horizontal segment, otherwise accumulate it + * @param aRenderingContext - the rendering context + */ +void +BCPaintBorderIterator::AccumulateOrPaintHorizontalSegment(nsIRenderingContext& aRenderingContext) +{ + + PRInt32 relColIndex = GetRelativeColIndex(); + // store the current col width if it hasn't been already + if (mVerInfo[relColIndex].mColWidth < 0) { + StoreColumnWidth(relColIndex); + } + + BCBorderOwner borderOwner = eCellOwner; + BCBorderOwner ignoreBorderOwner; + PRBool isSegStart = PR_TRUE; + PRBool ignoreSegStart; + + nscoord leftSegWidth = (mBCData) ? mBCData->GetLeftEdge(ignoreBorderOwner, + ignoreSegStart) : 0; + nscoord topSegHeight = (mBCData) ? mBCData->GetTopEdge(borderOwner, + isSegStart) : 0; + + if (mIsNewRow || (IsDamageAreaLeftMost() && IsDamageAreaBottomMost())) { + // reset for every new row and on the bottom of the last row + mHorSeg.mOffsetY = mNextOffsetY; + mNextOffsetY = mNextOffsetY + mRow->GetSize().height; + mHorSeg.mOffsetX = mInitialOffsetX; + mHorSeg.Start(*this, borderOwner, leftSegWidth, topSegHeight); + } + + if (!IsDamageAreaLeftMost() && (isSegStart || IsDamageAreaRightMost() || + VerticalSegmentOwnsCorner())) { + // paint the previous seg or the current one if IsDamageAreaRightMost() + if (mHorSeg.mLength > 0) { + mHorSeg.GetRightCorner(*this, leftSegWidth); + if (mHorSeg.mWidth > 0) { + mHorSeg.Paint(*this, aRenderingContext); + } + mHorSeg.AdvanceOffsetX(mColInc); + } + mHorSeg.Start(*this, borderOwner, leftSegWidth, topSegHeight); + } + mHorSeg.IncludeCurrentBorder(*this); + mVerInfo[relColIndex].mWidth = leftSegWidth; + mVerInfo[relColIndex].mLastCell = mCell; +} +/** + * Paint if necessary a vertical segment, otherwise it + * @param aRenderingContext - the rendering context + */ +void +BCPaintBorderIterator::AccumulateOrPaintVerticalSegment(nsIRenderingContext& aRenderingContext) +{ + BCBorderOwner borderOwner = eCellOwner; + BCBorderOwner ignoreBorderOwner; + PRBool isSegStart = PR_TRUE; + PRBool ignoreSegStart; + + nscoord verSegWidth = (mBCData) ? mBCData->GetLeftEdge(borderOwner, + isSegStart) : 0; + nscoord horSegHeight = (mBCData) ? mBCData->GetTopEdge(ignoreBorderOwner, + ignoreSegStart) : 0; + + PRInt32 relColIndex = GetRelativeColIndex(); + BCVerticalSeg& verSeg = mVerInfo[relColIndex]; + if (!verSeg.mCol) { // on the first damaged row and the first segment in the + // col + verSeg.Initialize(*this); + verSeg.Start(*this, borderOwner, verSegWidth, horSegHeight); + } + + if (!IsDamageAreaTopMost() && (isSegStart || IsDamageAreaBottomMost() || + IsAfterRepeatedHeader() || + StartRepeatedFooter())) { + // paint the previous seg or the current one if IsDamageAreaBottomMost() + if (verSeg.mLength > 0) { + verSeg.GetBottomCorner(*this, horSegHeight); + if (verSeg.mWidth > 0) { + verSeg.Paint(*this, aRenderingContext, horSegHeight); + } + verSeg.AdvanceOffsetY(); + } + verSeg.Start(*this, borderOwner, verSegWidth, horSegHeight); + } + verSeg.IncludeCurrentBorder(*this); + mPrevHorSegHeight = horSegHeight; +} + +/** + * Reset the vertical information cache + */ +void +BCPaintBorderIterator::ResetVerInfo() +{ + if (mVerInfo) { + memset(mVerInfo, 0, mDamageArea.width * sizeof(BCVerticalSeg)); + // XXX reinitialize properly + for (PRInt32 xIndex = 0; xIndex < mDamageArea.width; xIndex++) { + mVerInfo[xIndex].mColWidth = -1; + } + } +} + +/** + * Method to paint BCBorders, this does not use currently display lists although + * it will do this in future + * @param aRenderingContext - the rendering context + * @param aDirtyRect - inside this rectangle the BC Borders will redrawn + */ void nsTableFrame::PaintBCBorders(nsIRenderingContext& aRenderingContext, const nsRect& aDirtyRect) { - nsMargin childAreaOffset = GetChildAreaOffset(nsnull); - nsTableFrame* firstInFlow = (nsTableFrame*)GetFirstInFlow(); if (!firstInFlow) ABORT0(); - - PRInt32 startRowY = (GetPrevInFlow()) ? 0 : childAreaOffset.top; // y position of first row in damage area - - nsStyleContext* bgContext = nsCSSRendering::FindNonTransparentBackground(mStyleContext); - const nsStyleBackground* bgColor = bgContext->GetStyleBackground(); - // determine the damage area in terms of rows and columns and finalize startColX and startRowY - PRUint32 startRowIndex, endRowIndex, startColIndex, endColIndex; - startRowIndex = endRowIndex = startColIndex = endColIndex = 0; - - RowGroupArray rowGroups; - OrderRowGroups(rowGroups); - PRBool done = PR_FALSE; - PRBool haveIntersect = PR_FALSE; - nsTableRowGroupFrame* inFlowRG = nsnull; - nsTableRowFrame* inFlowRow = nsnull; - // find startRowIndex, endRowIndex, startRowY - PRInt32 rowY = startRowY; - for (PRUint32 rgX = 0; rgX < rowGroups.Length() && !done; rgX++) { - nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; - for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); rowFrame; - rowFrame = rowFrame->GetNextRow()) { - // conservatively estimate the half border widths outside the row - nscoord topBorderHalf = (GetPrevInFlow()) ? 0 : nsPresContext::CSSPixelsToAppUnits(rowFrame->GetTopBCBorderWidth() + 1); - nscoord bottomBorderHalf = (GetNextInFlow()) ? 0 : nsPresContext::CSSPixelsToAppUnits(rowFrame->GetBottomBCBorderWidth() + 1); - // get the row rect relative to the table rather than the row group - nsSize rowSize = rowFrame->GetSize(); - if (haveIntersect) { - if (aDirtyRect.YMost() >= (rowY - topBorderHalf)) { - nsTableRowFrame* fifRow = (nsTableRowFrame*)rowFrame->GetFirstInFlow(); if (!fifRow) ABORT0(); - endRowIndex = fifRow->GetRowIndex(); - } - else done = PR_TRUE; - } - else { - if ((rowY + rowSize.height + bottomBorderHalf) >= aDirtyRect.y) { - inFlowRG = rgFrame; - inFlowRow = rowFrame; - nsTableRowFrame* fifRow = (nsTableRowFrame*)rowFrame->GetFirstInFlow(); if (!fifRow) ABORT0(); - startRowIndex = endRowIndex = fifRow->GetRowIndex(); - haveIntersect = PR_TRUE; - } - else { - startRowY += rowSize.height; - } - } - rowY += rowSize.height; - } - } - // XXX comment refers to the obsolete NS_FRAME_OUTSIDE_CHILDREN flag - // XXX but I don't understand it, so not changing it for now - // outer table borders overflow the table, so the table might be - // target to other areas as the NS_FRAME_OUTSIDE_CHILDREN is set - // on the table - if (!haveIntersect) - return; - if (!inFlowRG || !inFlowRow) ABORT0(); - - PRInt32 startColX; - // find startColIndex, endColIndex, startColX - haveIntersect = PR_FALSE; - PRUint32 numCols = GetColCount(); - if (0 == numCols) return; - - PRInt32 leftCol, rightCol, colInc; // columns are in the range [leftCol, rightCol) - PRBool tableIsLTR = GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR; - if (tableIsLTR) { - startColX = childAreaOffset.left; // x position of first col in damage area - leftCol = 0; - rightCol = numCols; - colInc = 1; - } else { - startColX = mRect.width - childAreaOffset.right; // x position of first col in damage area - leftCol = numCols-1; - rightCol = -1; - colInc = -1; - } - - nscoord x = 0; - PRInt32 colX; - for (colX = leftCol; colX != rightCol; colX += colInc) { - nsTableColFrame* colFrame = firstInFlow->GetColFrame(colX); - if (!colFrame) ABORT0(); - // conservatively estimate the half border widths outside the col - nscoord leftBorderHalf = nsPresContext::CSSPixelsToAppUnits(colFrame->GetLeftBorderWidth() + 1); - nscoord rightBorderHalf = nsPresContext::CSSPixelsToAppUnits(colFrame->GetRightBorderWidth() + 1); - // get the col rect relative to the table rather than the col group - nsSize size = colFrame->GetSize(); - if (haveIntersect) { - if (aDirtyRect.XMost() >= (x - leftBorderHalf)) { - endColIndex = colX; - } - else break; - } - else { - if ((x + size.width + rightBorderHalf) >= aDirtyRect.x) { - startColIndex = endColIndex = colX; - haveIntersect = PR_TRUE; - } - else { - startColX += colInc * size.width; - } - } - x += size.width; - } - - if (!tableIsLTR) { - PRUint32 temp; - startColX = mRect.width - childAreaOffset.right; - temp = startColIndex; startColIndex = endColIndex; endColIndex = temp; - for (PRUint32 column = 0; column < startColIndex; column++) { - nsTableColFrame* colFrame = firstInFlow->GetColFrame(column); - if (!colFrame) ABORT0(); - nsSize size = colFrame->GetSize(); - startColX += colInc * size.width; - } - } - if (!haveIntersect) + // We first transfer the aDirtyRect into cellmap coordinates to compute which + // cell borders need to be painted + BCPaintBorderIterator iter(this); + if (!iter.SetDamageArea(aDirtyRect)) return; - // iterate the cell map and build up border segments - nsRect damageArea(startColIndex, startRowIndex, - 1 + PR_ABS(PRInt32(endColIndex - startColIndex)), - 1 + endRowIndex - startRowIndex); - BCVerticalSeg* verInfo = new BCVerticalSeg[damageArea.width + 1]; if (!verInfo) ABORT0(); - BCBorderOwner borderOwner, ignoreBorderOwner; - PRUint8 ownerSide; - nscoord cornerSubWidth, smallHalf, largeHalf; - nsRect rowRect(0,0,0,0); - PRBool isSegStart, ignoreSegStart; - nscoord prevHorSegHeight = 0; - PRPackedBool bevel; - PRInt32 repeatedHeaderY = -99; - PRBool afterRepeatedHeader = PR_FALSE; - PRBool startRepeatedFooter = PR_FALSE; - - // First, paint all of the vertical borders from top to bottom and left to right as they become complete - // They are painted first, since they are less efficient to paint than horizontal segments. They were - // stored with as few segments as possible (since horizontal borders are painted last and possibly over them). - BCMapBorderIterator iter(*this, *inFlowRG, *inFlowRow, damageArea); - for (iter.First(); !iter.atEnd; iter.Next()) { - nscoord verSegWidth = (iter.bcData) ? iter.bcData->GetLeftEdge(borderOwner, isSegStart) : 0; - nscoord horSegHeight = (iter.bcData) ? iter.bcData->GetTopEdge(ignoreBorderOwner, ignoreSegStart) : 0; - - PRInt32 xAdj = iter.x - iter.startX; - if (iter.isNewRow) { - prevHorSegHeight = 0; - rowRect = iter.row->GetRect(); - if (iter.isRepeatedHeader) { - repeatedHeaderY = iter.y; - } - afterRepeatedHeader = !iter.isRepeatedHeader && (iter.y == (repeatedHeaderY + 1)); - startRepeatedFooter = iter.isRepeatedFooter && (iter.y == iter.rowGroupStart) && (iter.y != iter.startY); - } - BCVerticalSeg& info = verInfo[xAdj]; - if (!info.col) { // on the first damaged row and the first segment in the col - info.col = iter.IsRightMostTable() ? verInfo[xAdj - 1].col : firstInFlow->GetColFrame(iter.x); - if (!info.col) ABORT0(); - if (0 == xAdj) { - info.colX = startColX; - } - // set colX for the next column - if (!iter.IsRightMost()) { - verInfo[xAdj + 1].colX = info.colX + colInc * info.col->GetSize().width; - } - info.segY = startRowY; - info.Start(iter, borderOwner, verSegWidth, prevHorSegHeight, horSegHeight, verInfo); - info.lastCell = iter.cell; - } - - if (!iter.IsTopMost() && (isSegStart || iter.IsBottomMost() || afterRepeatedHeader || startRepeatedFooter)) { - // paint the previous seg or the current one if iter.IsBottomMost() - if (info.segHeight > 0) { - if (iter.bcData) { - cornerSubWidth = iter.bcData->GetCorner(ownerSide, bevel); - } else { - cornerSubWidth = 0; - ownerSide = 0; // ??? - bevel = PR_FALSE; // ??? - } - PRBool endBevel = (info.segWidth > 0) ? bevel : PR_FALSE; - nscoord bottomHorSegHeight = NS_MAX(prevHorSegHeight, horSegHeight); - nscoord endOffset = CalcVerCornerOffset(ownerSide, cornerSubWidth, bottomHorSegHeight, - PR_FALSE, endBevel); - info.segHeight += endOffset; - if (info.segWidth > 0) { - // get the border style, color and paint the segment - PRUint8 side = (iter.IsRightMost()) ? NS_SIDE_RIGHT : NS_SIDE_LEFT; - nsTableRowFrame* row = info.firstRow; - nsTableRowGroupFrame* rowGroup = info.firstRowGroup; - nsTableColFrame* col = info.col; if (!col) ABORT0(); - nsTableCellFrame* cell = info.firstCell; - PRUint8 style = NS_STYLE_BORDER_STYLE_SOLID; - nscolor color = 0xFFFFFFFF; - PRBool ignoreIfRules = (iter.IsRightMostTable() || iter.IsLeftMostTable()); - - switch (info.owner) { - case eTableOwner: - ::GetPaintStyleInfo(this, side, style, color, tableIsLTR, PR_FALSE); - break; - case eAjaColGroupOwner: - side = NS_SIDE_RIGHT; - if (!iter.IsRightMostTable() && (xAdj > 0)) { - col = verInfo[xAdj - 1].col; - } // and fall through - case eColGroupOwner: - if (col) { - nsIFrame* cg = col->GetParent(); - if (cg) { - ::GetPaintStyleInfo(cg, side, style, color, tableIsLTR, ignoreIfRules); - } - } - break; - case eAjaColOwner: - side = NS_SIDE_RIGHT; - if (!iter.IsRightMostTable() && (xAdj > 0)) { - col = verInfo[xAdj - 1].col; - } // and fall through - case eColOwner: - if (col) { - ::GetPaintStyleInfo(col, side, style, color, tableIsLTR, ignoreIfRules); - } - break; - case eAjaRowGroupOwner: - NS_ASSERTION(PR_FALSE, "program error"); // and fall through - case eRowGroupOwner: - NS_ASSERTION(iter.IsLeftMostTable() || iter.IsRightMostTable(), "program error"); - if (rowGroup) { - ::GetPaintStyleInfo(rowGroup, side, style, color, tableIsLTR, ignoreIfRules); - } - break; - case eAjaRowOwner: - NS_ASSERTION(PR_FALSE, "program error"); // and fall through - case eRowOwner: - NS_ASSERTION(iter.IsLeftMostTable() || iter.IsRightMostTable(), "program error"); - if (row) { - ::GetPaintStyleInfo(row, side, style, color, tableIsLTR, ignoreIfRules); - } - break; - case eAjaCellOwner: - side = NS_SIDE_RIGHT; - cell = info.ajaCell; // and fall through - case eCellOwner: - if (cell) { - ::GetPaintStyleInfo(cell, side, style, color, tableIsLTR, PR_FALSE); - } - break; - } - DivideBCBorderSize(info.segWidth, smallHalf, largeHalf); - nsRect segRect(info.colX - nsPresContext::CSSPixelsToAppUnits(largeHalf), info.segY, - nsPresContext::CSSPixelsToAppUnits(info.segWidth), info.segHeight); - nscoord bottomBevelOffset = (endBevel) ? nsPresContext::CSSPixelsToAppUnits(bottomHorSegHeight) : 0; - PRUint8 bottomBevelSide = ((horSegHeight > 0) ^ !tableIsLTR) ? NS_SIDE_RIGHT : NS_SIDE_LEFT; - PRUint8 topBevelSide = ((info.bevelSide == NS_SIDE_RIGHT) ^ !tableIsLTR)? NS_SIDE_RIGHT : NS_SIDE_LEFT; - nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, bgColor, segRect, nsPresContext::AppUnitsPerCSSPixel(), - topBevelSide, nsPresContext::CSSPixelsToAppUnits(info.bevelOffset), - bottomBevelSide, bottomBevelOffset); - } // if (info.segWidth > 0) - info.segY = info.segY + info.segHeight - endOffset; - } // if (info.segHeight > 0) - info.Start(iter, borderOwner, verSegWidth, prevHorSegHeight, horSegHeight, verInfo); - } // if (!iter.IsTopMost() && (isSegStart || iter.IsBottomMost())) - - info.lastCell = iter.cell; - info.segHeight += rowRect.height; - prevHorSegHeight = horSegHeight; - } // for (iter.First(); !iter.atEnd; iter.Next()) - - // Next, paint all of the horizontal border segments from top to bottom reuse the verInfo - // array to keep tract of col widths and vertical segments for corner calculations - memset(verInfo, 0, damageArea.width * sizeof(BCVerticalSeg)); // XXX reinitialize properly - for (PRInt32 xIndex = 0; xIndex < damageArea.width; xIndex++) { - verInfo[xIndex].colWidth = -1; + // First, paint all of the vertical borders from top to bottom and left to + // right as they become complete. They are painted first, since they are less + // efficient to paint than horizontal segments. They were stored with as few + // segments as possible (since horizontal borders are painted last and + // possibly over them). For every cell in a row that fails in the damage are + // we look up if the current border would start a new segment, if so we paint + // the previously stored vertical segment and start a new segment. After + // this we the now active segment with the current border. These + // segments are stored in mVerInfo to be used on the next row + for (iter.First(); !iter.mAtEnd; iter.Next()) { + iter.AccumulateOrPaintVerticalSegment(aRenderingContext); } - PRInt32 nextY = startRowY; - BCHorizontalSeg horSeg; - iter.Reset(*this, *inFlowRG, *inFlowRow, damageArea); - for (iter.First(); !iter.atEnd; iter.Next()) { - nscoord leftSegWidth = (iter.bcData) ? iter.bcData->GetLeftEdge(ignoreBorderOwner, ignoreSegStart) : 0; - nscoord topSegHeight = (iter.bcData) ? iter.bcData->GetTopEdge(borderOwner, isSegStart) : 0; - - PRInt32 xAdj = iter.x - iter.startX; - // store the current col width if it hasn't been already - if (verInfo[xAdj].colWidth < 0) { - if (iter.IsRightMostTable()) { - verInfo[xAdj].colWidth = verInfo[xAdj - 1].colWidth; - } - else { - nsTableColFrame* col = firstInFlow->GetColFrame(iter.x); if (!col) ABORT0(); - verInfo[xAdj].colWidth = col->GetSize().width; - } - } - cornerSubWidth = (iter.bcData) ? iter.bcData->GetCorner(ownerSide, bevel) : 0; - nscoord verWidth = NS_MAX(nscoord(verInfo[xAdj].segWidth), leftSegWidth); - if (iter.isNewRow || (iter.IsLeftMost() && iter.IsBottomMost())) { - horSeg.y = nextY; - nextY = nextY + iter.row->GetSize().height; - horSeg.x = startColX; - horSeg.Start(iter, borderOwner, ownerSide, cornerSubWidth, bevel, verInfo[xAdj].segWidth, - leftSegWidth, topSegHeight, verInfo[xAdj].lastCell, tableIsLTR); - } - PRBool verOwnsCorner = (NS_SIDE_TOP == ownerSide) || (NS_SIDE_BOTTOM == ownerSide); - if (!iter.IsLeftMost() && (isSegStart || iter.IsRightMost() || verOwnsCorner)) { - // paint the previous seg or the current one if iter.IsRightMost() - if (horSeg.width > 0) { - PRBool endBevel = (horSeg.height > 0) ? bevel : 0; - nscoord endOffset = CalcHorCornerOffset(ownerSide, cornerSubWidth, verWidth, PR_FALSE, endBevel, tableIsLTR); - horSeg.width += endOffset; - if (horSeg.height > 0) { - // get the border style, color and paint the segment - PRUint8 side = (iter.IsBottomMost()) ? NS_SIDE_BOTTOM : NS_SIDE_TOP; - nsIFrame* rg = iter.rg; if (!rg) ABORT0(); - nsIFrame* row = iter.row; if (!row) ABORT0(); - nsIFrame* cell = horSeg.firstCell; if (!cell) ABORT0(); - nsIFrame* col; - - PRUint8 style = NS_STYLE_BORDER_STYLE_SOLID; - nscolor color = 0xFFFFFFFF; - PRBool ignoreIfRules = (iter.IsTopMostTable() || iter.IsBottomMostTable()); - - switch (horSeg.owner) { - case eTableOwner: - ::GetPaintStyleInfo(this, side, style, color, tableIsLTR, PR_FALSE); - break; - case eAjaColGroupOwner: - NS_ASSERTION(PR_FALSE, "program error"); // and fall through - case eColGroupOwner: { - NS_ASSERTION(iter.IsTopMostTable() || iter.IsBottomMostTable(), "program error"); - col = firstInFlow->GetColFrame(iter.x - 1); if (!col) ABORT0(); - nsIFrame* cg = col->GetParent(); if (!cg) ABORT0(); - ::GetPaintStyleInfo(cg, side, style, color, tableIsLTR, ignoreIfRules); - break; - } - case eAjaColOwner: - NS_ASSERTION(PR_FALSE, "program error"); // and fall through - case eColOwner: - NS_ASSERTION(iter.IsTopMostTable() || iter.IsBottomMostTable(), "program error"); - col = firstInFlow->GetColFrame(iter.x - 1); if (!col) ABORT0(); - ::GetPaintStyleInfo(col, side, style, color, tableIsLTR, ignoreIfRules); - break; - case eAjaRowGroupOwner: - side = NS_SIDE_BOTTOM; - rg = (iter.IsBottomMostTable()) ? iter.rg : iter.prevRg; // and fall through - case eRowGroupOwner: - if (rg) { - ::GetPaintStyleInfo(rg, side, style, color, tableIsLTR, ignoreIfRules); - } - break; - case eAjaRowOwner: - side = NS_SIDE_BOTTOM; - row = (iter.IsBottomMostTable()) ? iter.row : iter.prevRow; // and fall through - case eRowOwner: - if (row) { - ::GetPaintStyleInfo(row, side, style, color, tableIsLTR, iter.IsBottomMostTable()); - } - break; - case eAjaCellOwner: - side = NS_SIDE_BOTTOM; - // if this is null due to the damage area origin-y > 0, then the border won't show up anyway - cell = horSeg.ajaCell; - // and fall through - case eCellOwner: - if (cell) { - ::GetPaintStyleInfo(cell, side, style, color, tableIsLTR, PR_FALSE); - } - break; - } - - DivideBCBorderSize(horSeg.height, smallHalf, largeHalf); - nsRect segRect(horSeg.x, horSeg.y - nsPresContext::CSSPixelsToAppUnits(largeHalf), horSeg.width, - nsPresContext::CSSPixelsToAppUnits(horSeg.height)); - if (!tableIsLTR) - segRect.x -= segRect.width; - - nscoord rightBevelOffset = (endBevel) ? nsPresContext::CSSPixelsToAppUnits(verWidth) : 0; - PRUint8 rightBevelSide = (leftSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP; - if (tableIsLTR) { - nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, bgColor, segRect, nsPresContext::AppUnitsPerCSSPixel(), horSeg.leftBevelSide, - nsPresContext::CSSPixelsToAppUnits(horSeg.leftBevelOffset), - rightBevelSide, rightBevelOffset); - } - else { - nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, bgColor, segRect, nsPresContext::AppUnitsPerCSSPixel(), rightBevelSide, rightBevelOffset, - horSeg.leftBevelSide, nsPresContext::CSSPixelsToAppUnits(horSeg.leftBevelOffset)); - } - - } // if (horSeg.height > 0) - horSeg.x += colInc * (horSeg.width - endOffset); - } // if (horSeg.width > 0) - horSeg.Start(iter, borderOwner, ownerSide, cornerSubWidth, bevel, verInfo[xAdj].segWidth, - leftSegWidth, topSegHeight, verInfo[xAdj].lastCell, tableIsLTR); - } // if (!iter.IsLeftMost() && (isSegStart || iter.IsRightMost() || verOwnsCorner)) - horSeg.width += verInfo[xAdj].colWidth; - verInfo[xAdj].segWidth = leftSegWidth; - verInfo[xAdj].lastCell = iter.cell; + // Next, paint all of the horizontal border segments from top to bottom reuse + // the mVerInfo array to keep track of col widths and vertical segments for + // corner calculations + iter.Reset(); + for (iter.First(); !iter.mAtEnd; iter.Next()) { + iter.AccumulateOrPaintHorizontalSegment(aRenderingContext); } - delete [] verInfo; } PRBool nsTableFrame::RowHasSpanningCells(PRInt32 aRowIndex, PRInt32 aNumEffCols)