void SvgTinter::init() { if ( m_lastPalette != App::instance()->palette() || m_firstRun ) { m_tintMap.insert( "#666765", App::instance()->palette().window().color().name() ); //insert a color for bright ( highlight color ) m_tintMap.insert( "#66ffff", App::instance()->palette().highlight().color().name() ); //a slightly lighter than window color: m_tintMap.insert( "#e8e8e8", blendColors( App::instance()->palette().window().color(), "#ffffff", 90 ).name() ); //a slightly darker than window color: m_tintMap.insert( "#565755", blendColors( App::instance()->palette().window().color(), "#000000", 90 ).name() ); //list background: #ifdef Q_WS_MAC m_tintMap.insert( "#f0f0f0", blendColors( App::instance()->palette().window().color(), "#000000", 90 ).name() ); m_tintMap.insert( "#ffffff", blendColors( App::instance()->palette().window().color(), "#000000", 98 ).name() ); #else m_tintMap.insert( "#f0f0f0", App::instance()->palette().base().color().name() ); #endif //alternate list background: m_tintMap.insert( "#e0e0e0", App::instance()->palette().alternateBase().color().name() ); //highlight/window mix: m_tintMap.insert( "#123456", blendColors( App::instance()->palette().window().color(), App::instance()->palette().highlight().color().name(), 80 ).name() ); //text color, useful for adding contrast m_tintMap.insert( "#010101", App::instance()->palette().text().color().name() ); m_lastPalette = App::instance()->palette(); } }
void GfxPalette::setEGA() { int curColor; byte color1, color2; _sysPalette.colors[1].r = 0x000; _sysPalette.colors[1].g = 0x000; _sysPalette.colors[1].b = 0x0AA; _sysPalette.colors[2].r = 0x000; _sysPalette.colors[2].g = 0x0AA; _sysPalette.colors[2].b = 0x000; _sysPalette.colors[3].r = 0x000; _sysPalette.colors[3].g = 0x0AA; _sysPalette.colors[3].b = 0x0AA; _sysPalette.colors[4].r = 0x0AA; _sysPalette.colors[4].g = 0x000; _sysPalette.colors[4].b = 0x000; _sysPalette.colors[5].r = 0x0AA; _sysPalette.colors[5].g = 0x000; _sysPalette.colors[5].b = 0x0AA; _sysPalette.colors[6].r = 0x0AA; _sysPalette.colors[6].g = 0x055; _sysPalette.colors[6].b = 0x000; _sysPalette.colors[7].r = 0x0AA; _sysPalette.colors[7].g = 0x0AA; _sysPalette.colors[7].b = 0x0AA; _sysPalette.colors[8].r = 0x055; _sysPalette.colors[8].g = 0x055; _sysPalette.colors[8].b = 0x055; _sysPalette.colors[9].r = 0x055; _sysPalette.colors[9].g = 0x055; _sysPalette.colors[9].b = 0x0FF; _sysPalette.colors[10].r = 0x055; _sysPalette.colors[10].g = 0x0FF; _sysPalette.colors[10].b = 0x055; _sysPalette.colors[11].r = 0x055; _sysPalette.colors[11].g = 0x0FF; _sysPalette.colors[11].b = 0x0FF; _sysPalette.colors[12].r = 0x0FF; _sysPalette.colors[12].g = 0x055; _sysPalette.colors[12].b = 0x055; _sysPalette.colors[13].r = 0x0FF; _sysPalette.colors[13].g = 0x055; _sysPalette.colors[13].b = 0x0FF; _sysPalette.colors[14].r = 0x0FF; _sysPalette.colors[14].g = 0x0FF; _sysPalette.colors[14].b = 0x055; _sysPalette.colors[15].r = 0x0FF; _sysPalette.colors[15].g = 0x0FF; _sysPalette.colors[15].b = 0x0FF; for (curColor = 0; curColor <= 15; curColor++) { _sysPalette.colors[curColor].used = 1; } // Now setting colors 16-254 to the correct mix colors that occur when not doing a dithering run on // finished pictures for (curColor = 0x10; curColor <= 0xFE; curColor++) { _sysPalette.colors[curColor].used = 1; color1 = curColor & 0x0F; color2 = curColor >> 4; _sysPalette.colors[curColor].r = blendColors(_sysPalette.colors[color1].r, _sysPalette.colors[color2].r); _sysPalette.colors[curColor].g = blendColors(_sysPalette.colors[color1].g, _sysPalette.colors[color2].g); _sysPalette.colors[curColor].b = blendColors(_sysPalette.colors[color1].b, _sysPalette.colors[color2].b); } _sysPalette.timestamp = 1; setOnScreen(); }
/* blended color cycle */ static void ColorCycleBlend (TLN_Palette srcpalette, TLN_Palette dstpalette, struct Strip* strip, int t) { int c; int idx0, idx1; uint8_t *srcptr0, *srcptr1, *dstptr; int t0 = strip->t0; int t1 = strip->timer; int count = strip->count; int steps = strip->pos; /* map [t0 - t1] to [0 - 255] */ int f1 = lerp (t, t0,t1, 0,255); int f0 = 255 - f1; for (c=0; c<count; c++) { if (strip->dir) { idx0 = (c - steps + count) % count; idx1 = (c - steps - 1 + count) % count; } else { idx0 = (c + steps) % count; idx1 = (c + steps + 1) % count; } srcptr0 = GetPaletteData (srcpalette, strip->first + idx0); srcptr1 = GetPaletteData (srcpalette, strip->first + idx1); dstptr = GetPaletteData (dstpalette, strip->first + c); blendColors (srcptr0, srcptr1, dstptr, f0, f1); } }
sf::Color ColorGradient::sampleColor(float position) const { // Make sure the positions 0 and 1 are set assert(mColors.count(0.f) && mColors.count(1.f)); // Find first entry >= position, return color if entry == position auto nextColor = mColors.lower_bound(position); if (nextColor->first == position) return nextColor->second; // Find first entry < position auto prevColor = std::prev(nextColor); // Interpolate color between 2 entries float interpolation = (position - prevColor->first) / (nextColor->first - prevColor->first); return blendColors(prevColor->second, nextColor->second, interpolation); }
QColor TimelineColorScheme::frameColor(bool present, bool active) const { QColor color = Qt::transparent; if (present && !active) { color = m_d->baseColor; } else if (present && active) { QColor bgColor = qApp->palette().color(QPalette::Base); int darkenCoeff = bgColor.value() > 128 ? 130 : 80; color = m_d->baseColor.darker(darkenCoeff); } else if (!present && active) { QColor bgColor = qApp->palette().color(QPalette::Base); return blendColors(m_d->baseColor, bgColor, 0.2); } return color; }
void DialogScene::render() { Renderer::Ref renderer = m_manager->getRenderer(); CL_Sizef window = renderer->getGCSize(); // render underlying scene: m_topScene->render(); // fade out a little: CL_Rectf rcShadow = m_rcBub; rcShadow.translate(CL_Pointf(5.0f, 5.0f)); CL_Colorf bubColor = blendColors(getPhraseColor(m_newtype), getPhraseColor(m_oldtype), m_percent); CL_Draw::fill(renderer->getGC(), rcShadow, CL_Colorf::black); CL_Draw::fill(renderer->getGC(), m_rcBub, bubColor); m_layout.draw_layout(renderer->getGC()); }
LRESULT SpyFrame::onCustomDraw(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/) { LPNMLVCUSTOMDRAW plvcd = reinterpret_cast<LPNMLVCUSTOMDRAW>(pnmh); if (CDDS_PREPAINT == plvcd->nmcd.dwDrawStage) return CDRF_NOTIFYITEMDRAW; if (CDDS_ITEMPREPAINT == plvcd->nmcd.dwDrawStage) { ClientManagerListener::SearchReply re = (ClientManagerListener::SearchReply)(plvcd->nmcd.lItemlParam); //check if the file or dir is a dupe, then use the dupesetting color if (re == ClientManagerListener::SEARCH_HIT) plvcd->clrTextBk = SETTING(DUPE_COLOR); else if (re == ClientManagerListener::SEARCH_PARTIAL_HIT) //if it's a partial hit, try to use some simple blending plvcd->clrTextBk = blendColors(SETTING(DUPE_COLOR), SETTING(BACKGROUND_COLOR)); #ifdef FLYLINKDC_USE_LIST_VIEW_MATTRESS Colors::alternationBkColor(plvcd); // [+] IRainman #endif } return CDRF_DODEFAULT; }
QColor VQMCSimpleChannelRenderer<ShapeDraw>:: renderChannel(unsigned camera, unsigned ncameras, const VQMCChannelData& data, const VQMCCameraDataLimits& limits, bool clip, QPainter& painter, bool draw_fast) { QColor background_color = painter.background().color(); QColor return_color = background_color; if(data.fNoDraw)return return_color; if((!data.fNoData)&&(!data.fSuppress)) { double lim = qMax(fabs(limits.fLimitLo.fValue), fabs(limits.fLimitHi.fValue)); double x = data.fValue/lim; if(clip) { if(x>1)x=1; else if(x<-1)x=-1; } double radius = 0; QColor color = background_color; bool found_color = getChannelBaseColor(color, camera, data, limits); if((draw_fast)&&(color.alphaF()<1.0)) blendColors(1.0-color.alphaF(),color,background_color); QBrush brush(color,Qt::SolidPattern); switch(fValueMode) { case VM_RADIUS: radius = fabs(x); if(x<0)brush.setStyle(Qt::NoBrush); else return_color = color; break; case VM_AREA: radius = sqrt(fabs(x)); if(x<0)brush.setStyle(Qt::NoBrush); else return_color = color; break; case VM_ALPHA: if(x>=1) { return_color = color; radius = 1; } else if(x>0) { if(draw_fast)blendColors(x,color,background_color); else color.setAlphaF(color.alphaF()*x); brush.setColor(color); return_color = color; radius = 1; } else { radius = 0; } break; case VM_COLOR: radius = 1; return_color = color; break; } if((found_color)&&(radius>0)) { QPen saved_pen = painter.pen(); QBrush saved_brush = painter.brush(); QPen pen(saved_pen); pen.setWidth(0); if((radius<=1.0)||(!fDrawOutline))pen.setColor(color); else pen.setColor(fOutlineColor); painter.setPen(pen); painter.setBrush(brush); painter.scale(radius,radius); ShapeDraw::draw(camera,ncameras,painter,draw_fast); if((fDrawOutline)&&(radius<=1.0)) { const qreal rescale = 1.0/radius; painter.scale(rescale,rescale); pen.setColor(fOutlineColor); painter.setPen(pen); painter.setBrush(QBrush()); ShapeDraw::draw(camera,ncameras,painter,draw_fast); } painter.setBrush(saved_brush); painter.setPen(saved_pen); } else if(fDrawOutline) { QPen saved_pen = painter.pen(); QPen pen = painter.pen(); pen.setWidth(0); pen.setColor(fOutlineColor); painter.setPen(pen); ShapeDraw::draw(camera,ncameras,painter,draw_fast); painter.setPen(saved_pen); } } else if(fDrawOutline) { QPen saved_pen = painter.pen(); QPen pen = painter.pen(); pen.setWidth(0); pen.setColor(fOutlineColor); painter.setPen(pen); ShapeDraw::draw(camera,ncameras,painter,draw_fast); painter.setPen(saved_pen); } return return_color; }
static QColor blendRoles( QPalette::ColorRole src, QPalette::ColorRole dest, double alpha ) { QPalette palette = QApplication::palette(); return blendColors( palette.color( src ), palette.color( dest ), alpha ); }
void TextLayer::recreateTexture(VidgfxContext *gfx) { if(!m_isTexDirty) return; // Don't waste any time if it hasn't changed m_isTexDirty = false; // Delete existing texture if one exists if(m_texture != NULL) vidgfx_context_destroy_tex(gfx, m_texture); m_texture = NULL; // Determine texture size. We need to keep in mind that the text in the // document might extend outside of the layer's bounds. m_document.setTextWidth(m_rect.width()); QSize size( (int)ceilf(m_document.size().width()), (int)ceilf(m_document.size().height())); if(m_document.isEmpty() || size.isEmpty()) { // Nothing to display return; } // Create temporary canvas. We need to be careful here as text is rendered // differently on premultiplied vs non-premultiplied pixel formats. On a // premultiplied format text is rendered with subpixel rendering enabled // while on a non-premultiplied format it is not. As we don't want subpixel // rendering we use the standard ARGB32 format. QSize imgSize( size.width() + m_strokeSize * 2, size.height() + m_strokeSize * 2); QImage img(imgSize, QImage::Format_ARGB32); img.fill(Qt::transparent); QPainter p(&img); p.setRenderHint(QPainter::Antialiasing, true); // Render text //m_document.drawContents(&p); // Render stroke if(m_strokeSize > 0) { #define STROKE_TECHNIQUE 0 #if STROKE_TECHNIQUE == 0 // Technique 0: Use QTextDocument's built-in text outliner //quint64 timeStart = App->getUsecSinceExec(); QTextDocument *outlineDoc = m_document.clone(this); QTextCharFormat format; QPen pen(m_strokeColor, (double)(m_strokeSize * 2)); pen.setJoinStyle(Qt::RoundJoin); format.setTextOutline(pen); QTextCursor cursor(outlineDoc); cursor.select(QTextCursor::Document); cursor.mergeCharFormat(format); // Take into account the stroke offset p.translate(m_strokeSize, m_strokeSize); //quint64 timePath = App->getUsecSinceExec(); outlineDoc->drawContents(&p); delete outlineDoc; //quint64 timeEnd = App->getUsecSinceExec(); //appLog() << "Path time = " << (timePath - timeStart) << " usec"; //appLog() << "Render time = " << (timeEnd - timePath) << " usec"; //appLog() << "Full time = " << (timeEnd - timeStart) << " usec"; #elif STROKE_TECHNIQUE == 1 // Technique 1: Create a text QPainterPath and stroke it quint64 timeStart = App->getUsecSinceExec(); // Create the path for the text's stroke QPainterPath path; QTextBlock &block = m_document.firstBlock(); int numBlocks = m_document.blockCount(); for(int i = 0; i < numBlocks; i++) { QTextLayout *layout = block.layout(); for(int j = 0; j < layout->lineCount(); j++) { QTextLine &line = layout->lineAt(j); const QString text = block.text().mid( line.textStart(), line.textLength()); QPointF pos = layout->position() + line.position(); pos.ry() += line.ascent(); //appLog() << pos << ": " << text; path.addText(pos, block.charFormat().font(), text); } block = block.next(); } quint64 timePath = App->getUsecSinceExec(); path = path.simplified(); // Fixes gaps with large stroke sizes quint64 timeSimplify = App->getUsecSinceExec(); // Render the path //p.strokePath(path, QPen(m_strokeColor, m_strokeSize)); // Convert it to a stroke QPainterPathStroker stroker; stroker.setWidth(m_strokeSize); //stroker.setCurveThreshold(2.0); stroker.setJoinStyle(Qt::RoundJoin); path = stroker.createStroke(path); // Render the path p.fillPath(path, m_strokeColor); quint64 timeEnd = App->getUsecSinceExec(); appLog() << "Path time = " << (timePath - timeStart) << " usec"; appLog() << "Simplify time = " << (timeSimplify - timePath) << " usec"; appLog() << "Render time = " << (timeEnd - timeSimplify) << " usec"; appLog() << "Full time = " << (timeEnd - timeStart) << " usec"; #elif STROKE_TECHNIQUE == 2 // Technique 2: Similar to technique 1 but do each block separately quint64 timeStart = App->getUsecSinceExec(); quint64 timeTotalSimplify = 0; quint64 timeTotalRender = 0; // Create the path for the text's stroke QTextBlock &block = m_document.firstBlock(); int numBlocks = m_document.blockCount(); for(int i = 0; i < numBlocks; i++) { // Convert this block to a painter path QPainterPath path; QTextLayout *layout = block.layout(); for(int j = 0; j < layout->lineCount(); j++) { QTextLine &line = layout->lineAt(j); const QString text = block.text().mid( line.textStart(), line.textLength()); QPointF pos = layout->position() + line.position() + QPointF(m_strokeSize, m_strokeSize); pos.ry() += line.ascent(); //appLog() << pos << ": " << text; path.addText(pos, block.charFormat().font(), text); } // Prevent gaps appearing at larger stroke sizes quint64 timeA = App->getUsecSinceExec(); path = path.simplified(); quint64 timeB = App->getUsecSinceExec(); timeTotalSimplify += timeB - timeA; // Render the path QPen pen(m_strokeColor, m_strokeSize * 2); pen.setJoinStyle(Qt::RoundJoin); p.strokePath(path, pen); timeA = App->getUsecSinceExec(); timeTotalRender += timeA - timeB; // Iterate block = block.next(); } // Make the final draw take into account the stroke offset p.translate(m_strokeSize, m_strokeSize); quint64 timeEnd = App->getUsecSinceExec(); appLog() << "Simplify time = " << timeTotalSimplify << " usec"; appLog() << "Render time = " << timeTotalRender << " usec"; appLog() << "Full time = " << (timeEnd - timeStart) << " usec"; #elif STROKE_TECHNIQUE == 3 // Technique 3: Raster brute-force where for each destination pixel // we measure the distance to the closest opaque source pixel quint64 timeStart = App->getUsecSinceExec(); // Get bounding region based on text line bounding rects QRegion region; QTextBlock &block = m_document.firstBlock(); int numBlocks = m_document.blockCount(); for(int i = 0; i < numBlocks; i++) { QTextLayout *layout = block.layout(); for(int j = 0; j < layout->lineCount(); j++) { QTextLine &line = layout->lineAt(j); const QString text = block.text().mid( line.textStart(), line.textLength()); QRect rect = line.naturalTextRect() .translated(layout->position()).toAlignedRect(); if(rect.isEmpty()) continue; // Don't add empty rectangles rect.adjust(0, 0, 1, 0); // QTextLine is incorrect? rect.adjust( -m_strokeSize, -m_strokeSize, m_strokeSize, m_strokeSize); //appLog() << rect; region += rect; } // Iterate block = block.next(); } quint64 timeRegion = App->getUsecSinceExec(); #if 0 // Debug bounding region QPainterPath regionPath; regionPath.addRegion(region); regionPath.setFillRule(Qt::WindingFill); p.fillPath(regionPath, QColor(255, 0, 0, 128)); #endif // 0 // We cannot read and write to the same image at the same time so // create a second one. Note that this is not premultiplied. QImage imgOut(size, QImage::Format_ARGB32); imgOut.fill(Qt::transparent); // Do distance calculation. We assume that non-fully transparent // pixels are always next to a fully opaque one so if the closest // "covered" pixel is not fully opaque then we can use that pixel's // opacity to determine the distance to the shape's edge. for(int y = 0; y < img.height(); y++) { for(int x = 0; x < img.width(); x++) { if(!region.contains(QPoint(x, y))) continue; float dist = getDistance(img, x, y, m_strokeSize); // We fake antialiasing by blurring the edge by 1px float outEdge = (float)m_strokeSize; if(dist >= outEdge) continue; // Outside stroke completely float opacity = qMin(1.0f, outEdge - dist); QColor col = m_strokeColor; col.setAlphaF(col.alphaF() * opacity); // Blend the stroke so that it appears under the existing // pixel data QRgb origRgb = img.pixel(x, y); QColor origCol(origRgb); origCol.setAlpha(qAlpha(origRgb)); col = blendColors(col, origCol, 1.0f); imgOut.setPixel(x, y, col.rgba()); } } quint64 timeRender = App->getUsecSinceExec(); // Swap image data p.end(); img = imgOut; p.begin(&img); quint64 timeEnd = App->getUsecSinceExec(); appLog() << "Region time = " << (timeRegion - timeStart) << " usec"; appLog() << "Render time = " << (timeRender - timeRegion) << " usec"; appLog() << "Swap time = " << (timeEnd - timeRender) << " usec"; appLog() << "Full time = " << (timeEnd - timeStart) << " usec"; #endif // STROKE_TECHNIQUE } // Render text m_document.drawContents(&p); // Convert the image to a GPU texture m_texture = vidgfx_context_new_tex(gfx, img); // Preview texture for debugging //img.save(App->getDataDirectory().filePath("Preview.png")); }
void BaseEditor::paintEvent(QPaintEvent *e) { //copy from QPlainTextEditor QPainter painter(viewport()); Q_ASSERT(qobject_cast<QPlainTextDocumentLayout*>(document()->documentLayout())); QPointF offset(contentOffset()); QRect er = e->rect(); QRect viewportRect = viewport()->rect(); bool editable = !isReadOnly(); QTextBlock block = firstVisibleBlock(); qreal maximumWidth = document()->documentLayout()->documentSize().width(); //margin qreal lineX = 0; if (conf->isDisplayRightColumnMargin()) { // Don't use QFontMetricsF::averageCharWidth here, due to it returning // a fractional size even when this is not supported by the platform. lineX = QFontMetricsF(document()->defaultFont()).width(QLatin1Char('X')) * conf->getRightMarginColumn() + offset.x() + 4; if (lineX < viewportRect.width()) { const QBrush background = QBrush(QColor(239, 239, 239)); painter.fillRect(QRectF(lineX, er.top(), viewportRect.width() - lineX, er.height()), background); const QColor col = (palette().base().color().value() > 128) ? Qt::black : Qt::white; const QPen pen = painter.pen(); painter.setPen(blendColors(background.isOpaque() ? background.color() : palette().base().color(), col, 32)); painter.drawLine(QPointF(lineX, er.top()), QPointF(lineX, er.bottom())); painter.setPen(pen); } } // Set a brush origin so that the WaveUnderline knows where the wave started painter.setBrushOrigin(offset); // keep right margin clean from full-width selection int maxX = offset.x() + qMax((qreal)viewportRect.width(), maximumWidth) - document()->documentMargin(); er.setRight(qMin(er.right(), maxX)); painter.setClipRect(er); QAbstractTextDocumentLayout::PaintContext context = getPaintContext(); while (block.isValid()) { QRectF r = blockBoundingRect(block).translated(offset); QTextLayout *layout = block.layout(); if (!block.isVisible()) { offset.ry() += r.height(); block = block.next(); continue; } if (r.bottom() >= er.top() && r.top() <= er.bottom()) { QTextBlockFormat blockFormat = block.blockFormat(); QBrush bg = blockFormat.background(); if (bg != Qt::NoBrush) { QRectF contentsRect = r; contentsRect.setWidth(qMax(r.width(), maximumWidth)); fillBackground(&painter, contentsRect, bg); } QVector<QTextLayout::FormatRange> selections; int blpos = block.position(); int bllen = block.length(); for (int i = 0; i < context.selections.size(); ++i) { const QAbstractTextDocumentLayout::Selection &range = context.selections.at(i); const int selStart = range.cursor.selectionStart() - blpos; const int selEnd = range.cursor.selectionEnd() - blpos; if (selStart < bllen && selEnd > 0 && selEnd > selStart) { QTextLayout::FormatRange o; o.start = selStart; o.length = selEnd - selStart; o.format = range.format; selections.append(o); } else if (!range.cursor.hasSelection() && range.format.hasProperty(QTextFormat::FullWidthSelection) && block.contains(range.cursor.position())) { // for full width selections we don't require an actual selection, just // a position to specify the line. that's more convenience in usage. QTextLayout::FormatRange o; QTextLine l = layout->lineForTextPosition(range.cursor.position() - blpos); o.start = l.textStart(); o.length = l.textLength(); if (o.start + o.length == bllen - 1) ++o.length; // include newline o.format = range.format; selections.append(o); } } bool drawCursor = ((editable || (textInteractionFlags() & Qt::TextSelectableByKeyboard)) && context.cursorPosition >= blpos && context.cursorPosition < blpos + bllen); bool drawCursorAsBlock = drawCursor && overwriteMode() ; if (drawCursorAsBlock) { if (context.cursorPosition == blpos + bllen - 1) { drawCursorAsBlock = false; } else { QTextLayout::FormatRange o; o.start = context.cursorPosition - blpos; o.length = 1; o.format.setForeground(palette().base()); o.format.setBackground(palette().text()); selections.append(o); } } layout->draw(&painter, offset, selections, er); if ((drawCursor && !drawCursorAsBlock) || (editable && context.cursorPosition < -1 && !layout->preeditAreaText().isEmpty())) { int cpos = context.cursorPosition; if (cpos < -1) cpos = layout->preeditAreaPosition() - (cpos + 2); else cpos -= blpos; layout->drawCursor(&painter, offset, cpos, cursorWidth()); } } offset.ry() += r.height(); if (offset.y() > viewportRect.height()) break; block = block.next(); } if (backgroundVisible() && !block.isValid() && offset.y() <= er.bottom() && (centerOnScroll() || verticalScrollBar()->maximum() == verticalScrollBar()->minimum())) { painter.fillRect(QRect(QPoint((int)er.left(), (int)offset.y()), er.bottomRight()), palette().background()); } }