File indexing completed on 2024-04-14 14:36:02

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