Committing in .
[wxWidgets.git] / src / motif / colour.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: colour.cpp
3 // Purpose: wxColour class
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 17/09/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 //// TODO: make wxColour a ref-counted object,
13 //// so pixel values get shared.
14
15 #ifdef __GNUG__
16 #pragma implementation "colour.h"
17 #endif
18
19 #include "wx/gdicmn.h"
20 #include "wx/colour.h"
21 #include "wx/app.h"
22
23 #ifdef __VMS__
24 #pragma message disable nosimpint
25 #endif
26 #include <Xm/Xm.h>
27 #ifdef __VMS__
28 #pragma message enable nosimpint
29 #endif
30
31 #include "wx/motif/private.h"
32
33 #if !USE_SHARED_LIBRARY
34 IMPLEMENT_DYNAMIC_CLASS(wxColour, wxObject)
35 #endif
36
37 // Colour
38
39 wxColour::wxColour ()
40 {
41 m_isInit = FALSE;
42 m_red = m_blue = m_green = 0;
43 m_pixel = -1;
44 }
45
46 wxColour::wxColour (unsigned char r, unsigned char g, unsigned char b)
47 {
48 m_red = r;
49 m_green = g;
50 m_blue = b;
51 m_isInit = TRUE;
52 m_pixel = -1;
53 }
54
55 wxColour::wxColour (const wxColour& col)
56 {
57 m_red = col.m_red;
58 m_green = col.m_green;
59 m_blue = col.m_blue;
60 m_isInit = col.m_isInit;
61 m_pixel = col.m_pixel;
62 }
63
64 wxColour& wxColour::operator =(const wxColour& col)
65 {
66 m_red = col.m_red;
67 m_green = col.m_green;
68 m_blue = col.m_blue;
69 m_isInit = col.m_isInit;
70 m_pixel = col.m_pixel;
71 return *this;
72 }
73
74 void wxColour::InitFromName(const wxString& col)
75 {
76 wxColour *the_colour = wxTheColourDatabase->FindColour (col);
77 if (the_colour)
78 {
79 m_red = the_colour->Red ();
80 m_green = the_colour->Green ();
81 m_blue = the_colour->Blue ();
82 m_pixel = the_colour->m_pixel;
83 m_isInit = TRUE;
84 }
85 else
86 {
87 m_red = 0;
88 m_green = 0;
89 m_blue = 0;
90 m_isInit = FALSE;
91 }
92 }
93
94 wxColour::~wxColour ()
95 {
96 }
97
98 void wxColour::Set (unsigned char r, unsigned char g, unsigned char b)
99 {
100 m_red = r;
101 m_green = g;
102 m_blue = b;
103 m_isInit = TRUE;
104 m_pixel = -1;
105 }
106
107 // Allocate a colour, or nearest colour, using the given display.
108 // If realloc is TRUE, ignore the existing pixel, otherwise just return
109 // the existing one.
110 // Returns the old or allocated pixel.
111
112 // TODO: can this handle mono displays? If not, we should have an extra
113 // flag to specify whether this should be black or white by default.
114
115 int wxColour::AllocColour(WXDisplay* display, bool realloc)
116 {
117 if ((m_pixel != -1) && !realloc)
118 return m_pixel;
119
120 XColor color;
121 color.red = (unsigned short) Red ();
122 color.red |= color.red << 8;
123 color.green = (unsigned short) Green ();
124 color.green |= color.green << 8;
125 color.blue = (unsigned short) Blue ();
126 color.blue |= color.blue << 8;
127
128 color.flags = DoRed | DoGreen | DoBlue;
129
130 WXColormap cmap = wxTheApp->GetMainColormap(display);
131
132 if (!XAllocColor ((Display*) display, (Colormap) cmap, &color))
133 {
134 m_pixel = wxGetBestMatchingPixel((Display*) display, &color,(Colormap) cmap);
135 return m_pixel;
136 }
137 else
138 {
139 m_pixel = (int) color.pixel;
140 return m_pixel;
141 }
142 }
143
144 /*-------------------------------------------
145 Markus Emmenegger <mege@iqe.ethz.ch>
146 Find the pixel value with an assigned color closest to the desired color
147 Used if color cell allocation fails
148 As the returned pixel value may be in use by another application,
149 the color might change anytime.
150 But in many cases, that is still better than always using black.
151 --
152 Chris Breeze <chris@hel.co.uk>
153 Improvements:
154 1) More efficient calculation of RGB distance of colour cell from
155 the desired colour. There is no need to take the sqrt of 'dist', and
156 since we are only interested in the top 8-bits of R, G and B we
157 can perform integer arithmetic.
158 2) Attempt to allocate a read-only colour when a close match is found.
159 A read-only colour will not change.
160 3) Fall back to the closest match if no read-only colours are available.
161
162 Possible further improvements:
163 1) Scan the lookup table and sort the colour cells in order of
164 increasing
165 distance from the desired colour. Then attempt to allocate a
166 read-only
167 colour starting from the nearest match.
168 2) Linear RGB distance is not a particularly good method of colour
169 matching
170 (though it is quick). Converting the colour to HLS and then comparing
171 may give better matching.
172 -------------------------------------------*/
173
174 int wxGetBestMatchingPixel(Display *display, XColor *desiredColor, Colormap cmap)
175 {
176 if (cmap == (Colormap) NULL)
177 cmap = (Colormap) wxTheApp->GetMainColormap(display);
178
179 int numPixVals = XDisplayCells(display, DefaultScreen (display));
180 int mindist = 256 * 256 * 3;
181 int bestpixel = (int) BlackPixel (display, DefaultScreen (display));
182 int red = desiredColor->red >> 8;
183 int green = desiredColor->green >> 8;
184 int blue = desiredColor->blue >> 8;
185 const int threshold = 2 * 2 * 3; // allow an error of up to 2 in R,G & B
186
187 for (int pixelcount = 0; pixelcount < numPixVals; pixelcount++)
188 {
189 XColor matching_color;
190 matching_color.pixel = pixelcount;
191 XQueryColor(display,cmap,&matching_color);
192
193 int delta_red = red - (matching_color.red >> 8);
194 int delta_green = green - (matching_color.green >> 8);
195 int delta_blue = blue - (matching_color.blue >> 8);
196
197 int dist = delta_red * delta_red +
198 delta_green * delta_green +
199 delta_blue * delta_blue;
200
201 if (dist <= threshold)
202 {
203 // try to allocate a read-only colour...
204 if (XAllocColor (display, cmap, &matching_color))
205 {
206 return matching_color.pixel;
207 }
208 }
209 if (dist < mindist)
210 {
211 bestpixel = pixelcount;
212 mindist = dist;
213 }
214 }
215 return bestpixel;
216 }