www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - colour lib

reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
I'm blowing the dust off my colour proposal implementation.
I want to get it into a state where it may be accepted into
std.experimental, but I'm having trouble deciding on the scope of the
initial offering. Some people are arguing for 'complete', and I tend
to argue for minimal/un-contentious. It can grow in the future as
desired.

There is a slightly blurry line separating a colour lib from an image
lib, and I want to focus the boundaries.

My design propositions are this:

1. Colour type definitely belongs in the std library;
  - Colour types and conversions are hard to implement, non-experts
almost always get it wrong.
  - Implementation is more-or-less un-contentious, I don't think
there's much room for debate in terms of API design.
  - It's an enabler for a lot of further library work.
2. Image processing probably doesn't belong in the std library, at
least, not right now;
  - There are so many ways the API could look, no particular one is
objectively 'correct'.
  - We need time for conventions to proliferate before API decisions
for the std lib can reasonably be decided.
3. I am kinda drawing the line between 'colour' and 'image' at the
point you find yourself working with buffers of data. Ie, 'colour' is
strictly  nogc.

So, I guess that means this clearly encompasses colour types,
primitive operations, and then typical functions like interpolation,
and conversion. I think that's the scope, and I'm happy with that
definition, but it raises some questions...

Colour defined this way without buffered enhancements will be
inefficient; performing single-colour operations in series is not
cool. Image operations tend to want support for simd, loop unrolling,
etc, for efficient image library implementations. I don't really feel
it's the place for an image library to re-define these efficient array
versions of colour operations... so, should they be in the base colour
library?

This problem actually extends beyond colours... D positions ranges as
a core language mechanic, and I've often struggled with this problem
that defining an element type and how it behaves and using it in a
range is frequently not an efficient implementation. It's the classic
OOP design fail, but dressed up differently.

What is the current wisdom with respect to implementing efficient
batch implementations for various element types?

I'm feeling like I should not attempt to address this problem in the
initial colour library and just leave it as simple as possible; just
the colour type. It can be used for inefficient purposes for now (and
produce *correct* results) and we can worry about efficient batch
processing in a follow up, or when an image library comes along?


I have another question too; some of the operations are algorithmic in
nature. Take lerp for example. Interpolation may appear in an
algorithm, which means we should have a standard API for interpolation
for all possible types, and modules just provide implementations for
the types they introduce. There should be default implementations in
phobos for builtin types.

Where does this belong? (I haven't found one to already exist)
I feel like `lerp(T, F)(T a, T b, F t) { return a*(1-t) + b*t; }`
belongs kinda near min/max... but std.algorithm.comparison doesn't
feel right. I think a small suite of functions like this exists.
Aug 30 2016
next sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
So I'm replying without your post, so I will put my thoughts here:

1. Trust your gut on this one, your the expert in the problem domain, 
we're not.
2. As part of your argument I personally would prefer that it is at 
least tested in the context of an image library. We don't want any 
hidden problems there even if that is not your goal to implement the 
best image library.
3. Assuming pragma(inline, true) does in fact work, as long as the 
different operations are made simple I see no reason why it would be any 
less efficient, after all surely e.g. gdc/ldc can optimize and vectorize it?
Aug 30 2016
parent Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 31 August 2016 at 15:01, rikki cattermole via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 2. As part of your argument I personally would prefer that it is at least
 tested in the context of an image library. We don't want any hidden problems
 there even if that is not your goal to implement the best image library.
Sure. I use it in my own image library all the time. I'm confident the basic implementation will work perfectly in the context of an image library, my concern is it won't be very efficient. It will however offer a 'correct' reference implementation for optimised image library development to work against, so I guess it's an important first offering however you look at it.
 3. Assuming pragma(inline, true) does in fact work, as long as the different
 operations are made simple I see no reason why it would be any less
 efficient, after all surely e.g. gdc/ldc can optimize and vectorize it?
Batch functions are so radically different than single operations, if an optimiser/vectoriser is able to even start to approach an efficient batch implementation, I'll be astonished. It'll need to be hand written at some stage, but we don't really have mechanisms for this sort of thing among D's existing range patterns.
Aug 30 2016
prev sibling parent reply Chris Wright <dhasenan gmail.com> writes:
The color models I'm aware of are HSV, HSL, RGB[A], CMYK, Lab, Pantone, 
and the Open Colour Standard.

I'm not sure what common operations they have. Perhaps you could provide 
a link to your existing library?
Aug 30 2016
parent reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 31 August 2016 at 16:01, Chris Wright via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 The color models I'm aware of are HSV, HSL, RGB[A], CMYK, Lab, Pantone,
 and the Open Colour Standard.
I'll initially support, XYZ/xyY, RGB (which is a gigantic set), HSx, Lab, Yuv.
 I'm not sure what common operations they have.
All colours can add, subtract, scale luminance.
 Perhaps you could provide a link to your existing library?
https://github.com/TurkeyMan/color
Aug 30 2016
parent reply Andrea Fontana <nospam example.com> writes:
On Wednesday, 31 August 2016 at 06:17:15 UTC, Manu wrote:
 On 31 August 2016 at 16:01, Chris Wright via Digitalmars-d 
 <digitalmars-d puremagic.com> wrote:
 The color models I'm aware of are HSV, HSL, RGB[A], CMYK, Lab, 
 Pantone, and the Open Colour Standard.
I'll initially support, XYZ/xyY, RGB (which is a gigantic set), HSx, Lab, Yuv.
 I'm not sure what common operations they have.
All colours can add, subtract, scale luminance.
 Perhaps you could provide a link to your existing library?
https://github.com/TurkeyMan/color
I always think the perfect colour library should work using a superset of all colour spaces, for example cie xyz (is it a superset, isn't it?). isColour(T) then IMO should check if x,y,z properties exists (or toXYZ() method). In this way every algorithm like "blend" or anything else could be implemented just for xyz (and eventually specialized for other colours if we want to avoid conversion overhead). In this way it become easy to do cross-colour operations (for example: apply a rgb mask over another color space) and to implement new or strange color spaces (that automagically will work with all "blend" & co.). To implement a new color spaces you just need to map it to xyz (that should represent all - and more - visible colours: rgb is a subset of visibile colours) Andrea
Aug 31 2016
next sibling parent reply Andrea Fontana <nospam example.com> writes:
On Wednesday, 31 August 2016 at 07:58:36 UTC, Andrea Fontana 
wrote:

 I always think the perfect colour library should work using a 
 superset of all colour spaces, for example cie xyz (is it a 
 superset, isn't it?). isColour(T) then IMO should check if 
 x,y,z properties exists (or toXYZ() method).

 In this way every algorithm like "blend" or anything else could 
 be implemented just for xyz (and eventually specialized for 
 other colours if we want to avoid conversion overhead). In this 
 way it become easy to do cross-colour operations (for example: 
 apply a rgb mask over another color space) and to implement new 
 or strange color spaces (that automagically will work with all 
 "blend" & co.). To implement a new color spaces you just need 
 to map it to xyz (that should represent all - and more - 
 visible colours: rgb is a subset of visibile colours)

 Andrea
I forgot that in this way it's quite easy to wrap external class/struct to make them works with library. If we have a custom COLOR class provided by another library we just need to write a toXYZ(COLOR c) method (if it doesnt provide x,y,z properties) and it works.
Aug 31 2016
parent Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 31 August 2016 at 18:04, Andrea Fontana via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On Wednesday, 31 August 2016 at 07:58:36 UTC, Andrea Fontana wrote:

 I forgot that in this way it's quite easy to wrap external class/struct to
 make them works with library. If we have a custom COLOR class provided by
 another library we just need to write a toXYZ(COLOR c) method (if it doesnt
 provide x,y,z properties) and it works.
It's done.
Aug 31 2016
prev sibling parent reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 31 August 2016 at 17:58, Andrea Fontana via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On Wednesday, 31 August 2016 at 06:17:15 UTC, Manu wrote:
 On 31 August 2016 at 16:01, Chris Wright via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 The color models I'm aware of are HSV, HSL, RGB[A], CMYK, Lab, Pantone,
 and the Open Colour Standard.
I'll initially support, XYZ/xyY, RGB (which is a gigantic set), HSx, Lab, Yuv.
 I'm not sure what common operations they have.
All colours can add, subtract, scale luminance.
 Perhaps you could provide a link to your existing library?
https://github.com/TurkeyMan/color
I always think the perfect colour library should work using a superset of all colour spaces, for example cie xyz (is it a superset, isn't it?). isColour(T) then IMO should check if x,y,z properties exists (or toXYZ() method).
Yeah... that would be wickedly slow.
 In this way every algorithm like "blend" or anything else could be
 implemented just for xyz (and eventually specialized for other colours if we
 want to avoid conversion overhead).
Basically the whole point of colour spaces is to do blends in different colour spaces. XYZ is not a great space for blending, xyY is functionally identical for blending as RGB, except that you need to do conversions on either side of the blend. RGB tend to be integer colour spaces, whereas XYZ/xyY are necessarily float colour spaces, which implies a lot of int<->float conversion, etc. RGB is not at all perceptually uniform, which is why colour spaces like Lab were invented; if you use Lab, then your intent is to blend in Lab. Same story for HSx spaces, you use them when you want to blend with a linear hue axis.
 In this way it become easy to do
 cross-colour operations (for example: apply a rgb mask over another color
 space) and to implement new or strange color spaces (that automagically will
 work with all "blend" & co.). To implement a new color spaces you just need
 to map it to xyz (that should represent all - and more - visible colours:
 rgb is a subset of visibile colours)
The meat of your idea is already implemented in my code there. The way it works is that you define your own custom colour type, and then add a 'sign post' member, ie, `alias ParentColour = XYZ!float;`, then you need to implement convertColor() for MyCustomColour -> XYZ and vice-versa. With that, your custom colour will be convertible to any other colour format; using a basic path-finding search (using the sign posts) it will find a path from any colour type to any other colour type, and perform the series of conversions required to get there. XYZ is a valid superset, so you can always rely on it as a centerpoint for colour conversions... but this is for conversion, not blending, which is the whole point of defining your own colour space.
Aug 31 2016
next sibling parent reply Andrea Fontana <nospam example.com> writes:
On Wednesday, 31 August 2016 at 09:33:24 UTC, Manu wrote:
 On 31 August 2016 at 17:58, Andrea Fontana via Digitalmars-d 
 <digitalmars-d puremagic.com> wrote:
 On Wednesday, 31 August 2016 at 06:17:15 UTC, Manu wrote:
 On 31 August 2016 at 16:01, Chris Wright via Digitalmars-d 
 <digitalmars-d puremagic.com> wrote:
 The color models I'm aware of are HSV, HSL, RGB[A], CMYK, 
 Lab, Pantone, and the Open Colour Standard.
I'll initially support, XYZ/xyY, RGB (which is a gigantic set), HSx, Lab, Yuv.
 I'm not sure what common operations they have.
All colours can add, subtract, scale luminance.
 Perhaps you could provide a link to your existing library?
https://github.com/TurkeyMan/color
I always think the perfect colour library should work using a superset of all colour spaces, for example cie xyz (is it a superset, isn't it?). isColour(T) then IMO should check if x,y,z properties exists (or toXYZ() method).
Yeah... that would be wickedly slow.
 In this way every algorithm like "blend" or anything else 
 could be implemented just for xyz (and eventually specialized 
 for other colours if we want to avoid conversion overhead).
Basically the whole point of colour spaces is to do blends in different colour spaces. XYZ is not a great space for blending, xyY is functionally identical for blending as RGB, except that you need to do conversions on either side of the blend. RGB tend to be integer colour spaces, whereas XYZ/xyY are necessarily float colour spaces, which implies a lot of int<->float conversion, etc. RGB is not at all perceptually uniform, which is why colour spaces like Lab were invented; if you use Lab, then your intent is to blend in Lab. Same story for HSx spaces, you use them when you want to blend with a linear hue axis.
 In this way it become easy to do
 cross-colour operations (for example: apply a rgb mask over 
 another color
 space) and to implement new or strange color spaces (that 
 automagically will
 work with all "blend" & co.). To implement a new color spaces 
 you just need
 to map it to xyz (that should represent all - and more - 
 visible colours:
 rgb is a subset of visibile colours)
The meat of your idea is already implemented in my code there. The way it works is that you define your own custom colour type, and then add a 'sign post' member, ie, `alias ParentColour = XYZ!float;`, then you need to implement convertColor() for MyCustomColour -> XYZ and vice-versa. With that, your custom colour will be convertible to any other colour format; using a basic path-finding search (using the sign posts) it will find a path from any colour type to any other colour type, and perform the series of conversions required to get there. XYZ is a valid superset, so you can always rely on it as a centerpoint for colour conversions... but this is for conversion, not blending, which is the whole point of defining your own colour space.
So maybe I miss (more than) something reading source code. You should write a readme to explain how it works :)
Aug 31 2016
parent Ethan Watson <gooberman gmail.com> writes:
On Wednesday, 31 August 2016 at 09:37:41 UTC, Andrea Fontana 
wrote:

 So maybe I miss (more than) something reading source code. You 
 should write a readme to explain how it works :)
I can probably chip in and help here at some point (both with documentation and ensuring the API is intuitive).
Aug 31 2016
prev sibling parent Chris Wright <dhasenan gmail.com> writes:
On Wed, 31 Aug 2016 19:33:24 +1000, Manu via Digitalmars-d wrote:
 On 31 August 2016 at 17:58, Andrea Fontana via Digitalmars-d
 I always think the perfect colour library should work using a superset
 of all colour spaces, for example cie xyz (is it a superset, isn't
 it?). isColour(T) then IMO should check if x,y,z properties exists (or
 toXYZ() method).
Yeah... that would be wickedly slow.
That depends on how often you incur the conversion cost. If you're storing your whole image in your custom color type and converting to read every pixel, that's not so great. If you have an existing image with one colorspace and a user wants to add text in a color defined in a different colorspace, you only need to convert once, after the user uses the color picker. If you have two images that you want to combine and they're using different colorspaces, you must incur that cost anyway, and the image library should support that. The color library shouldn't put up barriers.
Aug 31 2016