如何使用 ScanLine属性的24位位图像素操纵?为什么我应该更喜欢使用它而不是经常使用 Pixels属性?

解决方法

1.介绍

在这篇文章中,我将尝试解释ScanLine属性的使用只为24位位图像素格式,如果你真的需要使用它。首先看看什么使这个属性如此重要。

2. ScanLine是否…?

你可以问自己为什么使用这样棘手的技术,如使用ScanLine属性貌似是当你可以简单地使用Pixels访问你的位图的像素。答案是,即使在相对较小的像素区域上执行像素修改时,性能差异也很大。

Pixels属性内部使用Windows API函数 – GetPixelSetPixel,用于获取和设置设备上下文颜色值。 Pixels技术的性能缺乏,你通常需要在修改像素颜色值之前,在内部意味着调用这两个提到的Windows API函数。 ScanLine属性赢得了这场比赛,因为提供了对存储位图像素数据的内存的直接访问。和直接内存访问只是比两个Windows API函数调用。

但是,这并不意味着Pixels属性是完全坏,你应该避免使用它在所有情况下。当你要修改只是几个像素(不是一个大的区域)偶尔,例如,然后Pixels可能就足够了。但是,当你要使用像素区域操作时,不要使用它。

深入像素内

3.1原始数据

位图的像素数据(现在我们称之为原始数据),你可以想象为一个一维的字节数组,包含每个像素的颜色分量的强度值序列。位图中的每个像素由根据所使用的像素格式的固定计数的字节组成。

例如,24位像素格式对于其每个颜色分量(红色,绿色和蓝色通道)具有1个字节。下图说明了如何想象这样的24位位图的原始数据字节数组。这里的每个彩色矩形代表一个字节:

3.2案例研究

想象一下,你有一个24位的位图3×2像素(宽度3px;高度2px),并保持在你的头脑,因为我会尝试解释一些内部,并显示一个原则的ScanLine属性使用它。它是如此之小,只是因为需要一个深层视图内部的空间(对于那些有明亮的视线是一个绿色的例子这样的图像在这里png格式这里↘↙:-)

3.3像素组成

首先让我们来看看我们的位图图像的像素数据是如何在内部存储的;看看原始数据。下图显示了原始数据字节数组,其中可以看到我们的微小位图的每个字节及其在该数组中的索引。您还可以注意到,3个字节的组如何形成各个像素,以及这些像素位于我们的位图上的坐标:

其另一视图提供以下图像。每个框表示我们的虚拟位图的一个像素。在每个像素中,您可以从原始数据字节数组中查看其坐标和3个字节及其索引的组:

生活与颜色

4.1。初始值

正如我们已经知道的,我们虚构的24位位图中的像素由3个字节组成 – 每个颜色通道1个字节。当你在想象中创建了这个位图时,所有像素中的所有字节都会被初始化为最大字节值 – 255。这意味着所有通道现在都具有最大颜色强度:

当我们看一看,每个像素的这些初始通道值混合了哪种颜色,我们将看到我们的位图是entirely white.因此,当您在Delphi中创建一个24位位图时,它最初是白色的。好的,白色将在每个像素格式的位图默认情况下,但他们可能不同的初始原始数据字节值。

ScanLine的秘密生活

从上面的读取我希望你理解,位图数据如何存储在原始数据字节数组和如何从这些数据形成单个像素。现在转到ScanLine属性本身,以及如何在直接原始数据处理中有用。

5.1。 ScanLine用途

这个帖子的主菜单,ScanLine属性,是一个只读的索引属性,它返回指向属于位图中指定行的原始数据字节数组的第一个字节的指针。换句话说,我们请求访问给定行的原始数据字节数组,我们收到的是指向该数组的第一个字节的指针。此属性的索引参数指定要为其获取这些数据的行的基于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;

因为我试图容忍那些有Delphi版本低于2009年,因为它使代码以某种方式更易于理解我不会使用指针算术迭代,但固定长度的数组与指针在下面的例子(指针算术在下面的Delphi 2009中将不太可读)。

因此,我们有一个像素的TRGBTriple结构,现在我们为行数组定义一个类型。这将简化位图行像素的迭代。这一个我只是从ShadowWnd.pas单位(一个有趣的类的家,反正)。这里是:

type
  PRGBTripleArray = ^TRGBTripleArray;
  TRGBTripleArray = array[0..4095] of TRGBTriple;

正如你所看到的,一行的限制为4096像素,对于通常宽的图像应该足够了。如果这不足以为你,只是增加上限。

6. ScanLine在实践中

6.1。使第二行为黑色

让我们从第一个例子开始。在这里我们对象化我们的虚拟位图,设置它适当的宽度,高度和像素格式(或如果你想,一个位深度)。然后我们使用ScanLine与行参数1获取指向第二行的原始数据字节数组的指针。我们得到的指针,我们将分配给指向TRGBTriple数组的RowPixels变量,所以从那以后我们可以把它作为一个行像素数组。然后我们在位图的整个宽度中迭代该数组,并将每个像素的所有颜色值设置为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;

6.2。使用亮度的灰度位图

作为一个有意义的例子,我在这里发布使用亮度灰度化位图的过程。它使用从顶部到底部的所有位图行的迭代。对于每一行,然后获得指向原始数据的指针,并且像以前一样作为像素阵列。然后,该阵列的每个像素通过以下公式计算亮度值:

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;

以及上述程序的可能用法。请注意,您只能对24位位图使用此过程:

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;

7.相关阅读

> Leonel Togniolli: How to Use Scanlines
> Earl F. Glynn: Manipulating Pixels With Delphi’s ScanLine Property

delphi – 如何使用ScanLine属性的24位位图?的更多相关文章

  1. canvas像素点操作之视频绿幕抠图

    这篇文章主要介绍了canvas像素点操作之视频绿幕抠图的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. html5利用canvas实现颜色容差抠图功能

    这篇文章主要介绍了html5利用canvas实现颜色容差抠图功能,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下

  3. 吃透移动端 1px的具体用法

    这篇文章主要介绍了吃透移动端 1px的具体用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  4. HTML5如何使用SVG的方法示例

    这篇文章主要介绍了HTML5如何使用SVG的方法示例,详细的介绍了什么是SVG以及如何使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  5. canvas中普通动效与粒子动效的实现代码示例

    canvas用于在网页上绘制图像、动画,可以将其理解为画布,在这个画布上构建想要的效果。本文详细的介绍了粒子特效,和普通动效进行对比,非常具有实用价值,需要的朋友可以参考下

  6. html5使用canvas实现弹幕功能示例

    这篇文章主要介绍了html5使用canvas实现弹幕功能示例的相关资料,需要的朋友可以参考下

  7. Canvas系列之滤镜效果

    Canvas 真的是一个神奇的东西,不仅能够绘制各种图形、文本和位图,还能够对位图进行复杂的像素运算和处理。因此像滤镜这些东西,其实 Canvas 也可以来实现。本文介绍了Canvas滤镜的实现,感兴趣的可以参考了解下

  8. Canvas 像素处理之改变透明度的实现代码

    这篇文章主要介绍了Canvas 像素处理之改变透明度的实现代码的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  9. 深入了解canvas在移动端绘制模糊的问题解决

    这篇文章主要介绍了深入了解canvas在移动端绘制模糊的问题解决,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  10. 前端实现弹幕效果的方法总结(包含css3和canvas的实现方式)

    这篇文章主要介绍了前端实现弹幕效果的方法总结(包含css3和canvas的实现方式)的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

随机推荐

  1. delphi – 主窗口按进程名称处理

    DelphiXe,Win7x64如何从进程名称(exe文件的完整路径)获取主窗口句柄,或至少一个类或窗口名称(如果该进程只有一个窗口).例:解决方法我同意Petesh的说法,你需要枚举顶级窗口并检查创建它的进程的模块文件名.为了帮助您开始枚举顶级窗口,这是一个delphi实现.首先,当你回调给你时,你需要一些与EnumWindows方法通信的方式.为此声明一条记录,该记录将保存您要查找的模块的文件

  2. 如何在Delphi中纯粹通过RTTI信息(即不使用任何实际对象实例)获取TObjectList的子项类型?

    我正在使用RTTI实现用于流式传输任意Delphi对象的通用代码,并且为了使其工作(更具体地说,为了使加载部分工作),我需要以某种方式获得TObjectList的子项类型<T>不使用任何实际对象实例的字段.要求不使用任何实际对象实例的明显原因是,在从流加载对象的情况下(仅基于要加载的对象的类类型的知识),我将不会有任何实例在加载完成之前完全可用–我宁愿只能访问相关类的纯RTTI数据.我希望能

  3. inno-setup – Inno Setup – 安装程序背景图片

    图像作为安装程序背景如何用inno5.5.9做到这一点?

  4. inno-setup – Inno Setup – 如何添加多个arc文件进行解压缩?

    使用InnoSetup解压缩弧文件.我希望有可能解压缩多个arc文件以从组件选择中安装文件(例如).但仍然显示所有提取的整体进度条.这可能吗?的回答的修改预备是相同的,参考其他答案.在ExtractArc中,为要提取的每个存档调用AddArchive.

  5. delphi – 如何在DataSet的帮助下在TAdvStringGrid中显示数据库中的BLOB图像

    解决方法CreateBlobStream正在创建一个TStream对象,而不是TMemoryStream.由于您不想将JPG写入数据库,因此应使用bmRead而不是bmReadWrite.我不习惯sqlite,但你必须确保使用合适的二进制日期类型.为了确保存储的图像真的是JPG,您应该编写JPG以进行测试,例如:

  6. inno-setup – 在Inno Setup的Code部分下载程序后运行程序

    如何运行我通过Internet下载的应用程序,在代码部分中使用,并等待该应用程序完成运行.我有,使用InnoTools下载程序,下载这两个文件,我想,在第二个完成下载后运行该下载,或jdk-8u111-windows-x64.exe,然后继续安装.解决方法使用其他下载插件,而不是ITD(请参阅下面的原因).例如,InnoDownloadPlugin.当您包含idp.iss时,它定义了一个全局IDP

  7. progress-bar – Inno Setup Run部分的简单进度页面

    我的安装程序非常简单,它基本上是:>欢迎页面>进展页面>最终页面欢迎页面和最终页面是标准页面.在Progress页面,我正在静默安装一堆其他程序.实际的脚本是在[Run]部分中安装每个程序.问题是酒吧达到100%然后停留在那里.我只能更改消息文本.我想要实现的是使用Pascal脚本显示进度,例如:这样我就可以显示更准确的进度条.这就是我所拥有的:问题是,当我构建安装程序时,它不显示欢迎页面.我做错了什么?

  8. delphi – 如何使“显示/隐藏桌面图标”设置生效?

    下面的代码调用SHGetSetSettings函数来隐藏桌面图标但它只是从视图菜单中取消选中“显示桌面图标”.我打电话给SHChangeNotify;更新桌面,但这不起作用?解决方法isa,要刷新桌面,您可以将F5键发送到progman窗口隐藏桌面图标的另一种方法是再次显示

  9. inno-setup – Inno Setup – 避免显示子安装程序的文件名

    我试图使用InnoSetup–Howtohidecertainfilenameswhileinstalling?(FilenameLabel)的想法Theonlysuresolutionistoavoidinstallingthefiles,youdonotwanttoshow,usingthe[Files]section.Installthemusingacodeinstead.UsetheEx

  10. inno-setup – Inno Setup磁力链接下载实施

    我目前正在使用InnoDownloadPlugin为我的安装程序下载文件,这个问题最大的问题是faila正确下载文件.因为连接不良等诸多原因.我想添加一种替代方法来下载文件,因此用户可以选择是否需要常规方式或torrent方式.我知道我可以使用aria2c.exe应用程序(https://aria2.github.io/),有人可以帮我实现它的inno设置代码吗?我需要的是使用torrent(ar

返回
顶部