From 70381978007e7fef659cfdb109cfc2908e50ac90 Mon Sep 17 00:00:00 2001 From: shawna-p <37048629+shawna-p@users.noreply.github.com> Date: Sat, 22 Apr 2023 10:06:33 -0400 Subject: [PATCH] Add four corner capability. Mobile Compatibility Correct ints to floats for mobile shader compatibility. --- color_picker.rpy | 184 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 151 insertions(+), 33 deletions(-) diff --git a/color_picker.rpy b/color_picker.rpy index 9c405fa..c911f64 100644 --- a/color_picker.rpy +++ b/color_picker.rpy @@ -5,6 +5,9 @@ init python: ## A shader which creates a gradient for a colour picker. renpy.register_shader("feniks.color_picker", variables=""" uniform vec4 u_gradient_top_right; + uniform vec4 u_gradient_top_left; + uniform vec4 u_gradient_bottom_left; + uniform vec4 u_gradient_bottom_right; uniform vec2 u_model_size; varying float v_gradient_x_done; varying float v_gradient_y_done; @@ -15,12 +18,12 @@ init python: """, fragment_300=""" float left_gradient = v_gradient_x_done; float top_gradient = v_gradient_y_done; - vec4 white = vec4(1.0); - vec4 black = vec4(0.0, 0.0, 0.0, 1.0); - // Mix white and the colour - gl_FragColor = mix(white, u_gradient_top_right, left_gradient); - // Mix that with black - gl_FragColor = mix(black, gl_FragColor, 1.0-top_gradient); + // Mix top left and the colour + gl_FragColor = mix(u_gradient_top_left, u_gradient_top_right, left_gradient); + // Mix the two bottom colours + vec4 bottom = mix(u_gradient_bottom_left, u_gradient_bottom_right, left_gradient); + // Mix that with the top + gl_FragColor = mix(bottom, gl_FragColor, 1.0-top_gradient); """) ## A shader which creates a spectrum. Generally for colour pickers. @@ -38,19 +41,19 @@ init python: """, fragment_functions=""" // HSL to RGB conversion adapted from https://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion float hue2rgb(float p, float q, float t){ - if(t < 0.) t += 1.; - if(t > 1.) t -= 1.; - if(t < 1./6.) return p + (q - p) * 6. * t; - if(t < 1./2.) return q; - if(t < 2./3.) return p + (q - p) * (2./3. - t) * 6.; + if(t < 0.0) t += 1.0; + if(t > 1.0) t -= 1.0; + if(t < 1.0/6.0) return p + (q - p) * 6.0 * t; + if(t < 1.0/2.0) return q; + if(t < 2.0/3.0) return p + (q - p) * (2.0/3.0 - t) * 6.0; return p; } vec3 hslToRgb(float h, float l, float s) { - float q = l < 0.5 ? l * (1. + s) : l + s - l * s; - float p = 2 * l - q; - float r = hue2rgb(p, q, h + 1./3.); + float q = l < 0.5 ? l * (1.0 + s) : l + s - l * s; + float p = 2.0 * l - q; + float r = hue2rgb(p, q, h + 1.0/3.0); float g = hue2rgb(p, q, h); - float b = hue2rgb(p, q, h - 1./3.); + float b = hue2rgb(p, q, h - 1.0/3.0); return vec3(r, g, b); } """, fragment_300=""" @@ -69,9 +72,12 @@ transform spectrum(horizontal=True, light=0.5, sat=1.0): u_horizontal float(horizontal) ## A transform which creates a square with a gradient from the top right -transform color_picker(color): +transform color_picker(top_right, bottom_right="#000", bottom_left="#000", top_left="#fff"): shader "feniks.color_picker" - u_gradient_top_right Color(color).rgba + u_gradient_top_right Color(top_right).rgba + u_gradient_top_left Color(top_left).rgba + u_gradient_bottom_left Color(bottom_left).rgba + u_gradient_bottom_right Color(bottom_right).rgba ################################################################################ ## CLASSES AND FUNCTIONS @@ -105,7 +111,8 @@ init python: True if the indicator is currently being dragged around. """ RED = Color("#f00") - def __init__(self, xsize, ysize, start_color="#f00", **kwargs): + def __init__(self, xsize, ysize, start_color=None, four_corners=None, + **kwargs): """ Create a ColorPicker object. @@ -117,12 +124,34 @@ init python: The height of the colour picker. start_color : str A hexadecimal colour code corresponding to the starting colour. + four_corners : tuple(Color, Color, Color, Color) + A tuple of four colours corresponding to the four corners of the + colour picker. The order is top right, bottom right, bottom + left, top left. If this is not None, it will override the + start_color parameter. """ super(ColorPicker, self).__init__(**kwargs) self.xsize = xsize self.ysize = ysize - self.set_color(start_color) + self.top_left = None + self.top_right = None + self.bottom_left = None + self.bottom_right = None + + if start_color is None and four_corners is None: + self.set_color("#f00") + elif four_corners is None: + self.set_color(start_color) + else: + self.top_right, self.bottom_right, self.bottom_left, self.top_left = four_corners + ## Convert to Color objects + self.top_right = Color(self.top_right) + self.bottom_right = Color(self.bottom_right) + self.bottom_left = Color(self.bottom_left) + self.top_left = Color(self.top_left) + self.set_color(self.top_right) + self.picker = Transform("#fff", xysize=(self.xsize, self.ysize)) self.dragging = False @@ -136,9 +165,18 @@ init python: The new colour to set the colour picker to. """ self.color = Color(color) - self.selector_xpos = self.color.hsv[1] - self.selector_ypos = 1.0 - self.color.hsv[2] - self.hue_rotation = self.color.hsv[0] + ## Check if this has four custom corners + if self.top_left is None: + ## No; set to saturation/value + self.selector_xpos = self.color.hsv[1] + self.selector_ypos = 1.0 - self.color.hsv[2] + self.hue_rotation = self.color.hsv[0] + else: + ## There isn't a good way to guess the position of a colour + ## with custom corners, so just set it to the top right + self.selector_xpos = 1.0 + self.selector_ypos = 0.0 + self.hue_rotation = 0.0 def visit(self): return [Image("selector")] @@ -149,9 +187,17 @@ init python: """ r = renpy.Render(self.xsize, self.ysize) - trc = self.RED.rotate_hue(self.hue_rotation) - # Colorize the picker into a gradient - picker = At(self.picker, color_picker(trc)) + if self.top_left is None: + trc = self.RED.rotate_hue(self.hue_rotation) + # Colorize the picker into a gradient + picker = At(self.picker, color_picker(trc)) + else: + # Custom four corners; no spectrum sliders + picker = At(self.picker, color_picker( + self.top_right.rotate_hue(self.hue_rotation), + self.bottom_right.rotate_hue(self.hue_rotation), + self.bottom_left.rotate_hue(self.hue_rotation), + self.top_left.rotate_hue(self.hue_rotation))) # Position the selector selector = Transform("selector", anchor=(0.5, 0.5), xpos=self.selector_xpos, ypos=self.selector_ypos) @@ -186,9 +232,20 @@ init python: self.selector_ypos = min(max(self.selector_ypos, 0.0), 1.0) # Figure out the colour under the selector - trc = self.RED.rotate_hue(self.hue_rotation) - self.color = Color("#fff").interpolate(trc, self.selector_xpos) - self.color = self.color.interpolate(Color("#000"), self.selector_ypos) + if self.top_left is None: + trc = self.RED.rotate_hue(self.hue_rotation) + tlc = Color("#fff") + brc = Color("#000") + blc = Color("#000") + else: + tlc = self.top_left.rotate_hue(self.hue_rotation) + trc = self.top_right.rotate_hue(self.hue_rotation) + brc = self.bottom_right.rotate_hue(self.hue_rotation) + blc = self.bottom_left.rotate_hue(self.hue_rotation) + + self.color = tlc.interpolate(trc, self.selector_xpos) + bottom = blc.interpolate(brc, self.selector_xpos) + self.color = self.color.interpolate(bottom, self.selector_ypos) return None def picker_color(st, at, picker, xsize=100, ysize=100): @@ -211,7 +268,7 @@ init python: A brief DynamicDisplayable demonstration of how to display color information in real-time. """ - return Fixed(Text(picker.color.hexcode), xysize=(200, 40)), 0.01 + return Fixed(Text(picker.color.hexcode), xsize=200, yfit=True), 0.01 ################################################################################ ## IMAGES @@ -252,7 +309,7 @@ screen color_picker(): spacing 25 ## A vertical bar which lets you change the hue of the picker. vbar value FieldValue(picker, "hue_rotation", 1.0): - xysize (80, 700) + xysize (50, 700) base_bar At(Transform("#000", xysize=(50, 700)), spectrum(horizontal=False)) thumb Transform("selector_bg", xysize=(50, 20)) thumb_offset 10 @@ -265,7 +322,7 @@ screen color_picker(): ## If you only need one swatch, use this: add picker_color ## Otherwise, the following code lets you switch between - ## two different swatches to choose more than one colour. + ## two different swatches to choose more than one colour: # if current_color == 1: # button: # padding (4, 4) background "#fff" @@ -297,13 +354,14 @@ screen color_picker(): ## The hex code is provided as an example. add picker_hex ## The DynamicDisplayable from earlier ## These update when the mouse button is released + ## since they aren't a dynamic displayable text "R: [picker.color.rgb[0]:.2f]" text "G: [picker.color.rgb[1]:.2f]" text "B: [picker.color.rgb[2]:.2f]" ## A horizontal bar that lets you change the hue of the picker bar value FieldValue(picker, "hue_rotation", 1.0): - xysize (700, 80) xpos 25+80 + xysize (700, 50) xpos 25+80 base_bar At(Transform("#000", xysize=(700, 50)), spectrum()) thumb Transform("selector_bg", xysize=(20, 50)) thumb_offset 10 @@ -312,6 +370,62 @@ screen color_picker(): ## is always stored in the picker's `color` attribute. textbutton "Return" action Return(picker.color) align (1.0, 1.0) +################################################################################ +## A sample screen to demonstrate the four corner colour picker. +## Simply write `call screen four_corner_picker()` to test it. +screen four_corner_picker(): + + ## The picker itself. Its size is 700x700, and it's given a colour for + ## all four corners (top right, bottom right, bottom left, top left) + ## You may declare this outside of the screen to make it easier to access. + default picker = ColorPicker(700, 700, four_corners=("#37dddd", "#010038", "#270129", "#daf0ea")) + ## The preview swatch. Needs to be provided the picker variable from above. + ## You can specify its size as well. + default picker_color = DynamicDisplayable(picker_color, picker=picker, + xsize=100, ysize=100) + ## The hexcode of the current colour. Demonstrates updating the picker + ## colour information in real-time. + default picker_hex = DynamicDisplayable(picker_hexcode, picker=picker) + + add "#333" + + vbox: + align (0.5, 0.5) spacing 25 + hbox: + spacing 25 + + ## The picker itself + add picker + vbox: + xsize 200 spacing 10 + ## The swatch + ## If you only need one swatch, use this: + add picker_color + + ## You can display other information on the color here, as desired + ## Some examples are provided. Note that these do not update in + ## tandem with the picker, but when the mouse is released. You + ## will need to use a DynamicDisplayable for real-time updates. + ## The hex code is provided as an example. + add picker_hex ## The DynamicDisplayable from earlier + ## These update when the mouse button is released + ## since they aren't a dynamic displayable + text "R: [picker.color.rgb[0]:.2f]" + text "G: [picker.color.rgb[1]:.2f]" + text "B: [picker.color.rgb[2]:.2f]" + + ## A horizontal bar that lets you change the hue of the picker + ## For a four-corner picker, you may not need this. + bar value FieldValue(picker, "hue_rotation", 1.0): + xysize (700, 50) + base_bar "#fff" + thumb Transform("selector_bg", xysize=(20, 50)) + thumb_offset 10 + + ## In this case, the screen returns the picker's colour. The colour itself + ## is always stored in the picker's `color` attribute. + textbutton "Return" action Return(picker.color) align (1.0, 1.0) + ################################################################################ ## TESTING ################################################################################ @@ -325,5 +439,9 @@ label how_to_use_color_picker(): ## This is used to put the returned colour into a colour text tag $ color_tag = "{color=%s}" % chosen_color "[color_tag]You chose the colour [chosen_color].{/color}" + "Next, you will be shown a four-corner colour picker." + call screen four_corner_picker() + $ chosen_color = _return.hexcode + $ color_tag = "{color=%s}" % chosen_color + "[color_tag]You chose the colour [chosen_color].{/color}" return -