#!/usr/bin/wish -f ## ## SCRIPT: make_gradient-on-canvas_6scales-2radiobuttons.tk ## ## PURPOSE: This TkGUI script facilitates the creation of ## rectangular color-gradient images that can be used, for example, ## for the background of 'buttons' in GUIs such as 'toolchests'. ## ## A screen/window capture utility (like 'gnome-screenshot' on Linux) ## can be used to capture the image in a PNG file, say. ## ## Then, if necessary, an image editor (like 'mtpaint' on Linux) ## can be used to crop the window capture image to get only the ## rectangular area of the canvas containing the color-gradient ## --- or some sub-rectangle of that area. ## ## Furthermore, utilities (such as the ImageMagick 'convert' command ## on Linux) can be used to 'mirror' or 'flip' a gradient image in ## an image file (PNG or JPEG or GIF). The 'mirror' and 'flip' ## operations can be applied vertically or horizontally --- and ## can be applied multiple times, for various visual effects. ## ## The resulting rectangular color-gradient image can then be used as a ## background in Tk widgets, such as button or canvas or label widgets ## in 'toolchests' or other types of GUIs. ## ######################## ## GUI LAYOUT and METHOD: ## ## The GUI contains a rectangular canvas widget into which the ## color gradient is drawn with canvas 'create line' commands, ## where the lines can be either horizontal (in the x direction) ## or vertical (in the y direction). ## ## In addition to the canvas widget (in a bottom frame of the GUI ## window), there are other frames of the GUI window: ## - one frame for 2 radiobuttons and an 'Exit' button. ## - 6 frames for 6 'scale' (slider) widgets. ## ## The 2 radiobuttons are to set the direction of the gradient --- ## in the x direction or the y direction. ## ## The 6 scales are to set 2 pairs of RGB values --- of the ## form r1 g1 b1 r2 g2 b2 --- for the left-color (or top-color) ## and right-color (bottom-color) of the gradient. ## ## Examples of 2 settings of the radiobuttons and the 6 scales: ## x 255 255 0 255 0 0 ## y 255 0 255 0 0 255 ## ## The first example says draw the lines horizontally starting ## from yellow on the left to red on the right. ## ## The second example says draw the lines vertically starting ## from magenta at the top to blue on the bottom. ## ## The seven parms (x/y r1 g1 b1 r2 g2 b2) ## are passed into a 'DrawGradient' proc that draws the lines ## within the canvas, filling the canvas with colored pixels. ## ## There is a '-command' parameter on the scale widget ## which could be used to call the 'DrawGradient' proc to redraw ## the color-gradient in the canvas as any scale (slider) changes. ## ## However, the 'DrawGradient' proc may not be fast enough to ## re-draw all the gradient-lines in a smooth fashion as the ## scale sliders are moved. ## ## In that case, we can use a button1-release binding on each ## scale widget to trigger a call to the 'DrawGradient' proc. ## ## By the time the user mouse-releases one scale and goes to ## move-and-release another scale, the 'DrawGradient' proc ## can complete the re-draw. ## ## Thus we get a 'semi-dynamic' (not quite immediate) GUI-update ## action from the scales, rather than a fully 'dynamic' ## (immediate) GUI-update action. ## ## However, the user/programmer can experiment by commenting ## out the 12 button1-release 'bind ' statements, and ## adding a '-command DrawGradient' parm to the 6 scale ## definition statements. ## ## REFERENCE: ## The 'DrawGradient' proc is based on a Tcl-Tk script by GPS and ## Damon Courtney --- published at http://wiki.tcl.tk/6100 - ## 'Drawing Gradients on a Canvas'. (downloaded 2011sep26) ## That script draws gradients on multiple rectangular canvases, packed ## top to bottom. You need to edit that script to change colors or ## gradient direction. No GUI for entry of those parameters is provided. ##+###################################################################### ## 'CANONICAL' STRUCTURE OF THIS CODE: ## ## 0) Set general window parms (win-name,win-position,color-scheme,fonts, ## widget-geometry-parms,win-size-control,text-array-for-labels-etc). ## 1a) Define ALL frames (and sub-frames, if any). ## 1b) Pack the frames and sub-frames. ## 2) Define all widgets in the frames, frame-by-frame. ## When all the widgets for a frame are defined, pack them. ## ## 3) Define keyboard and mouse/touchpad/touch-sensitive-screen 'event' ## BINDINGS, if needed. ## 4) Define PROCS, if needed. ## 5) Additional GUI INITIALIZATION (typically with one or two procs), ## if needed. ## ## ## Some detail of the code structure for this particular script: ## ## 1a) Define ALL frames: ## ## Top-level : '.fRbuttons' ## '.fRsliderR1' '.fRsliderG1' '.fRsliderB1' ## '.fRsliderR2' '.fRsliderG2' '.fRsliderB2' ## '.fRcanvas' ## ## Sub-frames: none ## ## 1b) Pack ALL frames. ## ## 2) Define all widgets in the frames (and pack them): ## ## - In '.fRbuttons': 1 button widget ('Exit') and ## 2 radiobuttons (for x or y), ## ## - In each '.fRslider' frame: 1 scale widget (with label widget) ## ## - In '.fRcanvas': one 'canvas' widget ## ## 3) Define bindings: six, for button1-release on the scale widgets ## two, for button1-release on the radiobutton widgets ## ## 4) Define procs: ## - 'DrawGradient' invoked by the 8 bindings and by a call in the ## additional-GUI-initialization section, next ## ## 5) Additional GUI initialization: Execute 'DrawGradient' once with ## an initial, example set of 7 parms ## --- x/y r1 g1 b1 r2 g2 b2 --- to start ## with a color-gradient in the canvas ## rather than a blank canvas. ##+######################################################################## ## DEVELOPED WITH: ## Tcl-Tk 8.5 on Ubuntu 9.10 (2009-october, 'Karmic Koala). ## ## $ wish ## % puts "$tcl_version $tk_version" ## showed 8.5 8.5 on Ubuntu 9.10. ##+####################################################################### ## MAINTENANCE HISTORY: ## Created by: Blaise Montandon 2012aug10 ## Changed by: Blaise Montandon 2012nov18 Added braces to 9 'expr' statements. ## Provided more consistent indenting ## of the code. Touched up the comments ## to match the final code. Added a ## text-array for labels,buttons,etc. ## Improved calc of minsize of window. ## Moved canvas to bottom of GUI. ##+####################################################################### ##+####################################################################### ## Set WINDOW TITLES. ##+####################################################################### wm title . "Draw-Color-Gradient in a Canvas" wm iconname . "DrawGradient" ##+###################################################################### ## Set WINDOW POSITION. ##+###################################################################### wm geometry . +15+30 ##+###################################################### ## Set the COLOR SCHEME for the window and its widgets --- ## radiobuttons. ##+###################################################### tk_setPalette "#e0e0e0" set radbuttBKGD "#ffffff" ##+########################################################### ## Set FONT-NAMES. ## We use a variable-width font for buttons and labels. ## ## We use a fixed-width font for entry fields, for easy access ## to narrow characters like i, j, l, and the number 1 --- ## in listboxes so that character columns in the list line up, ## and for text in a help window, so that columns will line up. ## However we do not have entry,listbox, or text widgets in ## this GUI --- unless we add a Help button. ##+########################################################### font create fontTEMP_varwidth \ -family {comic sans ms} \ -size -14 \ -weight bold \ -slant roman ## Some other possible (similar) variable width fonts: ## Arial ## Bitstream Vera Sans ## DejaVu Sans ## Droid Sans ## FreeSans ## Liberation Sans ## Nimbus Sans L ## Trebuchet MS ## Verdana font create fontTEMP_fixedwidth \ -family {liberation mono} \ -size -14 \ -weight bold \ -slant roman ## Some other possible fixed width fonts (esp. on Linux): ## Andale Mono ## Bitstream Vera Sans Mono ## Courier 10 Pitch ## DejaVu Sans Mono ## Droid Sans Mono ## FreeMono ## Nimbus Mono L ## TlwgMono ##+########################################################### ## SET GEOM VARS FOR THE VARIOUS WIDGET DEFINITIONS. ## (e.g. width and height of canvas, and padding for Buttons) ##+########################################################### ## CANVAS parms: set initCanWidthPx 300 set initCanHeightPx 24 set minCanHeightPx 24 # set BDwidthPx_canvas 2 set BDwidthPx_canvas 0 ## BUTTON (and RADIOBUTTON) parms: set PADXpx_button 0 set PADYpx_button 0 set BDwidthPx_button 2 ## LABEL parms: set PADXpx_label 0 set PADYpx_label 0 set BDwidthPx_label 2 ## SCALE parms: set BDwidthPx_scale 2 set scaleLengthPx 200 ##+###################################################### ## Set a MINSIZE of the window according to the ## approx max WIDTH of the chars in the Exit button frame ## --- and according to the approx HEIGHT of the 8 frames. ##+################################################################# ## We allow the window to be resizable. We pack the canvas with ## '-fill both' so that the canvas can be enlarged by enlarging the ## window. The 'Draw' proc can be used to re-fill the canvas with ## the user-specified color gradient. ##+################################################################# set minWinWidthPx [font measure fontTEMP_fixedwidth \ " Exit Gradient direction: x y"] ## Add some to account for right-left-side window border-widths ## (about 2x3=6 pixels) and widget border-widths --- about ## 4 widgets x 4 pixels/widget = 16 pixels. set minWinWidthPx [expr {22 + $minWinWidthPx}] ## For MIN-HEIGHT, allow: ## 1 char high for frame 'fRbuttons', ## 2 chars high for each of the 6 scale frames, ## 24 pixels high for frame 'fRcanvas'. set minWinHeightPx [font metrics fontTEMP_fixedwidth -linespace] set minWinHeightPx [expr {24 + 13 * $minWinHeightPx}] ## Add some to account for top-bottom window decoration (about 23 pixels) ## and frame/widget padding/borders (about ## 14 frames/widgets x 4 pixels/frame-widget = 56 pixels). set minWinHeightPx [expr {79 + $minWinHeightPx}] ## FOR TESTING: # puts "minWinWidthPx = $minWinWidthPx" # puts "minWinHeightPx = $minWinHeightPx" wm minsize . $minWinWidthPx $minWinHeightPx ## If you want to make the window un-resizable, ## you can use the following statement. # wm resizable . 0 0 ##+################################################ ## Load a TEXT-ARRAY variable with text for ## labels and other GUI widgets --- to facilitate ## 'internationalization' of this script. ##+################################################ ## if { "$VARlocale" == "en"} set aRtext(buttonEXIT) "Exit" set aRtext(labelDIR) "Gradient direction:" set scaleLabelWidthChars 15 set aRtext(labelSCALEGRP1) "\ Left/top Color r1:" set aRtext(labelSCALEGRP2) "\ Right/bottom Color r2:" ## END OF if { "$VARlocale" == "en"} ##+################################################################ ## DEFINE *ALL* THE FRAMES: ## ## Top-level : 'fRbuttons', ## 'fRsliderR1' , 'fRsliderG1' , 'fRsliderB1' ## 'fRsliderR2' , 'fRsliderG2' , 'fRsliderB2' ## 'fRcanvas' ## ## Sub-frames: none ##+################################################################ ## FOR TESTING: (of expansion of frames, esp. during window expansion) # set RELIEF_frame raised # set BDwidth_frame 2 set RELIEF_frame flat set BDwidth_frame 0 frame .fRbuttons -relief $RELIEF_frame -borderwidth $BDwidth_frame set RELIEF_frame2 raised set BDwidth_frame2 2 frame .fRsliderR1 -relief $RELIEF_frame2 -borderwidth $BDwidth_frame2 frame .fRsliderG1 -relief $RELIEF_frame2 -borderwidth $BDwidth_frame2 frame .fRsliderB1 -relief $RELIEF_frame2 -borderwidth $BDwidth_frame2 frame .fRsliderR2 -relief $RELIEF_frame2 -borderwidth $BDwidth_frame2 frame .fRsliderG2 -relief $RELIEF_frame2 -borderwidth $BDwidth_frame2 frame .fRsliderB2 -relief $RELIEF_frame2 -borderwidth $BDwidth_frame2 frame .fRcanvas -relief $RELIEF_frame -borderwidth $BDwidth_frame ##+############################## ## PACK the top-level FRAMES. ##+############################## pack .fRbuttons \ .fRsliderR1 \ .fRsliderG1 \ .fRsliderB1 \ .fRsliderR2 \ .fRsliderG2 \ .fRsliderB2 \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRcanvas \ -side top \ -anchor nw \ -fill both \ -expand 1 ## OK. All frames are defined and packed. ## Now define the widgets within the frames. ##+################################################################ ## IN THE '.fRbuttons' frame - ## DEFINE the 'Exit' button --- and ## 2 radiobuttons (with a label button). ##+################################################################ button .fRbuttons.buttEXIT \ -text "$aRtext(buttonEXIT)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {exit} ## Define label and 2 radiobuttons: label .fRbuttons.labDIR \ -text "$aRtext(labelDIR)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_button radiobutton .fRbuttons.radbuttX \ -text "x" \ -font fontTEMP_varwidth \ -anchor w \ -variable curDIRECTION \ -value "x" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd $BDwidthPx_button radiobutton .fRbuttons.radbuttY \ -text "y" \ -font fontTEMP_varwidth \ -anchor w \ -variable curDIRECTION \ -value "y" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd $BDwidthPx_button ##+############################################## ## Pack ALL the widgets in the 'fRbuttons' frame. ##+############################################## pack .fRbuttons.buttEXIT \ .fRbuttons.labDIR \ .fRbuttons.radbuttX \ .fRbuttons.radbuttY \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## In the FIRST 3 RGB 'slider' FRAMES - ## DEFINE a LABEL and a SLIDERBAR WIDGET in each one. ##+######################################################## set JUSTIFYlabel_scale left ## RED1 label .fRsliderR1.label \ -text "$aRtext(labelSCALEGRP1)" \ -width $scaleLabelWidthChars \ -font fontTEMP_varwidth \ -justify $JUSTIFYlabel_scale \ -anchor w \ -relief flat \ -bd $BDwidthPx_label scale .fRsliderR1.scale \ -orient h \ -digits 0 \ -from 0 -to 255 \ -length $scaleLengthPx \ -variable curR1 # -command {eval DrawGradient .fRcanvas.can $curDIRECTION \ # $curR1 $curG1 $curB1 $curR2 $curG2$curB2 } ## GREEN1 label .fRsliderG1.label \ -text "\ g1:" \ -width $scaleLabelWidthChars \ -font fontTEMP_varwidth \ -justify $JUSTIFYlabel_scale \ -anchor w \ -relief flat \ -bd $BDwidthPx_label scale .fRsliderG1.scale \ -orient h \ -digits 0 \ -from 0 -to 255 \ -length $scaleLengthPx \ -variable curG1 # -command {eval DrawGradient .fRcanvas.can $curDIRECTION \ # $curR1 $curG1 $curB1 $curR2 $curG2$curB2 } ## BLUE1 label .fRsliderB1.label \ -text "\ b1:" \ -width $scaleLabelWidthChars \ -font fontTEMP_varwidth \ -justify $JUSTIFYlabel_scale \ -anchor w \ -relief flat \ -bd $BDwidthPx_label scale .fRsliderB1.scale \ -orient h \ -digits 0 \ -from 0 -to 255 \ -length $scaleLengthPx \ -variable curB1 # -command {eval DrawGradient .fRcanvas.can $curDIRECTION \ # $curR1 $curG1 $curB1 $curR2 $curG2$curB2 } ##+######################################################## ## In the SECOND 3 RGB 'slider' frames - ## DEFINE a LABEL and a SLIDERBAR WIDGET in each one. ##+######################################################## ## RED2 label .fRsliderR2.label \ -text "$aRtext(labelSCALEGRP2)" \ -width $scaleLabelWidthChars \ -font fontTEMP_varwidth \ -justify $JUSTIFYlabel_scale \ -anchor w \ -relief flat \ -bd $BDwidthPx_label scale .fRsliderR2.scale \ -orient h \ -digits 0 \ -from 0 -to 255 \ -length $scaleLengthPx \ -variable curR2 # -command {eval DrawGradient .fRcanvas.can $curDIRECTION \ # $curR1 $curG1 $curB1 $curR2 $curG2$curB2 } ## GREEN2 label .fRsliderG2.label \ -text "\ g2:" \ -width $scaleLabelWidthChars \ -font fontTEMP_varwidth \ -justify $JUSTIFYlabel_scale \ -anchor w \ -relief flat \ -bd $BDwidthPx_label scale .fRsliderG2.scale \ -orient h \ -digits 0 \ -from 0 -to 255 \ -length $scaleLengthPx \ -variable curG2 # -command {eval DrawGradient .fRcanvas.can $curDIRECTION \ # $curR1 $curG1 $curB1 $curR2 $curG2$curB2 } ## BLUE2 label .fRsliderB2.label \ -text "\ b2:" \ -width $scaleLabelWidthChars \ -font fontTEMP_varwidth \ -justify $JUSTIFYlabel_scale \ -anchor w \ -relief flat \ -bd $BDwidthPx_label scale .fRsliderB2.scale \ -orient h \ -digits 0 \ -from 0 -to 255 \ -length $scaleLengthPx \ -variable curB2 # -command {eval DrawGradient .fRcanvas.can $curDIRECTION \ # $curR1 $curG1 $curB1 $curR2 $curG2$curB2 } ##+########################################################## ## Pack ALL the widgets in the 6 'fRslider' frames. ##+########################################################## ## We pack them separately in case we want to experiment with ## the pack parms, such as '-expand 1' on the scales. ############################################################# pack .fRsliderR1.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRsliderR1.scale \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRsliderG1.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRsliderG1.scale \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRsliderB1.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRsliderB1.scale \ -side left \ -anchor w \ -fill none \ -expand 0 ## Pack the 2nd group of 3 scales. pack .fRsliderR2.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRsliderR2.scale \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRsliderG2.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRsliderG2.scale \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRsliderB2.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRsliderB2.scale \ -side left \ -anchor w \ -fill none \ -expand 0 ##+############################### ## In the '.fRcanvas' frame - ## DEFINE-and-PACK CANVAS WIDGET: ##+############################### ## We set highlightthickness & borderwidth of the canvas to ## zero, as suggested on page 558, Chapter 37, 'The Canvas ## Widget', in the 4th edition of the book 'Practical ## Programming in Tcl and Tk'. ##+###################################################### canvas .fRcanvas.can \ -width $initCanWidthPx \ -height $initCanHeightPx \ -relief flat \ -highlightthickness 0 \ -borderwidth 0 pack .fRcanvas.can \ -side top \ -anchor nw \ -fill both \ -expand 1 ## OK. All widgets are defined and packed. ## Now define bindings and procs. ##+####################################################################### ## BINDINGS SECTION: ## - one for button1-release on each of the 6 scales ## - one for button1-release on each of the 2 radiobuttons ##+####################################################################### if {1} { bind .fRsliderR1.scale {DrawGradient \ .fRcanvas.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2} bind .fRsliderG1.scale {DrawGradient \ .fRcanvas.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2} bind .fRsliderB1.scale {DrawGradient \ .fRcanvas.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2} bind .fRsliderR2.scale {DrawGradient \ .fRcanvas.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2} bind .fRsliderG2.scale {DrawGradient \ .fRcanvas.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2} bind .fRsliderB2.scale {DrawGradient \ .fRcanvas.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2} ## button1 release bindings on 2 radiobuttons: bind .fRbuttons.radbuttX {DrawGradient \ .fRcanvas.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2} bind .fRbuttons.radbuttY {DrawGradient \ .fRcanvas.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2} } ## END OF 'if {1/0}' SECTION ## To easily disable the bindings. ##+###################################################################### ## PROCS SECTION: ## - 'DrawGradient' to fill the specified canvas according to the ## 7 parms --- x/y r1 g1 b1 r2 g2 b2 ##+###################################################################### ##+##################################################################### ## proc DrawGradient - ## ## PURPOSE: ## Draws the gradient on the canvas using canvas 'create line' ## commands. Draws vertical or horizontal lines according to ## the axis-specification: 'x' or 'y'. Interpolates between ## 2 RGB colors. ## ## CALLED BY: button1-release bindings on 6 scales and 2 radiobuttons, ## and in the additional-GUI-initialization section at ## the bottom of this script. ##+#################################################################### proc DrawGradient {win axis r1 g1 b1 r2 g2 b2} { global ENTRYstring # $win delete TAGgradient set width [winfo width $win] set height [winfo height $win] switch -- $axis { "x" { set max $width; set x 1 } "y" { set max $height; set x 0 } default { ## We could put the error msg on the end of the user-entry ## in the entry-field. # set ENTRYstring "$ENTRYstring ERR: Invalid 1st parm. Must be x or y." # return return -code error "Invalid 1st parm: $axis. Must be x or y" } } if { $r1 > 255 || $r1 < 0 } { return -code error "Invalid color value for r1: $r1" } if { $g1 > 255 || $g1 < 0 } { return -code error "Invalid color value for g1: $g1" } if { $b1 > 255 || $b1 < 0 } { return -code error "Invalid color value for b1: $b1" } if { $r2 > 255 || $r2 < 0 } { return -code error "Invalid color value for r2: $r2" } if { $g2 > 255 || $g2 < 0 } { return -code error "Invalid color value for g2: $g2" } if { $b2 > 255 || $b2 < 0 } { return -code error "Invalid color value for b2: $b2" } set rRange [expr {$r2 - double($r1)}] set gRange [expr {$g2 - double($g1)}] set bRange [expr {$b2 - double($b1)}] set rRatio [expr {$rRange / $max}] set gRatio [expr {$gRange / $max}] set bRatio [expr {$bRange / $max}] for {set i 0} {$i < $max} {incr i} { set nR [expr {int( $r1 + ($rRatio * $i) )}] set nG [expr {int( $g1 + ($gRatio * $i) )}] set nB [expr {int( $b1 + ($bRatio * $i) )}] set col [format {%2.2x} $nR] append col [format {%2.2x} $nG] append col [format {%2.2x} $nB] ## FOR TESTING: # puts "col = $col" if {$x} { $win create line $i 0 $i $height -tags TAGgradient -fill "#$col" } else { $win create line 0 $i $width $i -tags TAGgradient -fill "#$col" } } } ## END OF proc 'DrawGradient' ##+##################################################### ## Additional GUI initialization, if needed (or wanted). ## ## We draw a gradient on the canvas, rather than letting the ## GUI come up with an empty canvas. ##+##################################################### ##+################################################################ ## Set the init values for the 2 radiobuttons and the 6 scales. ##+################################################################ set curDIRECTION "x" set curR1 255 set curG1 255 set curB1 0 set curR2 255 set curG2 0 set curB2 0 update ## 'update' is needed before DrawGradient so that the ## canvas width and height are implemented. ## DrawGradient uses 'winfo' to get those dimensions. DrawGradient .fRcanvas.can $curDIRECTION \ $curR1 $curG1 $curB1 $curR2 $curG2 $curB2