Code archives/Miscellaneous/HSB Color Picker

This code has been declared by its author to be Public Domain code.

Download source code

HSB Color Picker by tesuji2011
Here's a simple Max2D color picker avoiding the use of the native OS version. Nothing too fancy, just a basic hue,saturation, brightness grid. + A fascinating article all about color : http://en.wikipedia.org/wiki/HSL_and_HSV
SuperStrict

' Tesuji 2011
' A simple color picker

Type TColorPicker

	Field hue:Float ' 0..360
	Field saturation:Float = 1.0 ' 0..1.0
	Field brightness:Float = 1.0 ' 0..1.0	
	Field red:Int,green:Int,blue:Int ' 0..255
	Field buttonClicked:Int = False ' true if ok button has just been clicked
	
	Field xPos:Int,yPos:Int

	Field gradientImage:TImage
	Field hueImage:TImage
	Field sbZone:TZone = New TZone.Create(1,1,256,256, 0)
	Field hZone:TZone = New TZone.Create(1+256+24,1,24,256, 1)
	Field dragZone:TZone
	Field buttonZone:TZone = New TZone.Create(1+256+24+24+24,1+208,48,48)

	Method Create:TColorPicker()
		gradientImage = drawGradientImage()
		hueImage = drawHueImage()
		Return Self
	End Method

	Function drawGradientImage:TImage()
		Local image:TImage = CreateImage(256,1)
		Local pixmap:TPixmap = LockImage(image)
		Local alpha:Int
		For Local i:Int = 0 To 255
			alpha = i Shl 24
			WritePixel(pixmap,i,0,$00ffffff | alpha)
		Next
		UnlockImage(image)
		Return image
	End Function

	Function drawHueImage:TImage()
		Local image:TImage = CreateImage(256,1)
		Local pixmap:TPixmap = LockImage(image)
		Local r:Float,g:Float,b:Float
		Local hi:Float = 360.0/256.0
		Local h:Float = 0
		Local red:Int,green:Int,blue:Int
		For Local i:Int = 0 To 255
			HSBtoRGB(h,1,1,r,g,b)
			red   = r*255
			green = g*255
			blue  = b*255
			WritePixel(pixmap,i,0,$FF000000 | (red Shl 16 | green Shl 8 | blue))
			h :+ hi
		Next
		UnlockImage(image)
		Return image
	End Function

	Method update(x:Int,y:Int)
		xPos = x
		yPos = y
		Local r:Float,g:Float,b:Float
		HSBtoRGB(hue,saturation,brightness,r,g,b)
		red = r*255
		green = g * 255
		blue = b * 255
		
		sbZone.update(MouseX()-x,MouseY()-y)
		hZone.update(MouseX()-x,MouseY()-y)
		buttonZone.update(MouseX()-x,MouseY()-y)
		buttonClicked = False
		
		If MouseDown(1)
			If sbZone.over Or (dragZone <> Null And dragZone.index = sbZone.index)
				dragZone = sbZone
				saturation = clamp((MouseX()-x-sbZone.x)/255.0,0.0,1.0)
				brightness = clamp(1.0-((MouseY()-y-sbZone.y)/255.0),0.0,1.0)
			End If
			
			If hZone.over Or (dragZone <> Null And dragZone.index = hZone.index)
				dragZone = hZone
				hue = clamp(((MouseY()-y-hZone.y)/255.0)*359,0,359)
			End If
			
		Else
			dragZone = Null
		End If

		If (MouseHit(1) And buttonZone.over) Or KeyHit(KEY_ENTER)
			buttonClicked = True
		End If


	End Method
	
	Function clamp:Float(value:Float,minimum:Float,maximum:Float)
		If value > maximum Then value = maximum
		If value < minimum Then value = minimum
		Return value
	End Function	
		
	Method render()
		renderBackground()
		renderColorBox(xPos,yPos)
		renderHueBox(xPos+1+256+24,yPos)
		renderSelectedColor(xPos+1+256+24+24+24,yPos)
		renderValues(xPos+1+256+24+24+24,yPos+1+48+16+8)
		renderButton()
		renderSBMarker()
		renderHMarker()
	End Method

	Method renderBackground()
		SetAlpha .75
		SetBlend ALPHABLEND
		SetColor 188,188,188
		DrawRect xPos-8,yPos-8, 256+96+48,256+16
	    SetBlend LIGHTBLEND
		SetColor 128,128,128
		DrawLine xPos-8,yPos-8, xPos-8+256+96+46, yPos-8
		DrawLine xPos-8,yPos-8, xPos-8, yPos-8+256+14
		SetBlend SHADEBLEND 
		DrawLine xPos-8,yPos-8+256+15, xPos-8+256+96+47, yPos-8+256+15
		DrawLine xPos-8+256+96+47,yPos-8, xPos-8+256+96+47, yPos-8+256+15
	End Method
	
	Method renderHMarker()
	
		Local yo:Int = (hue/360.0)*hZone.h
		Local x:Float = xPos+hZone.x+1
		Local y:Float = yPos+hZone.y+yo
		
		SetColor 0,0,0
		SetAlpha .5		
	    DrawPoly  ([x-1,y, x-8,y-7, x-8,y+7])
	    x = xPos+hZone.x+hZone.w+2
	    DrawPoly  ([x+1,y, x+7,y-7, x+7,y+7])
	
	End Method
	
	Method renderSBMarker()
	
		Local x:Int = xPos+sbZone.x+(saturation*sbZone.w)
		Local y:Int = yPos+sbZone.y+((1.0-brightness)*sbZone.h)
		SetAlpha 1.0
		Local xpoints:Int[] = [-1,1,1,-1,-2,2,0,0]
		Local ypoints:Int[] = [-1,1,-1,1,0,0,-2,2]
		SetColor 0,0,0		
		For Local i:Int = 0 To xpoints.length-1
			Plot x+xpoints[i],y+ypoints[i]
		Next
		xpoints = [-2,2,2,-2,-3,-3,3,3,-1,1,1,-1]
		ypoints = [-2,2,-2,2,-1,1,1,-1,-3,-3,3,3]
		SetColor 255,255,255
		For Local i:Int = 0 To xpoints.length-1
			Plot x+xpoints[i],y+ypoints[i]
		Next		
	
	End Method
	
	Method renderColorBox(x:Int,y:Int)
	    SetBlend SOLIDBLEND
		SetColor 0,0,0
		DrawRect x,y,256+2,256+2
		SetAlpha 1.0

		SetBlend ALPHABLEND
		SetColor 255,255,255
		DrawImageRect gradientImage,x+1+256,y+1,-256,256
		
		Local r:Float,g:Float,b:Float
		HSBtoRGB(hue,1,1,r,g,b)
		SetColor r*255,g*255,b*255
		SetBlend ALPHABLEND
		DrawImageRect gradientImage,x+1,y+1,256,256
		
		SetColor 0,0,0
		SetRotation 270
		SetAlpha 1.0
		DrawImageRect gradientImage,x+1+256,y+1,-256,-256
		
		SetRotation 0

	End Method
	
	Method renderHueBox(x:Int,y:Int)		
		SetBlend SOLIDBLEND
		SetColor 0,0,0
		DrawRect x,y,24+2,256+2		
		SetRotation 90
		SetColor 255,255,255
		DrawImageRect hueImage, x+1+24,y+1,256,24
		SetRotation 0
	End Method
	
	Method renderSelectedColor(x:Int,y:Int,w:Int=48,h:Int=48)
		SetBlend SOLIDBLEND
		SetColor 0,0,0
		DrawRect x,y,w+2,h+2
		SetColor red,green,blue
		DrawRect x+1,y+1,w,h			
	End Method
	
	Method renderValues(x:Int,y:Int)
		SetBlend ALPHABLEND
		Local height:Int = 20
		SetColor 0,0,0
		SetAlpha .75
		DrawText "H",x,y
		DrawText "S",x,y+height
		DrawText "B",x,y+height*2
		DrawText "R",x,y+height*3
		DrawText "G",x,y+height*4
		DrawText "B",x,y+height*5
		
		renderValue(x+16,y,          49-16, height-4, hue)
		renderValue(x+16,y+height,   49-16, height-4, saturation*100)
		renderValue(x+16,y+height*2, 49-16, height-4, brightness*100)
		renderValue(x+16,y+height*3, 49-16, height-4, red, 255,0,0)
		renderValue(x+16,y+height*4, 49-16, height-4, green, 0,255,0)
		renderValue(x+16,y+height*5, 49-16, height-4, blue, 0,0,255)

	End Method
	
	Method renderButton()
		SetBlend ALPHABLEND
		SetColor 0,0,0
		DrawRect xPos+buttonZone.x-1,yPos+buttonZone.y-1,buttonZone.w+2,buttonZone.h+2
		SetColor 255,255,255
		SetAlpha 0.5
		SetRotation 90
		DrawImageRect gradientImage,xPos+buttonZone.x+buttonZone.w,yPos+buttonZone.y,buttonZone.w,buttonZone.h

		SetRotation 0
		SetAlpha .5
		SetColor 255,255,255
		DrawText "OK", xPos+buttonZone.x+16,yPos+buttonZone.y+buttonZone.h-32
	End Method
	
	Method renderValue(x:Int,y:Int, w:Int,h:Int, value:Int, r:Int=255,g:Int=255,b:Int=255)
		SetBlend LIGHTBLEND
		SetColor r,g,b
		SetAlpha .15
		DrawRect x,y-2,w,h
		SetAlpha .5
		DrawImageRect gradientImage,x+1+(w-2),y+1-2,-(w-2),h-2
		SetBlend ALPHABLEND
		SetAlpha .5
		SetColor 0,0,0
		DrawText value, x+2,y
	End Method
	
	Method setFromRGB(r:Int,g:Int,b:Int)
		
		Local h:Float,s:Float,v:Float
		RGBtoHSB(r,g,b,h,s,v)
		hue = h
		saturation = s
		brightness = v/255.0		
		update(xPos,yPos)		
		
	End Method
	
	Method RGBtoHSB(r:Float, g:Float, b:Float, h:Float Var, s:Float Var, v:Float Var )
		Local mn!, mx!, dif!, ad!, dv!, md!
		If ( r < g And r < b )
			mn = r
		Else If ( g < b )
			mn = g
		Else
			mn = b
		EndIf

		If ( r > g And r > b )
			mx = r
			dif = g-b
			ad = 0!
		Else If ( g > b )
			dif = b-r
			mx = g
			ad = 120!
		Else
			dif = r-g
			ad = 240!
			mx = b
		EndIf
		
		md = mx-mn

		h = (60!*(dif / md))+ad
		s = md/mx
		v = mx
	End Method

	
	Function HSBtoRGB(h:Float, s:Float, v:Float, r:Float Var, g:Float Var, b:Float Var) 
 
	    Local i:Int 
	    Local f:Float, p:Float, q:Float, t:Float,hTemp:Float 
	  
	    If s = 0.0 Or h = -1.0   ' s==0? Totally unsaturated = grey so R,G And B all equal value 
	      	b = v
		 	g = v
		 	r = v 
	    	Return 
	    EndIf
	 
	    hTemp = h/60.0 
	    i = Floor(hTemp)                ' which sector 
	    f = hTemp - i                   ' how far through sector 
	    p = v * ( 1 - s ) 
	    q = v * ( 1 - s * f ) 
	    t = v * ( 1 - s * ( 1 - f ) ) 
	  
	    Select i  
	    	Case 0 r = v ; g = t ; b = p 
	    	Case 1 r = q ; g = v ; b = p 
	    	Case 2 r = p ; g = v ; b = t 
	    	Case 3 r = p ; g = q ; b = v
	    	Case 4 r = t ; g = p ; b = v 
	    	Case 5 r = v ; g = p ;b = q 
	    End Select 
	End Function 

End Type

Type TZone

	Field x:Int,y:Int,w:Int,h:Int
	Field index:Int = -1
	Field over:Int = False

	Function Create:TZone(x:Int=0,y:Int=0,w:Int=0,h:Int=0,index:Int=-1)
		Local tz:TZone = New TZone
		tz.x = x
		tz.y = y
		tz.w = w
		tz.h = h
		tz.index = index
		Return tz
	End Function
	
	Method update(mx:Int, my:Int)
		isOver(mx,my)
	End Method
	
	Method isOver:Int(mx:Int, my:Int)
		over = (mx >= x And mx < x+w And my >= y And my < y+h)
		Return over
	End Method

End Type

' -----------------------------------------------------------------
' Example usage
' -----------------------------------------------------------------
'Rem

Graphics 800,600

Local picker:TColorPicker = New TColorPicker.Create()
Local x:Int=200,y:Int=200
Local r:Int = 55, g:Int = 66, b:Int = 89
picker.setFromRGB(r,g,b)

While Not KeyHit(KEY_ESCAPE)
	Cls
	SetBlend SOLIDBLEND

	SetColor r,g,b
	DrawRect 0,0,GraphicsWidth(),GraphicsHeight()
	picker.update(x,y)
	picker.render()
	x :+ (KeyDown(KEY_RIGHT)-KeyDown(KEY_LEFT))
	y :+ (KeyDown(KEY_DOWN)-KeyDown(KEY_UP))
	If picker.buttonClicked 
		r = picker.red
		g = picker.green
		b = picker.blue
	End If
	SetBlend ALPHABLEND
	SetAlpha 1.0
	SetColor r~255,g~255,b~255
	DrawText "Hue Saturation Brightness RGB Colour Picker Example",0,0
	
	Flip
Wend

End

'End Rem

Comments

Stef662012
Hi Tesuji

Great color picker !!!
Question: How do I set the initial color ? It looks to me that I need to set the HSB fields, right ? By settings the RGB fields doesn't help, as the startup uses the HSB color space ...
Do you maybe have a RGBtoHSB function ?

grtz
stef


tesuji2012
Hi Stef

Thanks for trying it out.
I've added a setFromRGB method which uses a RGBtoHSB function.
Did this sometime ago as I also needed it but forgot to update this post.

Hope that helps.


Code Archives Forum