16 colour form and creating a bitmap from a byte array
16 colour form and creating a bitmap from a byte array
16 colour form and creating a bitmap from a byte array
16 colour form and creating a bitmap from a byte array
16 colour form and creating a bitmap from a byte array
16 colour form and creating a bitmap from a byte array 16 colour form and creating a bitmap from a byte array 16 colour form and creating a bitmap from a byte array 16 colour form and creating a bitmap from a byte array 16 colour form and creating a bitmap from a byte array 16 colour form and creating a bitmap from a byte array 16 colour form and creating a bitmap from a byte array 16 colour form and creating a bitmap from a byte array
16 colour form and creating a bitmap from a byte array 16 colour form and creating a bitmap from a byte array
16 colour form and creating a bitmap from a byte array
Go Back  Xtreme Visual Basic Talk > > > 16 colour form and creating a bitmap from a byte array


Closed Thread
 
Thread Tools Display Modes
  #1  
Old 01-02-2007, 12:31 PM
stokeentertainm's Avatar
stokeentertainm stokeentertainm is offline
Contributor
 
Join Date: Jan 2005
Location: Stoke-on-Trent, UK
Posts: 439
Default 16 colour form and creating a bitmap from a byte array


Hi,

I am trying to create a very small bitmap from a byte array, however I have no idea how to work with a bitmap.

My bitmap is only 12 x 6. I have an ever-changing byte array which is a fixed 12 bytes in length. Only the lower 6 bits of each byte are used, to determine if each pixel should be colour0 or colour1. I have created a blank bitmap easily enough, but how do I change the bitmap to match my byte array?

It may be easier to explain that I want to work on a form with only 16 colours, as it is very simple. Is it possible to create a form and limit its colours to 16, or set the first 16 colours myself? The reason I ask is because I want the colours on the form to change when I alter them. For example, if I draw a circle on the form using Colour0, say it's white, I want to then be able to change Colour0 of the form to whatever I want and the circle will change automatically.

Any help on pointing in the right direction for this would be appreciated.

Thanks,

Matt.
  #2  
Old 01-02-2007, 02:02 PM
OnErr0r's Avatar
OnErr0r16 colour form and creating a bitmap from a byte array OnErr0r is offline
Obsessive OPtimizer

Administrator
* Guru *
 
Join Date: Jun 2002
Location: Debug Window
Posts: 13,774
Default

There are several ways to accomplish this task. One would be to place your bitmap data in an array in the format expected for a windows bitmap. This would be four bits per pixel (16) color and a ColorPalette. Note that bitmap data is aligned on four byte boundries, so you'll need to pad appropriately. You can use the Drawing.Bitmap constructor which takes PixelFormat and Scan0. The downside of this method is the need to lock your byte array in memory with InteropServices.GCHandle.Alloc and AddrOfPinnedObject. Alternatively, use the constructor with Width, Height and PixelFormat and then LockBits with Marshal.Copy and copy your bitmap data to Scan0.
__________________
Quis custodiet ipsos custodues.
  #3  
Old 01-02-2007, 02:15 PM
stokeentertainm's Avatar
stokeentertainm stokeentertainm is offline
Contributor
 
Join Date: Jan 2005
Location: Stoke-on-Trent, UK
Posts: 439
Default

Thanks for your help. I understood some of that but most of it is beyond my understanding as I have never worked with graphics within .NET before. Is there a tutorial available I could work through to help my understanding?

Also, another quick question if I could - how do I create a color if I already have the RGB values (0-255) ? For example, how would I set the background color of my form to RGB 50, 100, 150 ?

Thanks,

Matt.
  #4  
Old 01-02-2007, 04:21 PM
OnErr0r's Avatar
OnErr0r16 colour form and creating a bitmap from a byte array OnErr0r is offline
Obsessive OPtimizer

Administrator
* Guru *
 
Join Date: Jun 2002
Location: Debug Window
Posts: 13,774
Default

If you could specifically mention which parts you don't understand, I might be of more assistance.


Drawing.Color.FromARGB(...) overloads.
__________________
Quis custodiet ipsos custodues.
  #5  
Old 01-03-2007, 11:09 AM
jo0ls16 colour form and creating a bitmap from a byte array jo0ls is offline
Senior Contributor

Forum Leader
* Expert *
 
Join Date: Feb 2005
Location: London
Posts: 1,050
Default

I had a go with a cdg file and got it to display using the info in that link.
To make sure we are on the same wavelength, download this "Mary had a little lamb" cdg file (no audio). (this is your motivation - what happened to the lamb?!).

So, the first thing to do is to read the data out from the file, 24 bytes at a time into a class or a structure. I made a class called packet, with these properties:
  • Command - 1 byte
  • Instruction - 1 byte
  • Parity - 2 bytes
  • Data - 16 bytes
  • ParityP - 4 bytes

As I created the packets, I added them to a generic.List(Of Packet), to store them. You can discard packets if the command does not = 9 as that means there is no CD-G information in the spare bits.

I overrode the ToString method of this class, so that it would dump out the data within the packet. So, with the sample cdg file, the first three packets are:

Code:
Instruction: 28 - DefineTransparentColor
Command: 9
00000000
00000000 ' etc - all 0's ...

Instruction: 30 - LoadColorTableLow
Command: 9
00100011
00001000
00000100
00010001
00111111
00110010
00110011
00100100
00010001
00100100
00100110
00110101
00010110
00000110
00011010
00110111

Instruction: 31 - LoadColorTableHigh
Command: 9
00101011
00010111
00100011
00001000
00111011
00101000
00100110
00011001
00101011
00011010
00101111
00001011
00110111
00011101
00111111
00111111
My class has already blanked off the top 2 bits of the Data bytes.

So, the first one presumably just says that the color at index 0 in the CLUT (color look up table) is the transparent color.
The second and third are fun ones. This is the color table I ended up with for these (I can tell it is right, as the website with the cdg file has an avi of it playing).

Code:
Index:  0 Color:FF80C080
Index:  1 Color:FF101010
Index:  2 Color:FFF0F020
Index:  3 Color:FFC0E040
Index:  4 Color:FF406040
Index:  5 Color:FF90B050
Index:  6 Color:FF508060
Index:  7 Color:FF60B070
Index:  8 Color:FFA0D070
Index:  9 Color:FF80C080
Index: 10 Color:FFE0E080
Index: 11 Color:FF909090
Index: 12 Color:FFA0D0A0
Index: 13 Color:FFB0C0B0
Index: 14 Color:FFD0D0D0
Index: 15 Color:FFF0F0F0
The colors are 32 bits per pixel ARGB - so 1 byte for each of alpha, red, blue and green.
So for example color at Index 1 has alpha - 255, Red - 16, Green - 16, Blue - 16.
I use this color format as it is the easiest one to use later on. I keep the colors as integers.
As this format has 8 bits per pixel, and the file uses a 4 bit per pixel color format, you need to convert between the two - you can just multiply each of r,g and b by 16 (or shift 4 bits left if you are that way inclined).

The first color is encoded with the first two bytes in the Data array attached to the LoadColorTableLow Instruction:
Code:
00100011
00001000

rewritten:
00100011 00001000
--RRRRGG --GGBBBB

So, do some bitshifting, ORing and ANDing to get the bits out, and then convert to an int32 that will represent the color.

R = 1000 = 8	=>  8 * 16 = 128 = &H80
G = 1100 = 12   => 12 * 16 = 192 = &HC0
B = 1000 = 8    =>  8 * 16 = 128 = &H80

We want a non-transparent color, so we set alpha to &HFF

So, the color is &HFF80C080
Should be enough for now...

Last edited by jo0ls; 01-03-2007 at 01:29 PM.
  #6  
Old 01-03-2007, 02:34 PM
stokeentertainm's Avatar
stokeentertainm stokeentertainm is offline
Contributor
 
Join Date: Jan 2005
Location: Stoke-on-Trent, UK
Posts: 439
Default

I've fallen at the first hurdle, sorry

So before you go on to the next step, it would be great if you could help me better understand your example by using my example.

Here, I have created what should be a simple section of code, with tiny lines to help me understand each one better.

Code:
For X = 0 To 14 Step 2
                Dim High As Byte = Data(X)
                Dim Low As Byte = Data(X + 1)
                Dim Red As Integer
                Dim Green As Integer
                Dim Blue As Integer

                Red = High And &H3C
                Green = (High & Low) And &H198
                Blue = Low And &HF

                MsgBox("Low=" & Low & " High=" & High & " Red=" & Red & " Green=" & Green & " Blue=" & Blue)
            Next
As you can see, my packet is stored in a Byte array called Data(). It is always 24 bytes in length, as it should be.

I'm trying to get the same values as you for the very first color (0) for the color table. I know that the first two bytes are correct because your example gives the output 00001000 and 00100011. My message box throws up Low=8 (which is correct) and High=35 (which is correct).

Extracting Red and Blue out of the binary data should both give 8, but in my example Red=32 and Blue=8. Am I completely screwing up the ANDing?

Also, am I going about getting the Green value correctly by combining the High and Low values and ANDing it with &H198 ?

Please help a man in need!

Thanks,

Matt.
  #7  
Old 01-03-2007, 03:54 PM
jo0ls16 colour form and creating a bitmap from a byte array jo0ls is offline
Senior Contributor

Forum Leader
* Expert *
 
Join Date: Feb 2005
Location: London
Posts: 1,050
Default

Try putting:

Option Explicit On
Option Strict On

at the top of your code. It forces you to explicitly cast variables from one type to another, but it highlights dumb mistakes.
So, the dumb mistake:

Green = (High & Low) And &H198

...................^ this is not an AND operator (which wouldn't work anyway)

It is the concatenation operator, so it converts high and low to strings, concatenates them, then converts it back to an integer and then does the AND - so the result is nonsense. That's why the hidden casting can be a problem!

So blue is ok.
Red is nearly ok, you have:

Code:
--RRRR--
00100011   Hi byte
00111100   &H3C
---------   AND 
00100000   result
00RRRR00

but you want: 0000RRRR
The problem is that all the bits in the result are 2 bits too far left. So use the Right bit shift operator >> to shift them two to the right...

(actually, there is no need to do the AND (so long as the top 2 bits are 0), when you shift it two to the right, the two lower bits drop off the end and are discarded. I'll go alter my code...)

Green - you need to build it up in stages.
Code:
------GG   Hi Byte
--gg----   Lo Byte

And you want to create 0000GGgg

So, mask off the bits again with AND to get:
000000GG
00gg0000

Then shift GG left two, and gg right 4 to get:
0000GG00
000000gg
---------  join with an OR
0000GGgg

Last edited by jo0ls; 01-03-2007 at 04:07 PM.
  #8  
Old 01-03-2007, 04:25 PM
stokeentertainm's Avatar
stokeentertainm stokeentertainm is offline
Contributor
 
Join Date: Jan 2005
Location: Stoke-on-Trent, UK
Posts: 439
Default

Thanks! That was a dumb error!

Brilliant, I have it working great now, brilliant.

Looking forward to the next part of your example

Thanks,

Matt.
  #9  
Old 01-04-2007, 02:41 PM
jo0ls16 colour form and creating a bitmap from a byte array jo0ls is offline
Senior Contributor

Forum Leader
* Expert *
 
Join Date: Feb 2005
Location: London
Posts: 1,050
Default

I guess the next part is to work out how you are going to get information into a bitmap. I used Lockbits and Marshal.Copy as OnError mentioned.

I'm using a UserControl to display the image. I have a private instance variable called canvas, this is the bitmap. I draw the bitmap to the surface of the control by overriding OnPaint and drawing it there.This will ensure that the bitmap gets drawn at the correct time - whenever windows decides to send a message to the control asking it to repaint itself. I also double buffered the control. The overall framework looks like this:

Code:
' This is a UserControl: ' Add new component - UserControl Public Class CDGViewer Private canvas As Bitmap Sub New() InitializeComponent() ' The size in the documentation. ' I've not bothered resizing it. canvas = New Bitmap(300, 216) ' We want to speed up drawing by double buffering. ' Setting opaque means we draw the background. Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or _ ControlStyles.Opaque Or _ ControlStyles.OptimizedDoubleBuffer Or _ ControlStyles.UserPaint, True) Me.UpdateStyles() Me.Size = New Size(300, 216) End Sub Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs) ' MyBase.OnPaint(e) - not needed. ' We are provided with a Graphics object in the eventArgs. ' This is the tool used to draw on the surface of this control. ' If we want to trigger this sub manually, for example once ' we have updated the bitmap after parsing an Instruction, we ' can call Me.Refresh(), or Me.Invalidate(rectangle) then Me.Update() ' Draw the bitmap to the surface. DrawImgageUnscaled is the fastest ' way to do it. e.Graphics.DrawImageUnscaled(canvas, New Point(0, 0)) End Sub End Class

So with that framework, we can:

- Read each instruction in turn
- Alter the bitmap as instructed
- Call Me.Refresh, or Me.Invalidate to update the display.

To alter the bitmap I've used Lockbits and Marshal.Copy for speed.
The basic idea is that you Lock the bitmap. This means that the location of the bitmap in memory is not going to change, and you are allowed to write directly to that memory whilst it is locked. This is fast. Once done, you Unlock the bitmap, and you can then just call Me.Refresh.

The alternatives would be to use GDI+ and drawing, getpixel/setpixel, or bitblt.

I finally figured out how to lock just a small portion of a 32bpp bitmap, which is good for this program as we write loads of the 6x12 pixel tiles to the bitmap. Anyway, more later...
  #10  
Old 01-04-2007, 03:16 PM
stokeentertainm's Avatar
stokeentertainm stokeentertainm is offline
Contributor
 
Join Date: Jan 2005
Location: Stoke-on-Trent, UK
Posts: 439
Default

Okay, I think I just need a little help with how to use the code.

I have my class as a seperate file. I assume that your class is built into the code with a form? The reason I ask is because SetStyle is not a member of Me within my class. It works fine if I type it into my main form, but that's not where I want my CDG bitmap to draw itself.

I have attempted to create a form when my class initializes using :-

Code:
Private Display As New Form
However, SetStyle is not a member of Display either.

I think it might be just another dumb error on my part which needs pointing out again.

Thanks,

Matt.
  #11  
Old 01-04-2007, 03:41 PM
jo0ls16 colour form and creating a bitmap from a byte array jo0ls is offline
Senior Contributor

Forum Leader
* Expert *
 
Join Date: Feb 2005
Location: London
Posts: 1,050
Default

My class is seperate, and not part of a form. It is not a plain class, it is a UserControl. Instead of right clicking in the solution explorer and then Add.. New.. Class, I did Add.. New.. UserControl.

I can use it just like any other control, it even shows up in the toolbox.
Here's an example use:

Code:
Public Class Form1 Private WithEvents cdg1 As New CDGControl.CDGViewer Sub New() InitializeComponent() Me.Controls.Add(cdg1) cdg1.LoadCDGFile("c:\1.cdg") End Sub ' Click the control to get it to run the next instruction... Private Sub cdg1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cdg1.Click cdg1.FollowNextInstruction() End Sub End Class

So it makes the code that uses the class really simple. I create one, put it on the form and can call its methods to get it to do things.
  #12  
Old 01-04-2007, 03:54 PM
jo0ls16 colour form and creating a bitmap from a byte array jo0ls is offline
Senior Contributor

Forum Leader
* Expert *
 
Join Date: Feb 2005
Location: London
Posts: 1,050
Default

And here's another example. This is a control that works the same way as the proposed cdgViewer, except it just draws a randomly filled bitmap to it's surface. Sneak peek of lockbits...

Code:
Public Class Form1 Private WithEvents randomBox1 As RandomBox Sub New() ' This call is required by the Windows Form Designer. InitializeComponent() ' Add any initialization after the InitializeComponent() call. randomBox1 = New RandomBox Me.Controls.Add(randomBox1) End Sub Private Sub randomBox1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles randomBox1.Click If randomBox1.IsRunning Then randomBox1.Stop() Else randomBox1.Start() End If End Sub End Class Public Class RandomBox Inherits UserControl ' another way to get a usercontrol. Private rand As Random Private canvas As Bitmap Private WithEvents t As New Timer Private running As Boolean Sub New() rand = New Random canvas = New Bitmap(100, 100) Me.size = canvas.Size Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or _ ControlStyles.Opaque Or _ ControlStyles.OptimizedDoubleBuffer Or _ ControlStyles.UserPaint, True) Me.UpdateStyles() t.Interval = 30 End Sub Private Sub t_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles t.Tick ' Lock the bitmap, get a bitmapdata object: Dim bmd As System.Drawing.Imaging.BitmapData = _ canvas.LockBits(New Rectangle(0, 0, canvas.Width, canvas.Height), _ Imaging.ImageLockMode.WriteOnly, Imaging.PixelFormat.Format32bppRgb) Dim numberOfPixels As Integer = canvas.Width * canvas.Height Dim numberOfBytes = numberOfPixels * 4 ' 4 bytes per pixel ' Array that stores the pixel colors. I'm using bytes ' as it is easy to randomise a byte array. Dim pixels(numberOfBytes - 1) As Byte ' Randomise the array rand.NextBytes(pixels) ' Copy the random pixels to the bitmap System.Runtime.InteropServices.Marshal.Copy(pixels, 0, bmd.Scan0, pixels.Length) ' Unlock the bitmap. canvas.UnlockBits(bmd) ' Trigger OnPaint Me.Refresh() End Sub Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs) ' MyBase.OnPaint(e) e.Graphics.DrawImageUnscaled(canvas, New Point(0, 0)) End Sub Public Sub Start() running = True t.Start() End Sub Public Sub [Stop]() ' stop is a reserved word, the brackets let us use it. running = False t.Stop() End Sub Public ReadOnly Property IsRunning() As Boolean Get Return running End Get End Property End Class
  #13  
Old 01-05-2007, 01:29 AM
jo0ls16 colour form and creating a bitmap from a byte array jo0ls is offline
Senior Contributor

Forum Leader
* Expert *
 
Join Date: Feb 2005
Location: London
Posts: 1,050
Default

Anyway, that's just the way I'm doing it. Instead you could have a plain class, that exposes the bitmap we are drawing to as a property. You could notify the consumer of the class when the bitmap changes by raising an event BitmapUpdated. It's then up to the user of the class to draw the bitmap however they see fit.

Another thought that cropped up is that the maximum 4 bit color component value is 1111 binary = 15. Which when multiplied by 16 is only 240, which means we are not using all of the 8 bit room. I did 256/16 when I should have been doing 255/15. So multiply by 17 and you use it all up. Just like washing powder, it should make the bright colors brighter, and white whiter.

The next two commands that come up are those to draw the border - BorderPreset, and clear the screen - MemoryPreset.

Edit: ok lockbits is not the fastest way to do either of these, use GDI+...

So, in your sub to Clear The screen:

- Get the color from the CLUT. Create a System.Drawing.Color using the FromARGB method.
- Get a Graphics object from the canvas:
Code:
Dim gfx As Graphics = Graphics.FromImage(canvas)
- use the Clear method of the Graphics class.

And in the sub to Draw the border...
The top and bottom margins are 12 pixels high.
The left and right margins are 6 pixels wide.
Define a rectangle for each margin.
And use Graphics.DrawRectangle to fill them in.

Or... Faster still...
Define one rectangle that is the central portion - i.e. the area that is Not the border.
Create a graphics object.
Use Graphics.SetClip Method (Rectangle, CombineMode) to set the clipping region of the Graphics object to Exclude this rectangle. This means if you draw something that would normally appear in this excluded region then it won't be drawn.
Use Graphics.Clear to clear the bitmap. It will only clear the borders!

And remember to call Dispose() on the Graphics object when you are done with it.

Last edited by jo0ls; 01-05-2007 at 02:01 AM.
  #14  
Old 01-05-2007, 02:55 AM
stokeentertainm's Avatar
stokeentertainm stokeentertainm is offline
Contributor
 
Join Date: Jan 2005
Location: Stoke-on-Trent, UK
Posts: 439
Default

Ok, cool, I've got a bit further with it now. I can honestly say I've never used a UserControl before (as you might have guessed), and they are really useful

I thought I would just appear to be stupid for the sake of the forum so here goes ...

Just to recap - this is my code for the screen color, and it seems to work fine. I've put my colors into an array called CLUT.

Code:
Dim MemColor As Byte = (Data(DataStart) And &HF)
            Dim MemRepeat As Byte = (Data(DataStart + 1) And &HF)
            Dim gfx As Graphics = Graphics.FromImage(canvas)
            gfx.Clear(CLUT(MemColor))
            gfx.Dispose()
Is it correct that the first screen color instruction should use a dark purple kind of color?

Also, the code for the border, which also seems to work.

Code:
Dim BorderColor As Byte = (Data(DataStart) And &HF)
            Dim gfx As Graphics = Graphics.FromImage(canvas)
            Dim rect As New Rectangle(6, 12, 288, 192)
            gfx.SetClip(rect)
            gfx.Clear(CLUT(BorderColor))
            gfx.Dispose()
This seems to clear the border to black. Is this the same as yours?

Now it's the bitmap section where I've become stuck. I'm not sure how to get data from my packet (12 bytes) into the pixel array. This is my code so far.

Code:
Dim Color0 As Integer = (Data(DataStart) And &HF)
            Dim Color1 As Integer = (Data(DataStart + 1) And &HF)
            Dim Row As Integer = (Data(DataStart + 2) And &H1F) * 12
            Dim Column As Integer = (Data(DataStart + 3) And &H3F) * 6
            ' Lock the bitmap, get a bitmapdata object:   
            Dim bmd As System.Drawing.Imaging.BitmapData = _
                canvas.LockBits(New Rectangle(0, 0, canvas.Width, canvas.Height), _
                Imaging.ImageLockMode.WriteOnly, Imaging.PixelFormat.Format32bppRgb)
            Dim numberOfPixels As Integer = canvas.Width * canvas.Height
            Dim numberOfBytes = numberOfPixels * 4 ' 4 bytes per pixel   
            ' Array that stores the pixel colors  
            Dim pixels(numberOfBytes - 1) As Byte

            ' Copy the random pixels to the bitmap   
            System.Runtime.InteropServices.Marshal.Copy(pixels, 0, bmd.Scan0, pixels.Length)
            ' Unlock the bitmap.   
            canvas.UnlockBits(bmd)
            ' Trigger OnPaint   
            Me.Refresh()
So, I get both color IDs from the packet (the first two bytes). I then get the row and column (the third and fourth bytes). The remaining data is the bitmap itself isn't it? So, how do I interpret that correctly into the pixels byte array?

Also, there is no mention so far of differentiating between the Normal tile and the XOR tile. The code above is used for both in my class.

Thanks,

Matt.
  #15  
Old 01-05-2007, 10:32 AM
jo0ls16 colour form and creating a bitmap from a byte array jo0ls is offline
Senior Contributor

Forum Leader
* Expert *
 
Join Date: Feb 2005
Location: London
Posts: 1,050
Default

The color's aren't right.
DataStart should be 4 I think - 0 is command, 1 is Instuction, 2&3 are parity. If that is correct then try adding:
Code:
Debug.WriteLine(CLUT(MemColor).ToString)
Border: Color [A=255, R=136, G=204, B=136]
Clear Screen: Color [A=255, R=136, G=204, B=136]

(To view the console output in the IDE, click View->Output, or View->Other Windows->Output.)

So, they are the same, and it is a green.

I'll do normal tiles next, I've got a bug, I think it is with the XOR Tiles...
  #16  
Old 01-05-2007, 12:35 PM
jo0ls16 colour form and creating a bitmap from a byte array jo0ls is offline
Senior Contributor

Forum Leader
* Expert *
 
Join Date: Feb 2005
Location: London
Posts: 1,050
Default

I split the code to draw tiles up into three parts:
DrawTileNormal
DrawTileXOR
DrawPortionOfBitmap

So the DrawNormalTiles job is to decode the 16 bytes of data, and create an array of colors, and a rectangle that defines the location in the bitmap where the tile belongs. It sends the array and rectangle to DrawPortionofBitmap which locks the bitmap and copies the pixel data in.

DrawNormalTile

I've still got the full 24 bytes of the packet coming in, see the attached image for the layout.

The link explains how to get the row, column, color0 and color1. I translated it as
Code:
Dim color0 As Integer = bytes(4) And &HF Dim color1 As Integer = bytes(5) And &HF ' Tile co-ordinates. Dim y As Integer = (bytes(6) And &H1F) * 12 ' convert the row to an x co-ord in the bitmap Dim x As Integer = (bytes(7) And &H3F) * 6 ' convert the col to a y co-ord in the bitmap Dim rec As New Rectangle(x, y, 6, 12) ' the rec we are going to draw to

Now we need an array to store the pixelColors. It will need to fit 6*12 items.
To get the colors we loop through the rows of tile data, and for each byte determine which of the bits from 0 to 5 are set (see diagram!).

I've tried a few ways to do it, here's an example of using the BitArray class
Code:
Dim bytes() As Byte = New Byte() {&HFF, &HC2} Dim ba As New BitArray(bytes) For i As Integer = 0 To 1 Debug.WriteLine(Convert.ToString(bytes(i), 2).PadLeft(8, "0"c)) Next For i As Integer = 0 To ba.Length - 1 Debug.WriteLine(ba(i).ToString) Next
Output:
Code:
11111111
11000010
True, True, True, True, True, True, True, True
False, True, False, False, False, False, True, True
From this you can see that the bitArray class converts the bits in the bytes passed in into booleans. The bytes are in order, it does the first byte first. And the bits are in order, but it is the bit order, so the reverse of the way we write down a binary number.

So from that you can:

Create a variable to track the index of this pixel in the array of colorValues.
  • Create a bit array from the full packet.
  • Loop Down the Tile Rows (see diagram)
  • Loop From Bit 5 to Bit 0 Step -1
  • Calculate the location of this bit in the bit Array
  • If bitarray(location) is true then set pixelColors(index) to color1, otherwise color2.
  • Increment index
  • Next bit loop
  • Next tile row loop

To calculate location - it is the same as usual:
number of bits in the rows above + number of bits in the current row.

So if you have:
Code:
For rowIndex As Integer = 8 To 19 For bit As Integer = 5 To 0 Step -1 ' bitarray -> color stuff Next Next

Then the location will be (rowIndex * 8) + bit


Then we copy it to the rectangle...
Attached Images
File Type: gif tileBits.gif (9.5 KB, 21 views)
  #17  
Old 01-05-2007, 01:38 PM
jo0ls16 colour form and creating a bitmap from a byte array jo0ls is offline
Senior Contributor

Forum Leader
* Expert *
 
Join Date: Feb 2005
Location: London
Posts: 1,050
Default

I've still got problems with it. See attached image. There's a freeware player called KaraFun. I appear to have the correct colors, so there is some other problem. I'll check the colors by scanning the output from karaFun and seeing what its color table is.

DrawPortionOfBitmap to follow...
Attached Images
File Type: gif wonky.gif (44.6 KB, 21 views)
  #18  
Old 01-05-2007, 04:27 PM
jo0ls16 colour form and creating a bitmap from a byte array jo0ls is offline
Senior Contributor

Forum Leader
* Expert *
 
Join Date: Feb 2005
Location: London
Posts: 1,050
Default

I've had difficulty getting this to work before - I lock a rectangle, but it behaves as if it has locked the entire image. The reason is that if the bitmap is a 32bppARGB bitmap, then you need to specify 32bppRGB for Lockbits when you lock a smaller portion. And vice versa: if the bitmap is 32bppRGB then you need to specify 32bppARGB for lockbits.
See the attached program which demonstrates this bug.

So, here's a sub that copies the pixels into the specified rectangle of the canvas bitmap (which is 32bppArgb)...

Code:
Private Sub UpdateBitmapPortion(ByVal rec As Rectangle, ByVal pixels() As Int32) Dim bmd As BitmapData = canvas.LockBits(rec, ImageLockMode.WriteOnly, Format32bppRgb) Marshal.Copy(pixels, 0, bmd.Scan0, pixels.Length) canvas.UnlockBits(bmd) ' Ask windows to repaint the control, but only the rec that changed. ' This is to speed things up, you could do me.refresh. Me.Invalidate(rec) Me.Update() End Sub
Attached Files
File Type: zip LockBitsBug.zip (12.0 KB, 6 views)
  #19  
Old 01-06-2007, 11:09 AM
stokeentertainm's Avatar
stokeentertainm stokeentertainm is offline
Contributor
 
Join Date: Jan 2005
Location: Stoke-on-Trent, UK
Posts: 439
Default

Ok, I found the color problem that I was having. It actually matches the color table that you originally listed now, which is good.

I'm still having problems getting the bitmap data into a seperate array. My first problem is that I don't know where the BitmapData object is located. How can I access it?

I suppose my other problems can be put on hold until I know how to use the BitmapData object

Also for information, you can download a tiny free plugin for WinAmp which plays CDG files by clicking here.

Thanks,

Matt.
  #20  
Old 01-08-2007, 12:48 AM
jo0ls16 colour form and creating a bitmap from a byte array jo0ls is offline
Senior Contributor

Forum Leader
* Expert *
 
Join Date: Feb 2005
Location: London
Posts: 1,050
Default

Well, the colors match ok (except the winamp plugin does *16 instead of *17), so yours will match the winamp plugin.

Mine looks freaky when it needs to xor the same tile a few times. I thought it might be something to do with transparency, by that plugin has ignored transparency.
So, I'm stumped for now. I'll probably figure it out eventually.

Are you missing the imports statements?
Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices

BitmapData is actually System.Drawing.Imaging.BitmapData

http://www.bobpowell.net/lockingbits.htm

If you want to just get things working, then you could use the much much slower bitmap.getpixel and bitmap.setpixel methods for now.

The 32bpp bitmap is a very simple thing. There is one byte for alpha, red, blue, green for each pixel. The data is stored in memory sequentially, it is actually stored like this:
Code:
BGRABGRABGRA.....
^^^^
This is the first pixel
The first pixel stored is the top left pixel in the image. It then stores the next pixel in that row of the image, then all the rest in that row, then moves down to the next row.
So it is just a long sequence of 4 bytes, each 4 bytes representing 1 pixel.

So, to GetPixels:

What we do is Lock the memory. The bitmapdata object is an object that is used to access the bitmap whilst it is locked. We are only interested in the bitmapdata object's scan0 property, which is the start address in memory of the pixel data. We need this address for the System.Runtime.InteropServices.Marshal.Copy command, which can copy from locked memory into an array - it needs to know the memory address.

We then just copy all the data from memory into an array.
We use an array of int32s (which is also 4 bytes in size), each int32 contains one pixel, and the order is the same for the bitmap. The bytes in the Int32 now look like ARGB though, not BGRA (but if you read out into a byte array, you do get B,G,R,A...)

We can then unlock the bitmap, so that it can be used as normal.

Writing data to the bitmap is much the same.

Things get a bit trickier with other pixelformat images (where 1 pixel is not stored using 4 bytes ARGB, but using some other arrangement).
Closed Thread


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off

Forum Jump

Advertisement:





Free Publications
The ASP.NET 2.0 Anthology
101 Essential Tips, Tricks & Hacks - Free 156 Page Preview. Learn the most practical features and best approaches for ASP.NET.
subscribe
Programmers Heaven C# School Book -Free 338 Page eBook
The Programmers Heaven C# School book covers the .NET framework and the C# language.
subscribe
Build Your Own ASP.NET 3.5 Web Site Using C# & VB, 3rd Edition - Free 219 Page Preview!
This comprehensive step-by-step guide will help get your database-driven ASP.NET web site up and running in no time..
subscribe
16 colour form and creating a bitmap from a byte array
16 colour form and creating a bitmap from a byte array
16 colour form and creating a bitmap from a byte array 16 colour form and creating a bitmap from a byte array
16 colour form and creating a bitmap from a byte array
16 colour form and creating a bitmap from a byte array
16 colour form and creating a bitmap from a byte array 16 colour form and creating a bitmap from a byte array 16 colour form and creating a bitmap from a byte array 16 colour form and creating a bitmap from a byte array 16 colour form and creating a bitmap from a byte array 16 colour form and creating a bitmap from a byte array 16 colour form and creating a bitmap from a byte array
16 colour form and creating a bitmap from a byte array
16 colour form and creating a bitmap from a byte array
 
16 colour form and creating a bitmap from a byte array
16 colour form and creating a bitmap from a byte array
 
-->