egon-r 3 роки тому
коміт
87ffa40f0e

+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+.qt/
+.idea/
+*-build-*/
+*.exe

+ 77 - 0
CMakeLists.txt

@@ -0,0 +1,77 @@
+include(cmake/GitUtils.cmake)
+cmake_minimum_required(VERSION 3.24)
+
+set(CMAKE_PREFIX_PATH "C:/Qt/6.6.0/mingw_64")
+set(WINDEPLOYQT_EXE "${CMAKE_PREFIX_PATH}/bin/windeployqt6.exe")
+
+project(wincap11 VERSION 0.1)
+SetGitHash()
+configure_file(src/util/Version.h.in src/util/Version.h)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+set(CMAKE_AUTOUIC ON)
+set(EXECUTABLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}/bin")
+
+find_package(Qt6 REQUIRED COMPONENTS
+        Core
+        Gui
+        Widgets
+        )
+
+add_executable(wincap11 src/main.cpp src/ui/mainwindow.cpp src/ui/mainwindow.h src/ui/mainwindow.ui src/util/ScreenshotUtil.cpp src/util/ScreenshotUtil.h src/ui/qpictureview.cpp src/ui/qpictureview.h src/util/Version.h.in src/ui/stylesheet.h)
+
+target_include_directories(wincap11 PUBLIC "${PROJECT_BINARY_DIR}/src")
+
+qt_import_plugins(wincap11
+        INCLUDE imageformats
+        )
+
+target_link_libraries(wincap11
+        Qt6::Core
+        Qt6::Gui
+        Qt6::Widgets
+        )
+
+if (WIN32)
+    set(DEBUG_SUFFIX)
+    set(QT_INSTALL_PATH "${CMAKE_PREFIX_PATH}")
+
+    # Release build
+    if (CMAKE_BUILD_TYPE STREQUAL "Release")
+        # Prevent opening a terminal window
+        set_property(TARGET ${PROJECT_NAME} PROPERTY WIN32_EXECUTABLE true)
+    endif ()
+
+    # run windeployqt to copy all required .dll files
+    execute_process(COMMAND ${WINDEPLOYQT_EXE} "${EXECUTABLE_OUTPUT_PATH}")
+
+    if (MSVC AND CMAKE_BUILD_TYPE MATCHES "Debug")
+        set(DEBUG_SUFFIX "d")
+    endif ()
+
+    if (NOT EXISTS "${QT_INSTALL_PATH}/bin")
+        set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..")
+        if (NOT EXISTS "${QT_INSTALL_PATH}/bin")
+            set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..")
+        endif ()
+    endif ()
+
+    if (EXISTS "${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll")
+        add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
+                COMMAND ${CMAKE_COMMAND} -E make_directory
+                "$<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins/platforms/")
+        add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
+                COMMAND ${CMAKE_COMMAND} -E copy
+                "${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll"
+                "$<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins/platforms/")
+    endif ()
+
+    foreach (QT_LIB Core Gui Widgets)
+        add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
+                COMMAND ${CMAKE_COMMAND} -E copy
+                "${QT_INSTALL_PATH}/bin/Qt6${QT_LIB}${DEBUG_SUFFIX}.dll"
+                "$<TARGET_FILE_DIR:${PROJECT_NAME}>")
+    endforeach (QT_LIB)
+endif ()

+ 23 - 0
README.md

@@ -0,0 +1,23 @@
+# wincap11
+Take screenshots on Windows 11 with transparent rounded corners. 
+
+> Snipping Tool
+>
+> ![](/README_assets/calc_snipping_tool2.png)
+
+> wincap11
+> 
+> ![](/README_assets/calc_wincap112.png)
+
+
+# Build
+### Requirements
+- cmake, c++ toolchain
+- Qt6
+- NSIS
+
+### Create Setup
+1. Check that `CMAKE_PREFIX_PATH` in `CMakeLists.txt` points to Qt (e.g. `"C:/Qt/6.6.0/mingw_64"`)
+2. Use `cmake` to create makefiles/project
+3. Build
+4. `makensis installer.nsi`

BIN
README_assets/calc_snipping_tool2.png


BIN
README_assets/calc_wincap112.png


BIN
README_assets/calc_wincap113.png


+ 9 - 0
cmake/GitUtils.cmake

@@ -0,0 +1,9 @@
+function(SetGitHash)
+    execute_process(
+            COMMAND git log -1 --format=%h
+            WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
+            OUTPUT_VARIABLE GIT_HASH
+            OUTPUT_STRIP_TRAILING_WHITESPACE
+    )
+    set(GIT_HASH ${GIT_HASH} CACHE INTERNAL "")
+endfunction()

+ 64 - 0
installer.nsi

@@ -0,0 +1,64 @@
+!define APP_NAME "wincap11"
+Name ${APP_NAME}
+
+OutFile "${APP_NAME}-setup.exe"
+
+InstallDir $PROGRAMFILES\${APP_NAME}
+; Store the install dir in the registry to use when updating
+InstallDirRegKey HKLM "Software\${APP_NAME}" "InstallDir"
+
+RequestExecutionLevel user
+
+;##################################
+; Pages
+Page components
+Page directory
+Page instfiles
+
+UninstPage uninstConfirm
+UninstPage instfiles
+
+;##################################
+; Install
+Section "${APP_NAME} (required)"
+  SectionIn RO
+
+  SetOutPath $INSTDIR
+
+  ; App Files
+  File /r "cmake-build-release\bin\*"
+
+  ; Write the installation path into the registry
+  WriteRegStr HKLM SOFTWARE\${APP_NAME} "InstallDir" "$INSTDIR"
+
+  ; Write the uninstall keys for Windows
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\YOURPROGRAM" "DisplayName" "${APP_NAME}"
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\YOURPROGRAM" "UninstallString" '"$INSTDIR\uninstall.exe"'
+  WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\YOURPROGRAM" "NoModify" 1
+  WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\YOURPROGRAM" "NoRepair" 1
+  WriteUninstaller "$INSTDIR\uninstall.exe"
+SectionEnd
+
+; Optional section (can be disabled by the user)
+Section "Start Menu Shortcuts"
+  SectionIn RO
+
+  CreateDirectory "$SMPROGRAMS\${APP_NAME}"
+  CreateShortcut "$SMPROGRAMS\${APP_NAME}\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0
+  CreateShortcut "$SMPROGRAMS\${APP_NAME}\${APP_NAME}.lnk" "$INSTDIR\${APP_NAME}.exe" "" "$INSTDIR\${APP_NAME}.exe" 0
+SectionEnd
+
+;##################################
+; Uninstall
+Section "Uninstall"
+  ; Remove registry keys
+  DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}"
+  DeleteRegKey HKLM SOFTWARE\${APP_NAME}
+
+  ; Remove shortcuts
+  Delete "$SMPROGRAMS\${APP_NAME}\*.*"
+
+  ; Remove install dir
+  RMDir "$SMPROGRAMS\${APP_NAME}"
+  RMDir /r "$INSTDIR"
+SectionEnd

+ 52 - 0
src/main.cpp

@@ -0,0 +1,52 @@
+#include <QApplication>
+#include <QCommandLineParser>
+#include <QCommandLineOption>
+#include <iostream>
+#include <QFileInfo>
+#include <QThread>
+#include "ui/mainwindow.h"
+#include "util/ScreenshotUtil.h"
+#include "ui/stylesheet.h"
+
+int main(int argc, char *argv[]) {
+    QApplication app(argc, argv);
+    app.setStyleSheet(stylesheet);
+
+    // cmd app
+    QCommandLineParser cmdParser;
+    auto helpOption = cmdParser.addHelpOption();
+    QCommandLineOption outputFileOption(
+            "o", QCoreApplication::translate("main", "Output file or directory."),
+            "output-file"
+    );
+    cmdParser.addOption(outputFileOption);
+    QCommandLineOption delayOption(
+            "d", QCoreApplication::translate("main", "Delay before creating a screenshot."),
+            "delay"
+    );
+    cmdParser.addOption(delayOption);
+    cmdParser.process(app);
+
+    if(cmdParser.isSet(helpOption)) {
+        return 0;
+    } else if(cmdParser.isSet(outputFileOption)) {
+        auto outFileName = cmdParser.value(outputFileOption);
+        ScreenshotUtil screenshotUtil;
+        int delay = cmdParser.value(delayOption).toInt();
+        if(delay <= 0) {
+            delay = 3000;
+        }
+        std::cout << "Screenshot of active window in" << delay << "ms ..." << std::endl;
+        QThread::msleep(delay);
+        auto windowImg = screenshotUtil.CaptureForegroundWindow();
+        windowImg->save(outFileName);
+        std::cout << outFileName.toStdString() << std::endl;
+        return 0;
+    }
+
+    // gui app
+    MainWindow mainWindow;
+    mainWindow.show();
+
+    return QApplication::exec();
+}

+ 91 - 0
src/ui/mainwindow.cpp

@@ -0,0 +1,91 @@
+//
+// Created by egonr on 2/11/2023.
+//
+
+// You may need to build the project (run Qt uic code generator) to get "ui_MainWindow.h" resolved
+
+#include "mainwindow.h"
+#include "ui_MainWindow.h"
+#include "windows.h"
+#include "../util/ScreenshotUtil.h"
+#include "qpictureview.h"
+#include <QScreen>
+#include <QPainter>
+#include <QRgb>
+#include <QColorSpace>
+#include <QDateTime>
+#include <QFileDialog>
+#include <QBuffer>
+#include "util/Version.h"
+
+MainWindow::MainWindow(QWidget *parent) :
+        QWidget(parent), ui(new Ui::MainWindow) {
+    ui->setupUi(this);
+    setWindowTitle(QString("wincap11 (v%1.%2-%3)").arg(wincap11_VERSION_MAJOR).arg(wincap11_VERSION_MINOR).arg(wincap11_GIT_HASH));
+
+    pictureView = QSharedPointer<QPictureView>(new QPictureView(ui->pictureViewContainer));
+    pictureView->show();
+
+    connect(ui->captureButton, SIGNAL(clicked()), this, SLOT(handleCaptureButton()));
+    connect(&captureTimer, SIGNAL(timeout()), this, SLOT(captureTimerTimeout()));
+    connect(ui->saveButton, SIGNAL(clicked()), this, SLOT(handleSaveButton()));
+}
+
+MainWindow::~MainWindow() {
+    delete ui;
+}
+
+void MainWindow::handleSaveButton() {
+    QString suggestedName = "Screenshot_" + currentScreenshotTime.toString("yyyyMMdd_HHmmss_zzz") + ".png";
+    QString saveFileName = QFileDialog::getSaveFileName(this, tr("Save File"), suggestedName);
+    currentScreenshot->save(saveFileName);
+}
+
+void MainWindow::handleCaptureButton() {
+    SetUIStateCapturing();
+    this->captureTimer.start(ui->delayBox->value());
+}
+
+void MainWindow::captureTimerTimeout() {
+    ScreenshotUtil screenshotUtil;
+    currentScreenshot = QSharedPointer<QImage>(screenshotUtil.CaptureForegroundWindow(WINDOWS_11));
+    currentScreenshotTime = QDateTime::currentDateTime();
+    pictureView->SetImage(currentScreenshot.get());
+
+    this->captureTimer.stop();
+    ui->delayBox->setValue(captureTimer.interval());
+
+    SetUIStateCaptureFinished();
+}
+
+void MainWindow::SetUIStateCapturing() {
+    ui->leftStatusLabel->setText("Capturing...");
+    ui->rightStatusLabel->setText("");
+    ui->captureButton->setEnabled(false);
+}
+
+void MainWindow::SetUIStateCaptureFinished() {
+    // window to front
+    raise();
+    activateWindow();
+
+    QBuffer tmpBuffer;
+    currentScreenshot->save(&tmpBuffer, "PNG");
+
+    ui->leftStatusLabel->setText(
+            QString("%1 x %2").arg(currentScreenshot->width()).arg(currentScreenshot->height())
+    );
+    ui->rightStatusLabel->setText(
+            QString("%1 (PNG)").arg(locale().formattedDataSize(tmpBuffer.size(), 1))
+    );
+    ui->captureButton->setEnabled(true);
+    ui->saveButton->setEnabled(true);
+    ui->saveButton->setFocus();
+}
+
+void MainWindow::paintEvent(QPaintEvent *event) {
+    if(this->captureTimer.isActive()) {
+        ui->delayBox->setValue(this->captureTimer.remainingTime());
+    }
+    QWidget::paintEvent(event);
+}

+ 48 - 0
src/ui/mainwindow.h

@@ -0,0 +1,48 @@
+//
+// Created by egonr on 2/11/2023.
+//
+
+#ifndef WINCAP11_MAINWINDOW_H
+#define WINCAP11_MAINWINDOW_H
+
+#include <QWidget>
+#include <QTimer>
+#include <QDateTime>
+#include <QGraphicsPixmapItem>
+#include "qpictureview.h"
+
+QT_BEGIN_NAMESPACE
+namespace Ui { class MainWindow; }
+QT_END_NAMESPACE
+
+
+class MainWindow : public QWidget {
+Q_OBJECT
+
+public:
+    explicit MainWindow(QWidget *parent = nullptr);
+
+    ~MainWindow() override;
+
+private:
+    Ui::MainWindow *ui;
+    QTimer captureTimer;
+    QSharedPointer<QPictureView> pictureView;
+    QDateTime currentScreenshotTime;
+    QSharedPointer<QImage> currentScreenshot;
+    QSharedPointer<QGraphicsPixmapItem> graphicsItem;
+
+
+private:
+    void SetUIStateCapturing();
+    void SetUIStateCaptureFinished();
+    void paintEvent(QPaintEvent *event) override;
+
+private slots:
+    void handleCaptureButton();
+    void captureTimerTimeout();
+    void handleSaveButton();
+};
+
+
+#endif //WINCAP11_MAINWINDOW_H

+ 111 - 0
src/ui/mainwindow.ui

@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QWidget" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>473</width>
+    <height>259</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>wincap11</string>
+  </property>
+  <property name="styleSheet">
+   <string notr="true"/>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="topControlsLayout">
+     <item>
+      <widget class="QPushButton" name="captureButton">
+       <property name="text">
+        <string>Capture</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QComboBox" name="modeComboBox">
+       <item>
+        <property name="text">
+         <string>Active Window</string>
+        </property>
+       </item>
+      </widget>
+     </item>
+     <item>
+      <widget class="QSpinBox" name="delayBox">
+       <property name="minimum">
+        <number>1</number>
+       </property>
+       <property name="maximum">
+        <number>99999999</number>
+       </property>
+       <property name="singleStep">
+        <number>1000</number>
+       </property>
+       <property name="value">
+        <number>3000</number>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="saveButton">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="text">
+        <string>Save</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QWidget" name="pictureViewContainer" native="true">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <widget class="QLabel" name="leftStatusLabel">
+       <property name="text">
+        <string> </string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QLabel" name="rightStatusLabel">
+       <property name="text">
+        <string> </string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 51 - 0
src/ui/qpictureview.cpp

@@ -0,0 +1,51 @@
+//
+// Created by egonr on 2/14/2023.
+//
+
+// You may need to build the project (run Qt uic code generator) to get "ui_QPictureView.h" resolved
+
+#include <QPainter>
+#include "qpictureview.h"
+
+
+QPictureView::QPictureView(QWidget *parent) : QLabel(parent) {
+    setAlignment(Qt::AlignmentFlag::AlignCenter);
+}
+
+
+QPictureView::~QPictureView() {
+}
+
+void QPictureView::paintEvent(QPaintEvent *event) {
+    setGeometry(0,0,parentWidget()->width(), parentWidget()->height());
+
+    QPainter painter;
+    painter.begin(this);
+    painter.fillRect(rect(), Qt::black);
+
+    if(!sourcePixmap.isNull()) {
+        int scaleWidth = this->width();
+        int scaleHeight = this->height();
+        if(this->width() > sourcePixmap.width()) {
+            scaleWidth = sourcePixmap.width();
+        }
+        if(this->height() > sourcePixmap.height()) {
+            scaleHeight = sourcePixmap.height();
+        }
+        setPixmap(sourcePixmap.scaled(scaleWidth, scaleHeight, Qt::KeepAspectRatio));
+    } else {
+        setText("...");
+    }
+
+    QLabel::paintEvent(event);
+}
+
+void QPictureView::SetImage(QImage *image) {
+    QPictureView::SetPixmap(new QPixmap(QPixmap::fromImage(*image)));
+}
+
+void QPictureView::SetPixmap(QPixmap *pixmap) {
+    sourcePixmap = *pixmap;
+    QLabel::setPixmap(sourcePixmap);
+}
+

+ 28 - 0
src/ui/qpictureview.h

@@ -0,0 +1,28 @@
+//
+// Created by egonr on 2/14/2023.
+//
+
+#ifndef WINCAP11_QPICTUREVIEW_H
+#define WINCAP11_QPICTUREVIEW_H
+
+#include <QLabel>
+
+class QPictureView : public QLabel {
+Q_OBJECT
+
+public:
+    explicit QPictureView(QWidget *parent = nullptr);
+    ~QPictureView() override;
+    void SetImage(QImage *image);
+    void SetPixmap(QPixmap *pixmap);
+
+private:
+    QPixmap sourcePixmap;
+
+private:
+    void paintEvent(QPaintEvent *event) override;
+
+};
+
+
+#endif //WINCAP11_QPICTUREVIEW_H

+ 53 - 0
src/ui/stylesheet.h

@@ -0,0 +1,53 @@
+//
+// Created by egonr on 2/16/2023.
+//
+
+#ifndef WINCAP11_STYLESHEET_H
+#define WINCAP11_STYLESHEET_H
+auto stylesheet = QString(R"(
+* {
+    background: #111;
+    color: #ddd;
+}
+
+QWidget::item:selected {
+    background: #4040ff;
+}
+
+QPushButton {
+    border: 1px solid #bbb;
+    padding: 4px 0px;
+    border-radius: 4px;
+    background-color: #191919;
+    min-width: 80px;
+}
+QPushButton:hover {
+    border: 1px solid #eee;
+    background-color: #222;
+}
+QPushButton:focus {
+    border: 1px solid #4040ff;
+    outline: none;
+}
+QPushButton:disabled {
+    background-color: #666;
+    color: #222;
+}
+QPushButton:pressed {
+    background-color: #333333;
+}
+
+QComboBox {
+    border: 1px solid #bbb;
+    padding: 3px 8px;
+    background-color: #191919;
+    min-width: 80px;
+}
+
+QSpinBox {
+    border: 1px solid #bbb;
+    padding: 3px 8px;
+    background-color: #191919;
+    min-width: 80px;
+})");
+#endif //WINCAP11_STYLESHEET_H

+ 174 - 0
src/util/ScreenshotUtil.cpp

@@ -0,0 +1,174 @@
+//
+// Created by egonr on 2/13/2023.
+//
+
+#include "ScreenshotUtil.h"
+#include <QGuiApplication>
+#include <QPainter>
+#include <QScreen>
+#include "windows.h"
+
+QImage *ScreenshotUtil::CaptureForegroundWindow(CropStyle cropStyle) {
+    auto windowRect = GetForegroundWindowRect(cropStyle);
+    if (windowRect.second) {
+        return ScreenshotRect(windowRect.first);
+    } else {
+        auto img = ScreenshotRect(windowRect.first);
+        return CropCorners(CropEdges(img, cropStyle), cropStyle);
+    }
+}
+
+QPair<QRect, bool> ScreenshotUtil::GetForegroundWindowRect(CropStyle cropStyle) {
+    HWND hwnd = GetForegroundWindow();
+
+    WINDOWPLACEMENT hwndPlacement;
+    GetWindowPlacement(hwnd, &hwndPlacement);
+    bool isMaximized = hwndPlacement.showCmd == SW_MAXIMIZE;
+
+    RECT hwndRect;
+    GetWindowRect(hwnd, &hwndRect);
+    QRect hwndQRect(hwndRect.left, hwndRect.top,
+                    abs(hwndRect.right - hwndRect.left),
+                    abs(hwndRect.bottom - hwndRect.top)
+    );
+
+    if (isMaximized) {
+        if (cropStyle == WINDOWS_11) {
+            hwndQRect = QRect(hwndRect.left + 8, hwndRect.top + 8,
+                              abs(hwndRect.right - hwndRect.left - 16),
+                              abs(hwndRect.bottom - hwndRect.top - 16)
+            );
+        }
+    }
+
+    return {hwndQRect, isMaximized};
+}
+
+QImage *ScreenshotUtil::ScreenshotRect(QRect windowRect) {
+    auto windowImg = new QImage(windowRect.width(), windowRect.height(), QImage::Format_ARGB32);
+    QPainter painter(windowImg);
+    auto screens = QGuiApplication::screens();
+    for (auto screen: screens) {
+        if (screen->geometry().intersects(windowRect)) {
+            auto intersectRect = screen->geometry().intersected(windowRect);
+            auto intersectPix = screen->grabWindow(0,
+                                                   abs(intersectRect.left() - screen->geometry().left()),
+                                                   abs(intersectRect.top() - screen->geometry().top()),
+                                                   intersectRect.width(), intersectRect.height()
+            );
+            painter.drawPixmap(intersectRect.left() - windowRect.left(),
+                               intersectRect.top() - windowRect.top(),
+                               intersectPix
+            );
+        }
+    }
+    return windowImg;
+}
+
+QImage *ScreenshotUtil::CropEdges(QImage *img, CropStyle cropStyle) {
+    return new QImage(img->copy(7, 0, img->width() - 14, img->height() - 7));
+}
+
+QImage *ScreenshotUtil::CropCorners(QImage *img, CropStyle cropStyle) {
+    // opacity map for antialiased border (0-100%)
+    // 00, 00, 00, 00, 25, 56, 73
+    // 00, 00, 00, 48
+    // 00, 00, 67
+    // 00, 48
+    // 25
+    // 56
+    // 73
+    QPainter painter(img);
+
+    // 1. remove corner and add fully transparent pixels
+    painter.setBrush(Qt::NoBrush);
+    painter.setCompositionMode(QPainter::CompositionMode::CompositionMode_Clear);
+    painter.setPen(Qt::transparent);
+    // top left
+    painter.drawLine(0, 0, 6, 0);
+    painter.drawLine(0, 0, 0, 6);
+    painter.drawLine(0, 1, 3, 1);
+    painter.drawLine(0, 2, 2, 2);
+    painter.drawLine(0, 3, 1, 3);
+    // bot left
+    painter.drawLine(0, img->height() - 7, 0, img->height() - 1);
+    painter.drawLine(0, img->height() - 1, 6, img->height() - 1);
+    painter.drawLine(0, img->height() - 2, 3, img->height() - 2);
+    painter.drawLine(0, img->height() - 3, 2, img->height() - 3);
+    painter.drawLine(0, img->height() - 4, 1, img->height() - 4);
+    // top right
+    painter.drawLine(img->width() - 7, 0, img->width() - 1, 0);
+    painter.drawLine(img->width() - 1, 0, img->width() - 1, 6);
+    painter.drawLine(img->width() - 4, 1, img->width() - 1, 1);
+    painter.drawLine(img->width() - 3, 2, img->width() - 1, 2);
+    painter.drawLine(img->width() - 2, 3, img->width() - 1, 3);
+    // bot right
+    painter.drawLine(img->width() - 1, img->height() - 1, img->width() - 7, img->height() - 1);
+    painter.drawLine(img->width() - 1, img->height() - 1, img->width() - 1, img->height() - 7);
+    painter.drawLine(img->width() - 1, img->height() - 2, img->width() - 4, img->height() - 2);
+    painter.drawLine(img->width() - 1, img->height() - 3, img->width() - 3, img->height() - 3);
+    painter.drawLine(img->width() - 1, img->height() - 4, img->width() - 2, img->height() - 4);
+
+    // 2. add partially transparent pixels
+    auto line = reinterpret_cast<QRgb *>(img->scanLine(0));
+    auto baseColor = line[10];
+    auto bc25 = qRgba(qRed(baseColor), qGreen(baseColor), qBlue(baseColor), 0xFF * 0.25);
+    auto bc56 = qRgba(qRed(baseColor), qGreen(baseColor), qBlue(baseColor), 0xFF * 0.56);
+    auto bc73 = qRgba(qRed(baseColor), qGreen(baseColor), qBlue(baseColor), 0xFF * 0.73);
+    auto bc48 = qRgba(qRed(baseColor), qGreen(baseColor), qBlue(baseColor), 0xFF * 0.48);
+    auto bc67 = qRgba(qRed(baseColor), qGreen(baseColor), qBlue(baseColor), 0xFF * 0.67);
+
+    // top left
+    img->setPixel(4, 0, bc25);
+    img->setPixel(5, 0, bc56);
+    img->setPixel(6, 0, bc73);
+
+    img->setPixel(3, 1, bc48);
+    img->setPixel(2, 2, bc67);
+    img->setPixel(1, 3, bc48);
+
+    img->setPixel(0, 4, bc25);
+    img->setPixel(0, 5, bc56);
+    img->setPixel(0, 6, bc73);
+
+    // bot left
+    img->setPixel(4, img->height() - 1, bc25);
+    img->setPixel(5, img->height() - 1, bc56);
+    img->setPixel(6, img->height() - 1, bc73);
+
+    img->setPixel(3, img->height() - 2, bc48);
+    img->setPixel(2, img->height() - 3, bc67);
+    img->setPixel(1, img->height() - 4, bc48);
+
+    img->setPixel(0, img->height() - 5, bc25);
+    img->setPixel(0, img->height() - 6, bc56);
+    img->setPixel(0, img->height() - 7, bc73);
+
+    // top right
+    img->setPixel(img->width() - 5, 0, bc25);
+    img->setPixel(img->width() - 6, 0, bc56);
+    img->setPixel(img->width() - 7, 0, bc73);
+
+    img->setPixel(img->width() - 4, 1, bc48);
+    img->setPixel(img->width() - 3, 2, bc67);
+    img->setPixel(img->width() - 2, 3, bc48);
+
+    img->setPixel(img->width() - 1, 4, bc25);
+    img->setPixel(img->width() - 1, 5, bc56);
+    img->setPixel(img->width() - 1, 6, bc73);
+
+    // bot right
+    img->setPixel(img->width() - 5, img->height() - 1, bc25);
+    img->setPixel(img->width() - 6, img->height() - 1, bc56);
+    img->setPixel(img->width() - 7, img->height() - 1, bc73);
+
+    img->setPixel(img->width() - 4, img->height() - 2, bc48);
+    img->setPixel(img->width() - 3, img->height() - 3, bc67);
+    img->setPixel(img->width() - 2, img->height() - 4, bc48);
+
+    img->setPixel(img->width() - 1, img->height() - 5, bc25);
+    img->setPixel(img->width() - 1, img->height() - 6, bc56);
+    img->setPixel(img->width() - 1, img->height() - 7, bc73);
+
+    return img;
+}

+ 36 - 0
src/util/ScreenshotUtil.h

@@ -0,0 +1,36 @@
+//
+// Created by egonr on 2/13/2023.
+//
+
+#ifndef WINCAP11_SCREENSHOTUTIL_H
+#define WINCAP11_SCREENSHOTUTIL_H
+
+#include <QRect>
+#include <QImage>
+
+enum CropStyle {
+    WINDOWS_11,
+    NONE
+};
+
+class ScreenshotUtil {
+public:
+    QImage *CaptureForegroundWindow(CropStyle cropStyle = WINDOWS_11);
+
+private:
+    /*!
+     * @return First value contains the geometry of foreground window.\n\n
+     * Second value indicates maximized status of the foreground window.
+     */
+    QPair<QRect, bool> GetForegroundWindowRect(CropStyle cropStyle = WINDOWS_11);
+
+    QImage *ScreenshotRect(QRect windowRect);
+
+    QImage *CropCorners(QImage *img, CropStyle cropStyle = WINDOWS_11);
+
+    QImage *CropEdges(QImage *img, CropStyle cropStyle = WINDOWS_11);
+
+};
+
+
+#endif //WINCAP11_SCREENSHOTUTIL_H

+ 12 - 0
src/util/Version.h.in

@@ -0,0 +1,12 @@
+//
+// Created by egonr on 2/14/2023.
+//
+
+#ifndef WINCAP11_VERSION_H_IN
+#define WINCAP11_VERSION_H_IN
+
+#define wincap11_VERSION_MAJOR @wincap11_VERSION_MAJOR@
+#define wincap11_VERSION_MINOR @wincap11_VERSION_MINOR@
+#define wincap11_GIT_HASH "@GIT_HASH@"
+
+#endif //WINCAP11_VERSION_H_IN