📄 render.cpp
字号:
// Set the background color to the default for clearing the bitmap
// We need to do this everytime since the bitmap dc may contain
// a different background color carried on from last line even thought
// _crBackground == _crCurBackground.
SetBkColor(_hdc, _crBackground);
#ifdef PWD_JUPITER
// GuyBark Jupiter 35187:
// Make sure the offscreen dc gets the same text color as the render dc.
// This allows outline symbols to be drawn in the correct color. The
// outline sybols are contained in monochrome bitmaps in the resource file.
SetTextColor(_hdc, colText);
#endif // PWD_JUPITER
// Clear the bit map
_osdc.FillBitmap(_rcRender.right - _rcRender.left, yHeightOfBitmap);
// Restore the background color if necessary
if (_crBackground != _crCurBackground)
{
SetBkColor(_hdc, _crCurBackground);
}
// Store the value to normalize the y offset for rendering
// to the off screen bitmap
yAdj = _rc.top;
// Normalize _rc and _ptCur height for rendering to the off
// screen bitmap.
_ptCur.y -= yAdj;
_rc.top = 0;
_rc.bottom -= yAdj;
// Store value to normalize the x offset for rendering
xAdj = _rcRender.left;
// Adjust the _rcRender & _rcView so they are relative to x of 0.
_rcRender.left = 0;
_rcRender.right -= xAdj;
_rcView.left -= xAdj;
_rcView.right -= xAdj;
_ptCur.x -= xAdj;
yRcIgnored = 0;
LONG yRcIgnoredTemp = yHeightRC - yHeightOfBitmap;
if (yRcIgnoredTemp > 0)
{
yRcIgnored = yRcIgnoredTemp;
_rc.bottom -= yRcIgnoredTemp;
}
return hdcSave;
}
/*
* CRenderer::RenderOffScreenBitmap(osdc, hdc, xAdj, yAdj, yRcIgnored)
*
* @mfunc
* Render off screen bit map and restore the state of the render.
*
* @devnote
* This is inline because it is only called in one place. If for some
* reason this becomes generally useful, please remove the inline.
*/
INLINE void CRenderer::RenderOffScreenBitmap(
COffScreenDC& osdc, //@parm off screen DC object
HDC hdc, //@parm DC to render to
LONG xAdj, //@parm offset to real x base
LONG yAdj, //@parm offset to real y base
LONG yRcIgnored) //@parm part of display rectangle that not processed
{
// Palettes for rendering bit map
HPALETTE hpalOld = NULL;
HPALETTE hpalNew = NULL;
// For clearing extra area in screen
RECT rcErase;
// Temp place for height of RC used in min calc below
LONG yHeightOfBitmap = CalcHeightBitmap(_rc.bottom - _rc.top);
// Restore the y offset
_ptCur.y += yAdj;
_rc.top += yAdj;
_rc.bottom += yAdj;
// Restore x offset
_rcRender.left += xAdj;
_rcRender.right += xAdj;
_rcView.left += xAdj;
_rcView.right += xAdj;
_ptCur.x += xAdj;
// Create a palette if one is needed
if (_plogpalette != NULL)
{
W32->ManagePalette(hdc, _plogpalette, hpalOld, hpalNew);
}
// Render the bit map to the real DC and restore _ptCur & _rc
_osdc.RenderBitMap(hdc, xAdj, yAdj,
_rcRender.right - _rcRender.left, yHeightOfBitmap);
// Restore palette after render if necessary
if (_plogpalette != NULL)
{
W32->ManagePalette(hdc, _plogpalette, hpalOld, hpalNew);
delete _plogpalette;
_plogpalette = NULL;
}
// Restore the HDC to the actual render DC
_hdc = hdc;
// Set this flag to what it should be for the restored DC
_fErase = TRUE;
// Is there display space that needs to be cleared?
if (yRcIgnored)
{
// Clear screen area that was ignored in the off screen DC
rcErase.top = _rc.bottom;
rcErase.bottom = _rc.bottom + yRcIgnored;
rcErase.left = _rcRender.left;
rcErase.right = _rcRender.right;
EraseTextOut(_hdc, &rcErase);
// Restore RC to its original state
_rc.bottom += yRcIgnored;
}
// Reset the screen DC font
// Set up the font on the non-screen DC
if (!FormatIsChanged())
{
// We are not on a new block so just set the font and color
SetFontAndColor(GetCF());
}
else
{
// We are on a new block so reset everything.
ResetCachediFormat();
SetNewFont();
}
}
/*
* CRenderer::RenderLine (&li, fLastLine)
*
* @mfunc
* Render visible part of current line
*
* @rdesc
* TRUE if should be called for next line
* FALSE if error or bottom of rendering rectangle reached
*/
BOOL CRenderer::RenderLine (
CLine & li,
BOOL fLastLine)
{
TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CRenderer::RenderLine");
LONG cchChunk;
LONG cchInTextRun;
LONG cch;
LONG cpSelMin;
LONG cpSelMost;
LONG cpIMEInvertMin;
LONG cpIMEInvertMost;
BOOL fSelectionInLine;
BOOL fIMEHighlight = FALSE;
const WCHAR *pstrToRender;
BOOL fAccelerator = FALSE;
HDC hdcSave = NULL;
LONG yAdj;
LONG xAdj;
LONG yRcIgnored;
WCHAR wcPasswordChar = GetPasswordChar();
CTempWcharBuf twcb;
BYTE bUnderlineSave = 0;
// This is used as a temporary buffer so that we can guarantee that we will
// display an entire format run in one ExtTextOut.
WCHAR * pszTempBuffer = NULL;
_pPF = GetPF(); // Be sure our CParaFormat ptr is OK
// Get the range that we should display for this rendering.
GetPed()->GetSelRangeForRender(&cpSelMin, &cpSelMost);
// Set flag indicating rendering last line of the display
_fLastLine = fLastLine;
// Init render at start of line
NewLine(li);
if(_fCollapsed) // Line is collapsed in Outline mode
{
Advance(_cch); // Bypass line
return TRUE; // Let dispml continue with next line
}
// Set flag indicating whether part of the line is selected
fSelectionInLine = (cpSelMost != cpSelMin)
&& (GetCp() < cpSelMost) && (GetCp() + (LONG)_cch > cpSelMin);
// Check IME to see if it needs to set text color or background
if ( !fSelectionInLine
&& IMECheckGetInvertRange(GetPed(), cpIMEInvertMin, cpIMEInvertMost))
{
fIMEHighlight = fSelectionInLine = (cpIMEInvertMost != cpIMEInvertMin)
&& (GetCp() < cpIMEInvertMost)
&& (GetCp() + (LONG)_cch > cpIMEInvertMin);
}
// Compute top of clipping rect
// a priori = top of rendering rect
_rc.top = _rcRender.top;
// limit to top of view rect if selection in line or line starts
// above that point
if(_rc.top < _rcView.top
&& (fSelectionInLine || (_ptCur.y < _rcView.top)
|| (_bFlags & fliUseOffScreenDC)))
{
_rc.top = _rcView.top;
_fClipTopToView = TRUE;
}
// limit to top of line if it's below the top of view
if(_ptCur.y > _rcView.top)
_rc.top = max(_rc.top, _ptCur.y);
// Compute bottom of clipping rect
// a priori = bottom of the rendering rect
_rc.bottom = _rcRender.bottom;
// limit to bottom of view rect if selection in line or line extends
// below that point
if(_rc.bottom > _rcView.bottom &&
(fSelectionInLine || (_ptCur.y + _yHeight > _rcView.bottom)
|| (_bFlags & fliUseOffScreenDC)))
{
_rc.bottom = _rcView.bottom;
_fClipBottomToView = TRUE;
}
// limit to bottom of line if not last line or selection in line
//if(!_fLastLine || fSelectionInLine)
{
_rc.bottom = min(_ptCur.y + _yHeight, _rc.bottom);
}
// We use an off screen DC if there are characters to render and the
// measurer determined that this line needs off screen rendering. The
// main reason for the measurer deciding that a line needs off screen
// rendering is if there are multiple formats in the line.
if (_cch > 0 && _fUseOffScreenDC && (_bFlags & fliUseOffScreenDC))
{
// Set up an off screen if we can. Note that if this fails,
// we just use the regular DC which won't look as nice but
// will at least display something readable.
hdcSave = SetUpOffScreenDC(_osdc, xAdj, yAdj, yRcIgnored);
// Is this a uniform text being rendered off screen?
if ((li._bFlags & fliOffScreenOnce) != 0)
{
// Yes - turn off the special rendering since the line
// has been rendered.
li._bFlags &= ~(fliOffScreenOnce | fliUseOffScreenDC);
}
}
// Allow for special rendering at start of line
LONG x = _ptCur.x;
if( !RenderStartLine() )
{
AssertSz(FALSE,"render start failed");
}
Assert(GetCp() + _cch <= (DWORD)GetTextLength());
cch = _cch;
if ((wcPasswordChar != 0) && IsRich())
{
// It is kind of bad to allow rich text password edit controls.
// However, it does make it that much easier to create a password
// edit control since you don't have to know to change the box to
// plain. Anyway, if there is such a thing, we don't want to put
// out password characters for EOPs in general and the final EOP
// specifically. Therefore, the following ...
if (GetPed()->TxGetMultiLine())
{
cch -= _cchEOP;
}
else
{
cch = GetPed()->GetAdjustedTextLength();
}
}
for(; cch > 0; cch -= cchChunk)
{
// Initial chunk (number of character to render in a single TextOut)
// is min between CHARFORMAT run length and line length.
pstrToRender = NULL;
// Set chunk count to number of characters left in current format run
cchChunk = GetCchLeftRunCF();
AssertSz(cchChunk != 0, "empty CHARFORMAT run");
if(GetCF()->dwEffects & CFE_HIDDEN) // Don't display hidden
{ // text
Advance(cchChunk);
continue;
}
// Limit chunk to number of characters we want to display.
cchChunk = min(cch, cchChunk);
// Get the number of characters in the text run
pstrToRender = _rpTX.GetPch(cchInTextRun);
AssertSz(cchInTextRun > 0, "empty text run");
if ((cchInTextRun < cchChunk) || (wcPasswordChar != 0))
{
// The amount of data in the backing store run is less than the
// number of characters we wish to display. We will copy the
// data out of the backing store.
// Allocate a buffer if it is needed
if (NULL == pszTempBuffer)
{
// Allocate a buffer big enough to handle all future
// requests in this loop.
pszTempBuffer = twcb.GetBuf(cch);
}
// Make the string to render point to the buffer
pstrToRender = pszTempBuffer;
if (0 == wcPasswordChar)
{
// Password not requested so copy the data to the buffer
// from the backing store.
_rpTX.GetText(cchChunk, pszTempBuffer);
}
else
{
// Fill the buffer with password characters
for (int i = 0; i < cchChunk; i++)
{
pszTempBuffer[i] = wcPasswordChar;
}
}
}
if (_cpAccelerator != -1)
{
// Get the current cp
LONG cpCur = GetCp();
// Does the accelerator character fall in this chunk?
if (cpCur < _cpAccelerator && (cpCur + cchChunk) > _cpAccelerator)
{
// Reduce the chunk to the character just before the accelerator
cchChunk = _cpAccelerator - cpCur;
}
// Is this character the accelerator?
else if (cpCur == _cpAccelerator)
{
// Set chunk size to 1 since we only want to output
// the character with the underline.
cchChunk = 1;
// Tell down stream routines that we are handling accelerator
fAccelerator = TRUE;
// We don't need to bother with the accelerator because
// it will be processed now.
_cpAccelerator = -1;
}
}
// Reduce chunk to account for selection if we are rendering for a
// display that cares about selections.
if(_fRenderSelection && cpSelMin != cpSelMost)
{
LONG cchSel = cpSelMin - GetCp();
if(cchSel > 0)
cchChunk = min(cchChunk, cchSel);
else if(GetCp() < cpSelMost)
{
cchSel = cpSelMost - GetCp();
if (cchSel >= cch)
_fSelectToEOL = TRUE;
else
cchChunk = min(cchChunk, cchSel);
_fSelected = TRUE;
}
}
// if start of CHARFORMAT run, select font and color
if (!_pccs || FormatIsChanged())
{
ResetCachediFormat();
if (!SetNewFont())
return FALSE;
}
if (fAccelerator)
{
bUnderlineSave = _bUnderlineType;
_bUnderlineType = CFU_UNDERLINE;
}
// Allow for further reduction of the chunk and rendering of
// interleaved rich text elements
if(RenderChunk(cchChunk, pstrToRender, cch))
{
AssertSz(cchChunk > 0, "CRenderer::RenderLine(): cchChunk == 0");
_fSelected = FALSE;
continue;
}
AssertSz(cchChunk > 0, "CRenderer::RenderLine() - cchChunk == 0");
// Figure if last chunk of the line
_fLastChunk = (cchChunk == cch);
// now render the text
TextOut(pstrToRender, cchChunk, fIMEHighlight);
fIMEHighlight = FALSE;
if (fAccelerator)
{
_bUnderlineType = bUnderlineSave;
// Turn off the special accelerator processing
fAccelerator = FALSE;
}
Advance(cchChunk);
// break if we went past the right of the render rect.
if (_ptCur.x >= min(_rcView.right, _rcRender.right))
{
cch -= cchChunk;
break;
}
}
extern const COLORREF g_Colors[];
if(_pPF->InTable())
{
LONG h = LXtoDX(_pPF->dxOffset);
x -= h;
LONG dx = LXtoDX(_pPF->dxStartIndent);
LONG xRight = x + LXtoDX(GetTabPos(_pPF->rgxTabs[_pPF->cTabCount - 1])) - dx;
HPEN pen = CreatePen(PS_SOLID, 0,
g_Colors[(_pPF->dwBorderColor & 0x1F) - 1]);
LONG yTop = _ptCur.y;
LONG yBot = yTop + _yHeight;
if(pen)
{
pen = SelectPen(_hdc, pen);
MoveToEx(_hdc, x, yTop , NULL);
LineTo (_hdc, xRight, yTop);
if(!_fNextInTable)
{
MoveToEx(_hdc, x, yBot - 1, NULL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -