1

I am trying to follow along with this:

to be able to mix up to c0 the c1 must be less or equal to c0 on per channel bases. So for example random lesser color:

r1 = (Random(256)*r0)>>8;
g1 = (Random(256)*g0)>>8;
b1 = (Random(256)*b0)>>8;

So say you have a color 00aa00. You want to know what colors mix to form this color. The way this answer does it is by generating a random initial color (the "first mixing value") that has all RGB channels less than the 00aa00 input/start value. But I would like to have more control over the first mixing value. I would like to let's say generate a palette of colors, say using chroma.js so the colors blend together nicely. Then I would use one of those colors as the first mixing value, and follow the equations to get the second mixing value that combines to make 00aa00. Simple as that.

But the problem is I don't know how to figure out which colors from my palette will have RGB channels that are all less than the input value. I can use a library like tinycolor to convert hex to RGB no problem, but I don't know how to define the palette of colors I'm using in such a way that they all (or some of them at least) fit the pattern that all their RGB channels are less than the input value's.

Wondering if one could demonstrate how to do this or explain how it works. How to go about generating a color palette in such a way that all or most of the colors in it fit the constraint that their RGB channels are all less than the input value's RGB channels.

Lance
  • 75,200
  • 93
  • 289
  • 503
  • How about just one color from a color palette, I don't see how. – Lance Jun 12 '19 at 07:09
  • @LancePollard I added sorted palette code and preview ... only the `c1` is sorted of coarse ... looks like Fraunhofer's lines on top of [visible spectra](https://stackoverflow.com/a/22681410/2521214) :) – Spektre Jun 12 '19 at 11:16

1 Answers1

1

the easiest is to directly map an ordinal index to a color c1 ...

For all colors just use increment (like increment on bignum arithmetics where channel is a digit).

If yo want jut some of the colors then compute the sequenced modulo of each used channel usable values count ... something like this in C++/VCL:

//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "win_main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
int xs=0,ys=0;                  // resolution
Graphics::TBitmap *bmp=NULL;    // screen back buffer
//---------------------------------------------------------------------------
const int _r=2; // channel order
const int _g=1;
const int _b=0;
const int _a=3;
union color
    {
    BYTE db[4]; // channel access
    DWORD dd;   // all 32 bit of color
    };
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
    {
    bmp=new Graphics::TBitmap;
    bmp->HandleType=bmDIB;
    bmp->PixelFormat=pf32bit;
    sb_rgbChange(this);
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::sb_rgbChange(TObject *Sender)
    {
    if ((!xs)||(!ys)) return;   // not initialized window yet?
    int i,n,x,y,yy,sz=10;
    DWORD **p,a;
    color c0,c1,c2,c;
    // direct pixel access
    p=new DWORD*[ys];
    for (y=0;y<ys;y++) p[y]=(DWORD*)bmp->ScanLine[y];
    bmp->Canvas->Font->Height=sz;
    bmp->Canvas->Font->Color=clWhite;
    bmp->Canvas->Brush->Style=bsClear;
    // clear screen
    for (y=0;y<ys;y++)
     for (x=0;x<xs;x++)
      p[y][x]=0;
    x=0; y=sz;
    // get c0 color from R,G,B sliders
    c0.db[_r]=255-sb_r->Position;
    c0.db[_g]=255-sb_g->Position;
    c0.db[_b]=255-sb_b->Position;
    c0.db[_a]=0;
    Caption=AnsiString().sprintf("Target color: %08Xh",c0.dd);
    // additive
    n=(c0.db[_r]+1)*(c0.db[_g]+1)*(c0.db[_b]+1);    // c1 all usable colors
    for (x=0;x<xs;x++)                              // chose xs colors from n
        {
        // colors
        a=x*(n-1)/(xs-1);                           // compute c1 as conversion x -> R,G,B
        c1.db[_a]=0;
        c1.db[_r]=a%(c0.db[_r]+1); a/=(c0.db[_r]+1);
        c1.db[_g]=a%(c0.db[_g]+1); a/=(c0.db[_g]+1);
        c1.db[_b]=a%(c0.db[_b]+1); a/=(c0.db[_b]+1);
        for (i=0;i<3;i++) c2.db[i]=c0.db[i]-c1.db[i]; c2.db[_a]=0;  // compute c2 = c0 - c1
        for (i=0;i<3;i++)  c.db[i]=c1.db[i]+c2.db[i];  c.db[_a]=0;  // verify  c  = c1 + c2
        // render to bitmap
        yy=y;                                       // remember last y
        bmp->Canvas->TextOutA(xs>>1,y+sz,"+");
        for (i=0;(y<ys)&&(i<sz);y++,i++) p[y][x]=c1.dd;     y+=sz;
        bmp->Canvas->TextOutA(xs>>1,y+sz,"=");
        for (i=0;(y<ys)&&(i<sz);y++,i++) p[y][x]=c2.dd;     y+=sz;
        for (i=0;(y<ys)&&(i<sz);y++,i++) p[y][x]=c .dd;     y+=sz;
        y=yy;                                       // restore last y
        }
    y+=7*sz;

    // substractive
    n=(256-c0.db[_r])*(256-c0.db[_g])*(256-c0.db[_b]);  // c1 all usable colors
    for (x=0;x<xs;x++)                              // chose xs colors from n
        {
        // colors
        a=x*(n-1)/(xs-1);                           // compute c1 as conversion x -> R,G,B
        c1.db[_a]=0;
        c1.db[_r]=c0.db[_r]+(a%(c0.db[_r]+1)); a/=(c0.db[_r]+1);
        c1.db[_g]=c0.db[_g]+(a%(c0.db[_g]+1)); a/=(c0.db[_g]+1);
        c1.db[_b]=c0.db[_b]+(a%(c0.db[_b]+1)); a/=(c0.db[_b]+1);
        for (i=0;i<3;i++) c2.db[i]=c1.db[i]-c0.db[i]; c2.db[_a]=0;  // compute c2 = c1 - c0
        for (i=0;i<3;i++)  c.db[i]=c1.db[i]-c2.db[i];  c.db[_a]=0;  // verify  c  = c1 - c2
        // render to bitmap
        yy=y;                                       // remember last y
        bmp->Canvas->TextOutA(xs>>1,y+sz,"-");
        for (i=0;(y<ys)&&(i<sz);y++,i++) p[y][x]=c1.dd;     y+=sz;
        bmp->Canvas->TextOutA(xs>>1,y+sz,"=");
        for (i=0;(y<ys)&&(i<sz);y++,i++) p[y][x]=c2.dd;     y+=sz;
        for (i=0;(y<ys)&&(i<sz);y++,i++) p[y][x]=c .dd;     y+=sz;
        y=yy;                                       // restore last y
        }
    y+=7*sz;

    bmp->Canvas->Brush->Style=bsSolid;
    Canvas->Draw(0,0,bmp);
    delete[] p;
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
    {
    xs=ClientWidth-pan_right->Width;
    ys=ClientHeight;
    bmp->SetSize(xs,ys);
    sb_rgbChange(this);
    }
//---------------------------------------------------------------------------

The code is based on my answer to your previous question (linked in OP). So instead of random I chose xs (width of rendered palette) from all the usable colors linearly:

screenshot

As you can see algo works correctly.

The idea is to compute how many usable colors there is into n variable and then any i-th color form <0,n-1> is just modulo of used values per each channel (so r0,g0,b0 or 256-r0,...)

You know if you want pack 3 integer variables x,y,z into one where

x = <0,xs)
y = <0,ys)
z = <0,zs)

then:

w = x + xs*y + xs*ys*z

and back:

x = w%xs
y = (w/xs)%ys
z = (w/(xs*ys))%zs

PS. If you want to better sort the colors you can convert chosen to HSV and sort by value,saturation,hue (that order looks best to me ... hue must be sorted last !!!) like this:

HSV sorted

//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "win_main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
int xs=0,ys=0;                  // resolution
Graphics::TBitmap *bmp=NULL;    // screen back buffer
//---------------------------------------------------------------------------
const int _r=2; // channel order
const int _g=1;
const int _b=0;
const int _a=3;
const int _h=2;
const int _s=1;
const int _v=0;
union color
    {
    BYTE db[4]; // channel access
    DWORD dd;   // all 32 bit of color
    };
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void rgb2bgr(color &c)
    {
    BYTE a;
    a                =c.db[_r];
    c.db[_r]=c.db[_b];
    c.db[_b]=a;
    }
//---------------------------------------------------------------------------
void rgb2hsv(color &c)
    {
    double r,g,b,min,max,del,h,s,v,dr,dg,db;
    r=c.db[_r]; r/=255.0;
    g=c.db[_g]; g/=255.0;
    b=c.db[_b]; b/=255.0;
    min=r; if (min>g) min=g; if(min>b) min=b;
    max=r; if (max<g) max=g; if(max<b) max=b;
    del=max-min;
    v=max;
    if (del<=1e-10) { h=0; s=0; }   // grayscale
    else{
        s=del/max;
        dr=(((max-r)/6.0)+(del/2.0))/del;
        dg=(((max-g)/6.0)+(del/2.0))/del;
        db=(((max-b)/6.0)+(del/2.0))/del;
        if      (fabs(r-max)<1e-10) h=db-dg;
        else if (fabs(g-max)<1e-10) h=(1.0/3.0)+dr-db;
        else if (fabs(b-max)<1e-10) h=(2.0/3.0)+dg-dr;
        if (h<0.0) h+=1.0;
        if (h>1.0) h-=1.0;
        }
    c.db[_h]=h*255.0;
    c.db[_s]=s*255.0;
    c.db[_v]=v*255.0;
    }
//---------------------------------------------------------------------------
void hsv2rgb(color &c)
    {
    int i;
    double r,g,b,h,s,v,vh,v1,v2,v3,f;
    h=c.db[_h]; h/=255.0;
    s=c.db[_s]; s/=255.0;
    v=c.db[_v]; v/=255.0;
    if (s<=1e-10) { r=v; g=v; b=v; }    // grayscale
    else{
        vh=h*6.0;
        if (vh>=6.0) vh=0.0;
        f=floor(vh); i=f;
        v1=v*(1.0-s);
        v2=v*(1.0-s*(    vh-f));
        v3=v*(1.0-s*(1.0-vh+f));
             if (i==0) { r=v ; g=v3; b=v1; }
        else if (i==1) { r=v2; g=v ; b=v1; }
        else if (i==2) { r=v1; g=v ; b=v3; }
        else if (i==3) { r=v1; g=v2; b=v ; }
        else if (i==4) { r=v3; g=v1; b=v ; }
        else           { r=v ; g=v1; b=v2; }
        }
    c.db[_r]=r*255.0;
    c.db[_g]=g*255.0;
    c.db[_b]=b*255.0;
    }
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
    {
    bmp=new Graphics::TBitmap;
    bmp->HandleType=bmDIB;
    bmp->PixelFormat=pf32bit;
    sb_rgbChange(this);
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::sb_rgbChange(TObject *Sender)
    {
    if ((!xs)||(!ys)) return;   // not initialized window yet?
    int i,n,x,y,yy,sz=10;
    DWORD **p,a;
    color c0,c1,c2,c;
    color *pal=new color[xs];   // palette size equal to rendered window width
    // direct pixel access
    p=new DWORD*[ys];
    for (y=0;y<ys;y++) p[y]=(DWORD*)bmp->ScanLine[y];
    bmp->Canvas->Font->Height=sz;
    bmp->Canvas->Font->Color=clWhite;
    bmp->Canvas->Brush->Style=bsClear;
    // clear screen
    for (y=0;y<ys;y++)
     for (x=0;x<xs;x++)
      p[y][x]=0;
    x=0; y=sz;
    // get c0 color from R,G,B sliders
    c0.db[_r]=255-sb_r->Position;
    c0.db[_g]=255-sb_g->Position;
    c0.db[_b]=255-sb_b->Position;
    c0.db[_a]=0;
    Caption=AnsiString().sprintf("Target color: %08Xh",c0.dd);
    // [additive]
    // compute c1 into pal[]
    n=(c0.db[_r]+1)*(c0.db[_g]+1)*(c0.db[_b]+1);    // c1 all usable colors
    for (x=0;x<xs;x++)                              // chose xs colors from n
        {
        a=x*(n-1)/(xs-1);                           // compute c1 as conversion x -> R,G,B
        c1.db[_a]=0;
        c1.db[_r]=a%(c0.db[_r]+1); a/=(c0.db[_r]+1);
        c1.db[_g]=a%(c0.db[_g]+1); a/=(c0.db[_g]+1);
        c1.db[_b]=a%(c0.db[_b]+1); a/=(c0.db[_b]+1);
        pal[x]=c1;
        }

    // sort pal (_h must be last to sort by)
    for (x=0;x<xs;x++) rgb2hsv(pal[x]);
    i=_v; for (yy=1,n=xs;(n>=2)&&(yy);n--) for (yy=0,x=1;x<n;x++) if (pal[x].db[i]>pal[x-1].db[i]) { c=pal[x]; pal[x]=pal[x-1]; pal[x-1]=c; yy=1; }
    i=_s; for (yy=1,n=xs;(n>=2)&&(yy);n--) for (yy=0,x=1;x<n;x++) if (pal[x].db[i]>pal[x-1].db[i]) { c=pal[x]; pal[x]=pal[x-1]; pal[x-1]=c; yy=1; }
    i=_h; for (yy=1,n=xs;(n>=2)&&(yy);n--) for (yy=0,x=1;x<n;x++) if (pal[x].db[i]>pal[x-1].db[i]) { c=pal[x]; pal[x]=pal[x-1]; pal[x-1]=c; yy=1; }
    for (x=0;x<xs;x++) hsv2rgb(pal[x]);

    // render c1, c2 and c1+c2
    bmp->Canvas->TextOutA(xs>>1,y+1*sz,"+");
    bmp->Canvas->TextOutA(xs>>1,y+3*sz,"=");
    for (x=0;x<xs;x++)                              // chose xs colors from n
        {
        // colors
        c1=pal[x];
        for (i=0;i<3;i++) c2.db[i]=c0.db[i]-c1.db[i]; c2.db[_a]=0;  // compute c2 = c0 - c1
        for (i=0;i<3;i++)  c.db[i]=c1.db[i]+c2.db[i];  c.db[_a]=0;  // verify  c  = c1 + c2
        // render to bitmap
        yy=y;                                       // remember last y
        for (i=0;(y<ys)&&(i<sz);y++,i++) p[y][x]=c1.dd;     y+=sz;
        for (i=0;(y<ys)&&(i<sz);y++,i++) p[y][x]=c2.dd;     y+=sz;
        for (i=0;(y<ys)&&(i<sz);y++,i++) p[y][x]=c .dd;     y+=sz;
        y=yy;                                       // restore last y
        }
    y+=7*sz;

    // [substractive]
    // compute c1 into pal[]
    n=(256-c0.db[_r])*(256-c0.db[_g])*(256-c0.db[_b]);  // c1 all usable colors
    for (x=0;x<xs;x++)                                  // chose xs colors from n
        {
        a=x*(n-1)/(xs-1);                               // compute c1 as conversion x -> R,G,B
        c1.db[_a]=0;
        c1.db[_r]=c0.db[_r]+(a%(c0.db[_r]+1)); a/=(c0.db[_r]+1);
        c1.db[_g]=c0.db[_g]+(a%(c0.db[_g]+1)); a/=(c0.db[_g]+1);
        c1.db[_b]=c0.db[_b]+(a%(c0.db[_b]+1)); a/=(c0.db[_b]+1);
        pal[x]=c1;
        }

    // sort pal (_h must be last to sort by)
    for (x=0;x<xs;x++) rgb2hsv(pal[x]);
    i=_v; for (yy=1,n=xs;(n>=2)&&(yy);n--) for (yy=0,x=1;x<n;x++) if (pal[x].db[i]>pal[x-1].db[i]) { c=pal[x]; pal[x]=pal[x-1]; pal[x-1]=c; yy=1; }
    i=_s; for (yy=1,n=xs;(n>=2)&&(yy);n--) for (yy=0,x=1;x<n;x++) if (pal[x].db[i]>pal[x-1].db[i]) { c=pal[x]; pal[x]=pal[x-1]; pal[x-1]=c; yy=1; }
    i=_h; for (yy=1,n=xs;(n>=2)&&(yy);n--) for (yy=0,x=1;x<n;x++) if (pal[x].db[i]>pal[x-1].db[i]) { c=pal[x]; pal[x]=pal[x-1]; pal[x-1]=c; yy=1; }
    for (x=0;x<xs;x++) hsv2rgb(pal[x]);

    // render c1, c2 and c1-c2
    bmp->Canvas->TextOutA(xs>>1,y+1*sz,"-");
    bmp->Canvas->TextOutA(xs>>1,y+3*sz,"=");
    for (x=0;x<xs;x++)                              // chose xs colors from n
        {
        c1=pal[x];
        for (i=0;i<3;i++) c2.db[i]=c1.db[i]-c0.db[i]; c2.db[_a]=0;  // compute c2 = c1 - c0
        for (i=0;i<3;i++)  c.db[i]=c1.db[i]-c2.db[i];  c.db[_a]=0;  // verify  c  = c1 - c2
        // render to bitmap
        yy=y;                                       // remember last y
        for (i=0;(y<ys)&&(i<sz);y++,i++) p[y][x]=c1.dd;     y+=sz;
        for (i=0;(y<ys)&&(i<sz);y++,i++) p[y][x]=c2.dd;     y+=sz;
        for (i=0;(y<ys)&&(i<sz);y++,i++) p[y][x]=c .dd;     y+=sz;
        y=yy;                                       // restore last y
        }
    y+=7*sz;

    bmp->Canvas->Brush->Style=bsSolid;
    Canvas->Draw(0,0,bmp);
    delete[] p;
    delete[] pal;
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
    {
    xs=ClientWidth-pan_right->Width;
    ys=ClientHeight;
    bmp->SetSize(xs,ys);
    sb_rgbChange(this);
    }
//---------------------------------------------------------------------------

I used just bubble sort so if you want more speed use quick sort instead ...

Spektre
  • 49,595
  • 11
  • 110
  • 380