qwt_color_map.cpp 9.92 KB
Newer Older
pixhawk's avatar
pixhawk committed
1 2 3 4 5 6 7 8 9 10
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 * Copyright (C) 1997   Josef Wilgen
 * Copyright (C) 2002   Uwe Rathmann
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

#include "qwt_color_map.h"
Bryant's avatar
Bryant committed
11 12 13
#include "qwt_math.h"
#include "qwt_interval.h"
#include <qnumeric.h>
pixhawk's avatar
pixhawk committed
14 15 16 17

class QwtLinearColorMap::ColorStops
{
public:
Bryant's avatar
Bryant committed
18 19 20
    ColorStops()
    {
        _stops.reserve( 256 );
pixhawk's avatar
pixhawk committed
21 22
    }

Bryant's avatar
Bryant committed
23 24
    void insert( double pos, const QColor &color );
    QRgb rgb( QwtLinearColorMap::Mode, double pos ) const;
pixhawk's avatar
pixhawk committed
25

Bryant's avatar
Bryant committed
26
    QVector<double> stops() const;
pixhawk's avatar
pixhawk committed
27 28 29 30 31 32 33

private:

    class ColorStop
    {
    public:
        ColorStop():
Bryant's avatar
Bryant committed
34 35 36
            pos( 0.0 ),
            rgb( 0 )
        {
pixhawk's avatar
pixhawk committed
37 38
        };

Bryant's avatar
Bryant committed
39 40 41 42 43 44 45
        ColorStop( double p, const QColor &c ):
            pos( p ),
            rgb( c.rgb() )
        {
            r = qRed( rgb );
            g = qGreen( rgb );
            b = qBlue( rgb );
pixhawk's avatar
pixhawk committed
46 47 48 49 50 51 52
        }

        double pos;
        QRgb rgb;
        int r, g, b;
    };

Bryant's avatar
Bryant committed
53 54
    inline int findUpper( double pos ) const;
    QVector<ColorStop> _stops;
pixhawk's avatar
pixhawk committed
55 56
};

Bryant's avatar
Bryant committed
57
void QwtLinearColorMap::ColorStops::insert( double pos, const QColor &color )
pixhawk's avatar
pixhawk committed
58 59 60 61 62 63 64 65
{
    // Lookups need to be very fast, insertions are not so important.
    // Anyway, a balanced tree is what we need here. TODO ...

    if ( pos < 0.0 || pos > 1.0 )
        return;

    int index;
Bryant's avatar
Bryant committed
66 67
    if ( _stops.size() == 0 )
    {
pixhawk's avatar
pixhawk committed
68
        index = 0;
Bryant's avatar
Bryant committed
69 70 71 72 73 74 75 76 77
        _stops.resize( 1 );
    }
    else
    {
        index = findUpper( pos );
        if ( index == _stops.size() ||
                qAbs( _stops[index].pos - pos ) >= 0.001 )
        {
            _stops.resize( _stops.size() + 1 );
pixhawk's avatar
pixhawk committed
78 79 80 81 82
            for ( int i = _stops.size() - 1; i > index; i-- )
                _stops[i] = _stops[i-1];
        }
    }

Bryant's avatar
Bryant committed
83
    _stops[index] = ColorStop( pos, color );
pixhawk's avatar
pixhawk committed
84 85
}

Bryant's avatar
Bryant committed
86
inline QVector<double> QwtLinearColorMap::ColorStops::stops() const
pixhawk's avatar
pixhawk committed
87
{
Bryant's avatar
Bryant committed
88 89
    QVector<double> positions( _stops.size() );
    for ( int i = 0; i < _stops.size(); i++ )
pixhawk's avatar
pixhawk committed
90 91 92 93
        positions[i] = _stops[i].pos;
    return positions;
}

Bryant's avatar
Bryant committed
94
inline int QwtLinearColorMap::ColorStops::findUpper( double pos ) const
pixhawk's avatar
pixhawk committed
95 96 97 98 99
{
    int index = 0;
    int n = _stops.size();

    const ColorStop *stops = _stops.data();
100

Bryant's avatar
Bryant committed
101 102
    while ( n > 0 )
    {
pixhawk's avatar
pixhawk committed
103 104 105
        const int half = n >> 1;
        const int middle = index + half;

Bryant's avatar
Bryant committed
106 107
        if ( stops[middle].pos <= pos )
        {
pixhawk's avatar
pixhawk committed
108 109
            index = middle + 1;
            n -= half + 1;
Bryant's avatar
Bryant committed
110 111
        }
        else
pixhawk's avatar
pixhawk committed
112 113 114 115 116 117 118
            n = half;
    }

    return index;
}

inline QRgb QwtLinearColorMap::ColorStops::rgb(
Bryant's avatar
Bryant committed
119
    QwtLinearColorMap::Mode mode, double pos ) const
pixhawk's avatar
pixhawk committed
120 121 122 123
{
    if ( pos <= 0.0 )
        return _stops[0].rgb;
    if ( pos >= 1.0 )
Bryant's avatar
Bryant committed
124
        return _stops[ _stops.size() - 1 ].rgb;
pixhawk's avatar
pixhawk committed
125

Bryant's avatar
Bryant committed
126 127 128
    const int index = findUpper( pos );
    if ( mode == FixedColors )
    {
pixhawk's avatar
pixhawk committed
129
        return _stops[index-1].rgb;
Bryant's avatar
Bryant committed
130 131 132
    }
    else
    {
pixhawk's avatar
pixhawk committed
133 134 135
        const ColorStop &s1 = _stops[index-1];
        const ColorStop &s2 = _stops[index];

Bryant's avatar
Bryant committed
136
        const double ratio = ( pos - s1.pos ) / ( s2.pos - s1.pos );
pixhawk's avatar
pixhawk committed
137

Bryant's avatar
Bryant committed
138 139 140
        const int r = s1.r + qRound( ratio * ( s2.r - s1.r ) );
        const int g = s1.g + qRound( ratio * ( s2.g - s1.g ) );
        const int b = s1.b + qRound( ratio * ( s2.b - s1.b ) );
141

Bryant's avatar
Bryant committed
142
        return qRgb( r, g, b );
pixhawk's avatar
pixhawk committed
143 144 145 146
    }
}

//! Constructor
Bryant's avatar
Bryant committed
147 148
QwtColorMap::QwtColorMap( Format format ):
    d_format( format )
pixhawk's avatar
pixhawk committed
149 150 151 152 153 154 155 156 157 158 159 160
{
}

//! Destructor
QwtColorMap::~QwtColorMap()
{
}

/*!
   Build and return a color map of 256 colors

   The color table is needed for rendering indexed images in combination
161
   with using colorIndex().
pixhawk's avatar
pixhawk committed
162 163 164 165

   \param interval Range for the values
   \return A color table, that can be used for a QImage
*/
Bryant's avatar
Bryant committed
166
QVector<QRgb> QwtColorMap::colorTable( const QwtInterval &interval ) const
pixhawk's avatar
pixhawk committed
167
{
Bryant's avatar
Bryant committed
168
    QVector<QRgb> table( 256 );
pixhawk's avatar
pixhawk committed
169

Bryant's avatar
Bryant committed
170 171 172 173 174
    if ( interval.isValid() )
    {
        const double step = interval.width() / ( table.size() - 1 );
        for ( int i = 0; i < table.size(); i++ )
            table[i] = rgb( interval, interval.minValue() + step * i );
pixhawk's avatar
pixhawk committed
175 176 177 178 179 180 181 182 183 184 185 186
    }

    return table;
}

class QwtLinearColorMap::PrivateData
{
public:
    ColorStops colorStops;
    QwtLinearColorMap::Mode mode;
};

187
/*!
pixhawk's avatar
pixhawk committed
188 189 190 191 192
   Build a color map with two stops at 0.0 and 1.0. The color
   at 0.0 is Qt::blue, at 1.0 it is Qt::yellow.

   \param format Preferred format of the color map
*/
Bryant's avatar
Bryant committed
193 194
QwtLinearColorMap::QwtLinearColorMap( QwtColorMap::Format format ):
    QwtColorMap( format )
pixhawk's avatar
pixhawk committed
195 196 197 198
{
    d_data = new PrivateData;
    d_data->mode = ScaledColors;

Bryant's avatar
Bryant committed
199
    setColorInterval( Qt::blue, Qt::yellow );
pixhawk's avatar
pixhawk committed
200 201 202
}

/*!
203
   Build a color map with two stops at 0.0 and 1.0.
pixhawk's avatar
pixhawk committed
204 205 206

   \param color1 Color used for the minimum value of the value interval
   \param color2 Color used for the maximum value of the value interval
Bryant's avatar
Bryant committed
207
   \param format Preferred format for the color map
pixhawk's avatar
pixhawk committed
208
*/
Bryant's avatar
Bryant committed
209 210 211
QwtLinearColorMap::QwtLinearColorMap( const QColor &color1,
        const QColor &color2, QwtColorMap::Format format ):
    QwtColorMap( format )
pixhawk's avatar
pixhawk committed
212 213 214
{
    d_data = new PrivateData;
    d_data->mode = ScaledColors;
Bryant's avatar
Bryant committed
215
    setColorInterval( color1, color2 );
pixhawk's avatar
pixhawk committed
216 217 218 219 220 221 222 223 224 225 226 227 228
}

//! Destructor
QwtLinearColorMap::~QwtLinearColorMap()
{
    delete d_data;
}

/*!
   \brief Set the mode of the color map

   FixedColors means the color is calculated from the next lower
   color stop. ScaledColors means the color is calculated
229
   by interpolating the colors of the adjacent stops.
pixhawk's avatar
pixhawk committed
230 231 232

   \sa mode()
*/
Bryant's avatar
Bryant committed
233
void QwtLinearColorMap::setMode( Mode mode )
pixhawk's avatar
pixhawk committed
234 235 236 237 238 239 240 241 242 243 244 245 246 247
{
    d_data->mode = mode;
}

/*!
   \return Mode of the color map
   \sa setMode()
*/
QwtLinearColorMap::Mode QwtLinearColorMap::mode() const
{
    return d_data->mode;
}

/*!
248
   Set the color range
pixhawk's avatar
pixhawk committed
249

250
   Add stops at 0.0 and 1.0.
pixhawk's avatar
pixhawk committed
251 252 253 254 255 256 257

   \param color1 Color used for the minimum value of the value interval
   \param color2 Color used for the maximum value of the value interval

   \sa color1(), color2()
*/
void QwtLinearColorMap::setColorInterval(
Bryant's avatar
Bryant committed
258
    const QColor &color1, const QColor &color2 )
pixhawk's avatar
pixhawk committed
259 260
{
    d_data->colorStops = ColorStops();
Bryant's avatar
Bryant committed
261 262
    d_data->colorStops.insert( 0.0, color1 );
    d_data->colorStops.insert( 1.0, color2 );
pixhawk's avatar
pixhawk committed
263 264 265 266 267
}

/*!
   Add a color stop

268
   The value has to be in the range [0.0, 1.0].
pixhawk's avatar
pixhawk committed
269 270 271 272 273 274
   F.e. a stop at position 17.0 for a range [10.0,20.0] must be
   passed as: (17.0 - 10.0) / (20.0 - 10.0)

   \param value Value between [0.0, 1.0]
   \param color Color stop
*/
Bryant's avatar
Bryant committed
275
void QwtLinearColorMap::addColorStop( double value, const QColor& color )
pixhawk's avatar
pixhawk committed
276 277
{
    if ( value >= 0.0 && value <= 1.0 )
Bryant's avatar
Bryant committed
278
        d_data->colorStops.insert( value, color );
pixhawk's avatar
pixhawk committed
279 280 281
}

/*!
Bryant's avatar
Bryant committed
282
   \return Positions of color stops in increasing order
pixhawk's avatar
pixhawk committed
283
*/
Bryant's avatar
Bryant committed
284
QVector<double> QwtLinearColorMap::colorStops() const
pixhawk's avatar
pixhawk committed
285 286 287 288
{
    return d_data->colorStops.stops();
}

289
/*!
pixhawk's avatar
pixhawk committed
290 291 292 293 294
  \return the first color of the color range
  \sa setColorInterval()
*/
QColor QwtLinearColorMap::color1() const
{
Bryant's avatar
Bryant committed
295
    return QColor( d_data->colorStops.rgb( d_data->mode, 0.0 ) );
pixhawk's avatar
pixhawk committed
296 297
}

298
/*!
pixhawk's avatar
pixhawk committed
299 300 301 302 303
  \return the second color of the color range
  \sa setColorInterval()
*/
QColor QwtLinearColorMap::color2() const
{
Bryant's avatar
Bryant committed
304
    return QColor( d_data->colorStops.rgb( d_data->mode, 1.0 ) );
pixhawk's avatar
pixhawk committed
305 306 307
}

/*!
Bryant's avatar
Bryant committed
308
  Map a value of a given interval into a RGB value
pixhawk's avatar
pixhawk committed
309 310

  \param interval Range for all values
Bryant's avatar
Bryant committed
311 312 313
  \param value Value to map into a RGB value

  \return RGB value for value
pixhawk's avatar
pixhawk committed
314 315
*/
QRgb QwtLinearColorMap::rgb(
Bryant's avatar
Bryant committed
316
    const QwtInterval &interval, double value ) const
pixhawk's avatar
pixhawk committed
317
{
Bryant's avatar
Bryant committed
318 319 320
    if ( qIsNaN(value) )
        return qRgba(0, 0, 0, 0);

pixhawk's avatar
pixhawk committed
321 322 323 324
    const double width = interval.width();

    double ratio = 0.0;
    if ( width > 0.0 )
Bryant's avatar
Bryant committed
325
        ratio = ( value - interval.minValue() ) / width;
pixhawk's avatar
pixhawk committed
326

Bryant's avatar
Bryant committed
327
    return d_data->colorStops.rgb( d_data->mode, ratio );
pixhawk's avatar
pixhawk committed
328 329 330
}

/*!
Bryant's avatar
Bryant committed
331
  \brief Map a value of a given interval into a color index
pixhawk's avatar
pixhawk committed
332 333 334

  \param interval Range for all values
  \param value Value to map into a color index
Bryant's avatar
Bryant committed
335 336

  \return Index, between 0 and 255
pixhawk's avatar
pixhawk committed
337 338
*/
unsigned char QwtLinearColorMap::colorIndex(
Bryant's avatar
Bryant committed
339
    const QwtInterval &interval, double value ) const
pixhawk's avatar
pixhawk committed
340 341 342
{
    const double width = interval.width();

Bryant's avatar
Bryant committed
343
    if ( qIsNaN(value) || width <= 0.0 || value <= interval.minValue() )
pixhawk's avatar
pixhawk committed
344 345 346
        return 0;

    if ( value >= interval.maxValue() )
Bryant's avatar
Bryant committed
347
        return 255;
pixhawk's avatar
pixhawk committed
348

Bryant's avatar
Bryant committed
349
    const double ratio = ( value - interval.minValue() ) / width;
350

pixhawk's avatar
pixhawk committed
351 352
    unsigned char index;
    if ( d_data->mode == FixedColors )
Bryant's avatar
Bryant committed
353
        index = static_cast<unsigned char>( ratio * 255 ); // always floor
pixhawk's avatar
pixhawk committed
354
    else
Bryant's avatar
Bryant committed
355
        index = static_cast<unsigned char>( qRound( ratio * 255 ) );
pixhawk's avatar
pixhawk committed
356 357 358 359 360 361 362 363 364 365 366 367

    return index;
}

class QwtAlphaColorMap::PrivateData
{
public:
    QColor color;
    QRgb rgb;
};


368
/*!
pixhawk's avatar
pixhawk committed
369 370 371
   Constructor
   \param color Color of the map
*/
Bryant's avatar
Bryant committed
372 373
QwtAlphaColorMap::QwtAlphaColorMap( const QColor &color ):
    QwtColorMap( QwtColorMap::RGB )
pixhawk's avatar
pixhawk committed
374 375 376
{
    d_data = new PrivateData;
    d_data->color = color;
Bryant's avatar
Bryant committed
377
    d_data->rgb = color.rgb() & qRgba( 255, 255, 255, 0 );
pixhawk's avatar
pixhawk committed
378 379 380 381 382 383 384 385 386
}

//! Destructor
QwtAlphaColorMap::~QwtAlphaColorMap()
{
    delete d_data;
}

/*!
387
   Set the color
pixhawk's avatar
pixhawk committed
388 389 390 391

   \param color Color
   \sa color()
*/
Bryant's avatar
Bryant committed
392
void QwtAlphaColorMap::setColor( const QColor &color )
pixhawk's avatar
pixhawk committed
393 394 395 396 397
{
    d_data->color = color;
    d_data->rgb = color.rgb();
}

398 399
/*!
  \return the color
pixhawk's avatar
pixhawk committed
400 401 402 403 404 405 406 407 408 409 410 411 412
  \sa setColor()
*/
QColor QwtAlphaColorMap::color() const
{
    return d_data->color;
}

/*!
  \brief Map a value of a given interval into a alpha value

  alpha := (value - interval.minValue()) / interval.width();

  \param interval Range for all values
Bryant's avatar
Bryant committed
413 414
  \param value Value to map into a RGB value
  \return RGB value, with an alpha value
pixhawk's avatar
pixhawk committed
415
*/
Bryant's avatar
Bryant committed
416
QRgb QwtAlphaColorMap::rgb( const QwtInterval &interval, double value ) const
pixhawk's avatar
pixhawk committed
417 418
{
    const double width = interval.width();
Bryant's avatar
Bryant committed
419 420 421 422
    if ( !qIsNaN(value) && width >= 0.0 )
    {
        const double ratio = ( value - interval.minValue() ) / width;
        int alpha = qRound( 255 * ratio );
pixhawk's avatar
pixhawk committed
423 424 425 426 427
        if ( alpha < 0 )
            alpha = 0;
        if ( alpha > 255 )
            alpha = 255;

Bryant's avatar
Bryant committed
428
        return d_data->rgb | ( alpha << 24 );
pixhawk's avatar
pixhawk committed
429 430 431 432 433 434
    }
    return d_data->rgb;
}

/*!
  Dummy function, needed to be implemented as it is pure virtual
435
  in QwtColorMap. Color indices make no sense in combination with
pixhawk's avatar
pixhawk committed
436 437 438 439 440
  an alpha channel.

  \return Always 0
*/
unsigned char QwtAlphaColorMap::colorIndex(
Bryant's avatar
Bryant committed
441
    const QwtInterval &, double ) const
pixhawk's avatar
pixhawk committed
442 443 444
{
    return 0;
}