Full Color Tile Driver Thread



  • BaggersBaggers Posts: 2,965
    edited 2010-11-22 - 07:19:56
    @Dr_Acula, et all,

    Here's the latest version, I've modded it, to have transparencies on the sprite, which is now done by RGB $FF00FF in the colour palette in the bmp file. this will come out as $00 in the data, as black is $03, but if you want purple just set it to $FE00FE it'll be rounded down to the correct VGA $CF value.

    I've also modified BmpToLite to now have allow transparencies $ff00ff, I've also allowed it to grab with not allowing repeats ie for fonts.

    bmptolite font -norep -vga4x8

    will grab the font.bmp image ( it's in the zip file ) and convert to chars for VGA you'll also see a print routine inside the VGA_JB_Demo.spin which will print a text string at X, Y position, for how to decode the characters, in case you want to add other characters to the font, but this is using the least amount of chars for the font + numbers, to save space.

    as you can see, although the chars are in a different place compared to the image, they can still be referenced by the display, as they done by an offset from the image charset.

    I've also included the code, it could be a tad difficult to read, as their are MANY MANY options in the converter, as it is for PropGFX Lite.
  • jazzedjazzed Posts: 11,803
    edited 2010-11-22 - 09:04:35
    I just booted this fantastic demo! Thanks Baggers!
  • BaggersBaggers Posts: 2,965
    edited 2010-11-22 - 13:12:38
    You're welcome Jazzed :D
    I hope to see some good things from this and potatohead's driver
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-23 - 05:13:22
    This is really quite an extraordinary demo.

    1) small pixel size
    2) 64 colors
    3) smooth movement of tiles

    I'm thinking not just a graphical OS, but also games!

    I've been playing around with vb.net code editing bitmaps. I think you already have something similar in your conversion program, but I think I need it as part of vb.net so I can automate the creation of operating system screens.

    Just to check - if we are modifying fonts, we need to do some scaling as your ratio is 64/60 and most fonts are going to start as 4:3? If so, I'm assuming it might be better to read in fonts at a big size, do the scaling while they are big, then ? shrink.

    Also - I see you have a line in the code
    chars file "mario.chr"

    Is this reading the file as part of the compilation process and storing it in hub? If so, I need to study your .chr format to work out how to build new screens.

    Amazing work!
    640 x 480 - 36K
  • BaggersBaggers Posts: 2,965
    edited 2010-11-23 - 06:54:13
    The line chars file "mario.chr" is reading the file "mario.chr" in the compilation process, which is the output file of the tiles, from the mario.bmp image that was converted with BMPtoLite, the file "mario.map" is the other output file from the BMPtoLite process, which is the tile layout, which gets written to the screen after multiplying it by 32 ( I might make that a process inside the BMPtoLite, so it is ready to go direct to the screen.
  • dr hydradr hydra Posts: 212
    edited 2010-11-23 - 11:29:58
    Are the small blue squares the sprites?
  • BaggersBaggers Posts: 2,965
    edited 2010-11-23 - 11:55:50
    dr_hydra yes, the small blue squares are sprites.
  • dr hydradr hydra Posts: 212
    edited 2010-11-23 - 13:24:32
    so...VGA...256x240...64 colors...64 sprites...transparencies...and only three cogs...that is amazing!!
  • BaggersBaggers Posts: 2,965
    edited 2010-11-23 - 15:20:16
    Thanks dr_hydra,
    btw, it's 3 render cogs + 1 vga driver cog
    you can do less sprites and 2 render cogs, or 0 sprites and 1 render cog ;D
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-23 - 18:52:58
    Hi Baggers,

    I'm looking at the .map file and trying to work out how it is coded.

    Here is a typical line in hex:
    00 00 00 00 00 00 00 00 30 01 31 01 32 01 33 01 
    34 01 35 01 36 01 37 01 38 01 39 01 3A 01 00 00 

    and I've worked out that this file is 3840 bytes long and the numbers like 00 mean 'white' (presumably tile 00 is white) and that numbers like 31 and 32 mean tile number 31 and 32.

    But 64*30 is 1920, not 3840 and I've noticed that between the tile values is 01.

    So each tile is described with two bytes. What does the 01 mean?

    Addit: Scratch that. I worked it out - 01 is the high byte. Ok, so you have more than 256 tiles. How many can you have in total?
  • Cluso99Cluso99 Posts: 16,395
    edited 2010-11-23 - 19:01:46
    Don't you just hate being off-air for a couple of days! It's almost an impossible task to catch up on what happened!

    This is brilliant :smilewinkgrin: Congratulations guys.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-23 - 21:17:42
    I think I'm getting there decoding the format.

    The .map file is a list of tiles, in order, with one word (two bytes) per tile. 3840 bytes.
    The .chr file is the tile data, with 32 bytes per tile.

    The .chr file can be unlimited in size, but clearly it has to fit into hub ram. Thus with a minimalist driver one can work out in advance how big the file is allowed to be, and generate a warning. Pictures are about as big as mario, unless they have large blocks of color the same.

    Tile data is %RRGGBB11 as described by Baggers above.

    Pictures will need scaling. I need to work out the exact ratio to make sure a square stays a square. I see from above that there was a timing issue that has kept the width to 256 rather than, say 320.

    I have the basics for picture manipulation at the pixel level in vb.net so hopefully can automate producing .map and .chr files from a suitably pre-scaled picture.
  • BaggersBaggers Posts: 2,965
    edited 2010-11-24 - 01:30:13
    Thanks Cluso :)

    Dr_Acula, yeah the map is a word ( 2 byte ) layout of each tile that was converted from the original BMP. and as you say, the .chr file can be any size as long as it fits into HUB-RAM :D
    You can have multiple sets of .chr files also ( if they fit obviously ) just remembering to set the tile WORD in the screen buffer to ((chrnum)<<5)+(@chrbase-@chars) where chrbase is the new .chr file you want to use, and chars is the render cogs initial charbase. the <<5 = *32 as there are 32 bytes per character.
  • Oldbitcollector (Jeff)Oldbitcollector (Jeff) Posts: 8,090
    edited 2010-11-24 - 08:25:39
    This is an amazing thread! Can't wait until the weekend to try some of this out!

  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-24 - 18:19:33
    Created a one click picture creation with the correct ratio. Circles now display as circles.

    1) Create a bitmap 640x480 (or any higher 1.333 ratio resolution)
    2) Save this as the working bitmap
    3) Resample in paintshop pro to 256x240 with the "maintain aspect ratio" unchecked and resave (maybe with '256' in the name as a reminder this is a narrowed picture).
    4) Run the image processing in the Catalina IDE - enter the file name and one click.
    5) Compile the spin program.

    Some things I have learnt along the way:

    *The maths involved in the ratio correction is a bit complex but just resizing fixes the problem.
    *Some blurring does occur on resampling, so some small fonts may need to be created in another way.
    *With the display driver stripped back (no fonts) the max tile filesize is about 21k (I display the file size in the vb.net code so if it is too big, shrink the picture and add more whitespace)
    * Doing the color reduction in vb.net is much quicker than doing it by hand in paintshop.
    * Adding text to pictures may look better if the text is added after scaling.

    The third picture is a screenshot of an example GUI using the Baggers driver. You can see the slight stretching of the text so it is wider and Tweety has a fat head. On the other hand, the text is crisp and clear. The gray needed to be darkened as the cutoff in the code is 192 and the gray values are around 200 for RGB. This picture took 6.2k of tile data so there is plenty more room when the display is buttons/radio boxes/text boxes. The white lines across the pictures are artifacts of taking my camera taking photographs of pictures with white backgrounds.

    The meaty bit of the vb.net code is below:
        Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
            Dim sizex As Integer
            Dim sizey As Integer
            Dim PixelValue As Color
            sizex = 256 - 1 ' baggers screen is 256x240
            sizey = 240 - 1
            Dim PixelArray(sizex, sizey, 3) As Byte ' R,G,B and calculated prop byte so 4 values
            Dim x, y, i As Integer
            Dim Propbyte As Byte
            Dim TileArray(1920 - 1) As String ' Up to 1920 tiles though this is more than hub ram
            Dim TileMap(1920 - 1) As Integer ' each entry is 2 bytes
            Dim OutputArray(30000) As Byte ' use for both tilearray and tilemap, can never be more than hub ram
            Dim TileX, TileY As Integer
            Dim TileString As String
            Dim HexString As String
            Dim t, TileCounter As Integer
            Dim TileMapCounter As Integer
            Dim TileExists As Integer
            Dim lsb, msb As Byte
            Dim Sourcefile As String
            Dim DestinationFile As String
            Sourcefile = TextBox4.Text + TextBox3.Text
            ' this little bit of code replaces new sytem.drawing.bitmap as the latter leaves the file locked eg so paintshop can't save a new file
            Dim img As Image
            Dim fs As New FileStream(Sourcefile, IO.FileMode.Open)
            img = Image.FromStream(fs)
            PictureBox3.Image = img
            'PictureBox3.Image = New System.Drawing.Bitmap(Sourcefile) ' load picture
            Dim MyBitmap As New System.Drawing.Bitmap(PictureBox3.Image) 'image from picture box
            For y = 0 To sizey
                For x = 0 To sizex
                    PixelValue = MyBitmap.GetPixel(x, y) ' get the color
                    PixelArray(x, y, 0) = PixelValue.R ' red byte
                    PixelArray(x, y, 1) = PixelValue.G ' green byte
                    PixelArray(x, y, 2) = PixelValue.B ' blue byte
                    PixelArray(x, y, 3) = 3 ' pre fill the prop byte with %00000011 for Baggers tiles
            ' create the propeller color byte
            For y = 0 To sizey
                For x = 0 To sizex
                    For i = 0 To 2
                        ConvertColor(PixelArray(x, y, i), Propbyte) ' get the value 0-3
                        Select Case i
                            Case 0 : Propbyte = Propbyte * 64 ' shift to red position
                            Case 1 : Propbyte = Propbyte * 16 ' shift to green position
                            Case 2 : Propbyte = Propbyte * 4 ' shift to blue position
                        End Select
                        PixelArray(x, y, 3) = PixelArray(x, y, 3) + Propbyte ' add to existing color
                    Next i
                Next x
            Next y
            'PictureBox3.Image = MyBitmap ' bitmap array to picturebox - need to convert pixelarray back to mybitmap to show the decreased color depth 
            ' now create the tiles and the map using the pixelarray data in dimension 3
            TileCounter = 0
            TileMapCounter = 0
            For TileY = 0 To 29 ' 30 rows of tiles
                For TileX = 0 To 63 ' 64 columns of tiles
                    TileString = ""
                    For y = TileY * 8 To TileY * 8 + 7
                        For x = TileX * 4 To TileX * 4 + 3
                            NumberToHex(PixelArray(x, y, 3), HexString) ' get the hex value
                            TileString = TileString + HexString ' add to the tilestring
                        Next x
                    Next y
                    ' does this tile exist already?
                    TileExists = -1
                    For t = 0 To TileCounter
                        If TileString = TileArray(t) Then TileExists = t ' found a match
                    Next t
                    If TileExists = -1 Then
                        ' no match so add as a new tile
                        TileArray(TileCounter) = TileString ' add to tile array
                        TileMap(TileMapCounter) = TileCounter ' add this to the map
                        TileCounter = TileCounter + 1 ' and add one to tile array counter
                        TileMapCounter = TileMapCounter + 1 ' and increment map counter
                        ' found a match at Tileexists so no need to add this tile
                        ' add this to the map, and increment the map
                        TileMap(TileMapCounter) = TileExists
                        TileMapCounter = TileMapCounter + 1
                    End If
                Next TileX
            Next TileY
            ' now turn these arrays into binary files
            ' the map file is two bytes, lsb first
            t = 0
            For i = 0 To 1920 - 1
                IntegerToBytes(TileMap(i), msb, lsb)
                OutputArray(t) = lsb
                t += 1
                OutputArray(t) = msb
                t += 1
            DestinationFile = TextBox4.Text + TextBox6.Text
            Dim OutputMap As New FileStream(DestinationFile, FileMode.Create, FileAccess.Write)
            OutputMap.Write(OutputArray, 0, 3840)
            ' and now the tiles
            t = 0
            For i = 0 To TileCounter - 1
                For x = 0 To 31 ' number of bytes in each tile
                    OutputArray(t) = Val("&H" + Strings.Mid(TileArray(i), x * 2 + 1, 2))
                    t += 1
            DestinationFile = TextBox4.Text + TextBox5.Text
            Dim OutputTile As New FileStream(DestinationFile, FileMode.Create, FileAccess.Write)
            OutputTile.Write(OutputArray, 0, TileCounter * 32) ' if tilecounter=1 then 32 bytes
            ' display the file size
            Label13.Text = "Tile size (bytes) = " + Strings.Str(TileCounter * 32)
        End Sub
        Private Sub ConvertColor(ByVal Bytevalue As Byte, ByRef Returnvalue As Byte)
            ' takes a number 0 to 255 and changes to the nearest propeller color
            ' prop colors are 00,01,10,11
            ' so 0-63 is 00, 64-127 is 01, 128 to 191 is 10 and 192 to 255 is 11
            ' this is the same as dividing by 64 but do it this way in case later
            ' need to tweak the values
            Select Case Bytevalue
                Case 0 To 63
                    Returnvalue = 0
                Case 64 To 127
                    Returnvalue = 1
                Case 128 To 191
                    Returnvalue = 2
                Case 192 To 255
                    Returnvalue = 3
            End Select
        End Sub
        Sub NumberToHex(ByRef N As Integer, ByRef Hexstring As String)
            ' 0 to 255 and returns 00 to FF
            Hexstring = Strings.Hex(N)
            Hexstring = Trim(Hexstring)
            If N < 16 Then Hexstring = "0" + Hexstring
        End Sub
        Sub IntegerToBytes(ByRef N As Integer, ByRef msb As Byte, ByRef lsb As Byte)
            Dim hexstring As String ' there is a way with mod and integer division as well
            hexstring = Strings.Hex(N)
            hexstring = Trim(hexstring)
            hexstring = "0000" + hexstring
            hexstring = Strings.Right(hexstring, 4)
            msb = Val("&H" + Strings.Left(hexstring, 2))
            lsb = Val("&H" + Strings.Right(hexstring, 2))
        End Sub
        Private Sub TextBox3_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox3.TextChanged
            If Strings.Len(TextBox3.Text) > 4 Then
                TextBox5.Text = Strings.Left(TextBox3.Text, Strings.Len(TextBox3.Text) - 4) + ".chr"
                TextBox6.Text = Strings.Left(TextBox3.Text, Strings.Len(TextBox3.Text) - 4) + ".map"
            End If
        End Sub
    1024 x 768 - 85K
    1600 x 1200 - 166K
    1600 x 1200 - 171K
  • JT CookJT Cook Posts: 485
    edited 2010-11-24 - 22:04:15
    That looks pretty awesome (I too have to wait until the weekend before I can check it out)! I am still a little fuzzy on somethings.
    You say the tiles are 32 bytes, are the tiles 4bpp or are the tiles odd sizes? Also it can do 64 sprites, is that total on screen or per scanline? And lastly what are the size of the sprites?
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-24 - 22:17:43
    I'm not quite up with the formal terminology of sprites and bpp, but the tiles are 32 bytes each - 4 bytes wide and 8 bytes high. Each pixel is a byte RRGGBBxx where I think xx is used for transparancy (I'm setting them to 11 for the moment).

    A screen is 256 x 240 pixels. So that is 64 x 30 tiles.

    Each of those tiles can be moved around the screen from code, and Baggers' demo does that very smoothly.
  • potatoheadpotatohead Posts: 10,071
    edited 2010-11-24 - 22:49:47
    The way Baggers writes renderers is very clever.

    (and I've been working at getting the TV driver to mesh, learning a lot right now)

    Sprites per line depends on the number of render cogs. Fewer cogs = fewer sprites per line, also, where the sprites are can impact sprites per line, so consider sorting your sprite list by horizontal screen position, if things are tight!! Put the last sprites in the list, farther right on screen to get the most out of the render cogs allocated.

    He mentioned multi-plexing to me. I think that means dedicating the sprites based on screen region, sorting the list during blank, like I mentioned.

    The renderer computes the scan line, then can work on the next one, meaning smart sprite placement operates down to the per-sprite level, failing gracefully, with part, or all of a sprite simply missing, with no other impact on the screen.

    Higher clock = more sprites per scan line. Sprite data is 4x8 pixels, same as the tiles, all one byte per pixel.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-25 - 05:09:05
    Pushing things to the limit here but we can get 16 picture icons on a screen. This uses a lot more memory than 'windows style' gray buttons.

    The problem is there is no room left in hub for any spin code.

    The only solution I can think of is Catalina running in external memory. That potentially gives a huge amount of code space, and means you could even have c code to rebuild tiles from bitmaps (or pixel arrays) on the fly within code.

    So I am thinking now of porting the Baggers video driver into Catalina somehow.
    906 x 804 - 99K
  • potatoheadpotatohead Posts: 10,071
    edited 2010-11-25 - 09:42:45
    All that really needs to be done is build the driver, and write it out to a SD card.

    I'm thinking the way to go is just add the SD object to the demo, then write out the COG image that the Prop Tool builds.

    Then, in the other language, do the variable setup, note the start address of the block of variables, fetch it from SD, or include it as a BLOB (binary), and cognew. Launch some render COG's, using a port of the small start loop found in the demo that fires those off.
  • BaggersBaggers Posts: 2,965
    edited 2010-11-25 - 11:23:37
    if you want I'll make SD binaries of each of the drivers ( VGA and RENDER cogs ) that way you can just load them into HUB-RAM with SD driver and then upload them to cogs.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-25 - 13:57:52
    SD binaries would be great. Building sd binaries for catalina is a two step process - the first and hardest step is stripping out all the extra spin code that usually accompanies cog code. But your demo has very little. The second step is adding some standard header code to handle talking to fixed locations in hub upper ram.

    I think that extra code is maybe 15 lines of pasm, so hopefully the cogs are not completely filled with code already?

    I presume there is no way this could ever be done with less than 4 cogs?
  • BaggersBaggers Posts: 2,965
    edited 2010-11-25 - 14:30:55
    My demo has very little spin for the apps, because they were designed with the intent to eventually be stand along binaries :)
    There's also PLENTY of room left in the cogs :D
    if you don't want sprites, it can be done in 2 cogs, VGA driver + 1 Render Cog. at 80Mhz
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-25 - 15:07:46
    Ah, this is all very good news.

    I took a look at the code - now I understand what "unrolled" means.

    Re if you don't want sprites, it can be done in 2 cogs, VGA driver + 1 Render Cog. at 80Mhz

    Could you explain that a bit more. What is a sprite?
  • potatoheadpotatohead Posts: 10,071
    edited 2010-11-25 - 15:22:51
    In this driver, there is "the background" which is essentially stationary 4x8 sprites, which we call tiles.

    Those are indexed by the screen memory array, with each value indexing into the tiles, so that any tile can be placed at any screen location. The locations are fixed, with each tile stacked right against the next one to fill the screen.

    On top of that, if one runs another COG, are movable tiles. They are also 4x8, but instead of being sequentially positioned, are positioned with a sprite list, which contains the tile address index (same method), and the screen position X and Y.

    The draw order is background, then sprites, all of which can share the same tile data.

    Sprites are generally limited by number per line, and that is what can consume extra render cogs.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-25 - 16:25:06
    Ok, that makes sense.

    So maybe one could think about two versions of the code - the 'sprite' version for games, and the 'background' version for a GUI?

    Also - maybe the sprite version could be used for a GUI - eg you draw a gray background (which would only take one tile, plus the 4k for the map), then you draw objects on the background using foreground sprites.

    What would a 'background only' version of the code look like - is it just a matter of leaving out one cog or is it a bit more complicated than that?
  • potatoheadpotatohead Posts: 10,071
    edited 2010-11-25 - 17:27:44
    It's just a matter of calling out two cogs, then not defining any sprites, or maybe just one sprite. (It will do a couple, regardless)

    The renderers just complete the background, then begin processing sprites until time runs out.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-25 - 17:42:53
    Potatohead, do you have a dracblade?

    I just had a crazy idea looking at your avatar - could we load screens quickly enough to make a movie?

    Eg, say we preload tiles and maps from sd card into external ram. Then move them quickly, maybe using a pasm routine, from ram into the video driver cog. Typical files might be 20k total. The ram driver code moves a byte from external to hub ram in about 20 pasm instructions, and can move bytes over 1 million per second. If a video 'frame' takes 20k, we ought to easily be able do 30 frames a second?

    Is your avatar a gif? If so, one could potentially take the frames and turn them into .map and .chr files (I could automate that with the vb.net code). Then it is a matter of how much video you could store. Only a second at 30 frames, but if you went to 5 frames a second it could be a number of seconds. And one could maybe be feeding data off an sd card into ram at the same time??
  • potatoheadpotatohead Posts: 10,071
    edited 2010-11-25 - 18:36:29
    No I don't, but I like where you are going with that.

    The entire GIF is about 30K! It's possible to display on the Propeller :) There are much longer ones out there too. Never thought about it that way before.

    IMHO, a movie could happen by only changing some of the tiles each frame, worst case. Maybe the best case is to kind of "compile" the whole thing, leaving most of the display intact... Probably exactly how video compression happens for real. Take a key frame, then compute deltas, every once in a while accumulating enough for another keyframe, repeat...
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-25 - 20:24:47
    Thinking about movies, I don't know what the timing is like, but if it is possible to move 20k of data for a new frame in between frames that would be easy. But... I don't think it will be fast enough without flicker.

    It could be tricky if the first tile is, say, all white, and the first tile in the map is this white tile, and so is the last tile in the map. You can't do any updating until the last tile is displayed.

    Looking at what you can display with different file sizes, it could be possible to sacrifice a little resolution and store two 10k files (.chr and .map) One is the display one, and one is being updated from external memory or sd card. Switch back and forth between each one.

    Or maybe there is a complex formula one could work out in advance.

    Or maybe go back to Kye's 160x120 driver for movies? I did try moving data one byte at a time from sd card to hub ram but it was far too slow. There may be scope to do a block move that could be faster - I haven't explored that. If not, then external ram may be another solution. Kye's code may be easier than tile code as one could be updating the pixels on the fly.

    Re background only code, when I get home I'll take a look at the code and see how it might work with one less cog.

    At some time I'd like to bring XMM catalina into this and see what could be done with some demo code. An example might be to use some existing C code for Pacman and see if one could build up tiles for the maze walls, corners, the dots and the ghosts.

    Addit: Some experiments with movies using Kye's 160x120 driver:
      pix: "VGA64_PIXEngine.spin"            ' thanks to Kye 160x120
      fat: "SD2.0_FATEngine.spin"            ' thanks to Kye
    PUB FastWallpaper
        fat.openfile(string("Prop160.vga"),"R")      ' 160x120
        fat.readdata(pix.displaypointer,19200) ' read data to display buffer location
        fat.closefile ' close sd file

    No flicker and loading frames every 200ms.

    Not bad really!
Sign In or Register to comment.