diff options
Diffstat (limited to 'src/imageplugins/coreplugin/ratiocrop/imageselectionwidget.cpp')
-rw-r--r-- | src/imageplugins/coreplugin/ratiocrop/imageselectionwidget.cpp | 1422 |
1 files changed, 1422 insertions, 0 deletions
diff --git a/src/imageplugins/coreplugin/ratiocrop/imageselectionwidget.cpp b/src/imageplugins/coreplugin/ratiocrop/imageselectionwidget.cpp new file mode 100644 index 00000000..17eaf415 --- /dev/null +++ b/src/imageplugins/coreplugin/ratiocrop/imageselectionwidget.cpp @@ -0,0 +1,1422 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-09 + * Description : image selection widget used by ratio crop tool. + * + * Copyright (C) 2007 by Jaromir Malenko <malenko at email.cz> + * Copyright (C) 2008 by Roberto Castagnola <roberto dot castagnola at gmail dot com> + * Copyright (C) 2004-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#define OPACITY 0.7 +#define RCOL 0xAA +#define GCOL 0xAA +#define BCOL 0xAA + +#define MINRANGE 0 + +// Golden number (1+sqrt(5))/2 +#define PHI 1.61803398874989479 +// 1/PHI +#define INVPHI 0.61803398874989479 + +// C++ includes. + +#include <iostream> +#include <cstdio> +#include <cmath> +#include <cstdlib> + +// TQt includes. + +#include <tqregion.h> +#include <tqcolor.h> +#include <tqpainter.h> +#include <tqbrush.h> +#include <tqpixmap.h> +#include <tqimage.h> +#include <tqpen.h> +#include <tqpoint.h> +#include <tqtimer.h> +#include <tqsizepolicy.h> + +// KDE includes. + +#include <kstandarddirs.h> +#include <kcursor.h> +#include <tdeglobal.h> + +// Local includes. + +#include "ddebug.h" +#include "imageiface.h" +#include "dimg.h" +#include "imageselectionwidget.h" +#include "imageselectionwidget.moc" + +namespace DigikamImagesPluginCore +{ + +class ImageSelectionWidgetPriv +{ +public: + + enum ResizingMode + { + ResizingNone = 0, + ResizingTopLeft, + ResizingTopRight, + ResizingBottomLeft, + ResizingBottomRight + }; + + ImageSelectionWidgetPriv() + { + currentResizing = ResizingNone; + iface = 0; + pixmap = 0; + guideSize = 1; + } + + // Golden guide types. + bool drawGoldenSection; + bool drawGoldenSpiralSection; + bool drawGoldenSpiral; + bool drawGoldenTriangle; + + // Golden guide translations. + bool flipHorGoldenGuide; + bool flipVerGoldenGuide; + + bool moving; + bool autoOrientation; + bool preciseCrop; + + int guideLinesType; + int guideSize; + + int currentAspectRatioType; + int currentResizing; + int currentOrientation; + + float currentWidthRatioValue; + float currentHeightRatioValue; + + TQPoint lastPos; + + TQRect rect; + TQRect image; // Real image dimension. + TQRect regionSelection; // Real size image selection. + TQRect localRegionSelection; // Local size selection. + + // Draggable local region selection corners. + TQRect localTopLeftCorner; + TQRect localBottomLeftCorner; + TQRect localTopRightCorner; + TQRect localBottomRightCorner; + + TQPixmap *pixmap; + + TQColor guideColor; + + Digikam::DImg preview; + + Digikam::ImageIface *iface; +}; + +ImageSelectionWidget::ImageSelectionWidget(int w, int h, TQWidget *parent, + int widthRatioValue, int heightRatioValue, + int aspectRatioType, int orient, int guideLinesType) + : TQWidget(parent, 0, TQt::WDestructiveClose) +{ + d = new ImageSelectionWidgetPriv; + d->currentAspectRatioType = aspectRatioType; + d->currentWidthRatioValue = widthRatioValue; + d->currentHeightRatioValue = heightRatioValue; + d->currentOrientation = orient; + d->guideLinesType = guideLinesType; + d->autoOrientation = false; + d->preciseCrop = false; + d->moving = true; + reverseRatioValues(); + + setBackgroundMode(TQt::NoBackground); + setMinimumSize(w, h); + setMouseTracking(true); + + d->iface = new Digikam::ImageIface(w, h); + uchar *data = d->iface->getPreviewImage(); + int width = d->iface->previewWidth(); + int height = d->iface->previewHeight(); + bool sixteenBit = d->iface->previewSixteenBit(); + bool hasAlpha = d->iface->previewHasAlpha(); + d->preview = Digikam::DImg(width, height, sixteenBit, hasAlpha, data); + delete [] data; + d->preview.convertToEightBit(); + d->pixmap = new TQPixmap(w, h); + + d->image = TQRect(0, 0, d->iface->originalWidth(), d->iface->originalHeight()); + d->rect = TQRect(w/2-d->preview.width()/2, h/2-d->preview.height()/2, + d->preview.width(), d->preview.height()); + updatePixmap(); + setGoldenGuideTypes(true, false, false, false, false, false); +} + +ImageSelectionWidget::~ImageSelectionWidget() +{ + delete d->iface; + delete d->pixmap; + delete d; +} + +Digikam::ImageIface* ImageSelectionWidget::imageIface() +{ + return d->iface; +} + +void ImageSelectionWidget::resizeEvent(TQResizeEvent *e) +{ + delete d->pixmap; + + int w = e->size().width(); + int h = e->size().height(); + + uchar *data = d->iface->setPreviewImageSize(w, h); + int width = d->iface->previewWidth(); + int height = d->iface->previewHeight(); + bool sixteenBit = d->iface->previewSixteenBit(); + bool hasAlpha = d->iface->previewHasAlpha(); + d->preview = Digikam::DImg(width, height, sixteenBit, hasAlpha, data); + delete [] data; + d->preview.convertToEightBit(); + + d->pixmap = new TQPixmap(w, h); + + d->rect = TQRect(w/2-d->preview.width()/2, h/2-d->preview.height()/2, + d->preview.width(), d->preview.height()); + updatePixmap(); +} + +int ImageSelectionWidget::getOriginalImageWidth() +{ + return d->image.width(); +} + +int ImageSelectionWidget::getOriginalImageHeight() +{ + return d->image.height(); +} + +TQRect ImageSelectionWidget::getRegionSelection() +{ + return d->regionSelection; +} + +int ImageSelectionWidget::getMinWidthRange() +{ + return MINRANGE; +} + +int ImageSelectionWidget::getMinHeightRange() +{ + return MINRANGE; +} + +int ImageSelectionWidget::getMaxWidthRange() +{ + int maxW = d->image.width() - d->regionSelection.left(); + + if (d->currentAspectRatioType != RATIONONE) + { + // Compute max width taking aspect ratio into account + int t = d->currentWidthRatioValue > d->currentHeightRatioValue ? 1 : 0; + int h = d->image.height() - d->regionSelection.top(); + int w = rint( ( h + t ) * d->currentWidthRatioValue / + d->currentHeightRatioValue ) - t; + if ( w < maxW ) + maxW = w; + } + + // Return max width adjusted if a precise crop is wanted + return computePreciseSize(maxW, d->currentWidthRatioValue); +} + +int ImageSelectionWidget::getMaxHeightRange() +{ + int maxH = d->image.height() - d->regionSelection.top(); + + if (d->currentAspectRatioType != RATIONONE) + { + // Compute max height taking aspect ratio into account + int t = d->currentHeightRatioValue > d->currentWidthRatioValue ? 1 : 0; + int w = d->image.width() - d->regionSelection.left(); + int h = rint( ( w + t ) * d->currentHeightRatioValue / + d->currentWidthRatioValue ) - t; + if ( h < maxH ) + maxH = h; + } + + // Return max height adjusted if a precise crop is wanted + return computePreciseSize(maxH, d->currentHeightRatioValue); +} + +int ImageSelectionWidget::getWidthStep() +{ + if ( d->preciseCrop && preciseCropAvailable() ) + return d->currentWidthRatioValue; + else + return 1; +} + +int ImageSelectionWidget::getHeightStep() +{ + if ( d->preciseCrop && preciseCropAvailable() ) + return d->currentHeightRatioValue; + else + return 1; +} + +// Draw a new centered selection with half width (if orientation = Landscape) +// or with half height (if orientation = Portrait) +void ImageSelectionWidget::resetSelection() +{ + d->regionSelection.setWidth(d->image.width()/2); + d->regionSelection.setHeight(d->image.height()/2); + applyAspectRatio(d->currentOrientation == Portrait, false); + + setCenterSelection(CenterImage); +} + +void ImageSelectionWidget::setCenterSelection(int centerType) +{ + // Adjust selection size if bigger than real image + if ( d->regionSelection.height() > d->image.height() ) + { + d->regionSelection.setHeight(d->image.height()); + applyAspectRatio(true, false); + } + if ( d->regionSelection.width() > d->image.width() ) + { + d->regionSelection.setWidth(d->image.width()); + applyAspectRatio(false, false); + } + + // Set center point for selection + TQPoint center = d->image.center(); + switch (centerType) + { + case CenterWidth: + center.setY(d->regionSelection.center().y()); + break; + + case CenterHeight: + center.setX(d->regionSelection.center().x()); + break; + } + d->regionSelection.moveCenter(center); + + // Repaint + updatePixmap(); + repaint(false); + regionSelectionChanged(); +} + +// Draw a new centered selection with max size +void ImageSelectionWidget::maxAspectSelection() +{ + d->regionSelection.setWidth(d->image.width()); + d->regionSelection.setHeight(d->image.height()); + if ( d->currentAspectRatioType != RATIONONE ) + applyAspectRatio(d->currentOrientation == Portrait, false); + + setCenterSelection(CenterImage); +} + +void ImageSelectionWidget::setGoldenGuideTypes(bool drawGoldenSection, bool drawGoldenSpiralSection, + bool drawGoldenSpiral, bool drawGoldenTriangle, + bool flipHorGoldenGuide, bool flipVerGoldenGuide) +{ + d->drawGoldenSection = drawGoldenSection; + d->drawGoldenSpiralSection = drawGoldenSpiralSection; + d->drawGoldenSpiral = drawGoldenSpiral; + d->drawGoldenTriangle = drawGoldenTriangle; + d->flipHorGoldenGuide = flipHorGoldenGuide; + d->flipVerGoldenGuide = flipVerGoldenGuide; +} + +void ImageSelectionWidget::slotGuideLines(int guideLinesType) +{ + d->guideLinesType = guideLinesType; + updatePixmap(); + repaint(false); +} + +void ImageSelectionWidget::slotChangeGuideColor(const TQColor &color) +{ + d->guideColor = color; + updatePixmap(); + repaint(false); +} + +void ImageSelectionWidget::slotChangeGuideSize(int size) +{ + d->guideSize = size; + updatePixmap(); + repaint(false); +} + +void ImageSelectionWidget::setSelectionOrientation(int orient) +{ + d->currentOrientation = orient; + reverseRatioValues(); + applyAspectRatio(true); + emit signalSelectionOrientationChanged( d->currentOrientation ); +} + +void ImageSelectionWidget::setSelectionAspectRatioType(int aspectRatioType) +{ + d->currentAspectRatioType = aspectRatioType; + + // Set ratio values + switch(aspectRatioType) + { + case RATIO01X01: + d->currentWidthRatioValue = 1.0; + d->currentHeightRatioValue = 1.0; + break; + + case RATIO03X04: + d->currentWidthRatioValue = 4.0; + d->currentHeightRatioValue = 3.0; + break; + + case RATIO02x03: + d->currentWidthRatioValue = 3.0; + d->currentHeightRatioValue = 2.0; + break; + + case RATIO05x07: + d->currentWidthRatioValue = 7.0; + d->currentHeightRatioValue = 5.0; + break; + + case RATIO07x10: + d->currentWidthRatioValue = 10.0; + d->currentHeightRatioValue = 7.0; + break; + + case RATIO04X05: + d->currentWidthRatioValue = 5.0; + d->currentHeightRatioValue = 4.0; + break; + + case RATIOGOLDEN: + d->currentWidthRatioValue = PHI; + d->currentHeightRatioValue = 1.0; + break; + } + + reverseRatioValues(); + applyAspectRatio(false); +} + +void ImageSelectionWidget::setSelectionAspectRatioValue(int widthRatioValue, + int heightRatioValue) +{ + int gdc = widthRatioValue; + + // Compute greatest common divisor using Euclidean algorithm + for (int tmp, mod = heightRatioValue; mod != 0; mod = tmp % mod) + { + tmp = gdc; + gdc = mod; + } + + d->currentWidthRatioValue = widthRatioValue / gdc; + d->currentHeightRatioValue = heightRatioValue / gdc; + d->currentAspectRatioType = RATIOCUSTOM; + + // Fix orientation + if ( d->autoOrientation ) + { + if ( heightRatioValue > widthRatioValue && + d->currentOrientation == Landscape ) + { + d->currentOrientation = Portrait; + emit signalSelectionOrientationChanged( d->currentOrientation ); + } + else if ( widthRatioValue > heightRatioValue && + d->currentOrientation == Portrait ) + { + d->currentOrientation = Landscape; + emit signalSelectionOrientationChanged( d->currentOrientation ); + } + } + else + reverseRatioValues(); + + applyAspectRatio(false); +} + +void ImageSelectionWidget::reverseRatioValues() +{ + // Reverse ratio values if needed + if ( ( d->currentWidthRatioValue > d->currentHeightRatioValue && + d->currentOrientation == Portrait ) || + ( d->currentHeightRatioValue > d->currentWidthRatioValue && + d->currentOrientation == Landscape ) ) + { + float tmp = d->currentWidthRatioValue; + d->currentWidthRatioValue = d->currentHeightRatioValue; + d->currentHeightRatioValue = tmp; + } +} + +bool ImageSelectionWidget::preciseCropAvailable() +{ + // Define when precise crop feature can be used + // No needed when aspect ratio is 1:1 + switch(d->currentAspectRatioType) + { + case RATIONONE: + case RATIO01X01: + case RATIOGOLDEN: + return false; + + case RATIOCUSTOM: + return ( d->currentWidthRatioValue != d->currentHeightRatioValue ); + + default: + return true; + } +} + +void ImageSelectionWidget::setPreciseCrop(bool precise) +{ + d->preciseCrop = precise; + applyAspectRatio(false, true); + regionSelectionChanged(); +} + +void ImageSelectionWidget::setAutoOrientation(bool orientation) +{ + d->autoOrientation = orientation; +} + +void ImageSelectionWidget::setSelectionX(int x) +{ + d->regionSelection.moveLeft(x); + regionSelectionMoved(); +} + +void ImageSelectionWidget::setSelectionY(int y) +{ + d->regionSelection.moveTop(y); + regionSelectionMoved(); +} + +void ImageSelectionWidget::setSelectionWidth(int w) +{ + d->regionSelection.setWidth(w); + applyAspectRatio(false, true); + + regionSelectionChanged(); +} + +void ImageSelectionWidget::setSelectionHeight(int h) +{ + d->regionSelection.setHeight(h); + applyAspectRatio(true, true); + + regionSelectionChanged(); +} + +TQPoint ImageSelectionWidget::convertPoint(const TQPoint pm, bool localToReal) +{ + return convertPoint(pm.x(), pm.y(), localToReal); +} + +TQPoint ImageSelectionWidget::convertPoint(int x, int y, bool localToReal) +{ + int pmX, pmY; + + if (localToReal) + { + pmX = ( x - d->rect.left() ) * (float)d->image.width() / + (float)d->preview.width(); + + pmY = ( y - d->rect.top() ) * (float)d->image.height() / + (float)d->preview.height(); + } + else + { + pmX = d->rect.left() + ( x * (float)d->preview.width() / + (float)d->image.width() ); + + pmY = d->rect.top() + ( y * (float)d->preview.height() / + (float)d->image.height() ); + } + + return TQPoint(pmX, pmY); +} + +int ImageSelectionWidget::computePreciseSize(int size, int step) +{ + // Adjust size if precise crop is wanted + if ( d->preciseCrop && preciseCropAvailable() ) + size = int(size / step) * step; + + return size; +} + +void ImageSelectionWidget::applyAspectRatio(bool useHeight, bool repaintWidget) +{ + // Save selection area for re-adjustment after changing width and height. + TQRect oldRegionSelection = d->regionSelection; + + if ( !useHeight ) // Width changed. + { + int w = computePreciseSize(d->regionSelection.width(), + d->currentWidthRatioValue); + + d->regionSelection.setWidth(w); + switch(d->currentAspectRatioType) + { + case RATIONONE: + break; + + default: + d->regionSelection.setHeight(rint( w * d->currentHeightRatioValue / + d->currentWidthRatioValue ) ); + break; + } + } + else // Height changed. + { + int h = computePreciseSize(d->regionSelection.height(), + d->currentHeightRatioValue); + + d->regionSelection.setHeight(h); + switch(d->currentAspectRatioType) + { + case RATIONONE: + break; + + default: + d->regionSelection.setWidth(rint( h * d->currentWidthRatioValue / + d->currentHeightRatioValue ) ); + break; + } + } + + // If we change selection size by a corner, re-adjust the oposite corner position. + switch(d->currentResizing) + { + case ImageSelectionWidgetPriv::ResizingTopLeft: + d->regionSelection.moveBottomRight( oldRegionSelection.bottomRight() ); + break; + + case ImageSelectionWidgetPriv::ResizingTopRight: + d->regionSelection.moveBottomLeft( oldRegionSelection.bottomLeft() ); + break; + + case ImageSelectionWidgetPriv::ResizingBottomLeft: + d->regionSelection.moveTopRight( oldRegionSelection.topRight() ); + break; + + case ImageSelectionWidgetPriv::ResizingBottomRight: + d->regionSelection.moveTopLeft( oldRegionSelection.topLeft() ); + break; + } + + if (repaintWidget) + { + updatePixmap(); + repaint(false); + } +} + +void ImageSelectionWidget::normalizeRegion() +{ + // Perform normalization of selection area. + + if (d->regionSelection.left() < d->image.left()) + d->regionSelection.moveLeft(d->image.left()); + + if (d->regionSelection.top() < d->image.top()) + d->regionSelection.moveTop(d->image.top()); + + if (d->regionSelection.right() > d->image.right()) + d->regionSelection.moveRight(d->image.right()); + + if (d->regionSelection.bottom() > d->image.bottom()) + d->regionSelection.moveBottom(d->image.bottom()); +} + +void ImageSelectionWidget::regionSelectionMoved() +{ + normalizeRegion(); + + updatePixmap(); + repaint(false); + + emit signalSelectionMoved( d->regionSelection ); +} + +void ImageSelectionWidget::regionSelectionChanged() +{ + // Compute the intersection of selection region and image region + TQRect cut = d->regionSelection & d->image; + + // Adjust selection size if it was cropped + if ( d->regionSelection.width() > cut.width() ) + { + d->regionSelection = cut; + applyAspectRatio(false); + } + if ( d->regionSelection.height() > cut.height() ) + { + d->regionSelection = cut; + applyAspectRatio(true); + } + + emit signalSelectionChanged( d->regionSelection ); +} + +void ImageSelectionWidget::updatePixmap() +{ + // Updated local selection region. + d->localRegionSelection.setTopLeft( + convertPoint(d->regionSelection.topLeft(), false)); + d->localRegionSelection.setBottomRight( + convertPoint(d->regionSelection.bottomRight(), false)); + + // Updated dragging corners region. + d->localTopLeftCorner.setRect(d->localRegionSelection.left(), + d->localRegionSelection.top(), 8, 8); + d->localBottomLeftCorner.setRect(d->localRegionSelection.left(), + d->localRegionSelection.bottom() - 7, 8, 8); + d->localTopRightCorner.setRect(d->localRegionSelection.right() - 7, + d->localRegionSelection.top(), 8, 8); + d->localBottomRightCorner.setRect(d->localRegionSelection.right() - 7, + d->localRegionSelection.bottom() - 7, 8, 8); + + // Drawing background and image. + d->pixmap->fill(colorGroup().background()); + + if (d->preview.isNull()) + return; + + // Drawing region outside selection grayed. + + Digikam::DImg image = d->preview.copy(); + + uchar* ptr = image.bits(); + uchar r, g, b; + + for (int y=d->rect.top() ; y <= d->rect.bottom() ; y++) + { + for (int x=d->rect.left() ; x <= d->rect.right() ; x++) + { + if (! d->localRegionSelection.contains(x, y, true) ) + { + b = ptr[0]; + g = ptr[1]; + r = ptr[2]; + + r += (uchar)((RCOL - r) * OPACITY); + g += (uchar)((GCOL - g) * OPACITY); + b += (uchar)((BCOL - b) * OPACITY); + + ptr[0] = b; + ptr[1] = g; + ptr[2] = r; + } + + ptr+=4; + } + } + + TQPixmap pix = d->iface->convertToPixmap(image); + bitBlt(d->pixmap, d->rect.x(), d->rect.y(), &pix); + + // Stop here if no selection to draw + if ( d->regionSelection.isEmpty() ) + return; + + TQPainter p(d->pixmap); + + // Drawing selection borders. + + p.setPen(TQPen(TQColor(250, 250, 255), 1, TQt::SolidLine)); + p.drawRect(d->localRegionSelection); + + // Drawing selection corners. + + p.drawRect(d->localTopLeftCorner); + p.drawRect(d->localBottomLeftCorner); + p.drawRect(d->localTopRightCorner); + p.drawRect(d->localBottomRightCorner); + + // Drawing guide lines. + + // Constraint drawing only on local selection region. + // This is needed because arcs and incurved lines can draw + // outside a little of local selection region. + p.setClipping(true); + p.setClipRect(d->localRegionSelection); + + switch (d->guideLinesType) + { + case RulesOfThirds: + { + int xThird = d->localRegionSelection.width() / 3; + int yThird = d->localRegionSelection.height() / 3; + + p.setPen(TQPen(TQt::white, d->guideSize, TQt::SolidLine)); + p.drawLine( d->localRegionSelection.left() + xThird, d->localRegionSelection.top(), + d->localRegionSelection.left() + xThird, d->localRegionSelection.bottom() ); + p.drawLine( d->localRegionSelection.left() + 2*xThird, d->localRegionSelection.top(), + d->localRegionSelection.left() + 2*xThird, d->localRegionSelection.bottom() ); + + p.drawLine( d->localRegionSelection.left(), d->localRegionSelection.top() + yThird, + d->localRegionSelection.right(), d->localRegionSelection.top() + yThird ); + p.drawLine( d->localRegionSelection.left(), d->localRegionSelection.top() + 2*yThird, + d->localRegionSelection.right(), d->localRegionSelection.top() + 2*yThird ); + + p.setPen(TQPen(d->guideColor, d->guideSize, TQt::DotLine)); + p.drawLine( d->localRegionSelection.left() + xThird, d->localRegionSelection.top(), + d->localRegionSelection.left() + xThird, d->localRegionSelection.bottom() ); + p.drawLine( d->localRegionSelection.left() + 2*xThird, d->localRegionSelection.top(), + d->localRegionSelection.left() + 2*xThird, d->localRegionSelection.bottom() ); + + p.drawLine( d->localRegionSelection.left(), d->localRegionSelection.top() + yThird, + d->localRegionSelection.right(), d->localRegionSelection.top() + yThird ); + p.drawLine( d->localRegionSelection.left(), d->localRegionSelection.top() + 2*yThird, + d->localRegionSelection.right(), d->localRegionSelection.top() + 2*yThird ); + break; + } + + case DiagonalMethod: + { + // Move coordinates to top, left + p.translate(d->localRegionSelection.topLeft().x(), d->localRegionSelection.topLeft().y()); + + float w = (float)d->localRegionSelection.width(); + float h = (float)d->localRegionSelection.height(); + + p.setPen(TQPen(TQt::white, d->guideSize, TQt::SolidLine)); + if (w > h) + { + p.drawLine( 0, 0, h, h); + p.drawLine( 0, h, h, 0); + p.drawLine( w-h, 0, w, h); + p.drawLine( w-h, h, w, 0); + + } + else + { + p.drawLine( 0, 0, w, w); + p.drawLine( 0, w, w, 0); + p.drawLine( 0, h-w, w, h); + p.drawLine( 0, h, w, h-w); + } + + p.setPen(TQPen(d->guideColor, d->guideSize, TQt::DotLine)); + if (w > h) + { + p.drawLine( 0, 0, h, h); + p.drawLine( 0, h, h, 0); + p.drawLine( w-h, 0, w, h); + p.drawLine( w-h, h, w, 0); + + } + else + { + p.drawLine( 0, 0, w, w); + p.drawLine( 0, w, w, 0); + p.drawLine( 0, h-w, w, h); + p.drawLine( 0, h, w, h-w); + } + break; + } + + case HarmoniousTriangles: + { + // Move coordinates to local center selection. + p.translate(d->localRegionSelection.center().x(), d->localRegionSelection.center().y()); + + // Flip horizontal. + if (d->flipHorGoldenGuide) + p.scale(-1, 1); + + // Flip verical. + if (d->flipVerGoldenGuide) + p.scale(1, -1); + + float w = (float)d->localRegionSelection.width(); + float h = (float)d->localRegionSelection.height(); + int dst = (int)((h*cos(atan(w/h)) / (cos(atan(h/w))))); + + p.setPen(TQPen(TQt::white, d->guideSize, TQt::SolidLine)); + p.drawLine( -d->localRegionSelection.width()/2, -d->localRegionSelection.height()/2, + d->localRegionSelection.width()/2, d->localRegionSelection.height()/2); + + p.drawLine( -d->localRegionSelection.width()/2 + dst, -d->localRegionSelection.height()/2, + -d->localRegionSelection.width()/2, d->localRegionSelection.height()/2); + + p.drawLine( d->localRegionSelection.width()/2, -d->localRegionSelection.height()/2, + d->localRegionSelection.width()/2 - dst, d->localRegionSelection.height()/2); + + p.setPen(TQPen(d->guideColor, d->guideSize, TQt::DotLine)); + p.drawLine( -d->localRegionSelection.width()/2, -d->localRegionSelection.height()/2, + d->localRegionSelection.width()/2, d->localRegionSelection.height()/2); + + p.drawLine( -d->localRegionSelection.width()/2 + dst, -d->localRegionSelection.height()/2, + -d->localRegionSelection.width()/2, d->localRegionSelection.height()/2); + + p.drawLine( d->localRegionSelection.width()/2, -d->localRegionSelection.height()/2, + d->localRegionSelection.width()/2 - dst, d->localRegionSelection.height()/2); + break; + } + + case GoldenMean: + { + // Move coordinates to local center selection. + p.translate(d->localRegionSelection.center().x(), d->localRegionSelection.center().y()); + + // Flip horizontal. + if (d->flipHorGoldenGuide) + p.scale(-1, 1); + + // Flip vertical. + if (d->flipVerGoldenGuide) + p.scale(1, -1); + + int w = d->localRegionSelection.width(); + int h = d->localRegionSelection.height(); + + // lengths for the golden mean and half the sizes of the region: + int w_g = (int)(w*INVPHI); + int h_g = (int)(h*INVPHI); + int w_2 = w/2; + int h_2 = h/2; + + TQRect R1(-w_2, -h_2, w_g, h); + // w - 2*w_2 corrects for one-pixel difference + // so that R2.right() is really at the right end of the region + TQRect R2(w_g-w_2, h_2-h_g, w-w_g+1-(w - 2*w_2), h_g); + + TQRect R3((int)(w_2 - R2.width()*INVPHI), -h_2, + (int)(R2.width()*INVPHI), h - R2.height()); + TQRect R4(R2.x(), R1.y(), R3.x() - R2.x(), + (int)(R3.height()*INVPHI)); + TQRect R5(R4.x(), R4.bottom(), (int)(R4.width()*INVPHI), + R3.height() - R4.height()); + TQRect R6(R5.x() + R5.width(), R5.bottom() - (int)(R5.height()*INVPHI), + R3.x() - R5.right(), (int)(R5.height()*INVPHI)); + TQRect R7(R6.right() - (int)(R6.width()*INVPHI), R4.bottom(), + (int)(R6.width()*INVPHI), R5.height() - R6.height()); + + p.setPen(TQPen(TQt::white, d->guideSize, TQt::SolidLine)); + + // Drawing Golden sections. + if (d->drawGoldenSection) + { + // horizontal lines: + p.drawLine( R1.left(), R2.top(), + R2.right(), R2.top()); + + p.drawLine( R1.left(), R1.top() + R2.height(), + R2.right(), R1.top() + R2.height()); + + // vertical lines: + p.drawLine( R1.right(), R1.top(), + R1.right(), R1.bottom() ); + + p.drawLine( R1.left()+R2.width(), R1.top(), + R1.left()+R2.width(), R1.bottom() ); + } + + // Drawing Golden triangle guides. + if (d->drawGoldenTriangle) + { + p.drawLine( R1.left(), R1.bottom(), + R2.right(), R1.top() ); + + p.drawLine( R1.left(), R1.top(), + R2.right() - R1.width(), R1.bottom()); + + p.drawLine( R1.left() + R1.width(), R1.top(), + R2.right(), R1.bottom() ); + } + + // Drawing Golden spiral sections. + if (d->drawGoldenSpiralSection) + { + p.drawLine( R1.topRight(), R1.bottomRight() ); + p.drawLine( R2.topLeft(), R2.topRight() ); + p.drawLine( R3.topLeft(), R3.bottomLeft() ); + p.drawLine( R4.bottomLeft(), R4.bottomRight() ); + p.drawLine( R5.topRight(), R5.bottomRight() ); + p.drawLine( R6.topLeft(), R6.topRight() ); + p.drawLine( R7.topLeft(), R7.bottomLeft() ); + } + + // Drawing Golden Spiral. + if (d->drawGoldenSpiral) + { + p.drawArc ( R1.left(), + R1.top() - R1.height(), + 2*R1.width(), 2*R1.height(), + 180*16, 90*16); + + p.drawArc ( R2.right() - 2*R2.width(), + R1.bottom() - 2*R2.height(), + 2*R2.width(), 2*R2.height(), + 270*16, 90*16); + + p.drawArc ( R2.right() - 2*R3.width(), + R3.top(), + 2*R3.width(), 2*R3.height(), + 0, 90*16); + + p.drawArc ( R4.left(), + R4.top(), + 2*R4.width(), 2*R4.height(), + 90*16, 90*16); + + p.drawArc ( R5.left(), + R5.top()-R5.height(), + 2*R5.width(), 2*R5.height(), + 180*16, 90*16); + + p.drawArc ( R6.left()-R6.width(), + R6.top()-R6.height(), + 2*R6.width(), 2*R6.height(), + 270*16, 90*16); + + p.drawArc ( R7.left()-R7.width(), + R7.top(), + 2*R7.width(), 2*R7.height(), + 0, 90*16); + } + + p.setPen(TQPen(d->guideColor, d->guideSize, TQt::DotLine)); + + // Drawing Golden sections. + if (d->drawGoldenSection) + { + // horizontal lines: + p.drawLine( R1.left(), R2.top(), + R2.right(), R2.top()); + + p.drawLine( R1.left(), R1.top() + R2.height(), + R2.right(), R1.top() + R2.height()); + + // vertical lines: + p.drawLine( R1.right(), R1.top(), + R1.right(), R1.bottom() ); + + p.drawLine( R1.left()+R2.width(), R1.top(), + R1.left()+R2.width(), R1.bottom() ); + } + + // Drawing Golden triangle guides. + if (d->drawGoldenTriangle) + { + p.drawLine( R1.left(), R1.bottom(), + R2.right(), R1.top() ); + + p.drawLine( R1.left(), R1.top(), + R2.right() - R1.width(), R1.bottom()); + + p.drawLine( R1.left() + R1.width(), R1.top(), + R2.right(), R1.bottom() ); + } + + // Drawing Golden spiral sections. + if (d->drawGoldenSpiralSection) + { + p.drawLine( R1.topRight(), R1.bottomRight() ); + p.drawLine( R2.topLeft(), R2.topRight() ); + p.drawLine( R3.topLeft(), R3.bottomLeft() ); + p.drawLine( R4.bottomLeft(), R4.bottomRight() ); + p.drawLine( R5.topRight(), R5.bottomRight() ); + p.drawLine( R6.topLeft(), R6.topRight() ); + p.drawLine( R7.topLeft(), R7.bottomLeft() ); + } + + // Drawing Golden Spiral. + if (d->drawGoldenSpiral) + { + p.drawArc ( R1.left(), + R1.top() - R1.height(), + 2*R1.width(), 2*R1.height(), + 180*16, 90*16); + + p.drawArc ( R2.right() - 2*R2.width(), + R1.bottom() - 2*R2.height(), + 2*R2.width(), 2*R2.height(), + 270*16, 90*16); + + p.drawArc ( R2.right() - 2*R3.width(), + R3.top(), + 2*R3.width(), 2*R3.height(), + 0, 90*16); + + p.drawArc ( R4.left(), + R4.top(), + 2*R4.width(), 2*R4.height(), + 90*16, 90*16); + + p.drawArc ( R5.left(), + R5.top()-R5.height(), + 2*R5.width(), 2*R5.height(), + 180*16, 90*16); + + p.drawArc ( R6.left()-R6.width(), + R6.top()-R6.height(), + 2*R6.width(), 2*R6.height(), + 270*16, 90*16); + + p.drawArc ( R7.left()-R7.width(), + R7.top(), + 2*R7.width(), 2*R7.height(), + 0, 90*16); + } + + break; + } + } + + p.setClipping(false); + + p.end(); +} + +void ImageSelectionWidget::paintEvent( TQPaintEvent * ) +{ + bitBlt(this, 0, 0, d->pixmap); +} + +TQPoint ImageSelectionWidget::opposite() +{ + TQPoint opp; + + switch(d->currentResizing) + { + case ImageSelectionWidgetPriv::ResizingTopRight: + opp = d->regionSelection.bottomLeft(); + break; + + case ImageSelectionWidgetPriv::ResizingBottomLeft: + opp = d->regionSelection.topRight(); + break; + + case ImageSelectionWidgetPriv::ResizingBottomRight: + opp = d->regionSelection.topLeft(); + break; + + case ImageSelectionWidgetPriv::ResizingTopLeft: + default: + opp = d->regionSelection.bottomRight(); + break; + } + + return opp; +} + +float ImageSelectionWidget::distance(TQPoint a, TQPoint b) +{ + return sqrt(pow(a.x() - b.x(), 2) + pow(a.y() - b.y(), 2)); +} + +void ImageSelectionWidget::setCursorResizing() +{ + switch(d->currentResizing) + { + case ImageSelectionWidgetPriv::ResizingTopLeft: + setCursor( KCursor::sizeFDiagCursor() ); + break; + + case ImageSelectionWidgetPriv::ResizingTopRight: + setCursor( KCursor::sizeBDiagCursor() ); + break; + + case ImageSelectionWidgetPriv::ResizingBottomLeft: + setCursor( KCursor::sizeBDiagCursor() ); + break; + + case ImageSelectionWidgetPriv::ResizingBottomRight: + setCursor( KCursor::sizeFDiagCursor() ); + break; + } +} + +void ImageSelectionWidget::placeSelection(TQPoint pm, bool symmetric, TQPoint center) +{ + // Set orientation + if ( d->autoOrientation ) + { + TQPoint rel = pm - opposite(); + + if ( abs(rel.x()) > abs(rel.y()) ) + { + if ( d->currentOrientation == Portrait ) + { + d->currentOrientation = Landscape; + reverseRatioValues(); + emit signalSelectionOrientationChanged( d->currentOrientation ); + } + } + else + { + if ( d->currentOrientation == Landscape ) + { + d->currentOrientation = Portrait; + reverseRatioValues(); + emit signalSelectionOrientationChanged( d->currentOrientation ); + } + } + } + + // Place the corner at the mouse + // If a symmetric selection is wanted, place opposite corner to + // the center, double selection size and move it to old center after + // computing aspect ratio. + switch(d->currentResizing) + { + case ImageSelectionWidgetPriv::ResizingTopLeft: + // Place corners to the proper position + d->regionSelection.setTopLeft(pm); + if ( symmetric ) + d->regionSelection.setBottomRight(center); + break; + + case ImageSelectionWidgetPriv::ResizingTopRight: + d->regionSelection.setTopRight(pm); + if ( symmetric ) + d->regionSelection.setBottomLeft(center); + break; + + case ImageSelectionWidgetPriv::ResizingBottomLeft: + d->regionSelection.setBottomLeft(pm); + if ( symmetric ) + d->regionSelection.setTopRight(center); + break; + + case ImageSelectionWidgetPriv::ResizingBottomRight: + d->regionSelection.setBottomRight(pm); + if ( symmetric ) + d->regionSelection.setTopLeft(center); + break; + } + + if ( symmetric ) + d->regionSelection.setSize(d->regionSelection.size()*2); + applyAspectRatio(d->currentOrientation == Portrait, false); + if ( symmetric ) + d->regionSelection.moveCenter(center); + + // Repaint + updatePixmap(); + repaint(false); +} + +void ImageSelectionWidget::mousePressEvent ( TQMouseEvent * e ) +{ + if ( e->button() == TQt::LeftButton ) + { + TQPoint pm = TQPoint(e->x(), e->y()); + TQPoint pmVirtual = convertPoint(pm); + d->moving = false; + + if ( (e->state() & TQt::ShiftButton) == TQt::ShiftButton ) + { + bool symmetric = (e->state() & TQt::ControlButton ) == TQt::ControlButton; + TQPoint center = d->regionSelection.center(); + + // Find the closest corner + + TQPoint points[] = { d->regionSelection.topLeft(), + d->regionSelection.topRight(), + d->regionSelection.bottomLeft(), + d->regionSelection.bottomRight() }; + int resizings[] = { ImageSelectionWidgetPriv::ResizingTopLeft, + ImageSelectionWidgetPriv::ResizingTopRight, + ImageSelectionWidgetPriv::ResizingBottomLeft, + ImageSelectionWidgetPriv::ResizingBottomRight }; + float dist = -1; + for (int i = 0 ; i < 4 ; i++) + { + TQPoint point = points[i]; + float dist2 = distance(pmVirtual, point); + if (dist2 < dist || d->currentResizing == ImageSelectionWidgetPriv::ResizingNone) + { + dist = dist2; + d->currentResizing = resizings[i]; + } + } + + setCursorResizing(); + + placeSelection(pmVirtual, symmetric, center); + } + else + { + if ( d->localTopLeftCorner.contains( pm ) ) + d->currentResizing = ImageSelectionWidgetPriv::ResizingTopLeft; + else if ( d->localTopRightCorner.contains( pm ) ) + d->currentResizing = ImageSelectionWidgetPriv::ResizingTopRight; + else if ( d->localBottomLeftCorner.contains( pm ) ) + d->currentResizing = ImageSelectionWidgetPriv::ResizingBottomLeft; + else if ( d->localBottomRightCorner.contains( pm ) ) + d->currentResizing = ImageSelectionWidgetPriv::ResizingBottomRight; + else + { + d->lastPos = pmVirtual; + setCursor( KCursor::sizeAllCursor() ); + + if (d->regionSelection.contains( pmVirtual ) ) + { + d->moving = true; + } + else + { + d->regionSelection.moveCenter( pmVirtual ); + normalizeRegion(); + updatePixmap(); + repaint(false); + } + } + } + } +} + +void ImageSelectionWidget::mouseReleaseEvent ( TQMouseEvent * ) +{ + if ( d->currentResizing != ImageSelectionWidgetPriv::ResizingNone ) + { + setCursor( KCursor::arrowCursor() ); + regionSelectionChanged(); + d->currentResizing = ImageSelectionWidgetPriv::ResizingNone; + } + else if ( d->regionSelection.contains( d->lastPos ) ) + { + setCursor( KCursor::handCursor() ); + regionSelectionMoved(); + } + else + { + setCursor( KCursor::arrowCursor() ); + regionSelectionMoved(); + } +} + +void ImageSelectionWidget::mouseMoveEvent ( TQMouseEvent * e ) +{ + if ( ( e->state() & TQt::LeftButton ) == TQt::LeftButton ) + { + if ( d->moving ) + { + setCursor( KCursor::sizeAllCursor() ); + TQPoint newPos = convertPoint(e->x(), e->y()); + + d->regionSelection.moveBy( newPos.x() - d->lastPos.x(), + newPos.y() - d->lastPos.y() ); + + d->lastPos = newPos; + + normalizeRegion(); + + updatePixmap(); + repaint(false); + } + else + { + TQPoint pmVirtual = convertPoint(e->x(), e->y()); + + if ( d->currentResizing == ImageSelectionWidgetPriv::ResizingNone ) + { + d->regionSelection.setTopLeft( pmVirtual ); + d->regionSelection.setBottomRight( pmVirtual ); + d->currentResizing = ImageSelectionWidgetPriv::ResizingTopLeft; // set to anything + } + + TQPoint center = d->regionSelection.center(); + bool symmetric = (e->state() & TQt::ControlButton ) == TQt::ControlButton; + + // Change resizing mode + + TQPoint opp = symmetric ? center : opposite(); + TQPoint dir = pmVirtual - opp; + + if ( dir.x() > 0 && dir.y() > 0 && d->currentResizing != ImageSelectionWidgetPriv::ResizingBottomRight) + { + d->currentResizing = ImageSelectionWidgetPriv::ResizingBottomRight; + d->regionSelection.setTopLeft( opp ); + setCursor( KCursor::sizeFDiagCursor() ); + } + else if ( dir.x() > 0 && dir.y() < 0 && d->currentResizing != ImageSelectionWidgetPriv::ResizingTopRight) + { + d->currentResizing = ImageSelectionWidgetPriv::ResizingTopRight; + d->regionSelection.setBottomLeft( opp ); + setCursor( KCursor::sizeBDiagCursor() ); + } + else if ( dir.x() < 0 && dir.y() > 0 && d->currentResizing != ImageSelectionWidgetPriv::ResizingBottomLeft) + { + d->currentResizing = ImageSelectionWidgetPriv::ResizingBottomLeft; + d->regionSelection.setTopRight( opp ); + setCursor( KCursor::sizeBDiagCursor() ); + } + else if ( dir.x() < 0 && dir.y() < 0 && d->currentResizing != ImageSelectionWidgetPriv::ResizingTopLeft) + { + d->currentResizing = ImageSelectionWidgetPriv::ResizingTopLeft; + d->regionSelection.setBottomRight( opp ); + setCursor( KCursor::sizeFDiagCursor() ); + } + else + { + if ( dir.x() == 0 && dir.y() == 0 ) + setCursor( KCursor::sizeAllCursor() ); + else if ( dir.x() == 0 ) + setCursor( KCursor::sizeHorCursor() ); + else if ( dir.y() == 0 ) + setCursor( KCursor::sizeVerCursor() ); + } + + placeSelection(pmVirtual, symmetric, center); + } + } + else + { + if ( d->localTopLeftCorner.contains( e->x(), e->y() ) || + d->localBottomRightCorner.contains( e->x(), e->y() ) ) + setCursor( KCursor::sizeFDiagCursor() ); + else if ( d->localTopRightCorner.contains( e->x(), e->y() ) || + d->localBottomLeftCorner.contains( e->x(), e->y() ) ) + setCursor( KCursor::sizeBDiagCursor() ); + else if ( d->localRegionSelection.contains( e->x(), e->y() ) ) + setCursor( KCursor::handCursor() ); + else + setCursor( KCursor::arrowCursor() ); + } +} + +} // NameSpace DigikamImagesPluginCore |