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
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
PixelArray(x, y, 3) = PixelArray(x, y, 3) + Propbyte ' add to existing color
'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
' does this tile exist already?
TileExists = -1
For t = 0 To TileCounter
If TileString = TileArray(t) Then TileExists = t ' found a match
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
' 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)
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
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
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))
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"
pix: "VGA64_PIXEngine.spin" ' thanks to Kye 160x120
fat: "SD2.0_FATEngine.spin" ' thanks to Kye
fat.openfile(string("Prop160.vga"),"R") ' 160x120
fat.readdata(pix.displaypointer,19200) ' read data to display buffer location
fat.closefile ' close sd file
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.
I hope to see some good things from this and potatohead's driver
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.
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.
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
I'm looking at the .map file and trying to work out how it is coded.
Here is a typical line in hex:
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?
This is brilliant :smilewinkgrin: Congratulations guys.
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.
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
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.
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:
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?
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.
(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.
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.
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.
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?
There's also PLENTY of room left in the cogs
if you don't want sprites, it can be done in 2 cogs, VGA driver + 1 Render Cog. at 80Mhz
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?
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.
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?
The renderers just complete the background, then begin processing sprites until time runs out.
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??
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...
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:
No flicker and loading frames every 200ms.
Not bad really!