- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
最佳答案
1.简介
在本文中,我将尝试说明 ScanLine
属性仅用于24位位图像素格式以及您是否实际需要使用它。首先看一下使此属性如此重要的原因。
2. ScanLine与否...?
您可以问自己为什么要使用诸如 ScanLine
属性之类的棘手技术,就像您只能使用 Pixels
访问位图的像素一样。答案是,即使在相对较小的像素区域上执行像素修改时,也存在明显的性能差异。
Pixels
属性在内部使用Windows API函数 GetPixel
和 SetPixel
来获取和设置设备上下文颜色值。 Pixels
技术的性能不足之处在于,通常需要在修改像素颜色值之前先获取它们,这在内部意味着同时调用了上述两个Windows API函数。 ScanLine
属性在这场竞赛中胜出,因为它提供了对存储位图像素数据的内存的直接访问。而且直接内存访问比两个Windows API函数调用要快。
但是,这并不意味着 Pixels
属性完全不好,您应该避免在所有情况下都使用它。例如,当您偶尔要修改几个像素(不是很大的区域)时,那么 Pixels
可能就足够了。但是,当您要使用像素区域进行操作时,请勿使用它。
3.像素深处
3.1原始数据
您可以将位图的像素数据想象为一个一维字节数组,其中包含每个像素的颜色分量的强度值序列。位图中的每个像素都由固定的字节数组成,具体取决于所使用的像素格式。
例如,24位像素格式的每个颜色分量都有1个字节-用于红色,绿色和蓝色通道。下图说明了如何想象这种24位位图的原始数据字节数组。此处每个彩色矩形代表一个字节:
3.2案例研究
想象一下,您有一个3x2像素(宽3px;高2px)的24位位图,请牢记在心,因为我将尝试解释一些内部原理,并在其上显示 ScanLine
属性用法的原理。它之所以如此之小,仅仅是因为它需要内部深处的空间(对于那些视野开阔的人来说,这种图像是png格式的绿色示例example↙:-)
3.3像素组成
首先,让我们看一下如何在内部存储位图图像的像素数据。看原始数据。下图显示了原始数据字节数组,您可以在其中看到微小位图的每个字节及其在该数组中的索引。您还会注意到,由3个字节组成的组如何形成各个像素,这些像素位于位图上的坐标是:
相同的另一个 View 提供以下图像。每个方框在那里代表我们虚构位图的一个像素。在每个像素中,您可以从原始数据字节数组中看到其坐标和3个字节的组及其索引:
4.充满色彩
4.1。初始值
众所周知,虚构的24位位图中的像素由3个字节组成-每个颜色通道1个字节。当您以自己的想象力创建了该位图时,所有像素中的所有这些字节都违背了您的意愿,被初始化为最大字节值-255。这意味着所有通道现在都具有最大的颜色强度:
当我们看一下从每个像素的初始通道值中混合出哪种颜色时,我们会看到位图为 entirely white
。因此,当您在Delphi中创建24位位图时,它最初是白色的。好吧,默认情况下,白色将是每种像素格式的位图,但它们的原始原始数据字节值可能有所不同。
5. ScanLine的 secret 生活
通过以上阅读,希望您能理解位图数据如何存储在原始数据字节数组中以及如何从这些数据中形成单个像素。现在转到 ScanLine
属性本身,以及如何在直接原始数据处理中有用。
5.1。 ScanLine目的
这篇文章的主要内容 ScanLine
属性是只读的索引属性,该属性返回指向原始数据字节数组的第一个字节的指针,该数组属于位图中的指定行。换句话说,我们请求访问给定行的原始数据字节数组,并且我们收到的是指向该数组第一个字节的指针。此属性的index参数指定我们要获取这些数据的行的从0开始的索引。
下图说明了我们的虚构位图以及使用不同的行索引通过 ScanLine
属性获得的指针:
5.2。 ScanLine的优势
因此,据我们所知,我们可以总结出 ScanLine
给了我们指向某个行数据字节数组的指针。使用原始数据的行数组,我们可以工作-我们可以读取或覆盖其字节,但只能在特定行的数组范围内:
好吧,对于特定行的每个像素,我们都有一个颜色强度数组。考虑这种数组的迭代;遍历此数组一个字节并仅调整像素的3个颜色部分之一就不太舒服。更好的方法是遍历像素并在每次迭代中一次调整所有3个颜色字节-就像我们以前使用的 Pixels
一样。
5.3。跳过像素
为了简化行阵列循环,我们需要一种与像素数据匹配的结构。幸运的是,对于24位位图,存在 RGBTRIPLE
结构;在Delphi中翻译为TRGBTriple
。简而言之,这种结构是这样的(每个成员代表一个颜色通道的强度):
type
TRGBTriple = packed record
rgbtBlue: Byte;
rgbtGreen: Byte;
rgbtRed: Byte;
end;
TRGBTriple
结构,现在我们为该行数组定义了一种类型。这将简化位图行像素的迭代。我刚刚从ShadowWnd.pas单元(无论如何是一个有趣的类的主页)借来的那个。这里是:
type
PRGBTripleArray = ^TRGBTripleArray;
TRGBTripleArray = array[0..4095] of TRGBTriple;
ScanLine
与行参数1一起使用,以获取指向第二行的原始数据字节数组的指针。我们将获得的指针分配给
RowPixels
变量,该变量指向
TRGBTriple
数组,因此自那时以来,我们可以将其视为行像素数组。然后,我们在位图的整个宽度上迭代此数组,并将每个像素的所有颜色值设置为0,这将导致位图的第一行为白色(默认情况下为白色,如上所述),第二行变为黑色。然后将该位图保存到文件中,但是当您看到它时不要感到惊讶,它真的很小:
type
PRGBTripleArray = ^TRGBTripleArray;
TRGBTripleArray = array[0..4095] of TRGBTriple;
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
Bitmap: TBitmap;
Pixels: PRGBTripleArray;
begin
Bitmap := TBitmap.Create;
try
Bitmap.Width := 3;
Bitmap.Height := 2;
Bitmap.PixelFormat := pf24bit;
// get pointer to the second row's raw data
Pixels := Bitmap.ScanLine[1];
// iterate our row pixel data array in a whole width
for I := 0 to Bitmap.Width - 1 do
begin
Pixels[I].rgbtBlue := 0;
Pixels[I].rgbtGreen := 0;
Pixels[I].rgbtRed := 0;
end;
Bitmap.SaveToFile('c:\Image.bmp');
finally
Bitmap.Free;
end;
end;
Luminance = 0.299 R + 0.587 G + 0.114 B
type
PRGBTripleArray = ^TRGBTripleArray;
TRGBTripleArray = array[0..4095] of TRGBTriple;
procedure GrayscaleBitmap(ABitmap: TBitmap);
var
X: Integer;
Y: Integer;
Gray: Byte;
Pixels: PRGBTripleArray;
begin
// iterate bitmap from top to bottom to get access to each row's raw data
for Y := 0 to ABitmap.Height - 1 do
begin
// get pointer to the currently iterated row's raw data
Pixels := ABitmap.ScanLine[Y];
// iterate the row's pixels from left to right in the whole bitmap width
for X := 0 to ABitmap.Width - 1 do
begin
// calculate luminance for the current pixel by the mentioned formula
Gray := Round((0.299 * Pixels[X].rgbtRed) +
(0.587 * Pixels[X].rgbtGreen) + (0.114 * Pixels[X].rgbtBlue));
// and assign the luminance to each color component of the current pixel
Pixels[X].rgbtRed := Gray;
Pixels[X].rgbtGreen := Gray;
Pixels[X].rgbtBlue := Gray;
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Bitmap: TBitmap;
begin
Bitmap := TBitmap.Create;
try
Bitmap.LoadFromFile('c:\ColorImage.bmp');
if Bitmap.PixelFormat <> pf24bit then
raise Exception.Create('Incorrect bit depth, bitmap must be 24-bit!');
GrayscaleBitmap(Bitmap);
Bitmap.SaveToFile('c:\GrayscaleImage.bmp');
finally
Bitmap.Free;
end;
end;
关于delphi - 如何对24位位图使用ScanLine属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13583451/
请在标记为重复之前阅读。 我正在创建一组依赖智能卡进行身份验证的应用程序。到目前为止,每个应用程序都单独控制智能卡读卡器。几周后,我的一些客户将同时使用多个应用程序。因此,我认为创建一个控制身份验证过
我想设置一个小程序,从数据库中检索信息,然后根据请求将该信息分发给另一个程序。例如,一个名为“Master”的程序将从数据库中检索数据并创建一个对象集合(列表、数组等,无论哪种效果最好),然后一个名为
我有两台电脑,都装有 XE2。我以为我在两者上安装了相同的安装,但在其中一个上安装第 3 方软件包时遇到问题,而另一个则正常。 无论如何,我希望两者都一样。最简单的人可能只是通过移入我的 Dropbo
有冲突吗? 最佳答案 所有新版本的 Delphi 始终可以安全地安装到旧版本的下一个版本。 每个新版本都应安装在其自己的目录中。 如果您要安装多个版本,请始终先安装最旧的版本,然后再安装最新版本。 我
快速提问:如果我从代码中删除 // 或 (* *) 中的注释,Delphi 2007 的执行时间会受到影响吗?最终结果是一个可能包含数千行注释的 EXE 文件。 最佳答案 编译器会简单地忽略注释,并且
我必须对照另一个文件检查文件的每一行。 如果第二个文件中存在第一个文件中的一行,则必须删除它。 现在,我正在使用2个列表框,并且“对于listbox1.items.count-1可以开始...” 我的
我正在尝试在访问数据库中添加一些数据。但是我有麻烦,因为这会返回错误: ADOQuery1 missing sql property 实现了对代码的几次修改,到目前为止没有任何效果。 我究竟做错了什么
我用Delphi 5编写了一个程序,在Windows 8 32位PC上可以正常运行。我发现在Windows 7 64位笔记本电脑上运行它最终会导致reallocmem错误,而该错误在32位PC上不会发
看来这是我需要的工具,用于提取XML并与TClientDataset连接。我已经在几篇文章和文档中看到了它,但是我无法在XE2组件列表中找到它-在任何地方!应该在哪里?是否在可能未安装的可选软件包中?
我正在寻找一个非常通用的TDBTree组件,我想听听一些建议。我正在特别寻找一种显示主记录和“ n”个链接表记录的记录。 (我的意思是来自各个表的记录)。例如,TDBTree将钩接到主表,明细表1,附
我需要将按钮制作成旋转三角形的形状(或者说是任何多边形)。谁能提供任何建议? 最佳答案 查看Win32 API CreatePolygonRgn()和SetWindowRgn()函数,以创建一个HRG
你好专家 我的JvPasswordForm1有一个旧的JVC组件。 似乎该组件不再存在:它替换为哪个组件? 重新获得 最佳答案 尝试查找TJvLoginDialog,TjvPassword已合并到其中
几天前,我已经设置了我的开发环境(在装有Win 7的VM和域上的用户的VM上安装了delphi 2009),并安装了我的组件(jedi's,devExpress,ADS等)。 今天,我启动机器,打开d
开始对控件进行子分类的正确位置/时间是什么? 恢复原始窗口proc的正确时间是几点? 现在我在表单创建过程中子类化: procedure TForm1.FormCreate(Sender: TObje
有人可以给我一些有关如何登录访问的网页(使用任何网络浏览器)的指示吗?我应该建立一个全球代理....钩住网络....吗?我需要记录的只是页面地址,而不是其中包含的信息。 我正在使用Delphi。 谢谢
我创建了一个像 TMyClass = class(TObject) private FList1: TObjectList; FList2: TObjectList; public end;
我有一个BPG文件,我已对其进行修改以用作我们公司的自动构建服务器的make文件。为了使其正常工作,我必须进行更改 用途*用途 'unit1.pas'中的unit1 * unit1 'unit2.pa
我将Delphi 7代码迁移到了Delphi XE4。我在Delphi XE4的LoadFromStram方法中遇到错误,但对于Delphi 7来说也可以正常工作。 错误: First chance
我正在尝试学习一些新技巧,以便更好地组织我在 Delphi 中的单元中的一些源代码。 我注意到我访问的一些函数或方法似乎是类中的类,但是我还没有成功地在类中创建一个工作类,虽然它编译得很好,但在执行代
我有一个包含许多类的大单元,现在我想通过将某些类分成新的单元来重构该单元。 我不得不承认我缺乏使用Delphi内置IDE功能的经验。利用内置功能“查找|查找对类型的本地引用”并没有多大帮助,因为类方法
我是一名优秀的程序员,十分优秀!