QPair<int, int> IndexColorPalette::getNeighbours(int mainClr) const { QVector<float> diffs; diffs.resize(numColors()); for(int i = 0; i < numColors(); ++i) diffs[i] = similarity(colors[i], colors[mainClr]); int darkerColor = 0; int brighterColor = 0; for(int i = 0; i < numColors(); ++i) { if(i != mainClr) { if(colors[i].L < colors[mainClr].L) { if(diffs[i] > diffs[darkerColor]) darkerColor = i; } else { if(diffs[i] > diffs[brighterColor]) brighterColor = i; } } } return qMakePair(darkerColor, brighterColor); }
QwtColorTable colorTable() const { QwtColorTable table(numColors()); for ( int i = 0; i < numColors(); i++ ) table[i] = color(i); return table; }
LabColor IndexColorPalette::getNearestIndex(LabColor clr) const { QVector<float> diffs; diffs.resize(numColors()); for(int i = 0; i < numColors(); ++i) diffs[i] = similarity(colors[i], clr); int primaryColor = 0; for(int i = 0; i < numColors(); ++i) if(diffs[i] > diffs[primaryColor]) primaryColor = i; return colors[primaryColor]; }
void KIconEditGrid::editPasteAsNew() { bool ok = false; const QImage *tmp = clipboardImage(ok); if(ok) { *img = *tmp; load(img); modified = true; //repaint(viewRect(), false); p = *img; emit changed(QPixmap(p)); emit sizechanged(numCols(), numRows()); emit colorschanged(numColors(), data()); emit newmessage(i18n("Done pasting")); } else { QString msg = i18n("Invalid pixmap data in clipboard!\n"); KMsgBox::message(this, i18n("Warning"), msg.data()); } delete tmp; }
void KIconEditGrid::loadBlank( int w, int h ) { img->create(w, h, 32); img->fill(TRANSPARENT); setNumRows(h); setNumCols(w); fill(TRANSPARENT); emit sizechanged(numCols(), numRows()); emit colorschanged(numColors(), data()); }
void KIconEditGrid::load( QImage *image) { debug("KIconEditGrid::load"); setUpdatesEnabled(false); if(image != 0L) { *img = *fixTransparence(image); //*img = image->convertDepth(32); //img->setAlphaBuffer(true); } else { QString msg = i18n("There was an error loading a blank image.\n"); KMsgBox::message (this, i18n("Error"), msg.data()); return; } setNumRows(img->height()); setNumCols(img->width()); for(int y = 0; y < numRows(); y++) { uint *l = (uint*)img->scanLine(y); for(int x = 0; x < numCols(); x++, l++) { /* //uint gray = (qRgb(200, 200, 200) | OPAQUE_MASK); //uint bc = (TRANSPARENT | OPAQUE_MASK); //if(*l == gray || *l == bc || *l < 0xff000000) // this is a hack but I couldn't save it as transparent otherwise if(*l < 0xff000000 || *l == (TRANSPARENT|OPAQUE_MASK)) // this is a hack but I couldn't save it as transparent otherwise { *l = TRANSPARENT; } //debug("KIcnGrid::load: %d %%", (((y*ncols)+x)/imgsize) * 100); //debug("KIconEditGrid::load: RGB: %d %d %d", qRed(*l), qGreen(*l), qBlue(*l)); */ setColor((y*numCols())+x, *l, false); } //debug("Row: %d", y); kapp->processEvents(200); } updateColors(); emit sizechanged(numCols(), numRows()); emit colorschanged(numColors(), data()); emit changed(pixmap()); setUpdatesEnabled(true); emit needPainting(); //repaint(viewRect(), false); }
void IndexColorPalette::mergeMostReduantColors() { QVector<ColorString> colorHood; colorHood.resize(numColors()); for(int i = 0; i < numColors(); ++i) { colorHood[i].color = i; colorHood[i].neighbours = getNeighbours(i); float lSimilarity = 0.05f, rSimilarity = 0.05f; // There will be exactly 2 colors that have only 1 neighbour, the darkest and the brightest, we don't want to remove those if(colorHood[i].neighbours.first != -1) lSimilarity = similarity(colors[colorHood[i].neighbours.first], colors[i]); if(colorHood[i].neighbours.second != -1) rSimilarity = similarity(colors[colorHood[i].neighbours.second], colors[i]); colorHood[i].similarity = (lSimilarity + rSimilarity) / 2; } int mostSimilarColor = 0; for(int i = 0; i < numColors(); ++i) if(colorHood[i].similarity > colorHood[mostSimilarColor].similarity) mostSimilarColor = i; int darkerIndex = colorHood[mostSimilarColor].neighbours.first; int brighterIndex = colorHood[mostSimilarColor].neighbours.second; if(darkerIndex != -1 && brighterIndex != -1) { LabColor clrA = colors[darkerIndex]; LabColor clrB = colors[mostSimilarColor]; LabColor clrC = colors[brighterIndex]; // Remove two, add one = 1 color less colors.remove(darkerIndex); colors.remove(mostSimilarColor); //colors.remove(brighterIndex); insertShades(clrA, clrB, 1); //insertShades(clrB, clrC, 1); } }
QImage *KIconEditGrid::getSelection(bool cut) { const QRect rect = pntarray.boundingRect(); int nx = 0, ny = 0, nw = 0, nh = 0; rect.rect(&nx, &ny, &nw, &nh); QImage *tmp = new QImage(nw, nh, 32); tmp->fill(TRANSPARENT); int s = pntarray.size(); //((rect.size().width()) * (rect.size().height())); for(int i = 0; i < s; i++) { int x = pntarray[i].x(); int y = pntarray[i].y(); if(img->valid(x, y) && rect.contains(QPoint(x, y))) { *((uint*)tmp->scanLine(y-ny) + (x-nx)) = *((uint*)img->scanLine(y) + x); if(cut) { *((uint*)img->scanLine(y) + x) = TRANSPARENT; setColor( (y*numCols()) + x, TRANSPARENT, false ); } } } QPointArray a(pntarray.copy()); pntarray.resize(0); drawPointArray(a, Mark); emit selecteddata(false); if(cut) { updateColors(); repaint(rect.x()*cellSize(), rect.y()*cellSize(), rect.width()*cellSize(), rect.height()*cellSize(), false); p = *img; emit changed(p); emit colorschanged(numColors(), data()); emit newmessage(i18n("Selected area cutted")); } else emit newmessage(i18n("Selected area copied")); return tmp; }
void PadConfiguration::drawPicture() { if(_picture == nullptr) { drawMapping(); return; } uint8_t* pointer = _picture; uint16_t current(0); #define READ_8 current = *pointer; ++pointer; #define READ_16 current = *((uint16_t*)pointer); ++pointer; ++pointer; READ_16; TFTscreen.fillScreen(current); READ_8; uint16_t numColors(current); for(uint16_t currentColor = 0; currentColor < numColors; ++currentColor) { READ_16; uint16_t color = current; READ_16; uint32_t numRects(current); for(uint16_t currentRect = 0; currentRect < numRects; ++currentRect) { READ_8; int16_t x(current); READ_8; int16_t y(current); READ_8; int16_t w(current); READ_8; int16_t h(current); TFTscreen.fillRect(x, y, w, h, color); } READ_16; uint16_t numVLines(current); for(uint16_t currentVLine = 0; currentVLine < numVLines; ++currentVLine) { READ_8; int16_t x(current); READ_8; int16_t y(current); READ_8; int16_t l(current); TFTscreen.drawFastVLine(x, y, l, color); } READ_16; uint16_t numHLines(current); for(uint16_t currentHLine = 0; currentHLine < numHLines; ++currentHLine) { READ_8; int16_t x(current); READ_8; int16_t y(current); READ_8; int16_t l(current); TFTscreen.drawFastHLine(x, y, l, color); } READ_16; uint16_t numPix(current); for(uint16_t currentPix = 0; currentPix < numPix; ++currentPix) { READ_8; int16_t x(current); READ_8; int16_t y(current); TFTscreen.drawPixel(x, y, color); } } #undef READ_8 #undef READ_16 }
void KIconEditGrid::editPaste(bool paste) { bool ok = false; const QImage *tmp = clipboardImage(ok); fixTransparence((QImage*)tmp); Properties *pprops = props(this); if(ok) { if( (tmp->size().width() > img->size().width()) || (tmp->size().height() > img->size().height()) ) { if(KMsgBox::yesNo(this, i18n("Warning"), i18n("The clipboard image is larger than the current image!\nPaste as new image?")) == 1) { editPasteAsNew(); } delete tmp; return; } else if(!paste) { ispasting = true; cbsize = tmp->size(); //debug("insrect size: %d x %d", insrect.width(), insrect.height()); return; emit newmessage(i18n("Pasting")); } else { //debug("KIconEditGrid: Pasting at: %d x %d", insrect.x(), insrect.y()); QApplication::setOverrideCursor(waitCursor); for(int y = insrect.y(), ny = 0; y < numRows(), ny < insrect.height(); y++, ny++) { uint *l = ((uint*)img->scanLine(y)+insrect.x()); uint *cl = (uint*)tmp->scanLine(ny); for(int x = insrect.x(), nx = 0; x < numCols(), nx < insrect.width(); x++, nx++, l++, cl++) { if(*cl != TRANSPARENT || pprops->pastetransparent) { *l = *cl; setColor((y*numCols())+x, (uint)*cl, false); } } } updateColors(); repaint(insrect.x()*cellSize(), insrect.y()*cellSize(), insrect.width()*cellSize(), insrect.height()*cellSize(), false); QApplication::restoreOverrideCursor(); modified = true; p = *img; emit changed(QPixmap(p)); emit sizechanged(numCols(), numRows()); emit colorschanged(numColors(), data()); emit newmessage(i18n("Done pasting")); } } else { QString msg = i18n("Invalid pixmap data in clipboard!\n"); KMsgBox::message(this, i18n("Warning"), msg.data()); } delete tmp; }
void ColorReducer::reduce(Logger *log, unsigned minColors) { /* * This is a median-cut style color reducer. We start with a * single box that encloses all pixels in the image, and we * iteratively subdivide boxes until we reach the targetted MSE * metric. */ if (log) log->taskBegin("Optimizing palette"); if (colors.size() >= 1) { // Base case: One single color. box root = { 0, (unsigned)colors.size() }; boxes.clear(); boxes.push_back(root); boxQueue.clear(); boxQueue.push_back(0); /* * Keep splitting until all colors are within the acceptable * tolerance, or we run out of boxes. * * It's actually much more expensive to calculate the MSE and the * color LUT than it is to perform palette splits. So, we're extra * careful here to minimize the overhead of error measurement. The * LUT is calculated lazily, and we always avoid error * calculations for colors that couldn't possibly contribute to * the success or failure of the current iteration: We always stop * after finding one color that's out of the acceptable tolerance, * and we avoid re-checking colors that have already been solved. * * The lazy LUT calculations are handled automatically by * nearest(), but we use a stack of not-yet-solved colors here in * order to reduce the number of LUT entries we ever have to * touch. */ std::vector<uint16_t> errorStack; for (unsigned i = 0; i < LUT_SIZE; i++) errorStack.push_back(i); while (!boxQueue.empty()) { /* * Try to reduce the size of the error stack. Any colors that * are now within range can be popped off of it permanently. */ while (errorStack.size()) { unsigned v = errorStack.back(); RGB565 color((uint16_t)v); double maxMSE = colorMSE[v]; double mse = CIELab(nearest(color)).meanSquaredError(CIELab(color)); if (mse <= maxMSE) errorStack.pop_back(); else break; } if (log && (boxes.size() % 64 == 0 || !errorStack.size())) log->taskProgress("%d colors in palette", (int)boxes.size()); if (!errorStack.size() && numColors() >= minColors) break; /* * Perform the next split */ unsigned boxIndex = *boxQueue.begin(); struct box& b = boxes[boxIndex]; boxQueue.pop_front(); int major = CIELab::findMajorAxis(&colors[b.begin], b.end - b.begin); std::sort(colors.begin() + b.begin, colors.begin() + b.end, CIELab::sortAxis(major)); splitBox(b); // Invalidate all inverseLUT entries newestLUTStamp++; } } if (log) log->taskEnd(); }