File indexing completed on 2025-01-19 12:59:17
0001 /************************************************** -*- mode:c++; -*- *** 0002 * * 0003 * This file is part of libkscan, a KDE scanning library. * 0004 * * 0005 * Copyright (C) 2013 Jonathan Marten <jjm@keelhaul.me.uk> * 0006 * Copyright (C) 1999 Klaas Freitag <freitag@suse.de> * 0007 * * 0008 * This library is free software; you can redistribute it and/or * 0009 * modify it under the terms of the GNU Library General Public * 0010 * License as published by the Free Software Foundation and appearing * 0011 * in the file COPYING included in the packaging of this file; * 0012 * either version 2 of the License, or (at your option) any later * 0013 * version. * 0014 * * 0015 * This program is distributed in the hope that it will be useful, * 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0018 * GNU General Public License for more details. * 0019 * * 0020 * You should have received a copy of the GNU General Public License * 0021 * along with this program; see the file COPYING. If not, write to * 0022 * the Free Software Foundation, Inc., 51 Franklin Street, * 0023 * Fifth Floor, Boston, MA 02110-1301, USA. * 0024 * * 0025 ************************************************************************/ 0026 0027 #ifndef IMAGECANVAS_H 0028 #define IMAGECANVAS_H 0029 0030 #include "kookascan_export.h" 0031 0032 #include <qgraphicsview.h> 0033 #include <qvector.h> 0034 0035 #include "scanimage.h" 0036 0037 class QGraphicsScene; 0038 class QGraphicsPixmapItem; 0039 0040 class QMenu; 0041 0042 class SelectionItem; 0043 0044 /** 0045 * @short Image display canvas widget. 0046 * 0047 * Displays a scalable image in a scrolling area, along with an interactive 0048 * selection area and any number of optional highlight areas. 0049 * 0050 * The selection area is used for selecting a part of the image for scanning 0051 * or cropping, while the highlight areas are used for indicating words 0052 * while performing OCR spell checking. 0053 * 0054 * @author Klaas Freitag 0055 * @author Jonathan Marten 0056 **/ 0057 0058 class KOOKASCAN_EXPORT ImageCanvas : public QGraphicsView 0059 { 0060 Q_OBJECT 0061 0062 public: 0063 /** 0064 * Constructor. 0065 * 0066 * @param parent the parent widget 0067 **/ 0068 explicit ImageCanvas(QWidget *parent = nullptr); 0069 0070 /** 0071 * Destructor. 0072 **/ 0073 ~ImageCanvas() override; 0074 0075 /** 0076 * Scaling types for the image as displayed on the canvas. 0077 **/ 0078 enum ScaleType { 0079 ScaleUnspecified, /**< No scale specified */ 0080 ScaleDynamic, /**< Best fit */ 0081 ScaleOriginal, /**< Original size */ 0082 ScaleFitWidth, /**< Fit to width */ 0083 ScaleFitHeight, /**< Fit to height */ 0084 ScaleZoom /**< Fit to arbitrary zoom */ 0085 }; 0086 0087 /** 0088 * Request for the canvas to perform an action. This will usually be 0089 * as a result of a user action in the application. 0090 * 0091 * @see ImgScaleDialog 0092 **/ 0093 enum UserAction { 0094 UserActionZoom, /**< Display an @c ImgScaleDialog to set a zoom factor */ 0095 UserActionFitWidth, /**< Scale the image to fit the width */ 0096 UserActionFitHeight, /**< Scale the image to fit the height */ 0097 UserActionOrigSize, /**< Reset the scale to the original image size */ 0098 UserActionClose, /**< Emit the @c closingRequested() signal */ 0099 }; 0100 0101 /** 0102 * The style used to draw a highlight box. 0103 **/ 0104 enum HighlightStyle { 0105 HighlightBox, /**< A rectangular box */ 0106 HighlightUnderline /**< A line along the bottom box edge */ 0107 }; 0108 0109 /** 0110 * Get a context menu for the image canvas. 0111 * 0112 * The menu is created on demand, and the first time that this function 0113 * is called an empty menu will be returned. The calling application 0114 * must populate the menu with actions and handle them when they are 0115 * triggered; if an action on the canvas is required, it should request 0116 * them via @c slotUserAction(). Do not delete the returned menu, 0117 * it is owned by the @c ImageCanvas object. 0118 * 0119 * @return The context menu 0120 * @see slotUserAction 0121 **/ 0122 QMenu *contextMenu(); 0123 0124 /** 0125 * Set the brightness of the displayed image. 0126 * 0127 * @param b The new brightness value 0128 * @note Not currently implemented, the value specified will be ignored. 0129 **/ 0130 void setBrightness(int b) 0131 { 0132 mBrightness = b; 0133 } 0134 0135 /** 0136 * Set the contrast of the displayed image. 0137 * 0138 * @param c The new contrast value 0139 * @note Not currently implemented, the value specified will be ignored. 0140 **/ 0141 void setContrast(int c) 0142 { 0143 mContrast = c; 0144 } 0145 0146 /** 0147 * Set the gamma of the displayed image. 0148 * 0149 * @param g The new gamma value 0150 * @note Not currently implemented, the value specified will be ignored. 0151 **/ 0152 void setGamma(int g) 0153 { 0154 mGamma = g; 0155 } 0156 0157 /** 0158 * Get the current brightness setting. 0159 * 0160 * @return The brightness value 0161 * @note Not currently implemented, an undefined value will be returned. 0162 **/ 0163 int getBrightness() const 0164 { 0165 return (mBrightness); 0166 } 0167 0168 /** 0169 * Get the current contrast setting. 0170 * 0171 * @return The contrast value 0172 * @note Not currently implemented, an undefined value will be returned. 0173 **/ 0174 int getContrast() const 0175 { 0176 return (mContrast); 0177 } 0178 0179 /** 0180 * Get the current gamma setting. 0181 * 0182 * @return The gamma value 0183 * @note Not currently implemented, an undefined value will be returned. 0184 **/ 0185 int getGamma() const 0186 { 0187 return (mGamma); 0188 } 0189 0190 /** 0191 * Set the scale factor to be used for display. The image is immediately 0192 * resized in accordance with the scale factor. 0193 * 0194 * @param f The scale factor as a percentage. 100 means original size, 0195 * while 0 means dynamic (best fit) scaling. 0196 **/ 0197 void setScaleFactor(int f); 0198 0199 /** 0200 * Get the scale factor currently in use. 0201 * 0202 * @return The current scale factor as a percentage. 0203 * Original size scaling returns a value of 100, dynamic 0204 * scaling returns a value of 0. 0205 **/ 0206 int getScaleFactor() const 0207 { 0208 return (mScaleFactor); 0209 } 0210 0211 /** 0212 * Set the scale type to be used for display. The image is immediately 0213 * resized in accordance with the scale type. 0214 * 0215 * @param type The new scale type to be set 0216 * @note Do not use this function to set a @p type of @c ScaleZoom. 0217 * Use @c setScaleFactor with the required scale factor instead. 0218 * @see ScaleType 0219 **/ 0220 void setScaleType(ImageCanvas::ScaleType type); 0221 0222 /** 0223 * Get the current scaling type in use. 0224 * 0225 * @return The scale type 0226 * @see ScaleType 0227 **/ 0228 ImageCanvas::ScaleType scaleType() const; 0229 0230 /** 0231 * Get a textual description for the current scaling type. 0232 * 0233 * @return An I18N'ed description 0234 **/ 0235 const QString scaleTypeString() const; 0236 0237 /** 0238 * Set the default scaling type that will be applied to a new image, 0239 * either on construction or after setting a new image without the 0240 * zoom hold in effect. 0241 * 0242 * @param type The new default scale type 0243 * @note If this function is not used. the default scaling type is @c Original. 0244 * @note It is not useful to use this function to set a @p type of @c ScaleZoom. 0245 **/ 0246 void setDefaultScaleType(ImageCanvas::ScaleType type) 0247 { 0248 mDefaultScaleType = type; 0249 } 0250 0251 /** 0252 * Get the default scaling type that will be applied to a new image. 0253 * 0254 * @return The default scale type 0255 **/ 0256 ImageCanvas::ScaleType defaultScaleType() const 0257 { 0258 return (mDefaultScaleType); 0259 } 0260 0261 /** 0262 * Access the image displayed. 0263 * 0264 * @return The image, or @c nullptr if no image is currently set. 0265 **/ 0266 ScanImage::Ptr rootImage() const { return (mImage); } 0267 0268 /** 0269 * Check whether an image is currently set and displayed 0270 * 0271 * @return @c true if an image is currently set 0272 **/ 0273 bool hasImage() const; 0274 0275 /** 0276 * Check whether the image canvas is read-only. 0277 * 0278 * @return @c true if the image is read-only, @c false if it allows interaction. 0279 * @see setReadOnly 0280 * @see imageReadOnly 0281 **/ 0282 bool isReadOnly() const 0283 { 0284 return (mReadOnly); 0285 } 0286 0287 /** 0288 * Get the bounds of the currently selected area, in absolute image pixels. 0289 * 0290 * @return The selected rectangle, in source image pixels. 0291 * If there is no selection, a null (invalid) rectangle is returned. 0292 * @see setSelectionRect 0293 * @see QRect::isValid() 0294 **/ 0295 QRect selectedRect() const; 0296 0297 /** 0298 * Get the bounds of the currently selected area, as a scaled proportion of 0299 * the image size. 0300 * 0301 * @return The selected rectangle, scaled to the image size: 0302 * for example, 0.5 means 50% of the image width or height. 0303 * If there is no selection, a null (invalid) rectangle is returned. 0304 * @see setSelectionRect 0305 * @see QRectF::isValid() 0306 **/ 0307 QRectF selectedRectF() const; 0308 0309 /** 0310 * Check whether there is a currently selected area. 0311 * 0312 * @return @c true if there is a selection. 0313 * @see selectedRect 0314 * @see selectedRectF 0315 **/ 0316 bool hasSelectedRect() const; 0317 0318 /** 0319 * Set a new selected area, in absolute image pixels. 0320 * 0321 * @param rect The new selected rectangle, in source image pixels. 0322 * @see selectedRect 0323 **/ 0324 void setSelectionRect(const QRect &rect); 0325 0326 /** 0327 * Set a new selected area, as a scaled proportion of 0328 * the image size. 0329 * 0330 * @param rect The new selected rectangle, in source image pixels: 0331 * for example, 0.5 means 50% of the image width or height. 0332 * @see selectedRectF 0333 **/ 0334 void setSelectionRect(const QRectF &rect); 0335 0336 /** 0337 * Get a copy of the selected image area. 0338 * 0339 * @return A copy of the selected image area, or a null image if there 0340 * is no image displayed or if there is no selected area. 0341 **/ 0342 ScanImage::Ptr selectedImage() const; 0343 0344 /** 0345 * Get a textual description of the image size and depth. 0346 * 0347 * @return The I18N'ed description, in the format "WxH pixels, D bpp", 0348 * or "-" if no image is set. 0349 **/ 0350 const QString imageInfoString() const; 0351 0352 /** 0353 * As above, but return a description for the specified 0354 * width, height and depth. 0355 * 0356 * @param w Width 0357 * @param h Height 0358 * @param d Bit depth 0359 * @return The description 0360 **/ 0361 static const QString imageInfoString(int w, int h, int d); 0362 0363 /** 0364 * As above, but return a description for the specified image. 0365 * 0366 * @param img The image 0367 * @return Its description 0368 **/ 0369 static const QString imageInfoString(const QImage *img); 0370 0371 /** 0372 * Display a new image. 0373 * 0374 * The old image is forgotten, and if no more references to it remain 0375 * then it is deleted. The selection is cleared, but the @c newRect() 0376 * signal is are not emitted. All current highlights are removed. 0377 * Unless the @p holdZoom option is set, or @c setKeepZoom(true) has been 0378 * called, the scaling type is reset to the default. 0379 * 0380 * @param newImage The new image to display. If this is null, 0381 * no new image is set. 0382 * @param holdZoom If set to @c true, do not change the current 0383 * scaling type or scaling factor; if set to @c false, reset the 0384 * scaling type to that set by @c setDefaultScaleType(). 0385 * @see setDefaultScaleType 0386 * @see setKeepZoom 0387 **/ 0388 void newImage(ScanImage::Ptr newImage, bool holdZoom = false); 0389 0390 /** 0391 * Highlight a rectangular area on the current image, 0392 * using the previously specified style, brush and pen. 0393 * 0394 * @param rect The rectangle to be highlighted. Unlike the selection 0395 * rectangle, this is specified in absolute image pixels. 0396 * @param ensureVis If set to @c true, the new highlight rectangle 0397 * will be made visible by scrolling to it if necessary. 0398 * @return The new highlight ID. 0399 * @see removeHighlight 0400 * @see setHighlightStyle 0401 * @see scrollTo 0402 */ 0403 int addHighlight(const QRect &rect, bool ensureVis = false); 0404 0405 /** 0406 * Remove a highlight from the image. 0407 * 0408 * @param id The ID of the highlight to be removed. 0409 * @see addHighlight 0410 */ 0411 void removeHighlight(int id); 0412 0413 /** 0414 * Remove all highlights from the image. 0415 * 0416 * @see addHighlight 0417 */ 0418 void removeAllHighlights(); 0419 0420 /** 0421 * Set the style, pen and brush to be used for subsequently 0422 * added highlight rectangles. 0423 * 0424 * @param style The new style 0425 * @param pen The new line pen 0426 * @param brush The new fill brush 0427 * @note The default @p style is HighlightBox, but there are no defaults 0428 * for @p pen or @p brush. 0429 * @note The @p pen is always forced to be cosmetic. 0430 * @see QPen::setCosmetic 0431 **/ 0432 void setHighlightStyle(ImageCanvas::HighlightStyle style, const QPen &pen = QPen(), const QBrush &brush = QBrush()); 0433 0434 /** 0435 * Scroll if necessary so that the specified rectangle is visible. 0436 * The rectangle is specified in absolute image pixels. 0437 * 0438 * @param rect The rectangle area of interest 0439 * @see QGraphicsView::ensureVisible 0440 **/ 0441 void scrollTo(const QRect &rect); 0442 0443 public slots: 0444 /** 0445 * Set whether the current scaling settings are retained when a 0446 * new image is set. 0447 * 0448 * @param k The new setting. If @c true, the scaling settings are always 0449 * retained when setting a new image, regardless of the @c holdZoom 0450 * parameter to @c newImage(). The default is @c false. 0451 * @see newImage 0452 **/ 0453 void setKeepZoom(bool k) 0454 { 0455 mKeepZoom = k; 0456 } 0457 0458 /** 0459 * Set whether the aspect ratio of the image is maintained for 0460 * dynamic scaling mode. 0461 * 0462 * @param ma The new setting. If @c true, the aspect ratio of the 0463 * image will be maintained; if @c false, the width and height will 0464 * be scaled independently for the best fit. The default is @c true. 0465 **/ 0466 void setMaintainAspect(bool ma) 0467 { 0468 mMaintainAspect = ma; 0469 } 0470 0471 /** 0472 * Set whether the image is considered to be read-only or whether 0473 * it allows user interaction. Setting the read-only status using 0474 * this function causes the imageReadOnly() signal to be emitted. 0475 * 0476 * @param ro If set to @c true, the image is set to be read-only. 0477 * The default is @c false allowing user interaction. 0478 * @note If the image is read-only mouse actions are ignored and the 0479 * selection cannot be set interactively, although it can still be set 0480 * by calling the appropriate functions. 0481 * @see readOnly 0482 * @see imageReadOnly 0483 **/ 0484 void setReadOnly(bool ro); 0485 0486 /** 0487 * Perform a user-requested action on the image. 0488 * 0489 * @param act The action to be performed 0490 * @note For all scaling setting actions (i.e. all apart 0491 * from @c UserActionClose), the image is repainted at 0492 * the new setting. The @c UserActionClose action emits 0493 * the @c closingRequested() signal. 0494 * @see UserAction 0495 * @see closingRequested 0496 **/ 0497 void performUserAction(ImageCanvas::UserAction act); 0498 0499 signals: 0500 /** 0501 * Emitted when a new selection rectangle is created by the user 0502 * dragging an area. The rectangle is provided as absolute image pixels. 0503 * 0504 * @param rect The new rectangle, the same area and in the same units 0505 * as would be returned by @c selectedRect(), or a null @c QRect if 0506 * there is no selection. 0507 * @see selectedRect 0508 **/ 0509 void newRect(const QRect &rect); 0510 0511 /** 0512 * Emitted when a new selection rectangle is created by the user 0513 * dragging an area. The rectangle is provided as a proportion of 0514 * the source image size. 0515 * 0516 * @param rect The new rectangle, the same area and in the same units 0517 * as would be returned by @c selectedRectF(), or a null @c QRectF if 0518 * there is no selection. 0519 * @see selectedRectF 0520 **/ 0521 void newRect(const QRectF &rect); 0522 0523 /** 0524 * Emitted when the user requests to close the image, 0525 * by calling @c slotUserAction() with @c UserActionClose. 0526 **/ 0527 void closingRequested(); 0528 0529 /** 0530 * Emitted when the scaling type is set by @c setScaleType(). 0531 * Can be monitored by the calling application in order to set 0532 * a status bar indicator or similar. 0533 * 0534 * @param scaleType The new scale type as a string, as would 0535 * be returned by scaleTypeString(). 0536 * @see scaleTypeString 0537 * @see setScaleFactor 0538 **/ 0539 void scalingChanged(const QString &scaleType); 0540 0541 /** 0542 * Emitted when the read-only state of the current image changes. 0543 * 0544 * @param isRO Set to @c true if the image is now read-only, 0545 * or @c false if it is not. 0546 */ 0547 void imageReadOnly(bool isRO); 0548 0549 /** 0550 * Emitted for a mouse double-click over the image. 0551 * 0552 * @param p The clicked point, in absolute image pixel coordinates. 0553 * @note This signal is emitted even if the image is read-only. 0554 */ 0555 void doubleClicked(const QPoint &p); 0556 0557 protected: 0558 /** 0559 * Update and redraw the "marching ants" area rectangle. 0560 * 0561 * @param ev The timer event 0562 **/ 0563 void timerEvent(QTimerEvent *ev) override; 0564 0565 /** 0566 * Resize and redraw the currently displayed image to fit the new size. 0567 * 0568 * @param ev The resize event 0569 **/ 0570 void resizeEvent(QResizeEvent *ev) override; 0571 0572 /** 0573 * If a context menu has been created by calling @c contextMenu(), 0574 * then pop it up at the current mouse position. If there is no 0575 * context menu, then do nothing. 0576 * 0577 * @param ev The menu event 0578 **/ 0579 void contextMenuEvent(QContextMenuEvent *ev) override; 0580 0581 /** 0582 * Possibly start to drag a new selection area, or move or resize 0583 * the current area. 0584 * 0585 * @param ev The mouse event 0586 **/ 0587 void mousePressEvent(QMouseEvent *ev) override; 0588 0589 /** 0590 * Finish dragging a selection area, and emit the @c newRect signals. 0591 * 0592 * @param ev The mouse event 0593 * @see newRect 0594 **/ 0595 void mouseReleaseEvent(QMouseEvent *ev) override; 0596 0597 /** 0598 * Continue to drag and redraw the selection area. 0599 * 0600 * @param ev The mouse event 0601 **/ 0602 void mouseMoveEvent(QMouseEvent *ev) override; 0603 0604 /** 0605 * Detect a double click on the image and emit a signal with 0606 * the image coordinates. 0607 * 0608 * @param ev The mouse event 0609 * @see doubleClicked 0610 **/ 0611 void mouseDoubleClickEvent(QMouseEvent *ev) override; 0612 0613 private: 0614 enum MoveState { 0615 MoveNone, 0616 MoveTopLeft, 0617 MoveTopRight, 0618 MoveBottomLeft, 0619 MoveBottomRight, 0620 MoveLeft, 0621 MoveRight, 0622 MoveTop, 0623 MoveBottom, 0624 MoveWhole, 0625 MoveNew 0626 }; 0627 0628 void startMarqueeTimer(); 0629 void stopMarqueeTimer(); 0630 0631 ImageCanvas::MoveState classifyPoint(const QPoint &p) const; 0632 void setCursorShape(Qt::CursorShape cs); 0633 void recalculateViewScale(); 0634 0635 QMenu *mContextMenu; 0636 int mTimerId; 0637 0638 ScanImage::Ptr mImage; 0639 0640 int mScaleFactor; 0641 int mBrightness; 0642 int mContrast; 0643 int mGamma; 0644 0645 bool mMaintainAspect; 0646 0647 ImageCanvas::MoveState mMoving; 0648 Qt::CursorShape mCurrentCursor; 0649 0650 bool mKeepZoom; // keep zoom setting if image changes 0651 bool mReadOnly; 0652 0653 ImageCanvas::ScaleType mScaleType; 0654 ImageCanvas::ScaleType mDefaultScaleType; 0655 0656 QGraphicsScene *mScene; // internal graphics scene 0657 QGraphicsPixmapItem *mPixmapItem; // item for background pixmap 0658 SelectionItem *mSelectionItem; // item for selection box 0659 0660 QPoint mStartPoint; // start point of drag 0661 QPoint mLastPoint; // previous point of drag 0662 0663 QVector<QGraphicsItem *> mHighlights; // list of highlight rectangles 0664 0665 ImageCanvas::HighlightStyle mHighlightStyle; // for newly created highlights 0666 QPen mHighlightPen; 0667 QBrush mHighlightBrush; 0668 }; 0669 0670 #endif // IMAGECANVAS_H