【问题标题】:How to implement the scanline access of TBitmap correctly?如何正确实现 TBitmap 的扫描线访问?
【发布时间】:2012-05-01 16:26:45
【问题描述】:

我正在尝试根据article on Embarcadero 访问位图的扫描线。使用像

这样的扫描线
for y := 0 to n do
begin
   line := bitmap.scanline [y];
   for x := 0 to n do line [x] := value;

我以前实现过。我注意到访问扫描线需要相当多的时间,上面提到的文章提供了一个解决方案。我无法正确实现它。我的代码是:

unit SCTester;

interface

uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
     ExtCtrls;

type
   TRGBQuad = packed record
      b: uInt8;
      g: uInt8;
      r: uInt8;
      alpha: uInt8;
   end; // Record: TQuad //

// Override the definitions in Graphics.pas
   TRGBQuadArray = packed array [0..MaxInt div SizeOf (TRGBQuad) - 1] of TRGBQuad;
   PRGBQuadArray = ^TRGBQuadArray;

  TForm1 = class(TForm)
    Image: TImage;
    procedure ImageDblClick(Sender: TObject);
  end;

var Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.ImageDblClick(Sender: TObject);
var Bitmap: TBitmap;
    q: TRGBQuad;
    x, y: NativeInt;
    FirstLine: PRGBQuadArray;
    idx: NativeInt;
    LineLength: NativeInt;
begin
   q.r := 0; q.g := 0;
   Bitmap := TBitmap.Create;
   Bitmap.Height := Image.Height;
   Bitmap.Width  := Image.Width;
   Bitmap.PixelFormat := pf32Bit;
   FirstLine := Bitmap.ScanLine [0];
   LineLength := (NativeInt (Bitmap.Scanline [1]) - NativeInt (FirstLine)) div SizeOf (TRGBQuad);
   try
      for y := Bitmap.Height - 1 downto 0 do
      begin
         for x := 0 to Bitmap.Width - 1 do
         begin
            q.b := (x xor y) mod 255;
            idx := y * LineLength + x;
            FirstLine [idx] := q;
         end; // for
      end; // for
      Image.Picture.Assign (Bitmap);
   finally
       Bitmap.Free;
   end; // try..finally
end;

end.

当 y=1 和 x=0 时,我总是得到非法访问。 LineLength 是负数(位图的宽度),但这可能是预期的。我做错了什么?

编辑:上面的代码被改变以反映到目前为止处理的备注。

【问题讨论】:

  • idx 应声明为 NativeInt,因此您的代码也可以在 x64 中使用。 LineLength 不能为负数(因此非法访问)。我的结论是您正在以 64 位模式运行此代码。
  • @LURD,我的想法完全正确 - 每个 LongInt(...) 都应该替换为 NativeUInt(...)
  • @LURD 确定代码在 64 位模式下不正确,因为指针转换为 longints
  • @Arnold 是的,我在 windows7 64 位上有 Delphi XE,你的代码可以工作,除了在 Delphi XE2 64 位模式下将指针转换为 longints 之外,我在你的代码中没有发现其他问题。
  • LineLength := (Longint (Bitmap.Scanline [1]) - Longint (FirstLine)) 更改为LineLength := (NativeInt (Bitmap.Scanline [1]) - NativeInt (FirstLine)) 并将所有LongInt 声明更改为NativeInt

标签: delphi graphics bitmap delphi-xe2 scanline


【解决方案1】:

为了不访问任何负索引,我会这样做

procedure TForm1.Button1Click(Sender: TObject);
var Bitmap: TBitmap;
    q: TRGBQuad;
    x, y: LongInt;
    line{, FirstLine}: PRGBQuadArray;
    idx: NativeInt;
    LastLine: PRGBQuadArray;
    LineLength: NativeInt;
begin
   q.r := 0; q.g := 0;
   Bitmap := TBitmap.Create;
   Bitmap.Height := Image.Height;
   Bitmap.Width  := Image.Width;
   Bitmap.PixelFormat := pf32Bit;

   LastLine := Bitmap.ScanLine[Bitmap.Height - 1];
   LineLength := (NativeInt(Bitmap.Scanline[Bitmap.Height - 2]) - NativeInt(Lastline)) div SizeOf(TRGBQuad);
   try
      for y := 0 to Bitmap.Height - 1 do
      begin
         for x := 0 to Bitmap.Width - 1 do
         begin
            q.b := (x xor y) mod 255;
            idx := y * LineLength + x;
            LastLine [idx] := q;
         end; // for
      end; // for
      Image.Picture.Assign (Bitmap);
   finally
       Bitmap.Free;
   end; // try..finally
end;

【讨论】:

  • 有效!非常感谢!这确实是避免负数的好方法。这将帮助我加快位图速度。
  • @Arnold - 不客气!.. 不要忘记验证您是否拥有自下而上的位图。
  • 没错!我在有关扫描线的文章中读到的是,扫描线“通常”是从上到下排序的,因此会产生负线长。应该知道,为了让这段代码在所有情况下都能正确运行,必须明确地对正负行长度进行编码。编辑:我应该参考自下而上和自上而下的有序位图,以避免负线长或正线长度的混淆术语。
  • 它没有解释原始 OP 代码中的错误 - 编译器设置、64 位模式或其他。 OP 甚至没有说他是在 32 位还是 64 位模式下编译 - 总是不喜欢这样的问题。
  • @Serg - 当我参与其中时,由于在“TRGBQuadArray”上使用负索引(11th comment 现在的问题),OP 遇到了范围检查错误。其他可能的错误可能已经解决了。
【解决方案2】:

LineLength 对于很多位图来说都是负数,因为它们经常使用自底向上的方法来存储线条。 MSDN: BITMAPINFOHEADER。所以这个解决方案应该针对这种情况进行修改。

【讨论】:

  • 我明白你的意思,但我发现“LineLength 为负”有点令人困惑。我想说,在自下而上的位图中,第一条扫描线是内存布局中的最后一条。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-10
  • 2014-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-22
  • 1970-01-01
相关资源
最近更新 更多