Go to the documentation of this file.00001
00022 #include "color_palette.hpp"
00023 #include <cmath>
00024 #include <QFile>
00025 #include <QTextStream>
00026 #include <QHash>
00027 #include <QPainter>
00028 #include <QFileInfo>
00029
00030 namespace color_widgets {
00031
00032 class ColorPalette::Private
00033 {
00034 public:
00035 QVector<QPair<QColor,QString> > colors;
00036 int columns;
00037 QString name;
00038 QString fileName;
00039 bool dirty;
00040
00041 bool valid_index(int index)
00042 {
00043 return index >= 0 && index < colors.size();
00044 }
00045 };
00046
00047 ColorPalette::ColorPalette(const QVector<QColor>& colors,
00048 const QString& name,
00049 int columns)
00050 : p ( new Private )
00051 {
00052 setName(name);
00053 setColumns(columns);
00054 setColors(colors);
00055 }
00056
00057 ColorPalette::ColorPalette(const QString& name)
00058 : p ( new Private )
00059 {
00060 setName(name);
00061 p->columns = 0;
00062 p->dirty = false;
00063 }
00064
00065 ColorPalette::ColorPalette(const QVector<QPair<QColor,QString> >& colors,
00066 const QString& name,
00067 int columns)
00068 {
00069 setName(name);
00070 setColumns(columns);
00071 setColors(colors);
00072 p->dirty = false;
00073 }
00074
00075 ColorPalette::ColorPalette(const ColorPalette& other)
00076 : QObject(), p ( new Private(*other.p) )
00077 {
00078 }
00079
00080 ColorPalette& ColorPalette::operator=(const ColorPalette& other)
00081 {
00082 *p = *other.p;
00083 emitUpdate();
00084 return *this;
00085 }
00086
00087 ColorPalette::~ColorPalette()
00088 {
00089 delete p;
00090 }
00091
00092 ColorPalette::ColorPalette(ColorPalette&& other)
00093 : QObject(), p ( other.p )
00094 {
00095 other.p = nullptr;
00096 }
00097 ColorPalette& ColorPalette::operator=(ColorPalette&& other)
00098 {
00099 std::swap(p, other.p);
00100 emitUpdate();
00101 return *this;
00102 }
00103
00104 void ColorPalette::emitUpdate()
00105 {
00106 emit colorsChanged(p->colors);
00107 emit columnsChanged(p->columns);
00108 emit nameChanged(p->name);
00109 emit fileNameChanged(p->fileName);
00110 emit dirtyChanged(p->dirty);
00111 }
00112
00113 QColor ColorPalette::colorAt(int index) const
00114 {
00115 return p->valid_index(index) ? p->colors[index].first : QColor();
00116 }
00117
00118 QString ColorPalette::nameAt(int index) const
00119 {
00120 return p->valid_index(index) ? p->colors[index].second : QString();
00121 }
00122
00123 QVector<QPair<QColor,QString> > ColorPalette::colors() const
00124 {
00125 return p->colors;
00126 }
00127
00128 int ColorPalette::count() const
00129 {
00130 return p->colors.size();
00131 }
00132
00133 int ColorPalette::columns()
00134 {
00135 return p->columns;
00136 }
00137
00138 QString ColorPalette::name() const
00139 {
00140 return p->name;
00141 }
00142
00143 void ColorPalette::loadColorTable(const QVector<QRgb>& color_table)
00144 {
00145 p->colors.clear();
00146 p->colors.reserve(color_table.size());
00147 for ( QRgb c : color_table )
00148 {
00149 QColor color ( c );
00150 color.setAlpha(255);
00151 p->colors.push_back(qMakePair(color,QString()));
00152 }
00153 emit colorsChanged(p->colors);
00154 setDirty(true);
00155 }
00156
00157 bool ColorPalette::loadImage(const QImage& image)
00158 {
00159 if ( image.isNull() )
00160 return false;
00161 setColumns(image.width());
00162
00163 p->colors.clear();
00164 p->colors.reserve(image.width()*image.height());
00165 for ( int y = 0; y < image.height(); y++ )
00166 {
00167 for ( int x = 0; x < image.width(); x++ )
00168 {
00169 QColor color ( image.pixel(x, y) );
00170 color.setAlpha(255);
00171 p->colors.push_back(qMakePair(color,QString()));
00172 }
00173 }
00174 emit colorsChanged(p->colors);
00175 setDirty(true);
00176 return true;
00177 }
00178
00179 ColorPalette ColorPalette::fromImage(const QImage& image)
00180 {
00181 ColorPalette p;
00182 p.fromImage(image);
00183 return p;
00184 }
00185
00186 bool ColorPalette::load(const QString& name)
00187 {
00188 p->fileName = name;
00189 p->colors.clear();
00190 p->columns = 0;
00191 p->dirty = false;
00192 p->name = QFileInfo(name).baseName();
00193
00194 QFile file(name);
00195
00196 if ( !file.open(QFile::ReadOnly|QFile::Text) )
00197 {
00198 emitUpdate();
00199 return false;
00200 }
00201
00202 QTextStream stream( &file );
00203
00204 if ( stream.readLine() != "GIMP Palette" )
00205 {
00206 emitUpdate();
00207 return false;
00208 }
00209
00210 QString line;
00211
00212
00213 QHash<QString,QString> properties;
00214 while( !stream.atEnd() )
00215 {
00216 line = stream.readLine();
00217 if ( line.isEmpty() )
00218 continue;
00219 if ( line[0] == '#' )
00220 break;
00221 int colon = line.indexOf(':');
00222 if ( colon == -1 )
00223 break;
00224 properties[line.left(colon).toLower()] =
00225 line.right(line.size() - colon - 1).trimmed();
00226 }
00228 setName(properties["name"]);
00229 setColumns(properties["columns"].toInt());
00230
00231
00232 if ( !stream.atEnd() && line[0] == '#' )
00233 while( !stream.atEnd() )
00234 {
00235 qint64 pos = stream.pos();
00236 line = stream.readLine();
00237 if ( !line.isEmpty() && line[0] != '#' )
00238 {
00239 stream.seek(pos);
00240 break;
00241 }
00242 }
00243
00244 while( !stream.atEnd() )
00245 {
00246 int r = 0, g = 0, b = 0;
00247 stream >> r >> g >> b;
00248 line = stream.readLine().trimmed();
00249 p->colors.push_back(qMakePair(QColor(r, g, b), line));
00250 }
00251
00252 emit colorsChanged(p->colors);
00253 setDirty(false);
00254
00255 return true;
00256 }
00257
00258 ColorPalette ColorPalette::fromFile(const QString& name)
00259 {
00260 ColorPalette p;
00261 p.load(name);
00262 return p;
00263 }
00264
00265 bool ColorPalette::save(const QString& filename)
00266 {
00267 setFileName(filename);
00268 return save();
00269 }
00270
00271 bool ColorPalette::save()
00272 {
00273 QString filename = p->fileName;
00274 if ( filename.isEmpty() )
00275 {
00276 filename = unnamed(p->name)+".gpl";
00277 }
00278
00279 QFile file(filename);
00280 if ( !file.open(QFile::Text|QFile::WriteOnly) )
00281 return false;
00282
00283 QTextStream stream(&file);
00284
00285 stream << "GIMP Palette\n";
00286 stream << "Name: " << unnamed(p->name) << '\n';
00287 if ( p->columns )
00288 stream << "Columns: " << p->columns << '\n';
00290 stream << "#\n";
00291
00292 for ( int i = 0; i < p->colors.size(); i++ )
00293 {
00294 stream << qSetFieldWidth(3) << p->colors[i].first.red() << qSetFieldWidth(0) << ' '
00295 << qSetFieldWidth(3) << p->colors[i].first.green() << qSetFieldWidth(0) << ' '
00296 << qSetFieldWidth(3) << p->colors[i].first.blue() << qSetFieldWidth(0) << '\t'
00297 << unnamed(p->colors[i].second) << '\n';
00298 }
00299
00300 if ( !file.error() )
00301 {
00302 setDirty(false);
00303 return true;
00304 }
00305
00306 return false;
00307 }
00308
00309
00310 QString ColorPalette::fileName() const
00311 {
00312 return p->fileName;
00313 }
00314
00315
00316 void ColorPalette::setColumns(int columns)
00317 {
00318 if ( columns <= 0 )
00319 columns = 0;
00320
00321 if ( columns != p->columns )
00322 {
00323 setDirty(true);
00324 emit columnsChanged( p->columns = columns );
00325 }
00326 }
00327
00328 void ColorPalette::setColors(const QVector<QColor>& colors)
00329 {
00330 p->colors.clear();
00331 foreach(const QColor& col, colors)
00332 p->colors.push_back(qMakePair(col,QString()));
00333 setDirty(true);
00334 emit colorsChanged(p->colors);
00335 }
00336
00337 void ColorPalette::setColors(const QVector<QPair<QColor,QString> >& colors)
00338 {
00339 p->colors = colors;
00340 setDirty(true);
00341 emit colorsChanged(p->colors);
00342 }
00343
00344
00345 void ColorPalette::setColorAt(int index, const QColor& color)
00346 {
00347 if ( !p->valid_index(index) )
00348 return;
00349
00350 p->colors[index].first = color;
00351
00352 setDirty(true);
00353 emit colorChanged(index);
00354 emit colorsUpdated(p->colors);
00355 }
00356
00357 void ColorPalette::setColorAt(int index, const QColor& color, const QString& name)
00358 {
00359 if ( !p->valid_index(index) )
00360 return;
00361
00362 p->colors[index].first = color;
00363 p->colors[index].second = name;
00364 setDirty(true);
00365 emit colorChanged(index);
00366 emit colorsUpdated(p->colors);
00367 }
00368
00369 void ColorPalette::setNameAt(int index, const QString& name)
00370 {
00371 if ( !p->valid_index(index) )
00372 return;
00373
00374 p->colors[index].second = name;
00375
00376 setDirty(true);
00377 emit colorChanged(index);
00378 emit colorsUpdated(p->colors);
00379 }
00380
00381
00382 void ColorPalette::appendColor(const QColor& color, const QString& name)
00383 {
00384 p->colors.push_back(qMakePair(color,name));
00385 setDirty(true);
00386 emit colorAdded(p->colors.size()-1);
00387 emit colorsUpdated(p->colors);
00388 }
00389
00390 void ColorPalette::insertColor(int index, const QColor& color, const QString& name)
00391 {
00392 if ( index < 0 || index > p->colors.size() )
00393 return;
00394
00395 p->colors.insert(index, qMakePair(color, name));
00396
00397 setDirty(true);
00398 emit colorAdded(index);
00399 emit colorsUpdated(p->colors);
00400 }
00401
00402 void ColorPalette::eraseColor(int index)
00403 {
00404 if ( !p->valid_index(index) )
00405 return;
00406
00407 p->colors.remove(index);
00408
00409 setDirty(true);
00410 emit colorRemoved(index);
00411 emit colorsUpdated(p->colors);
00412 }
00413
00414 void ColorPalette::setName(const QString& name)
00415 {
00416 setDirty(true);
00417 p->name = name;
00418 }
00419
00420 void ColorPalette::setFileName(const QString& name)
00421 {
00422 setDirty(true);
00423 p->fileName = name;
00424 }
00425
00426 QString ColorPalette::unnamed(const QString& name) const
00427 {
00428 return name.isEmpty() ? tr("Unnamed") : name;
00429 }
00430
00431
00432 QPixmap ColorPalette::preview(const QSize& size, const QColor& background) const
00433 {
00434 if ( !size.isValid() || p->colors.empty() )
00435 return QPixmap();
00436
00437 QPixmap out( size );
00438 out.fill(background);
00439 QPainter painter(&out);
00440
00441 int count = p->colors.size();
00442 int columns = p->columns;
00443 if ( !columns )
00444 columns = std::ceil( std::sqrt( count * float(size.width()) / size.height() ) );
00445 int rows = std::ceil( float(count) / columns );
00446 QSizeF color_size(float(size.width()) / columns, float(size.height()) / rows);
00447
00448 for ( int y = 0, i = 0; y < rows && i < count; y++ )
00449 {
00450 for ( int x = 0; x < columns && i < count; x++, i++ )
00451 {
00452 painter.fillRect(QRectF(x*color_size.width(), y*color_size.height(),
00453 color_size.width(), color_size.height()),
00454 p->colors[i].first
00455 );
00456 }
00457 }
00458
00459 return out;
00460 }
00461
00462 bool ColorPalette::dirty() const
00463 {
00464 return p->dirty;
00465 }
00466
00467 void ColorPalette::setDirty(bool dirty)
00468 {
00469 if ( dirty != p->dirty )
00470 emit dirtyChanged( p->dirty = dirty );
00471 }
00472
00473 QVector<QColor> ColorPalette::onlyColors() const
00474 {
00475 QVector<QColor> out;
00476 out.reserve(p->colors.size());
00477 for ( int i = 0; i < p->colors.size(); i++ )
00478 out.push_back(p->colors[i].first);
00479 return out;
00480 }
00481
00482 QVector<QRgb> ColorPalette::colorTable() const
00483 {
00484 QVector<QRgb> out;
00485 out.reserve(p->colors.size());
00486 for ( const auto& color_pair : p->colors )
00487 out.push_back(color_pair.first.rgba());
00488 return out;
00489 }
00490
00491 ColorPalette ColorPalette::fromColorTable(const QVector<QRgb>& table)
00492 {
00493 ColorPalette palette;
00494 palette.loadColorTable(table);
00495 return palette;
00496 }
00497
00498 }