delphi – 如何使用ScanLine属性的24位位图?
如何使用
ScanLine 属性的24位位图像素操纵?为什么我应该更喜欢使用它而不是经常使用
Pixels 属性?
解决方法
1.介绍
在这篇文章中,我将尝试解释 2. ScanLine是否…? 你可以问自己为什么使用这样棘手的技术,如使用
但是,这并不意味着 深入像素内 3.1原始数据 位图的像素数据(现在我们称之为原始数据),你可以想象为一个一维的字节数组,包含每个像素的颜色分量的强度值序列。位图中的每个像素由根据所使用的像素格式的固定计数的字节组成。 例如,24位像素格式对于其每个颜色分量(红色,绿色和蓝色通道)具有1个字节。下图说明了如何想象这样的24位位图的原始数据字节数组。这里的每个彩色矩形代表一个字节: 3.2案例研究 想象一下,你有一个24位的位图3×2像素(宽度3px;高度2px),并保持在你的头脑,因为我会尝试解释一些内部,并显示一个原则的 3.3像素组成 首先让我们来看看我们的位图图像的像素数据是如何在内部存储的;看看原始数据。下图显示了原始数据字节数组,其中可以看到我们的微小位图的每个字节及其在该数组中的索引。您还可以注意到,3个字节的组如何形成各个像素,以及这些像素位于我们的位图上的坐标: 其另一视图提供以下图像。每个框表示我们的虚拟位图的一个像素。在每个像素中,您可以从原始数据字节数组中查看其坐标和3个字节及其索引的组: 生活与颜色 4.1。初始值 正如我们已经知道的,我们虚构的24位位图中的像素由3个字节组成 – 每个颜色通道1个字节。当你在想象中创建了这个位图时,所有像素中的所有字节都会被初始化为最大字节值 – 255。这意味着所有通道现在都具有最大颜色强度: 当我们看一看,每个像素的这些初始通道值混合了哪种颜色,我们将看到我们的位图是 ScanLine的秘密生活 从上面的读取我希望你理解,位图数据如何存储在原始数据字节数组和如何从这些数据形成单个像素。现在转到 5.1。 ScanLine用途 这个帖子的主菜单, 下图说明了我们的虚拟位图和我们使用不同行索引的 5.2。 ScanLine优势 所以,从我们所知道的,我们可以总结, 嗯,我们有一个行的每个像素的颜色强度的数组。考虑这种阵列的迭代;它将不会舒服的循环通过这个数组一个字节,并只调整像素的3个颜色部分之一。更好的是循环通过像素,并与每次迭代一次调整所有3个颜色字节 – 就像我们以前做的 5.3。跳过像素 为了简化行数组循环,我们需要一个与我们的像素数据匹配的结构。幸运的是,对于24位位图,有 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。使第二行为黑色 让我们从第一个例子开始。在这里我们对象化我们的虚拟位图,设置它适当的宽度,高度和像素格式(或如果你想,一个位深度)。然后我们使用 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 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |