richedit: Avoid re-calculating positions found in wrapping for painting.

When the text is wrapped, the positions for all the runs, paragraphs,
and cells are already calculated and stored.  The only thing left to do
for painting is to offset them by the formatting rectangle and the
scroll position.
This commit is contained in:
Dylan Smith 2008-12-19 09:54:58 -05:00 committed by Alexandre Julliard
parent dc03b6b2f2
commit 97a83147b3
2 changed files with 81 additions and 125 deletions

View File

@ -23,10 +23,12 @@
WINE_DEFAULT_DEBUG_CHANNEL(richedit);
void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, const RECT *rcUpdate) {
void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, const RECT *rcUpdate)
{
ME_DisplayItem *item;
ME_Context c;
int yoffset;
int ys, ye;
editor->nSequence++;
yoffset = ME_GetYScrollPos(editor);
@ -34,116 +36,71 @@ void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, const RECT *
SetBkMode(hDC, TRANSPARENT);
ME_MoveCaret(editor); /* Calls ME_WrapMarkedParagraphs */
item = editor->pBuffer->pFirst->next;
c.pt.y -= yoffset;
while(item != editor->pBuffer->pLast) {
int yTextOffset = 0;
int ys, ye;
/* This context point is an offset for the paragraph positions stored
* during wrapping. It shouldn't be modified during painting. */
c.pt.x = c.rcView.left;
c.pt.y = c.rcView.top - yoffset;
while(item != editor->pBuffer->pLast)
{
assert(item->type == diParagraph);
ys = c.pt.y + item->member.para.pt.y;
if (item->member.para.pCell
!= item->member.para.next_para->member.para.pCell)
{
ME_Cell *cell = NULL;
cell = &ME_FindItemBack(item->member.para.next_para, diCell)->member.cell;
ye = cell->pt.y + cell->nHeight - yoffset;
ye = c.pt.y + cell->pt.y + cell->nHeight;
} else {
ye = c.pt.y + item->member.para.nHeight;
ye = ys + item->member.para.nHeight;
}
if (!(item->member.para.nFlags & MEPF_ROWEND) &&
if (item->member.para.pCell && !(item->member.para.nFlags & MEPF_ROWEND) &&
item->member.para.pCell != item->member.para.prev_para->member.para.pCell)
{
ME_DisplayItem *cell;
if (item->member.para.prev_para->member.para.nFlags & MEPF_ROWSTART)
cell = item->member.para.pCell;
else
cell = item->member.para.prev_para->member.para.pCell;
assert(cell);
/* the border shifts the text down */
yTextOffset = cell->member.cell.yTextOffset;
ye += yTextOffset;
ys -= item->member.para.pCell->member.cell.yTextOffset;
}
if (!bOnlyNew || (item->member.para.nFlags & MEPF_REPAINT))
{
/* Draw the pargraph if any of the paragraph is in the update region. */
BOOL bPaint = (rcUpdate == NULL);
ys = c.pt.y;
if (rcUpdate)
bPaint = ys + c.rcView.top < rcUpdate->bottom &&
ye + c.rcView.top > rcUpdate->top;
bPaint = ys < rcUpdate->bottom && ye > rcUpdate->top;
if (bPaint)
{
c.pt.y += yTextOffset;
ME_DrawParagraph(&c, item);
/* Clear the repaint flag if the whole paragraph is in the
* update region. */
if (!rcUpdate || (rcUpdate->top <= ys + c.rcView.top &&
rcUpdate->bottom >= ye + c.rcView.top))
if (!rcUpdate || (rcUpdate->top <= ys && rcUpdate->bottom >= ye))
item->member.para.nFlags &= ~MEPF_REPAINT;
}
}
if (item->member.para.pCell)
{
ME_Cell *cell = &item->member.para.pCell->member.cell;
ME_DisplayItem *next_para = item->member.para.next_para;
c.pt.x = cell->pt.x + cell->nWidth;
if (item->member.para.pCell == next_para->member.para.pCell &&
!(next_para->member.para.nFlags & (MEPF_ROWSTART|MEPF_ROWEND)))
{
c.pt.y = ye;
} else {
if (next_para->member.para.nFlags & MEPF_ROWSTART)
{
cell = &ME_FindItemFwd(next_para, diCell)->member.cell;
}
else if (next_para->member.para.nFlags & MEPF_ROWEND)
{
cell = &cell->next_cell->member.cell;
}
else
{
cell = &next_para->member.para.pCell->member.cell;
}
c.pt.y = cell->pt.y - yoffset;
}
} else if (!(item->member.para.nFlags & MEPF_ROWSTART)) {
c.pt.y = ye;
}
item = item->member.para.next_para;
}
if (c.pt.y < c.rcView.bottom - c.rcView.top)
if (c.pt.y + editor->nTotalLength < c.rcView.bottom)
{
/* Fill space after the end of the text. */
int xs = c.rcView.left, xe = c.rcView.right;
int ys = c.rcView.top + c.pt.y, ye = c.rcView.bottom;
RECT rc;
rc.top = c.pt.y + editor->nTotalLength;
rc.left = c.rcView.left;
rc.bottom = c.rcView.bottom;
rc.right = c.rcView.right;
if (bOnlyNew)
{
/* Only erase region drawn from previous call to ME_PaintContent */
int y1 = editor->nTotalLength-yoffset, y2 = editor->nLastTotalLength-yoffset;
if (y1<y2)
ys = y1, ye = y2+1;
if (editor->nTotalLength < editor->nLastTotalLength)
rc.bottom = c.pt.y + editor->nLastTotalLength;
else
ys = ye; /* empty fill area */
SetRectEmpty(&rc);
}
if (rcUpdate)
{
/* Clip to update region */
xs = max(xs, rcUpdate->left);
xe = min(xe, rcUpdate->right);
ys = max(ys, rcUpdate->top);
ye = min(ye, rcUpdate->bottom);
}
IntersectRect(&rc, &rc, rcUpdate);
if (xe > xs && ye > ys)
{
RECT rc;
rc.left = xs;
rc.top = ys;
rc.right = xe;
rc.bottom = ye;
if (!IsRectEmpty(&rc))
FillRect(hDC, &rc, c.editor->hbrBackground);
}
}
if (editor->nTotalLength != editor->nLastTotalLength)
ME_SendRequestResize(editor, FALSE);
editor->nLastTotalLength = editor->nTotalLength;
@ -451,7 +408,7 @@ static void ME_DrawRun(ME_Context *c, int x, int y, ME_DisplayItem *rundi, ME_Pa
if (runofs >= nSelFrom && runofs < nSelTo)
{
ME_HighlightSpace(c, x, y, wszSpace, 1, run->style, 0, 0, 1,
c->rcView.top + c->pt.y + start->member.row.pt.y,
c->pt.y + para->pt.y + start->member.row.pt.y,
start->member.row.nHeight);
}
return;
@ -463,7 +420,7 @@ static void ME_DrawRun(ME_Context *c, int x, int y, ME_DisplayItem *rundi, ME_Pa
* an unwanted symbol can be inserted instead. */
ME_DrawTextWithStyle(c, x, y, wszSpace, 1, run->style, run->nWidth,
nSelFrom-runofs, nSelTo-runofs,
c->rcView.top + c->pt.y + start->member.row.pt.y,
c->pt.y + para->pt.y + start->member.row.pt.y,
start->member.row.nHeight);
return;
}
@ -478,7 +435,7 @@ static void ME_DrawRun(ME_Context *c, int x, int y, ME_DisplayItem *rundi, ME_Pa
ME_DrawTextWithStyle(c, x, y,
szMasked->szData, ME_StrVLen(szMasked), run->style, run->nWidth,
nSelFrom-runofs,nSelTo-runofs,
c->rcView.top + c->pt.y + start->member.row.pt.y,
c->pt.y + para->pt.y + start->member.row.pt.y,
start->member.row.nHeight);
ME_DestroyString(szMasked);
}
@ -486,7 +443,7 @@ static void ME_DrawRun(ME_Context *c, int x, int y, ME_DisplayItem *rundi, ME_Pa
ME_DrawTextWithStyle(c, x, y,
run->strText->szData, ME_StrVLen(run->strText), run->style, run->nWidth,
nSelFrom-runofs,nSelTo-runofs,
c->rcView.top + c->pt.y + start->member.row.pt.y,
c->pt.y + para->pt.y + start->member.row.pt.y,
start->member.row.nHeight);
}
}
@ -742,11 +699,11 @@ static void ME_DrawTableBorders(ME_Context *c, ME_DisplayItem *paragraph)
int width;
BOOL atTop = (para->pCell != para->prev_para->member.para.pCell);
BOOL atBottom = (para->pCell != para->next_para->member.para.pCell);
int top = c->rcView.top + (atTop ? cell->pt.y : para->pt.y) - ME_GetYScrollPos(c->editor);
int top = c->pt.y + (atTop ? cell->pt.y : para->pt.y);
int bottom = (atBottom ?
c->rcView.top + cell->pt.y + cell->nHeight - ME_GetYScrollPos(c->editor):
c->pt.y + cell->pt.y + cell->nHeight :
top + para->nHeight + (atTop ? cell->yTextOffset : 0));
rc.left = c->rcView.left + cell->pt.x;
rc.left = c->pt.x + cell->pt.x;
rc.right = rc.left + cell->nWidth;
if (atTop) {
/* Erase gap before text if not all borders are the same height. */
@ -759,7 +716,7 @@ static void ME_DrawTableBorders(ME_Context *c, ME_DisplayItem *paragraph)
}
}
/* Draw cell borders.
* The borders borders are draw in is left, top, bottom, right in order
* The order borders are draw in is left, top, bottom, right in order
* to be consistent with native richedit. This is noticeable from the
* overlap of borders of different colours. */
if (!(para->nFlags & MEPF_ROWEND)) {
@ -812,7 +769,7 @@ static void ME_DrawTableBorders(ME_Context *c, ME_DisplayItem *paragraph)
ME_DisplayItem *nextEndCell;
nextEndCell = ME_FindItemBack(ME_GetTableRowEnd(paraAfterRow), diCell);
assert(nextEndCell && !nextEndCell->member.cell.next_cell);
rc.left = c->rcView.left + nextEndCell->member.cell.pt.x;
rc.left = c->pt.x + nextEndCell->member.cell.pt.x;
/* FIXME: Native draws FROM the bottom of the table rather than
* TO the bottom of the table in this case, but just doing so here
* will cause the next row to erase the border. */
@ -874,12 +831,12 @@ static void ME_DrawTableBorders(ME_Context *c, ME_DisplayItem *paragraph)
oldpen = SelectObject(c->hDC, pen);
/* Find the start relative to the text */
firstX = c->rcView.left + ME_FindItemFwd(paragraph, diRun)->member.run.pt.x;
firstX = c->pt.x + ME_FindItemFwd(paragraph, diRun)->member.run.pt.x;
/* Go back by the horizontal gap, which is stored in dxOffset */
firstX -= ME_twips2pointsX(c, para->pFmt->dxOffset);
/* The left edge, stored in dxStartIndent affected just the first edge */
startX = firstX - ME_twips2pointsX(c, para->pFmt->dxStartIndent);
rowY = c->rcView.top + c->pt.y;
rowY = c->pt.y + para->pt.y;
if (para->pFmt->dwMask & PFM_SPACEBEFORE)
rowY += ME_twips2pointsY(c, para->pFmt->dySpaceBefore);
nHeight = ME_FindItemFwd(paragraph, diStartRow)->member.row.nHeight;
@ -928,45 +885,45 @@ void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph)
ME_Run *run;
ME_Paragraph *para = NULL;
RECT rc, bounds;
int y = c->rcView.top + c->pt.y;
int y;
int height = 0, baseline = 0, no=0;
BOOL visible = FALSE;
c->pt.x = c->rcView.left;
rc.left = c->rcView.left;
rc.left = c->pt.x;
rc.right = c->rcView.right;
for (p = paragraph; p!=paragraph->member.para.next_para; p = p->next) {
switch(p->type) {
case diParagraph:
para = &p->member.para;
assert(para);
assert(paragraph);
para = &paragraph->member.para;
y = c->pt.y + para->pt.y;
if (para->pCell)
{
ME_Cell *cell = &para->pCell->member.cell;
rc.left = c->rcView.left + cell->pt.x;
rc.left = c->pt.x + cell->pt.x;
rc.right = rc.left + cell->nWidth;
}
if (para->nFlags & MEPF_ROWSTART) {
ME_Cell *cell = &para->next_para->member.para.pCell->member.cell;
rc.right = c->rcView.left + cell->pt.x;
rc.right = c->pt.x + cell->pt.x;
} else if (para->nFlags & MEPF_ROWEND) {
ME_Cell *cell = &para->prev_para->member.para.pCell->member.cell;
rc.left = c->rcView.left + cell->pt.x + cell->nWidth;
rc.left = c->pt.x + cell->pt.x + cell->nWidth;
}
ME_DrawParaDecoration(c, para, y, &bounds);
y += bounds.top;
rc.left += bounds.left;
rc.right -= bounds.right;
for (p = paragraph->next; p != para->next_para; p = p->next)
{
switch(p->type) {
case diParagraph:
assert(FALSE);
break;
case diStartRow:
/* we should have seen a diParagraph before */
assert(para);
y += height;
rc.top = y;
if (para->nFlags & MEPF_ROWSTART) {
ME_Cell *cell = &para->next_para->member.para.pCell->member.cell;
rc.bottom = y + cell->nHeight;
} else if (para->nFlags & MEPF_ROWEND) {
ME_Cell *cell = &para->prev_para->member.para.pCell->member.cell;
rc.bottom = y + cell->nHeight;
if (para->nFlags & (MEPF_ROWSTART|MEPF_ROWEND)) {
rc.bottom = y + para->nHeight;
} else {
rc.bottom = y + p->member.row.nHeight;
}
@ -991,9 +948,10 @@ void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph)
assert(para);
run = &p->member.run;
if (visible && me_debug) {
rc.left = c->rcView.left + run->pt.x;
RECT rc;
rc.left = c->pt.x + run->pt.x;
rc.right = rc.left + run->nWidth;
rc.top = c->rcView.top + c->pt.y + run->pt.y;
rc.top = c->pt.y + para->pt.y + run->pt.y;
rc.bottom = rc.bottom + height;
TRACE("rc = (%d, %d, %d, %d)\n", rc.left, rc.top, rc.right, rc.bottom);
if (run->nFlags & MERF_SKIPPED)
@ -1002,30 +960,27 @@ void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph)
FrameRect(c->hDC, &rc, GetSysColorBrush(COLOR_GRAYTEXT));
}
if (visible)
ME_DrawRun(c, c->rcView.left + run->pt.x,
c->rcView.top + c->pt.y + run->pt.y + baseline,
p, &paragraph->member.para);
ME_DrawRun(c, c->pt.x + run->pt.x,
c->pt.y + para->pt.y + run->pt.y + baseline, p, para);
if (me_debug)
{
/* I'm using %ls, hope wsprintfW is not going to use wrong (4-byte) WCHAR version */
const WCHAR wszRunDebug[] = {'[','%','d',':','%','x',']',' ','%','l','s',0};
WCHAR buf[2560];
POINT pt;
pt.x = run->pt.x;
pt.y = c->rcView.top + c->pt.y + run->pt.y;
pt.x = c->pt.x + run->pt.x;
pt.y = c->pt.y + para->pt.y + run->pt.y;
wsprintfW(buf, wszRunDebug, no, p->member.run.nFlags, p->member.run.strText->szData);
ME_DebugWrite(c->hDC, &pt, buf);
}
/* c->pt.x += p->member.run.nWidth; */
break;
case diCell:
/* Clear any space at the bottom of the cell after the text. */
if (para->nFlags & MEPF_ROWSTART)
if (para->nFlags & (MEPF_ROWSTART|MEPF_ROWEND))
break;
y += height;
rc.top = y;
rc.bottom = c->rcView.top + p->member.cell.pt.y
+ p->member.cell.nHeight - ME_GetYScrollPos(c->editor);
rc.top = c->pt.y + para->pt.y + para->nHeight;
rc.bottom = c->pt.y + p->member.cell.pt.y + p->member.cell.nHeight;
if (RectVisible(c->hDC, &rc))
{
FillRect(c->hDC, &rc, c->editor->hbrBackground);

View File

@ -693,6 +693,7 @@ BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor)
c.pt.x = cell->pt.x + cell->nWidth;
c.pt.y = cell->pt.y;
cell->next_cell->member.cell.pt = c.pt;
if (!(item->member.para.next_para->member.para.nFlags & MEPF_ROWEND))
c.pt.y += cell->yTextOffset;
}
else