Code archives/Miscellaneous/Photoshop Like ColorPicker
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
Originally coded for a module to extend maxgui, this color picker is made to be easy to use, and can be used as an include. You only need to call PickColor (with optional initial color argument) to launch a window in the style of photoshop color picker. The color being modified is updated in the loop while the window is still on top. It allows to modify the color of something without the need to hit "Ok" and try again with another color. Also, if you hit "Cancel" or close the window, it will generate a last EVENT_COLOR event with the initial color as EventData(), so you can reset to the initial color the stuff that has been modified. The module has not been tested in depth, so post comment if you find any kind of bug. The Photoshop-like ColorPicker can convert RGB to HSL and back. So, it's pretty accurate for almost any case. | |||||
'Module mdt.uisdk Import maxgui.drivers Global EVENT_COLOR:Int = AllocHookId ( ); Type TColorPicker Extends TProxygadget Const _cursorS:Int = 12; Field _sourceEvent:Object; Field _initColor:Int; Field _color:Int; Field _rgb:Int[]; Field _hsl:Int[]; Field style:Int; Field _win:TGadget; Field _core:TGadget; Field _pix_sl:TPixmap; Field _pix_hue:TPixmap; Field _col_sl:TGadget; Field _col_hue:TGadget; Field _col_col:TGadget; Field _col_cH:TGadget; Field _col_tH:TGadget; Field _col_cS:TGadget; Field _col_tS:TGadget; Field _col_cL:TGadget; Field _col_tL:TGadget; Field _col_cR:TGadget; Field _col_tR:TGadget; Field _col_cG:TGadget; Field _col_tG:TGadget; Field _col_cB:TGadget; Field _col_tB:TGadget; Field _col_tHex:TGadget; Field _cursorHue:TGadget; Field _ok:TGadget; Field _cc:TGadget; Field _hueHit:Byte; Field _slHit:Byte; Function RGB2HSL:Int[](rgb:Int[]) Local i0:Int = 0, i1:Int = 1, i2:Int = 2, t:Int = 0; If rgb[i1]>rgb[i0] Then t = i0; i0 = i1; i1=t; If rgb[i2]>rgb[i0] Then t = i0; i0 = i2; i2=t; If rgb[i2]>rgb[i1] Then t = i1; i1 = i2; i2=t; i1 = (i0+1)Mod(3); i2 = (i1+1)Mod(3); Local c:Int = rgb[i0] - rgb[i2]; If c=0 Then Return [0,0, (100*rgb[i0])/255]; Return [(60*(rgb[i1]-rgb[i2]))/c+i0*120, (100*c)/rgb[i0], (100*rgb[i0])/255]; End Function Function HSL2RGB :Int [](hsl :Int []) Local h:Float = Float(hsl[0])/60.0; Local s:Float = Float(hsl[1])/100.0; Local l:Float = Float(hsl[2])/100.0; Local r:Int, g:Int, b:Int, t:Float = h Mod(1.0); If h<1 r = 255; g = 255*t; b = 0; ElseIf h<2 r = 255-255*t; g = 255; b = 0; ElseIf h<3 r = 0; g = 255; b = 255*t; ElseIf h<4 r = 0; g = 255-255*t; b = 255; ElseIf h<5 r = 255*t; g = 0; b = 255; Else r = 255 g = 0 b = 255-255*t; End If Return [Int ( (255+(r-255)*s)*l), Int ( (255+(g-255)*s)*l),Int ( (255+(b-255)*s)*l) ]; End Function Function rgbFromRGB:Int[](rgb:Int) Return [rgb Shr(16) & $FF, rgb Shr(8) & $FF, rgb & $FF]; End Function Function getInstance:TColorPicker ( initRGBColor:Int=$FFFFFF, source:Object = Null, Style:Int = 0 ) Local cp:TColorPicker = New TColorPicker; cp._sourceEvent = source; cp._initColor = initRGBColor; cp._rgb = rgbFromRGB(initRGBColor); cp._hsl = rgb2hsl(cp._rgb); cp.style = style; cp._win = CreateWindow ( "Color Picker" .. , 0,0.. , 384, 278.. , Null.. , WINDOW_TITLEBAR|WINDOW_CLIENTCOORDS|.. WINDOW_CENTER|WINDOW_HIDDEN.. ); cp._pix_sl = CreatePixmap ( 256, 256, PF_RGB888 );ClearPixels(cp._pix_sl, initRGBColor ); cp._pix_hue = CreatePixmap ( 1, 360, PF_RGB888 );ClearPixels(cp._pix_hue, initRGBColor ); cp._core = CreatePanel ( 0,0, ClientWidth(cp._win), ClientHeight(cp._win), cp._win); cp._col_hue = CreatePanel ( 276,010, 020, 256, cp._core, PANEL_ACTIVE ); cp._col_sl = CreatePanel ( 010,010, 256, 256, cp._core, PANEL_ACTIVE ); cp._col_col = CreatePanel ( 306,010, 065, 035, cp._core, PANEL_SUNKEN | PANEL_ACTIVE ); cp._col_cH = CreateLabel ( "H", 306, 055, 030, 020, cp._core ); cp._col_tH = CreateTextField ( 339, 055, 032, 020, cp._core ); SetButtonState ( cp._col_cH, True ); cp._col_cS = CreateLabel ( "S", 306, 077, 030, 020, cp._core ); cp._col_tS = CreateTextField ( 339, 077, 032, 020, cp._core ); cp._col_cL = CreateLabel ( "L", 306, 099, 030, 020, cp._core ); cp._col_tL = CreateTextField ( 339, 099, 032, 020, cp._core ); cp._col_cR = CreateLabel ( "R", 306, 124, 030, 020, cp._core ); cp._col_tR = CreateTextField ( 339, 124, 032, 020, cp._core ); cp._col_cG = CreateLabel ( "G", 306, 146, 030, 020, cp._core ); cp._col_tG = CreateTextField ( 339, 146, 032, 020, cp._core ); cp._col_cB = CreateLabel ( "B", 306, 168, 030, 020, cp._core ); cp._col_tB = CreateTextField ( 339, 168, 032, 020, cp._core ); CreateLabel ( "#", 306, 190, 015, 020, cp._core ); cp._col_tHex = CreateTextField ( 321, 190, 050, 020, cp._core ); cp._ok = CreateButton ( "Ok", 306, 214, 065, 020, cp._core, BUTTON_OK ); cp._cc = CreateButton ( "Cancel", 306, 236, 065, 020, cp._core, BUTTON_CANCEL ); cp._cursorHue = CreatePanel ( 271,010, 030,001, cp._core ); SetPanelColor ( cp._cursorHue, 1,1,1 ); cp.SetProxy(cp._win); AddHook ( EmitEventHook, EventHook, cp ); cp .SetCurrentColor ( initRGBColor ); cp ._updateHuePix ( ); cp ._updateSLPix ( ); cp ._updateUI ( ); SetGadgetPixmap ( cp._col_hue , cp._pix_hue, PANELPIXMAP_STRETCH ); SetGadgetPixmap ( cp._col_sl , cp._pix_sl ); ShowGadget ( cp._win ); RedrawGadget ( cp._win ); Return cp; End Function Function clampI(i:Int var, m0:Int, m1:Int) i=max(min(i, m1), m0); End Function Method pickHueColor(r:Int Var, g:Int Var, b:Int Var ) Local rgb:Int = Self._pix_hue.ReadPixel(0,359-Self._hsl[0]); r = rgb Shr(16) & $FF; g = rgb Shr(8) & $FF; b = rgb & $FF; Return; End Method ' update ui gadgets Method _updateUI ( ) ' update color Self._color = (Self._rgb[0] Shl(16)) + (Self._rgb[1] Shl(8)) + Self._rgb[2]; ' textfields Self._col_tHex .SetText ( Right(Hex(Self._color),6) ); Self._col_tH .SetText ( Self._hsl[0] ); Self._col_tS .SetText ( Self._hsl[1] ); Self._col_tL .SetText ( Self._hsl[2] ); Self._col_tR .SetText ( Self._rgb[0] ); Self._col_tG .SetText ( Self._rgb[1] ); Self._col_tB .SetText ( Self._rgb[2] ); ' hue cursor SetGadgetShape ( Self._cursorHue, 271, 10+255-Self._hsl[0]*255/359, 30, 1); ' refresh color panel SetPanelColor ( Self._col_col, Self._rgb[0], Self._rgb[1], Self._rgb[2] ); RedrawGadget ( Self._win ); ' emit the event for the main program to receive the modification in realtime. EmitEvent ( CreateEvent(EVENT_COLOR, Self._sourceEvent, Self._color) ); End Method Method SetCurrentColor(RGB:Int) Self._rgb = rgbFromRGB ( RGB ); Self._hsl = rgb2hsl ( Self._rgb ); clampI ( Self._hsl[0], 0,359 ); clampI ( Self._hsl[1], 0,99 ); clampI ( Self._hsl[2], 0,99 ); Self ._updateHuePix ( ); Self ._updateSLPix ( ); Self ._updateUI ( ); End Method Method _updateHuePix(); Local r:Int, g:Int, b:Int, u0:Int, u1:Int; Local du1:Int = 60; Local du2:Int = 60; Local x:Int = 0; u1 = 0; u0 = u1; u1 = u0+du1; ' R(255) - B(0->255) For Local y0:Int = u0 Until u1 r = 255; g = 0; b = 255*(y0-u0)/(u1-u0); Self._pix_hue.WritePixel(x,y0, r Shl(16) | g Shl(8) | b); Next; u0 = u1; u1 = u0+du2; ' R(255->0) - B(255) For Local y1:Int = u0 Until u1 r = 255-255*(y1-u0)/(u1-u0); g = 0; b = 255; Self._pix_hue.WritePixel(x,y1, r Shl(16) | g Shl(8) | b); Next; u0 = u1; u1 = u0+du1; ' B(255) - G(0->255) For Local y2:Int = u0 Until u1 r = 0; g = 255*(y2-u0)/(u1-u0); b = 255; Self._pix_hue.WritePixel(x,y2, r Shl(16) | g Shl(8) | b); Next u0 = u1; u1 = u0+du2; ' B(255->0) - G(255) For Local y3:Int = u0 Until u1 r = 0; g = 255; b = 255-255*(y3-u0)/(u1-u0); Self._pix_hue.WritePixel(x,y3, r Shl(16) | g Shl(8) | b); Next; u0 = u1; u1 = u0+du1; ' G(255) - R(0->255) For Local y4:Int = u0 Until u1 r = 255*(y4-u0)/(u1-u0); g = 255; b = 0; Self._pix_hue.WritePixel(x,y4, r Shl(16) | g Shl(8) | b); Next; u0 = u1; u1 = u0+du2; ' G(255->0) - R(255) For Local y5:Int = u0 Until u1 r = 255; g = 255-255*(y5-u0)/(u1-u0); b = 0; Self._pix_hue.WritePixel(x,y5, r Shl(16) | g Shl(8) | b); Next; SetGadgetPixmap ( Self._col_hue, Self._pix_hue, PANELPIXMAP_STRETCH ); End Method Method _updateSLPix() Local r:Int=0, g:Int=0, b:Int=0; Self.pickHueColor(r,g,b); For Local y:Int = 0 To 255 Local rnb:Float = 1.0-Float(y) / 255; For Local x:Int = 0 To 255 Local whyte:Float = Float(x)/255; Local r_:Int = (255+(r-255)*whyte)*rnb; Local g_:Int = (255+(g-255)*whyte)*rnb; Local b_:Int = (255+(b-255)*whyte)*rnb; Self._pix_sl.WritePixel(x,y, r_ Shl(16) | g_ Shl(8) | b_); Next; Next; Self._updateSLCursor(); SetGadgetPixmap ( Self._col_sl , Self._pix_sl ); End Method Method _updateSLCursor() ' draw the new cursor Local cr0:Int = Floor(Float(_cursorS)*.5); Local perimeter:Int = cr0 * 2 * Pi; Local cr1:Int = cr0-1; Local sat:Int = (255 * Self._hsl[1])/100; Local lit:Int = (255 * (100-Self._hsl[2]))/100; For Local p:Int = 0 To perimeter Local ang:Float = Float(p*360)/perimeter; Local x0:Int = sat + Cos(ang)*cr0; Local y0:Int = lit + Sin(ang)*cr0; Local x1:Int = sat + Cos(ang)*cr1; Local y1:Int = lit + Sin(ang)*cr1; If x0>=0 And x0<256 If y0>=0 And y0<256 Then Self._pix_sl.WritePixel(x0,y0, $FF010101); EndIf; If x1>=0 And x1<256 If y1>=0 And y1<256 Then Self._pix_sl.WritePixel(x1,y1, $FFFFFFFF); EndIf; Next; SetGadgetPixmap ( Self._col_sl , Self._pix_sl ); End Method Method clearSLCursor() ' overwrite old cursor Local r:Int=Self._rgb[0], g:Int=Self._rgb[1], b:Int=Self._rgb[2]; Self.pickHueColor(r,g,b); Local sat:Int = 255*Float(Self._hsl[1])/99; Local lit:Int = 255*Float(99-Self._hsl[2])/99; For Local y:Int = -_cursorS-1 To _cursorS+1 Local j:Int = lit+y; If j>=0 And j<256 Local rnb:Float = 1.0-Float(j) / 255; For Local x:Int = -_cursorS-1 To _cursorS+1 Local i:Int = sat+x; If i>=0 And i<256 Local whyte:Float = Float(i)/255; Local r_:Int = (255+(r-255)*whyte)*rnb; Local g_:Int = (255+(g-255)*whyte)*rnb; Local b_:Int = (255+(b-255)*whyte)*rnb; Self._pix_sl.WritePixel(i,j, r_ Shl(16) | g_ Shl(8) | b_); End If; Next; End If; Next; End Method Method setHue ( h:Int ) ' clamp hue clampI(h, 0,359); ' apply only if different from current pos If Self._hsl[0]<>h Self._hsl[0] = h; ' update current color Self._rgb = hsl2rgb(Self._hsl); ' update gadgets Self ._updateSLPix ( ); Self ._updateUI ( ); EndIf; End Method Method setSL ( s:Int, l:Int ) clampI(s, 0,100); clampI(l, 0,100); If ( (s<>Self._hsl[1]) Or (l<> Self._hsl[2]) ) Self .clearSLCursor ( ) Self._hsl[1] = s; Self._hsl[2] = l; Self._rgb = hsl2rgb(Self._hsl); Self ._updateSLCursor( ); Self ._updateUI ( ); EndIf; End Method Method setSat(s:Int) clampI(s, 0,100); If (Self._hsl[1]<>s) Self .clearSLCursor ( ); Self._hsl[1] = s; Self._rgb = HSL2RGB(Self._hsl); Self ._updateSLCursor( ); Self._updateUI (); EndIf; End Method Method setLight(l:Int) clampI(l, 0, 100); If Self._hsl[2]<>l Self .clearSLCursor ( ); Self._hsl[2] = l; Self._rgb = HSL2RGB(Self._hsl); Self ._updateSLCursor( ); Self._updateUI (); EndIf; End Method Method setRed(r:Int) clampI(r, 0, 255); If Self._rgb[0]<>r Self._rgb[0] = r; Self._hsl = rgb2hsl(Self._rgb); Self._updateUI (); EndIf; End Method Method setGreen(g:Int) clampI(g, 0, 255); If Self._rgb[1]<>g Self._rgb[1] = g; Self._hsl = rgb2hsl(Self._rgb); Self._updateUI(); EndIf; End Method Method setBlue(b:Int) clampI(b, 0, 255); If Self._rgb[2]<>b Self._rgb[2] = b; Self._hsl = RGB2HSL(Self._rgb); Self._updateUI(); EndIf; End Method Method ProcessEvent:Int(event:TEvent) Select event.id Case EVENT_WINDOWCLOSE If event.Source=Self FreeGadget Self; Return True; EndIf; Case EVENT_MOUSEMOVE Select event.source ' mouse down on hue pixmap Case Self._col_hue If Self._hueHit Then Self.setHue(359-Float(359*event.y)/255); Self._slHit = False; Return True; ' mouse down on saturation/brightness pixmap Case Self._col_sl If Self._slHit Then Self.setsl(100*Float(event.x)/255.0, 100-100*Float(event.y)/255.0); Self._hueHit = False; Return True; Default Self._slHit = False; Self._hueHit = False; Return False; End Select Case EVENT_MOUSEUP Self._slHit = False; Self._hueHit = False; Return False; Case EVENT_MOUSEDOWN Select event.source Case Self._col_hue Self.setHue(359-Float(359*event.y)/255); Self._slHit = False; Self._hueHit = True; Return True; Case Self._col_sl Self.setSL(100*Float(event.x)/255.0, 100-100*Float(event.y)/255.0); Self._slHit = True; Self._hueHit = False; Return True; End Select 'Case EVENT_MOUSEENTER 'Case EVENT_MOUSELEAVE Case EVENT_GADGETACTION Select event.Source Case Self._col_tHex Self.SetCurrentColor ( ("$"+Self._col_tHex.GetText()).ToInt() ); Return True; Case Self._col_tH Self.setHue(Self._col_tH.GetText().ToInt()); Return True; Case Self._col_tS Self.setSat(Self._col_tS.GetText().ToInt()); Return True; Case Self._col_tL Self.setLight(Self._col_tL.GetText().ToInt()); Return True; Case Self._col_tR Self.setRed(Self._col_tR.GetText().ToInt()); Return True; Case Self._col_tG Self.setGreen(Self._col_tG.GetText().ToInt()); Return True; Case Self._col_tB Self.setBlue(Self._col_tB.GetText().ToInt()); Return True; Case Self._ok FreeGadget Self; Return True; ' cancel Case Self._cc ' reset color to its initial value EmitEvent CreateEvent ( EVENT_COLOR, Self._sourceEvent, Self._initColor ); FreeGadget Self; Return True; End Select; End Select; Return False; EndMethod Function EventHook:Object(id:Int,data:Object,context:Object) If TEvent(data)<>Null Then If TColorPicker(context)<>Null Then If TColorPicker(context).ProcessEvent(TEvent(data)) Then Return Null; Return data; EndFunction Method CleanUp() _sourceEvent=Null; _rgb =Null; _hsl=Null; _col_col = Null; _pix_sl = Null; _col_tHex = Null; _pix_hue = Null; _col_sl = Null; _col_hue = Null; _col_cH = Null; _col_tH = Null; _col_cS = Null; _col_tS = Null; _col_cL = Null; _col_tL = Null; _col_cR = Null; _col_tR = Null; _col_cG = Null; _col_tG = Null; _col_cB = Null; _col_tB = Null; _cursorHue=Null; _ok=Null; _cc=Null; If _core<>Null Then FreeGadget(_core); _core=Null; If _win<>Null Then FreeGadget(_win); _win=Null; RemoveHook(EmitEventHook, EventHook, Self); Super.CleanUp(); End Method Function Demo() Local WIN:TGadget = CreateWindow("test", 0,0,600,300,Null, WINDOW_TITLEBAR|WINDOW_CENTER|WINDOW_RESIZABLE) Local button:TGadget = CreateButton("pick", 2,2,ClientWidth(WIN)-4,ClientHeight(WIN)-4,WIN) SetGadgetLayout(button, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED); Local rgb:Int = $FF8000 Repeat While PollEvent()<>Null Select EventID() Case EVENT_WINDOWCLOSE End Case EVENT_GADGETACTION If EventSource() = button Then pickColor(rgb) Case EVENT_COLOR rgb = EventData(); SetGadgetColor button, rgb Shr(16) & $ff, rgb Shr(8) & $ff, rgb & $ff, 1 End Select Wend; Delay 30; Forever End Function End Type Function pickColor ( initColor:Int=$FFFFFF, source:Object=Null, Style:Int = 0 ) TColorPicker.getInstance ( initColor, source, Style); End Function TColorPicker.Demo(); End; |
Comments
None.
Code Archives Forum