summaryrefslogtreecommitdiff
path: root/common/colors.cpp
blob: 0cc6042542f549151bea469c5a163626affaf865 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
 * Copyright (C) 2014 KiCad Developers, see CHANGELOG.TXT for contributors.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * or you may search the http://www.gnu.org website for the version 2 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

#include <colors.h>
#include <i18n_utility.h>


/**
 * The predefined colors used in KiCad.
 * Please: if you change a value, remember these values are carefully chosen
 * to have good results in Pcbnew, that uses the ORed value of basic colors
 * when displaying superimposed objects
 * This list must have exactly NBCOLORS items
 */

const StructColors g_ColorRefs[NBCOLORS] =
{
    { 0,    0,   0,   BLACK,         _HKI( "Black" ),     DARKDARKGRAY      },
    { 72,   72,  72,  DARKDARKGRAY,  _HKI( "Gray 1" ),    DARKGRAY          },
    { 132,  132, 132, DARKGRAY,      _HKI( "Gray 2" ),    LIGHTGRAY         },
    { 194,  194, 194, LIGHTGRAY,     _HKI( "Gray 3" ),    WHITE             },
    { 255,  255, 255, WHITE,         _HKI( "White" ),     WHITE             },
    { 194,  255, 255, LIGHTYELLOW,   _HKI( "L.Yellow" ),  WHITE             },
    { 72,   0,   0,   DARKBLUE,      _HKI( "Blue 1" ),    BLUE              },
    { 0,    72,  0,   DARKGREEN,     _HKI( "Green 1" ),   GREEN             },
    { 72,   72,  0,   DARKCYAN,      _HKI( "Cyan 1" ),    CYAN              },
    { 0,    0,   72,  DARKRED,       _HKI( "Red 1" ),     RED               },
    { 72,   0,   72,  DARKMAGENTA,   _HKI( "Magenta 1" ), MAGENTA           },
    { 0,    72,  72,  DARKBROWN,     _HKI( "Brown 1" ),   BROWN             },
    { 132,  0,   0,   BLUE,          _HKI( "Blue 2" ),    LIGHTBLUE         },
    { 0,    132, 0,   GREEN,         _HKI( "Green 2" ),   LIGHTGREEN        },
    { 132,  132, 0,   CYAN,          _HKI( "Cyan 2" ),    LIGHTCYAN         },
    { 0,    0,   132, RED,           _HKI( "Red 2" ),     LIGHTRED          },
    { 132,  0,   132, MAGENTA,       _HKI( "Magenta 2" ), LIGHTMAGENTA      },
    { 0,    132, 132, BROWN,         _HKI( "Brown 2" ),   YELLOW            },
    { 194,  0,   0,   LIGHTBLUE,     _HKI( "Blue 3" ),    PUREBLUE,         },
    { 0,    194, 0,   LIGHTGREEN,    _HKI( "Green 3" ),   PUREGREEN         },
    { 194,  194, 0,   LIGHTCYAN,     _HKI( "Cyan 3" ),    PURECYAN          },
    { 0,    0,   194, LIGHTRED,      _HKI( "Red 3" ),     PURERED           },
    { 194,  0,   194, LIGHTMAGENTA,  _HKI( "Magenta 3" ), PUREMAGENTA       },
    { 0,    194, 194, YELLOW,        _HKI( "Yellow 3" ),  PUREYELLOW        },
    { 255,  0,   0,   PUREBLUE,      _HKI( "Blue 4" ),    WHITE             },
    { 0,    255, 0,   PUREGREEN,     _HKI( "Green 4" ),   WHITE             },
    { 255,  255, 0,   PURECYAN,      _HKI( "Cyan 4" ),    WHITE             },
    { 0,    0,   255, PURERED,       _HKI( "Red 4" ),     WHITE             },
    { 255,  0,   255, PUREMAGENTA,   _HKI( "Magenta 4" ), WHITE             },
    { 0,    255, 255, PUREYELLOW,    _HKI( "Yellow 4" ),  WHITE             },
};


EDA_COLOR_T ColorByName( const wxString& aName )
{
    // look for a match in the palette itself
    for( EDA_COLOR_T trying = BLACK; trying < NBCOLORS; trying = NextColor(trying) )
    {
        if( 0 == aName.CmpNoCase( ColorGetName( trying ) ) )
            return trying;
    }

    // Not found, no idea...
    return UNSPECIFIED_COLOR;
}


bool ColorIsLight( EDA_COLOR_T aColor )
{
    const StructColors &c = g_ColorRefs[ColorGetBase( aColor )];
    int r = c.m_Red;
    int g = c.m_Green;
    int b = c.m_Blue;
    return ((r * r) + (g * g) + (b * b)) > (128 * 128 * 3);
}


EDA_COLOR_T ColorFindNearest( const wxColour &aColor )
{
    return ColorFindNearest( aColor.Red(), aColor.Green(), aColor.Blue() );
}


EDA_COLOR_T ColorFindNearest( int aR, int aG, int aB )
{
    EDA_COLOR_T candidate = BLACK;

    /* Find the 'nearest' color in the palette. This is fun. There is
       a gazilion of metrics for the color space and no one of the
       useful one is in the RGB color space. Who cares, this is a CAD,
       not a photosomething...

       I hereby declare that the distance is the sum of the square of the
       component difference. Think about the RGB color cube. Now get the
       euclidean distance, but without the square root... for ordering
       purposes it's the same, obviously. Also each component can't be
       less of the target one, since I found this currently work better...
       */
    int nearest_distance = 255 * 255 * 3 + 1; // Can't beat this

    for( EDA_COLOR_T trying = BLACK; trying < NBCOLORS; trying = NextColor(trying) )
    {
        const StructColors &c = g_ColorRefs[trying];
        int distance = (aR - c.m_Red) * (aR - c.m_Red) +
            (aG - c.m_Green) * (aG - c.m_Green) +
            (aB - c.m_Blue) * (aB - c.m_Blue);

        if( distance < nearest_distance && c.m_Red >= aR &&
            c.m_Green >= aG && c.m_Blue >= aB )
        {
            nearest_distance = distance;
            candidate = trying;
        }
    }

    return candidate;
}


EDA_COLOR_T ColorMix( EDA_COLOR_T aColor1, EDA_COLOR_T aColor2 )
{
    /* Memoization storage. This could be potentially called for each
     * color merge so a cache is useful (there are few colours anyway) */
    static EDA_COLOR_T mix_cache[NBCOLORS][NBCOLORS];

    // TODO how is alpha used? it's a mac only thing, I have no idea
    aColor1 = ColorGetBase( aColor1 );
    aColor2 = ColorGetBase( aColor2 );

    // First easy thing: a black gives always the other colour
    if( aColor1 == BLACK )
        return aColor2;

    if( aColor2 == BLACK)
        return aColor1;

    /* Now we are sure that black can't occur, so the rule is:
     * BLACK means not computed yet. If we're lucky we already have
     * an answer */
    EDA_COLOR_T candidate = mix_cache[aColor1][aColor2];

    if( candidate != BLACK )
        return candidate;

    // Blend the two colors (i.e. OR the RGB values)
    const StructColors &c1 = g_ColorRefs[aColor1];
    const StructColors &c2 = g_ColorRefs[aColor2];

    // Ask the palette for the nearest color to the mix
    wxColour mixed( c1.m_Red | c2.m_Red,
                    c1.m_Green | c2.m_Green,
                    c1.m_Blue | c2.m_Blue );
    candidate = ColorFindNearest( mixed );

    /* Here, BLACK is *not* a good answer, since it would recompute the next time.
     * Even theorically its not possible (with the current rules), but
     * maybe the metric will change in the future */
    if( candidate == BLACK )
        candidate = DARKDARKGRAY;

    // Store the result in the cache. The operation is commutative, too
    mix_cache[aColor1][aColor2] = candidate;
    mix_cache[aColor2][aColor1] = candidate;
    return candidate;
}