⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usppaint.c

📁 支持Unicode及Uniscribe的多语言输入的文本编辑器源码。
💻 C
字号:
//
//	UspPaint.c
//
//	Contains routines used for the display of styled Unicode text
//	
//	UspTextOut
//	UspInitFont
//	UspFreeFont
//	UspSetSelColor
//	
//	Written by J Brown 2006 Freeware
//	www.catch22.net
//

#define _WIN32_WINNT 0x501

#ifndef _UNICODE
#define _UNICODE
#endif

#ifndef UNICODE
#define UNICODE
#endif

#define STRICT
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <usp10.h>
#include <tchar.h>
#include "usplib.h"

// UspLib.c
ITEM_RUN *GetItemRun(USPDATA *uspData, int visualIdx);

// UspCtrl.c
void	InitCtrlChar(HDC hdc, USPFONT *uspFont);
void	PaintCtrlCharRun(USPDATA *uspData, USPFONT *uspFont, ITEM_RUN *itemRun, HDC hdc, int xpos, int ypos);

//
//	Locate the glyph-index positions for the specified logical character positions
//
static
void GetGlyphClusterIndices (  
		ITEM_RUN	* itemRun, 
		WORD		* clusterList,
		int			  clusterIdx1, 
		int			  clusterIdx2, 
		int			* glyphIdx1, 
		int			* glyphIdx2
	)
{
	// RTL scripts
	if(itemRun->analysis.fRTL)
	{
		*glyphIdx1 = clusterIdx1 < itemRun->len ? clusterList[clusterIdx1] + 1 : 0;
		*glyphIdx2 = clusterList[clusterIdx2];
	}
	// LTR scripts
	else
	{
		*glyphIdx1 = clusterList[clusterIdx2];
		*glyphIdx2 = clusterIdx1 < itemRun->len ? clusterList[clusterIdx1] - 1 : itemRun->glyphCount - 1;
	}
}

//
//	Draw a rectangle in a single colour. Depending on the run-direction (left/right),
//	the rectangle's position may need to be mirrored within the run before output
//
static 
void PaintRectBG(USPDATA *uspData, ITEM_RUN *itemRun, HDC hdc, int xpos, RECT *rect, ATTR *attr)
{
	RECT rc = *rect;
	
	// rectangle must be mirrored within the run for RTL scripts
	if(itemRun->analysis.fRTL)
	{
		int rtlOffset = xpos*2 + itemRun->width - rc.right - rc.left;
		OffsetRect(&rc, rtlOffset, 0);
	}
	
	// draw selection highlight + add to clipping region
	if(attr->sel)
	{
		SetBkColor(hdc, uspData->selBG);
		ExtTextOut(hdc, 0, 0, ETO_OPAQUE|ETO_CLIPPED, &rc, 0, 0, 0);
		ExcludeClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
	}
	// draw normal background 
	else
	{
		SetBkColor(hdc, attr->bg);
		ExtTextOut(hdc, 0, 0, ETO_OPAQUE|ETO_CLIPPED, &rc, 0, 0, 0);
	}
}

//
//	Paint a single run's background
//
static 
void PaintItemRunBackground ( 
		USPDATA		* uspData,
		ITEM_RUN	* itemRun,
		HDC			  hdc, 
		int			  xpos, 
		int			  ypos,
		int			  lineHeight,
		RECT		* bounds
	)
{
	int i, lasti;

	RECT rect = { xpos, ypos, xpos, ypos+lineHeight };

	WORD  * clusterList	 = uspData->clusterList		+ itemRun->charPos; 
	ATTR  * attrList	 = uspData->attrList		+ itemRun->charPos;
	int   * widthList	 = uspData->widthList		+ itemRun->glyphPos;

	ATTR attr = attrList[0];

	// loop over the character-positions in the run, in logical order
	for(i = 0, lasti = 0; i <= itemRun->len; i++)
	{
		// search for a cluster boundary (or end of run)
		if(i == itemRun->len || clusterList[lasti] != clusterList[i])
		{
			int clusterWidth;
			int advanceWidth;
			int a;
			int glyphIdx1, glyphIdx2;

			// locate glyph-positions for the cluster
			GetGlyphClusterIndices(itemRun, clusterList, i, lasti, &glyphIdx1, &glyphIdx2);
			
			// measure cluster width
			for(clusterWidth = 0; glyphIdx1 <= glyphIdx2; )
				clusterWidth += widthList[glyphIdx1++];

			// divide the cluster-width by the number of code-points that cover it
			// advanceWidth = clusterWidth / (i - lasti);
			advanceWidth = MulDiv(clusterWidth, 1, i-lasti);
				
			// interpolate the characters-positions (between lasti and i) 
			// over the whole cluster
			for(a = lasti; a <= i; a++)
			{
				// adjust for possible rounding errors in the 
				// last iteration (due to the division)
				if(a == i)
					rect.right += clusterWidth - advanceWidth * (i-lasti);

				// look for change in attribute background
				if(a == itemRun->len ||
				   attr.bg  != attrList[a].bg || 
				   attr.sel != attrList[a].sel)
				{
					PaintRectBG(uspData, itemRun, hdc, xpos, &rect, &attr);
					rect.left = rect.right;
				}

				if(a != i)
				{
					attr = attrList[a];
					rect.right += advanceWidth;
				}
			}

			lasti = i;
		}
	}
}

//
//	Paint the entire line's background
//
static 
void PaintBackground (
		USPDATA   * uspData,
		HDC			hdc, 
		int			xpos, 
		int			ypos, 
		int			lineHeight,
		RECT	  * bounds
	)
{
	int			i;
	ITEM_RUN *	itemRun;

	//
	//	Loop over each item-run in turn
	//
	for(i = 0; i < uspData->itemRunCount; i++)
	{
		//	Generate glyph data for this run
		if((itemRun = GetItemRun(uspData, i)) == 0)
			break;

		// if run is not visible then don't bother displaying it!
		if(xpos + itemRun->width < bounds->left || xpos > bounds->right)
		{
			xpos += itemRun->width;
			continue;
		}

		// paint the background of the specified item-run
		PaintItemRunBackground(
			uspData,
			itemRun,
			hdc,
			xpos,
			ypos,
			lineHeight,
			bounds
 		  );

		xpos += itemRun->width;
	}
}

//
//	PaintItemRunForeground
//
//	Output a whole ITEM_RUN of text. Use clusterList and attrList 
//	to break the glyphs into whole-cluster runs before calling ScriptTextOut
//
//	We don't attempt to interpolate colours over each cluster. If there
//	is more than one ATTR per cluster then tough - only one gets used and
//	whole clusters (even if they contain multiple glyphs) get painted in
//	a single colour
//
static 
void PaintItemRunForeground (	
		USPDATA		* uspData,
		USPFONT		* uspFont,
		ITEM_RUN	* itemRun,	
		HDC			  hdc,
		int			  xpos,
		int			  ypos,
		RECT		* bounds,
		BOOL		  forcesel
	)
{
	HRESULT hr;

	int  i, lasti, g;
	int  glyphIdx1;
	int  glyphIdx2;
	int  runWidth;
	int	 runDir = 1;
	UINT oldMode;

	// make pointers to the run's various glyph buffers
	ATTR	* attrList		= uspData->attrList			+ itemRun->charPos;
	WORD	* clusterList	= uspData->clusterList		+ itemRun->charPos;
	WORD	* glyphList		= uspData->glyphList		+ itemRun->glyphPos;
	int		* widthList		= uspData->widthList		+ itemRun->glyphPos;
	GOFFSET	* offsetList	= uspData->offsetList		+ itemRun->glyphPos;

	// right-left runs can be drawn backwards for simplicity
	if(itemRun->analysis.fRTL)
	{
		oldMode = SetTextAlign(hdc, TA_RIGHT);
		xpos += itemRun->width;
		runDir = -1;
	}

	// loop over all the logical character-positions
	for(lasti = 0, i = 0; i <= itemRun->len; i++)
	{
		// find a change in attribute
		if(i == itemRun->len || attrList[i].fg != attrList[lasti].fg )
		{
			// scan forward to locate end of cluster (we must always
			// handle whole-clusters because the attr[] might fall in the middle)
			for( ; i < itemRun->len; i++)
				if(clusterList[i - 1] != clusterList[i])
					break;

			// locate glyph-positions for the cluster [i,lasti]
			GetGlyphClusterIndices(itemRun, clusterList, i, lasti, &glyphIdx1, &glyphIdx2);

			// measure the width (in pixels) of the run
			for(runWidth = 0, g = glyphIdx1; g <= glyphIdx2; g++)
				runWidth += widthList[g];

			// only need the text colour as we are drawing transparently
			SetTextColor(hdc, forcesel ? uspData->selFG : attrList[lasti].fg);
			
			//
			//	Finally output the run of glyphs
			//
			hr = ScriptTextOut(
				hdc, 
				&uspFont->scriptCache,
				xpos,
				ypos,
				0,
				NULL,					
				&itemRun->analysis, 
				0,
				0,
				glyphList  + glyphIdx1,
				glyphIdx2  - glyphIdx1 + 1,
				widthList  + glyphIdx1,
				0,
				offsetList + glyphIdx1
			);	
			
			// +ve/-ve depending on run direction
			xpos     += runWidth * runDir;		
			lasti     = i;
		}
	}

	// restore the mapping mode
	if(itemRun->analysis.fRTL)
		oldMode = SetTextAlign(hdc, oldMode);
}

//
// optimization: if the two neighbouring item-runs share the
// same 'selection state' as the current run, then we can completely skip drawing
//
static
BOOL CanSkip(USPDATA *uspData, int i, BOOL forcesel)
{
	BOOL canSkip = FALSE;

	if(i > 0 && i < uspData->itemRunCount - 1)
	{
		ITEM_RUN *prevRun = GetItemRun(uspData, i - 1);
		ITEM_RUN *thisRun = GetItemRun(uspData, i    );
		ITEM_RUN *nextRun = GetItemRun(uspData, i + 1);

		// skip drawing regular text if all three runs are selected
		if(forcesel == FALSE && prevRun->selstate == 1 && thisRun->selstate == 1 && nextRun->selstate == 1 &&
								prevRun->width != 0    && thisRun->width != 0    && nextRun->width != 0)
		{
			return TRUE;
		}
			   
		// skip drawing selected text if all three runs are unselected
		if(forcesel == TRUE  && prevRun->selstate == 0 && thisRun->selstate == 0 && nextRun->selstate == 0 &&
							    prevRun->width != 0    && thisRun->width != 0    && nextRun->width != 0)
		{
			return TRUE;
		}
	}

	return FALSE;
}

//
//	Paint the entire line of text
//
static
int PaintForeground ( 
		USPDATA   * uspData,
		HDC			hdc, 
		int			xpos, 
		int			ypos, 
		RECT	  * bounds, 
		BOOL		forcesel
	)
{
	int			i;
	int			yoff;
	ITEM_RUN  * itemRun;
	USPFONT	  * uspFont;

	//
	//	Loop over each item-run in turn
	//
	for(i = 0; i < uspData->itemRunCount; i++)
	{
		//	Generate glyph data for this run
		if((itemRun = GetItemRun(uspData, i)) == 0)
			break;

		// if run is not visible then don't bother displaying it!
		if(xpos + itemRun->width < bounds->left || xpos > bounds->right)
		{
			xpos += itemRun->width;
			continue;
		}

		// see if we can avoid drawing this run
		if(CanSkip(uspData, i, forcesel))
		{
			xpos += itemRun->width;
			continue;
		}
		
		// select the appropriate font
		uspFont = &uspData->uspFontList[itemRun->font];
		SelectObject(hdc, uspFont->hFont);
		yoff = uspFont->yoffset;
	
		if(itemRun->ctrl && !itemRun->tab)
		{
			// draw control-characters, as long as it's not a tab
			PaintCtrlCharRun(
				uspData,
				uspFont,
				itemRun,
				hdc, 
				xpos, 
				ypos + yoff
			);
		}
		else
		{
			// display a whole run of glyphs
			PaintItemRunForeground(
				uspData, 
				uspFont,
				itemRun,
				hdc,
				xpos, 
				ypos + yoff, 
				bounds, 
				forcesel
				);
		}

		xpos += itemRun->width;
	}

	return xpos;
}

//
//	UspAttrTextOut
//
//	Display a line of text previously analyzed with UspAnalyze. The stored
//	ATTR[] visual-attribute list is used to stylize the text as it is drawn.
//  
//	Coloured text-display using Uniscribe is very complicated. 
//  Three passes are required if high-quality text output is the goal.
//
//	Returns: adjusted x-coordinate 
//
int WINAPI UspTextOut (
		USPDATA  *	uspData,
		HDC			hdc, 
		int			xpos, 
		int			ypos, 
		int			lineHeight,
		int			lineAdjustY,
		RECT	 *	bounds
	)
{
	HRGN hrgn, hrgnClip;

	if(uspData->stringLen == 0 || uspData->glyphCount == 0)
		return xpos;

	hrgnClip = CreateRectRgn(0,0,1,1); 
	GetClipRgn(hdc, hrgnClip);

	//
	//	1. draw all background colours, including selection-highlights;
	//	   selected areas are added to the HDC clipping region which prevents
	//	   step#2 (below) from drawing over them
	//
	SetBkMode(hdc, OPAQUE);
	PaintBackground(uspData, hdc, xpos, ypos, lineHeight, bounds);

	//
	//  2. draw the text normally. Selected areas are left untouched
	//	   because of the clipping-region created in step#1
	//
	SetBkMode(hdc, TRANSPARENT);
	PaintForeground(uspData, hdc, xpos, ypos + lineAdjustY, bounds, FALSE);

	//
	//  3. redraw the text using a single text-selection-colour (i.e. white)
	//	   in the same position, directly over the top of the text drawn in step#2
	//
	//	   Before we do this, the HDC clipping-region is inverted, 
	//	   so only selection areas are modified this time 
	//
	hrgn = CreateRectRgnIndirect(bounds);
	ExtSelectClipRgn(hdc, hrgn, RGN_XOR);

	SetBkMode(hdc, TRANSPARENT);
	xpos = 
	PaintForeground(uspData, hdc, xpos, ypos + lineAdjustY, bounds, TRUE);
	
	// remove clipping regions
	SelectClipRgn(hdc, hrgnClip);
	DeleteObject(hrgn);
	DeleteObject(hrgnClip);

	return xpos;
}

//
//	Set the colours used for drawing text-selection
//
void WINAPI UspSetSelColor (
		USPDATA		* uspData, 
		COLORREF	  fg, 
		COLORREF	  bg
	)
{
	uspData->selFG = fg;
	uspData->selBG = bg;
}

//
//	Used to initialize a font. 
//
//	Caller must call UspFreeFont when he is finished, and manually 
//	do a DeleteObject on the HFONT. That is, caller is responsible for
//	managing the lifetime of the HFONT
//
void WINAPI UspInitFont (
		USPFONT		* uspFont, 
		HDC			  hdc, 
		HFONT		  hFont
	)
{
	ZeroMemory(uspFont, sizeof(USPFONT));

	uspFont->hFont				= hFont;
	uspFont->scriptCache		= 0;

	hFont = SelectObject(hdc, hFont);

	GetTextMetrics(hdc, &uspFont->tm);
	InitCtrlChar(hdc, uspFont);

	SelectObject(hdc, hFont);
}

//
//	Free the font resources
//
void WINAPI UspFreeFont (
		USPFONT		* uspFont
	)
{
//	DeleteObject(uspFont->hFont);
	ScriptFreeCache(&uspFont->scriptCache);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -