Skip to content

Commit

Permalink
Refactoring - do not relay on value convention, check RGB type
Browse files Browse the repository at this point in the history
  • Loading branch information
jpsacha committed Mar 24, 2023
1 parent cbbf251 commit 4adaa05
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 88 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Image/J Plugins
* Copyright (C) 2002-2022 Jarek Sacha
* Copyright (C) 2002-2023 Jarek Sacha
* Author's email: jpsacha at gmail dot com
*
* This library is free software; you can redistribute it and/or
Expand Down Expand Up @@ -249,17 +249,23 @@ enum RGBWorkingSpace(
gamma = 2.2
)

/** sRGB */
/**
* sRGB
*
* Note: The gamma of sRGB is not exactly 2.2, but rather, is a grafting together of two different functions,
* that when viewed together, may be approximated by a simple 2.2 gamma curve.
* See [[http://www.brucelindbloom.com/index.html?WorkingSpaceInfo.html Bruce Lindbloom's RGB Working Space Information]]
*/
case sRGB
extends RGBWorkingSpace(
"sRGB",
xR = 0.64,
yR = 0.33,
xG = 0.30,
yG = 0.60,
xB = 0.15,
yB = 0.06,
refWhite = D65,
extends RGBWorkingSpace(
"sRGB",
xR = 0.64,
yR = 0.33,
xG = 0.30,
yG = 0.60,
xB = 0.15,
yB = 0.06,
refWhite = D65,
gamma = -2.2
)

Expand Down Expand Up @@ -307,48 +313,6 @@ enum RGBWorkingSpace(
).transpose

val xyz2rgb: Matrix3x3 = rgb2xyz.inverse

/** Convert a value in this RGB color space to XYZ with the same reference white. */
def convertRGB2XYZ(r: Double, g: Double, b: Double): XYZ = {
val r1 = invCompand(r)
val g1 = invCompand(g)
val b1 = invCompand(b)

val x = r1 * rgb2xyz.m00 + g1 * rgb2xyz.m10 + b1 * rgb2xyz.m20
val y = r1 * rgb2xyz.m01 + g1 * rgb2xyz.m11 + b1 * rgb2xyz.m21
val z = r1 * rgb2xyz.m02 + g1 * rgb2xyz.m12 + b1 * rgb2xyz.m22

XYZ(x, y, z)
}

final private def invCompand(companded: Double): Double = {
if (gamma > 0.0) {
if (companded >= 0.0) pow(companded, gamma) else -pow(-companded, gamma)
} else if (gamma < 0.0) {
/* sRGB */
val (c, sign) =
if (companded < 0.0) {
(-companded, -1.0d)
} else {
(companded, 1.0d)
}
sign * (if (c <= 0.04045) c / 12.92 else pow((c + 0.055) / 1.055, 2.4))
} else {
/* L* */
val (c, sign) =
if (companded < 0.0) {
(-companded, -1)
} else {
(companded, 1)
}
sign * (if (c <= 0.08) {
2700.0 * companded / 24389.0
} else {
(((1000000.0 * c + 480000.0) * c + 76800.0) * c + 4096.0) / 1560896.0
})
}
}

}

object RGBWorkingSpace extends WithNameCompanion[RGBWorkingSpace]
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Image/J Plugins
* Copyright (C) 2002-2021 Jarek Sacha
* Copyright (C) 2002-2023 Jarek Sacha
* Author's email: jpsacha at gmail dot com
*
* This library is free software; you can redistribute it and/or
Expand Down Expand Up @@ -29,9 +29,9 @@ import scala.math.pow
/** Color conversion constants */
object ColorConverter {

val kE: Double = 216.0 / 24389.0
val kK: Double = 24389.0 / 27.0
val kKE: Double = 8.0
private val kE: Double = 216.0 / 24389.0
private val kK: Double = 24389.0 / 27.0
private val kKE: Double = 8.0
}

/**
Expand Down Expand Up @@ -220,57 +220,72 @@ final class ColorConverter(

/** Create copy of this object with a modified field. */
def copyWith(
refWhite: ReferenceWhite = refWhite,
rgbSpace: RGBWorkingSpace = rgbSpace,
chromaticAdaptation: Option[ChromaticAdaptation] = chromaticAdaptation,
rgbScale: Double = rgbScale,
xyzScale: Double = xyzScale
): ColorConverter = {
refWhite: ReferenceWhite = refWhite,
rgbSpace: RGBWorkingSpace = rgbSpace,
chromaticAdaptation: Option[ChromaticAdaptation] = chromaticAdaptation,
rgbScale: Double = rgbScale,
xyzScale: Double = xyzScale
): ColorConverter = {
new ColorConverter(refWhite, rgbSpace, chromaticAdaptation, rgbScale, xyzScale)
}

/**
* De-linearize
*
* @param linear linear value
* @return
*/
private def compand(linear: Double): Double = {
rgbSpace match {
case RGBWorkingSpace.sRGB =>
assert(rgbSpace.gamma < 0)
val (l, sign) = if (linear < 0.0) (-linear, -1.0) else (linear, 1.0)
val c = if (l <= 0.0031308) l * 12.92 else 1.055 * math.pow(l, 1.0 / 2.4) - 0.055
val c = if (l <= 0.0031308) l * 12.92 else 1.055 * math.pow(l, 1.0 / 2.4) - 0.055
c * sign
case RGBWorkingSpace.ECIRGBv2 =>
assert(rgbSpace.gamma == 0)
val (l, sign) = if (linear < 0.0) (-linear, -1.0) else (linear, 1.0)
val c = if (l <= (216.0 / 24389.0)) l * 24389.0 / 2700.0 else 1.16 * math.pow(l, 1.0 / 3.0) - 0.16
val c = if (l <= (216.0 / 24389.0)) l * 24389.0 / 2700.0 else 1.16 * math.pow(l, 1.0 / 3.0) - 0.16
c * sign
case _ =>
assert(rgbSpace.gamma > 0)
if (linear >= 0.0) math.pow(linear, 1.0 / rgbSpace.gamma) else -math.pow(-linear, 1.0 / rgbSpace.gamma)
}
}

/**
* Linearize
*
* @param companded de-linearized valuer
* @return
*/
private def invCompand(companded: Double): Double =
if (rgbSpace.gamma > 0.0) {
if (companded >= 0.0) pow(companded, rgbSpace.gamma) else -pow(-companded, rgbSpace.gamma)
} else if (rgbSpace.gamma < 0.0) {
/* sRGB */
val (c, sign) =
if (companded < 0.0) {
(-companded, -1.0d)
} else {
(companded, 1.0d)
}
sign * (if (c <= 0.04045) c / 12.92 else pow((c + 0.055) / 1.055, 2.4))
} else {
/* L* */
val (c, sign) =
if (companded < 0.0) {
(-companded, -1)
rgbSpace match {
case RGBWorkingSpace.sRGB =>
assert(rgbSpace.gamma < 0)
val (c, sign) =
if (companded < 0.0) {
(-companded, -1.0d)
} else {
(companded, 1.0d)
}
sign * (if (c <= 0.04045) c / 12.92 else pow((c + 0.055) / 1.055, 2.4))
case RGBWorkingSpace.ECIRGBv2 =>
assert(rgbSpace.gamma == 0)
/* L* */
val (c, sign) =
if (companded < 0.0) {
(-companded, -1)
} else {
(companded, 1)
}
sign * (if (c <= 0.08) {
2700.0 * companded / 24389.0
} else {
(companded, 1)
}
sign * (if (c <= 0.08) {
2700.0 * companded / 24389.0
} else {
(((1000000.0 * c + 480000.0) * c + 76800.0) * c + 4096.0) / 1560896.0
})
(((1000000.0 * c + 480000.0) * c + 76800.0) * c + 4096.0) / 1560896.0
})
case _ =>
assert(rgbSpace.gamma > 0)
if (companded >= 0.0) pow(companded, rgbSpace.gamma) else -pow(-companded, rgbSpace.gamma)
}
}

0 comments on commit 4adaa05

Please sign in to comment.