谁知道如何实现高效的描边?

毕加索的画 2002-09-20 10:17:10
实现的与如下代码相同的效果,
void __fastcall TForm1::PaintBox1Paint(TObject *Sender)
{
const AnsiString OUT_TEXT ="字体描边 - TextOutline";
const int x = 10;
const int y = 10;
TCanvas *pCanvas = PaintBox1->Canvas;
pCanvas->Brush->Style = bsClear;
pCanvas->Font->Color = clBlack;
pCanvas->TextOutA(x - 1, y, OUT_TEXT);
pCanvas->TextOutA(x + 1, y, OUT_TEXT);
pCanvas->TextOutA(x, y - 1, OUT_TEXT);
pCanvas->TextOutA(x, y + 1, OUT_TEXT);
pCanvas->Font->Color = clWhite;
pCanvas->TextOutA(x, y, OUT_TEXT);
}

显然这段代码不够高效,属于投机式的实现,谁有更好、更通用的(比如可用与位图描边)的方法来实现,能讲出原理最好了。
...全文
438 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
cenphoenix 2002-10-27
  • 打赏
  • 举报
回复
关注
  • 打赏
  • 举报
回复
/****************************************************************************
* FUNCTION : DrawT2Outline
*
* PURPOSE : Decode the GGO_NATIVE outline, create a sequence of Beziers
* for each contour, draw with PolyBezier. Color and relative
* positioning provided by caller. The coordinates of hDC are
* assumed to have MM_TEXT orientation.
*
* The outline data is not scaled. To draw a glyph unhinted
* the caller should create the font at its EMSquare size
* and retrieve the outline data. Then setup a mapping mode
* prior to calling this function.
*
* RETURNS : none.
****************************************************************************/
void DrawT2Outline(HDC hDC, LPTTPOLYGONHEADER lpHeader, DWORD size)
{
WORD i;
UINT cTotal = 0; // Total points in a contour.
LPTTPOLYGONHEADER lpStart; // the start of the buffer
LPTTPOLYCURVE lpCurve; // the current curve of a contour
LPPOINT pt; // the bezier buffer
POINTFX ptStart; // The starting point of a curve
DWORD dwMaxPts = size/sizeof(POINTFX); // max possible pts.
DWORD dwBuffSize;

dwBuffSize = dwMaxPts * // Maximum possible # of contour points.
sizeof(POINT) * // sizeof buffer element
3; // Worst case multiplier of one additional point
// of line expanding to three points of a bezier

lpStart = lpHeader;
pt = (LPPOINT)malloc( dwBuffSize );

// Loop until we have processed the entire buffer of contours.
// The buffer may contain one or more contours that begin with
// a TTPOLYGONHEADER. We have them all when we the end of the buffer.
while ((DWORD)lpHeader < (DWORD)(((LPSTR)lpStart) + size) && pt != NULL)
{
if (lpHeader->dwType == TT_POLYGON_TYPE)
// Draw each coutour, currently this is the only valid
// type of contour.
{
// Convert the starting point. It is an on curve point.
// All other points are continuous from the "last"
// point of the contour. Thus the start point the next
// bezier is always pt[cTotal-1] - the last point of the
// previous bezier. See PolyBezier.
cTotal = 1;
pt[0].x = IntFromFixed(lpHeader->pfxStart.x);
pt[0].y = IntFromFixed(lpHeader->pfxStart.y);

// Get to first curve of contour -
// it starts at the next byte beyond header
lpCurve = (LPTTPOLYCURVE) (lpHeader + 1);

// Walk this contour and process each curve( or line ) segment
// and add it to the Beziers
while ((DWORD)lpCurve < (DWORD)(((LPSTR)lpHeader) + lpHeader->cb))
{
//**********************************************
// Format assumption:
// The bytes immediately preceding a POLYCURVE
// structure contain a valid POINTFX.
//
// If this is first curve, this points to the
// pfxStart of the POLYGONHEADER.
// Otherwise, this points to the last point of
// the previous POLYCURVE.
//
// In either case, this is representative of the
// previous curve's last point.
//**********************************************

ptStart = *(LPPOINTFX)((LPSTR)lpCurve - sizeof(POINTFX));
if (lpCurve->wType == TT_PRIM_LINE)
{
// convert the line segments to Bezier segments
cTotal += AppendPolyLineToBezier( &pt[cTotal], ptStart, lpCurve );
i = lpCurve->cpfx;
}
else if (lpCurve->wType == TT_PRIM_QSPLINE)
{
// Decode each Quadratic B-Spline segment, convert to bezier,
// and append to the Bezier segments
cTotal += AppendQuadBSplineToBezier( &pt[cTotal], ptStart, lpCurve );
i = lpCurve->cpfx;
}
else
// Oops! A POLYCURVE format we don't understand.
; // error, error, error

// Move on to next curve in the contour.
lpCurve = (LPTTPOLYCURVE)&(lpCurve->apfx[i]);
}

// Add points to close the contour.
// All contours are implied closed by TrueType definition.
// Depending on the specific font and glyph being used, these
// may not always be needed.
if ( pt[cTotal-1].x != pt[0].x || pt[cTotal-1].y != pt[0].y )
{
cTotal += CloseContour( pt, cTotal );
}

// flip coordinates to get glyph right side up (Windows coordinates)
// TT native coordiantes are zero originate at lower-left.
// Windows MM_TEXT are zero originate at upper-left.
for (i = 0; i < cTotal; i++)
pt[i].y = 0 - pt[i].y;

// Draw the contour
PolyBezier( hDC, pt, cTotal );
}
else
// Bad, bail, must have a bogus buffer.
break; // error, error, error

// Move on to next Contour.
// Its header starts immediate after this contour
lpHeader = (LPTTPOLYGONHEADER)(((LPSTR)lpHeader) + lpHeader->cb);
}

free( pt );
}
FIXED DoubleToFixed(double d)
{
long l;

l=(long)(d*65536.0);

return *(FIXED *)&l;
}
UINT PackDBCS(BYTE Lead, BYTE Trail)
{
UINT nChar = 0;

nChar = Lead; // Lead byte first.
nChar = nChar << 8; // Shift to next highest order byte.
nChar = nChar | Trail; // Then trail byte.

return nChar;
}

void __fastcall TForm1::Button1Click(TObject *Sender)
{
// CFont font,*old_font;

UINT Char;
GLYPHMETRICS gm;
DWORD buffer_size;
void *buffer;
MAT2 mat2;

char* pStr = "猛";
if (IsDBCSLeadByte(*pStr))
{
Char = PackDBCS(pStr[0], pStr[1]);
}
else
{
Char = pStr[0];
}
mat2.eM11=DoubleToFixed(1.0);
mat2.eM12=DoubleToFixed(0.0);
mat2.eM21=DoubleToFixed(0.0);
mat2.eM22=DoubleToFixed(1.0);

Image1->Canvas->Font->Size =100;
Image1->Canvas->Font->Name = "隶书";

HDC hDC = Image1->Canvas->Handle;

buffer_size = GetGlyphOutline(hDC ,Char,GGO_NATIVE,&gm,0,NULL,&mat2);
buffer=new char [buffer_size];

POINT oldOrg, newOrg;
GetViewportOrgEx(hDC,&oldOrg);
// Align based on cell origin.
OffsetViewportOrgEx(hDC, 100, 100 ,&newOrg);

GetGlyphOutline(hDC ,Char,GGO_NATIVE,&gm,buffer_size,buffer,&mat2);
DrawT2Outline(hDC , (LPTTPOLYGONHEADER) buffer, buffer_size);

}
  • 打赏
  • 举报
回复
/****************************************************************************
* FUNCTION : AppendPolyLineToBezier
*
* PURPOSE : Converts line segments into their Bezier point
* representation and appends them to a list of Bezier points.
*
* WARNING - The array must have at least one valid
* start point prior to the address of the element passed.
*
* RETURNS : number of Bezier points added to the POINT array.
****************************************************************************/
UINT AppendPolyLineToBezier( LPPOINT pt, POINTFX start, LPTTPOLYCURVE lpCurve )
{
int i;
UINT cTotal = 0;
POINT endpt;
POINT startpt;
POINT bezier[4];

endpt.x = IntFromFixed(start.x);
endpt.y = IntFromFixed(start.y);

for (i = 0; i < lpCurve->cpfx; i++)
{
// define the line segment
startpt = endpt;
endpt.x = IntFromFixed(lpCurve->apfx[i].x);
endpt.y = IntFromFixed(lpCurve->apfx[i].y);

// convert a line to a bezier representation
MakeBezierFromLine( bezier, startpt, endpt );

// append the Bezier to the existing ones
// Point 0 is Point 3 of previous.
pt[cTotal++] = bezier[1]; // Point 1
pt[cTotal++] = bezier[2]; // Point 2
pt[cTotal++] = bezier[3]; // Point 3

}

return cTotal;
}


/****************************************************************************
* FUNCTION : AppendQuadBSplineToBezier
*
* PURPOSE : Converts Quadratic spline segments into their Bezier point
* representation and appends them to a list of Bezier points.
*
* WARNING - The array must have at least one valid
* start point prior to the address of the element passed.
*
* RETURNS : number of Bezier points added to the POINT array.
****************************************************************************/
UINT AppendQuadBSplineToBezier( LPPOINT pt, POINTFX start, LPTTPOLYCURVE lpCurve )
{
WORD i;
UINT cTotal = 0;
POINTFX spline[3]; // a Quadratic is defined by 3 points
POINT bezier[4]; // a Cubic by 4

// The initial A point is on the curve.
spline[0] = start;

for (i = 0; i < lpCurve->cpfx;)
{
// The B point.
spline[1] = lpCurve->apfx[i++];

// Calculate the C point.
if (i == (lpCurve->cpfx - 1))
{
// The last C point is described explicitly
// i.e. it is on the curve.
spline[2] = lpCurve->apfx[i++];
}
else
{
// C is midpoint between B and next B point
// because that is the on curve point of
// a Quadratic B-Spline.
spline[2].x = fxDiv2(
lpCurve->apfx[i-1].x,
lpCurve->apfx[i].x
);
spline[2].y = fxDiv2(
lpCurve->apfx[i-1].y,
lpCurve->apfx[i].y
);
}

// convert the Q Spline to a Bezier
MakeBezierFromQBSpline( bezier, spline );

// append the Bezier to the existing ones
// Point 0 is Point 3 of previous.
pt[cTotal++] = bezier[1]; // Point 1
pt[cTotal++] = bezier[2]; // Point 2
pt[cTotal++] = bezier[3]; // Point 3

// New A point for next slice of spline is the
// on curve C point of this B-Spline
spline[0] = spline[2];
}

return cTotal;
}

/****************************************************************************
* FUNCTION : CloseContour
*
* PURPOSE : Adds a bezier line to close the circuit defined in pt.
*
*
* RETURNS : number of points aded to the pt POINT array.
****************************************************************************/
UINT CloseContour( LPPOINT pt, UINT cTotal )
{
POINT endpt,
startpt; // definition of a line
POINT bezier[4];

// connect the first and last points by a line segment
startpt = pt[cTotal-1];
endpt = pt[0];

// convert a line to a bezier representation
MakeBezierFromLine( bezier, startpt, endpt );

// append the Bezier to the existing ones
// Point 0 is Point 3 of previous.
pt[cTotal++] = bezier[1]; // Point 1
pt[cTotal++] = bezier[2]; // Point 2
pt[cTotal++] = bezier[3]; // Point 3

return 3;
}
  • 打赏
  • 举报
回复
/****************************************************************************
* FUNCTION : IntFromFixed
* RETURNS : int value approximating the FIXED value.
****************************************************************************/
int PASCAL NEAR IntFromFixed(FIXED f)
{
if (f.fract >= 0x8000)
return(f.value + 1);
else
return(f.value);
}

/****************************************************************************
* FUNCTION : fxDiv2
* RETURNS : (val1 + val2)/2 for FIXED values
****************************************************************************/
FIXED PASCAL NEAR fxDiv2(FIXED fxVal1, FIXED fxVal2)
{
long l;

l = (*((long far *)&(fxVal1)) + *((long far *)&(fxVal2)))/2;
return(*(FIXED *)&l);
}

/****************************************************************************
* FUNCTION : MakeBezierFromLine
*
* PURPOSE : Converts a line define by two points to a four point Bezier
* spline representation of the line in pPts.
*
*
* RETURNS : number of Bezier points placed into the pPts POINT array.
****************************************************************************/
UINT MakeBezierFromLine( POINT *pPts, POINT startpt, POINT endpt )
{
UINT cTotal = 0;

// starting point of Bezier
pPts[cTotal] = startpt;
cTotal++;

// 1rst Control, pt == endpoint makes Bezier a line
pPts[cTotal].x = endpt.x;
pPts[cTotal].y = endpt.y;
cTotal++;

// 2nd Control, pt == startpoint makes Bezier a line
pPts[cTotal].x = startpt.x;
pPts[cTotal].y = startpt.y;
cTotal++;

// ending point of Bezier
pPts[cTotal] = endpt;
cTotal++;

return cTotal;
}

/****************************************************************************
* FUNCTION : MakeBezierFromQBSpline
*
* PURPOSE : Converts a quadratic spline in pSline to a four point Bezier
* spline in pPts.
*
*
* RETURNS : number of Bezier points placed into the pPts POINT array.
****************************************************************************/
UINT MakeBezierFromQBSpline( POINT *pPts, POINTFX *pSpline )
{
POINT P0, // Quadratic on curve start point
P1, // Quadratic control point
P2; // Quadratic on curve end point
UINT cTotal = 0;

// Convert the Quadratic points to integer
P0.x = IntFromFixed( pSpline[0].x );
P0.y = IntFromFixed( pSpline[0].y );
P1.x = IntFromFixed( pSpline[1].x );
P1.y = IntFromFixed( pSpline[1].y );
P2.x = IntFromFixed( pSpline[2].x );
P2.y = IntFromFixed( pSpline[2].y );

// conversion of a quadratic to a cubic

// Cubic P0 is the on curve start point
pPts[cTotal] = P0;
cTotal++;

// Cubic P1 in terms of Quadratic P0 and P1
pPts[cTotal].x = P0.x + 2*(P1.x - P0.x)/3;
pPts[cTotal].y = P0.y + 2*(P1.y - P0.y)/3;
cTotal++;

// Cubic P2 in terms of Qudartic P1 and P2
pPts[cTotal].x = P1.x + 1*(P2.x - P1.x)/3;
pPts[cTotal].y = P1.y + 1*(P2.y - P1.y)/3;
cTotal++;

// Cubic P3 is the on curve end point
pPts[cTotal] = P2;
cTotal++;

return cTotal;
}

shally5 2002-10-15
  • 打赏
  • 举报
回复
学习!
毕加索的画 2002-09-22
  • 打赏
  • 举报
回复
pazee(耙子)能把你用GetGlyphOutline的代码贴出来吗?
这个API蛮复杂的。
woainihaha 2002-09-20
  • 打赏
  • 举报
回复
今天几个师兄回母校,陪他们喝酒,结果……
现在头还疼,明天帮你看看,应该没问题,我用过很多次了,但主要不是用在这方面的!呵呵,用API很好的,就是 pazee(耙子)老大的方法!
毕加索的画 2002-09-20
  • 打赏
  • 举报
回复
代码中问题不少,虽然最后调通了,但结果只是一段混乱的点阵。
我以前没接触过图像处理算法,对"高通滤波"的原理不甚了解,可否说明一下。
耙子 2002-09-20
  • 打赏
  • 举报
回复
API GetGlyphOutline 可以得到TTF字体的轮廓,但是效果没你得好看。
耙子 2002-09-20
  • 打赏
  • 举报
回复
哈哈!效果不错!
我给你找找,好像有这个api
woainihaha 2002-09-20
  • 打赏
  • 举报
回复
有两种方法:
一是通过高通滤波,但是需要将文字放到足够大的中间图象上处理,然后再按比例显示到你要的地方!为了避免速度问题,最好只采用1BIT图象.
二是通过描边算法,追逐边缘特性,算法简单,但工作量大,而且无法处理内包含情况(二次处理才可以).

现就高通滤波举个简单的例子(没有调试,可能部分代码有问题):

Graphics::TBitmap *fontback=new Graphics::TBitmap;
//Graphics::TBitmap *yourbmp=new Graphics::TBitmap;

//yourfontsize and yourfontlength yourmust give by yourself
fontback->Width=youfontsize*yourfonrlength+2;
fontback->Height=yourfontsize +2;
fontback->Canvas->Brush->Color=clBlack;
fontback->Canvas->FillRect(0,0,fontback->Width,fontback->Height);

fontback->Canvas->Textout(1,1,"yourtext");

int youtmask[3][3]={{-1,-1,-1},{-1,1,-1},{-1,-1,-1}};
Byte *tempbt;
int *dat=new [fontback->Width][fontback->Height];

//Following code is use to copying data
//You can do it by a better way,such as *tempbt[fontback->Heght]
fontback->PixelFormat=pf24bits;

for (int i=0;i<fontback->Height;i++)
{
tempbt=(Byte *)fontback->Canvas->ScanLine[i];
for (int j=0;j<fontback->Width;j++)
{
//Only one color need to save
dat[j]=tempbt[3*j];
}
}

fontback->Canvas->FillRect(Rect(0,0,fontback->Width,fontback->Height));

//Now begin
for (int y=1;y<fontback->Height;y++)
{
tempbt=fontback->Canvas->ScanLine[y];
for (int x=1;x<fontback->Width;x++)
{
//High pass
int tempsum;
for (int j=-1;j<=1;j++)
for (int i=-1;i<=1;i++)
tempsum=dat[x+i][y+j]*yourmask[i+1][j+1];
//The black color is zero,chang edge color
if (tempsum)
{
tempbt[3*x]=255;
tempbt[3*x+1]=255;
tempbt[3*x+2]=255;
}
}
}

//Ok finished!Use copymode copy it to your canvas

大概是这样吧,没有调试,临时写的肯定有很多错误,自行更正!
其实你的做法很对,在字体处理的时候用你的方法比较简单!
如需要描边代码,请留言!
s98231106 2002-09-20
  • 打赏
  • 举报
回复
这个可能有点复杂

呵呵,UP
gfh21cn 2002-09-20
  • 打赏
  • 举报
回复
可能要去研究汉字显示了
呵呵
gfh21cn 2002-09-20
  • 打赏
  • 举报
回复
呵呵
你的办法倒真的是个投机的方法
呵呵
这个可能有点复杂
关注
sjd163 2002-09-20
  • 打赏
  • 举报
回复
不懂帮你UP
毕加索的画 2002-09-20
  • 打赏
  • 举报
回复
谁会?
yjz80 2002-09-20
  • 打赏
  • 举报
回复
up
不会
毕加索的画 2002-09-20
  • 打赏
  • 举报
回复
今天是怎么了,是不是贴子太短了,不显眼,怎么没人吭一声呀.
简介欢迎使用SuperTextView,这篇文档将会向你展示如何使用这个控件来提高你构建项目的效率。CoverSuperTextView继承自TextView,它能够大量的减少布局的复杂程度,并且使得一些常见的效果变得十分容易实现高效。同时,它内置了动画驱动,你只需要合理编写Adjuster,然后startAnim()就可以看到预期的动画效果。它仅仅是一个控件,所以你可以不费吹灰之力的在你的项目中集成使用。特点你从此不必再为背景图编写和管理大量文件了。重新优化的状态图功能使得你能够精确的控制状态图的大小,以及在SuperTextView中的位置。支持设置圆角,并且能够精确的控制圆角位置。能够轻松的实现控件边框效果。支持文字描边,这使得空心文字效果成为了可能。内置动画驱动,你只需配合Adjuster合理的使用即可。Adjuster的出现,使得你对控件的绘制过程具有了掌控权,良好的设计使得它能够完美的实现绝大部分你脑海中的效果。使用指南支持的属性SuperTextView十分方便的支持在xml中直接设置属性,并且你能够立即看到效果。就像你平时使用TextView一样方便。描边功能。     //注意,启用这个模式之后通过setTextColor()设置的颜色将会被覆盖。     //你需要通过text_fill_color来设置文字的颜色。     app:text_stroke="true"      // 文字的描边颜色。默认为Color.BLACK。     app:text_stroke_color="@color/black"     // 文字描边的宽度。     app:text_stroke_width="1dp"     // 文

13,825

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder相关内容讨论区
社区管理员
  • 基础类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧