Recent

Author Topic: Copy the contents of a 2D dynamic array to another one  (Read 27828 times)

han

  • Full Member
  • ***
  • Posts: 137
Re: Copy the contents of a 2D dynamic array to another one
« Reply #60 on: January 31, 2019, 03:38:05 pm »
In this case object behaves exactly like array, i.e. no code changes are needed.
Looks interesting. I like to try it but I'm struggling with the object definition. Could you help me with the complete object definition?

Later:  There are some good examples available at:
http://wiki.freepascal.org/Fast_direct_pixel_access

But is it possible to mimic array access like B[x,y] rather then B(x,y)?
« Last Edit: January 31, 2019, 05:15:28 pm by han »

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 881
Re: Copy the contents of a 2D dynamic array to another one
« Reply #61 on: January 31, 2019, 08:34:46 pm »
But is it possible to mimic array access like B[x,y] rather then B(x,y)?
What do you mean? You can't access it like B(x, y). See my example and "Raw dynamic memory" section of your example. You can access it like B.Pixels[x, y], but if you would declare Pixels property as default, you would be able to access it like B[x, y]. It's obvious, that only one properly can be default.

If you need reference counting, then use interfaces. Interface itself is nothing more, than abstract class with reference counting. Therefore it can contain method declarations only. But properties are also possible there. Then you just need to declare class, that will implement it.

Example of interface declaration:
Code: Pascal  [Select][+][-]
  1. IMyData = interface
  2.   procedure SetData(AX, AY, AZ:Integer;AItem:TItem);
  3.   function GetData(AX, AY, AZ:Integer):TItem;
  4.   property Data[AX, AY, AZ:Integer]:TItem read GetItem write SetItem;default;
  5. end;
  6.  
  7. //TInterfacedObject implements IUnknown methods, so we don't need to bother about them
  8. TMyData = class(TInterfacedObject, IMyData)
  9. //Everything else is the same, as in my previous example
  10. end;
  11.  
  12. X:IMyData;
  13.  
  14. X := TMyData.Create(10, 10, 10);
  15.  
  16. X[0, 0, 0] := 1.0;
  17.  
« Last Edit: January 31, 2019, 08:36:22 pm by Mr.Madguy »
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 881
Re: Copy the contents of a 2D dynamic array to another one
« Reply #62 on: February 02, 2019, 06:14:50 am »
For han. Next time please read docs for reference, cuz it's just example of syntax to give you main idea, that isn't tested to be workable, and I just can't write complete programs for everybody. Please note, that it still isn't tested to make sure, that it works 100% correctly. But at least it compiles now.

P.S. TItem - is any data, you want. You just needed to declare it.

P.S.S. This class can even be turned into generic to make it universal.
Code: Pascal  [Select][+][-]
  1. unit unit1;
  2. {$mode objfpc}{$H+}
  3. interface
  4.  
  5. uses
  6.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs;
  7.  
  8. type
  9.   TItem = Real;
  10.   TData = array[0..0] of TItem;
  11.   PData = ^TData;
  12.  
  13.   IMyData = interface
  14.     procedure SetData(AX, AY, AZ:Integer;AItem:TItem);
  15.     function GetData(AX, AY, AZ:Integer):TItem;
  16.     function Copy:IMyData;
  17.     property Data[AX, AY, AZ:Integer]:TItem read GetData write SetData;default;
  18.   end;
  19.  
  20.   TMyData = class(TInterfacedObject, IMyData)
  21.   protected
  22.     FData:PData;
  23.     FLengthX, FLengthY, FLengthZ:Integer;
  24.     procedure SetData(AX, AY, AZ:Integer;AData:TItem);
  25.     function GetData(AX, AY, AZ:Integer):TItem;
  26.   public
  27.     constructor Create(ALengthX, ALengthY, ALengthZ:Integer);
  28.     destructor Destroy;override;
  29.     function Copy:IMyData;
  30.     property Data[AX, AY, AZ:Integer]:TItem read GetData write SetData;default;
  31.   end;
  32.  
  33. type
  34.  
  35.   { TForm1 }
  36.  
  37.   TForm1 = class(TForm)
  38.     procedure FormCreate(Sender: TObject);
  39.   private
  40.  
  41.   public
  42.  
  43.   end;
  44.  
  45. var
  46.   Form1: TForm1;
  47.  
  48. implementation
  49. {$R *.lfm}
  50.  
  51. { TForm1 }
  52.  
  53. procedure TForm1.FormCreate(Sender: TObject);
  54.   var X, Y:IMyData;
  55. begin
  56.   X := TMyData.Create(10, 10, 10);
  57.  
  58.   X[0, 0, 0] := 1.0;
  59.  
  60.   Y := X.Copy;
  61. end;
  62.  
  63.  
  64. constructor TMyData.Create(ALengthX, ALengthY, ALengthZ:Integer);
  65. begin
  66.   FLengthX := ALengthX;
  67.   FLengthY := ALengthY;
  68.   FLengthZ := ALengthZ;
  69.   FData := GetMem(FLengthX * FLengthY * FLengthZ * SizeOf(TItem));
  70. end;
  71.  
  72. destructor TMyData.Destroy;
  73. begin
  74.   FreeMem(FData);
  75. end;
  76.  
  77. procedure TMyData.SetData(AX, AY, AZ:Integer;AData:TItem);
  78. begin
  79.   //Check ranges here
  80.   FData^[(((AZ * FLengthY) + AY) * FLengthX) + AX] := AData;
  81. end;
  82.  
  83. function TMyData.GetData(AX, AY, AZ:Integer):TItem;
  84. begin
  85.   //Check ranges here
  86.   Result := FData^[(((AZ * FLengthY) + AY) * FLengthX) + AX];
  87. end;
  88.  
  89. function TMyData.Copy:IMyData;
  90.   var Temp:TMyData;
  91. begin
  92.   Temp := TMyData.Create(FLengthX, FLengthY, FLengthZ);
  93.   Move(FData^[0], Temp.FData^[0], FLengthX * FLengthY * FLengthZ * SizeOf(TItem));
  94.   Result := Temp;
  95. end;
  96.  
  97. end.
  98.  
« Last Edit: February 02, 2019, 10:26:16 am by Mr.Madguy »
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

User137

  • Hero Member
  • *****
  • Posts: 1791
    • Nxpascal home
Re: Copy the contents of a 2D dynamic array to another one
« Reply #63 on: February 02, 2019, 09:27:16 am »
There shouldn't be any notable speed difference between GetMem() and Setlength() approach, i stopped using pointer arrays years ago at least with problems that don't really benefit from them. Even storing pixel data for OpenGL is dynamic arrays. But one thing is sure, dynamic arrays are easier to use.
« Last Edit: February 02, 2019, 09:30:01 am by User137 »

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 881
Re: Copy the contents of a 2D dynamic array to another one
« Reply #64 on: February 02, 2019, 10:30:36 am »
There shouldn't be any notable speed difference between GetMem() and Setlength() approach, i stopped using pointer arrays years ago at least with problems that don't really benefit from them. Even storing pixel data for OpenGL is dynamic arrays. But one thing is sure, dynamic arrays are easier to use.
For 1D array - yes. But for multi-dimentional array it's not true. Memory alloc/free operations aren't free. More such operations - bigger performance penalty. And of course memory copying operations have their small tax too, so one operation is always better, than many.
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

han

  • Full Member
  • ***
  • Posts: 137
Re: Copy the contents of a 2D dynamic array to another one
« Reply #65 on: September 12, 2023, 10:23:16 pm »
This is an old topic, but I like to revisit it using Lazarus 3.0RC1 and I have updated my test program with a class array (method 6, 7) as proposed by @Mr.Madguy. See attachment.

The fastest method to copy an image as an array is method 5 or 6.  The 3D data is stored in a 1D array.

However if individual pixel access is required using a 3D dynamic array (method 1, 17 sec))  is faster then putting the 3D data in a 1D dynamic array (method 4, 46 sec). The new class MyArray is very slow (method 7, execution time 2:17)

Using 3D dynamic array for image handling seems still the best option. Both duplicating and individual pixel access is fast.

Method 2:
Code: Pascal  [Select][+][-]
  1. type
  2.   image_array = array of array of array of Single;
  3.  
  4. function duplicate2(img:image_array) :image_array;
  5. var
  6.   c,w,h,k,i: integer;
  7. begin
  8.   c:=length(img);
  9.   h:=length(img[0]);
  10.   w:=length(img[0,0]);
  11.  
  12.   setlength(result,c,h,w);
  13.   for k:=0 to c-1 do
  14.    for i:=0 to h-1 do
  15.    begin
  16.      result[k,i]:=copy(img[k,i],0,w);
  17.    end;
  18. end;

The largest dimension of the array is best put last. So for a landscape image setlength(image_array,nrcolors,height,width);


Han


The test results:

 Method 1, using a 3D array, for k:=0 to nrcolors-1 do for i:=0 to height2-1 do for j:=0 to width2-1 do D[k,i,j]:=C[k,i,j]; 
Excution time 00:00:17

 Method 2, using a 3D array, for k:=0 to nrcolors-1 do for i:=0 to height2-1 do D[k,i]:=copy(C[k,i],0,width2);
Excution time 00:00:04

 Method 3, Using 3D array, D:=C;{copy pointer} setlength(D,nrcolors,height2,width2);{make real duplicate}
Excution time 00:00:07

 Method 4, Using a 1D array to store 3D data.
Excution time 00:00:46

 Method 5, Using 1D array, F:=copy(E,0,nrcolors*height2*width2-1);
Excution time 00:00:05

 Method 6, Using 3D array using a new class MyArray. H := G.Copy;
Excution time 00:00:05

 Method 7, Using 3D array using a new class MyArray. For k:=0 to nrcolors-1 do for i:=0 to height2-1 do for j:=0 to width2-1 do H[k,i,j]:=G[k,i,j];
 This is very slow and will typically take a few minutes.
Execution time 00:02:17
« Last Edit: September 21, 2023, 01:53:03 pm by han »

 

TinyPortal © 2005-2018