Author Topic: FB Graphics #2: Color  (Read 6765 times)

0 Members and 1 Guest are viewing this topic.

Offline rdc

  • Pentium
  • *****
  • Posts: 1495
  • Karma: 140
  • Yes, it is me.
    • View Profile
    • Clark Productions
FB Graphics #2: Color
« on: August 01, 2009 »
Introduction

In this section of tutorials we will be adding color to the screen object. In keeping with the OOP format of the tutorials, we will implement color as an object and integrate that into the screen object. Defining color as an object will allow us to easily manipulate the color and simplifies the process of adding color support to our screen object.

Attached you will find the source code for the color object and the updated screen object along with a demo program. A screenshot of the program in action is also attached for reference. In the next tutorial I will go over the color object code in detail but first we will look at the changes in our screen object.

Interface Changes

Even though we want to minimize interface changes, since we are still in the development process, it is safe to make some changes to the interface. In this version of the screen object, I changed some of the names to make the operation a bit more clear.

  • GetWidth changed to ScreenWidth
  • GetHeight changed to ScreenHeight
  • DrawToBuffer changed to PokeBuffer
  • CopyBuffer changed to Redraw

PokeBuffer may seem an odd name, but it more accurately reflects the action of the procedure, since in essence we are poking a color value into the buffer. If you look at the source code you will see that there are actually two versions of the PokeBuffer routine.

Code: [Select]
Declare Sub PokeBuffer (ByRef x As Integer, ByRef y As Integer, ByRef clr As UInteger) 'Sets the x and y location of buffer to color.
Declare Sub PokeBuffer (ByRef x As Integer, ByRef y As Integer, ByRef clr As clrobj) 'Sets the x and y location of buffer to color.

One version takes a Uinteger value and the other version takes a color object (clrobj). What we have done is to overload the procedure so that we can call the same routine with different parameters. The compiler can determine which procedure to call based on the passed parameters. This is the key to overloading a procedure: the parameters must be different for each overloaded procedure. The color object version is nearly identical to the Uinteger version except that is accesses the color object's ColorValue method to retrieve the value of the color.

Code: [Select]
'Copies color to x, y position in buffer using color object. Provides clipping.
Sub screenobject.PokeBuffer (ByRef x As Integer, ByRef y As Integer, ByRef clr As clrobj)
'Make sure the object was initialized.
If _initok = TRUE Then
If (x >= 0) And (y >= 0) And (x < _width) And (y < _height) Then
_buffer[y * _width + x] = clr.ColorValue
EndIf
End If
End Sub

The ClearBuffer method has also been overloaded so that we can pass a color or color object that can be used to fill the screen.

Code: [Select]
Declare Sub ClearBuffer ()'Clears buffer to black.
Declare Sub ClearBuffer (ByRef clr As UInteger)'Clears buffer to color.
Declare Sub ClearBuffer (ByRef clr As clrobj)'Clears buffer using color object.

The version without the parameters is the same as the previous version, except the built-in Clear method is used instead of the CRT memset function. Although the syntax is slightly different, Clear is just a wrapper for the memset function so it works the same.

Code: [Select]
'Screen object ClearBuffer sub.
'Clears buffer by setting buffer to clr.
Sub screenobject.ClearBuffer ()

'Make sure the object was initialized.
If _initok = TRUE Then
Clear _buffer[0], 0, _width * _height * SizeOf(UInteger)
End If
End Sub

The other two procedures use either a color value or a color object.

Code: [Select]
'Screen object ClearBuffer sub.
'Clears buffer by setting buffer to clr.
Sub screenobject.ClearBuffer (ByRef clr As UInteger)

'Make sure the object was initialized.
If _initok = TRUE Then
For i As Integer = 0 To (_width * _height) - 1
_buffer[i] = clr
Next
End If
End Sub

'Screen object ClearBuffer sub.
'Clears buffer by setting buffer to clr.
Sub screenobject.ClearBuffer (ByRef clr As clrobj)

'Make sure the object was initialized.
If _initok = TRUE Then
For i As Integer = 0 To (_width * _height) - 1
_buffer[i] = clr.ColorValue
Next
End If
End Sub

As you can see the memset function isn't being used for these methods. Memset converts the numeric value that is used to fill the buffer to an unsigned char which limits the values to a maximum of 255, which of course will not work with an RGB color value. We could write some assembly code to to do the filling for us, but we are sticking to plain FreeBasic code in this project, so we will simply take a brute force method and manually fill the buffer with the desired color. Since in most cases the buffer will be cleared to black using the first version, we aren't losing too much performance, and is the main reason why the almost original ClearBuffer was left in the object.

New Interface Elements

In addition to the above changes, some new methods have been added to the screen object.

Code: [Select]
Declare Sub PokeAlpha (ByRef x As Integer, ByRef y As Integer, ByRef clr As UInteger, ByRef Alpha As UByte) 'Sets the x and y location of buffer to color.
Declare Sub PokeAlpha (ByRef x As Integer, ByRef y As Integer, ByRef clr As clrobj) 'Sets the x and y location of buffer to color.
Declare Sub PeekBuffer (ByRef x As Integer, y As Integer, ByRef clr As UInteger)'Returns the color at x, y in buffer.
Declare Sub PeekBuffer (ByRef x As Integer, y As Integer, ByRef clr As clrobj)'Returns the color at x, y in buffer.

The two PokeAlpha procedures take the current value of the buffer at the passed x and y location and performs an AlphaBlend with the passed color value or color object. These methods use a private procedure _AlphaBlend to combine the two colors.

Code: [Select]
'Returns an alpha blended color.
Function screenobject._AlphaBlend(Alpha As UByte, fcolor As UInteger, bcolor As UInteger) As UInteger
    Dim As Integer invalpha, r, g, b
    Dim As Pixel fc, bc
   
    If Alpha > 255 Then Alpha = 255
    If Alpha < 0 Then Alpha = 0   
    invalpha = 255 - Alpha
    fc.value = fcolor
    bc.value = bcolor
    r = ((fc.channel.r * Alpha) + (bc.channel.r * invalpha)) Shr 8
    g = ((fc.channel.g * Alpha) + (bc.channel.g * invalpha)) Shr 8
    b = ((fc.channel.b * Alpha) + (bc.channel.b * invalpha)) Shr 8
   
    Return RGB(r, g, b)   
End Function

This code breaks the color into its different components, or channels, and combines the foreground color (the passed color) with the background color (the color from the buffer). The alpha value must be between 0 and 255, with 0 being totally transparent and 255 being totally opaque. The RGB macro recombines the processed channels into an RGB color and returns the new color value.

The PokeAlpha code is actually quite simple.

Code: [Select]
'Alpha blends passed color with background at x, y.
Sub screenobject.PokeAlpha (ByRef x As Integer, ByRef y As Integer, ByRef clr As UInteger, ByRef Alpha As UByte)
Dim As UInteger bc, cc
Dim al As UByte

'Make sure the object was initialized.
If _initok = TRUE Then
If (x >= 0) And (y >= 0) And (x < _width) And (y < _height) Then
'Get the current buffer value.
bc = _buffer[y * _width + x]
'Get the alpha value.
al = Alpha
'Get the alpha blended color.
cc = _AlphaBlend (al, clr, bc)
_buffer[y * _width + x] = cc
EndIf
End If
End Sub

The first version takes a color value and an alpha value, retrieves the color value from the buffer at the passed x and y location and calls the _AlphaBlend function. The returned value is then placed back in the buffer. As with the PokeBuffer method, clipping is provided to make sure the x and y values are within range of the buffer.

Code: [Select]
'Alpha blends passed color object with background at x, y.
Sub screenobject.PokeAlpha (ByRef x As Integer, ByRef y As Integer, ByRef clr As clrobj)
Dim As UInteger bc, cc
Dim al As UByte

'Make sure the object was initialized.
If _initok = TRUE Then
If (x >= 0) And (y >= 0) And (x < _width) And (y < _height) Then
'Get the current buffer value.
bc = _buffer[y * _width + x]
'Get the alpha value.
al = clr.ChannelAlpha
'Get the alpha blended color.
cc = _AlphaBlend (al, clr.ColorValue, bc)
_buffer[y * _width + x] = cc
EndIf
End If
End Sub

The second version is identical except that a color object is passed to the method. As you will see in the next tutorial, the color object keeps track of the alpha value for a particular color so the alpha value doesn't have to be passed as a parameter.

The last addition to the screen object are the PeekBuffer methods which return a value from the buffer either as a color value or color object.

Code: [Select]
Declare Sub PeekBuffer (ByRef x As Integer, y As Integer, ByRef clr As UInteger)'Returns the color at x, y in buffer.
Declare Sub PeekBuffer (ByRef x As Integer, y As Integer, ByRef clr As clrobj)'Returns the color at x, y in buffer.

You may be wondering why these are subroutines rather than functions. You cannot overload a function that has the same parameter set, even though the return types may be different. The reason for this becomes apparent in the case of the Print statement. Suppose you have an overloaded routine called Add which returns either an Integer or a Double. How does the compiler resolve the following statement?

Code: [Select]
Print “Add = “; Add

Does it call the Integer or Double version? There is really no way to resolve this conflict so it is not possible to overload a function like this. However, we need PeekBuffer to be overloaded, so the way to handle this is to create a subroutine and pass the return value through the parameter set. The return value for PeekBuffer is either clr as Uinteger or clr as clrobj which is why they are passed by reference. The x and y are also passed by reference but their values do not change; it is simple faster to use ByRef then ByVal and this method may be called many times, so any speed gain we can get will help.

The code for the PeekBuffer routines is simply the reverse of the PokeBuffer routines.

Code: [Select]
'Returns the color at x, y in the buffer in clr parameter.
Sub screenobject.PeekBuffer (ByRef x As Integer, y As Integer, ByRef clr As UInteger)
'Make sure the object was initialized.
If _initok = TRUE Then
If (x >= 0) And (y >= 0) And (x < _width) And (y < _height) Then
clr = _buffer[y * _width + x]
EndIf
End If
End Sub

'Returns the color at x, y in the buffer in clr parameter.
Sub screenobject.PeekBuffer (ByRef x As Integer, y As Integer, ByRef clr As clrobj)
'Make sure the object was initialized.
If _initok = TRUE Then
If (x >= 0) And (y >= 0) And (x < _width) And (y < _height) Then
clr.ColorValue = _buffer[y * _width + x]
EndIf
End If
End Sub

Example Program

In order to test the functionality of the screen object, a small demo program is included in the project files.

Code: [Select]
' FB Graphics Series for DBFInteractive: http://www.dbfinteractive.com/
' Richard D. Clark
' Basic Graphics Framework
' GPL 3.0
' This program is free software; you can redistribute it and/or modify it
' but WITHOUT ANY WARRANTY; without even the implied warranty of
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
' =======================================================================================================
' This program is the test bed for a graphic screen object that will be used as a base for the graphics series.
' The series will use only the built-in graphics and the standard FB libraries.
' =======================================================================================================
' Include the screen object.
#Include "fbscrobj.bi"
'Set the screen object namespace.
Using scrobj
' =======================================================================================================
' Main Program Code
' =======================================================================================================
'Init the random number generator.
Randomize Timer
'Create the screen object which will initialize and create the graphics screen.
Dim As screenobject aScreen = screenobject(640, 480)
Dim As clrobj myColor

'Make sure the screen intilizled properly.
If aScreen.GetStatus = FALSE Then
End
EndIf

myColor.ChannelSat = 255
'Iterates through brightness levels.
For x As Integer = 0 To 255
'Set the color using hue.
For y As Integer = 0 To 359
myColor.ChannelHue = y
mycolor.ChannelValue = x
aScreen.PokeBuffer x, y, myColor
Next
Next

myColor.ChannelValue = 255
'Iterates through saturation levels.
For x As Integer = 0 To 255
'Set the color using hue.
For y As Integer = 0 To 359
myColor.ChannelHue = y
mycolor.ChannelSat = 255 - x
aScreen.PokeBuffer x + 255, y, myColor
Next
Next

'Draw some XOR textures.
myColor.ChannelSat = 255
myColor.ChannelValue = 255
For x As Integer = 0 To 63
For y As Integer = 0 To 63
myColor.ChannelHue = x Xor y
aScreen.PokeBuffer x + 1, y + 365, myColor
Next
Next

myColor.ChannelHue = Rnd * 360
myColor.ChannelValue = 255
For x As Integer = 0 To 63
For y As Integer = 0 To 63
myColor.ChannelSat = x Xor y
aScreen.PokeBuffer x + 70, y + 365, myColor
Next
Next

myColor.ChannelHue = Rnd * 360
myColor.ChannelSat = 255
For x As Integer = 0 To 63
For y As Integer = 0 To 63
myColor.ChannelValue = x Xor y
aScreen.PokeBuffer x + 140, y + 365, myColor
Next
Next

'Alpha blend some XOR textures.
myColor.ChannelSat = 255
myColor.ChannelValue = 255
For x As Integer = 0 To 63
For y As Integer = 0 To 63
myColor.ChannelHue = x Xor y
aScreen.PokeBuffer x + 210, y + 365, myColor
Next
Next

myColor.ChannelHue = Rnd * 360
myColor.ChannelSat = 255
For x As Integer = 0 To 63
For y As Integer = 0 To 63
myColor.ChannelValue = x Xor y
aScreen.PokeAlpha x + 250, y + 365, myColor.ColorValue, 128
Next
Next

myColor.ChannelHue = Rnd * 360
myColor.ChannelValue = 255
myColor.ChannelAlpha = 64
For x As Integer = 0 To 63
For y As Integer = 0 To 63
myColor.ChannelSat = x Xor y
aScreen.PokeAlpha x + 290, y + 365, myColor
Next
Next

aScreen.Redraw

Locate 1, 66
Print "Color Test"
Locate 3, 66
Print "HSV Color"
Locate 5, 66
Print "Xor Texture"
Locate 7, 66
Print "Alpha Blending"
 
Sleep

'Clear with color using color value.
myColor = RGBA(255, 0, 0, 255)
aScreen.ClearBuffer myColor.ColorValue
aScreen.Redraw
Sleep

'Clear with color using color object.
myColor = RGBA(0, 0, 255, 255)
aScreen.ClearBuffer myColor.ColorValue
aScreen.Redraw
Sleep

'Clear to black.
aScreen.ClearBuffer
aScreen.Redraw

Sleep

As is familiar by now, a screen object is created and checked to make sure the screen was properly created. A color object is also created with the line:

Code: [Select]
Dim As clrobj myColor

We will go over the color object in depth in the next tutorial, but you can see how a color objects works in the demo code. The color object supports both RGBA and HSV color spaces and you can see the HSV color space in action in the following code section.

Code: [Select]
myColor.ChannelSat = 255
'Iterates through brightness levels.
For x As Integer = 0 To 255
'Set the color using hue.
For y As Integer = 0 To 359
myColor.ChannelHue = y
mycolor.ChannelValue = x
aScreen.PokeBuffer x, y, myColor
Next
Next

myColor.ChannelValue = 255
'Iterates through saturation levels.
For x As Integer = 0 To 255
'Set the color using hue.
For y As Integer = 0 To 359
myColor.ChannelHue = y
mycolor.ChannelSat = 255 - x
aScreen.PokeBuffer x + 255, y, myColor
Next
Next

These routines poke the HSV color space into the buffer, first with a varying brightness level and then with a varying saturation level. The HSV color space is quite useful in programming since it is much easier to create color palettes using HSV than it is using RGB.

Code: [Select]
'Draw some XOR textures.
myColor.ChannelSat = 255
myColor.ChannelValue = 255
For x As Integer = 0 To 63
For y As Integer = 0 To 63
myColor.ChannelHue = x Xor y
aScreen.PokeBuffer x + 1, y + 365, myColor
Next
Next

myColor.ChannelHue = Rnd * 360
myColor.ChannelValue = 255
For x As Integer = 0 To 63
For y As Integer = 0 To 63
myColor.ChannelSat = x Xor y
aScreen.PokeBuffer x + 70, y + 365, myColor
Next
Next

myColor.ChannelHue = Rnd * 360
myColor.ChannelSat = 255
For x As Integer = 0 To 63
For y As Integer = 0 To 63
myColor.ChannelValue = x Xor y
aScreen.PokeBuffer x + 140, y + 365, myColor
Next
Next

'Alpha blend some XOR textures.
myColor.ChannelSat = 255
myColor.ChannelValue = 255
For x As Integer = 0 To 63
For y As Integer = 0 To 63
myColor.ChannelHue = x Xor y
aScreen.PokeBuffer x + 210, y + 365, myColor
Next
Next

myColor.ChannelHue = Rnd * 360
myColor.ChannelSat = 255
For x As Integer = 0 To 63
For y As Integer = 0 To 63
myColor.ChannelValue = x Xor y
aScreen.PokeAlpha x + 250, y + 365, myColor.ColorValue, 128
Next
Next

myColor.ChannelHue = Rnd * 360
myColor.ChannelValue = 255
myColor.ChannelAlpha = 64
For x As Integer = 0 To 63
For y As Integer = 0 To 63
myColor.ChannelSat = x Xor y
aScreen.PokeAlpha x + 290, y + 365, myColor
Next
Next

aScreen.Redraw

The demo then creates some XOR textures, again using the HSV color space, and some alpha-blended XOR textures to demonstrate the PokeAlpha methods. Once all the writing to the buffer is completed, the ReDraw method is called to display the buffer on the screen.

Code: [Select]
'Clear with color using color value.
myColor = RGBA(255, 0, 0, 255)
aScreen.ClearBuffer myColor.ColorValue
aScreen.Redraw
Sleep

'Clear with color using color object.
myColor = RGBA(0, 0, 255, 255)
aScreen.ClearBuffer myColor
aScreen.Redraw
Sleep

'Clear to black.
aScreen.ClearBuffer
aScreen.Redraw

Sleep

To round out the demo, the ClearBuffer is called three times, first with a color value, then with a color object and finally with no parameter which will clear the buffer to Black (0). The screen is updated after each clear so you can see the results.

Summary

Color is an important aspect to programming, and our screen object now has the capability to handle color as an object which as you can see makes working with color quite easy. In the next tutorial we will dig into the color object and you will see how all this comes together.


« Last Edit: September 07, 2009 by rdc »

Offline neriakX

  • Atari ST
  • ***
  • Posts: 117
  • Karma: 29
  • CodeNoob
    • View Profile
Re: FB Graphics #2: Color
« Reply #1 on: October 04, 2011 »
Attached you will find the source code for the color object and the updated screen object along with a demo program. A screenshot of the program in action is also attached for reference.
In case anyone following this great tutorials is looking for the missing source-codes (like me), you can find the attachments in this thread: http://www.dbfinteractive.com/forum/index.php?topic=4364.0

cheers!
dizphunkt
cheers,
neriakX

Challenge Trophies Won:

Offline Shockwave

  • good/evil
  • Founder Member
  • DBF Aficionado
  • ********
  • Posts: 17407
  • Karma: 498
  • evil/good
    • View Profile
    • My Homepage
Re: FB Graphics #2: Color
« Reply #2 on: October 04, 2011 »
Thanks Dizphunkt, K+
Shockwave ^ Codigos
Challenge Trophies Won: