├── .gitignore ├── ImageCropper.pro ├── ImageCropper.pro.user ├── README.md ├── assets └── README │ ├── 001.png │ ├── 002.png │ ├── 003.png │ ├── 004.png │ ├── 005.png │ ├── 006.png │ ├── cropper_shape.png │ └── import_functions.png ├── base ├── imagecropperdialog.h ├── imagecropperlabel.cpp └── imagecropperlabel.h ├── example ├── imagecropperdemo.cpp ├── imagecropperdemo.h ├── main.cpp ├── mainwindow.cpp └── mainwindow.h └── res ├── color-palette.ico ├── save.ico └── select-file.ico /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /ImageCropper.pro: -------------------------------------------------------------------------------- 1 | QT += core gui 2 | 3 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 4 | 5 | CONFIG += c++11 6 | 7 | # The following define makes your compiler emit warnings if you use 8 | # any Qt feature that has been marked deprecated (the exact warnings 9 | # depend on your compiler). Please consult the documentation of the 10 | # deprecated API in order to know how to port your code away from it. 11 | DEFINES += QT_DEPRECATED_WARNINGS 12 | 13 | # You can also make your code fail to compile if it uses deprecated APIs. 14 | # In order to do so, uncomment the following line. 15 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 16 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 17 | 18 | SOURCES += \ 19 | base/imagecropperlabel.cpp \ 20 | example/imagecropperdemo.cpp \ 21 | example/main.cpp \ 22 | example/mainwindow.cpp 23 | 24 | HEADERS += \ 25 | base/imagecropperdialog.h \ 26 | base/imagecropperlabel.h \ 27 | example/imagecropperdemo.h \ 28 | example/mainwindow.h 29 | 30 | # Default rules for deployment. 31 | qnx: target.path = /tmp/$${TARGET}/bin 32 | else: unix:!android: target.path = /opt/$${TARGET}/bin 33 | !isEmpty(target.path): INSTALLS += target 34 | -------------------------------------------------------------------------------- /ImageCropper.pro.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | EnvironmentId 7 | {5891aaf8-91ff-44bd-a6da-fe5c450a3732} 8 | 9 | 10 | ProjectExplorer.Project.ActiveTarget 11 | 0 12 | 13 | 14 | ProjectExplorer.Project.EditorSettings 15 | 16 | true 17 | false 18 | true 19 | 20 | Cpp 21 | 22 | CppGlobal 23 | 24 | 25 | 26 | QmlJS 27 | 28 | QmlJSGlobal 29 | 30 | 31 | 2 32 | UTF-8 33 | false 34 | 4 35 | false 36 | 80 37 | true 38 | true 39 | 1 40 | true 41 | false 42 | 0 43 | true 44 | true 45 | 0 46 | 8 47 | true 48 | 1 49 | true 50 | true 51 | true 52 | false 53 | 54 | 55 | 56 | ProjectExplorer.Project.PluginSettings 57 | 58 | 59 | true 60 | 61 | 62 | 63 | ProjectExplorer.Project.Target.0 64 | 65 | Desktop Qt 5.12.6 GCC 64bit 66 | Desktop Qt 5.12.6 GCC 64bit 67 | qt.qt5.5126.gcc_64_kit 68 | 0 69 | 0 70 | 0 71 | 72 | %{CurrentProject:Path}/build/Debug 73 | 74 | 75 | true 76 | qmake 77 | 78 | QtProjectManager.QMakeBuildStep 79 | true 80 | 81 | false 82 | false 83 | false 84 | 85 | 86 | true 87 | Make 88 | 89 | Qt4ProjectManager.MakeStep 90 | 91 | false 92 | 93 | 94 | false 95 | 96 | 2 97 | Build 98 | 99 | ProjectExplorer.BuildSteps.Build 100 | 101 | 102 | 103 | true 104 | Make 105 | 106 | Qt4ProjectManager.MakeStep 107 | 108 | true 109 | clean 110 | 111 | false 112 | 113 | 1 114 | Clean 115 | 116 | ProjectExplorer.BuildSteps.Clean 117 | 118 | 2 119 | false 120 | 121 | Debug 122 | Debug 123 | Qt4ProjectManager.Qt4BuildConfiguration 124 | 2 125 | true 126 | 127 | 128 | /home/icrystal/dev/cpp/ImageCropper/build/Release 129 | 130 | 131 | true 132 | qmake 133 | 134 | QtProjectManager.QMakeBuildStep 135 | false 136 | 137 | false 138 | false 139 | true 140 | 141 | 142 | true 143 | Make 144 | 145 | Qt4ProjectManager.MakeStep 146 | 147 | false 148 | 149 | 150 | false 151 | 152 | 2 153 | Build 154 | 155 | ProjectExplorer.BuildSteps.Build 156 | 157 | 158 | 159 | true 160 | Make 161 | 162 | Qt4ProjectManager.MakeStep 163 | 164 | true 165 | clean 166 | 167 | false 168 | 169 | 1 170 | Clean 171 | 172 | ProjectExplorer.BuildSteps.Clean 173 | 174 | 2 175 | false 176 | 177 | Release 178 | Release 179 | Qt4ProjectManager.Qt4BuildConfiguration 180 | 0 181 | true 182 | 183 | 184 | /home/icrystal/dev/cpp/ImageCropper/build/Profile 185 | 186 | 187 | true 188 | qmake 189 | 190 | QtProjectManager.QMakeBuildStep 191 | true 192 | 193 | false 194 | true 195 | true 196 | 197 | 198 | true 199 | Make 200 | 201 | Qt4ProjectManager.MakeStep 202 | 203 | false 204 | 205 | 206 | false 207 | 208 | 2 209 | Build 210 | 211 | ProjectExplorer.BuildSteps.Build 212 | 213 | 214 | 215 | true 216 | Make 217 | 218 | Qt4ProjectManager.MakeStep 219 | 220 | true 221 | clean 222 | 223 | false 224 | 225 | 1 226 | Clean 227 | 228 | ProjectExplorer.BuildSteps.Clean 229 | 230 | 2 231 | false 232 | 233 | Profile 234 | Profile 235 | Qt4ProjectManager.Qt4BuildConfiguration 236 | 0 237 | true 238 | 239 | 3 240 | 241 | 242 | 0 243 | Deploy 244 | 245 | ProjectExplorer.BuildSteps.Deploy 246 | 247 | 1 248 | Deploy Configuration 249 | 250 | ProjectExplorer.DefaultDeployConfiguration 251 | 252 | 1 253 | 254 | 255 | dwarf 256 | 257 | cpu-cycles 258 | 259 | 260 | 250 261 | -F 262 | true 263 | 4096 264 | false 265 | false 266 | 1000 267 | 268 | true 269 | 270 | false 271 | false 272 | false 273 | false 274 | true 275 | 0.01 276 | 10 277 | true 278 | kcachegrind 279 | 1 280 | 25 281 | 282 | 1 283 | true 284 | false 285 | true 286 | valgrind 287 | 288 | 0 289 | 1 290 | 2 291 | 3 292 | 4 293 | 5 294 | 6 295 | 7 296 | 8 297 | 9 298 | 10 299 | 11 300 | 12 301 | 13 302 | 14 303 | 304 | 2 305 | 306 | ImageCropper 307 | 308 | Qt4ProjectManager.Qt4RunConfiguration:/home/icrystal/dev/cpp/ImageCropper/ImageCropper.pro 309 | 310 | 3768 311 | false 312 | true 313 | true 314 | false 315 | false 316 | true 317 | %{CurrentProject:Path} 318 | /home/icrystal/dev/cpp/ImageCropper/build/Debug 319 | 320 | 1 321 | 322 | 323 | 324 | ProjectExplorer.Project.TargetCount 325 | 1 326 | 327 | 328 | ProjectExplorer.Project.Updater.FileVersion 329 | 22 330 | 331 | 332 | Version 333 | 22 334 | 335 | 336 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## \[C++\]\[Qt\] custom class : ImageCropperLabel 2 | 3 | ## 一、简介 4 | 5 | 一个图像裁剪控件,可以用于但不限于:用户头像的裁剪。 6 | 7 | 支持输出矩形(包括正方形)、椭圆形(包括圆形)图像。 8 | 9 | ```txt 10 | ├── base 11 | │ ├── imagecropperdialog.h 12 | │ ├── imagecropperlabel.cpp <======== source file 13 | │ └── imagecropperlabel.h <======== header file 14 | └── example 15 | ├── imagecropperdemo.cpp 16 | ├── imagecropperdemo.h 17 | ├── main.cpp 18 | ├── mainwindow.cpp 19 | └── mainwindow.h 20 | ``` 21 | 22 | 类的定义在`base/imagecropperlabel.h`文件中,(`base/imagecropperlabel.cpp`为类的实现文件),实际使用只需要用到这两个文件。 23 | 24 | 另外,`base/imagecropperdialog.h`文件实现了对该类的封装,只需如下一行代码即可弹出一个窗口,用户截取图像后,返回截取的图像。(见倒数第三张附图) 25 | 26 | ```C++ 27 | QPixmap image = ImageCropperDialog::getCroppedImage("test.png", 600, 400, CropperShape::CIRCLE); 28 | ``` 29 | 30 | 类似于Qt自带的`QColorDialog::getColor(...)` 31 | 32 | ## 二、类中的主要方法 33 | 34 | ### 1. 构造函数 35 | 36 | `ImageCropperLabel(int width, int height, QWidget* parent = nullptr);` 37 | 38 | 构造时必须提供该控件的固定大小,即width 和 height。 39 | 40 | ### 2. 设置原始图像 41 | 42 | `void setOriginalImage(const QPixmap& pixmap);` 43 | 44 | ### 3. 设置 裁剪器 (形状和大小) 45 | 46 | ```C++ 47 | void setRectCropper(); 48 | void setSquareCropper(); 49 | void setEllipseCropper(); 50 | void setCircleCropper(); 51 | void setFixedRectCropper(QSize size); 52 | void setFixedEllipseCropper(QSize size); 53 | 54 | // Not recommended 55 | void setCropper(CropperShape shape, QSize size); 56 | ``` 57 | 58 | ![image](assets/README/cropper_shape.png) 59 | 60 | ```C++ 61 | /***************************************************************************** 62 | * Set cropper's fixed size (Only work for FIXED_RECT and FIXED_ELLIPSE) 63 | *****************************************************************************/ 64 | void setCropperFixedSize(int fixedWidth, int fixedHeight); 65 | void setCropperFixedWidth(int fixedWidht); 66 | void setCropperFixedHeight(int fixedHeight); 67 | 68 | /***************************************************************************** 69 | * Set cropper's minimum size 70 | * default: the twice of minimum of the edge lenght of drag square 71 | *****************************************************************************/ 72 | void setCropperMinimumSize(int minWidth, int minHeight); 73 | void setCropperMinimumWidth(int minWidth); 74 | void setCropperMinimumHeight(int minHeight); 75 | ``` 76 | 77 | ### 3. 某些效果的显示 78 | 79 | > 下图左侧的就是一个 ImageCropperLabel 控件 80 | > 81 | > 这是一个Demo窗口,用于测试该控件,从右侧可以方便的调节各种参数 82 | > 83 | > 代码在 "example" 目录下 84 | 85 | ![important_functions](assets/README/import_functions.png) 86 | 87 | ## 三、如何使用 88 | 89 | ```C++ 90 | QPixmap pixmap; 91 | pixmap.load("test.png"); 92 | 93 | // 创建控件,并设置固定大小 94 | ImageCropperLabel* imgCropperLabel = new ImageCropperLabel(600, 400, this); 95 | // 设置(圆形)裁剪器 96 | imgCropperLabel->setCircleCropper(); 97 | // 设置原始图像 98 | imgCropperLabel->setOriginalImage(pixmap); 99 | // 设置输出图像的形状 100 | // OutputShape::RECT --> 矩形(正方形) 101 | // OutputShape::ELLIPSE --> 椭圆形(圆形) 102 | imgCropperLabel->setOutputShape(OutputShape::RECT); 103 | // 启用不透明效果,裁剪区域外不透明显示(默认启用) 104 | //imgCropperLabel->enableOpacity(true); 105 | // 设置不透明度(0~1的浮点数) 106 | imgCropperLabel->setOpacity(0.6); // 默认: 0.6 107 | // 显示四个角(四条边)上的矩形方块,用于捕获鼠标,调整裁剪器的大小(默认显示) 108 | //imgCropperLabel->setShowDragSquare(true); 109 | // 设置四个角(四条边)上的矩形方块的大小,颜色 110 | imgCropperLabel->setDragSquareEdge(8); // 默认: 8 111 | imgCropperLabel->setDragSquareColor(Qt::green); // 默认: Qt::white 112 | 113 | // ... 114 | 115 | // SIGNAL:void croppedImageChagned() 116 | // 用户调整裁剪区域时,会触发 croppedImageChanged() 信号 117 | // 调用 getCroppedImage() 可以获取裁剪区域的图像 118 | 119 | QPixmap resultImage = imgCropperLabel->getCroppedImage(/*OutputShape::RECT*/); 120 | ``` 121 | 122 | ## 四、Screenshots 123 | 124 | ![001](assets/README/001.png) 125 | 126 | ![002](assets/README/002.png) 127 | 128 | ![003](assets/README/003.png) 129 | 130 | ![004](assets/README/004.png) 131 | 132 | ![005](assets/README/005.png) 133 | 134 | ![006](assets/README/006.png) 135 | 136 | ## END 137 | 138 | 139 | -------------------------------------------------------------------------------- /assets/README/001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Leopard-C/ImageCropper/8fb7e22bc76676b3f7cf80cf5e80d3bcedaeb55c/assets/README/001.png -------------------------------------------------------------------------------- /assets/README/002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Leopard-C/ImageCropper/8fb7e22bc76676b3f7cf80cf5e80d3bcedaeb55c/assets/README/002.png -------------------------------------------------------------------------------- /assets/README/003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Leopard-C/ImageCropper/8fb7e22bc76676b3f7cf80cf5e80d3bcedaeb55c/assets/README/003.png -------------------------------------------------------------------------------- /assets/README/004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Leopard-C/ImageCropper/8fb7e22bc76676b3f7cf80cf5e80d3bcedaeb55c/assets/README/004.png -------------------------------------------------------------------------------- /assets/README/005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Leopard-C/ImageCropper/8fb7e22bc76676b3f7cf80cf5e80d3bcedaeb55c/assets/README/005.png -------------------------------------------------------------------------------- /assets/README/006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Leopard-C/ImageCropper/8fb7e22bc76676b3f7cf80cf5e80d3bcedaeb55c/assets/README/006.png -------------------------------------------------------------------------------- /assets/README/cropper_shape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Leopard-C/ImageCropper/8fb7e22bc76676b3f7cf80cf5e80d3bcedaeb55c/assets/README/cropper_shape.png -------------------------------------------------------------------------------- /assets/README/import_functions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Leopard-C/ImageCropper/8fb7e22bc76676b3f7cf80cf5e80d3bcedaeb55c/assets/README/import_functions.png -------------------------------------------------------------------------------- /base/imagecropperdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef IMAGECROPPER_H 2 | #define IMAGECROPPER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "imagecropperlabel.h" 16 | 17 | /******************************************************* 18 | * Loacl private class, which do image-cropping 19 | * Used in class ImageCropper 20 | *******************************************************/ 21 | class ImageCropperDialogPrivate : public QDialog { 22 | Q_OBJECT 23 | public: 24 | ImageCropperDialogPrivate(const QPixmap& imageIn, QPixmap& outputImage, 25 | int windowWidth, int windowHeight, 26 | CropperShape shape, QSize cropperSize = QSize()) : 27 | QDialog(nullptr), outputImage(outputImage) 28 | { 29 | this->setAttribute(Qt::WA_DeleteOnClose, true); 30 | this->setWindowTitle("Image Cropper"); 31 | this->setMouseTracking(true); 32 | this->setModal(true); 33 | 34 | imageLabel = new ImageCropperLabel(windowWidth, windowHeight, this); 35 | imageLabel->setCropper(shape, cropperSize); 36 | imageLabel->setOutputShape(OutputShape::RECT); 37 | imageLabel->setOriginalImage(imageIn); 38 | imageLabel->enableOpacity(true); 39 | 40 | QHBoxLayout* btnLayout = new QHBoxLayout(); 41 | btnOk = new QPushButton("OK", this); 42 | btnCancel = new QPushButton("Cancel", this); 43 | btnLayout->addStretch(); 44 | btnLayout->addWidget(btnOk); 45 | btnLayout->addWidget(btnCancel); 46 | 47 | QVBoxLayout* mainLayout = new QVBoxLayout(this); 48 | mainLayout->addWidget(imageLabel); 49 | mainLayout->addLayout(btnLayout); 50 | 51 | connect(btnOk, &QPushButton::clicked, this, [this](){ 52 | this->outputImage = this->imageLabel->getCroppedImage(); 53 | this->close(); 54 | }); 55 | connect(btnCancel, &QPushButton::clicked, this, [this](){ 56 | this->outputImage = QPixmap(); 57 | this->close(); 58 | }); 59 | } 60 | 61 | private: 62 | ImageCropperLabel* imageLabel; 63 | QPushButton* btnOk; 64 | QPushButton* btnCancel; 65 | QPixmap& outputImage; 66 | }; 67 | 68 | 69 | /******************************************************************* 70 | * class ImageCropperDialog 71 | * create a instane of class ImageCropperDialogPrivate 72 | * and get cropped image from the instance(after closing) 73 | ********************************************************************/ 74 | class ImageCropperDialog : QObject { 75 | public: 76 | static QPixmap getCroppedImage(const QString& filename,int windowWidth, int windowHeight, 77 | CropperShape cropperShape, QSize crooperSize = QSize()) 78 | { 79 | QPixmap inputImage; 80 | QPixmap outputImage; 81 | 82 | if (!inputImage.load(filename)) { 83 | QMessageBox::critical(nullptr, "Error", "Load image failed!", QMessageBox::Ok); 84 | return outputImage; 85 | } 86 | 87 | ImageCropperDialogPrivate* imageCropperDo = 88 | new ImageCropperDialogPrivate(inputImage, outputImage, 89 | windowWidth, windowHeight, 90 | cropperShape, crooperSize); 91 | imageCropperDo->exec(); 92 | 93 | return outputImage; 94 | } 95 | }; 96 | 97 | 98 | 99 | #endif // IMAGECROPPER_H 100 | -------------------------------------------------------------------------------- /base/imagecropperlabel.cpp: -------------------------------------------------------------------------------- 1 | #include "imagecropperlabel.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | ImageCropperLabel::ImageCropperLabel(int width, int height, QWidget* parent) : 10 | QLabel(parent) 11 | { 12 | this->setFixedSize(width, height); 13 | this->setAlignment(Qt::AlignCenter); 14 | this->setMouseTracking(true); 15 | 16 | borderPen.setWidth(1); 17 | borderPen.setColor(Qt::white); 18 | borderPen.setDashPattern(QVector() << 3 << 3 << 3 << 3); 19 | } 20 | 21 | void ImageCropperLabel::setOriginalImage(const QPixmap &pixmap) { 22 | originalImage = pixmap; 23 | 24 | int imgWidth = pixmap.width(); 25 | int imgHeight = pixmap.height(); 26 | int labelWidth = this->width(); 27 | int labelHeight = this->height(); 28 | int imgWidthInLabel; 29 | int imgHeightInLabel; 30 | 31 | if (imgWidth * labelHeight < imgHeight * labelWidth) { 32 | scaledRate = labelHeight / double(imgHeight); 33 | imgHeightInLabel = labelHeight; 34 | imgWidthInLabel = int(scaledRate * imgWidth); 35 | imageRect.setRect((labelWidth - imgWidthInLabel) / 2, 0, 36 | imgWidthInLabel, imgHeightInLabel); 37 | } 38 | else { 39 | scaledRate = labelWidth / double(imgWidth); 40 | imgWidthInLabel = labelWidth; 41 | imgHeightInLabel = int(scaledRate * imgHeight); 42 | imageRect.setRect(0, (labelHeight - imgHeightInLabel) / 2, 43 | imgWidthInLabel, imgHeightInLabel); 44 | } 45 | 46 | tempImage = originalImage.scaled(imgWidthInLabel, imgHeightInLabel, 47 | Qt::KeepAspectRatio, Qt::SmoothTransformation); 48 | this->setPixmap(tempImage); 49 | 50 | if (cropperShape >= CropperShape::FIXED_RECT) { 51 | cropperRect.setWidth(int(cropperRect_.width() * scaledRate)); 52 | cropperRect.setHeight(int(cropperRect_.height() * scaledRate)); 53 | } 54 | resetCropperPos(); 55 | } 56 | 57 | 58 | /***************************************** 59 | * set cropper's shape (and size) 60 | *****************************************/ 61 | void ImageCropperLabel::setRectCropper() { 62 | cropperShape = CropperShape::RECT; 63 | resetCropperPos(); 64 | } 65 | 66 | void ImageCropperLabel::setSquareCropper() { 67 | cropperShape = CropperShape::SQUARE; 68 | resetCropperPos(); 69 | } 70 | 71 | void ImageCropperLabel::setEllipseCropper() { 72 | cropperShape = CropperShape::ELLIPSE; 73 | resetCropperPos(); 74 | } 75 | 76 | void ImageCropperLabel::setCircleCropper() { 77 | cropperShape = CropperShape::CIRCLE; 78 | resetCropperPos(); 79 | } 80 | 81 | void ImageCropperLabel::setFixedRectCropper(QSize size) { 82 | cropperShape = CropperShape::FIXED_RECT; 83 | cropperRect_.setSize(size); 84 | resetCropperPos(); 85 | } 86 | 87 | void ImageCropperLabel::setFixedEllipseCropper(QSize size) { 88 | cropperShape = CropperShape::FIXED_ELLIPSE; 89 | cropperRect_.setSize(size); 90 | resetCropperPos(); 91 | } 92 | 93 | // not recommended 94 | void ImageCropperLabel::setCropper(CropperShape shape, QSize size) { 95 | cropperShape = shape; 96 | cropperRect_.setSize(size); 97 | resetCropperPos(); 98 | } 99 | 100 | /***************************************************************************** 101 | * Set cropper's fixed size 102 | *****************************************************************************/ 103 | void ImageCropperLabel::setCropperFixedSize(int fixedWidth, int fixedHeight) { 104 | cropperRect_.setSize(QSize(fixedWidth, fixedHeight)); 105 | resetCropperPos(); 106 | } 107 | 108 | void ImageCropperLabel::setCropperFixedWidth(int fixedWidth) { 109 | cropperRect_.setWidth(fixedWidth); 110 | resetCropperPos(); 111 | } 112 | 113 | void ImageCropperLabel::setCropperFixedHeight(int fixedHeight) { 114 | cropperRect_.setHeight(fixedHeight); 115 | resetCropperPos(); 116 | } 117 | 118 | /********************************************** 119 | * Move cropper to the center of the image 120 | * And resize to default 121 | **********************************************/ 122 | void ImageCropperLabel::resetCropperPos() { 123 | int labelWidth = this->width(); 124 | int labelHeight = this->height(); 125 | 126 | if (cropperShape == CropperShape::FIXED_RECT || cropperShape == CropperShape::FIXED_ELLIPSE) { 127 | cropperRect.setWidth(int(cropperRect_.width() * scaledRate)); 128 | cropperRect.setHeight(int(cropperRect_.height() * scaledRate)); 129 | } 130 | 131 | switch (cropperShape) { 132 | case CropperShape::UNDEFINED: 133 | break; 134 | case CropperShape::FIXED_RECT: 135 | case CropperShape::FIXED_ELLIPSE: { 136 | cropperRect.setRect((labelWidth - cropperRect.width()) / 2, 137 | (labelHeight - cropperRect.height()) / 2, 138 | cropperRect.width(), cropperRect.height()); 139 | break; 140 | } 141 | case CropperShape::RECT: 142 | case CropperShape::SQUARE: 143 | case CropperShape::ELLIPSE: 144 | case CropperShape::CIRCLE: { 145 | int imgWidth = tempImage.width(); 146 | int imgHeight = tempImage.height(); 147 | int edge = int((imgWidth > imgHeight ? imgHeight : imgWidth) * 3 / 4.0); 148 | cropperRect.setRect((labelWidth - edge) / 2, (labelHeight - edge) / 2, edge, edge); 149 | break; 150 | } 151 | } 152 | } 153 | 154 | QPixmap ImageCropperLabel::getCroppedImage() { 155 | return getCroppedImage(this->outputShape); 156 | } 157 | 158 | QPixmap ImageCropperLabel::getCroppedImage(OutputShape shape) { 159 | int startX = int((cropperRect.left() - imageRect.left()) / scaledRate); 160 | int startY = int((cropperRect.top() - imageRect.top()) / scaledRate); 161 | int croppedWidth = int(cropperRect.width() / scaledRate); 162 | int croppedHeight = int(cropperRect.height() / scaledRate); 163 | 164 | QPixmap resultImage(croppedWidth, croppedHeight); 165 | resultImage = originalImage.copy(startX, startY, croppedWidth, croppedHeight); 166 | 167 | // Set ellipse mask (cut to ellipse shape) 168 | if (shape == OutputShape::ELLIPSE) { 169 | QSize size(croppedWidth, croppedHeight); 170 | QBitmap mask(size); 171 | QPainter painter(&mask); 172 | painter.setRenderHint(QPainter::Antialiasing); 173 | painter.setRenderHint(QPainter::SmoothPixmapTransform); 174 | painter.fillRect(0, 0, size.width(), size.height(), Qt::white); 175 | painter.setBrush(QColor(0, 0, 0)); 176 | painter.drawRoundRect(0, 0, size.width(), size.height(), 99, 99); 177 | resultImage.setMask(mask); 178 | } 179 | 180 | return resultImage; 181 | } 182 | 183 | 184 | void ImageCropperLabel::paintEvent(QPaintEvent *event) { 185 | // Draw original image 186 | QLabel::paintEvent(event); 187 | 188 | // Draw cropper and set some effects 189 | switch (cropperShape) { 190 | case CropperShape::UNDEFINED: 191 | break; 192 | case CropperShape::FIXED_RECT: 193 | drawRectOpacity(); 194 | break; 195 | case CropperShape::FIXED_ELLIPSE: 196 | drawEllipseOpacity(); 197 | break; 198 | case CropperShape::RECT: 199 | drawRectOpacity(); 200 | drawSquareEdge(!ONLY_FOUR_CORNERS); 201 | break; 202 | case CropperShape::SQUARE: 203 | drawRectOpacity(); 204 | drawSquareEdge(ONLY_FOUR_CORNERS); 205 | break; 206 | case CropperShape::ELLIPSE: 207 | drawEllipseOpacity(); 208 | drawSquareEdge(!ONLY_FOUR_CORNERS); 209 | break; 210 | case CropperShape::CIRCLE: 211 | drawEllipseOpacity(); 212 | drawSquareEdge(ONLY_FOUR_CORNERS); 213 | break; 214 | } 215 | 216 | // Draw cropper rect 217 | if (isShowRectBorder) { 218 | QPainter painter(this); 219 | painter.setPen(borderPen); 220 | painter.drawRect(cropperRect); 221 | } 222 | } 223 | 224 | void ImageCropperLabel::drawSquareEdge(bool onlyFourCorners) { 225 | if (!isShowDragSquare) 226 | return; 227 | 228 | // Four corners 229 | drawFillRect(cropperRect.topLeft(), dragSquareEdge, dragSquareColor); 230 | drawFillRect(cropperRect.topRight(), dragSquareEdge, dragSquareColor); 231 | drawFillRect(cropperRect.bottomLeft(), dragSquareEdge, dragSquareColor); 232 | drawFillRect(cropperRect.bottomRight(), dragSquareEdge, dragSquareColor); 233 | 234 | // Four edges 235 | if (!onlyFourCorners) { 236 | int centralX = cropperRect.left() + cropperRect.width() / 2; 237 | int centralY = cropperRect.top() + cropperRect.height() / 2; 238 | drawFillRect(QPoint(cropperRect.left(), centralY), dragSquareEdge, dragSquareColor); 239 | drawFillRect(QPoint(centralX, cropperRect.top()), dragSquareEdge, dragSquareColor); 240 | drawFillRect(QPoint(cropperRect.right(), centralY), dragSquareEdge, dragSquareColor); 241 | drawFillRect(QPoint(centralX, cropperRect.bottom()), dragSquareEdge, dragSquareColor); 242 | } 243 | } 244 | 245 | void ImageCropperLabel::drawFillRect(QPoint centralPoint, int edge, QColor color) { 246 | QRect rect(centralPoint.x() - edge / 2, centralPoint.y() - edge / 2, edge, edge); 247 | QPainter painter(this); 248 | painter.fillRect(rect, color); 249 | } 250 | 251 | // Opacity effect 252 | void ImageCropperLabel::drawOpacity(const QPainterPath& path) { 253 | QPainter painterOpac(this); 254 | painterOpac.setOpacity(opacity); 255 | painterOpac.fillPath(path, QBrush(Qt::black)); 256 | } 257 | 258 | void ImageCropperLabel::drawRectOpacity() { 259 | if (isShowOpacityEffect) { 260 | QPainterPath p1, p2, p; 261 | p1.addRect(imageRect); 262 | p2.addRect(cropperRect); 263 | p = p1.subtracted(p2); 264 | drawOpacity(p); 265 | } 266 | } 267 | 268 | void ImageCropperLabel::drawEllipseOpacity() { 269 | if (isShowOpacityEffect) { 270 | QPainterPath p1, p2, p; 271 | p1.addRect(imageRect); 272 | p2.addEllipse(cropperRect); 273 | p = p1.subtracted(p2); 274 | drawOpacity(p); 275 | } 276 | } 277 | 278 | bool ImageCropperLabel::isPosNearDragSquare(const QPoint& pt1, const QPoint& pt2) { 279 | return abs(pt1.x() - pt2.x()) * 2 <= dragSquareEdge 280 | && abs(pt1.y() - pt2.y()) * 2 <= dragSquareEdge; 281 | } 282 | 283 | int ImageCropperLabel::getPosInCropperRect(const QPoint &pt) { 284 | if (isPosNearDragSquare(pt, QPoint(cropperRect.right(), cropperRect.center().y()))) 285 | return RECT_RIGHT; 286 | if (isPosNearDragSquare(pt, cropperRect.bottomRight())) 287 | return RECT_BOTTOM_RIGHT; 288 | if (isPosNearDragSquare(pt, QPoint(cropperRect.center().x(), cropperRect.bottom()))) 289 | return RECT_BOTTOM; 290 | if (isPosNearDragSquare(pt, cropperRect.bottomLeft())) 291 | return RECT_BOTTOM_LEFT; 292 | if (isPosNearDragSquare(pt, QPoint(cropperRect.left(), cropperRect.center().y()))) 293 | return RECT_LEFT; 294 | if (isPosNearDragSquare(pt, cropperRect.topLeft())) 295 | return RECT_TOP_LEFT; 296 | if (isPosNearDragSquare(pt, QPoint(cropperRect.center().x(), cropperRect.top()))) 297 | return RECT_TOP; 298 | if (isPosNearDragSquare(pt, cropperRect.topRight())) 299 | return RECT_TOP_RIGHT; 300 | if (cropperRect.contains(pt, true)) 301 | return RECT_INSIDE; 302 | return RECT_OUTSIZD; 303 | } 304 | 305 | /************************************************* 306 | * 307 | * Change mouse cursor type 308 | * Arrow, SizeHor, SizeVer, etc... 309 | * 310 | *************************************************/ 311 | 312 | void ImageCropperLabel::changeCursor() { 313 | switch (cursorPosInCropperRect) { 314 | case RECT_OUTSIZD: 315 | setCursor(Qt::ArrowCursor); 316 | break; 317 | case RECT_BOTTOM_RIGHT: { 318 | switch (cropperShape) { 319 | case CropperShape::SQUARE: 320 | case CropperShape::CIRCLE: 321 | case CropperShape::RECT: 322 | case CropperShape::ELLIPSE: 323 | setCursor(Qt::SizeFDiagCursor); 324 | break; 325 | default: 326 | break; 327 | } 328 | break; 329 | } 330 | case RECT_RIGHT: { 331 | switch (cropperShape) { 332 | case CropperShape::RECT: 333 | case CropperShape::ELLIPSE: 334 | setCursor(Qt::SizeHorCursor); 335 | break; 336 | default: 337 | break; 338 | } 339 | break; 340 | } 341 | case RECT_BOTTOM: { 342 | switch (cropperShape) { 343 | case CropperShape::RECT: 344 | case CropperShape::ELLIPSE: 345 | setCursor(Qt::SizeVerCursor); 346 | break; 347 | default: 348 | break; 349 | } 350 | break; 351 | } 352 | case RECT_BOTTOM_LEFT: { 353 | switch (cropperShape) { 354 | case CropperShape::RECT: 355 | case CropperShape::ELLIPSE: 356 | case CropperShape::SQUARE: 357 | case CropperShape::CIRCLE: 358 | setCursor(Qt::SizeBDiagCursor); 359 | break; 360 | default: 361 | break; 362 | } 363 | break; 364 | } 365 | case RECT_LEFT: { 366 | switch (cropperShape) { 367 | case CropperShape::RECT: 368 | case CropperShape::ELLIPSE: 369 | setCursor(Qt::SizeHorCursor); 370 | break; 371 | default: 372 | break; 373 | } 374 | break; 375 | } 376 | case RECT_TOP_LEFT: { 377 | switch (cropperShape) { 378 | case CropperShape::RECT: 379 | case CropperShape::ELLIPSE: 380 | case CropperShape::SQUARE: 381 | case CropperShape::CIRCLE: 382 | setCursor(Qt::SizeFDiagCursor); 383 | break; 384 | default: 385 | break; 386 | } 387 | break; 388 | } 389 | case RECT_TOP: { 390 | switch (cropperShape) { 391 | case CropperShape::RECT: 392 | case CropperShape::ELLIPSE: 393 | setCursor(Qt::SizeVerCursor); 394 | break; 395 | default: 396 | break; 397 | } 398 | break; 399 | } 400 | case RECT_TOP_RIGHT: { 401 | switch (cropperShape) { 402 | case CropperShape::SQUARE: 403 | case CropperShape::CIRCLE: 404 | case CropperShape::RECT: 405 | case CropperShape::ELLIPSE: 406 | setCursor(Qt::SizeBDiagCursor); 407 | break; 408 | default: 409 | break; 410 | } 411 | break; 412 | } 413 | case RECT_INSIDE: { 414 | setCursor(Qt::SizeAllCursor); 415 | break; 416 | } 417 | } 418 | } 419 | 420 | /***************************************************** 421 | * 422 | * Mouse Events 423 | * 424 | *****************************************************/ 425 | 426 | void ImageCropperLabel::mousePressEvent(QMouseEvent *e) { 427 | currPos = lastPos = e->pos(); 428 | isLButtonPressed = true; 429 | } 430 | 431 | void ImageCropperLabel::mouseMoveEvent(QMouseEvent *e) { 432 | currPos = e->pos(); 433 | if (!isCursorPosCalculated) { 434 | cursorPosInCropperRect = getPosInCropperRect(currPos); 435 | changeCursor(); 436 | } 437 | 438 | if (!isLButtonPressed) 439 | return; 440 | if (!imageRect.contains(currPos)) 441 | return; 442 | 443 | isCursorPosCalculated = true; 444 | 445 | int xOffset = currPos.x() - lastPos.x(); 446 | int yOffset = currPos.y() - lastPos.y(); 447 | lastPos = currPos; 448 | 449 | int disX = 0; 450 | int disY = 0; 451 | 452 | // Move cropper 453 | switch (cursorPosInCropperRect) { 454 | case RECT_OUTSIZD: 455 | break; 456 | case RECT_BOTTOM_RIGHT: { 457 | disX = currPos.x() - cropperRect.left(); 458 | disY = currPos.y() - cropperRect.top(); 459 | switch (cropperShape) { 460 | case CropperShape::UNDEFINED: 461 | case CropperShape::FIXED_RECT: 462 | case CropperShape::FIXED_ELLIPSE: 463 | break; 464 | case CropperShape::SQUARE: 465 | case CropperShape::CIRCLE: 466 | setCursor(Qt::SizeFDiagCursor); 467 | if (disX >= cropperMinimumWidth && disY >= cropperMinimumHeight) { 468 | if (disX > disY && cropperRect.top() + disX <= imageRect.bottom()) { 469 | cropperRect.setRight(currPos.x()); 470 | cropperRect.setBottom(cropperRect.top() + disX); 471 | emit croppedImageChanged(); 472 | } 473 | else if (disX <= disY && cropperRect.left() + disY <= imageRect.right()) { 474 | cropperRect.setBottom(currPos.y()); 475 | cropperRect.setRight(cropperRect.left() + disY); 476 | emit croppedImageChanged(); 477 | } 478 | } 479 | break; 480 | case CropperShape::RECT: 481 | case CropperShape::ELLIPSE: 482 | setCursor(Qt::SizeFDiagCursor); 483 | if (disX >= cropperMinimumWidth) { 484 | cropperRect.setRight(currPos.x()); 485 | emit croppedImageChanged(); 486 | } 487 | if (disY >= cropperMinimumHeight) { 488 | cropperRect.setBottom(currPos.y()); 489 | emit croppedImageChanged(); 490 | } 491 | break; 492 | } 493 | break; 494 | } 495 | case RECT_RIGHT: { 496 | disX = currPos.x() - cropperRect.left(); 497 | switch (cropperShape) { 498 | case CropperShape::UNDEFINED: 499 | case CropperShape::FIXED_RECT: 500 | case CropperShape::FIXED_ELLIPSE: 501 | case CropperShape::SQUARE: 502 | case CropperShape::CIRCLE: 503 | break; 504 | case CropperShape::RECT: 505 | case CropperShape::ELLIPSE: 506 | if (disX >= cropperMinimumWidth) { 507 | cropperRect.setRight(currPos.x()); 508 | emit croppedImageChanged(); 509 | } 510 | break; 511 | } 512 | break; 513 | } 514 | case RECT_BOTTOM: { 515 | disY = currPos.y() - cropperRect.top(); 516 | switch (cropperShape) { 517 | case CropperShape::UNDEFINED: 518 | case CropperShape::FIXED_RECT: 519 | case CropperShape::FIXED_ELLIPSE: 520 | case CropperShape::SQUARE: 521 | case CropperShape::CIRCLE: 522 | break; 523 | case CropperShape::RECT: 524 | case CropperShape::ELLIPSE: 525 | if (disY >= cropperMinimumHeight) { 526 | cropperRect.setBottom(cropperRect.bottom() + yOffset); 527 | emit croppedImageChanged(); 528 | } 529 | break; 530 | } 531 | break; 532 | } 533 | case RECT_BOTTOM_LEFT: { 534 | disX = cropperRect.right() - currPos.x(); 535 | disY = currPos.y() - cropperRect.top(); 536 | switch (cropperShape) { 537 | case CropperShape::UNDEFINED: 538 | break; 539 | case CropperShape::FIXED_RECT: 540 | case CropperShape::FIXED_ELLIPSE: 541 | case CropperShape::RECT: 542 | case CropperShape::ELLIPSE: 543 | if (disX >= cropperMinimumWidth) { 544 | cropperRect.setLeft(currPos.x()); 545 | emit croppedImageChanged(); 546 | } 547 | if (disY >= cropperMinimumHeight) { 548 | cropperRect.setBottom(currPos.y()); 549 | emit croppedImageChanged(); 550 | } 551 | break; 552 | case CropperShape::SQUARE: 553 | case CropperShape::CIRCLE: 554 | if (disX >= cropperMinimumWidth && disY >= cropperMinimumHeight) { 555 | if (disX > disY && cropperRect.top() + disX <= imageRect.bottom()) { 556 | cropperRect.setLeft(currPos.x()); 557 | cropperRect.setBottom(cropperRect.top() + disX); 558 | emit croppedImageChanged(); 559 | } 560 | else if (disX <= disY && cropperRect.right() - disY >= imageRect.left()) { 561 | cropperRect.setBottom(currPos.y()); 562 | cropperRect.setLeft(cropperRect.right() - disY); 563 | emit croppedImageChanged(); 564 | } 565 | } 566 | break; 567 | } 568 | break; 569 | } 570 | case RECT_LEFT: { 571 | disX = cropperRect.right() - currPos.x(); 572 | switch (cropperShape) { 573 | case CropperShape::UNDEFINED: 574 | case CropperShape::FIXED_RECT: 575 | case CropperShape::FIXED_ELLIPSE: 576 | case CropperShape::SQUARE: 577 | case CropperShape::CIRCLE: 578 | break; 579 | case CropperShape::RECT: 580 | case CropperShape::ELLIPSE: 581 | if (disX >= cropperMinimumHeight) { 582 | cropperRect.setLeft(cropperRect.left() + xOffset); 583 | emit croppedImageChanged(); 584 | } 585 | break; 586 | } 587 | break; 588 | } 589 | case RECT_TOP_LEFT: { 590 | disX = cropperRect.right() - currPos.x(); 591 | disY = cropperRect.bottom() - currPos.y(); 592 | switch (cropperShape) { 593 | case CropperShape::UNDEFINED: 594 | case CropperShape::FIXED_RECT: 595 | case CropperShape::FIXED_ELLIPSE: 596 | break; 597 | case CropperShape::RECT: 598 | case CropperShape::ELLIPSE: 599 | if (disX >= cropperMinimumWidth) { 600 | cropperRect.setLeft(currPos.x()); 601 | emit croppedImageChanged(); 602 | } 603 | if (disY >= cropperMinimumHeight) { 604 | cropperRect.setTop(currPos.y()); 605 | emit croppedImageChanged(); 606 | } 607 | break; 608 | case CropperShape::SQUARE: 609 | case CropperShape::CIRCLE: 610 | if (disX >= cropperMinimumWidth && disY >= cropperMinimumHeight) { 611 | if (disX > disY && cropperRect.bottom() - disX >= imageRect.top()) { 612 | cropperRect.setLeft(currPos.x()); 613 | cropperRect.setTop(cropperRect.bottom() - disX); 614 | emit croppedImageChanged(); 615 | } 616 | else if (disX <= disY && cropperRect.right() - disY >= imageRect.left()) { 617 | cropperRect.setTop(currPos.y()); 618 | cropperRect.setLeft(cropperRect.right() - disY); 619 | emit croppedImageChanged(); 620 | } 621 | } 622 | break; 623 | } 624 | break; 625 | } 626 | case RECT_TOP: { 627 | disY = cropperRect.bottom() - currPos.y(); 628 | switch (cropperShape) { 629 | case CropperShape::UNDEFINED: 630 | case CropperShape::FIXED_RECT: 631 | case CropperShape::FIXED_ELLIPSE: 632 | case CropperShape::SQUARE: 633 | case CropperShape::CIRCLE: 634 | break; 635 | case CropperShape::RECT: 636 | case CropperShape::ELLIPSE: 637 | if (disY >= cropperMinimumHeight) { 638 | cropperRect.setTop(cropperRect.top() + yOffset); 639 | emit croppedImageChanged(); 640 | } 641 | break; 642 | } 643 | break; 644 | } 645 | case RECT_TOP_RIGHT: { 646 | disX = currPos.x() - cropperRect.left(); 647 | disY = cropperRect.bottom() - currPos.y(); 648 | switch (cropperShape) { 649 | case CropperShape::UNDEFINED: 650 | case CropperShape::FIXED_RECT: 651 | case CropperShape::FIXED_ELLIPSE: 652 | break; 653 | case CropperShape::RECT: 654 | case CropperShape::ELLIPSE: 655 | if (disX >= cropperMinimumWidth) { 656 | cropperRect.setRight(currPos.x()); 657 | emit croppedImageChanged(); 658 | } 659 | if (disY >= cropperMinimumHeight) { 660 | cropperRect.setTop(currPos.y()); 661 | emit croppedImageChanged(); 662 | } 663 | break; 664 | case CropperShape::SQUARE: 665 | case CropperShape::CIRCLE: 666 | if (disX >= cropperMinimumWidth && disY >= cropperMinimumHeight) { 667 | if (disX < disY && cropperRect.left() + disY <= imageRect.right()) { 668 | cropperRect.setTop(currPos.y()); 669 | cropperRect.setRight(cropperRect.left() + disY); 670 | emit croppedImageChanged(); 671 | } 672 | else if (disX >= disY && cropperRect.bottom() - disX >= imageRect.top()) { 673 | cropperRect.setRight(currPos.x()); 674 | cropperRect.setTop(cropperRect.bottom() - disX); 675 | emit croppedImageChanged(); 676 | } 677 | } 678 | break; 679 | } 680 | break; 681 | } 682 | case RECT_INSIDE: { 683 | // Make sure the cropperRect is entirely inside the imageRecct 684 | if (xOffset > 0) { 685 | if (cropperRect.right() + xOffset > imageRect.right()) 686 | xOffset = 0; 687 | } 688 | else if (xOffset < 0) { 689 | if (cropperRect.left() + xOffset < imageRect.left()) 690 | xOffset = 0; 691 | } 692 | if (yOffset > 0) { 693 | if (cropperRect.bottom() + yOffset > imageRect.bottom()) 694 | yOffset = 0; 695 | } 696 | else if (yOffset < 0) { 697 | if (cropperRect.top() + yOffset < imageRect.top()) 698 | yOffset = 0; 699 | } 700 | cropperRect.moveTo(cropperRect.left() + xOffset, cropperRect.top() + yOffset); 701 | emit croppedImageChanged(); 702 | } 703 | break; 704 | } 705 | 706 | repaint(); 707 | } 708 | 709 | void ImageCropperLabel::mouseReleaseEvent(QMouseEvent *) { 710 | isLButtonPressed = false; 711 | isCursorPosCalculated = false; 712 | setCursor(Qt::ArrowCursor); 713 | } 714 | 715 | -------------------------------------------------------------------------------- /base/imagecropperlabel.h: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * class: ImageCropperLabel 3 | * author: github@Leopard-C 4 | * email: leopard.c@outlook.com 5 | * last change: 2020-03-06 6 | *************************************************************************/ 7 | #ifndef IMAGECROPPERLABEL_H 8 | #define IMAGECROPPERLABEL_H 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | enum class CropperShape { 15 | UNDEFINED = 0, 16 | RECT = 1, 17 | SQUARE = 2, 18 | FIXED_RECT = 3, 19 | ELLIPSE = 4, 20 | CIRCLE = 5, 21 | FIXED_ELLIPSE = 6 22 | }; 23 | 24 | enum class OutputShape { 25 | RECT = 0, 26 | ELLIPSE = 1 27 | }; 28 | 29 | enum class SizeType { 30 | fixedSize = 0, 31 | fitToMaxWidth = 1, 32 | fitToMaxHeight = 2, 33 | fitToMaxWidthHeight = 3, 34 | }; 35 | 36 | 37 | class ImageCropperLabel : public QLabel { 38 | Q_OBJECT 39 | public: 40 | ImageCropperLabel(int width, int height, QWidget* parent); 41 | 42 | void setOriginalImage(const QPixmap& pixmap); 43 | void setOutputShape(OutputShape shape) { outputShape = shape; } 44 | QPixmap getCroppedImage(); 45 | QPixmap getCroppedImage(OutputShape shape); 46 | 47 | /***************************************** 48 | * Set cropper's shape 49 | *****************************************/ 50 | void setRectCropper(); 51 | void setSquareCropper(); 52 | void setEllipseCropper(); 53 | void setCircleCropper(); 54 | void setFixedRectCropper(QSize size); 55 | void setFixedEllipseCropper(QSize size); 56 | void setCropper(CropperShape shape, QSize size); // not recommended 57 | 58 | /***************************************************************************** 59 | * Set cropper's fixed size 60 | *****************************************************************************/ 61 | void setCropperFixedSize(int fixedWidth, int fixedHeight); 62 | void setCropperFixedWidth(int fixedWidht); 63 | void setCropperFixedHeight(int fixedHeight); 64 | 65 | /***************************************************************************** 66 | * Set cropper's minimum size 67 | * default: the twice of minimum of the edge lenght of drag square 68 | *****************************************************************************/ 69 | void setCropperMinimumSize(int minWidth, int minHeight) 70 | { cropperMinimumWidth = minWidth; cropperMinimumHeight = minHeight; } 71 | void setCropperMinimumWidth(int minWidth) { cropperMinimumWidth = minWidth; } 72 | void setCropperMinimumHeight(int minHeight) { cropperMinimumHeight = minHeight; } 73 | 74 | /************************************************* 75 | * Set the size, color, visibility of rectangular border 76 | *************************************************/ 77 | void setShowRectBorder(bool show) { isShowRectBorder = show; } 78 | QPen getBorderPen() { return borderPen; } 79 | void setBorderPen(const QPen& pen) { borderPen = pen; } 80 | 81 | /************************************************* 82 | * Set the size, color of drag square 83 | *************************************************/ 84 | void setShowDragSquare(bool show) { isShowDragSquare = show; } 85 | void setDragSquareEdge(int edge) { dragSquareEdge = (edge >= 3 ? edge : 3); } 86 | void setDragSquareColor(const QColor& color) { dragSquareColor = color; } 87 | 88 | /***************************************** 89 | * Opacity Effect 90 | *****************************************/ 91 | void enableOpacity(bool b = true) { isShowOpacityEffect = b; } 92 | void setOpacity(double newOpacity) { opacity = newOpacity; } 93 | 94 | signals: 95 | void croppedImageChanged(); 96 | 97 | protected: 98 | /***************************************** 99 | * Event 100 | *****************************************/ 101 | virtual void paintEvent(QPaintEvent *event) override; 102 | virtual void mousePressEvent(QMouseEvent *e) override; 103 | virtual void mouseMoveEvent(QMouseEvent *e) override; 104 | virtual void mouseReleaseEvent(QMouseEvent *e) override; 105 | 106 | private: 107 | /*************************************** 108 | * Draw shapes 109 | ***************************************/ 110 | void drawFillRect(QPoint centralPoint, int edge, QColor color); 111 | void drawRectOpacity(); 112 | void drawEllipseOpacity(); 113 | void drawOpacity(const QPainterPath& path); // shadow effect 114 | void drawSquareEdge(bool onlyFourCorners); 115 | 116 | /*************************************** 117 | * Other utility methods 118 | ***************************************/ 119 | int getPosInCropperRect(const QPoint& pt); 120 | bool isPosNearDragSquare(const QPoint& pt1, const QPoint& pt2); 121 | void resetCropperPos(); 122 | void changeCursor(); 123 | 124 | enum { 125 | RECT_OUTSIZD = 0, 126 | RECT_INSIDE = 1, 127 | RECT_TOP_LEFT, RECT_TOP, RECT_TOP_RIGHT, RECT_RIGHT, 128 | RECT_BOTTOM_RIGHT, RECT_BOTTOM, RECT_BOTTOM_LEFT, RECT_LEFT 129 | }; 130 | 131 | const bool ONLY_FOUR_CORNERS = true; 132 | 133 | private: 134 | QPixmap originalImage; 135 | QPixmap tempImage; 136 | 137 | bool isShowRectBorder = true; 138 | QPen borderPen; 139 | 140 | CropperShape cropperShape = CropperShape::UNDEFINED; 141 | OutputShape outputShape = OutputShape::RECT; 142 | 143 | QRect imageRect; // the whole image area in the label (not real size) 144 | QRect cropperRect; // a rectangle frame to choose image area (not real size) 145 | QRect cropperRect_; // cropper rect (real size) 146 | double scaledRate = 1.0; 147 | 148 | bool isLButtonPressed = false; 149 | bool isCursorPosCalculated = false; 150 | int cursorPosInCropperRect = RECT_OUTSIZD; 151 | QPoint lastPos; 152 | QPoint currPos; 153 | 154 | bool isShowDragSquare = true; 155 | int dragSquareEdge = 8; 156 | QColor dragSquareColor = Qt::white; 157 | 158 | int cropperMinimumWidth = dragSquareEdge * 2; 159 | int cropperMinimumHeight = dragSquareEdge * 2; 160 | 161 | bool isShowOpacityEffect = false; 162 | double opacity = 0.6; 163 | }; 164 | 165 | #endif // IMAGECROPPERLABEL_H 166 | -------------------------------------------------------------------------------- /example/imagecropperdemo.cpp: -------------------------------------------------------------------------------- 1 | #include "imagecropperdemo.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | ImageCropperDemo::ImageCropperDemo(QWidget* parent) : 9 | QDialog(parent) 10 | { 11 | setupLayout(); 12 | init(); 13 | 14 | this->setAttribute(Qt::WA_DeleteOnClose, true); 15 | this->setWindowTitle("Image Cropper Demo"); 16 | } 17 | 18 | 19 | void ImageCropperDemo::setupLayout() { 20 | imgCropperLabel = new ImageCropperLabel(600, 500, this); 21 | imgCropperLabel->setFrameStyle(1); 22 | 23 | comboOutputShape = new QComboBox(this); 24 | comboCropperShape = new QComboBox(this); 25 | 26 | labelPreviewImage = new QLabel(this); 27 | 28 | editOriginalImagePath = new QLineEdit(this); 29 | btnChooseOriginalImagePath = new QPushButton(this); 30 | QHBoxLayout* hOriginalImagePathLayout = new QHBoxLayout(); 31 | hOriginalImagePathLayout->addWidget(editOriginalImagePath); 32 | hOriginalImagePathLayout->addWidget(btnChooseOriginalImagePath); 33 | 34 | editCropperFixedWidth = new QLineEdit(this); 35 | editCropperFixedHeight = new QLineEdit(this); 36 | QHBoxLayout* hCropperFixedSizeLayout = new QHBoxLayout(); 37 | hCropperFixedSizeLayout->addWidget(editCropperFixedWidth); 38 | hCropperFixedSizeLayout->addWidget(editCropperFixedHeight); 39 | 40 | editCropperMinWidth = new QLineEdit("8", this); 41 | editCropperMinHeight = new QLineEdit("8", this); 42 | QHBoxLayout* hCropperMinSizeLayout = new QHBoxLayout(); 43 | hCropperMinSizeLayout->addWidget(editCropperMinWidth); 44 | hCropperMinSizeLayout->addWidget(editCropperMinHeight); 45 | 46 | checkEnableOpacity = new QCheckBox(this); 47 | sliderOpacity = new QSlider(Qt::Horizontal, this); 48 | 49 | checkShowDragSquare = new QCheckBox(this); 50 | editDragSquareEdge = new QLineEdit("8", this); 51 | checkShowRectBorder = new QCheckBox(this); 52 | 53 | labelRectBorderColor = new QLabel(this); 54 | btnChooseRectBorderCorlor = new QPushButton(this); 55 | QHBoxLayout* hRectBorderColorLayout = new QHBoxLayout(); 56 | hRectBorderColorLayout->addWidget(labelRectBorderColor); 57 | hRectBorderColorLayout->addWidget(btnChooseRectBorderCorlor); 58 | 59 | labelDragSquareColor = new QLabel(this); 60 | btnChooseDragSquareColor = new QPushButton(this); 61 | QHBoxLayout* hDragSquareColorLayout = new QHBoxLayout(); 62 | hDragSquareColorLayout->addWidget(labelDragSquareColor); 63 | hDragSquareColorLayout->addWidget(btnChooseDragSquareColor); 64 | 65 | QFormLayout* formLayout1 = new QFormLayout(); 66 | formLayout1->addRow(new QLabel("Preview:"), labelPreviewImage); 67 | formLayout1->addRow(new QLabel("OriginalImage:", this), hOriginalImagePathLayout); 68 | formLayout1->addRow(new QLabel("OutputShape:", this), comboOutputShape); 69 | formLayout1->addRow(new QLabel("CropperShape:", this), comboCropperShape); 70 | formLayout1->addRow(new QLabel("FixedSize:", this), hCropperFixedSizeLayout); 71 | formLayout1->addRow(new QLabel("MinimumSize:", this), hCropperMinSizeLayout); 72 | 73 | QFormLayout* formLayout2 = new QFormLayout(); 74 | formLayout2->addRow(new QLabel("EnableOpacity:", this), checkEnableOpacity); 75 | formLayout2->addRow(new QLabel("Opacity:", this), sliderOpacity); 76 | 77 | QFormLayout* formLayout3 = new QFormLayout(); 78 | formLayout3->addRow(new QLabel("ShowDragSquare:", this), checkShowDragSquare); 79 | formLayout3->addRow(new QLabel("DragSquareEdge:", this), editDragSquareEdge); 80 | formLayout3->addRow(new QLabel("DragSquareColor:", this), hDragSquareColorLayout); 81 | 82 | QFormLayout* formLayout4 = new QFormLayout(); 83 | formLayout4->addRow(new QLabel("ShowRectBorder:", this), checkShowRectBorder); 84 | formLayout4->addRow(new QLabel("RectBorderColor:", this), hRectBorderColorLayout); 85 | 86 | btnSavePreview = new QPushButton("Save", this); 87 | btnQuit = new QPushButton("Quit", this); 88 | QHBoxLayout* btnLayout = new QHBoxLayout(); 89 | btnLayout->addStretch(); 90 | btnLayout->addWidget(btnSavePreview); 91 | btnLayout->addStretch(); 92 | btnLayout->addWidget(btnQuit); 93 | btnLayout->addStretch(); 94 | 95 | QVBoxLayout* vLayout = new QVBoxLayout(); 96 | vLayout->addLayout(formLayout1); 97 | vLayout->addStretch(); 98 | vLayout->addLayout(formLayout2); 99 | vLayout->addStretch(); 100 | vLayout->addLayout(formLayout3); 101 | vLayout->addStretch(); 102 | vLayout->addLayout(formLayout4); 103 | vLayout->addStretch(); 104 | vLayout->addLayout(btnLayout); 105 | 106 | mainLayout = new QHBoxLayout(this); 107 | mainLayout->addWidget(imgCropperLabel); 108 | mainLayout->addLayout(vLayout); 109 | } 110 | 111 | void ImageCropperDemo::init() { 112 | imgCropperLabel->setRectCropper(); 113 | editCropperFixedWidth->setEnabled(false); 114 | editCropperFixedHeight->setEnabled(false); 115 | 116 | labelPreviewImage->setFixedSize(96, 96); 117 | labelPreviewImage->setAlignment(Qt::AlignCenter); 118 | labelPreviewImage->setFrameStyle(QFrame::Panel | QFrame::Sunken); 119 | connect(imgCropperLabel, &ImageCropperLabel::croppedImageChanged, 120 | this, &ImageCropperDemo::onUpdatePreview); 121 | 122 | btnChooseOriginalImagePath->setIcon(QIcon("res/select-file.ico")); 123 | btnChooseOriginalImagePath->setFixedWidth(30); 124 | connect(btnChooseOriginalImagePath, &QPushButton::clicked, 125 | this, &ImageCropperDemo::onChooseOriginalImage); 126 | 127 | comboOutputShape->addItem("Rect/Square"); 128 | comboOutputShape->addItem("Ellipse/Circle"); 129 | connect(comboOutputShape, SIGNAL(currentIndexChanged(int)), 130 | this, SLOT(onOutputShapeChanged(int))); 131 | 132 | comboCropperShape->addItem("Rect"); 133 | comboCropperShape->addItem("Square"); 134 | comboCropperShape->addItem("FixedRect"); 135 | comboCropperShape->addItem("Ellipse"); 136 | comboCropperShape->addItem("Circle"); 137 | comboCropperShape->addItem("FixedEllipse"); 138 | connect(comboCropperShape, SIGNAL(currentIndexChanged(int)), 139 | this, SLOT(onCropperShapeChanged(int))); 140 | 141 | connect(editCropperFixedWidth, &QLineEdit::textChanged, 142 | this, &ImageCropperDemo::onFixedWidthChanged); 143 | connect(editCropperFixedHeight, &QLineEdit::textChanged, 144 | this, &ImageCropperDemo::onFixedHeightChanged); 145 | connect(editCropperMinWidth, &QLineEdit::textChanged, 146 | this, &ImageCropperDemo::onMinWidthChanged); 147 | connect(editCropperMinHeight, &QLineEdit::textChanged, 148 | this, &ImageCropperDemo::onMinHeightChanged); 149 | 150 | checkEnableOpacity->setCheckState(Qt::Checked); 151 | imgCropperLabel->enableOpacity(true); 152 | connect(checkEnableOpacity, &QCheckBox::stateChanged, 153 | this, &ImageCropperDemo::onEnableOpacityChanged); 154 | 155 | checkShowDragSquare->setCheckState(Qt::Checked); 156 | imgCropperLabel->setShowDragSquare(true); 157 | connect(checkShowDragSquare, &QCheckBox::stateChanged, 158 | this, &ImageCropperDemo::onShowDragSquareChanged); 159 | connect(editDragSquareEdge, &QLineEdit::textChanged, 160 | this, &ImageCropperDemo::onDragSquareEdgeChanged); 161 | 162 | sliderOpacity->setRange(0, 100); 163 | sliderOpacity->setValue(60); 164 | connect(sliderOpacity, &QSlider::valueChanged, 165 | this, &ImageCropperDemo::onOpacityChanged); 166 | 167 | checkShowRectBorder->setCheckState(Qt::Checked); 168 | connect(checkShowRectBorder, &QCheckBox::stateChanged, 169 | this, &ImageCropperDemo::onShowRectBorder); 170 | 171 | setLabelColor(labelRectBorderColor, Qt::white); 172 | btnChooseRectBorderCorlor->setIcon(QIcon("res/color-palette.ico")); 173 | btnChooseRectBorderCorlor->setFixedWidth(40); 174 | connect(btnChooseRectBorderCorlor, &QPushButton::clicked, 175 | this, &ImageCropperDemo::onChooseRectBorderColor); 176 | 177 | setLabelColor(labelDragSquareColor, Qt::white); 178 | btnChooseDragSquareColor->setIcon(QIcon("res/color-palette.ico")); 179 | btnChooseDragSquareColor->setFixedWidth(40); 180 | connect(btnChooseDragSquareColor, &QPushButton::clicked, 181 | this, &ImageCropperDemo::onChooseDragSquareColor); 182 | 183 | connect(btnSavePreview, &QPushButton::clicked, 184 | this, &ImageCropperDemo::onSaveCroppedImage); 185 | connect(btnQuit, &QPushButton::clicked, 186 | this, &ImageCropperDemo::close); 187 | 188 | imgCropperLabel->update(); 189 | } 190 | 191 | 192 | /***************************************************************************** 193 | * 194 | * slots 195 | * 196 | *****************************************************************************/ 197 | 198 | void ImageCropperDemo::onChooseOriginalImage() { 199 | QString filename = QFileDialog::getOpenFileName(this, "Select a picture", "", 200 | "picture (*.jpg *.png *.bmp)"); 201 | if (filename.isNull()) 202 | return; 203 | 204 | QPixmap pixmap; 205 | if (!pixmap.load(filename)) { 206 | QMessageBox::critical(this, "Error", "Load image failed", QMessageBox::Ok); 207 | return; 208 | } 209 | 210 | editOriginalImagePath->setText(filename); 211 | imgCropperLabel->setOriginalImage(pixmap); 212 | imgCropperLabel->update(); 213 | onUpdatePreview(); 214 | labelPreviewImage->setFrameStyle(0); 215 | } 216 | 217 | void ImageCropperDemo::onOutputShapeChanged(int idx) { 218 | // Output: Rectangular 219 | if (idx == 0) 220 | imgCropperLabel->setOutputShape(OutputShape::RECT); 221 | else 222 | imgCropperLabel->setOutputShape(OutputShape::ELLIPSE); 223 | onUpdatePreview(); 224 | } 225 | 226 | void ImageCropperDemo::onCropperShapeChanged(int idx) { 227 | switch (CropperShape(idx + 1)) { 228 | case CropperShape::RECT: { 229 | imgCropperLabel->setRectCropper(); 230 | editCropperFixedWidth->setEnabled(false); 231 | editCropperFixedHeight->setEnabled(false); 232 | editCropperMinWidth->setEnabled(true); 233 | editCropperMinHeight->setEnabled(true); 234 | checkShowDragSquare->setEnabled(true); 235 | editDragSquareEdge->setEnabled(true); 236 | btnChooseDragSquareColor->setEnabled(true); 237 | break; 238 | } 239 | case CropperShape::SQUARE: { 240 | imgCropperLabel->setSquareCropper(); 241 | editCropperFixedWidth->setEnabled(false); 242 | editCropperFixedHeight->setEnabled(false); 243 | editCropperMinWidth->setEnabled(true); 244 | editCropperMinHeight->setEnabled(true); 245 | checkShowDragSquare->setEnabled(true); 246 | editDragSquareEdge->setEnabled(true); 247 | btnChooseDragSquareColor->setEnabled(true); 248 | break; 249 | } 250 | case CropperShape::FIXED_RECT: { 251 | imgCropperLabel->setFixedRectCropper(QSize(64, 64)); 252 | editCropperFixedWidth->setEnabled(true); 253 | editCropperFixedHeight->setEnabled(true); 254 | editCropperMinWidth->setEnabled(false); 255 | editCropperMinHeight->setEnabled(false); 256 | editCropperFixedWidth->setText("64"); 257 | editCropperFixedHeight->setText("64"); 258 | checkShowDragSquare->setEnabled(false); 259 | editDragSquareEdge->setEnabled(false); 260 | btnChooseDragSquareColor->setEnabled(false); 261 | break; 262 | } 263 | case CropperShape::ELLIPSE: { 264 | imgCropperLabel->setEllipseCropper(); 265 | editCropperFixedWidth->setEnabled(false); 266 | editCropperFixedHeight->setEnabled(false); 267 | editCropperMinWidth->setEnabled(true); 268 | editCropperMinHeight->setEnabled(true); 269 | checkShowDragSquare->setEnabled(true); 270 | editDragSquareEdge->setEnabled(true); 271 | btnChooseDragSquareColor->setEnabled(true); 272 | break; 273 | } 274 | case CropperShape::CIRCLE: { 275 | imgCropperLabel->setCircleCropper(); 276 | editCropperFixedWidth->setEnabled(false); 277 | editCropperFixedHeight->setEnabled(false); 278 | editCropperMinWidth->setEnabled(true); 279 | editCropperMinHeight->setEnabled(true); 280 | checkShowDragSquare->setEnabled(true); 281 | editDragSquareEdge->setEnabled(true); 282 | btnChooseDragSquareColor->setEnabled(true); 283 | break; 284 | } 285 | case CropperShape::FIXED_ELLIPSE: 286 | imgCropperLabel->setFixedEllipseCropper(QSize(64, 64)); 287 | editCropperFixedWidth->setEnabled(true); 288 | editCropperFixedHeight->setEnabled(true); 289 | editCropperMinWidth->setEnabled(false); 290 | editCropperMinHeight->setEnabled(false); 291 | editCropperFixedWidth->setText("64"); 292 | editCropperFixedHeight->setText("64"); 293 | checkShowDragSquare->setEnabled(false); 294 | editDragSquareEdge->setEnabled(false); 295 | btnChooseDragSquareColor->setEnabled(false); 296 | break; 297 | case CropperShape::UNDEFINED: 298 | break; 299 | } 300 | 301 | imgCropperLabel->update(); 302 | onUpdatePreview(); 303 | } 304 | 305 | void ImageCropperDemo::onEnableOpacityChanged(int state) { 306 | if (state == Qt::Checked) { 307 | sliderOpacity->setEnabled(true); 308 | imgCropperLabel->enableOpacity(true); 309 | } 310 | else { 311 | sliderOpacity->setEnabled(false); 312 | imgCropperLabel->enableOpacity(false); 313 | } 314 | imgCropperLabel->update(); 315 | } 316 | 317 | void ImageCropperDemo::onShowDragSquareChanged(int state) { 318 | if (state == Qt::Checked) { 319 | editDragSquareEdge->setEnabled(true); 320 | btnChooseDragSquareColor->setEnabled(true); 321 | imgCropperLabel->setShowDragSquare(true); 322 | } 323 | else { 324 | editDragSquareEdge->setEnabled(false); 325 | btnChooseDragSquareColor->setEnabled(false); 326 | imgCropperLabel->setShowDragSquare(false); 327 | } 328 | imgCropperLabel->update(); 329 | } 330 | 331 | void ImageCropperDemo::onDragSquareEdgeChanged(QString edge) { 332 | imgCropperLabel->setDragSquareEdge(edge.toInt()); 333 | imgCropperLabel->update(); 334 | } 335 | 336 | void ImageCropperDemo::onOpacityChanged(int val) { 337 | imgCropperLabel->setOpacity(val / 100.0); 338 | imgCropperLabel->update(); 339 | } 340 | 341 | void ImageCropperDemo::onFixedWidthChanged(QString width) { 342 | imgCropperLabel->setCropperFixedWidth(width.toInt()); 343 | imgCropperLabel->update(); 344 | } 345 | 346 | void ImageCropperDemo::onFixedHeightChanged(QString height) { 347 | imgCropperLabel->setCropperFixedHeight(height.toInt()); 348 | imgCropperLabel->update(); 349 | } 350 | 351 | void ImageCropperDemo::onMinWidthChanged(QString width) { 352 | imgCropperLabel->setCropperMinimumWidth(width.toInt()); 353 | imgCropperLabel->update(); 354 | } 355 | 356 | void ImageCropperDemo::onMinHeightChanged(QString height) { 357 | imgCropperLabel->setMinimumHeight(height.toInt()); 358 | imgCropperLabel->update(); 359 | } 360 | 361 | void ImageCropperDemo::onShowRectBorder(int state) { 362 | if (state == Qt::Checked) { 363 | btnChooseRectBorderCorlor->setEnabled(true); 364 | imgCropperLabel->setShowRectBorder(true); 365 | } 366 | else { 367 | btnChooseRectBorderCorlor->setEnabled(false); 368 | imgCropperLabel->setShowRectBorder(false); 369 | } 370 | imgCropperLabel->update(); 371 | } 372 | 373 | void ImageCropperDemo::onChooseRectBorderColor() { 374 | QColor color = QColorDialog::getColor(imgCropperLabel->getBorderPen().color(), this); 375 | if (color.isValid()) { 376 | setLabelColor(labelRectBorderColor, color); 377 | QPen pen = imgCropperLabel->getBorderPen(); 378 | pen.setColor(color); 379 | imgCropperLabel->setBorderPen(pen); 380 | imgCropperLabel->update(); 381 | } 382 | } 383 | 384 | void ImageCropperDemo::onChooseDragSquareColor() { 385 | QColor color = QColorDialog::getColor(Qt::white, this); 386 | if (color.isValid()) { 387 | setLabelColor(labelDragSquareColor, color); 388 | imgCropperLabel->setDragSquareColor(color); 389 | imgCropperLabel->update(); 390 | } 391 | } 392 | 393 | void ImageCropperDemo::onUpdatePreview() { 394 | QPixmap preview = imgCropperLabel->getCroppedImage(); 395 | preview = preview.scaled(labelPreviewImage->width(), labelPreviewImage->height(), 396 | Qt::KeepAspectRatio, Qt::SmoothTransformation); 397 | labelPreviewImage->setPixmap(preview); 398 | } 399 | 400 | void ImageCropperDemo::onSaveCroppedImage() { 401 | const QPixmap* pixmap = labelPreviewImage->pixmap(); 402 | if (!pixmap) { 403 | QMessageBox::information(this, "Error", "There is no cropped image to save.", QMessageBox::Ok); 404 | return ; 405 | } 406 | 407 | QString filename = QFileDialog::getSaveFileName(this, "Save cropped image", "", "picture (*.png)"); 408 | if (!filename.isNull()) { 409 | if (imgCropperLabel->getCroppedImage().save(filename, "PNG")) 410 | QMessageBox::information(this, "Prompt", "Saved successfully", QMessageBox::Ok); 411 | else 412 | QMessageBox::information(this, "Error", "Save image failed!", QMessageBox::Ok); 413 | } 414 | } 415 | 416 | -------------------------------------------------------------------------------- /example/imagecropperdemo.h: -------------------------------------------------------------------------------- 1 | #ifndef TESTIMAGECROPPERLABEL_H 2 | #define TESTIMAGECROPPERLABEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "../base/imagecropperlabel.h" 15 | 16 | class ImageCropperDemo : public QDialog 17 | { 18 | Q_OBJECT 19 | public: 20 | ImageCropperDemo(QWidget* parent = nullptr); 21 | 22 | void setupLayout(); 23 | 24 | void init(); 25 | 26 | public slots: 27 | void onOutputShapeChanged(int idx); 28 | void onCropperShapeChanged(int idx); 29 | void onEnableOpacityChanged(int state); 30 | void onShowDragSquareChanged(int state); 31 | void onDragSquareEdgeChanged(QString edge); 32 | void onOpacityChanged(int val); 33 | void onFixedWidthChanged(QString width); 34 | void onFixedHeightChanged(QString height); 35 | void onMinWidthChanged(QString width); 36 | void onMinHeightChanged(QString height); 37 | void onShowRectBorder(int state); 38 | void onChooseRectBorderColor(); 39 | void onChooseDragSquareColor(); 40 | 41 | void onChooseOriginalImage(); 42 | void onUpdatePreview(); 43 | void onSaveCroppedImage(); 44 | 45 | private: 46 | void setLabelColor(QLabel* label, QColor color) { 47 | QPixmap pixmap(QSize(80, 25)); 48 | pixmap.fill(color); 49 | label->setPixmap(pixmap); 50 | } 51 | 52 | private: 53 | ImageCropperLabel* imgCropperLabel; 54 | QHBoxLayout* mainLayout; 55 | 56 | QLabel* labelPreviewImage; 57 | 58 | QComboBox* comboOutputShape; 59 | QComboBox* comboCropperShape; 60 | 61 | QLineEdit* editOriginalImagePath; 62 | QPushButton* btnChooseOriginalImagePath; 63 | 64 | QLineEdit* editCropperFixedWidth; 65 | QLineEdit* editCropperFixedHeight; 66 | QLineEdit* editCropperMinWidth; 67 | QLineEdit* editCropperMinHeight; 68 | 69 | QCheckBox* checkShowDragSquare; 70 | QCheckBox* checkEnableOpacity; 71 | QSlider* sliderOpacity; 72 | QLineEdit* editDragSquareEdge; 73 | 74 | QCheckBox* checkShowRectBorder; 75 | QLabel* labelRectBorderColor; 76 | QPushButton* btnChooseRectBorderCorlor; 77 | 78 | QLabel* labelDragSquareColor; 79 | QPushButton* btnChooseDragSquareColor; 80 | 81 | QPushButton* btnSavePreview; 82 | QPushButton* btnQuit; 83 | }; 84 | 85 | #endif // TESTIMAGECROPPERLABEL_H 86 | -------------------------------------------------------------------------------- /example/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | 3 | #include 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | QApplication a(argc, argv); 8 | MainWindow w; 9 | w.show(); 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /example/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "imagecropperdemo.h" 3 | 4 | #include "../base/imagecropperdialog.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | MainWindow::MainWindow(QWidget *parent) 12 | : QMainWindow(parent) 13 | { 14 | setupLayout(); 15 | } 16 | 17 | MainWindow::~MainWindow() 18 | { 19 | } 20 | 21 | void MainWindow::setupLayout() { 22 | QWidget* centralWidget = new QWidget(this); 23 | 24 | btnCustomCrop = new QPushButton("Custom Crop", centralWidget); 25 | btnSimpleCrop = new QPushButton("Simple Crop", centralWidget); 26 | 27 | QVBoxLayout* mainLayout = new QVBoxLayout(centralWidget); 28 | mainLayout->addWidget(btnCustomCrop); 29 | mainLayout->addWidget(btnSimpleCrop); 30 | this->setCentralWidget(centralWidget); 31 | 32 | connect(btnCustomCrop, &QPushButton::clicked, this, &MainWindow::onCustomCrop); 33 | connect(btnSimpleCrop, &QPushButton::clicked, this, &MainWindow::onSimpleCrop); 34 | } 35 | 36 | void MainWindow::onCustomCrop() { 37 | ImageCropperDemo* dialog = new ImageCropperDemo(this); 38 | dialog->show(); 39 | } 40 | 41 | void MainWindow::onSimpleCrop() { 42 | QMessageBox::information(this, "Prompt", "Please select a picture", QMessageBox::Ok); 43 | QString filename = QFileDialog::getOpenFileName(this, "Select image", "", "image (*.png *.jpg)"); 44 | if (filename.isNull()) 45 | return; 46 | 47 | // ********* 48 | // ******* 49 | // ***** 50 | // *** 51 | // * 52 | QPixmap image = ImageCropperDialog::getCroppedImage(filename, 600, 400, CropperShape::CIRCLE); 53 | if (image.isNull()) 54 | return; 55 | 56 | QDialog* dialog = new QDialog(nullptr); 57 | dialog->setAttribute(Qt::WA_DeleteOnClose, true); 58 | QLabel* label = new QLabel(dialog); 59 | label->setFixedSize(image.size()); 60 | label->setPixmap(image); 61 | dialog->exec(); 62 | } 63 | -------------------------------------------------------------------------------- /example/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | 7 | class MainWindow : public QMainWindow 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | MainWindow(QWidget *parent = nullptr); 13 | ~MainWindow(); 14 | 15 | public slots: 16 | void onCustomCrop(); 17 | void onSimpleCrop(); 18 | 19 | private: 20 | void setupLayout(); 21 | 22 | private: 23 | QPushButton* btnCustomCrop; 24 | QPushButton* btnSimpleCrop; 25 | }; 26 | #endif // MAINWINDOW_H 27 | -------------------------------------------------------------------------------- /res/color-palette.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Leopard-C/ImageCropper/8fb7e22bc76676b3f7cf80cf5e80d3bcedaeb55c/res/color-palette.ico -------------------------------------------------------------------------------- /res/save.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Leopard-C/ImageCropper/8fb7e22bc76676b3f7cf80cf5e80d3bcedaeb55c/res/save.ico -------------------------------------------------------------------------------- /res/select-file.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Leopard-C/ImageCropper/8fb7e22bc76676b3f7cf80cf5e80d3bcedaeb55c/res/select-file.ico --------------------------------------------------------------------------------