How to generate CRC table for CRC-6? - delphi

I want to create a table for CRC-6/CDMA2000-A. I tried for various CRC-8 and this algo below works fine but for CRC-6 I get arrays which don't work for me.
Either the array is wrong or the function I use to calculate CRC-6 with the array.
const POLYNOMIAL = $27;
BitsCRC = 6;
var Table: array[0..255] of Byte;
i: Integer;
j: Integer;
temp: Byte;
S: String;
Mask: Byte;
begin
mask := (1 shl (BitsCRC - 1));
for i:=0 to 255 do Table[i] := i ;
for i:=0 to 255 do begin
for j:=7 downto 0 do begin
temp := Table[i] and Mask;
if (temp <> 0) then begin
Table[i] := Table[i] shl 1;
Table[i] := Table[i] xor POLYNOMIAL;
end
else begin
Table[i] := Table[i] shl 1;
end;
end;
end;
S := '';
for i:=0 to 255 do begin
S := S + '$' + IntToHex(Table[i], 2) +', ';
if i mod 16 = 15 then begin
Memo1.Lines.Add(s);
S := '';
end;
end;
I get this array from the code above:
$00, $F5, $CD, $38, $9A, $6F, $57, $A2, $34, $C1, $F9, $0C, $AE, $5B, $63, $96,
$4F, $BA, $82, $77, $D5, $20, $18, $ED, $7B, $8E, $B6, $43, $E1, $14, $2C, $D9,
$9E, $6B, $53, $A6, $04, $F1, $C9, $3C, $AA, $5F, $67, $92, $30, $C5, $FD, $08,
$D1, $24, $1C, $E9, $4B, $BE, $86, $73, $E5, $10, $28, $DD, $7F, $8A, $B2, $47,
$00, $F5, $CD, $38, $9A, $6F, $57, $A2, $34, $C1, $F9, $0C, $AE, $5B, $63, $96,
$4F, $BA, $82, $77, $D5, $20, $18, $ED, $7B, $8E, $B6, $43, $E1, $14, $2C, $D9,
$9E, $6B, $53, $A6, $04, $F1, $C9, $3C, $AA, $5F, $67, $92, $30, $C5, $FD, $08,
$D1, $24, $1C, $E9, $4B, $BE, $86, $73, $E5, $10, $28, $DD, $7F, $8A, $B2, $47,
$00, $F5, $CD, $38, $9A, $6F, $57, $A2, $34, $C1, $F9, $0C, $AE, $5B, $63, $96,
$4F, $BA, $82, $77, $D5, $20, $18, $ED, $7B, $8E, $B6, $43, $E1, $14, $2C, $D9,
$9E, $6B, $53, $A6, $04, $F1, $C9, $3C, $AA, $5F, $67, $92, $30, $C5, $FD, $08,
$D1, $24, $1C, $E9, $4B, $BE, $86, $73, $E5, $10, $28, $DD, $7F, $8A, $B2, $47,
$00, $F5, $CD, $38, $9A, $6F, $57, $A2, $34, $C1, $F9, $0C, $AE, $5B, $63, $96,
$4F, $BA, $82, $77, $D5, $20, $18, $ED, $7B, $8E, $B6, $43, $E1, $14, $2C, $D9,
$9E, $6B, $53, $A6, $04, $F1, $C9, $3C, $AA, $5F, $67, $92, $30, $C5, $FD, $08,
$D1, $24, $1C, $E9, $4B, $BE, $86, $73, $E5, $10, $28, $DD, $7F, $8A, $B2, $47
I try to generate CRC-6 like this:
procedure crc6_update(var CRC: Byte; Str: String);
var i: Integer;
begin
for i:=1 to Length(Str) do
CRC := CRC_Table[(CRC shl 2) xor ord(Str[i]) ] ;
end;
CRC := $3f;
crc6_update(CRC, '123456789');
Caption := IntToHex(CRC, 2);

There are a few problems. You are shifting junk up into the top two bits of the table entries, but then not getting rid of them with an and. More importantly, you are ignoring the top two bits, bits 6 and 7, of each initial table entry (i) with your mask, which is looking at bit 5.
To do this correctly, you need to compute the CRC in the top six bits of the byte (i) being processed. You mask bit 7 instead of bit 5, and you shift the polynomial up two bits to match that. If you are doing this in a byte, then that will take care of the junk being shifted up, since it falls off of the top of the byte.
You want:
Mask := 1 shl 7;
and:
Table[i] := Table[i] xor (POLYNOMIAL shl 2);
Now the CRCs in your table are actually where you'd want them to be anyway, which is in the top six bits of each byte. Then you don't need to do the CRC shl 2 for every single byte you compute. Instead that line in your calculation becomes:
CRC := CRC_Table[CRC xor ord(Str[i])];
Then after your loop you need to shift the CRC down two to return the CRC in the low six bits:
CRC := CRC shr 2;
and the initial value needs to be shifted up two:
CRC := $fc;
This will give the correct answer for that test vector, which is $0D.
I am not clear on why you have three loops with an array for your table generation. It seems like it would be simpler to do it like this, with one loop and no array (not tested -- I don't know Pascal/Delphi):
var
i: Integer;
j: Integer;
crc: Byte;
S: String;
begin
S := '';
for i := 0 to 255 do begin
crc := i;
for j := 0 to 7 do
if ((crc and $80) <> 0) then
crc := (crc shl 1) xor $9c
else
crc := crc shl 1;
S := S + '$' + IntToHex(crc, 2) + ', ';
if i mod 16 = 15 then begin
Memo1.Lines.Add(S);
S := '';
end;
end;
end.

Related

Calculate CRC-6 GSM using lookup table

I am trying to calculate CRC-6 GSM but the result is wrong ($16).
For '123456789' it should return $13 according to
https://reveng.sourceforge.io/crc-catalogue/all.htm#crc.cat.crc-6-gsm
var Table: array[0..255] of Byte = (
$3F, $10, $0E, $21, $32, $1D, $03, $2C,
$25, $0A, $14, $3B, $28, $07, $19, $36,
$0B, $24, $3A, $15, $06, $29, $37, $18,
$11, $3E, $20, $0F, $1C, $33, $2D, $02,
$38, $17, $09, $26, $35, $1A, $04, $2B,
$22, $0D, $13, $3C, $2F, $00, $1E, $31,
$0C, $23, $3D, $12, $01, $2E, $30, $1F,
$16, $39, $27, $08, $1B, $34, $2A, $05,
$31, $1E, $00, $2F, $3C, $13, $0D, $22,
$2B, $04, $1A, $35, $26, $09, $17, $38,
$05, $2A, $34, $1B, $08, $27, $39, $16,
$1F, $30, $2E, $01, $12, $3D, $23, $0C,
$36, $19, $07, $28, $3B, $14, $0A, $25,
$2C, $03, $1D, $32, $21, $0E, $10, $3F,
$02, $2D, $33, $1C, $0F, $20, $3E, $11,
$18, $37, $29, $06, $15, $3A, $24, $0B,
$23, $0C, $12, $3D, $2E, $01, $1F, $30,
$39, $16, $08, $27, $34, $1B, $05, $2A,
$17, $38, $26, $09, $1A, $35, $2B, $04,
$0D, $22, $3C, $13, $00, $2F, $31, $1E,
$24, $0B, $15, $3A, $29, $06, $18, $37,
$3E, $11, $0F, $20, $33, $1C, $02, $2D,
$10, $3F, $21, $0E, $1D, $32, $2C, $03,
$0A, $25, $3B, $14, $07, $28, $36, $19,
$2D, $02, $1C, $33, $20, $0F, $11, $3E,
$37, $18, $06, $29, $3A, $15, $0B, $24,
$19, $36, $28, $07, $14, $3B, $25, $0A,
$03, $2C, $32, $1D, $0E, $21, $3F, $10,
$2A, $05, $1B, $34, $27, $08, $16, $39,
$30, $1F, $01, $2E, $3D, $12, $0C, $23,
$1E, $31, $2F, $00, $13, $3C, $22, $0D,
$04, $2B, $35, $1A, $09, $26, $38, $17
);
function GSM_Update(Msg: PByte; Length: Integer): Byte;
var i: Integer;
H: Byte;
begin
H := $00;
for i:=0 to Length-1 do begin
H := Table[H xor Msg^]; //I believe this line might be wrong but don't know how to change it
Inc(Msg);
end;
Result := H xor $3F;
end;
Invoking is quite simple:
var Msg: AnsiString;
...
GSM_Update(#Msg[1], Length(Msg));
or
var Msg: array of AnsiChar;
Len: Integer;
...
SetLength(Msg, Len);
GSM_Update(#Msg[0], Len);
I'm not certain about the Pascal operators and syntax, but for the table in your code, that line needs to be:
H := (not Table[(H shl 2) xor Msg^]) and $3F;
This could be simplified and sped up by using a more appropriate table. I would replace each byte in the table by its one's complement shifted left by two bits. I.e. $00, $bc, $c4, $78, ... Then that line would be what you currently have:
H := Table[H xor Msg^];
and the last line would need to be changed to:
Result := (H shr 2) xor $3F;

Loading xmm register with two UInt64s that are in a pointed to array

I'm trying to load a 128-bit xmm register with two UInt64 integer in Delphi (XE6).
Background
An XMM register is 128-bits, and can be loaded with multiple, independent, integers. You can then have the CPU add those multiple integers all in parallel.
For example you can load up xmm0 and xmm1 with four UInt32s each, and then have the CPU add all four pairs simultaneously.
xmm0: $00001000 $00000100 $00000010 $00000001
+ + + +
xmm1: $00002000 $00000200 $00000020 $00000002
= = = =
xmm0: $00003000 $00000300 $00000030 $00000003
After loading xmm0 and xmm0, you perform the add of the four pairs using:
paddd xmm0, xmm1 //Add packed 32-bit integers (i.e. xmm0 := xmm0 + xmm1)
You could also do it using 8 x 16-bit integers:
xmm0: $001F $0013 $000C $0007 $0005 $0003 $0002 $0001
+ + + + + + + +
xmm1: $0032 $001F $0013 $000C $0007 $0005 $0003 $0002
= = = = = = = =
xmm0: $0051 $0032 $001F $0013 $000C $0007 $0005 $0003
With the instruction
paddw xmm0, xmm1 //Add packed 16-bit integers
Now for 64-bit integers
To load two 64-bit integers into an xmm register, you have to use either:
movdqu: Move double-quadword (unaligned)
movdqa: Move double-quadword (aligned)
In this simple example we won't worry about our UInt64s being aligned, and we'll simply use the unaligned version (movdqu)
The first thing that we have to deal with is that the Delphi compiler knows that movdqu needs a 128-bit something to load - it's loading double quadwords.
For this we will create a 128-bit structure, which also nicely lets us address the two 64-bit values:
TDoubleQuadword = packed record
v1: UInt64; //value 1
v2: UInt64; //value 2
end;
And now we can use this type in a test console app:
procedure Main;
var
x, y: TDoubleQuadword;
begin
//[1,5] + [2,7] = ?
x.v1 := $0000000000000001;
x.v2 := $0000000000000005;
y.v1 := $0000000000000002;
y.v2 := $0000000000000007;
asm
movdqu xmm0, x //move unaligned double quadwords (xmm0 := x)
movdqu xmm1, y //move unaligned double quadwords (xmm1 := y)
paddq xmm0, xmm1 //add packed quadword integers (xmm0 := xmm0 + xmm1)
movdqu x, xmm0 //move unaligned double quadwords (x := xmm0)
end;
WriteLn(IntToStr(x.v1)+', '+IntToSTr(x.v2));
end;
And this works, printing out:
3, 12
Eye on the prize
With an eye towards the goal of having the x and y be aligned (but not a necessary part of my question), lets say we have a pointer to a TDoubleQuadword structure:
TDoubleQuadword = packed record
v1: UInt64; //value 1
v2: UInt64; //value 2
end;
PDoubleQuadword = ^TDoubleQuadword;
we now change up our hypothetical test function to use PDoubleQuadword:
procedure AlignedStuff;
var
x, y: PDoubleQuadword;
begin
x := GetMemory(sizeof(TDoubleQuadword));
x.v1 := $0000000000000001;
x.v2 := $0000000000000005;
y := GetMemory(sizeof(TDoubleQuadword));
y.v1 := $0000000000000002;
y.v2 := $0000000000000007;
asm
movdqu xmm0, x //move unaligned double quadwords (xmm0 := x)
movdqu xmm1, y //move unaligned double quadwords (xmm1 := y)
paddq xmm0, xmm1 //add packed quadword integers (xmm0 := xmm0 + xmm1)
movdqu x, xmm0 //move unaligned double quadwords (v1 := xmm0)
end;
WriteLn(IntToStr(x.v1)+', '+IntToSTr(x.v2));
end;
Now this doesn't compile, and it makes sense why:
movdqu xmm0, x //E2107 Operand size mismatch
That makes sense. The x argument must be 128-bits, and the compiler knows that x is really only a (32-bit) pointer.
But what should it be?
Now we come to my question: what should it be? I've randomly mashed various things on my keyboard, hoping that the compiler gods would just accept what i obviously mean. But nothing works.
//Don't try to pass the 32-bit pointer itself, pass the thing it points to:
movdqu xmm0, x^ //E2107 Operand size mismatch
//Try casting it
movdqu xmm0, TDoubleQuadword(x^) //E2105 Inline assembler error
//i've seen people using square brackets to mean "contents of":
movdqu xmm0, [x] //E2107 Operand size mismatch
And now we give up on rational thought
movdqu xmm0, Pointer(x)
movdqu xmm0, Addr(x^)
movdqu xmm0, [Addr(x^)]
movdqu xmm0, [Pointer(TDoubleQuadword(x))^]
I did get one thing to compile:
movdqu xmm0, TDoubleQuadword(x)
But of course that loads the address of x into the register, rather than the values inside x.
So i give up.
Complete Minimal Example
program Project3;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
TDoubleQuadword = packed record
v1: UInt64; //value 1
v2: UInt64; //value 2
end;
PDoubleQuadword = ^TDoubleQuadword;
TVectorUInt64 = array[0..15] of UInt64;
PVectorUInt64 = ^TVectorUInt64;
procedure AlignedStuff;
var
x, y: PVectorUInt64;
begin
x := GetMemory(sizeof(TVectorUInt64));
//x[0] := ...
//x[1] := ...
// ...
//x[3] := ...
x[4] := $0000000000000001;
x[5] := $0000000000000005;
y := GetMemory(sizeof(TVectorUInt64));
//y[0] := ...
//y[1] := ...
// ...
//y[3] := ...
y[4] := $0000000000000002;
y[5] := $0000000000000007;
asm
movdqu xmm0, TDoubleQuadword(x[4]) //move unaligned double quadwords (xmm0 := x)
movdqu xmm1, TDoubleQuadword(y[4]) //move unaligned double quadwords (xmm1 := y)
paddq xmm0, xmm1 //add packed quadword integers (xmm0 := xmm0 + xmm1)
movdqu TDoubleQuadword(x[4]), xmm0 //move unaligned double quadwords (v1 := xmm0)
end;
WriteLn(IntToStr(x[4])+', '+IntToSTr(x[5]));
end;
begin
try
AlignedStuff;
Writeln('Press enter to close...');
Readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Pointer?
The reason the question is asking about pointers is because:
you cannot use stack variables (Delphi doesn't guarantee alignment of stack variables)
you could copy them into a register (e.g. EAX), but then you're doing a wasted copy and function call
i already have the data aligned in memory
If i give an example of the code that just involves adding UInt64s:
TVectorUInt64 = array[0..15] of UInt64;
PVectorUInt64 = ^TVectorUInt64;
var
v: PVectorUInt64;
begin
v := GetMemoryAligned(sizeof(TVectorUInt64), 64); //64-byte alignment
//v is initalized
for i := 0 to 15 do
begin
v[0] := v[0] + v[4];
v[1] := v[1] + v[5];
v[2] := v[2] + v[6];
v[3] := v[3] + v[7];
//..and some more changes to v0..v3
//..and some more changes to v12..v15
v[8] := v[8] + v[12];
v[9] := v[9] + v[13];
v[10] := v[10] + v[14];
v[11] := v[11] + v[15];
//...and some more changes to v4..v7
v[0] := v[0] + v[4];
v[1] := v[1] + v[5];
v[2] := v[2] + v[6];
v[3] := v[3] + v[7];
//...and some more changes to v0..v3
//...and some more changes to v12..v15
v[8] := v[8] + v[12];
v[9] := v[9] + v[13];
v[10] := v[10] + v[14];
v[11] := v[11] + v[15];
//...and some more changes to v4..v7
v[0] := v[0] + v[4];
v[1] := v[1] + v[5];
v[2] := v[2] + v[6];
v[3] := v[3] + v[7];
//..and some more changes to v0..v3
//..and some more changes to v12..v15
v[8] := v[8] + v[12];
v[9] := v[9] + v[13];
v[10] := v[10] + v[14];
v[11] := v[11] + v[15];
//...and some more changes to v4..v7
v[0] := v[0] + v[4];
v[1] := v[1] + v[5];
v[2] := v[2] + v[6];
v[3] := v[3] + v[7];
//...and some more changes to v0..v3
//...and some more changes to v12..v15
v[8] := v[8] + v[12];
v[9] := v[9] + v[13];
v[10] := v[10] + v[14];
v[11] := v[11] + v[15];
//...and some more changes to v4..v7
end;
It is conceptually very easy to change the code to:
//v[0] := v[0] + v[4];
//v[1] := v[1] + v[5];
asm
movdqu xmm0, v[0]
movdqu xmm1, v[4]
paddq xmm0, xmm1
movdqu v[0], xmm0
end
//v[2] := v[2] + v[6];
//v[3] := v[3] + v[7];
asm
movdqu xmm0, v[2]
movdqu xmm1, v[6]
paddq xmm0, xmm1
movdqu v[2], xmm0
end
//v[8] := v[8] + v[12];
//v[9] := v[9] + v[13];
asm
movdqu xmm0, v[8]
movdqu xmm1, v[12]
paddq xmm0, xmm1
movdqu v[8], xmm0
end
//v[10] := v[10] + v[14];
//v[11] := v[11] + v[15];
asm
movdqu xmm0, v[10]
movdqu xmm1, v[14]
paddq xmm0, xmm1
movdqu v[10], xmm0
end
The trick is getting the Delphi compiler to accept it.
it works for immediate data
it fails for pointer to data
and you would think [contentsOfSquareBrackets] would work
Bonus Chatter
Using David's solution (of function calling overhead) leads to a performance improvement of -7% (90 MB/s -> 83 MB/s of algorithm throughput)
It seems like, in the XE6 compiler, it is valid to conceptually call:
movdqu xmm0, TPackedQuadword
but the compiler just doesn't have the brains to let you perform the conceptual call:
movdqu xmm0, PPackedQuadword^
or it's moral equivalent.
If that's the answer, don't be afraid of it. Embrace it, and put it as the form of an answer:
*"The compiler does not support dereferencing a pointer inside an asm block. No matter if you try that with a caret (^), or square brackets ([...]). It just cannot be done.
If that's the answer: answer it.
If it's not the case, and the compiler can support pointers in an asm block, then post the answer.
The documentation for inline assembler in Delphi isn't as comprehensive as it should be and a lot of the functionality is simply not documented. So I can't be sure of this, but to the best of my knowledge there is simply no support for the assembler statement that you are trying to write, where one operand is a local variable of pointer type.
I would strongly urge you to avoid mixing Pascal code and assembler code in the same function. It makes very hard to produce efficient code, and makes it very hard to manage register usage as you move between Pascal code and assembler code in the same function.
I personally make it a rule never to mix Pascal and inline assembler. Always write pure assembler functions. For instance, for 32 bit code you would write a complete program like this:
{$APPTYPE CONSOLE}
type
PDoubleQuadword = ^TDoubleQuadword;
TDoubleQuadword = record
v1: UInt64;
v2: UInt64;
end;
function AddDoubleQuadword(const dqw1, dqw2: TDoubleQuadword): TDoubleQuadword;
asm
movdqu xmm0, [eax]
movdqu xmm1, [edx]
paddq xmm0, xmm1
movdqu [ecx], xmm0
end;
procedure AlignedStuff;
var
x, y: PDoubleQuadword;
begin
New(x);
x.v1 := $0000000000000001;
x.v2 := $0000000000000005;
New(y);
y.v1 := $0000000000000002;
y.v2 := $0000000000000007;
x^ := AddDoubleQuadword(x^, y^);
Writeln(x.v1, ', ', x.v2);
end;
begin
AlignedStuff;
Readln;
end.
This program outputs:
3, 12
Or you could use a record with operators:
type
PDoubleQuadword = ^TDoubleQuadword;
TDoubleQuadword = record
v1: UInt64;
v2: UInt64;
class operator Add(const dqw1, dqw2: TDoubleQuadword): TDoubleQuadword;
end;
class operator TDoubleQuadword.Add(const dqw1, dqw2: TDoubleQuadword): TDoubleQuadword;
asm
movdqu xmm0, [eax]
movdqu xmm1, [edx]
paddq xmm0, xmm1
movdqu [ecx], xmm0
end;
And then at the call site you have:
x^ := x^ + y^;
Working code:
asm
mov eax, x
mov edx, y
movdqu xmm0, DQWORD PTR [eax] //move unaligned double quadwords (xmm0 := x)
movdqu xmm1, DQWORD PTR [edx] //move unaligned double quadwords (xmm1 := y)
paddq xmm0, xmm1 //add packed quadword integers (xmm0 := xmm0 + xmm1)
movdqu DQWORD PTR [eax], xmm0 //move unaligned double quadwords (v1 := xmm0)
end;
IntToStr(x.v1)+', '+IntToSTr(x.v2); prints 3,12

Piping between two programs Delphi

I want to make a program that will do piping between two programs
Getting input from first, giving it to second that will do processing on it and give it back to me which I will give back to first
if Input <> '-' then
InS := TFileStream.Create(Input, fmOpenRead or fmShareDenyWrite)
else
InS := THandleStream.Create(GetStdHandle(STD_INPUT_HANDLE));
if Output <> '-' then
OutS := TFileStream.Create(Output, fmCreate or fmShareExclusive)
else
OutS := THandleStream.Create(GetStdHandle(STD_OUTPUT_HANDLE));
FillChar(StartupInfo, sizeof(StartupInfo), 0);
FillChar(ProcessInfo, sizeof(ProcessInfo), 0);
SecurityAttributes.nLength := sizeof(SecurityAttributes);
SecurityAttributes.bInheritHandle := True;
SecurityAttributes.lpSecurityDescriptor := Nil;
CreatePipe(OutPipe, InPipe, #SecurityAttributes, 0);
StartupInfo.cb := sizeof(StartupInfo);
StartupInfo.wShowWindow := SW_HIDE;
StartupInfo.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
StartupInfo.hStdInput := InPipe;
StartupInfo.hStdOutput := OutPipe;
Handle := CreateProcess(PChar(CLSAppInfo.CLSExeName),
PChar(Format('a - -', [])), nil, nil, True, 0, nil, PChar(GetCurrentDir),
StartupInfo, ProcessInfo);
if Handle then
begin
GetMem(Buffer, BLOCK_SIZE);
repeat
ReadByte := InS.Read(Buffer^, BLOCK_SIZE);
isOK := WriteFile(InPipe, Buffer^, ReadByte, WroteByte, nil);
WriteLn(ReadByte.ToString);
WriteLn(isOK.ToString());
if (not isOK) or (ReadByte = 0) then
break;
repeat
isOK := ReadFile(OutPipe, Buffer^, 255, ReadByte, nil);
if not isOK then
break;
if ReadByte = 0 then
break;
OutS.Write(Buffer, ReadByte);
until 0 = 1;
until 0 = 1;
FreeMem(Buffer);
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
end;
CloseHandle(InPipe);
CloseHandle(OutPipe);
OutS.Free;
InS.Free;
But this just starts the programs and the programs ends without doing anything

SSE2 optimisation in Delphi?

I know in C++ you can use some c++ SSE intrinsic functions to do vector calculations, but not sure if this is possible in latest Delphi compilers.
I have some code like this and they are in the core algorithms that are being called many many times, I would like to speed up this bit.
Any advices would be appreciated.
type
Vector = array [1..3] of Single;
VectorInt = array [1..3] of Integer;
function TSomeCalculation.getCubePosRound(const xyz: Vector): VectorInt;
begin
// Self.RESINV and Self.RangeMin are type of Vector
Result[1] := Round((xyz[1] - Self.RangeMin[1]) * Self.RESINV[1]);
Result[2] := Round((xyz[2] - Self.RangeMin[2]) * Self.RESINV[2]);
Result[3] := Round((xyz[3] - Self.RangeMin[3]) * Self.RESINV[3]);
end;
Quick sketch. Note using of 4-element types.
Edit. Changed movdqu to movups (not essential)
type
Vector = array [0..3] of Single;
VectorInt = array [0..3] of Integer;
...
var
Form1: TForm1;
Vec: Vector;
VecI: VectorInt;
RESINV: Vector;
RangeMin: Vector;
procedure TForm1.Button6Click(Sender: TObject);
begin
vec[0] := 10;
vec[1] := 12;
vec[2] := 14;
vec[3] := 16;
RangeMin[0] := 4.2;
RangeMin[1] := 5.2;
RangeMin[2] := 6.2;
RangeMin[3] := 7.2;
RESINV[0] := 0;
RESINV[1] := 1.1;
RESINV[2] := 2.2;
RESINV[3] := 3.3;
vecI := getCubePosRound(vec);
end;
function Tform1.getCubePosRound(const xyz: Vector): VectorInt;
asm
movups xmm1, [xyz] //load 16 bytes unaligned
movups xmm2, [RangeMin]
movups xmm3, [RESINV]
subps xmm1, xmm2 //subtract packed singles
mulps xmm1, xmm3 //multiply
cvtps2dq xmm0, xmm1 //rounded according to MXCSR register
movdqu [Result], xmm0 //store 16 bytes
end;

How to get bitmap transparancy without the need to paint first?

A newly created bitmap seems to have a (white) background by default. At least, a query on the Pixels property confirms. But why is that background color not used as the transparent color when Transparent is set true?
Consider this simple test code:
procedure TForm1.Button1Click(Sender: TObject);
var
Bmp: TBitmap;
begin
Bmp := TBitmap.Create;
try
Bmp.Width := 100;
Bmp.Height := 100;
Bmp.Transparent := True;
Canvas.Draw(0, 0, Bmp); // A white block is drawn
Bmp.Canvas.Brush.Color := Bmp.Canvas.Pixels[0, 99]; // = 'clWhite'
Bmp.Canvas.FillRect(Rect(0, 0, 100, 100));
Canvas.Draw(0, 100, Bmp); // "Nothing" is drawn
finally
Bmp.Free;
end;
end;
For some reason, the entire bitmap surface has to be painted before it can appear transparent, which sounds kind of odd.
The following variations are tried to eliminate the call to FillRect, all with the same outcome (no transparancy):
Only setting Brush.Color,
Brush.Handle := CreateSolidBrush(clWhite),
PixelFormat := pf32Bit,
IgnorePalette := True,
TransparantColor := clWhite,
TransparantMode := tmFixed,
Canvas.Pixels[0, 99] := clWhite which makes only thát pixel transparent,
Modified := True.
So, the wish is to paint only a portion of a newly created bitmap and get the remaining surface transparent.
Using: Delphi 7, Win 7/64.
Just set TransparentColor and Canvas.Brush.Color before setting dimensions of the bitmap.
I really needed to be able to create a completely transparent (and otherwise empty/blank) TBitmap of an arbitrary size in 32bit RGBA format. Many times. Lazarus is able to load such a bitmap into TBitmap and after it's loaded, you can manipulate it with scanline and what not using RGBA format. But it just doesn't work when you create TBitmap yourself. Pixel format seems to be completely ignored. So what I did is so out-of-the box, and simple, that it's almost AMUZING (!). But it is practical, works super nice, and is completely independent of LCL and any 3rd party libraries. Even does not depend on Graphics unit, becasue it generates the actual 32bit RGBA BMP file (I generate it to TMemoryStream, you can generate differently). Then once you have it, elsewhere in your code you can just load it using TBitmap.LoadFrom source or TPicture.LoadFrom source.
The background story
I initially wanted to generate properly formatted BMP file following the format as described here: http://www.fileformat.info/format/bmp/egff.htm
But there were few variants of BMP format, and I was not clear on which one I was supposed to follow. So I decided to go with a reverse engineering approach, but the format description helped me later on. I used a graphical editor (I used GIMP) to create an empty 1x1 pixel 32 RGBA BMP file, and called it alpha1p.bmp, it only contained transparency nothing else.
Then I resized the canvas to 10x10 pixels, and saved as alpha10p.bmp file.
Then I compared the two files:
compating two bmp files in vbindiff on Ubuntu
So I found out that the only differences were the added pixels (every one was 4 bytes all zeros RGBA), and few other bytes in the header. Because of the format documentation at the linked I shared, I figured out that these were: FileSize (in bytes), BitmapWidth (in pixels), BitmapHeight (in pixels) and BitmapDataSize (in bytes). The last one was BitmapWidth*BitmapHeight*4, because each pixel in RGBA is 4 bytes. So now, I could just generate that entire sequence of bytes as seen inside alpha1p.bmp files, minus 4 bytes from the end (the 1st of the BitmapData), then add 4 bytes (all zeroes) of RGBA data for each pixel of the BMP I want to generate, then come back to the initial sequence and update the variable parts: FileSize, width, height and BMP data size. And it works flawlessly! I just had to add test for BigEndian and would swap word and dword numbers before writting. That would become issue on ARM platforms working in BigEndian.
The code
const
C_BLANK_ALPHA_BMP32_PREFIX : array[0..137]of byte
= ($42, $4D, $00, $00, $00, $00, $00, $00, $00, $00, $8A, $00, $00, $00, $7C, $00,
$00, $00, $0A, $00, $00, $00, $0A, $00, $00, $00, $01, $00, $20, $00, $03, $00,
$00, $00, $90, $01, $00, $00, $13, $0B, $00, $00, $13, $0B, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $FF, $00, $00, $FF, $00, $00, $FF,
$00, $00, $FF, $00, $00, $00, $42, $47, $52, $73, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $02, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00 );
(...)
Function RenderEmptyAlphaBitmap(AWidth,AHeight: integer): TMemoryStream;
var
buf : array[1..4096]of byte;
i,p : int64;
w : word;
dw : dword;
BE : Boolean;
begin
buf[low(buf)] := $00; //this is jyst to prevent compiler warning about not initializing buf variable
Result := TMemoryStream.Create;
if(AWidth <1)then AWidth := 1;
if(AHeight<1)then AHeight := 1;
//Write File Header:
Result.Write(C_BLANK_ALPHA_BMP32_PREFIX, SizeOf(C_BLANK_ALPHA_BMP32_PREFIX));
//Now start writing the pixels:
FillChar(buf[Low(buf)],Length(buf),$00);
p := Result.Position;
Result.Size := Result.Size+int64(AWidth)*int64(AHeight)*4;
Result.Position := p;
i := int64(AWidth)*int64(AHeight)*4; //4 because RGBA has 4 bytes
while(i>0)do
begin
if(i>Length(buf))
then w := Length(buf)
else w := i;
Result.Write(buf[Low(buf)], w);
dec(i,w);
end;
//Go back to the original header and update FileSize, Width, Height, and offset fields:
BE := IsBigEndian;
Result.Position := 2; dw := Result.Size;
if BE then SwapEndian(dw); Result.Write(dw, SizeOf(dw));
Result.Position := 18; dw := AWidth;
if BE then SwapEndian(dw); Result.Write(dw, SizeOf(dw));
Result.Position := 22; dw := AHeight;
if BE then SwapEndian(dw); Result.Write(dw, SizeOf(dw));
Result.Position := 34; dw := AWidth*AHeight*4;
if BE then SwapEndian(dw); Result.Write(dw, SizeOf(dw));
//Done:
Result.Position := 0;
end;
Notice how C_BLANK_ALPHA_BMP32_PREFIX constant is basically the copy of byte sequence from my sample alpha1p.bmp file, minus last 4 bytes, which were the RGBA pixel. :D
Also, I am using IsBigEndian function which goes like this:
Function IsBigEndian: Boolean;
type
Q = record case Boolean of
True : (i: Integer);
False : (p: array[1..4] of Byte);
end;
var
x : ^Q;
begin
New(x);
x^.i := 5;
Result := (x^.p[4]=5);
Dispose(x);
end;
This is copied from Lazarus Wiki: http://wiki.freepascal.org/Writing_portable_code_regarding_the_processor_architecture you can skip this part if you don't deal with BigEndian platforms, or you can use a compilers IFDEF directive. The thing is that if you use {$IFDEF ENDIAN_BIG} then it is what compiler things is the case, whereas the function actually tests the system. This is explained in the linked wiki.
Sample usage
Procedure TForm1.Button1Click(Sender: TObject);
var
MS : TMemoryStream;
begin
MS := RenderEmptyAlphaBitmap(Image1.Width, Image1.Height);
try
if Assigned(MS)then Image1.Picture.LoadFromStream(MS);
//you can also MS.SaveToFile('my_file.bmp'); if you want
finally
FreeAndNil(MS);
end;
end;
This will draw a Red square and the rest is transparent.
procedure TForm1.btnDrawClick(Sender: TObject);
var
Bmp: TBitmap;
begin
Bmp := TBitmap.Create;
try
Bmp.Width := 100;
Bmp.Height := 100;
Bmp.Transparent := TRUE;
Bmp.TransparentColor := clWhite;
Bmp.Canvas.Brush.Color := clWhite;
Bmp.Canvas.FillRect(Rect(0, 0, Bmp.Width, Bmp.Height));
Bmp.Canvas.Brush.Color := clRed;
Bmp.Canvas.FillRect(Rect(42, 42, 20, 20));
Canvas.Draw(12, 12, Bmp);
finally
Bmp.Free;
end;
end;

Resources