diff --git a/src/groovylight/gamma.py b/src/groovylight/gamma.py new file mode 100644 index 0000000..f314678 --- /dev/null +++ b/src/groovylight/gamma.py @@ -0,0 +1,52 @@ +# Gamma correction by adjusting the display OE/Expose timings. + + +# Most gamma correction is done on the values being displayed. +# i.e gammacorrect (RGB) -> RGB (adjusted). However this adds +# a complex look-up step which adds complexity and cycles. +# There is a simpler solution which uses some properties of the +# gamma function as well as the fact that we are manually doing +# color depth using BCM/PWM. +# +# Consider the default BCM timing layout: +# MSB MSB-1 MSB-2 MSB-3 +# P*8 P*4 P*2 P +# that is, we have a baseline display, measured in clocks/us/whatever +# and then the next most significant bit is displayed for twice that, +# four times that, and so on. +# But, we can adjust the individual bit timings to adjust the brightness +# curve as we see fit. This has numerous advantages: +# 1. It's free, we don't have to do any math on the board, just adjusting +# an existing process. +# 2. We can go more granular that n-bits of color. This means that the gamma +# curve will be effective and accurate regardless of the color depth. +# +# This file contains code to generate these timing adjustments and +# control/quantify them. + +from math import pow + + + +def _gammavec(vals: [float], g: float) -> [float]: + return [pow(x,g) for x in vals] + +def _nbit_scale(f, nbits:int) -> [float]: + """Computes the equivalent linear value for each bit of n_bits. + That is, the list contains scalar values that are doubling as they progress, + [ x, 2x, 4x ] such that the sum(list) = 7x = f + """ + base = float(f) / (pow(2.0, nbits) - 1.0) + return [base * pow(2.0, x) for x in range(nbits)] + + +def gamma_timings(gamma:float = 2.2, nbits:int = 8, max_clocks: int = 4096): + """Computes the clock cycle timings for a given gamma correction. + """ + linear_values = _nbit_scale(1.0, nbits) + gamma_values = _gammavec(linear_values, gamma) + + bclk_ratio = max_clocks / gamma_values[-1] + result = [round(bclk_ratio * x) for x in gamma_values] + return result +