Last active
October 29, 2017 21:11
-
-
Save awwsmm/1bf4557e6205edb37da5d7f701e14aca to your computer and use it in GitHub Desktop.
Scala extension to java.awt.Color with Hex, HSV, RGB, and decimal methods, color conversions, and gradient methods
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.awt.Color | |
import scala.math._ | |
//////////////////////////////////////////////////////////////////////////////// | |
/// | |
/// SColor extends java.awt.Color and provides the additional methods: | |
/// | |
/// asRGB(): List[Int] | |
/// provides a (red: Int, green: Int, blue: Int) representation of the | |
/// color, where each component has a range [0,255] | |
/// | |
/// asHEX(): String | |
/// provides a hexadecimal string representation of the color, ie. "F9E013" | |
/// | |
/// asHSV(): List[Double] | |
/// provides a (hue: Double, saturation: Double, value: Double) represent- | |
/// ation of the color, where each component has a range [0.0,1.0] | |
/// | |
/// asDEC(): Int | |
/// provides an integer representation of the color, where "the red | |
/// component in bits 16-23, the green component in bits 8-15, and the blue | |
/// component in bits 0-7", per java.awt.Color standards | |
/// | |
/// color1[SColor].gradientRGB(color2: SColor, nSteps: Int): List[String] | |
/// given an initial SColor color1 and a final SColor color2, this method | |
/// returns a list (length: nSteps >= 1) of hexadecimal strings representing | |
/// the evenly-spaced intermediate colors in RGB space | |
/// | |
/// color1[SColor].gradientHSV(color2: SColor, nSteps: Int, inverse: Boolean): List[String] | |
/// same as above, but in HSV space. The option "inverse" allows the HSV | |
/// space to be travelled anticlockwise in hue, rather than clockwise. | |
/// | |
/// This class uses Double values instead of Java floats, for easier use in | |
/// Scala. | |
/// | |
//////////////////////////////////////////////////////////////////////////////// | |
class SColor private ( r: Float, g: Float, b: Float, a: Float ) extends java.awt.Color (r, g, b, a) { | |
///--------------------------------------------------------------------------- | |
/// constructors | |
///--------------------------------------------------------------------------- | |
// java.awt.Color | |
def this(color: Color) = { this( | |
(color.getRed /255.0).toFloat, // r | |
(color.getGreen/255.0).toFloat, // g | |
(color.getBlue /255.0).toFloat, // b | |
(color.getAlpha/255.0).toFloat // a | |
)} | |
/// RGB constructors | |
// Double: r, g, b | |
def this(r: Double, g: Double, b: Double) = { this( | |
r.toFloat, // r | |
g.toFloat, // g | |
b.toFloat, // b | |
1.0f // a | |
)} | |
// Double: r, g, b, a | |
def this(r: Double, g: Double, b: Double, a: Double) = { this( | |
r.toFloat, // r | |
g.toFloat, // g | |
b.toFloat, // b | |
a.toFloat // a | |
)} | |
// Int: r, g, b | |
def this(r: Int, g: Int, b: Int) = { this( | |
(r/255.0).toFloat, // r | |
(g/255.0).toFloat, // g | |
(b/255.0).toFloat, // b | |
1.0f // a | |
)} | |
// Int: r, g, b, a | |
def this(r: Int, g: Int, b: Int, a: Int) = { this( | |
(r/255.0).toFloat, // r | |
(g/255.0).toFloat, // g | |
(b/255.0).toFloat, // b | |
(a/255.0).toFloat // a | |
)} | |
/// HSV constructors | |
// Double: h, s, v | |
def this(h: Double, s: Double, v: Double, isHSV: Boolean) = { this( | |
// r | |
if (isHSV) { | |
val rgb = Color.HSBtoRGB(h.toFloat, s.toFloat, v.toFloat); | |
(((rgb & 0x00ff0000) >> 16)/255.0).toFloat | |
} else { | |
h.toFloat | |
}, | |
// g | |
if (isHSV) { | |
val rgb = Color.HSBtoRGB(h.toFloat, s.toFloat, v.toFloat); | |
(((rgb & 0x0000ff00) >> 8)/255.0).toFloat | |
} else { | |
s.toFloat | |
}, | |
// b | |
if (isHSV) { | |
val rgb = Color.HSBtoRGB(h.toFloat, s.toFloat, v.toFloat); | |
(((rgb & 0x000000ff) )/255.0).toFloat | |
} else { | |
v.toFloat | |
}, | |
// a | |
1.0f | |
)} | |
// Int: h, s, v | |
def this(h: Int, s: Int, v: Int, isHSV: Boolean) = { this( | |
// r | |
if (isHSV) { | |
val hsv = Color.HSBtoRGB((h/255.0).toFloat, (s/255.0).toFloat, (v/255.0).toFloat); | |
(((hsv & 0x00ff0000) >> 16)/255.0).toFloat | |
} else { | |
(h/255.0).toFloat | |
}, | |
// g | |
if (isHSV) { | |
val hsv = Color.HSBtoRGB((h/255.0).toFloat, (s/255.0).toFloat, (v/255.0).toFloat); | |
(((hsv & 0x0000ff00) >> 8)/255.0).toFloat | |
} else { | |
(s/255.0).toFloat | |
}, | |
// b | |
if (isHSV) { | |
val hsv = Color.HSBtoRGB((h/255.0).toFloat, (s/255.0).toFloat, (v/255.0).toFloat); | |
(((hsv & 0x000000ff) )/255.0).toFloat | |
} else { | |
(v/255.0).toFloat | |
}, | |
// a | |
1.0f | |
)} | |
/// HEX constructors | |
// String: hex | |
def this(hex: String) = { this( | |
(Integer.parseInt(hex.substring(0,2), 16)/255.0).toFloat, // r | |
(Integer.parseInt(hex.substring(2,4), 16)/255.0).toFloat, // g | |
(Integer.parseInt(hex.substring(4,6), 16)/255.0).toFloat, // b | |
1.0f // a | |
)} | |
/// DEC constructors | |
// Int: rgba | |
def this(rgba: Int, hasalpha: Boolean) = { this( | |
(((rgba & 0x00ff0000) >> 16)/255.0).toFloat, // r | |
(((rgba & 0x0000ff00) >> 8)/255.0).toFloat, // g | |
(((rgba & 0x000000ff) )/255.0).toFloat, // b | |
if (hasalpha) | |
(((rgba & 0xff000000) >> 24) & 0x000000ff).toFloat // a | |
else | |
1.0f | |
)} | |
// Int: rgb | |
def this(rgb: Int) = { this( | |
(((rgb & 0x00ff0000) >> 16)/255.0).toFloat, // r | |
(((rgb & 0x0000ff00) >> 8)/255.0).toFloat, // g | |
(((rgb & 0x000000ff) )/255.0).toFloat, // b | |
1.0f // a | |
)} | |
///--------------------------------------------------------------------------- | |
/// methods: different representations of this color | |
///--------------------------------------------------------------------------- | |
def asRGB: List[Int] = { | |
List((r*255).toInt, (g*255).toInt, (b*255).toInt) | |
} | |
def asHEX: String = { | |
val rgb = this.asRGB | |
String.format("%2s%2s%2s", | |
rgb(0).toHexString.toUpperCase, | |
rgb(1).toHexString.toUpperCase, | |
rgb(2).toHexString.toUpperCase).replace(' ','0') | |
} | |
def asHSV: List[Double] = { | |
val rgb = this.asRGB | |
Color.RGBtoHSB(rgb(0), rgb(1), rgb(2), null).to[List].map(_.toDouble) | |
} | |
def asDEC: Int = { | |
val hex = this.asHEX | |
Integer.parseInt(hex, 16) | |
} | |
///--------------------------------------------------------------------------- | |
/// methods: gradients | |
///--------------------------------------------------------------------------- | |
// forward and backward gradients through HSV space | |
def gradientHSV(that: SColor, nSteps: Int = 10, inverse: Boolean = false): List[String] = { | |
val (color1, color2) = (this.asHSV, that.asHSV) | |
var (hStart, hEnd, hRange) = (color1(0), color2(0), color2(0)-color1(0)) | |
val (sStart, sEnd, sRange) = (color1(1), color2(1), color2(1)-color1(1)) | |
val (vStart, vEnd, vRange) = (color1(2), color2(2), color2(2)-color1(2)) | |
if (!inverse) hRange -= signum(hRange) | |
((0 to nSteps).map { n => // + hDir* | |
new SColor(hStart + hRange*n/nSteps, | |
sStart + sRange*n/nSteps, | |
vStart + vRange*n/nSteps, true).asHEX }).toList | |
} | |
// gradient through RGB space | |
def gradientRGB(that: SColor, nSteps: Int = 10): List[String] = { | |
val (color1, color2) = (this.asRGB, that.asRGB) | |
val (rStart, rEnd, rRange) = (color1(0), color2(0), color2(0)-color1(0)) | |
val (gStart, gEnd, gRange) = (color1(1), color2(1), color2(1)-color1(1)) | |
val (bStart, bEnd, bRange) = (color1(2), color2(2), color2(2)-color1(2)) | |
((0 to nSteps).map { n => | |
new SColor(rStart + rRange*n/nSteps, | |
gStart + gRange*n/nSteps, | |
bStart + bRange*n/nSteps).asHEX }).toList | |
} | |
} | |
object SColorTest { | |
def main(args: Array[String]){ | |
val cola = new SColor(0.3, 0.5, 0.5) // use (r: Double, g: Double, b: Double) constructor | |
val colb = new SColor(127, 255, 0) // use (r: Int, g: Int, b: Int) constructor | |
val colc = new SColor(0.9, 0.8, 0.6, true) // use (h: Double, s: Double, v: Double) constructor | |
val cold = new SColor(34, 125, 88, true) // use (h: Int, s: Int, v: Int) constructor | |
val cole = new SColor("4C7F7F") // use "HEXCODE" constructor | |
val colf = new SColor(8388352) // use (rgb: Int) constructor | |
val colg = new SColor(153,31,104) // use (r: Int, g: Int, b: Int) constructor | |
println(cola.asHEX) | |
println(colb.asDEC) | |
println(cole.asRGB) | |
println(colf.asRGB) | |
println(colc.asRGB) | |
println(colg.asHSV) | |
/// color gradients | |
println(""); (new SColor(Color.blue)).gradientRGB(new SColor(Color.red)).foreach(println) | |
println(""); (new SColor(Color.blue)).gradientHSV(new SColor(Color.red)).foreach(println) | |
println(""); (new SColor(Color.blue)).gradientHSV(new SColor(Color.red), inverse=true).foreach(println) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment