Does anyone know what algo XP -> full screen DOS?

Windows specific questions.
Post Reply
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Does anyone know what algo XP -> full screen DOS?

Post by MrSwiss »

lizard wrote:The numbers 1 - 25 span only a part of the screen here.
Yes, of course. The reason is: 480 / 16 = 30 (rows).
lizard
Posts: 440
Joined: Oct 17, 2017 11:35
Location: Germany

Re: Does anyone know what algo XP -> full screen DOS?

Post by lizard »

But 480 / 25 = 19.2 ?
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Does anyone know what algo XP -> full screen DOS?

Post by MrSwiss »

You don't seem to understand. The Font's height is 16 (pixels), it's width is 8.
See paul doe's code:

Code: Select all

width sWidth / 8, sHeight / 16
This then gives: 80 columns, 30 rows
(screen is: graphics, not console)
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Does anyone know what algo XP -> full screen DOS?

Post by paul doe »

lizard wrote:But 480 / 25 = 19.2 ?
MrSwiss wrote:(screen is: graphics, not console)
Of course, lizard. But, as MrSwiss points, it's just a graphical window, not real console mode. Old VGA displays were actually 640x400, not 640x480. If you change it in the code, you'll see that it effectively gives you an 80x25 'text' mode. The 'width' call tells the FBGFX library to use the 8x16 internal font size.
CGAMan
Posts: 31
Joined: Nov 24, 2017 18:23

Re: Does anyone know what algo XP -> full screen DOS?

Post by CGAMan »

counting_pine wrote:As best I recall, natural LCD scaling tends to look something like this:

Code: Select all

function avg(col1 as long, col2 as long, w1 as single, w2 as single) as long
    dim as ubyte r1 = col1 shr 16, r2 = col2 shr 16
    dim as ubyte g1 = col1 shr  8, g2 = col2 shr 8
    dim as ubyte b1 = col1       , b2 = col2

    var r = r1 + (r2 - r1) * w2 / (w1 + w2)
    var g = g1 + (g2 - g1) * w2 / (w1 + w2)
    var b = b1 + (b2 - b1) * w2 / (w1 + w2)

    return rgb(r, g, b)
end function

function spoint(x1 as single, y1 as single, x2 as single, y2 as single) as long
    dim as long col1, col2
    dim as single w1, w2
    if int(x1) = int(x2) then
        if int(y1) = int(y2) then
            return point(x1, y1)
        else
            col1 = point(x1, y1)
            col2 = point(x1, y1+1)
            w1 = int(y1 + 1) - y1
            w2 = y2 - int(y2)
            return avg(col1, col2, w1, w2)
        end if
    else
        col1 = spoint(x1, y1, x1, y2)
        col2 = spoint(x2, y1, x2, y2)
        w1 = int(x1 + 1) - x1
        w2 = x2 - int(x2)
        return avg(col1, col2, w1, w2)
    end if
end function

screenres 1024, 768, 24

dim as integer w = 320, h = 240
dim as single dx = 320 / 1024, dy = 240 / 768

circle (160, 120), 100, &h00ff00
paint (160, 120), &h0000ff, &h00ff00
line (140, 100)-(180,140), &hff0000, b
sleep

for y as integer = 768 to 0 step -1
    for x as integer = 1024 to 0 step -1
        pset (x, y), spoint(x * dx, y * dy, (x + 1) * dx, (y + 1) * dy)
    next x
next y
sleep
It preserves the basic "rectangular" nature of the pixels, like with nearest neighbour scaling, but with blurrier edges between the original pixels.
It can be nicer than nearest-neighbour, because it doesn't give the horrible "jagged" effect that happens when one pixel is enlarged to a pixel wider than its neighbour.

The way I picture it is like laying a set of smaller mosaic tiles (say 1 square cm) over a set of larger mosaic tiles (say 1 square inch).
If the smaller mosaic tile lands entirely within a larger tile, it gets the colour of that tile.
Otherwise, if it straddles two tiles (or four tiles), its colour is the average of the tiles it covers, weighted by how much of the smaller tile covers it.

I don't know what this method is called. But it's pretty much what you'd get if you converted some pixel art to an SVG and zoomed in
Thanks, this was exactly what I was looking for. Nearest neighbor upscaling but without the jagged edges.
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Does anyone know what algo XP -> full screen DOS?

Post by MrSwiss »

@paul doe,

AFAIR, you are mixing up the old, graphics Standards:
  • CGA = 320 x 200
    EGA = 640 x 400
    VGA = 640 x 480
(just to clarify matters)
caseih
Posts: 2157
Joined: Feb 26, 2007 5:32

Re: Does anyone know what algo XP -> full screen DOS?

Post by caseih »

Depends on whether you mean graphics mode or text mode. Vga text mode is actually 640x400. And actually sometimes it's 720x400 and the graphics card would duplicate the last column of the character cell's pixels, which made those special fill characters look funny.

Btw, Ega graphics mode was 640x350. And yes you are right that vga graphics mode is indeed 640x480. Ega used 8x14 cells for text and vga used 8x16. 30 rows.
CGAMan
Posts: 31
Joined: Nov 24, 2017 18:23

Re: Does anyone know what algo XP -> full screen DOS?

Post by CGAMan »

counting_pine wrote:As best I recall, natural LCD scaling tends to look something like this:

Code: Select all

function avg(col1 as long, col2 as long, w1 as single, w2 as single) as long
    dim as ubyte r1 = col1 shr 16, r2 = col2 shr 16
    dim as ubyte g1 = col1 shr  8, g2 = col2 shr 8
    dim as ubyte b1 = col1       , b2 = col2

    var r = r1 + (r2 - r1) * w2 / (w1 + w2)
    var g = g1 + (g2 - g1) * w2 / (w1 + w2)
    var b = b1 + (b2 - b1) * w2 / (w1 + w2)

    return rgb(r, g, b)
end function

function spoint(x1 as single, y1 as single, x2 as single, y2 as single) as long
    dim as long col1, col2
    dim as single w1, w2
    if int(x1) = int(x2) then
        if int(y1) = int(y2) then
            return point(x1, y1)
        else
            col1 = point(x1, y1)
            col2 = point(x1, y1+1)
            w1 = int(y1 + 1) - y1
            w2 = y2 - int(y2)
            return avg(col1, col2, w1, w2)
        end if
    else
        col1 = spoint(x1, y1, x1, y2)
        col2 = spoint(x2, y1, x2, y2)
        w1 = int(x1 + 1) - x1
        w2 = x2 - int(x2)
        return avg(col1, col2, w1, w2)
    end if
end function

screenres 1024, 768, 24

dim as integer w = 320, h = 240
dim as single dx = 320 / 1024, dy = 240 / 768

circle (160, 120), 100, &h00ff00
paint (160, 120), &h0000ff, &h00ff00
line (140, 100)-(180,140), &hff0000, b
sleep

for y as integer = 768 to 0 step -1
    for x as integer = 1024 to 0 step -1
        pset (x, y), spoint(x * dx, y * dy, (x + 1) * dx, (y + 1) * dy)
    next x
next y
sleep
It preserves the basic "rectangular" nature of the pixels, like with nearest neighbour scaling, but with blurrier edges between the original pixels.
It can be nicer than nearest-neighbour, because it doesn't give the horrible "jagged" effect that happens when one pixel is enlarged to a pixel wider than its neighbour.

The way I picture it is like laying a set of smaller mosaic tiles (say 1 square cm) over a set of larger mosaic tiles (say 1 square inch).
If the smaller mosaic tile lands entirely within a larger tile, it gets the colour of that tile.
Otherwise, if it straddles two tiles (or four tiles), its colour is the average of the tiles it covers, weighted by how much of the smaller tile covers it.

I don't know what this method is called. But it's pretty much what you'd get if you converted some pixel art to an SVG and zoomed in
I tried this code in FreeBasic. Works great! So, I tried to adapt this into C# and ran into a few problems.

Am I correct to assume int(6.7) = 6 in FreeBasic, whereas Convert.ToInt32(6.7) in C# might yield 7?


Update: I got the code to work in C#. Just have to use Convert.ToInt32(Math.Floor()) where int() is found. It's really slow, so now I have to learn about parallel processing...
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Does anyone know what algo XP -> full screen DOS?

Post by dodicat »

You can't make a silk purse out of a sow's ear, so they say.
Here is my effort

Code: Select all


#include "file.bi"
Function grey(c As Ulong) As Ulong
    dim as ubyte v=.299*((c Shr 16)And 255)+.587*((c Shr 8)And 255)+.114*(c And 255)
    Return Rgb(v,v,v)
End Function

function resize(picture As String,_x As long=0,_y As long=0,greyflag as long=0) as any ptr
    if fileexists (picture)=0 then print picture;"  not found":sleep:end
    #define map(a,b,x,c,d) ((d)-(c))*((x)-(a))/((b)-(a))+(c)
    dim as long dimensionx,dimensiony
     Open picture For Binary access read As #1
    Get #1, 19, dimensionx
    Get #1, 23, dimensiony
   ' get #1, 29, b
    Close #1
    if _x* _y=0 then _x=dimensionx:_y=dimensiony
    dim as single dx=_x/dimensionx,dy=_y/dimensiony
    dx=dx/2:dy=dy/2
    dim as any ptr im=Imagecreate(dimensionx,dimensiony)
    dim as any ptr tim=Imagecreate(_x,_y)
            Bload picture,im
            Dim As Ulong col
            For y As long=0 To (dimensiony-1)
                For x As long=0 To (dimensionx-1)
                    Dim As long xx=map(0,(dimensionx-1),x,0,_x)
                    Dim As long yy=map(0,(dimensiony-1),y,0,_y)
                    if greyflag then
                   Line tim,(xx-dx,yy-dy)-(xx+dx,yy+dy),grey(point(x,y,im)),bf
               else
                    Line tim,(xx-dx,yy-dy)-(xx+dx,yy+dy),point(x,y,im),bf
                   end if
                Next x
            Next y
        return tim
End function

Function Filter(Byref tim As Ulong Pointer,_
    byval rad As Single,_
    byval destroy As long=1,_
    byval fade As long=0) As Ulong Pointer
    #define map(a,b,_x_,c,d) ((d)-(c))*((_x_)-(a))/((b)-(a))+(c)
    If fade<0 Then fade=0:If fade>100 Then fade=100
    Type p2
        As long x,y
        As Ulong col
    End Type
    #macro ppoint(_x,_y,colour)
    pixel=row+pitch*(_y)+(_x)*4
    (colour)=*pixel
    #endmacro
    #macro ppset(_x,_y,colour)
    pixel=row+pitch*(_y)+(_x)*4
    *pixel=(colour)
    #endmacro
    #macro average()
    ar=0:ag=0:ab=0:inc=0
    xmin=x:If xmin>rad Then xmin=rad
    xmax=rad:If x>=(_x-1-rad) Then xmax=_x-1-x
    ymin=y:If ymin>rad Then ymin=rad
    ymax=rad:If y>=(_y-1-rad) Then ymax=_y-1-y
    For y1 As long=-ymin To ymax
        For x1 As long=-xmin To xmax
            inc=inc+1 
            ar=ar+(NewPoints(x+x1,y+y1).col Shr 16 And 255)
            ag=ag+(NewPoints(x+x1,y+y1).col Shr 8 And 255)
            ab=ab+(NewPoints(x+x1,y+y1).col And 255)
        Next x1
    Next y1
    If fade=0 Then
        averagecolour=Rgb(ar/(inc),ag/(inc),ab/(inc))
    Else
        averagecolour=Rgb(fd*ar/(inc),fd*ag/(inc),fd*ab/(inc))
    End If
    #endmacro
    Dim As Single fd=map(0,100,fade,1,0)
    Dim As integer _x,_y
    Imageinfo tim,_x,_y
    Dim  As Ulong Pointer im=Imagecreate(_x,_y)
    Dim As integer pitch
    Dim  As Any Pointer row
    Dim As Ulong Pointer pixel
    Dim As Ulong col
    Imageinfo tim,,,,pitch,row
    Dim As p2 NewPoints(_x-1,_y-1)
    For y As long=0 To (_y)-1
        For x As long=0 To (_x)-1
            ppoint(x,y,col)
            NewPoints(x,y)=Type<p2>(x,y,col)
        Next x
    Next y
    Dim As Ulong averagecolour
    Dim As long ar,ag,ab
    Dim As long xmin,xmax,ymin,ymax,inc
    Imageinfo im,,,,pitch,row
    For y As long=0 To _y-1
        For x As long=0 To _x-1  
            average()
            ppset((NewPoints(x,y).x),(NewPoints(x,y).y),averagecolour) 
        Next x
    Next y
    If destroy Then Imagedestroy tim: tim = 0
    Function= im
End Function

Function sharpen(Byref im As Any Ptr) As Any Ptr
    'point and pset speeded up
    #macro _point(_x,_y,colour)
    pixel=row+pitch*(_y)+4*(_x)
    (colour)=*pixel
    #endmacro
    #macro _pset(_x,_y,colour)
    pixel=row+pitch*(_y)+4*(_x)
    *pixel=(colour)
    #endmacro      
    Dim   As Long Sieve(2,2)={ {-1,-1,-1}, _
    {-1,12,-1}, _
    {-1,-1,-1}}
    Dim As integer szx,szy
    Dim As integer pitch
    Dim  As Any Pointer row
    Dim As Ulong Pointer pixel
    Dim As Ulong col
    Imageinfo im,szx,szy,,pitch,row
    Dim _out(szx-2, szy-2, 2) As Long
    Dim As Long f,r,g,b
    Dim As Ulong c
    
    For Y As Long = 1 To szy-2
        For X As Long = 1 To szx-2
            R = 0 : G = 0 : B = 0
            For I As Long = -1 To 1
                For J As Long = -1 To 1
                        
                    _point((x+i),(y+j),c)
                    f = Sieve(I+1,J+1)
                    r += f * ( ( c ) Shr 16 And 255 )
                    g += f * ( ( c ) Shr  8 And 255 )
                    b += f * ( ( c )        And 255 )
                Next
            Next
            If r < 0 Then r = 0 :End If:If r > 1020 Then  r = 1020
            If g < 0 Then g = 0 :End If:If g > 1020 Then  g = 1020
            If b < 0 Then b = 0 :End If:If b > 1020 Then  b = 1020
            _out(x,y,0) = r \ 4 
            _out(x,y,1) = g \ 4 '
            _out(x,y,2) = b \ 4  
        Next
    Next
    For y As Long = 1 To szy-2
        For x As Long = 1 To szx-2
           
            _pset(x,y,Rgb(_out(x,y,0),_out(x,y,1),_out(x,y,2)))
        Next x
    Next y
    
    Return im
    
End Function


screenres 1024, 768, 24

circle (160, 120), 100, &h00ff00
paint (160, 120), &h0000ff, &h00ff00
draw string(140,70),"Hello"
line (140, 100)-(180,140), &hff0000, b
dim as any ptr i=imagecreate(211,211)
line(60,20)-(260,220),rgb(0,100,200),b
get (60-5,20-5)-(260+5,220+5),i
bsave "Temporary.bmp",i
dim as any ptr i2=resize("Temporary.bmp",700,700)


i2=filter(i2,2)' try 1  2  3 ...
'i2=sharpen(i2)'try

put(260,40),i2

sleep


imagedestroy i
imagedestroy i2
kill "Temporary.bmp"


 
some functions are unused
counting_pine
Site Admin
Posts: 6323
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Re: Does anyone know what algo XP -> full screen DOS?

Post by counting_pine »

CGAMan wrote:Update: I got the code to work in C#. Just have to use Convert.ToInt32(Math.Floor()) where int() is found. It's really slow, so now I have to learn about parallel processing...
Yeah.. my code isn't much more than "reference implementation", the algorithm can be rewritten in much more efficient ways.
There are lots of calculations done that only need to happen once per row, or per column, or perhaps only once ever.
I think it should be possible to do it all in integer math, with only one multiplication per pixel per colour channel.
CGAMan
Posts: 31
Joined: Nov 24, 2017 18:23

Re: Does anyone know what algo XP -> full screen DOS?

Post by CGAMan »

counting_pine wrote:
CGAMan wrote:Update: I got the code to work in C#. Just have to use Convert.ToInt32(Math.Floor()) where int() is found. It's really slow, so now I have to learn about parallel processing...
Yeah.. my code isn't much more than "reference implementation", the algorithm can be rewritten in much more efficient ways.
There are lots of calculations done that only need to happen once per row, or per column, or perhaps only once ever.
I think it should be possible to do it all in integer math, with only one multiplication per pixel per colour channel.
I thought it was going to be a complicated ordeal to implement parallel processing in C#, since the Bitmap class doesn't do parallel processing. As I was using custom GetPixel(), SetPixel() methods found on the web to test out this algorithm, I discovered that these methods had already been made parallel processing-capable (using LockBits and Marshal.Copy on a bytes array to bypass C#'s inability to do parallel processing on bitmaps). So, it was a simple task of me rewriting for loops to Parallel.For.

I was then able to achieve near real-time upscaling of my very small bitmap!

So, the next question is, seeing how FreeBasic does this single threaded way faster than C#, can FreeBasic do parallel processing on this algorithm as well? (Coz then my image should render way faster in fbc using parallel processing than in C#...)
counting_pine
Site Admin
Posts: 6323
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Re: Does anyone know what algo XP -> full screen DOS?

Post by counting_pine »

Here's a reimplementation of the algorithm that takes most of the hard math out of the inner loop.

Code: Select all

function avg(c1 as long, c2 as long, w as single) as long
    dim as integer r1, g1, b1, r2, g2, b2
    r1 = c1 shr 16 and 255: r2 = c2 shr 16 and 255
    g1 = c1 shr  8 and 255: g2 = c2 shr  8 and 255
    b1 = c1        and 255: b2 = c2        and 255
    
    r1 += ((r2 - r1) * w)
    g1 += ((g2 - g1) * w)': g1 = (g1 + 128) \ 2
    b1 += ((b2 - b1) * w)
    
    return rgb(r1, g1, b1)
end function

sub scale_put(src as any ptr, sw as integer, sh as integer, dw as integer, dh as integer)
    #define srcpoint(x, y) point(scalex(x), scaley(y), src)
    #define dstpoint(x, y) point(x, y)

    dim as integer scalex(0 to dw-1), scaley(0 to dh-1)
    dim as single splitx(0 to dw-1), splity(0 to dh-1)
    dim as long c1, c2

    '' x lookups for scaled coordinates and splits
    for x as integer = 0 to dw-1
        scalex(x) = (x * sw) \ dw
        
        if ((x+1) * sw) \ dw = (x * sw) \ dw then
            splitx(x) = 0
        else
            splitx(x) = frac(((x+1) * sw) / dw) * dw / sw
        end if
    next x

    '' y lookups for scaled coordinates and splits
    for y as integer = 0 to dh-1
        scaley(y) = (y * sh) \ dh
        
        if ((y+1) * sh) \ dh = (y * sh) \ dh then
            splity(y) = 0
        else
            splity(y) = frac(((y+1) * sh) / dh) * dh / sh
        end if
    next y

    'screenlock
    
        for y as integer = dh to 0 step -1 '0 to dh-1
            if splity(y) = 0 then
                for x as integer = 0 to dw-1
                    if splitx(x) = 0 then
                        pset (x, y), srcpoint(x, y)
                    else
                        c1 = srcpoint(x, y)
                        c2 = srcpoint(x+1, y)
                        pset (x, y), avg(c1, c2, splitx(x))
                    end if
                next x
            else
                for x as integer = 0 to dw-1
                    if splitx(x) = 0 then
                        c1 = srcpoint(x, y)
                        c2 = srcpoint(x, y+1)
                        pset (x, y), avg(c1, c2, splity(y))
                    else
                        c1 = avg(srcpoint(x, y  ), srcpoint(x+1, y  ), splitx(x))
                        c2 = avg(srcpoint(x, y+1), srcpoint(x+1, y+1), splitx(x))
                        pset (x, y), avg(c1, c2, splity(y))
                    end if
                next x
            end if
        next y
    
    'screenunlock
end sub

screenres 1024, 768, 32
dim as any ptr img = imagecreate(321, 241)

'bload "test.bmp", img
for i as integer = 0 to 321*241
    pset img, (i mod 321, i \ 321), (i mod 2 = 0)
next i

scale_put(img, 321, 241, 1024, 768)

imagedestroy img
sleep
It's still slow, but I think it's because of the use of pset/point, and working with two different image buffers. (It's faster to read and write from just the screen, and I've coded the y loop to work bottom-up to allow that, to prevent the destination overwriting the source.)

There are also places where pixel values are calculated multiple times for the same colours, where instead it could reuse pixel values previously set (e.g. the value just before it, horizontally or vertically).

One way to parallelise it might be to split the image into horizontal strips and create a thread for each strip. But if you optimise the code to reuse values from the previous row, you'd have to be careful not to do that on the first row of each strip.
CGAMan
Posts: 31
Joined: Nov 24, 2017 18:23

Re: Does anyone know what algo XP -> full screen DOS?

Post by CGAMan »

You guys... The algos found on these pages are the best at emulating that old CRT display look! The ones I've tried were meant to both upscale and downscale bitmaps and excelled at neither.

Anyways, thank you all for your contributions.

What worries me is I see so many projects wanting to preserve the back-ends of legacy languages.

IMO, the legacy front-end should be preserved as well, ie. how the output should have looked like back in 199x...
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Does anyone know what algo XP -> full screen DOS?

Post by paul doe »

I'm not sure if this is what you want:

https://github.com/glasyalabolas/fb-image-resize-demo

Image

The algorithm is very similar to the one by counting_pine, but without the smoothing (adding it is no problem, though). The resize procedure in question is this:
EDIT: I removed the code, as it will get outdated pretty soon.

In my box, I get ~500 FPS in 720p, but I don't know what kind of application would you want to use it for (aside from the need to be real-time). You'll get the best image quality if you keep the original image's aspect ratio, of course.
Last edited by paul doe on Dec 14, 2017 15:00, edited 1 time in total.
counting_pine
Site Admin
Posts: 6323
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Re: Does anyone know what algo XP -> full screen DOS?

Post by counting_pine »

paul doe wrote:I'm not sure if this is what you want:

https://github.com/glasyalabolas/fb-image-resize-demo

The algorithm is very similar to the one by counting_pine, but without the smoothing (adding it is no problem, though).
(Are you glasyalabolas?)

If it's no problem for you to add the smoothing, please do!

I don't much enjoy working with FB image buffers - I tend to feel the need to cover cases like using the screen buffer, or where the source/destination buffer are the same, and it makes things a lot more complicated.

There's more math involved in smoothing, so 500fps is probably too much to hope for, but a few multiplications per (smoothed) pixel shouldn't result in unplayable framerates if done well.
Post Reply