void ImagePreviewItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { Q_D(GraphicsDImgItem); QRect drawRect = option->exposedRect.intersected(boundingRect()).toAlignedRect(); QRect pixSourceRect; QPixmap pix; QSize completeSize = boundingRect().size().toSize(); /* For high resolution ("retina") displays, Mac OS X / Qt report only half of the physical resolution in terms of pixels, i.e. every logical pixels corresponds to 2x2 physical pixels. However, UI elements and fonts are nevertheless rendered at full resolution, and pixmaps as well, provided their resolution is high enough (that is, higher than the reported, logical resolution). To work around this, we render the photos not a logical resolution, but with the photo's full resolution, but at the screen's aspect ratio. When we later draw this high resolution bitmap, it is up to Qt to scale the photo to the true physical resolution. The ratio computed below is the ratio between the photo and screen resolutions, or equivalently the factor by which we need to increase the pixel size of the rendered pixmap. */ #ifdef USE_QT_SCALING double xratio = double(d->image.width()) / completeSize.width(); double yratio = double(d->image.height()) / completeSize.height(); double ratio = qMax(qMin(xratio, yratio), 1.0); #else double ratio = 1.0; #endif QRect scaledDrawRect = QRectF(ratio*drawRect.x(), ratio*drawRect.y(), ratio*drawRect.width(), ratio*drawRect.height()).toRect(); // scale "as if" scaling to whole image, but clip output to our exposed region QSize scaledCompleteSize = QSizeF(ratio*completeSize.width(), ratio*completeSize.height()).toSize(); DImg scaledImage = d->image.smoothScaleClipped(scaledCompleteSize.width(), scaledCompleteSize.height(), scaledDrawRect.x(), scaledDrawRect.y(), scaledDrawRect.width(), scaledDrawRect.height()); if (d->cachedPixmaps.find(scaledDrawRect, &pix, &pixSourceRect)) { if (pixSourceRect.isNull()) { painter->drawPixmap(drawRect, pix); } else { painter->drawPixmap(drawRect, pix, pixSourceRect); } } else { // TODO: factoring ICC settings code using ImageIface/EditorCore methods. // Apply CM settings. bool doSoftProofing = EditorCore::defaultInstance()->softProofingEnabled(); ICCSettingsContainer iccSettings = EditorCore::defaultInstance()->getICCSettings(); if (iccSettings.enableCM && (iccSettings.useManagedView || doSoftProofing)) { IccManager manager(scaledImage); IccTransform monitorICCtrans; if (doSoftProofing) { monitorICCtrans = manager.displaySoftProofingTransform(iccSettings.defaultProofProfile, widget); } else { monitorICCtrans = manager.displayTransform(widget); } pix = scaledImage.convertToPixmap(monitorICCtrans); } else { pix = scaledImage.convertToPixmap(); } d->cachedPixmaps.insert(scaledDrawRect, pix); painter->drawPixmap(drawRect, pix); } // Show the Over/Under exposure pixels indicators ExposureSettingsContainer* const expoSettings = EditorCore::defaultInstance()->getExposureSettings(); if (expoSettings) { if (expoSettings->underExposureIndicator || expoSettings->overExposureIndicator) { QImage pureColorMask = scaledImage.pureColorMask(expoSettings); QPixmap pixMask = QPixmap::fromImage(pureColorMask); painter->drawPixmap(drawRect, pixMask); } } }
/** Take data from image, draw text at x|y with specified parameters. If destPainter is null, draw to image, if destPainter is not null, draw directly using the painter. Returns modified area of image. */ QRect InsertTextWidget::composeImage(DImg* const image, QPainter* const destPainter, int x, int y, QFont font, float pointSize, int textRotation, QColor textColor, int textOpacity, int alignMode, const QString& textString, bool transparentBackground, QColor backgroundColor, BorderMode borderMode, int borderWidth, int spacing, float fontScale) { /* The problem we have to solve is that we have no pixel access to font rendering, we have to let Qt do the drawing. On the other hand we need to support 16 bit, which cannot be done with QPixmap. The current solution cuts out the text area, lets Qt do its drawing, converts back and blits to original. */ int maxWidth, maxHeight; if (x == -1 && y == -1) { maxWidth = image->width(); maxHeight = image->height(); } else { maxWidth = image->width() - x; maxHeight = image->height() - y; } fontScale = qMax(0.01f, fontScale); // find out size of the area that we are drawing to font.setPointSizeF(pointSize); QFontMetrics fontMt(font); QRect fontRect = fontMt.boundingRect(0, 0, qRound(maxWidth / fontScale), qRound(maxHeight / fontScale), alignMode, textString); fontRect.setWidth(qRound(fontRect.width() * fontScale)); fontRect.setHeight(qRound(fontRect.height() * fontScale)); if (!fontRect.isValid()) { return QRect(); } int fontWidth, fontHeight; switch (textRotation) { case ROTATION_NONE: case ROTATION_180: default: fontWidth = fontRect.width(); fontHeight = fontRect.height(); break; case ROTATION_90: case ROTATION_270: fontWidth = fontRect.height(); fontHeight = fontRect.width(); break; } // x, y == -1 means that we have to find a good initial position for the text here if (x == -1 && y == -1) { int boxWidth = fontWidth + 2 * borderWidth + 2 * spacing; int boxHeight = fontHeight + 2 * borderWidth + 2 * spacing; // was a valid position hint stored from last use? if (d->positionHint.isValid()) { // We assume that people tend to orient text along the edges, // so we do some guessing so that positions such as "in the lower right corner" // will be remembered across different image sizes. // get relative positions float fromTop = (float)d->positionHint.top() / 10000.0; float fromBottom = 1.0 - (float)d->positionHint.bottom() / 10000.0; float fromLeft = (float)d->positionHint.left() / 10000.0; float fromRight = 1.0 - (float)d->positionHint.right() / 10000.0; // calculate horizontal position if (fromLeft < fromRight) { x = qRound(fromLeft * maxWidth); // we are placing from the smaller distance, // so if now the larger distance is actually too small, // fall back to standard placement, nothing to lose. if (x + boxWidth > maxWidth) { x = qMax( (maxWidth - boxWidth) / 2, 0); } } else { x = maxWidth - qRound(fromRight * maxWidth) - boxWidth; if ( x < 0 ) { x = qMax( (maxWidth - boxWidth) / 2, 0); } } // calculate vertical position if (fromTop < fromBottom) { y = qRound(fromTop * maxHeight); if (y + boxHeight > maxHeight) { y = qMax( (maxHeight - boxHeight) / 2, 0); } } else { y = maxHeight - qRound(fromBottom * maxHeight) - boxHeight; if ( y < 0 ) { y = qMax( (maxHeight - boxHeight) / 2, 0); } } if (! QRect(x, y, boxWidth, boxHeight). intersects(QRect(0, 0, maxWidth, maxHeight)) ) { // emergency fallback - nothing is visible x = qMax( (maxWidth - boxWidth) / 2, 0); y = qMax( (maxHeight - boxHeight) / 2, 0); } // invalidate position hint, use only once d->positionHint = QRect(); } else { // use standard position x = qMax( (maxWidth - boxWidth) / 2, 0); y = qMax( (maxHeight - boxHeight) / 2, 0); } } // create a rectangle relative to image QRect drawRect( x, y, fontWidth + 2 * borderWidth + 2 * spacing, fontHeight + 2 * borderWidth + 2 * spacing); // create a rectangle relative to textArea, excluding the border QRect textAreaBackgroundRect( borderWidth, borderWidth, fontWidth + 2 * spacing, fontHeight + 2 * spacing); // create a rectangle relative to textArea, excluding the border and spacing QRect textAreaTextRect( borderWidth + spacing, borderWidth + spacing, fontWidth, fontHeight ); // create a rectangle relative to textArea, including the border, // for drawing the rectangle, taking into account that the width of the QPen goes in and out in equal parts QRect textAreaDrawRect( borderWidth / 2, borderWidth / 2, fontWidth + borderWidth + 2 * spacing, fontHeight + borderWidth + 2 * spacing ); // cut out the text area DImg textArea = image->copy(drawRect); if (textArea.isNull()) { return QRect(); } // compose semi-transparent background over textArea DColorComposer* composer = DColorComposer::getComposer(DColorComposer::PorterDuffNone); if (transparentBackground) { DImg transparentLayer(textAreaBackgroundRect.width(), textAreaBackgroundRect.height(), textArea.sixteenBit(), true); DColor transparent(backgroundColor); transparent.setAlpha(d->transparency); if (image->sixteenBit()) { transparent.convertToSixteenBit(); } transparentLayer.fill(transparent); textArea.bitBlendImage(composer, &transparentLayer, 0, 0, transparentLayer.width(), transparentLayer.height(), textAreaBackgroundRect.x(), textAreaBackgroundRect.y()); } DImg textNotDrawn; if (textArea.sixteenBit()) { textNotDrawn = textArea.copy(); textNotDrawn.convertToEightBit(); } else { textNotDrawn = textArea; } // We have no direct pixel access to font rendering, so now we need to use Qt/X11 for the drawing // convert text area to pixmap QPixmap pixmap; if (destPainter) { // We working on tool preview, deal with CM as well pixmap = d->iface->convertToPixmap(textNotDrawn); } else { // We working on target image. Do no apply double CM adjustment here. pixmap = textNotDrawn.convertToPixmap(); } int fontScaleWidth = qRound(fontWidth / fontScale); int fontScaleHeight = qRound(fontHeight / fontScale); QPixmap textPixmap(fontScaleWidth, fontScaleHeight); textPixmap.fill(Qt::transparent); QPainter tp(&textPixmap); tp.setOpacity((qreal)textOpacity / 100.0); tp.setPen(QPen(textColor, 1)); tp.setFont(font); switch (textRotation) { case ROTATION_NONE: tp.drawText(0, 0, fontScaleWidth, fontScaleHeight, alignMode, textString); break; case ROTATION_90: tp.translate(fontScaleWidth, 0); tp.rotate(90.0); tp.drawText(0, 0, fontScaleHeight, fontScaleWidth, alignMode, textString); break; case ROTATION_180: tp.translate(fontScaleWidth, fontScaleHeight); tp.rotate(180.0); tp.drawText(0, 0, fontScaleWidth, fontScaleHeight, alignMode, textString); break; case ROTATION_270: tp.translate(0, fontScaleHeight); tp.rotate(270.0); tp.drawText(0, 0, fontScaleHeight, fontScaleWidth, alignMode, textString); break; } tp.end(); // paint on pixmap QPainter p(&pixmap); p.drawPixmap(textAreaTextRect, textPixmap.scaled(fontWidth, fontHeight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); // Drawing rectangle around text. if (borderMode == BORDER_NORMAL) // Decorative border using text color. { p.setPen( QPen(textColor, borderWidth, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin) ) ; p.drawRect(textAreaDrawRect); } else if (borderMode == BORDER_SUPPORT) // Make simple dot line border to help user. { p.setPen(QPen(Qt::white, 1, Qt::SolidLine)); p.drawRect(textAreaDrawRect); p.setPen(QPen(Qt::red, 1, Qt::DotLine)); p.drawRect(textAreaDrawRect); } p.end(); if (!destPainter) { // convert to QImage, then to DImg QImage pixmapImage = pixmap.toImage(); DImg textDrawn(pixmapImage.width(), pixmapImage.height(), false, true, pixmapImage.bits()); // This does not work: during the conversion, colors are altered significantly (diffs of 1 to 10 in each component), // so we cannot find out which pixels have actually been touched. /* // Compare the result of drawing with the previous version. // Set all unchanged pixels to transparent DColor color, ncolor; uchar *ptr, *nptr; ptr = textDrawn.bits(); nptr = textNotDrawn.bits(); int bytesDepth = textDrawn.bytesDepth(); int numPixels = textDrawn.width() * textDrawn.height(); for (int i = 0; i < numPixels; ++i, ptr+= bytesDepth, nptr += bytesDepth) { color.setColor(ptr, false); ncolor.setColor(nptr, false); if ( color.red() == ncolor.red() && color.green() == ncolor.green() && color.blue() == ncolor.blue()) { color.setAlpha(0); color.setPixel(ptr); } } // convert to 16 bit if needed */ textDrawn.convertToDepthOfImage(&textArea); // now compose to original: only pixels affected by drawing text and border are changed, not whole area textArea.bitBlendImage(composer, &textDrawn, 0, 0, textDrawn.width(), textDrawn.height(), 0, 0); // copy result to original image image->bitBltImage(&textArea, drawRect.x(), drawRect.y()); } else { destPainter->drawPixmap(drawRect.x(), drawRect.y(), pixmap, 0, 0, pixmap.width(), pixmap.height()); } delete composer; return drawRect; }
void ImageRegionItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { Q_D(GraphicsDImgItem); d_ptr->drawRect = option->exposedRect.intersected(boundingRect()).toAlignedRect(); QRect pixSourceRect; QPixmap pix; QSize completeSize = boundingRect().size().toSize(); // scale "as if" scaling to whole image, but clip output to our exposed region DImg scaledImage = d->image.smoothScaleClipped(completeSize.width(), completeSize.height(), d_ptr->drawRect.x(), d_ptr->drawRect.y(), d_ptr->drawRect.width(), d_ptr->drawRect.height()); if (d->cachedPixmaps.find(d_ptr->drawRect, &pix, &pixSourceRect)) { if (pixSourceRect.isNull()) { painter->drawPixmap(d_ptr->drawRect.topLeft(), pix); } else { painter->drawPixmap(d_ptr->drawRect.topLeft(), pix, pixSourceRect); } } else { // TODO: factoring ICC settings code using ImageIface/EditorCore methods. // Apply CM settings. bool doSoftProofing = EditorCore::defaultInstance()->softProofingEnabled(); ICCSettingsContainer iccSettings = EditorCore::defaultInstance()->getICCSettings(); if (iccSettings.enableCM && (iccSettings.useManagedView || doSoftProofing)) { IccManager manager(scaledImage); IccTransform monitorICCtrans; if (doSoftProofing) { monitorICCtrans = manager.displaySoftProofingTransform(iccSettings.defaultProofProfile, widget); } else { monitorICCtrans = manager.displayTransform(widget); } pix = scaledImage.convertToPixmap(monitorICCtrans); } else { pix = scaledImage.convertToPixmap(); } d->cachedPixmaps.insert(d_ptr->drawRect, pix); painter->drawPixmap(d_ptr->drawRect.topLeft(), pix); } paintExtraData(painter); // Show the Over/Under exposure pixels indicators ExposureSettingsContainer* const expoSettings = EditorCore::defaultInstance()->getExposureSettings(); if (expoSettings) { if (expoSettings->underExposureIndicator || expoSettings->overExposureIndicator) { QImage pureColorMask = scaledImage.pureColorMask(expoSettings); QPixmap pixMask = QPixmap::fromImage(pureColorMask); painter->drawPixmap(d_ptr->drawRect.topLeft(), pixMask); } } }
QLayout* ColorCorrectionDlg::createPreviews() const { QGridLayout* const grid = new QGridLayout; QLabel* const originalTitle = new QLabel; if (d->mode == ProfileMismatch) { originalTitle->setText(i18n("Original Colors:")); } else if (d->mode == MissingProfile) { originalTitle->setText(i18n("Uncorrected Colors:")); } else if (d->mode == UncalibratedColor) { originalTitle->setText(i18n("Raw Colors:")); } originalTitle->setWordWrap(true); QLabel* const previewOriginal = new QLabel; DImg copyOriginal = d->preview.copy(); IccManager manager(copyOriginal); manager.transformForDisplay(); previewOriginal->setPixmap(copyOriginal.convertToPixmap()); QLabel* const targetTitle = new QLabel; if (d->mode == ProfileMismatch) { targetTitle->setText(i18n("Resulting Colors:")); } else if (d->mode == MissingProfile) { targetTitle->setText(i18n("Correction Applied:")); } else if (d->mode == UncalibratedColor) { targetTitle->setText(i18n("Corrected Colors:")); } targetTitle->setWordWrap(true); d->previewTarget = new QLabel; if (d->preview.width() > d->preview.height()) { grid->addWidget(originalTitle, 0, 0, Qt::AlignTop); grid->addWidget(previewOriginal, 1, 0); grid->addWidget(targetTitle, 2, 0, Qt::AlignTop); grid->addWidget(d->previewTarget, 3, 0); } else { grid->addWidget(originalTitle, 0, 0, Qt::AlignTop); grid->addWidget(previewOriginal, 1, 0); grid->addWidget(targetTitle, 0, 1, Qt::AlignTop); grid->addWidget(d->previewTarget, 1, 1); } return grid; }