PDA

View Full Version : C to Pascal conversion hq2x



jdarling
05-06-2007, 04:37 PM
System: Windows, Linux, other
Compiler/IDE: FPC, Lazarus
Libraries/API: None

After seeing the threads and playing with hq2x myself I started looking for a pascal equivalent. There is none :(. So, I converted it myself.

I converted the entire contents of hp2x from C++ to FPC/Lazarus (should work in Delphi too). Problem is the output image is always black (read blank). I know that this is due to pointer problems, but it may also be due to the fact that I had to use LazarusRGB to load the images (this is so I can get the pointer to the pixels).

If anyone feels frisky download http://www.eonclash.com/PGD/hq2x.zip and see if you can fix it.

I think that there is quite a few of us that could benefit from just such a unit.

In case your lazy, my conversion of the pointers looks as follows:
C Source:
*((int*)pc) = (c1*3+c2) >> 2;
Pascal:Longword(pc{$IFDEF DeReferenceOutPointers}^{$ENDIF}) := (c1*3+c2) shr 2;

If you have DeReferenceInPointers defined then the app will blow up with an invalid index (I think this is due to the alpha channel in Lazarus RGB pixel pointer), if you don't have it defined then you get a black output image. Undefining DeReferenceOutPointers won't allow the application to compile, as the compiler can't match the types. I also had to make one minor change to the init method due to FPC complaining about signed assignment when there wasn't a problem in the first place :).

Lets get this working for all of our sakes :)

savage
05-06-2007, 05:58 PM
I can't compile your code in Delphi and I hate pointer arithmetic with a passion, but I wanted to ask why you changed all the integer declarations to Longword?

dmantione
05-06-2007, 06:03 PM
Why use the classes unit in procedural code?

jdarling
05-06-2007, 06:07 PM
Looking at longwords they are 4 bytes wide and unsigned. The way that the original source used the int's was as a unsigned value so I thought long was appropriate. I'll give normal ints a try :).

The classes thing is just me being lazy and not cleaning up lazarus' default template :)

Setharian
05-06-2007, 07:48 PM
try this, I've updated the unit and refactored it....unable to test it because I do not have Lazarus installed and Delphi does not have that TRGB32Bitmap class....http://filebeam.com/aac2781e878589afdd56a03a380d92b7
it *should* work now, the pointer arithmetic was translated incorrectly....

JSoftware
05-06-2007, 08:44 PM
I also tried to convert it. I didn't inline it however and use nested procedures for all the pixel things and a bit other naughty things like operator overloading for pointer types...

It compiles in both fpc and delphi though. I didn't bother testing it but here it is if you want it :P
http://www.graesdal.dk/opengl/laks/hq2x.pas

dmantione
05-06-2007, 09:09 PM
Why not:


{$ifdef FPC}
type Pbyte=^byte;
{$else}
ppbyte = ^byte;

pbyte = record
value: ppbyte;
class operator Implicit(x: pbyte): ppbyte;
class operator Add(a: pbyte; x: int): pbyte;
class operator Subtract(a: pbyte; x: int): pbyte;
end;
{$endif}

... so you don't need the ugly pointer arithmetic emulation on FPC's side?

JSoftware
05-06-2007, 09:11 PM
Well I didn't think that long. I primarily ported it for Delphi and then added fpc emulation afterwards

Sly
05-06-2007, 10:03 PM
In case your lazy, my conversion of the pointers looks as follows:
C Source:
*((int*)pc) = (c1*3+c2) >> 2;
Pascal:Longword(pc{$IFDEF DeReferenceOutPointers}^{$ENDIF}) := (c1*3+c2) shr 2;
The literal conversion of that C code would be
(PInteger(pc))^ := (c1 * 3 + c2) shr 2;
Note the dereference operator outside the parentheses, so it is dereferencing the typecasted pointer.

Setharian
06-06-2007, 07:17 AM
Why not:


{$ifdef FPC}
type Pbyte=^byte;
{$else}
ppbyte = ^byte;

pbyte = record
value: ppbyte;
class operator Implicit(x: pbyte): ppbyte;
class operator Add(a: pbyte; x: int): pbyte;
class operator Subtract(a: pbyte; x: int): pbyte;
end;
{$endif}

... so you don't need the ugly pointer arithmetic emulation on FPC's side?
why redeclaring PByte type anyways....Delphi allows direct pointer arithmetic only with PChars (not sure why though), so I changed it to a PChar which should work also under FPC because it allows pointer arithmetic on all pointer types (?)...

EDIT:
the function is not thread-safe because it has YUV1 and YUV2 variables declared as global even if they are local to the function....modified that (now it is threadsafe) and included $G- directive to speed up global memory access under Delphi....the link in my previous post has been updated....no pass this time....

jdarling
06-06-2007, 02:01 PM
Updated the download link above with Setharian's version. I did make one minor change from longword to hq2xnumber this way in 64 bit compilers it should be easy to find the right type when someone decides to change it.

I've also started to convert the cimage unit, as it seems that RGB won't work due to the internal pixel format :(. The converted cimage.pas file doesn't work properly right now. There are problems with the convert16 and convert24 methods. Basically I ran the C source through a converter and then cleaned it up until it compiled. Looking at the output of the images after convert16 is called it appears that I have my pointers jacked up again :), the colors are quite off but the pattern is correct.

Thanks everyone for the help so far, hopefully soon we will have a pure pascal version of hq2x. When this is complete I think working on 2xSAI would make sense. Any interest on converting this one as well?

savage
08-06-2007, 12:49 PM
So any screenies from this project yet?

jdarling
08-06-2007, 12:57 PM
Not yet, I'm still working on getting cimage working in its converted state. The hq2x download listed above, or in my zip file, seems to do exactly what its supposed to but needs a 16bit surface to work against. So until I get cimage converted I think it is almost useless as I can't find any image component for Lazarus that uses 16bit.

I will say that you get some pretty cool and interesting results pushing a Laz RGB image into the routine though :)

jdarling
11-06-2007, 03:20 PM
Ok, well sorta... Using the Vampyre Imaging Library (http://imaginglib.sourceforge.net/) I am able to prove that the conversion of hq2x works perfectly! It loads, scales, and saves out an image just fine.

This brings in an interesting problem though. I can load the resulting image in anything except for a Lazarus TImage. Oh well, loading with paint and then resaving works just fine. I wonder if I used Vampyre to save it to a more compatible format if it would work ok.

Anyways, the download link above has the updated code that works fine.

Here are the two samples, input and output, sorry for the bitmap posting but thats the format hq2x plays in and I don't want to show 3rd party conversions.

http://www.eonclash.com/PGD/test.bmphttp://www.eonclash.com/PGD/test2.bmp

Just in case, here is the download link (same as above) again:
http://www.eonclash.com/PGD/hq2x.zip

savage
11-06-2007, 03:29 PM
Looks good. Did it take you long to find the last few pointer issues?
How quick is it compared to other scalers?

jdarling
11-06-2007, 04:02 PM
Looks good. Did it take you long to find the last few pointer issues?
How quick is it compared to other scalers?

I still haven't managed to fix cimage, but when Vampyre worked I stopped looking :).

As for speed, I don't know haven't actually benchmarked many 2x scalers in my life. On my system it performs a scale of a 256x256 image easily at 60fps real time.

WILL
11-06-2007, 04:33 PM
If it performs well in window mode, I might just have to take advantage of it for my final version of S33. ;)

Nice work Jeremy.

Galfar
12-06-2007, 11:03 PM
This brings in an interesting problem though. I can load the resulting image in anything except for a Lazarus TImage. Oh well, loading with paint and then resaving works just fine. I wonder if I used Vampyre to save it to a more compatible format if it would work ok.

I noticed that too. Lazarus' bitmap loader for some reason only loads bottom-up and not top-down bitmaps (which Imaging creates). I'll probably change bitmap saver in Imaging to save bitmaps as bottom-up instead (these should work everywhere).

savage
13-06-2007, 09:41 AM
Jeremy, are there any plans to write an SDL compatible implementation by any chance :)?

jdarling
13-06-2007, 01:48 PM
Well, Galfar actually fixed my original translation so I'll have to get to where I understand his changes. But, yes, I plan on building out a version that works with SDL images eventually.

Jimmy Valavanis
02-07-2007, 05:24 AM
Using SHR directly in Delphi and FPC does not gives the appropriate results while converting from C to Pascal.



The >> in C while using integers is interptreted as sar in dissassemply!
The >> in C while using longwords is interptreted as shl in dissassemply!


Try implement the followng functions:




// Left shift between integers
function _SHL(const x: integer; const bits: integer): integer; assembler;
asm
// mov eax, x
// mov ecx, bits
mov ecx, edx
sal eax, cl
end;

// Left shift between longwords
function _SHLW(const x: LongWord; const bits: LongWord): LongWord;
begin
result := x shl bits;
end;

// Right shift between integers
function _SHR(const x: integer; const bits: integer): integer; assembler;
asm
mov ecx, edx
sar eax, cl
end;
{begin
result := x div (1 shl bits);
end;}

// Right shift between longwords
function _SHRW(const x: LongWord; const bits: LongWord): LongWord;
begin
result := x shr bits;
end;

jdarling
02-07-2007, 12:23 PM
Jim, how well is this going to compile on different platforms though? The idea of keeping this in FPC is that you can build it for Mac, Linux, Windows, or any other platform that FPC supports. I know that staying at the top of the assembly tree (SHL, SHR, MOV, CPY, etc...) is fairly safe between Windows and Linux but haven't tested the theory on Mac :).

Setharian
02-07-2007, 01:45 PM
the code posted will compile only on x86 or x86-64 cpu architectures...

basicly the problem is that shr operator with signed arguments generates an unsigned shift which does not preserve the sign of the number....

Jimmy Valavanis
03-07-2007, 04:00 AM
the code posted will compile only on x86 or x86-64 cpu architectures...


The following code will compile also on non x86 machines:



// Left shift between integers
function _SHL(const x: integer; const bits: integer): integer;
begin
result := x * (1 shl bits);
end;

// Left shift between longwords
function _SHLW(const x: LongWord; const bits: LongWord): LongWord;
begin
result := x shl bits;
end;

// Right shift between integers
function _SHR(const x: integer; const bits: integer): integer;
begin
result := x div (1 shl bits);
end;

// Right shift between longwords
function _SHRW(const x: LongWord; const bits: LongWord): LongWord;
begin
result := x shr bits;
end;

Setharian
03-07-2007, 06:29 AM
first - there is no such thing as a signed shift left so the function _SHL is completely redundant..._SHLU and _SHRU are just wrappers around the normal operators...._SHR does arithmetic shift by division and that is awfully slow, eliminating any advantage of performing a shift...you can as well as use div in the source code, it will even do a better because the division value can be a constant, no need to do "1 shl bits" to get the divisor...

I just found out that at least Delphi (haven't tested FPC) emits SAR when you use the div operator with a power of 2 divisor...doesn't solve the case when the number of bits to shift should vary....