From 1a46ef348e09ab5d5ef500186f0caff4259a5a0a Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Thu, 30 Dec 2021 14:53:35 +0100 Subject: [PATCH] implement sRGB wrapper Signed-off-by: Elias Naur --- LICENSE | 24 +++++++++++++++++++ go.mod | 5 ++++ go.sum | 4 ++++ srgb.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+) create mode 100644 LICENSE create mode 100644 go.mod create mode 100644 go.sum create mode 100644 srgb.go diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..efb9808 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..40cb516 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module eliasnaur.com/colorspace + +go 1.17 + +require golang.org/x/image v0.0.0-20211028202545-6944b10bf410 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..71c2453 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/srgb.go b/srgb.go new file mode 100644 index 0000000..a1a6a07 --- /dev/null +++ b/srgb.go @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: Unlicense + +// Package colorspace implements color space conversion wrappers +// compatible with image.Image. +// +// It is a proof of concept for https://github.com/golang/go/issues/11420. +package colorspace + +import ( + "image" + "image/color" + "math" + + "golang.org/x/image/draw" +) + +// SRGBA wraps an Image encoded in the sRGB color space. +type SRGBA struct { + image.Image +} + +func NewSRGBA(img image.Image) *SRGBA { + return &SRGBA{img} +} + +// At converts an sRGB color from the underlying image to linear RGB. +func (p *SRGBA) At(x, y int) color.Color { + c := p.Image.At(x, y) + r, g, b, a := c.RGBA() + ret := color.RGBA64{ + uint16(sRGBToLinear(float32(r)/0xffff)*0xffff + .5), + uint16(sRGBToLinear(float32(g)/0xffff)*0xffff + .5), + uint16(sRGBToLinear(float32(b)/0xffff)*0xffff + .5), + uint16(a), + } + return ret +} + +// Set converts a linear RGB color to sRGB and stores it in the underlying image. +func (p *SRGBA) Set(x, y int, c color.Color) { + r, g, b, a := c.RGBA() + srgb := color.RGBA{ + uint8(linearTosRGB(float32(r)/0xffff)*0xff + .5), + uint8(linearTosRGB(float32(g)/0xffff)*0xff + .5), + uint8(linearTosRGB(float32(b)/0xffff)*0xff + .5), + uint8(a >> 8), + } + p.Image.(draw.Image).Set(x, y, srgb) +} + +func linearTosRGB(c float32) float32 { + // Formula from EXT_sRGB. + switch { + case c <= 0: + return 0 + case 0 < c && c < 0.0031308: + return 12.92 * c + case 0.0031308 <= c && c < 1: + return 1.055*float32(math.Pow(float64(c), 0.41666)) - 0.055 + } + return 1 +} + +func sRGBToLinear(c float32) float32 { + // Formula from EXT_sRGB. + if c <= 0.04045 { + return c / 12.92 + } else { + return float32(math.Pow(float64((c+0.055)/1.055), 2.4)) + } +} -- 2.45.2