首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >德尔福的TCanvas中有错误吗?

德尔福的TCanvas中有错误吗?
EN

Stack Overflow用户
提问于 2011-05-22 10:16:55
回答 2查看 1.7K关注 0票数 5

我要把它扔在这里,得到一些反馈,我称之为"记得数到零“(感谢Andreas提供的链接)。结果发现,当与像素一起工作时,它被称为“被一个问题关闭”。我说的记得数零是什么意思?如果您实现了一个需要计算矩形操作(例如FillRectCopyRect)所涉及的像素数的例程,您必须记住0 (0,0)是一个像素。但是,将零视为像素而不是数个无值的规则,似乎只适用于涉及<=0值的坐标。举个例子:

代码语言:javascript
复制
mRect:=Rect(0,0,10,10);
mRectWidth:=mRect.right-mRect.left; // returns 10 - 0 = 10

看到问题了吗?矩形实际上定义了一个从位置0,0到位置10,10的区域,实际上是11步,而不是10步(对于x:=0到10实际上是11步)。弥补失去的像素(零没有质量,并消失当你移动到正或负空间。我似乎记得的关于上帝的毕达哥拉斯定理)大多数人只是把1加到最后的结果上,如下所示:

代码语言:javascript
复制
function getRectWidth(const aRect:TRect):Integer;
Begin
  result:=(aRect.right-aRect.left) +1;
End;

实际上,它工作得非常好,90%的图形库都使用这一技术来计算矩形的宽度和高度。但是就像强大的英雄阿齐利一样,它也有一个弱点,即空矩形的质量为1(如果你用闪光灯的话,它也能创造出各种有趣的AV)。

代码语言:javascript
复制
mRect:=Rect(0,0,0,0);
mRectWidth:=(mRect.right-mRect.left) + 1;

这大致等同于0 – 0 = 0 : +1 = 1,这意味着如果您不注意盲点,就会呈现一个像素。令我困惑的是,Delphi实际上似乎有一个剪裁问题(?),或者至少在术语上存在矛盾。因为你实际上失去了一个像素在底部和最大的权利,如果你画它。ClientRect不应该将整个绘图范围从第一个像素返回到最后一个像素吗?-但是如果您尝试这样做的话:

代码语言:javascript
复制
mRect:=getClientRect;
MoveTo(mRect.left,mRect.Bottom);
LineTo(mRect.right,mRect.bottom);

你什么都看不到!因为Delphi剪辑了最后一个像素(错误?)。奇怪的是,当您请求clientrect时,您必须手动调整它吗?

我从零开始编写了自己的图形库(用于快速dib访问和屏幕外呈现,与这个特殊情况无关),因此我已经在这些方法中工作了很长时间。当涉及到编码时,总是有一些新的东西需要学习,但是没有人能告诉我,在这种材料中没有盲点。

当我比较VCL与其他库,特别是用C#编写的库时,我还注意到很多库都很喜欢我--并确保clientrect是您可以使用的区域的全部范围。当他们在教堂外闪动和处理重叠的长方形时,他们也将高度作为“盲点”。

盲点的情况

假设您正在将一个矩形从一个位图复制到另一个位图。您的blit的目标是Rect(-10,-10,10,10)。为了在这里正确地“剪辑”目标,这样您就不会因为在内存缓冲区之外写入而受到访问冲突,您必须计算X1/Y1与您的cliprect之间的距离(这里被认为是0,0,Width-1,Height-1)。

这将给出必须添加到目标矩形和源矩形的偏移量。否则,您将在缓冲区外写入,但也会从源缓冲区中的错误位置读取。

现在,这取决于您如何在课程之外实现这一点。但是有很多图书馆没有考虑到零。当X1和X2值相同时,出现盲点,但x1为负值.因为人们通常会写:mOffset:= x2 - abs(x1)。在我们的例子中,它变成10-10 = 0。只要cliprect设置为0,0,它就能正常工作。但是当你的cliprect移动到正空间的时候,你就会离开一个像素。如果你自动地在你的getRectWidth (例如mWidth:=aRect.right-aRect.left +1)中的值-你将关闭2像素取决于源矩形(我知道,这是主要无聊的东西)。

在Mac上的C#下,使用GTK#和本机MonoMac绑定- clientrect是绝对的。这意味着您可以绘制到mRect.bottommRect.right并具有可见的结果。因此,我发现奇怪的是,我最喜欢的语言和工具包,德尔菲,我们总是要做手动调整每个ownerdrawn或自定义控件的clientrect当我们使用它。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2011-05-22 10:35:23

这就是GDI的工作方式,Delphi的TCanvas仅仅反映了底层框架。

例如,考虑LineTo()

LineTo函数从当前位置到指定点(但不包括)绘制一条直线。

FillRect()

FillRect函数使用指定的画笔填充矩形。此函数包括左边框和上边框,但不包括矩形的右边框和底部边框。

Rectangle()

绘制的矩形不包括底部和右侧边缘。

以此类推。

现在考虑一下API函数GetWindowRect()

检索指定窗口的边框的尺寸。尺寸是以屏幕坐标表示的,相对于屏幕的左上角。

返回的bottomright值比窗口边界高出1个像素。因此,窗口的宽度实际上是width = right-left,对于高度也是这样。我猜想,大会的选择是为了使这一平等得以维持。

你所报告的行为不是德尔福的TCanvas代码中的一个错误--这些代码的工作原理是正确的,完全符合设计的。

到目前为止,开发人员使用Windows的最佳方法是遵循相同的约定。尝试采用您自己的不同约定只会导致混淆和错误。

票数 14
EN

Stack Overflow用户

发布于 2011-05-22 10:26:51

我非常抱歉你为这一现象编写了一个完整的库,但你完全错了。

例如,您的代码应该类似于:

代码语言:javascript
复制
mRect := getClientRect;
MoveTo(mRect.left, mRect.Bottom - 1);
LineTo(mRect.right - 1, mRect.bottom - 1);

始终要考虑像FillRect()这样的例程在X = Rect.RightY = Rect.Bottom上什么都不做。他们都抽签直到Right - 1Bottom - 1。它应该是这样的:对于带有Left = 10Width = 10的按钮,在X = 19而不是X = 20中找到了最右边的像素。

也许这有点让人困惑,但你可以很容易地在纸方块上看到这一点。

票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6087286

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档