1 
2 /**********************************************************************
3 
4   Audacity: A Digital Audio Editor
5 
6   AColor.cpp
7 
8   Dominic Mazzoni
9 
10 
11 ********************************************************************//**
12 
13 \class AColor
14 \brief AColor Manages color brushes and pens
15 
16 It is also a place to document colour usage policy in Audacity
17 
18 *//********************************************************************/
19 
20 #include "AColor.h"
21 #include "AColorResources.h"
22 
23 #include <wx/colour.h>
24 #include <wx/dc.h>
25 #include <wx/dcmemory.h>
26 #include <wx/graphics.h>
27 #include <wx/settings.h>
28 #include <wx/utils.h>
29 
30 #include "AllThemeResources.h"
31 #include "Theme.h"
32 
33 bool AColor::inited = false;
34 wxBrush AColor::lightBrush[2];
35 wxBrush AColor::mediumBrush[2];
36 wxBrush AColor::darkBrush[2];
37 wxPen AColor::lightPen[2];
38 wxPen AColor::mediumPen[2];
39 wxPen AColor::darkPen[2];
40 
41 wxPen AColor::cursorPen;
42 wxBrush AColor::indicatorBrush[2];
43 wxPen AColor::indicatorPen[2];
44 // wxPen AColor::playRegionPen[2];
45 wxBrush AColor::playRegionBrush[1];
46 
47 wxBrush AColor::muteBrush[2];
48 wxBrush AColor::soloBrush;
49 
50 wxPen AColor::clippingPen;
51 
52 wxBrush AColor::envelopeBrush;
53 wxPen AColor::envelopePen;
54 wxPen AColor::WideEnvelopePen;
55 
56 wxBrush AColor::labelTextNormalBrush;
57 wxBrush AColor::labelTextEditBrush;
58 wxBrush AColor::labelUnselectedBrush;
59 wxBrush AColor::labelSelectedBrush;
60 wxBrush AColor::labelSyncLockSelBrush;
61 wxPen AColor::labelUnselectedPen;
62 wxPen AColor::labelSelectedPen;
63 wxPen AColor::labelSyncLockSelPen;
64 wxPen AColor::labelSurroundPen;
65 wxPen AColor::trackFocusPens[3];
66 wxPen AColor::snapGuidePen;
67 
68 wxPen AColor::tooltipPen;
69 wxBrush AColor::tooltipBrush;
70 
71 // The spare pen and brush possibly help us cut down on the
72 // number of pens and brushes we need.
73 wxPen AColor::sparePen;
74 wxBrush AColor::spareBrush;
75 
76 wxPen AColor::uglyPen;
77 wxBrush AColor::uglyBrush;
78 
79 //
80 // Draw an upward or downward pointing arrow.
81 //
Arrow(wxDC & dc,wxCoord x,wxCoord y,int width,bool down)82 void AColor::Arrow(wxDC & dc, wxCoord x, wxCoord y, int width, bool down)
83 {
84    if (width & 0x01) {
85       width--;
86    }
87 
88    wxPoint pt[3];
89    int half = width / 2;
90 
91    if (down) {
92       pt[0].x =     0; pt[0].y = 0;
93       pt[1].x = width; pt[1].y = 0;
94       pt[2].x =  half; pt[2].y = half;
95    }
96    else {
97       pt[0].x =     0; pt[0].y = half;
98       pt[1].x =  half; pt[1].y = 0;
99       pt[2].x = width; pt[2].y = half;
100    }
101 
102    dc.DrawPolygon(3, pt, x, y);
103 }
104 
105 //
106 // Draw a line, inclusive of endpoints,
107 // compensating for differences in wxWidgets versions across platforms
108 //
Line(wxDC & dc,wxCoord x1,wxCoord y1,wxCoord x2,wxCoord y2)109 void AColor::Line(wxDC & dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
110 {
111    const wxPoint points[] { { x1, y1 }, { x2, y2 } };
112    Lines( dc, 2, points );
113 }
114 
115 // Draw lines, INCLUSIVE of all endpoints
Lines(wxDC & dc,size_t nPoints,const wxPoint points[])116 void AColor::Lines(wxDC &dc, size_t nPoints, const wxPoint points[])
117 {
118    if ( nPoints <= 1 ) {
119       if (nPoints == 1)
120          dc.DrawPoint( points[0] );
121       return;
122    }
123 
124    for (size_t ii = 0; ii < nPoints - 1; ++ii) {
125       const auto &p1 = points[ii];
126       const auto &p2 = points[ii + 1];
127 
128       // As of 2.8.9 (possibly earlier), wxDC::DrawLine() on the Mac draws the
129       // last point since it is now based on the NEW wxGraphicsContext system.
130       // Make the other platforms do the same thing since the other platforms
131       // "may" follow they get wxGraphicsContext going.
132 
133       // PRL:  as of 3.1.1, I still observe that on Mac, the last point is
134       // included, contrary to what documentation says.  Also that on Windows,
135       // sometimes it is the first point that is excluded.
136 
137 #if defined(__WXMAC__) || defined(__WXGTK3__)
138       dc.DrawLine(p1, p2);
139 #else
140       dc.DrawPoint(p1);
141       if ( p1 != p2 ) {
142          dc.DrawLine(p1, p2);
143       }
144 #endif
145    }
146 
147 #if defined(__WXMAC__) || defined(__WXGTK3__)
148       ;
149 #else
150       dc.DrawPoint( points[ nPoints - 1 ] );
151 #endif
152 }
153 
154 //
155 // Draws a focus rectangle (Taken directly from wxWidgets source)
156 //
DrawFocus(wxDC & dc,wxRect & rect)157 void AColor::DrawFocus(wxDC & dc, wxRect & rect)
158 {
159    // draw the pixels manually: note that to behave in the same manner as
160    // DrawRect(), we must exclude the bottom and right borders from the
161    // rectangle
162    wxCoord x1 = rect.GetLeft(),
163          y1 = rect.GetTop(),
164          x2 = rect.GetRight(),
165          y2 = rect.GetBottom();
166 
167    // -1 for brush, so it just sets the pen colour, and does not change the brush.
168    UseThemeColour( &dc, -1, clrTrackPanelText );
169 
170    wxCoord z;
171    for ( z = x1 + 1; z < x2; z += 2 )
172       dc.DrawPoint(z, y1);
173 
174    wxCoord shift = z == x2 ? 0 : 1;
175    for ( z = y1 + shift; z < y2; z += 2 )
176       dc.DrawPoint(x2, z);
177 
178    shift = z == y2 ? 0 : 1;
179    for ( z = x2 - shift; z > x1; z -= 2 )
180       dc.DrawPoint(z, y2);
181 
182    shift = z == x1 ? 0 : 1;
183    for ( z = y2 - shift; z > y1; z -= 2 )
184       dc.DrawPoint(x1, z);
185 
186 }
187 
Bevel(wxDC & dc,bool up,const wxRect & r)188 void AColor::Bevel(wxDC & dc, bool up, const wxRect & r)
189 {
190    if (up)
191       AColor::Light(&dc, false);
192    else
193       AColor::Dark(&dc, false);
194 
195    AColor::Line(dc, r.x, r.y, r.x + r.width, r.y);
196    AColor::Line(dc, r.x, r.y, r.x, r.y + r.height);
197 
198    if (!up)
199       AColor::Light(&dc, false);
200    else
201       AColor::Dark(&dc, false);
202 
203    AColor::Line(dc, r.x + r.width, r.y, r.x + r.width, r.y + r.height);
204    AColor::Line(dc, r.x, r.y + r.height, r.x + r.width, r.y + r.height);
205 }
206 
Bevel2(wxDC & dc,bool up,const wxRect & r,bool bSel,bool bHighlight)207 void AColor::Bevel2
208 (wxDC & dc, bool up, const wxRect & r, bool bSel, bool bHighlight)
209 {
210    int index = 0;
211    // There are eight button states in the TCP.
212    // A theme might not differentiate between them all.  That's up to
213    // the theme designer.
214    //   Button highlighted (i.e. hovered over) or not.
215    //   Track selected or not
216    //   Button up or down.
217    // Highlight in most themes is lighter than not highlighted.
218    if ( bHighlight && bSel)
219       index = up ? bmpHiliteUpButtonExpandSel : bmpHiliteButtonExpandSel;
220    else if ( bHighlight )
221       index = up ? bmpHiliteUpButtonExpand : bmpHiliteButtonExpand;
222    else if( bSel )
223       index = up ? bmpUpButtonExpandSel : bmpDownButtonExpandSel;
224    else
225       index = up ? bmpUpButtonExpand : bmpDownButtonExpand;
226 
227    wxBitmap & Bmp = theTheme.Bitmap( index );
228    wxMemoryDC memDC;
229    memDC.SelectObject(Bmp);
230    int h = wxMin( r.height, Bmp.GetHeight() );
231 
232 
233    dc.Blit( r.x,r.y,r.width/2, h, &memDC, 0, 0, wxCOPY, true );
234    int r2 = r.width - r.width/2;
235    dc.Blit( r.x+r.width/2,r.y,r2, h, &memDC,
236       Bmp.GetWidth() - r2, 0, wxCOPY, true );
237 }
238 
Blend(const wxColour & c1,const wxColour & c2)239 wxColour AColor::Blend( const wxColour & c1, const wxColour & c2 )
240 {
241    wxColour c3(
242       (c1.Red() + c2.Red())/2,
243       (c1.Green() + c2.Green())/2,
244       (c1.Blue() + c2.Blue())/2);
245    return c3;
246 }
247 
BevelTrackInfo(wxDC & dc,bool up,const wxRect & r,bool highlight)248 void AColor::BevelTrackInfo(wxDC & dc, bool up, const wxRect & r, bool highlight)
249 {
250 #ifndef EXPERIMENTAL_THEMING
251    Bevel( dc, up, r );
252 #else
253    // Note that the actually drawn rectangle extends one pixel right of and
254    // below the given
255 
256    wxColour col;
257    col = Blend( theTheme.Colour( clrTrackInfo ), up ? wxColour( 255,255,255):wxColour(0,0,0));
258 
259    wxPen pen( highlight ? uglyPen : col );
260    dc.SetPen( pen );
261 
262    dc.DrawLine(r.x, r.y, r.x + r.width, r.y);
263    dc.DrawLine(r.x, r.y, r.x, r.y + r.height);
264 
265    col = Blend( theTheme.Colour( clrTrackInfo ), up ? wxColour(0,0,0): wxColour(255,255,255));
266 
267    pen.SetColour( col );
268    dc.SetPen( highlight ? uglyPen : pen );
269 
270    dc.DrawLine(r.x + r.width, r.y, r.x + r.width, r.y + r.height);
271    dc.DrawLine(r.x, r.y + r.height, r.x + r.width, r.y + r.height);
272 #endif
273 }
274 
275 // Set colour of and select brush and pen.
276 // Use -1 to omit brush or pen.
277 // If pen omitted, then the same colour as the brush will be used.
278 // alpha for the brush is normally 255, but if set will make a difference
279 // on mac (only) currently.
UseThemeColour(wxDC * dc,int iBrush,int iPen,int alpha)280 void AColor::UseThemeColour( wxDC * dc, int iBrush, int iPen, int alpha )
281 {
282    if (!inited)
283       Init();
284    // do nothing if no colours set.
285    if( (iBrush == -1) && ( iPen ==-1))
286       return;
287    wxColour col = wxColour(0,0,0);
288    if( iBrush !=-1 ){
289       col = theTheme.Colour( iBrush );
290       col.Set( col.Red(), col.Green(), col.Blue(), alpha);
291       spareBrush.SetColour( col );
292       dc->SetBrush( spareBrush );
293    }
294    if( iPen != -1)
295       col = theTheme.Colour( iPen );
296    sparePen.SetColour( col );
297    dc->SetPen( sparePen );
298 }
299 
UseThemeColour(wxGraphicsContext * gc,int iBrush,int iPen,int alpha)300 void AColor::UseThemeColour( wxGraphicsContext * gc, int iBrush, int iPen, int alpha )
301 {
302    if (!inited)
303       Init();
304    // do nothing if no colours set.
305    if( (iBrush == -1) && ( iPen ==-1))
306       return;
307    wxColour col = wxColour(0,0,0);
308    if( iBrush !=-1 ){
309       col = theTheme.Colour( iBrush );
310       col.Set( col.Red(), col.Green(), col.Blue(), alpha);
311       spareBrush.SetColour( col );
312       gc->SetBrush( spareBrush );
313    }
314    if( iPen != -1)
315       col = theTheme.Colour( iPen );
316    sparePen.SetColour( col );
317    gc->SetPen( sparePen );
318 }
319 
320 
Light(wxDC * dc,bool selected,bool highlight)321 void AColor::Light(wxDC * dc, bool selected, bool highlight)
322 {
323    if (!inited)
324       Init();
325    int index = (int) selected;
326    auto &brush = highlight ? AColor::uglyBrush : lightBrush[index];
327    dc->SetBrush( brush );
328    auto &pen = highlight ? AColor::uglyPen : lightPen[index];
329    dc->SetPen( pen );
330 }
331 
Medium(wxDC * dc,bool selected)332 void AColor::Medium(wxDC * dc, bool selected)
333 {
334    if (!inited)
335       Init();
336    int index = (int) selected;
337    dc->SetBrush(mediumBrush[index]);
338    dc->SetPen(mediumPen[index]);
339 }
340 
MediumTrackInfo(wxDC * dc,bool selected)341 void AColor::MediumTrackInfo(wxDC * dc, bool selected)
342 {
343 #ifdef EXPERIMENTAL_THEMING
344    UseThemeColour( dc, selected ? clrTrackInfoSelected : clrTrackInfo );
345 #else
346    Medium( dc, selected );
347 #endif
348 }
349 
350 
Dark(wxDC * dc,bool selected,bool highlight)351 void AColor::Dark(wxDC * dc, bool selected, bool highlight)
352 {
353    if (!inited)
354       Init();
355    int index = (int) selected;
356    auto &brush = highlight ? AColor::uglyBrush : darkBrush[index];
357    dc->SetBrush( brush );
358    auto &pen = highlight ? AColor::uglyPen : darkPen[index];
359    dc->SetPen( pen );
360 }
361 
TrackPanelBackground(wxDC * dc,bool selected)362 void AColor::TrackPanelBackground(wxDC * dc, bool selected)
363 {
364 #ifdef EXPERIMENTAL_THEMING
365    UseThemeColour( dc, selected ? clrMediumSelected : clrTrackBackground );
366 #else
367    Dark( dc, selected );
368 #endif
369 }
370 
CursorColor(wxDC * dc)371 void AColor::CursorColor(wxDC * dc)
372 {
373    if (!inited)
374       Init();
375 
376    dc->SetLogicalFunction(wxCOPY);
377    dc->SetPen(cursorPen);
378 }
379 
IndicatorColor(wxDC * dc,bool bIsNotRecording)380 void AColor::IndicatorColor(wxDC * dc, bool bIsNotRecording)
381 {
382    if (!inited)
383       Init();
384    int index = (int) bIsNotRecording;
385    dc->SetPen(indicatorPen[index]);
386    dc->SetBrush(indicatorBrush[index]);
387 }
388 
TrackFocusPen(wxDC * dc,int level)389 void AColor::TrackFocusPen(wxDC * dc, int level)
390 {
391    if (!inited)
392       Init();
393    dc->SetPen(trackFocusPens[level]);
394 }
395 
SnapGuidePen(wxDC * dc)396 void AColor::SnapGuidePen(wxDC * dc)
397 {
398    if (!inited)
399       Init();
400    dc->SetPen(snapGuidePen);
401 }
402 
Mute(wxDC * dc,bool on,bool selected,bool soloing)403 void AColor::Mute(wxDC * dc, bool on, bool selected, bool soloing)
404 {
405    if (!inited)
406       Init();
407    int index = (int) selected;
408    if (on) {
409       dc->SetPen(*wxBLACK_PEN);
410       dc->SetBrush(muteBrush[(int) soloing]);
411    }
412    else {
413       dc->SetPen(*wxTRANSPARENT_PEN);
414       dc->SetBrush(mediumBrush[index]);
415    }
416 }
417 
Solo(wxDC * dc,bool on,bool selected)418 void AColor::Solo(wxDC * dc, bool on, bool selected)
419 {
420    if (!inited)
421       Init();
422    int index = (int) selected;
423    if (on) {
424       dc->SetPen(*wxBLACK_PEN);
425       dc->SetBrush(soloBrush);
426    }
427    else {
428       dc->SetPen(*wxTRANSPARENT_PEN);
429       dc->SetBrush(mediumBrush[index]);
430    }
431 }
432 
433 bool AColor::gradient_inited = 0;
434 
ReInit()435 void AColor::ReInit()
436 {
437    inited=false;
438    Init();
439    gradient_inited=0;
440    PreComputeGradient();
441 }
442 
InvertOfColour(const wxColour & c)443 wxColour InvertOfColour( const wxColour & c )
444 {
445    return wxColour( 255-c.Red(), 255-c.Green(), 255-c.Blue() );
446 }
447 
448 // Fix up the cursor colour, if it is 'unacceptable'.
449 // Unacceptable if it is too close to the background colour.
CursorColour()450 wxColour CursorColour( )
451 {
452    wxColour cCursor = theTheme.Colour( clrCursorPen );
453    wxColour cBack = theTheme.Colour( clrMedium );
454 
455    int d = theTheme.ColourDistance( cCursor, cBack );
456 
457    // Pen colour is fine, if there is plenty of contrast.
458    if( d  > 200 )
459       return theTheme.Colour( clrCursorPen );
460 
461    // otherwise return same colour as a selection.
462    return theTheme.Colour( clrSelected );
463 }
464 
Init()465 void AColor::Init()
466 {
467    if (inited)
468       return;
469 
470    wxColour light = theTheme.Colour( clrLight );
471    // wxSystemSettings::GetColour(wxSYS_COLOUR_3DHIGHLIGHT);
472    wxColour med = theTheme.Colour( clrMedium );
473    // wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
474    wxColour dark = theTheme.Colour( clrDark );
475    // wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW);
476 
477    wxColour lightSelected = theTheme.Colour( clrLightSelected );
478    wxColour medSelected = theTheme.Colour( clrMediumSelected );
479    wxColour darkSelected = theTheme.Colour( clrDarkSelected );
480 
481 
482    clippingPen.SetColour(0xCC, 0x11, 0x00);
483 
484    theTheme.SetPenColour(   envelopePen,     clrEnvelope );
485    theTheme.SetPenColour(   WideEnvelopePen, clrEnvelope );
486    theTheme.SetBrushColour( envelopeBrush,   clrEnvelope );
487 
488    WideEnvelopePen.SetWidth( 3 );
489 
490    theTheme.SetBrushColour( labelTextNormalBrush,  clrLabelTextNormalBrush );
491    theTheme.SetBrushColour( labelTextEditBrush,    clrLabelTextEditBrush );
492    theTheme.SetBrushColour( labelUnselectedBrush,  clrLabelUnselectedBrush );
493    theTheme.SetBrushColour( labelSelectedBrush,    clrLabelSelectedBrush );
494    theTheme.SetBrushColour( labelSyncLockSelBrush, clrSyncLockSel );
495    theTheme.SetPenColour( labelUnselectedPen,   clrLabelUnselectedPen );
496    theTheme.SetPenColour( labelSelectedPen,     clrLabelSelectedPen );
497    theTheme.SetPenColour( labelSyncLockSelPen,  clrSyncLockSel );
498    theTheme.SetPenColour( labelSurroundPen,     clrLabelSurroundPen );
499 
500    // These colors were modified to avoid using reserved colors red and green
501    // for the buttons.
502    theTheme.SetBrushColour( muteBrush[0],      clrMuteButtonActive);
503    theTheme.SetBrushColour( muteBrush[1],      clrMuteButtonVetoed);
504    theTheme.SetBrushColour( soloBrush,         clrMuteButtonActive);
505 
506    cursorPen.SetColour( CursorColour()  );
507    theTheme.SetPenColour(   indicatorPen[0],   clrRecordingPen);
508    theTheme.SetPenColour(   indicatorPen[1],   clrPlaybackPen);
509    theTheme.SetBrushColour( indicatorBrush[0], clrRecordingBrush);
510    theTheme.SetBrushColour( indicatorBrush[1], clrPlaybackBrush);
511 
512    theTheme.SetBrushColour( playRegionBrush[0],clrRulerRecordingBrush);
513    // theTheme.SetPenColour(   playRegionPen[0],  clrRulerRecordingPen);
514    // theTheme.SetBrushColour( playRegionBrush[1],clrRulerPlaybackBrush);
515    // theTheme.SetPenColour(   playRegionPen[1],  clrRulerPlaybackPen);
516 
517    //Determine tooltip color
518    tooltipPen.SetColour( wxSystemSettingsNative::GetColour(wxSYS_COLOUR_INFOTEXT) );
519    tooltipBrush.SetColour( wxSystemSettingsNative::GetColour(wxSYS_COLOUR_INFOBK) );
520 
521    uglyPen.SetColour( wxColour{ 0, 255, 0 } ); // saturated green
522    uglyBrush.SetColour( wxColour{ 255, 0, 255 } ); // saturated magenta
523 
524    // A tiny gradient of yellow surrounding the current focused track
525    theTheme.SetPenColour(   trackFocusPens[0],  clrTrackFocus0);
526    theTheme.SetPenColour(   trackFocusPens[1],  clrTrackFocus1);
527    theTheme.SetPenColour(   trackFocusPens[2],  clrTrackFocus2);
528 
529    // A vertical line indicating that the selection or sliding has
530    // been snapped to the nearest boundary.
531    theTheme.SetPenColour(   snapGuidePen,      clrSnapGuide);
532 
533    // unselected
534    lightBrush[0].SetColour(light);
535    mediumBrush[0].SetColour(med);
536    darkBrush[0].SetColour(dark);
537    lightPen[0].SetColour(light);
538    mediumPen[0].SetColour(med);
539    darkPen[0].SetColour(dark);
540 
541    // selected
542    lightBrush[1].SetColour(lightSelected);
543    mediumBrush[1].SetColour(medSelected);
544    darkBrush[1].SetColour(darkSelected);
545    lightPen[1].SetColour(lightSelected);
546    mediumPen[1].SetColour(medSelected);
547    darkPen[1].SetColour(darkSelected);
548 
549    inited = true;
550 }
551 
552 // These colours are chosen so that black text shows up OK on them.
553 const int AColor_midicolors[16][3] = {
554    {255, 102, 102},             // 1=salmon
555    {204, 0, 0},                 // 2=red
556    {255, 117, 23},              // 3=orange
557    {255, 255, 0},               // 4=yellow
558    {0, 204, 0},                 // 5=green
559    {0, 204, 204},               // 6=turquoise
560    {125, 125, 255},             // 7=blue
561    {153, 0, 255},               // 8=blue-violet
562 
563    {140, 97, 54},               // 9=brown
564    {120, 120, 120},             // 10=gray (drums)
565    {255, 175, 40},              // 11=lt orange
566    {102, 255, 102},             // 12=lt green
567    {153, 255, 255},             // 13=lt turquoise
568    {190, 190, 255},             // 14=lt blue
569    {204, 102, 255},             // 15=lt blue-violet
570    {255, 51, 204}               // 16=lt red-violet
571 };
572 
MIDIChannel(wxDC * dc,int channel)573 void AColor::MIDIChannel(wxDC * dc, int channel /* 1 - 16 */ )
574 {
575    if (channel >= 1 && channel <= 16) {
576       const int *colors = AColor_midicolors[channel - 1];
577 
578       dc->SetPen(wxPen(wxColour(colors[0],
579                                 colors[1], colors[2]), 1, wxPENSTYLE_SOLID));
580       dc->SetBrush(wxBrush(wxColour(colors[0],
581                                     colors[1], colors[2]), wxBRUSHSTYLE_SOLID));
582    } else {
583       dc->SetPen(wxPen(wxColour(153, 153, 153), 1, wxPENSTYLE_SOLID));
584       dc->SetBrush(wxBrush(wxColour(153, 153, 153), wxBRUSHSTYLE_SOLID));
585    }
586 
587 }
588 
LightMIDIChannel(wxDC * dc,int channel)589 void AColor::LightMIDIChannel(wxDC * dc, int channel /* 1 - 16 */ )
590 {
591    if (channel >= 1 && channel <= 16) {
592       const int *colors = AColor_midicolors[channel - 1];
593 
594       dc->SetPen(wxPen(wxColour(127 + colors[0] / 2,
595                                 127 + colors[1] / 2,
596                                 127 + colors[2] / 2), 1, wxPENSTYLE_SOLID));
597       dc->SetBrush(wxBrush(wxColour(127 + colors[0] / 2,
598                                     127 + colors[1] / 2,
599                                     127 + colors[2] / 2), wxBRUSHSTYLE_SOLID));
600    } else {
601       dc->SetPen(wxPen(wxColour(204, 204, 204), 1, wxPENSTYLE_SOLID));
602       dc->SetBrush(wxBrush(wxColour(204, 204, 204), wxBRUSHSTYLE_SOLID));
603    }
604 
605 }
606 
DarkMIDIChannel(wxDC * dc,int channel)607 void AColor::DarkMIDIChannel(wxDC * dc, int channel /* 1 - 16 */ )
608 {
609    if (channel >= 1 && channel <= 16) {
610       const int *colors = AColor_midicolors[channel - 1];
611 
612       dc->SetPen(wxPen(wxColour(colors[0] / 2,
613                                 colors[1] / 2,
614                                 colors[2] / 2), 1, wxPENSTYLE_SOLID));
615       dc->SetBrush(wxBrush(wxColour(colors[0] / 2,
616                                     colors[1] / 2,
617                                     colors[2] / 2), wxBRUSHSTYLE_SOLID));
618    } else {
619       dc->SetPen(wxPen(wxColour(102, 102, 102), 1, wxPENSTYLE_SOLID));
620       dc->SetBrush(wxBrush(wxColour(102, 102, 102), wxBRUSHSTYLE_SOLID));
621    }
622 
623 }
624 
625 
626 
627 unsigned char AColor::gradient_pre[ColorGradientTotal][colorSchemes][gradientSteps][3];
628 
PreComputeGradient()629 void AColor::PreComputeGradient() {
630    if (gradient_inited) return;
631    gradient_inited = 1;
632 
633    // Keep in correspondence with enum SpectrogramSettings::ColorScheme
634 
635    // colorScheme 0: Color (New)
636    std::copy_n(&specColormap[0][0], gradientSteps * 3, &gradient_pre[ColorGradientUnselected][0][0][0]);
637    std::copy_n(&selColormap[0][0], gradientSteps * 3, &gradient_pre[ColorGradientTimeSelected][0][0][0]);
638    std::copy_n(&freqSelColormap[0][0], gradientSteps * 3, &gradient_pre[ColorGradientTimeAndFrequencySelected][0][0][0]);
639    std::fill_n(&gradient_pre[ColorGradientEdge][0][0][0], gradientSteps * 3, 0);
640 
641 
642    for (int selected = 0; selected < ColorGradientTotal; selected++) {
643       // Get color scheme from Theme
644       const int gsteps = 4;
645       float gradient[gsteps + 1][3];
646       theTheme.Colour( clrSpectro1 ) = theTheme.Colour( clrUnselected );
647       theTheme.Colour( clrSpectro1Sel ) = theTheme.Colour( clrSelected );
648       int clrFirst = (selected == ColorGradientUnselected ) ? clrSpectro1 : clrSpectro1Sel;
649       for(int j=0;j<(gsteps+1);j++){
650          wxColour c = theTheme.Colour( clrFirst+j );
651          gradient[ j] [0] = c.Red()/255.0;
652          gradient[ j] [1] = c.Green()/255.0;
653          gradient[ j] [2] = c.Blue()/255.0;
654       }
655 
656       // colorScheme 1: Color (from theme)
657       for (int i = 0; i<gradientSteps; i++) {
658          float r, g, b;
659          float value = float(i)/gradientSteps;
660 
661          int left = (int)(value * gsteps);
662          int right = (left == gsteps ? gsteps : left + 1);
663 
664          float rweight = (value * gsteps) - left;
665          float lweight = 1.0 - rweight;
666 
667          r = (gradient[left][0] * lweight) + (gradient[right][0] * rweight);
668          g = (gradient[left][1] * lweight) + (gradient[right][1] * rweight);
669          b = (gradient[left][2] * lweight) + (gradient[right][2] * rweight);
670 
671          switch (selected) {
672          case ColorGradientUnselected:
673             // not dimmed
674             break;
675 
676          case ColorGradientTimeAndFrequencySelected:
677             float temp;
678             temp = r;
679             r = g;
680             g = b;
681             b = temp;
682             break;
683 
684          case ColorGradientTimeSelected:
685             // partly dimmed
686             r *= 0.75f;
687             g *= 0.75f;
688             b *= 0.75f;
689             break;
690 
691 
692          // For now edge colour is just black (or white if grey-scale)
693          // Later we might invert or something else funky.
694          case ColorGradientEdge:
695             // fully dimmed
696             r = 0;
697             g = 0;
698             b = 0;
699             break;
700          }
701          gradient_pre[selected][1][i][0] = (unsigned char) (255 * r);
702          gradient_pre[selected][1][i][1] = (unsigned char) (255 * g);
703          gradient_pre[selected][1][i][2] = (unsigned char) (255 * b);
704       }
705 
706       // colorScheme 3: Inverse Grayscale
707       for (int i = 0; i < gradientSteps; i++) {
708          float r, g, b;
709          float value = float(i) / gradientSteps;
710 
711          r = g = b = value;
712 
713          switch (selected) {
714          case ColorGradientUnselected:
715             // not dimmed
716             break;
717 
718          case ColorGradientTimeAndFrequencySelected:
719             // else fall through to SAME grayscale colour as normal selection.
720             // The white lines show it up clearly enough.
721 
722          case ColorGradientTimeSelected:
723             // partly dimmed
724             r = r * 0.75f + 0.25f;
725             g = g * 0.75f + 0.25f;
726             b = b * 0.75f + 0.25f;
727             break;
728 
729          case ColorGradientEdge:
730             r = 1.0f;
731             g = 1.0f;
732             b = 1.0f;
733             break;
734          }
735          gradient_pre[selected][3][i][0] = (unsigned char)(255 * r);
736          gradient_pre[selected][3][i][1] = (unsigned char)(255 * g);
737          gradient_pre[selected][3][i][2] = (unsigned char)(255 * b);
738       }
739 
740       // colorScheme 2: Grayscale (=Old grayscale)
741       for (int i = 0; i<gradientSteps; i++) {
742          float r, g, b;
743          float value = float(i)/gradientSteps;
744 
745          r = g = b = 0.84 - 0.84 * value;
746 
747          switch (selected) {
748          case ColorGradientUnselected:
749             // not dimmed
750             break;
751 
752          case ColorGradientTimeAndFrequencySelected:
753             // else fall through to SAME grayscale colour as normal selection.
754             // The white lines show it up clearly enough.
755 
756          case ColorGradientTimeSelected:
757             // partly dimmed
758             r *= 0.75f;
759             g *= 0.75f;
760             b *= 0.75f;
761             break;
762 
763 
764          // For now edge colour is just black (or white if grey-scale)
765          // Later we might invert or something else funky.
766          case ColorGradientEdge:
767             // fully dimmed
768             r = 1.0f;
769             g = 1.0f;
770             b = 1.0f;
771             break;
772          }
773          gradient_pre[selected][2][i][0] = (unsigned char) (255 * r);
774          gradient_pre[selected][2][i][1] = (unsigned char) (255 * g);
775          gradient_pre[selected][2][i][2] = (unsigned char) (255 * b);
776       }
777    }
778 }
779