Compare commits

..

62 commits

Author SHA1 Message Date
jklingen
c1ad1d4a93 Remove Debug Info from Release Builds
Some checks failed
Build and Deploy / build (push) Has been cancelled
Build and Deploy / deploy (push) Has been cancelled
2025-08-10 17:11:30 +02:00
jklingen
82702c4830
Chore: Update Change Log
Some checks failed
Build and Deploy / build (push) Has been cancelled
Build and Deploy / deploy (push) Has been cancelled
2025-08-09 16:43:42 +02:00
Christian Schulz
b69c415669
Fix: Define plugin name for exclusion in INI (#644)
Fixes #642 #648
2025-08-09 16:38:32 +02:00
jklingen
e81abdcd7b
Chore: Update Change Log with Latest Releases 2025-08-09 16:29:55 +02:00
jklingen
dbbfbb654e
Installer: Allow Choice between All-Users (Administrative) and Current-User Installation #625 (#641)
Some checks failed
Build and Deploy / build (push) Has been cancelled
Build and Deploy / deploy (push) Has been cancelled
Also fixes #546, #611, #598
2025-07-26 16:17:08 +02:00
jklingen
7360791872
Add Possibility to Publish Test Releases from Branches (#631)
... without causing a mess and a lot of confusion by using the same build numbers as in release branch.
2025-07-24 18:04:11 +02:00
jklingen
aa98a7ce78
Add Trademark and Logo Usage Policy 2025-07-18 11:24:21 +02:00
jklingen
dbfdfe9a05
Chore: Enhance Github Pages Rebuild Trigger with other release types
... to make sure the list on the website is always up to date, without manual interaction.
2025-07-17 17:40:11 +02:00
Thomas Braun
2121547387
added PrivilegesRequiredOverridesAllowed=commandline 2025-07-16 12:11:18 +02:00
jklingen
af04773497
Add workflow_dispatch Trigger to Build and Deploy Workflow
Some checks failed
Build and Deploy / build (push) Has been cancelled
Build and Deploy / deploy (push) Has been cancelled
2025-07-15 12:38:22 +02:00
Nathan_B
8e98cdbfb5
Add Hotkey for Resize Button in Editor (#480) 2025-05-23 15:02:44 +02:00
Jens Glathe
789a569706
fix: handle picky Win11 ToastNotificationService (#487) 2025-05-23 14:55:54 +02:00
Julien Richard
6400b08a8c
Fix scaling with fixed ratio (#514) 2025-05-23 14:43:28 +02:00
Nathan_B
1f0ae08a52
#572 Fix Error when Opening .greenshot File with Arrows 2025-05-22 18:42:02 +02:00
jklingen
95d4c1c2d1
Publish Unsigned Release on Commit and Purge CloudFlare Cache on Pages Build (#583) 2025-05-21 12:53:32 +02:00
Christian Schulz
cc063b6426
Calculate optimal font size for StepLabel #457 (#460) 2025-05-21 07:40:19 +02:00
Thomas Braun
ff260bae52 Merge branch 'release/1.3' of https://github.com/greenshot/greenshot into release/1.3 2025-05-16 15:53:24 +02:00
Thomas Braun
15d5888090 changed token 2025-05-16 15:52:31 +02:00
jklingen
c8f424b72e
Fix Reference to Token 2025-05-16 15:49:26 +02:00
Thomas Braun
262307e61e fixed branch changes 2025-05-16 15:34:51 +02:00
jklingen
cac566c70a
Add Release Script (#581) 2025-05-16 15:17:26 +02:00
jklingen
245f6f261b
Enhance Readme with Link to Releases (#549) 2024-08-18 11:22:45 +02:00
jklingen
bb7a374390
Create SECURITY.md 2023-10-03 16:11:09 +02:00
Jens Glathe
a3e65fee6f
add install option to disable the default win11 prtscr tool (#484)
Signed-off-by: Jens Glathe <jens.glathe@oldschoolsolutions.biz>
2023-04-16 19:25:14 +02:00
Robin Krom
f862e79485 Fixed SvgContainer support for the .greenshot file 2023-03-27 22:43:03 +02:00
Robin Krom
099e656963 Brought back IconContainer support to the .greenshot format, Svg is still causing issue. 2023-03-27 22:27:02 +02:00
Robin Krom
a152e2883f This should improve the backwards compatibility for the .greenshot file from earlier versions. This was also addressed in #375 2023-03-27 21:51:01 +02:00
Robin Krom
7e005f741a
Fixed #441, swapped left & top. 2022-10-05 22:29:01 +02:00
Robin Krom
4c6707b468
Fixed #432, inflate is not the best way of increasing a rectangle size. 2022-09-28 20:53:57 +02:00
Robin Krom
9634f8abbc
Updated dependencies. 2022-09-04 15:35:38 +02:00
Robin Krom
029d47f479
Added some NPE protections for BUG-2991 2022-09-03 15:11:16 +02:00
Robin Krom
511034a34b
Fixed the return value of the GetOfficeExePath, this should be null so it's clear the file hasn't been found. 2022-08-17 23:12:55 +02:00
Robin Krom
296dc9f340
Formatting 2022-08-17 23:09:44 +02:00
Robin Krom
af3c22c38c
Optimized the code to find the office executable even more. 2022-08-17 23:08:59 +02:00
Robin Krom
3e88093846
Some aftercare for #431, moved the code to find the path for the office executables to the office plugin, as this is where it's needed. Also optimized the code a bit, using modern features. 2022-08-17 23:01:24 +02:00
jdavila71
ba8ed074c8
Fix of BUG-2951, issues with finding Office installations (#431) 2022-08-17 22:01:01 +02:00
Robin Krom
2b5e45e33e
Fixed a log error, and made the ClipboardHotkey optional. 2022-06-30 23:47:38 +02:00
Robin Krom
bfa8e2444e
This should hopefully fix an overflow exception while decoding the dib format. 2022-06-30 23:34:51 +02:00
Robin Krom
48675b01f0
Modified the configuration handling, added code to remove tokens when upgrading from 1.2 as these no longer work. See #421 2022-06-30 22:57:55 +02:00
Robin Krom
36a285ebd4
Fix capturing Maximised windows. 2022-06-28 22:17:56 +02:00
Robin Krom
f50f205b70
Removed old Windows 8 logic, which caused #403 (which is a very unlucky issue number LOL)
This should actually make Greenshot a tiny bit faster and use less resources, but I am not 100% sure about the effects as some windows still seem to have the window class for Windows 8.
2022-06-28 16:28:26 +02:00
Robin Krom
4c7494dd74
Some left over fixes for the move to Dapplo.Windows, also updated dependencies. 2022-06-26 01:40:53 +02:00
Robin Krom
3f6f9863f8
Removed copying all the dependencies for every plugin, this should fix #413. 2022-06-03 11:23:27 +02:00
Christian Schulz
bd03e18ddc
Bugfix crop selection scaling #404 (#407)
Disable scaling for horizontal and vertical crop modes.
2022-05-21 22:57:20 +02:00
Robin Krom
94591e5b14
Small fixes to the VectorGraphicsContainer, to align it with the possible coming EmojiContainer. Still having issues with Scaling code, this needs to be fixed. [skip ci] 2022-04-14 11:58:52 +02:00
Robin Krom
8880578f77
Fixed an issue with the adorner size, which stayed on 0, so it wasn't visible. 2022-04-13 20:42:33 +02:00
Robin Krom
acdbdeca3c
Fix drawing (dragging) a new element on the surface, which happened after the previous larger PR. Note: the adorners are somehow not visible yet. [skip ci] 2022-04-13 15:32:36 +02:00
Robin Krom
13e2e67e7c
Refactoring to use Dapplo.Windows (#398)
* Removed a LOT of the native "Win32" invokes in favor of Dapplo.Windows which was created mainly for Greenshot.
* Changed all usages of Point, Rectangle, RectangleF, Size to the Native versions of those from Dapplo.Windows.Common.
* Small fix for the OCR text feature.
2022-04-13 09:56:00 +02:00
Christian Schulz
8aca1c8282
DrawableContainer extended to indicate if confirm/cancel is available. #397 (#399) 2022-04-13 09:53:46 +02:00
bovirus
15db5a1a3f
Update Italian language (#394) 2022-04-02 21:15:15 +02:00
Robin Krom
64c77ea8d9
Some code improvements which make some later features possible (maybe having a bezier for a line) [skip ci] 2022-03-29 22:38:28 +02:00
Robin Krom
78cbb055cb
Added better error handling for reading the bitmap [skip ci] 2022-03-29 20:35:43 +02:00
Robin Krom
9ae5598ab8
Solved the clipboard loading for the DIB file format too, this is related to #396 2022-03-29 09:05:25 +02:00
Robin Krom
83b308cb4b
Boolean logic fix to help with #396, this at least should solve the issue for .PNG, other formats are pending. [skip ci] 2022-03-28 16:00:51 +02:00
Robin Krom
d6055416c8
Enhanced the adorner interface and the drawing to make it easier to configure with different colors. [skip ci] 2022-03-28 14:53:17 +02:00
Robin Krom
2721ee06a2
Fix #392 , The DibFileFormatHandler wasn't registered with the name which is stored in DataFormats.Dib 2022-03-23 21:59:18 +01:00
Robin Krom
5bc52e4d55
Validate the FreehandSensitivity as was reported in BUG-2923. [skip ci] 2022-03-23 09:46:14 +01:00
Christian Schulz
56c26ac038
Enhanced ability to crop an image vertically and horizontally. #249 (#388)
Co-authored-by: Robin Krom <robin@getgreenshot.org>
2022-03-22 11:57:01 +01:00
Robin Krom
4a66a4dbe2
Removed some chattiness in the log. [skip ci] 2022-03-22 11:42:52 +01:00
Robin Krom
8014199bb6
Fix for #390 by limiting the length of the displayed title. 2022-03-22 11:32:30 +01:00
Robin Krom
60956771c8
Feature Improve file format support (#385)
This feature will make it easier to support different file formats later on, and also adds a "VectorGraphicsContainer" base class to better support non pixel graphics in the editor. Examples are SVG, WMF/EMF and Emoji which scale automatically to the correct size and are not resized from pixels. Multiple new formats have been added for reading, Jpeg XR for writing.
2022-02-20 13:21:32 +01:00
Christian Schulz
a32cc1888b
Fix exception when rotating when counter are used, by prevent negative fontsize (#382) 2022-02-19 01:25:07 +01:00
304 changed files with 7037 additions and 9034 deletions

View file

@ -0,0 +1,14 @@
name: Purge CloudFlare Cache
on:
page_build:
jobs:
purge_cache:
runs-on: ubuntu-latest
steps:
- name: purge
uses: jakejarvis/cloudflare-purge-action@master
env:
CLOUDFLARE_ZONE: ${{ secrets.CLOUDFLARE_ZONE }}
CLOUDFLARE_TOKEN: ${{ secrets.CLOUDFLARE_TOKEN }}

163
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,163 @@
env:
IS_RELEASE_BRANCH: ${{ startsWith(github.ref, 'refs/heads/release/') }}
BRANCH_NAME: ${{ github.ref_name }}
name: Build and Deploy
on:
workflow_dispatch:
push:
branches:
- 'release/1.*'
paths-ignore:
- '.github/**'
- '.gitignore'
- '*.md'
- 'LICENSE'
- 'build-and-deploy.ps1'
jobs:
build:
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v2
- name: Set up .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '7.x'
- name: Restore NuGet packages
run: msbuild src/Greenshot.sln /p:Configuration=Release /restore /t:PrepareForBuild
env:
Box13_ClientId: ${{ secrets.Box13_ClientId }}
Box13_ClientSecret: ${{ secrets.Box13_ClientSecret }}
DropBox13_ClientId: ${{ secrets.DropBox13_ClientId }}
DropBox13_ClientSecret: ${{ secrets.DropBox13_ClientSecret }}
Flickr_ClientId: ${{ secrets.Flickr_ClientId }}
Flickr_ClientSecret: ${{ secrets.Flickr_ClientSecret }}
Imgur13_ClientId: ${{ secrets.Imgur13_ClientId }}
Imgur13_ClientSecret: ${{ secrets.Imgur13_ClientSecret }}
Photobucket_ClientId: ${{ secrets.Photobucket_ClientId }}
Photobucket_ClientSecret: ${{ secrets.Photobucket_ClientSecret }}
Picasa_ClientId: ${{ secrets.Picasa_ClientId }}
Picasa_ClientSecret: ${{ secrets.Picasa_ClientSecret }}
- name: Build and package
run: msbuild src/Greenshot.sln /p:Configuration=Release /t:Rebuild /v:normal
env:
Box13_ClientId: ${{ secrets.Box13_ClientId }}
Box13_ClientSecret: ${{ secrets.Box13_ClientSecret }}
DropBox13_ClientId: ${{ secrets.DropBox13_ClientId }}
DropBox13_ClientSecret: ${{ secrets.DropBox13_ClientSecret }}
Flickr_ClientId: ${{ secrets.Flickr_ClientId }}
Flickr_ClientSecret: ${{ secrets.Flickr_ClientSecret }}
Imgur13_ClientId: ${{ secrets.Imgur13_ClientId }}
Imgur13_ClientSecret: ${{ secrets.Imgur13_ClientSecret }}
Photobucket_ClientId: ${{ secrets.Photobucket_ClientId }}
Photobucket_ClientSecret: ${{ secrets.Photobucket_ClientSecret }}
Picasa_ClientId: ${{ secrets.Picasa_ClientId }}
Picasa_ClientSecret: ${{ secrets.Picasa_ClientSecret }}
- name: Copy Files
run: |
mkdir -p ${{ github.workspace }}/artifacts
cp installer/Greenshot-INSTALLER-*.exe ${{ github.workspace }}/artifacts/
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: drop
path: ${{ github.workspace }}/artifacts
deploy:
runs-on: windows-latest
needs: build
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: drop # Name of the artifact uploaded in previous steps
path: drop # Local folder where artifacts are downloaded
- name: Extract version from file name
if: env.IS_RELEASE_BRANCH == 'true'
id: version_from_filename
run: |
$file = Get-ChildItem drop -Filter "Greenshot-INSTALLER-*.exe" | Select-Object -First 1
if (-not $file) {
throw "No matching file found in 'drop' directory."
}
if ($file.Name -match "Greenshot-INSTALLER-([\d\.]+)(.*)\.exe") {
echo "version=$($matches[1])" >> $Env:GITHUB_OUTPUT
} else {
throw "Version number could not be extracted from file name: $($file.Name)"
}
shell: pwsh
- name: Set version from sanitized branch name
if: env.IS_RELEASE_BRANCH != 'true'
id: version_from_branchname
run: |
$branch = "${{ github.ref }}" -replace '^refs/heads/', ''
$sanitized = $branch -replace '[^a-zA-Z0-9._-]', '_'
echo "version=$sanitized" >> $Env:GITHUB_OUTPUT
shell: pwsh
- name: Set version info
id: version_info
run: |
echo "version=${{ steps.version_from_filename.outputs.version || steps.version_from_branchname.outputs.version }}" >> $GITHUB_OUTPUT
shell: bash
- name: Create tag
if: env.IS_RELEASE_BRANCH == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "v${{ steps.version_info.outputs.version }}" -m "v${{ steps.version_info.outputs.version }}"
git push origin "v${{ steps.version_info.outputs.version }}"
- name: Rename installer for non-release branch
if: env.IS_RELEASE_BRANCH != 'true'
run: |
branch="${BRANCH_NAME:-${GITHUB_REF#refs/heads/}}"
sanitized=$(echo "$branch" | sed 's/[^a-zA-Z0-9._-]/_/g')
mv drop/Greenshot-INSTALLER-*.exe "drop/Greenshot-INSTALLER-${sanitized}.exe"
shell: bash
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
name: "Greenshot ${{ steps.version_info.outputs.version }} (continuous build)"
tag_name: ${{ env.IS_RELEASE_BRANCH == 'true' && format('v{0}', steps.version_info.outputs.version) || env.BRANCH_NAME }}
files: drop/*.exe
generate_release_notes: true
draft: ${{ env.IS_RELEASE_BRANCH != 'true' }}
prerelease: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Trigger GitHub Pages rebuild
shell: bash
run: |
curl -X POST \
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/${{ github.repository }}/pages/builds

18
.github/workflows/update-gh-pages.yml vendored Normal file
View file

@ -0,0 +1,18 @@
name: Update GitHub Pages
on:
workflow_dispatch:
release:
types: [published, unpublished, created, edited, deleted, prereleased, released]
jobs:
update-gh-pages:
runs-on: ubuntu-latest
steps:
- name: Trigger GitHub Pages rebuild
shell: bash
run: |
curl -X POST \
-H "Authorization: Bearer ${{ secrets.GH_PAGES_TOKEN }}" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/${{ github.repository }}/pages/builds

4
.gitignore vendored
View file

@ -214,4 +214,6 @@ ModelManifest.xml
*.credentials.cs *.credentials.cs
# Rider files # Rider files
.idea .idea
/installer/Greenshot-INSTALLER-*.exe

View file

@ -21,4 +21,25 @@ Being easy to understand and configurable, Greenshot is an efficient tool for pr
About this repository About this repository
--------------------- ---------------------
This repository is for Greenshot 1.3, currently in development, but is the next planned release This is the development branch is for Greenshot 1.3, which has been first released on 2025-07-14.
Releases
--------
You can find a list of all releases (stable and unstable) in the [Github releases](https://github.com/greenshot/greenshot/releases) or in the [version history on our website](https://getgreenshot.org/version-history/).
The [downloads page on our website](https://getgreenshot.org/downloads/) always links to the latest stable release.
Trademark and Logo Usage Policy
-------------------------------
The Greenshot logo and trademark are the property of the Greenshot development team. Unauthorized use of the logo and trademark is generally prohibited. However, we allow the use of the Greenshot name and logo in the following contexts:
* In blog posts, articles, or reviews that discuss or promote the Greenshot, provided that the usage is fair and does not imply endorsement by Greenshot.
* In educational materials or presentations that accurately represent the project.
Please refrain from using the Greenshot logo and trademark in any promotional materials, products, or in a manner that may cause confusion or imply endorsement without prior written permission.
If you have any questions or wish to seek permission for other uses, please contact us.
Thank you for your understanding and cooperation.

5
SECURITY.md Normal file
View file

@ -0,0 +1,5 @@
# Security Policy
## Reporting a Vulnerability
If you think you found a security issue in Greenshot, please report it responsibly [in our security section](https://github.com/greenshot/greenshot/security). We try to look into it as soon as possible - please give us some time for reaction, though.

View file

@ -1,106 +0,0 @@
# .NET Desktop
# Build and run tests for .NET Desktop or Windows classic desktop solutions.
# Add steps that publish symbols, save build artifacts, and more:
# https://docs.microsoft.com/azure/devops/pipelines/apps/windows/dot-net
trigger:
batch: true
branches:
include:
- 'release/1.*'
exclude:
- 'develop'
stages:
- stage: Build
jobs:
- job: Build
variables:
- group: 'Plug-in Credentials'
- name: solution
value: 'src/Greenshot.sln'
- name: buildPlatform
value: 'Any CPU'
- name: buildConfiguration
value: 'Release'
pool:
vmImage: 'Windows-latest'
steps:
- task: MSBuild@1
displayName: Restore nuget packages and generate credential templates
inputs:
solution: '$(solution)'
platform: $(buildPlatform)
configuration: $(buildConfiguration)
msbuildArguments: '/restore /t:PrepareForBuild'
- task: MSBuild@1
displayName: Build and package
inputs:
solution: '$(solution)'
platform: $(buildPlatform)
configuration: $(buildConfiguration)
env:
Box13_ClientId: $(Box13_ClientId)
Box13_ClientSecret: $(Box13_ClientSecret)
DropBox13_ClientId: $(DropBox13_ClientId)
DropBox13_ClientSecret: $(DropBox13_ClientSecret)
Flickr_ClientId: $(Flickr_ClientId)
Flickr_ClientSecret: $(Flickr_ClientSecret)
Imgur13_ClientId: $(Imgur13_ClientId)
Imgur13_ClientSecret: $(Imgur13_ClientSecret)
Photobucket_ClientId: $(Photobucket_ClientId)
Photobucket_ClientSecret: $(Photobucket_ClientSecret)
Picasa_ClientId: $(Picasa_ClientId)
Picasa_ClientSecret: $(Picasa_ClientSecret)
- task: CopyFiles@2
displayName: 'Copy Files to: $(build.artifactstagingdirectory)'
inputs:
SourceFolder: '$(Build.SourcesDirectory)\installer'
Contents: Greenshot-INSTALLER-*.exe
TargetFolder: '$(build.artifactstagingdirectory)'
flattenFolders: true
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact: drop'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'
- stage: Deploy
jobs:
- deployment: GitHub_Release
pool:
vmImage: 'Windows-latest'
environment: 'GitHub Release'
strategy:
# default deployment strategy
runOnce:
deploy:
steps:
- download: current
artifact: drop
# Create a GitHub release
- task: GitHubRelease@0
inputs:
gitHubConnection: GitHub Release
repositoryName: '$(Build.Repository.Name)'
action: 'create' # Options: create, edit, delete
target: '$(Build.SourceVersion)' # Required when action == Create || Action == Edit
tagSource: 'manual' # Required when action == Create# Options: auto, manual
tag: 'v$(Build.BuildNumber)' # Required when action == Edit || Action == Delete || TagSource == Manual
title: Greenshot $(Build.BuildNumber) unstable # Optional
#releaseNotesSource: 'file' # Optional. Options: file, input
#releaseNotesFile: # Optional
#releaseNotes: # Optional
assets: '$(Pipeline.Workspace)/drop/*.exe'
#assetUploadMode: 'delete' # Optional. Options: delete, replace
isDraft: true # Optional
isPreRelease: true # Optional
addChangeLog: true # Optional
#compareWith: 'lastFullRelease' # Required when addChangeLog == True. Options: lastFullRelease, lastRelease, lastReleaseByTag
#releaseTag: # Required when compareWith == LastReleaseByTag

149
build-and-deploy.ps1 Normal file
View file

@ -0,0 +1,149 @@
# USAGE
# * Enable script execution in Powershell: 'Set-ExecutionPolicy RemoteSigned'
# * Create a GitHub personal access token (PAT) for greenshot repository
# * user must be owner of the repository
# * token needs read and write permissions ""for Contents"" and ""Pages""
# * Execute the script and paste your token
# Prompt the user to securely input the Github token
$SecureToken = Read-Host "Please enter your GitHub personal access token" -AsSecureString
$ReleaseToken = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureToken))
# Variables
$RepoPath = "." # Replace with your local repo path
$ArtifactsPath = "$RepoPath\artifacts"
$SolutionFile = "$RepoPath\src\Greenshot.sln"
# Step 0: Update Local Repository
git pull
# Step 1: Restore NuGet Packages
Write-Host "Restoring NuGet packages..."
msbuild "$SolutionFile" /p:Configuration=Release /restore /t:PrepareForBuild
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to restore NuGet packages."
exit $LASTEXITCODE
}
# Step 2: Build and Package
Write-Host "Building and packaging the solution..."
msbuild "$SolutionFile" /p:Configuration=Release /t:Rebuild /v:normal
if ($LASTEXITCODE -ne 0) {
Write-Error "Build failed."
exit $LASTEXITCODE
}
# Step 3: Copy Installer Files
Write-Host "Copying installer files..."
if (-not (Test-Path $ArtifactsPath)) {
New-Item -ItemType Directory -Force -Path $ArtifactsPath
}
Copy-Item "$RepoPath\installer\Greenshot-INSTALLER-*.exe" -Destination $ArtifactsPath -Force
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to copy installer files."
exit $LASTEXITCODE
}
# Step 4: Extract Version from File Name
Write-Host "Extracting version from installer file name..."
$InstallerFile = Get-ChildItem $ArtifactsPath -Filter "Greenshot-INSTALLER-*.exe" | Select-Object -Last 1
if (-not $InstallerFile) {
Write-Error "No matching installer file found in '$ArtifactsPath'."
exit 1
}
if ($InstallerFile.Name -match "Greenshot-INSTALLER-([\d\.]+).*\.exe") {
$Version = $matches[1]
Write-Host "Extracted version: $Version"
} else {
Write-Error "Version number could not be extracted from file name: $($InstallerFile.Name)"
exit 1
}
# Step 5: Create Git Tag
Write-Host "Creating Git tag..."
cd $RepoPath
#git config user.name "local-script"
#git config user.email "local-script@example.com"
git tag -a "v$Version" -m "v$Version"
git push origin "v$Version"
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to create Git tag."
exit $LASTEXITCODE
}
# Step 6: Create GitHub Release
Write-Host "Creating GitHub release..."
$Headers = @{
Authorization = "Bearer $ReleaseToken"
Accept = "application/vnd.github+json"
}
$ReleaseData = @{
tag_name = "v$Version"
name = "Greenshot $Version unstable"
body = "Pre-release of Greenshot $Version."
draft = $true
prerelease = $true
generate_release_notes = $true
}
$ReleaseResponse = Invoke-RestMethod `
-Uri "https://api.github.com/repos/greenshot/greenshot/releases" `
-Method POST `
-Headers $Headers `
-Body (ConvertTo-Json $ReleaseData -Depth 10)
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to create GitHub release."
exit $LASTEXITCODE
}
Write-Host "Release created successfully."
# Get the release ID from the response
$ReleaseId = $ReleaseResponse.id
Write-Host "Release ID: $ReleaseId"
# Step 7: Upload .exe File to Release
Write-Host "Uploading .exe file to GitHub release..."
$ExeFilePath = "$ArtifactsPath\$($InstallerFile.Name)"
if (-Not (Test-Path $ExeFilePath)) {
Write-Error "Built .exe file not found: $ExeFilePath"
exit 1
}
# GitHub API for uploading release assets
$UploadUrl = $ReleaseResponse.upload_url -replace "{.*}", ""
# Upload the file
$FileHeaders = @{
Authorization = "Bearer $ReleaseToken"
ContentType = "application/octet-stream"
}
$FileName = [System.IO.Path]::GetFileName($ExeFilePath)
Invoke-RestMethod `
-Uri "$($UploadUrl)?name=$FileName" `
-Method POST `
-Headers $FileHeaders `
-InFile $ExeFilePath `
-ContentType "application/octet-stream"
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to upload .exe file to release."
exit $LASTEXITCODE
}
Write-Host "File uploaded successfully: $FileName"
# Step 7: Trigger GitHub Pages Rebuild
#Write-Host "Triggering GitHub Pages rebuild..."
#Invoke-RestMethod `
# -Uri "https://api.github.com/repos/greenshot/greenshot/pages/builds" `
# -Method POST `
# -Headers $Headers
#if ($LASTEXITCODE -ne 0) {
# Write-Error "Failed to trigger GitHub Pages rebuild."
# exit $LASTEXITCODE
#}
#
#Write-Host "GitHub Pages rebuild triggered successfully."

View file

@ -7,7 +7,29 @@ CHANGE LOG:
All details to our tickets can be found here: https://greenshot.atlassian.net All details to our tickets can be found here: https://greenshot.atlassian.net
# Release notes for Greenshot 1.3 # Greenshot 1.3.xxx
Bugs fixed:
* greenshot.ini: Exclude Plugins and Include Plugins setting broken [#648](https://github.com/greenshot/greenshot/issues/648) [#642](https://github.com/greenshot/greenshot/issues/642) thanks to @Christian-Schulz for providing the fix
Features added:
# Greenshot 1.3.296
Bugs fixed
* Fix Administrative installation via user interface [#546](https://github.com/greenshot/greenshot/issues/546) [#611](https://github.com/greenshot/greenshot/issues/611) [#598](https://github.com/greenshot/greenshot/issues/598)
Features added:
* Installer: Allow Choice between All-Users (Administrative) and Current-User Installation [#625](https://github.com/greenshot/greenshot/pull/625)
# Greenshot 1.3.292
Bugs fixed:
* Fix Administrative installation via command line using /ALLUSERS [#601](https://github.com/greenshot/greenshot/issues/601) [#619](https://github.com/greenshot/greenshot/issues/619)
# Greenshot 1.3.290
Note: the version information for the first 1.3 release is outdated/incomplete. Due to the long timespan and large amount of changes between 1.2 and 1.3 we lost track. Sorry.
Greenshot 1.3 is the first Greenshot which targets .NET 4.7.2 which just by doing to solves some general issues in the area of Internet Explorer capturing, TLS communication and some other minor issues. Greenshot 1.3 is the first Greenshot which targets .NET 4.7.2 which just by doing to solves some general issues in the area of Internet Explorer capturing, TLS communication and some other minor issues.
@ -646,3 +668,5 @@ Features added:
* when clicking two overlapping elements, the one created later gets selected [ 1725175 ] * when clicking two overlapping elements, the one created later gets selected [ 1725175 ]
* created textboxes can now be edited with a doubleclick [ 1704408 ] * created textboxes can now be edited with a doubleclick [ 1704408 ]
* selected font is now stored in the application config file [ 1704411 ] * selected font is now stored in the application config file [ 1704411 ]

View file

@ -7,6 +7,7 @@
#define BinDir "bin\Release\net472" #define BinDir "bin\Release\net472"
#define ReleaseDir "..\..\src\Greenshot\bin\Release\net472" #define ReleaseDir "..\..\src\Greenshot\bin\Release\net472"
#define PluginDir "..\..\src\Greenshot\bin\Release\net472\Plugins" #define PluginDir "..\..\src\Greenshot\bin\Release\net472\Plugins"
#define CertumThumbprint GetEnv('CertumThumbprint')
; Include the scripts to install .NET Framework ; Include the scripts to install .NET Framework
; See https://www.codeproject.com/KB/install/dotnetfx_innosetup_instal.aspx ; See https://www.codeproject.com/KB/install/dotnetfx_innosetup_instal.aspx
@ -82,33 +83,34 @@ Source: {#LanguagesDir}\*zh-CN*; Excludes: "*installer*,*website*"; DestDir: {ap
Source: {#LanguagesDir}\*zh-TW*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\zhTW; Flags: overwritereadonly ignoreversion replacesameversion; Source: {#LanguagesDir}\*zh-TW*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\zhTW; Flags: overwritereadonly ignoreversion replacesameversion;
;Office Plugin ;Office Plugin
Source: {#PluginDir}\Greenshot.Plugin.Office\*.dll; DestDir: {app}\Plugins\Office; Components: plugins\office; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; Source: {#PluginDir}\Greenshot.Plugin.Office\Greenshot.Plugin.Office.dll; DestDir: {app}\Plugins\Office; Components: plugins\office; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion;
;JIRA Plugin ;JIRA Plugin
Source: {#PluginDir}\Greenshot.Plugin.Jira\*.dll; DestDir: {app}\Plugins\Jira; Components: plugins\jira; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; Source: {#PluginDir}\Greenshot.Plugin.Jira\*Jira*.dll; DestDir: {app}\Plugins\Jira; Components: plugins\jira; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion;
Source: {#BaseDir}\Greenshot.Plugin.Jira\Languages\language_jira*.xml; DestDir: {app}\Languages\Plugins\Jira; Components: plugins\jira; Flags: overwritereadonly ignoreversion replacesameversion; Source: {#BaseDir}\Greenshot.Plugin.Jira\Languages\language_jira*.xml; DestDir: {app}\Languages\Plugins\Jira; Components: plugins\jira; Flags: overwritereadonly ignoreversion replacesameversion;
;Imgur Plugin ;Imgur Plugin
Source: {#PluginDir}\Greenshot.Plugin.Imgur\*.dll; DestDir: {app}\Plugins\Imgur; Components: plugins\imgur; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; Source: {#PluginDir}\Greenshot.Plugin.Imgur\Greenshot.Plugin.Imgur.dll; DestDir: {app}\Plugins\Imgur; Components: plugins\imgur; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion;
Source: {#BaseDir}\Greenshot.Plugin.Imgur\Languages\language_imgur*.xml; DestDir: {app}\Languages\Plugins\Imgur; Components: plugins\imgur; Flags: overwritereadonly ignoreversion replacesameversion; Source: {#BaseDir}\Greenshot.Plugin.Imgur\Languages\language_imgur*.xml; DestDir: {app}\Languages\Plugins\Imgur; Components: plugins\imgur; Flags: overwritereadonly ignoreversion replacesameversion;
;Box Plugin ;Box Plugin
Source: {#PluginDir}\Greenshot.Plugin.Box\*.dll; DestDir: {app}\Plugins\Box; Components: plugins\box; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; Source: {#PluginDir}\Greenshot.Plugin.Box\Greenshot.Plugin.Box.dll; DestDir: {app}\Plugins\Box; Components: plugins\box; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion;
Source: {#BaseDir}\Greenshot.Plugin.Box\Languages\language_box*.xml; DestDir: {app}\Languages\Plugins\Box; Components: plugins\box; Flags: overwritereadonly ignoreversion replacesameversion; Source: {#BaseDir}\Greenshot.Plugin.Box\Languages\language_box*.xml; DestDir: {app}\Languages\Plugins\Box; Components: plugins\box; Flags: overwritereadonly ignoreversion replacesameversion;
;DropBox Plugin ;DropBox Plugin
Source: {#PluginDir}\Greenshot.Plugin.DropBox\*.dll; DestDir: {app}\Plugins\DropBox; Components: plugins\dropbox; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; Source: {#PluginDir}\Greenshot.Plugin.DropBox\Greenshot.Plugin.DropBox.dll; DestDir: {app}\Plugins\DropBox; Components: plugins\dropbox; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion;
Source: {#BaseDir}\Greenshot.Plugin.DropBox\Languages\language_dropbox*.xml; DestDir: {app}\Languages\Plugins\DropBox; Components: plugins\dropbox; Flags: overwritereadonly ignoreversion replacesameversion; Source: {#BaseDir}\Greenshot.Plugin.DropBox\Languages\language_dropbox*.xml; DestDir: {app}\Languages\Plugins\DropBox; Components: plugins\dropbox; Flags: overwritereadonly ignoreversion replacesameversion;
;Flickr Plugin ;Flickr Plugin
Source: {#PluginDir}\Greenshot.Plugin.Flickr\*.dll; DestDir: {app}\Plugins\Flickr; Components: plugins\flickr; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; Source: {#PluginDir}\Greenshot.Plugin.Flickr\Greenshot.Plugin.Flickr.dll; DestDir: {app}\Plugins\Flickr; Components: plugins\flickr; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion;
Source: {#BaseDir}\Greenshot.Plugin.Flickr\Languages\language_flickr*.xml; DestDir: {app}\Languages\Plugins\Flickr; Components: plugins\flickr; Flags: overwritereadonly ignoreversion replacesameversion; Source: {#BaseDir}\Greenshot.Plugin.Flickr\Languages\language_flickr*.xml; DestDir: {app}\Languages\Plugins\Flickr; Components: plugins\flickr; Flags: overwritereadonly ignoreversion replacesameversion;
;Photobucket Plugin ;Photobucket Plugin
Source: {#PluginDir}\Greenshot.Plugin.Photobucket\*.dll; DestDir: {app}\Plugins\Photobucket; Components: plugins\photobucket; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; Source: {#PluginDir}\Greenshot.Plugin.Photobucket\Greenshot.Plugin.Photobucket.dll; DestDir: {app}\Plugins\Photobucket; Components: plugins\photobucket; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion;
Source: {#BaseDir}\Greenshot.Plugin.Photobucket\Languages\language_photo*.xml; DestDir: {app}\Languages\Plugins\Photobucket; Components: plugins\photobucket; Flags: overwritereadonly ignoreversion replacesameversion; Source: {#BaseDir}\Greenshot.Plugin.Photobucket\Languages\language_photo*.xml; DestDir: {app}\Languages\Plugins\Photobucket; Components: plugins\photobucket; Flags: overwritereadonly ignoreversion replacesameversion;
;Confluence Plugin ;Confluence Plugin
Source: {#PluginDir}\Greenshot.Plugin.Confluence\*.dll; DestDir: {app}\Plugins\Confluence; Components: plugins\confluence; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; Source: {#PluginDir}\Greenshot.Plugin.Confluence\Greenshot.Plugin.Confluence.dll; DestDir: {app}\Plugins\Confluence; Components: plugins\confluence; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion;
Source: {#BaseDir}\Greenshot.Plugin.Confluence\Languages\language_confluence*.xml; DestDir: {app}\Languages\Plugins\Confluence; Components: plugins\confluence; Flags: overwritereadonly ignoreversion replacesameversion; Source: {#BaseDir}\Greenshot.Plugin.Confluence\Languages\language_confluence*.xml; DestDir: {app}\Languages\Plugins\Confluence; Components: plugins\confluence; Flags: overwritereadonly ignoreversion replacesameversion;
;ExternalCommand Plugin ;ExternalCommand Plugin
Source: {#PluginDir}\Greenshot.Plugin.ExternalCommand\*.dll; DestDir: {app}\Plugins\ExternalCommand; Components: plugins\externalcommand; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; Source: {#PluginDir}\Greenshot.Plugin.ExternalCommand\Greenshot.Plugin.ExternalCommand.dll; DestDir: {app}\Plugins\ExternalCommand; Components: plugins\externalcommand; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion;
Source: {#BaseDir}\Greenshot.Plugin.ExternalCommand\Languages\language_externalcommand*.xml; DestDir: {app}\Languages\Plugins\ExternalCommand; Components: plugins\externalcommand; Flags: overwritereadonly ignoreversion replacesameversion; Source: {#BaseDir}\Greenshot.Plugin.ExternalCommand\Languages\language_externalcommand*.xml; DestDir: {app}\Languages\Plugins\ExternalCommand; Components: plugins\externalcommand; Flags: overwritereadonly ignoreversion replacesameversion;
;Win 10 Plugin ;Win 10 Plugin
Source: {#PluginDir}\Greenshot.Plugin.Win10\*.dll; DestDir: {app}\Plugins\Win10; Components: plugins\win10; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; Source: {#PluginDir}\Greenshot.Plugin.Win10\Greenshot.Plugin.Win10.dll; DestDir: {app}\Plugins\Win10; Components: plugins\win10; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion;
Source: {#PluginDir}\Greenshot.Plugin.Win10\Microsoft.Toolkit.Uwp.Notifications.dll; DestDir: {app}\Plugins\Win10; Components: plugins\win10; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion;
[Setup] [Setup]
; changes associations is used when the installer installs new extensions, it clears the explorer icon cache ; changes associations is used when the installer installs new extensions, it clears the explorer icon cache
@ -125,21 +127,27 @@ AppVersion={#Version}
ArchitecturesInstallIn64BitMode=x64 ArchitecturesInstallIn64BitMode=x64
Compression=lzma2/ultra64 Compression=lzma2/ultra64
SolidCompression=yes SolidCompression=yes
DefaultDirName={code:DefDirRoot}\{#ExeName} DefaultDirName={autopf}\{#ExeName}
DefaultGroupName={#ExeName} DefaultGroupName={#ExeName}
InfoBeforeFile=..\additional_files\readme.txt InfoBeforeFile=..\additional_files\readme.txt
LicenseFile=..\additional_files\license.txt LicenseFile=..\additional_files\license.txt
LanguageDetectionMethod=uilanguage LanguageDetectionMethod=uilanguage
MinVersion=6.1sp1 MinVersion=6.1sp1
OutputBaseFilename={#ExeName}-INSTALLER-{#Version}-UNSTABLE
OutputDir=..\ OutputDir=..\
; user may choose between all-users vs. current-user installation in a dialog or by using the /ALLUSERS flag (on the command line)
; in registry section, HKA will take care of the appropriate root key (HKLM vs. HKCU), see https://jrsoftware.org/ishelp/index.php?topic=admininstallmode
PrivilegesRequiredOverridesAllowed=dialog
; admin privileges not required, unless user chooses all-users installation
; the installer will ask for elevation if needed
PrivilegesRequired=lowest PrivilegesRequired=lowest
SetupIconFile=..\..\src\Greenshot\icons\applicationIcon\icon.ico SetupIconFile=..\..\src\Greenshot\icons\applicationIcon\icon.ico
; Create a SHA1 signature #if CertumThumbprint != ""
; SignTool=SignTool sign /debug /fd sha1 /tr https://time.certum.pl /td sha1 $f OutputBaseFilename={#ExeName}-INSTALLER-{#Version}-UNSTABLE
; Append a SHA256 to the previous SHA1 signature (this is what as does) SignTool=SignTool sign /sha1 "{#CertumThumbprint}" /tr http://time.certum.pl /td sha256 /fd sha256 /v $f
; SignTool=SignTool sign /debug /as /fd sha256 /tr https://time.certum.pl /td sha256 $f SignedUninstaller=yes
; SignedUninstaller=yes #else
OutputBaseFilename={#ExeName}-INSTALLER-{#Version}-UNSTABLE-UNSIGNED
#endif
UninstallDisplayIcon={app}\{#ExeName}.exe UninstallDisplayIcon={app}\{#ExeName}.exe
Uninstallable=true Uninstallable=true
VersionInfoCompany={#ExeName} VersionInfoCompany={#ExeName}
@ -151,6 +159,7 @@ VersionInfoVersion={#Version}
WizardImageFile=installer-large.bmp WizardImageFile=installer-large.bmp
; Reference a bitmap, max size 55x58 ; Reference a bitmap, max size 55x58
WizardSmallImageFile=installer-small.bmp WizardSmallImageFile=installer-small.bmp
[Registry] [Registry]
; Delete all startup entries, so we don't have leftover values ; Delete all startup entries, so we don't have leftover values
Root: HKCU; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; Root: HKCU; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror;
@ -169,22 +178,16 @@ Root: HKLM; Subkey: Software\Classes\.greenshot; ValueType: none; ValueName: {#E
Root: HKLM; Subkey: Software\Classes\Greenshot; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; Root: HKLM; Subkey: Software\Classes\Greenshot; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror;
; Create the startup entries if requested to do so ; Create the startup entries if requested to do so
; HKEY_LOCAL_USER - for current user only Root: HKA; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: string; ValueName: {#ExeName}; ValueData: """{app}\{#ExeName}.exe"""; Flags: uninsdeletevalue noerror; Tasks: startup
Root: HKCU; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: string; ValueName: {#ExeName}; ValueData: """{app}\{#ExeName}.exe"""; Permissions: users-modify; Flags: uninsdeletevalue noerror; Tasks: startup; Check: IsRegularUser
; HKEY_LOCAL_MACHINE - for all users when admin
Root: HKLM; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: string; ValueName: {#ExeName}; ValueData: """{app}\{#ExeName}.exe"""; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Tasks: startup; Check: not IsRegularUser
; Register our own filetype for all users ; Register our own filetype for all users
; HKEY_LOCAL_USER - for current user only Root: HKA; Subkey: Software\Classes\.greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot"; Flags: uninsdeletevalue noerror
Root: HKCU; Subkey: Software\Classes\.greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot"; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser Root: HKA; Subkey: Software\Classes\Greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot File"; Flags: uninsdeletevalue noerror
Root: HKCU; Subkey: Software\Classes\Greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot File"; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser Root: HKA; Subkey: Software\Classes\Greenshot\DefaultIcon; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE,0"""; Flags: uninsdeletevalue noerror
Root: HKCU; Subkey: Software\Classes\Greenshot\DefaultIcon; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE,0"""; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser Root: HKA; Subkey: Software\Classes\Greenshot\shell\open\command; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE"" --openfile ""%1"""; Flags: uninsdeletevalue noerror
Root: HKCU; Subkey: Software\Classes\Greenshot\shell\open\command; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE"" --openfile ""%1"""; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser
; HKEY_LOCAL_MACHINE - for all users when admin ; Disable the default PRTSCR Snipping Tool in Windows 11
Root: HKLM; Subkey: Software\Classes\.greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot"; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser Root: HKCU; Subkey: Control Panel\Keyboard; ValueType: dword; ValueName: "PrintScreenKeyForSnippingEnabled"; ValueData: "0"; Flags: uninsdeletevalue; Check: ShouldDisableSnippingTool
Root: HKLM; Subkey: Software\Classes\Greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot File"; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser
Root: HKLM; Subkey: Software\Classes\Greenshot\DefaultIcon; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE,0"""; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser
Root: HKLM; Subkey: Software\Classes\Greenshot\shell\open\command; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE"" --openfile ""%1"""; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser
[Icons] [Icons]
Name: {group}\{#ExeName}; Filename: {app}\{#ExeName}.exe; WorkingDir: {app}; AppUserModelID: "{#ExeName}" Name: {group}\{#ExeName}; Filename: {app}\{#ExeName}.exe; WorkingDir: {app}; AppUserModelID: "{#ExeName}"
@ -268,6 +271,7 @@ en.win10=Windows 10 plug-in
en.UninstallIconDescription=Uninstall en.UninstallIconDescription=Uninstall
en.ShowLicense=Show license en.ShowLicense=Show license
en.ShowReadme=Show Readme en.ShowReadme=Show Readme
en.disablewin11snippingtool=Disable Win11 default PrtScr snipping tool
de.confluence=Confluence Plug-in de.confluence=Confluence Plug-in
de.default=Standard installation de.default=Standard installation
@ -280,6 +284,7 @@ de.optimize=Optimierung der Leistung, kann etwas dauern.
de.startgreenshot={#ExeName} starten de.startgreenshot={#ExeName} starten
de.startup={#ExeName} starten wenn Windows hochfährt de.startup={#ExeName} starten wenn Windows hochfährt
de.win10=Windows 10 Plug-in de.win10=Windows 10 Plug-in
de.disablewin11snippingtool=Deaktiviere das Standard Windows 11 Snipping Tool auf "Druck"
es.confluence=Extensión para Confluence es.confluence=Extensión para Confluence
es.default=${default} es.default=${default}
@ -481,6 +486,7 @@ Name: "compact"; Description: "{code:CompactInstall}"
Name: "custom"; Description: "{code:CustomInstall}"; Flags: iscustom Name: "custom"; Description: "{code:CustomInstall}"; Flags: iscustom
[Components] [Components]
Name: "disablesnippingtool"; Description: {cm:disablewin11snippingtool}; Flags: disablenouninstallwarning; Types: default full custom; Check: IsWindows11OrNewer()
Name: "greenshot"; Description: "Greenshot"; Types: default full compact custom; Flags: fixed Name: "greenshot"; Description: "Greenshot"; Types: default full compact custom; Flags: fixed
;Name: "plugins\networkimport"; Description: "Network Import Plugin"; Types: full ;Name: "plugins\networkimport"; Description: "Network Import Plugin"; Types: full
Name: "plugins\box"; Description: {cm:box}; Types: full custom; Flags: disablenouninstallwarning Name: "plugins\box"; Description: {cm:box}; Types: full custom; Flags: disablenouninstallwarning
@ -530,23 +536,8 @@ Name: "languages\ukUA"; Description: {cm:ukUA}; Types: full custom; Flags: disab
Name: "languages\viVN"; Description: {cm:viVN}; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('e') Name: "languages\viVN"; Description: {cm:viVN}; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('e')
Name: "languages\zhCN"; Description: {cm:zhCN}; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('a') Name: "languages\zhCN"; Description: {cm:zhCN}; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('a')
Name: "languages\zhTW"; Description: {cm:zhTW}; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('9') Name: "languages\zhTW"; Description: {cm:zhTW}; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('9')
[Code] [Code]
// Do we have a regular user trying to install this?
function IsRegularUser(): Boolean;
begin
Result := not (IsAdmin or IsAdminInstallMode);
end;
// The following code is used to select the installation path, this is localappdata if non poweruser
function DefDirRoot(Param: String): String;
begin
if IsRegularUser then
Result := ExpandConstant('{localappdata}')
else
Result := ExpandConstant('{pf}')
end;
function FullInstall(Param : String) : String; function FullInstall(Param : String) : String;
begin begin
result := SetupMessage(msgFullInstallation); result := SetupMessage(msgFullInstallation);
@ -744,6 +735,19 @@ begin
Result := IsWindowsVersionOrNewer(10, 0); Result := IsWindowsVersionOrNewer(10, 0);
end; end;
function IsWindows11OrNewer: Boolean;
var
WindowsVersion: TWindowsVersion;
begin
GetWindowsVersionEx(WindowsVersion);
Result := (WindowsVersion.Major >= 10) and (WindowsVersion.Build >= 22000);
end;
function ShouldDisableSnippingTool: Boolean;
begin
Result := IsComponentSelected('disablesnippingtool');
end;
[Run] [Run]
Filename: "{app}\{#ExeName}.exe"; Description: "{cm:startgreenshot}"; Parameters: "{code:GetParamsForGS}"; WorkingDir: "{app}"; Flags: nowait postinstall runasoriginaluser Filename: "{app}\{#ExeName}.exe"; Description: "{cm:startgreenshot}"; Parameters: "{code:GetParamsForGS}"; WorkingDir: "{app}"; Flags: nowait postinstall runasoriginaluser
Filename: "https://getgreenshot.org/thank-you/?language={language}&version={#Version}"; Flags: shellexec runasoriginaluser Filename: "https://getgreenshot.org/thank-you/?language={language}&version={#Version}"; Flags: shellexec runasoriginaluser

6
nuget.config Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>

View file

@ -1,13 +1,13 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Copyright>Copyright © Greenshot 2004-2021</Copyright> <Copyright>Copyright © Greenshot 2004-2022</Copyright>
<Authors>Greenshot</Authors> <Authors>Greenshot</Authors>
<PackageIconUrl>https://getgreenshot.org/favicon.ico</PackageIconUrl> <PackageIconUrl>https://getgreenshot.org/favicon.ico</PackageIconUrl>
<RepositoryUrl>https://github.com/greenshot/greenshot</RepositoryUrl> <RepositoryUrl>https://github.com/greenshot/greenshot</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PackageProjectUrl>https://github.com/greenshot/greenshot</PackageProjectUrl> <PackageProjectUrl>https://github.com/greenshot/greenshot</PackageProjectUrl>
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression> <PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
<LangVersion>9</LangVersion> <LangVersion>latest</LangVersion>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<RuntimeIdentifiers>win10-x64;win10-x86;win-x64;win-x86</RuntimeIdentifiers> <RuntimeIdentifiers>win10-x64;win10-x86;win-x64;win-x86</RuntimeIdentifiers>
@ -46,7 +46,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="!$(MSBuildProjectName.Contains('Tests')) And $(MSBuildProjectName.StartsWith('Greenshot'))"> <ItemGroup Condition="!$(MSBuildProjectName.Contains('Tests')) And $(MSBuildProjectName.StartsWith('Greenshot'))">
<PackageReference Include="Nerdbank.GitVersioning" Version="3.4.255"> <PackageReference Include="Nerdbank.GitVersioning" Version="3.5.113">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference> </PackageReference>

View file

@ -1,13 +1,65 @@
<Project> <Project>
<UsingTask TaskName="ApplyTokenReplacements" TaskFactory="CodeTaskFactory" AssemblyName="Microsoft.Build.Tasks.Core">
<ParameterGroup>
<InputLines ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
<Tokens ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
<OutputLines ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" />
</ParameterGroup>
<Task>
<Reference Include="System.Text.RegularExpressions" />
<Code Type="Fragment" Language="cs">
<![CDATA[
var output = new List<ITaskItem>();
foreach (var line in InputLines)
{
string text = line.ItemSpec;
foreach (var token in Tokens)
{
string tokenName = token.ItemSpec;
// Skip if tokenName is null or empty
if (string.IsNullOrEmpty(tokenName))
continue;
string replacementValue = token.GetMetadata("ReplacementValue");
if (!string.IsNullOrEmpty(replacementValue))
{
string placeholder = "$"+ "{"+tokenName+"}"; // Token-Format wie $(Box13_ClientId)
text = text.Replace(placeholder, replacementValue);
}
}
output.Add(new Microsoft.Build.Utilities.TaskItem(text));
}
OutputLines = output.ToArray();
]]>
</Code>
</Task>
</UsingTask>
<PropertyGroup Condition="Exists('$(ProjectDir)$(ProjectName).Credentials.template')"> <PropertyGroup Condition="Exists('$(ProjectDir)$(ProjectName).Credentials.template')">
<MSBuildCommunityTasksPath>$(PkgMSBuildTasks)\tools\</MSBuildCommunityTasksPath> <MSBuildCommunityTasksPath>$(NuGetPackageRoot)msbuildtasks/1.5.0.235/tools/</MSBuildCommunityTasksPath>
</PropertyGroup> </PropertyGroup>
<Import Project="$(MSBuildCommunityTasksPath)MSBuild.Community.Tasks.Targets" Condition="Exists('$(ProjectDir)$(ProjectName).Credentials.template')"/> <Import Project="$(MSBuildCommunityTasksPath)MSBuild.Community.Tasks.Targets" Condition="Exists('$(ProjectDir)$(ProjectName).Credentials.template')" />
<Target Name="ProcessTemplates" BeforeTargets="PrepareForBuild" Condition="Exists('$(ProjectDir)$(ProjectName).Credentials.template')"> <Target Name="ProcessTemplates" BeforeTargets="PrepareForBuild" Condition="Exists('$(ProjectDir)$(ProjectName).Credentials.template')">
<Message Text="Processing: $(ProjectDir)$(ProjectName).Credentials.template" Importance="high"/> <Message Text="Processing: $(ProjectDir)$(ProjectName).Credentials.template" Importance="high"/>
<TemplateFile Template="$(ProjectDir)$(ProjectName).Credentials.template" OutputFilename="$(ProjectDir)$(ProjectName).Credentials.cs" Tokens="@(Tokens)" />
<ReadLinesFromFile File="$(ProjectDir)$(ProjectName).Credentials.template">
<Output TaskParameter="Lines" ItemName="TemplateLines" />
</ReadLinesFromFile>
<ApplyTokenReplacements InputLines="@(TemplateLines)" Tokens="@(Tokens)">
<Output TaskParameter="OutputLines" ItemName="ProcessedLines" />
</ApplyTokenReplacements>
<WriteLinesToFile
File="$(ProjectDir)$(ProjectName).Credentials.cs"
Lines="@(ProcessedLines)"
Overwrite="true" />
</Target> </Target>
</Project> </Project>

View file

@ -102,6 +102,20 @@ namespace Greenshot.Base.Controls
/// </summary> /// </summary>
protected bool ToFront { get; set; } protected bool ToFront { get; set; }
protected GreenshotForm()
{
DpiChanged += (sender, dpiChangedEventArgs) => DpiChangedHandler(dpiChangedEventArgs.DeviceDpiOld, dpiChangedEventArgs.DeviceDpiNew);
}
/// <summary>
/// This is the basic DpiChangedHandler responsible for all the DPI relative changes
/// </summary>
/// <param name="oldDpi"></param>
/// <param name="newDpi"></param>
protected virtual void DpiChangedHandler(int oldDpi, int newDpi)
{
}
#if DEBUG #if DEBUG
/// <summary> /// <summary>
/// Code to initialize the language etc during design time /// Code to initialize the language etc during design time
@ -350,7 +364,7 @@ namespace Greenshot.Base.Controls
{ {
if (!Language.TryGetString(languageKey, out langString)) if (!Language.TryGetString(languageKey, out langString))
{ {
LOG.WarnFormat("Unknown language key '{0}' configured for control '{1}', this might be okay.", languageKey, applyTo.Name); LOG.DebugFormat("Unknown language key '{0}' configured for control '{1}', this might be okay.", languageKey, applyTo.Name);
return; return;
} }
@ -382,10 +396,10 @@ namespace Greenshot.Base.Controls
protected void ApplyLanguage(Control applyTo) protected void ApplyLanguage(Control applyTo)
{ {
if (!(applyTo is IGreenshotLanguageBindable languageBindable)) if (applyTo is not IGreenshotLanguageBindable languageBindable)
{ {
// check if it's a menu! // check if it's a menu!
if (!(applyTo is ToolStrip toolStrip)) if (applyTo is not ToolStrip toolStrip)
{ {
return; return;
} }
@ -402,20 +416,14 @@ namespace Greenshot.Base.Controls
ApplyLanguage(applyTo, languageBindable.LanguageKey); ApplyLanguage(applyTo, languageBindable.LanguageKey);
// Repopulate the combox boxes // Repopulate the combox boxes
if (applyTo is IGreenshotConfigBindable configBindable && applyTo is GreenshotComboBox comboxBox) if (applyTo is not (IGreenshotConfigBindable configBindable and GreenshotComboBox comboxBox)) return;
{ if (string.IsNullOrEmpty(configBindable.SectionName) || string.IsNullOrEmpty(configBindable.PropertyName)) return;
if (!string.IsNullOrEmpty(configBindable.SectionName) && !string.IsNullOrEmpty(configBindable.PropertyName)) IniSection section = IniConfig.GetIniSection(configBindable.SectionName);
{ if (section == null) return;
IniSection section = IniConfig.GetIniSection(configBindable.SectionName); // Only update the language, so get the actual value and than repopulate
if (section != null) Enum currentValue = comboxBox.GetSelectedEnum();
{ comboxBox.Populate(section.Values[configBindable.PropertyName].ValueType);
// Only update the language, so get the actual value and than repopulate comboxBox.SetValue(currentValue);
Enum currentValue = comboxBox.GetSelectedEnum();
comboxBox.Populate(section.Values[configBindable.PropertyName].ValueType);
comboxBox.SetValue(currentValue);
}
}
}
} }
/// <summary> /// <summary>
@ -458,10 +466,9 @@ namespace Greenshot.Base.Controls
continue; continue;
} }
if (!(controlObject is Control applyToControl)) if (controlObject is not Control applyToControl)
{ {
ToolStripItem applyToItem = controlObject as ToolStripItem; if (controlObject is not ToolStripItem applyToItem)
if (applyToItem == null)
{ {
LOG.DebugFormat("No Control or ToolStripItem: {0}", field.Name); LOG.DebugFormat("No Control or ToolStripItem: {0}", field.Name);
continue; continue;
@ -530,7 +537,7 @@ namespace Greenshot.Base.Controls
/// <summary> /// <summary>
/// Fill all GreenshotControls with the values from the configuration /// Fill all GreenshotControls with the values from the configuration
/// </summary> /// </summary>
protected void FillFields() private void FillFields()
{ {
foreach (FieldInfo field in GetCachedFields(GetType())) foreach (FieldInfo field in GetCachedFields(GetType()))
{ {

View file

@ -120,6 +120,7 @@ namespace Greenshot.Base.Controls
private void PrepareFilterOptions() private void PrepareFilterOptions()
{ {
// TODO: Change to the FileFormatHandlerRegistry to look for all the supported extensions
OutputFormat[] supportedImageFormats = (OutputFormat[]) Enum.GetValues(typeof(OutputFormat)); OutputFormat[] supportedImageFormats = (OutputFormat[]) Enum.GetValues(typeof(OutputFormat));
_filterOptions = new FilterOption[supportedImageFormats.Length]; _filterOptions = new FilterOption[supportedImageFormats.Length];
for (int i = 0; i < _filterOptions.Length; i++) for (int i = 0; i < _filterOptions.Length; i++)
@ -166,7 +167,7 @@ namespace Greenshot.Base.Controls
// if the filename contains a valid extension, which is the same like the selected filter item's extension, the filename is okay // if the filename contains a valid extension, which is the same like the selected filter item's extension, the filename is okay
if (fn.EndsWith(Extension, StringComparison.CurrentCultureIgnoreCase)) return fn; if (fn.EndsWith(Extension, StringComparison.CurrentCultureIgnoreCase)) return fn;
// otherwise we just add the selected filter item's extension // otherwise we just add the selected filter item's extension
else return fn + "." + Extension; return fn + "." + Extension;
} }
set set
{ {

View file

@ -22,12 +22,15 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
using Dapplo.Windows.Common.Extensions;
using Dapplo.Windows.Common.Structs;
using Dapplo.Windows.DesktopWindowsManager;
using Dapplo.Windows.DesktopWindowsManager.Structs;
using Dapplo.Windows.User32;
using Dapplo.Windows.User32.Enums;
using Greenshot.Base.Core; using Greenshot.Base.Core;
using Greenshot.Base.Core.Enums; using Greenshot.Base.Core.Enums;
using Greenshot.Base.IniFile; using Greenshot.Base.IniFile;
using Greenshot.Base.UnmanagedHelpers;
using Greenshot.Base.UnmanagedHelpers.Enums;
using Greenshot.Base.UnmanagedHelpers.Structs;
namespace Greenshot.Base.Controls namespace Greenshot.Base.Controls
{ {
@ -70,7 +73,7 @@ namespace Greenshot.Base.Controls
{ {
if (_thumbnailHandle == IntPtr.Zero) return; if (_thumbnailHandle == IntPtr.Zero) return;
DWM.DwmUnregisterThumbnail(_thumbnailHandle); DwmApi.DwmUnregisterThumbnail(_thumbnailHandle);
_thumbnailHandle = IntPtr.Zero; _thumbnailHandle = IntPtr.Zero;
} }
@ -83,19 +86,19 @@ namespace Greenshot.Base.Controls
{ {
UnregisterThumbnail(); UnregisterThumbnail();
DWM.DwmRegisterThumbnail(Handle, window.Handle, out _thumbnailHandle); DwmApi.DwmRegisterThumbnail(Handle, window.Handle, out _thumbnailHandle);
if (_thumbnailHandle == IntPtr.Zero) return; if (_thumbnailHandle == IntPtr.Zero) return;
var result = DWM.DwmQueryThumbnailSourceSize(_thumbnailHandle, out var sourceSize); var result = DwmApi.DwmQueryThumbnailSourceSize(_thumbnailHandle, out var sourceSize);
if (result.Failed()) if (result.Failed())
{ {
DWM.DwmUnregisterThumbnail(_thumbnailHandle); DwmApi.DwmUnregisterThumbnail(_thumbnailHandle);
return; return;
} }
if (sourceSize.IsEmpty) if (sourceSize.IsEmpty)
{ {
DWM.DwmUnregisterThumbnail(_thumbnailHandle); DwmApi.DwmUnregisterThumbnail(_thumbnailHandle);
return; return;
} }
@ -110,17 +113,17 @@ namespace Greenshot.Base.Controls
Width = thumbnailWidth; Width = thumbnailWidth;
Height = thumbnailHeight; Height = thumbnailHeight;
// Prepare the displaying of the Thumbnail // Prepare the displaying of the Thumbnail
var dwmThumbnailProperties = new DWM_THUMBNAIL_PROPERTIES var dwmThumbnailProperties = new DwmThumbnailProperties()
{ {
Opacity = 255, Opacity = 255,
Visible = true, Visible = true,
SourceClientAreaOnly = false, SourceClientAreaOnly = false,
Destination = new RECT(0, 0, thumbnailWidth, thumbnailHeight) Destination = new NativeRect(0, 0, thumbnailWidth, thumbnailHeight)
}; };
result = DWM.DwmUpdateThumbnailProperties(_thumbnailHandle, ref dwmThumbnailProperties); result = DwmApi.DwmUpdateThumbnailProperties(_thumbnailHandle, ref dwmThumbnailProperties);
if (result.Failed()) if (result.Failed())
{ {
DWM.DwmUnregisterThumbnail(_thumbnailHandle); DwmApi.DwmUnregisterThumbnail(_thumbnailHandle);
return; return;
} }
@ -137,13 +140,13 @@ namespace Greenshot.Base.Controls
// Make sure it's on "top"! // Make sure it's on "top"!
if (parentControl != null) if (parentControl != null)
{ {
User32.SetWindowPos(Handle, parentControl.Handle, 0, 0, 0, 0, WindowPos.SWP_NOMOVE | WindowPos.SWP_NOSIZE | WindowPos.SWP_NOACTIVATE); User32Api.SetWindowPos(Handle, parentControl.Handle, 0, 0, 0, 0, WindowPos.SWP_NOMOVE | WindowPos.SWP_NOSIZE | WindowPos.SWP_NOACTIVATE);
} }
} }
public void AlignToControl(Control alignTo) public void AlignToControl(Control alignTo)
{ {
var screenBounds = WindowCapture.GetScreenBounds(); var screenBounds = DisplayInfo.ScreenBounds;
if (screenBounds.Contains(alignTo.Left, alignTo.Top - Height)) if (screenBounds.Contains(alignTo.Left, alignTo.Top - Height))
{ {
Location = new Point(alignTo.Left + (alignTo.Width / 2) - (Width / 2), alignTo.Top - Height); Location = new Point(alignTo.Left + (alignTo.Width / 2) - (Width / 2), alignTo.Top - Height);

View file

@ -24,15 +24,18 @@ using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Threading; using System.Threading;
using System.Windows.Forms; using System.Windows.Forms;
using Dapplo.Windows.Common.Extensions;
using Dapplo.Windows.Common.Structs;
using Dapplo.Windows.Dpi;
using Dapplo.Windows.User32;
using Greenshot.Base.IniFile; using Greenshot.Base.IniFile;
using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces;
using Greenshot.Base.UnmanagedHelpers;
using log4net; using log4net;
namespace Greenshot.Base.Core namespace Greenshot.Base.Core
{ {
/// <summary> /// <summary>
/// Description of AbstractDestination. /// The AbstractDestination is a default implementation of IDestination
/// </summary> /// </summary>
public abstract class AbstractDestination : IDestination public abstract class AbstractDestination : IDestination
{ {
@ -41,7 +44,7 @@ namespace Greenshot.Base.Core
public virtual int CompareTo(object obj) public virtual int CompareTo(object obj)
{ {
if (!(obj is IDestination other)) if (obj is not IDestination other)
{ {
return 1; return 1;
} }
@ -176,7 +179,7 @@ namespace Greenshot.Base.Core
ExportInformation exportInformation = new ExportInformation(Designation, Language.GetString("settings_destination_picker")); ExportInformation exportInformation = new ExportInformation(Designation, Language.GetString("settings_destination_picker"));
var menu = new ContextMenuStrip var menu = new ContextMenuStrip
{ {
ImageScalingSize = CoreConfig.ScaledIconSize, ImageScalingSize = CoreConfig.IconSize,
Tag = null, Tag = null,
TopLevel = true TopLevel = true
}; };
@ -184,10 +187,10 @@ namespace Greenshot.Base.Core
menu.Opening += (sender, args) => menu.Opening += (sender, args) =>
{ {
// find the DPI settings for the screen where this is going to land // find the DPI settings for the screen where this is going to land
var screenDpi = DpiHelper.GetDpi(menu.Location); var screenDpi = NativeDpiMethods.GetDpi(menu.Location);
var scaledIconSize = DpiHelper.ScaleWithDpi(CoreConfig.IconSize, screenDpi); var scaledIconSize = DpiCalculator.ScaleWithDpi(CoreConfig.IconSize, screenDpi);
menu.SuspendLayout(); menu.SuspendLayout();
var fontSize = DpiHelper.ScaleWithDpi(12f, screenDpi); var fontSize = DpiCalculator.ScaleWithDpi(12f, screenDpi);
menu.Font = new Font(FontFamily.GenericSansSerif, fontSize, FontStyle.Regular, GraphicsUnit.Pixel); menu.Font = new Font(FontFamily.GenericSansSerif, fontSize, FontStyle.Regular, GraphicsUnit.Pixel);
menu.ImageScalingSize = scaledIconSize; menu.ImageScalingSize = scaledIconSize;
menu.ResumeLayout(); menu.ResumeLayout();
@ -304,7 +307,7 @@ namespace Greenshot.Base.Core
ShowMenuAtCursor(menu); ShowMenuAtCursor(menu);
return exportInformation; return exportInformation;
} }
/// <summary> /// <summary>
/// This method will show the supplied context menu at the mouse cursor, also makes sure it has focus and it's not visible in the taskbar. /// This method will show the supplied context menu at the mouse cursor, also makes sure it has focus and it's not visible in the taskbar.
/// </summary> /// </summary>
@ -312,21 +315,21 @@ namespace Greenshot.Base.Core
private static void ShowMenuAtCursor(ContextMenuStrip menu) private static void ShowMenuAtCursor(ContextMenuStrip menu)
{ {
// find a suitable location // find a suitable location
Point location = Cursor.Position; NativePoint location = Cursor.Position;
Rectangle menuRectangle = new Rectangle(location, menu.Size); var menuRectangle = new NativeRect(location, menu.Size);
menuRectangle.Intersect(WindowCapture.GetScreenBounds()); menuRectangle = menuRectangle.Intersect(DisplayInfo.ScreenBounds);
if (menuRectangle.Height < menu.Height) if (menuRectangle.Height < menu.Height)
{ {
location.Offset(-40, -(menuRectangle.Height - menu.Height)); location = location.Offset(-40, -(menuRectangle.Height - menu.Height));
} }
else else
{ {
location.Offset(-40, -10); location = location.Offset(-40, -10);
} }
// This prevents the problem that the context menu shows in the task-bar // This prevents the problem that the context menu shows in the task-bar
User32.SetForegroundWindow(SimpleServiceProvider.Current.GetInstance<NotifyIcon>().ContextMenuStrip.Handle); User32Api.SetForegroundWindow(SimpleServiceProvider.Current.GetInstance<NotifyIcon>().ContextMenuStrip.Handle);
menu.Show(location); menu.Show(location);
menu.Focus(); menu.Focus();

View file

@ -22,6 +22,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using Dapplo.Windows.Common.Structs;
namespace Greenshot.Base.Core namespace Greenshot.Base.Core
{ {
@ -280,19 +281,19 @@ namespace Greenshot.Base.Core
/// <summary> /// <summary>
/// Implementation of the RectangleAnimator /// Implementation of the RectangleAnimator
/// </summary> /// </summary>
public class RectangleAnimator : AnimatorBase<Rectangle> public class RectangleAnimator : AnimatorBase<NativeRect>
{ {
public RectangleAnimator(Rectangle first, Rectangle last, int frames) public RectangleAnimator(NativeRect first, NativeRect last, int frames)
: base(first, last, frames, EasingType.Linear, EasingMode.EaseIn) : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn)
{ {
} }
public RectangleAnimator(Rectangle first, Rectangle last, int frames, EasingType easingType) public RectangleAnimator(NativeRect first, NativeRect last, int frames, EasingType easingType)
: base(first, last, frames, easingType, EasingMode.EaseIn) : base(first, last, frames, easingType, EasingMode.EaseIn)
{ {
} }
public RectangleAnimator(Rectangle first, Rectangle last, int frames, EasingType easingType, EasingMode easingMode) public RectangleAnimator(NativeRect first, NativeRect last, int frames, EasingType easingType, EasingMode easingMode)
: base(first, last, frames, easingType, easingMode) : base(first, last, frames, easingType, easingMode)
{ {
} }
@ -300,8 +301,8 @@ namespace Greenshot.Base.Core
/// <summary> /// <summary>
/// Calculate the next frame object /// Calculate the next frame object
/// </summary> /// </summary>
/// <returns>Rectangle</returns> /// <returns>NativeRect</returns>
public override Rectangle Next() public override NativeRect Next()
{ {
if (!NextFrame) if (!NextFrame)
{ {
@ -317,7 +318,7 @@ namespace Greenshot.Base.Core
double dh = Last.Height - First.Height; double dh = Last.Height - First.Height;
int width = First.Width + (int) (easingValue * dw); int width = First.Width + (int) (easingValue * dw);
int height = First.Height + (int) (easingValue * dh); int height = First.Height + (int) (easingValue * dh);
Current = new Rectangle(x, y, width, height); Current = new NativeRect(x, y, width, height);
return Current; return Current;
} }
@ -326,19 +327,19 @@ namespace Greenshot.Base.Core
/// <summary> /// <summary>
/// Implementation of the PointAnimator /// Implementation of the PointAnimator
/// </summary> /// </summary>
public class PointAnimator : AnimatorBase<Point> public class PointAnimator : AnimatorBase<NativePoint>
{ {
public PointAnimator(Point first, Point last, int frames) public PointAnimator(NativePoint first, NativePoint last, int frames)
: base(first, last, frames, EasingType.Linear, EasingMode.EaseIn) : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn)
{ {
} }
public PointAnimator(Point first, Point last, int frames, EasingType easingType) public PointAnimator(NativePoint first, NativePoint last, int frames, EasingType easingType)
: base(first, last, frames, easingType, EasingMode.EaseIn) : base(first, last, frames, easingType, EasingMode.EaseIn)
{ {
} }
public PointAnimator(Point first, Point last, int frames, EasingType easingType, EasingMode easingMode) public PointAnimator(NativePoint first, NativePoint last, int frames, EasingType easingType, EasingMode easingMode)
: base(first, last, frames, easingType, easingMode) : base(first, last, frames, easingType, easingMode)
{ {
} }
@ -347,7 +348,7 @@ namespace Greenshot.Base.Core
/// Calculate the next frame value /// Calculate the next frame value
/// </summary> /// </summary>
/// <returns>Point</returns> /// <returns>Point</returns>
public override Point Next() public override NativePoint Next()
{ {
if (NextFrame) if (NextFrame)
{ {
@ -357,7 +358,7 @@ namespace Greenshot.Base.Core
int x = First.X + (int) (easingValue * dx); int x = First.X + (int) (easingValue * dx);
int y = First.Y + (int) (easingValue * dy); int y = First.Y + (int) (easingValue * dy);
Current = new Point(x, y); Current = new NativePoint(x, y);
} }
return Current; return Current;
@ -367,19 +368,19 @@ namespace Greenshot.Base.Core
/// <summary> /// <summary>
/// Implementation of the SizeAnimator /// Implementation of the SizeAnimator
/// </summary> /// </summary>
public class SizeAnimator : AnimatorBase<Size> public class SizeAnimator : AnimatorBase<NativeSize>
{ {
public SizeAnimator(Size first, Size last, int frames) public SizeAnimator(NativeSize first, NativeSize last, int frames)
: base(first, last, frames, EasingType.Linear, EasingMode.EaseIn) : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn)
{ {
} }
public SizeAnimator(Size first, Size last, int frames, EasingType easingType) public SizeAnimator(NativeSize first, NativeSize last, int frames, EasingType easingType)
: base(first, last, frames, easingType, EasingMode.EaseIn) : base(first, last, frames, easingType, EasingMode.EaseIn)
{ {
} }
public SizeAnimator(Size first, Size last, int frames, EasingType easingType, EasingMode easingMode) public SizeAnimator(NativeSize first, NativeSize last, int frames, EasingType easingType, EasingMode easingMode)
: base(first, last, frames, easingType, easingMode) : base(first, last, frames, easingType, easingMode)
{ {
} }
@ -388,7 +389,7 @@ namespace Greenshot.Base.Core
/// Calculate the next frame values /// Calculate the next frame values
/// </summary> /// </summary>
/// <returns>Size</returns> /// <returns>Size</returns>
public override Size Next() public override NativeSize Next()
{ {
if (NextFrame) if (NextFrame)
{ {
@ -397,7 +398,7 @@ namespace Greenshot.Base.Core
double dh = Last.Height - First.Height; double dh = Last.Height - First.Height;
int width = First.Width + (int) (easingValue * dw); int width = First.Width + (int) (easingValue * dw);
int height = First.Height + (int) (easingValue * dh); int height = First.Height + (int) (easingValue * dh);
Current = new Size(width, height); Current = new NativeSize(width, height);
} }
return Current; return Current;

View file

@ -22,6 +22,9 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using Dapplo.Windows.Common.Extensions;
using Dapplo.Windows.Common.Structs;
using Dapplo.Windows.User32;
using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Ocr; using Greenshot.Base.Interfaces.Ocr;
using log4net; using log4net;
@ -36,18 +39,18 @@ namespace Greenshot.Base.Core
{ {
private static readonly ILog Log = LogManager.GetLogger(typeof(Capture)); private static readonly ILog Log = LogManager.GetLogger(typeof(Capture));
private Rectangle _screenBounds; private NativeRect _screenBounds;
/// <summary> /// <summary>
/// Get/Set the screen bounds /// Get/Set the screen bounds
/// </summary> /// </summary>
public Rectangle ScreenBounds public NativeRect ScreenBounds
{ {
get get
{ {
if (_screenBounds == Rectangle.Empty) if (_screenBounds.IsEmpty)
{ {
_screenBounds = WindowCapture.GetScreenBounds(); _screenBounds = DisplayInfo.ScreenBounds;
} }
return _screenBounds; return _screenBounds;
@ -124,23 +127,23 @@ namespace Greenshot.Base.Core
/// </summary> /// </summary>
public bool CursorVisible { get; set; } public bool CursorVisible { get; set; }
private Point _cursorLocation = Point.Empty; private NativePoint _cursorLocation = NativePoint.Empty;
/// <summary> /// <summary>
/// Get/Set the CursorLocation /// Get/Set the CursorLocation
/// </summary> /// </summary>
public Point CursorLocation public NativePoint CursorLocation
{ {
get => _cursorLocation; get => _cursorLocation;
set => _cursorLocation = value; set => _cursorLocation = value;
} }
private Point _location = Point.Empty; private NativePoint _location = NativePoint.Empty;
/// <summary> /// <summary>
/// Get/set the Location /// Get/set the Location
/// </summary> /// </summary>
public Point Location public NativePoint Location
{ {
get => _location; get => _location;
set => _location = value; set => _location = value;
@ -162,7 +165,7 @@ namespace Greenshot.Base.Core
/// </summary> /// </summary>
public Capture() public Capture()
{ {
_screenBounds = WindowCapture.GetScreenBounds(); _screenBounds = DisplayInfo.ScreenBounds;
_captureDetails = new CaptureDetails(); _captureDetails = new CaptureDetails();
} }
@ -214,8 +217,8 @@ namespace Greenshot.Base.Core
/// <summary> /// <summary>
/// Crops the capture to the specified rectangle (with Bitmap coordinates!) /// Crops the capture to the specified rectangle (with Bitmap coordinates!)
/// </summary> /// </summary>
/// <param name="cropRectangle">Rectangle with bitmap coordinates</param> /// <param name="cropRectangle">NativeRect with bitmap coordinates</param>
public bool Crop(Rectangle cropRectangle) public bool Crop(NativeRect cropRectangle)
{ {
Log.Debug("Cropping to: " + cropRectangle); Log.Debug("Cropping to: " + cropRectangle);
if (!ImageHelper.Crop(ref _image, ref cropRectangle)) if (!ImageHelper.Crop(ref _image, ref cropRectangle))
@ -245,7 +248,7 @@ namespace Greenshot.Base.Core
/// <param name="y">y coordinates to move the mouse</param> /// <param name="y">y coordinates to move the mouse</param>
public void MoveMouseLocation(int x, int y) public void MoveMouseLocation(int x, int y)
{ {
_cursorLocation.Offset(x, y); _cursorLocation = _cursorLocation.Offset(x, y);
} }
// TODO: Enable when the elements are usable again. // TODO: Enable when the elements are usable again.
@ -261,7 +264,7 @@ namespace Greenshot.Base.Core
//private void MoveElements(List<ICaptureElement> listOfElements, int x, int y) { //private void MoveElements(List<ICaptureElement> listOfElements, int x, int y) {
// foreach(ICaptureElement childElement in listOfElements) { // foreach(ICaptureElement childElement in listOfElements) {
// Rectangle bounds = childElement.Bounds; // NativeRect bounds = childElement.Bounds;
// bounds.Offset(x, y); // bounds.Offset(x, y);
// childElement.Bounds = bounds; // childElement.Bounds = bounds;
// MoveElements(childElement.Children, x, y); // MoveElements(childElement.Children, x, y);

View file

@ -20,15 +20,16 @@
*/ */
using System.Drawing; using System.Drawing;
using Dapplo.Windows.Common.Structs;
namespace Greenshot.Base.Core namespace Greenshot.Base.Core
{ {
/// <summary> /// <summary>
/// This is the method signature which is used to capture a rectangle from the screen. /// This is the method signature which is used to capture a rectangle from the screen.
/// </summary> /// </summary>
/// <param name="captureBounds"></param> /// <param name="captureBounds">NativeRect</param>
/// <returns>Captured Bitmap</returns> /// <returns>Captured Bitmap</returns>
public delegate Bitmap CaptureScreenRectangleHandler(Rectangle captureBounds); public delegate Bitmap CaptureScreenRectangleHandler(NativeRect captureBounds);
/// <summary> /// <summary>
/// This is a hack to experiment with different screen capture routines /// This is a hack to experiment with different screen capture routines

View file

@ -30,11 +30,17 @@ using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Windows.Forms; using System.Windows.Forms;
using Dapplo.Windows.Clipboard;
using Dapplo.Windows.Common.Structs;
using Dapplo.Windows.Gdi32.Enums;
using Dapplo.Windows.Gdi32.Structs;
using Dapplo.Windows.User32;
using Greenshot.Base.Core.Enums; using Greenshot.Base.Core.Enums;
using Greenshot.Base.Core.FileFormatHandlers;
using Greenshot.Base.IniFile; using Greenshot.Base.IniFile;
using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Base.Interfaces.Plugin; using Greenshot.Base.Interfaces.Plugin;
using Greenshot.Base.UnmanagedHelpers;
using log4net; using log4net;
using HtmlDocument = HtmlAgilityPack.HtmlDocument; using HtmlDocument = HtmlAgilityPack.HtmlDocument;
@ -63,7 +69,8 @@ namespace Greenshot.Base.Core
//private static readonly string FORMAT_HTML = "HTML Format"; //private static readonly string FORMAT_HTML = "HTML Format";
// Template for the HTML Text on the clipboard // Template for the HTML Text on the clipboard
// see: https://msdn.microsoft.com/en-us/library/ms649015%28v=vs.85%29.aspx // see: https://msdn.microsoft.com/en-us/library/ms649015%28v=v
// s.85%29.aspx
// or: https://msdn.microsoft.com/en-us/library/Aa767917.aspx // or: https://msdn.microsoft.com/en-us/library/Aa767917.aspx
private const string HtmlClipboardString = @"Version:0.9 private const string HtmlClipboardString = @"Version:0.9
StartHTML:<<<<<<<1 StartHTML:<<<<<<<1
@ -112,12 +119,12 @@ EndSelection:<<<<<<<4
string owner = null; string owner = null;
try try
{ {
IntPtr hWnd = User32.GetClipboardOwner(); IntPtr hWnd = ClipboardNative.CurrentOwner;
if (hWnd != IntPtr.Zero) if (hWnd != IntPtr.Zero)
{ {
try try
{ {
User32.GetWindowThreadProcessId(hWnd, out var pid); User32Api.GetWindowThreadProcessId(hWnd, out var pid);
using Process me = Process.GetCurrentProcess(); using Process me = Process.GetCurrentProcess();
using Process ownerProcess = Process.GetProcessById(pid); using Process ownerProcess = Process.GetProcessById(pid);
// Exclude myself // Exclude myself
@ -139,9 +146,7 @@ EndSelection:<<<<<<<4
catch (Exception e) catch (Exception e)
{ {
Log.Warn("Non critical error: Couldn't get clipboard process, trying to use the title.", e); Log.Warn("Non critical error: Couldn't get clipboard process, trying to use the title.", e);
var title = new StringBuilder(260, 260); owner = User32Api.GetText(hWnd);
User32.GetWindowText(hWnd, title, title.Capacity);
owner = title.ToString();
} }
} }
} }
@ -279,6 +284,9 @@ EndSelection:<<<<<<<4
{ {
if (dataObject == null) return false; if (dataObject == null) return false;
IList<string> formats = GetFormats(dataObject);
Log.DebugFormat("Found formats: {0}", string.Join(",", formats));
if (dataObject.GetDataPresent(DataFormats.Bitmap) if (dataObject.GetDataPresent(DataFormats.Bitmap)
|| dataObject.GetDataPresent(DataFormats.Dib) || dataObject.GetDataPresent(DataFormats.Dib)
|| dataObject.GetDataPresent(DataFormats.Tiff) || dataObject.GetDataPresent(DataFormats.Tiff)
@ -298,14 +306,15 @@ EndSelection:<<<<<<<4
{ {
return true; return true;
} }
var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances<IFileFormatHandler>();
foreach (var fileData in IterateClipboardContent(dataObject)) var supportedExtensions = fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadDrawableFromStream).ToList();
foreach (var (stream, filename) in IterateClipboardContent(dataObject))
{ {
try try
{ {
using (ImageHelper.FromStream(fileData)) var extension = Path.GetExtension(filename)?.ToLowerInvariant();
if (supportedExtensions.Contains(extension))
{ {
// If we get here, there is an image
return true; return true;
} }
} }
@ -315,7 +324,7 @@ EndSelection:<<<<<<<4
} }
finally finally
{ {
fileData?.Dispose(); stream?.Dispose();
} }
} }
@ -327,7 +336,8 @@ EndSelection:<<<<<<<4
var imageStream = clipboardContent as MemoryStream; var imageStream = clipboardContent as MemoryStream;
if (IsValidStream(imageStream)) if (IsValidStream(imageStream))
{ {
using (ImageHelper.FromStream(imageStream)) // TODO: How to check if we support "just a stream"?
using (ImageIO.FromStream(imageStream))
{ {
// If we get here, there is an image // If we get here, there is an image
return true; return true;
@ -373,9 +383,10 @@ EndSelection:<<<<<<<4
/// Iterate the clipboard content /// Iterate the clipboard content
/// </summary> /// </summary>
/// <param name="dataObject">IDataObject</param> /// <param name="dataObject">IDataObject</param>
/// <returns>IEnumerable{MemoryStream}</returns> /// <returns>IEnumerable{(MemoryStream,string)}</returns>
private static IEnumerable<MemoryStream> IterateClipboardContent(IDataObject dataObject) private static IEnumerable<(MemoryStream stream,string filename)> IterateClipboardContent(IDataObject dataObject)
{ {
if (dataObject == null) yield break;
var fileDescriptors = AvailableFileDescriptors(dataObject); var fileDescriptors = AvailableFileDescriptors(dataObject);
if (fileDescriptors == null) yield break; if (fileDescriptors == null) yield break;
@ -413,8 +424,8 @@ EndSelection:<<<<<<<4
/// </summary> /// </summary>
/// <param name="fileDescriptors">IEnumerable{FileDescriptor}</param> /// <param name="fileDescriptors">IEnumerable{FileDescriptor}</param>
/// <param name="dataObject">IDataObject</param> /// <param name="dataObject">IDataObject</param>
/// <returns>IEnumerable{MemoryStream}</returns> /// <returns>IEnumerable{(MemoryStream stream, string filename)}</returns>
private static IEnumerable<MemoryStream> IterateFileDescriptors(IEnumerable<FileDescriptor> fileDescriptors, IDataObject dataObject) private static IEnumerable<(MemoryStream stream, string filename)> IterateFileDescriptors(IEnumerable<FileDescriptor> fileDescriptors, IDataObject dataObject)
{ {
if (fileDescriptors == null) if (fileDescriptors == null)
{ {
@ -445,7 +456,7 @@ EndSelection:<<<<<<<4
if (fileData?.Length > 0) if (fileData?.Length > 0)
{ {
fileData.Position = 0; fileData.Position = 0;
yield return fileData; yield return (fileData, fileDescriptor.FileName);
} }
fileIndex++; fileIndex++;
@ -489,8 +500,12 @@ EndSelection:<<<<<<<4
public static Image GetImage() public static Image GetImage()
{ {
IDataObject clipboardData = GetDataObject(); IDataObject clipboardData = GetDataObject();
if (clipboardData == null)
{
return null;
}
// Return the first image // Return the first image
foreach (Image clipboardImage in GetImages(clipboardData)) foreach (var clipboardImage in GetImages(clipboardData))
{ {
return clipboardImage; return clipboardImage;
} }
@ -503,57 +518,152 @@ EndSelection:<<<<<<<4
/// Returned images must be disposed by the calling code! /// Returned images must be disposed by the calling code!
/// </summary> /// </summary>
/// <param name="dataObject"></param> /// <param name="dataObject"></param>
/// <returns>IEnumerable of Image</returns> /// <returns>IEnumerable of Bitmap</returns>
public static IEnumerable<Image> GetImages(IDataObject dataObject) public static IEnumerable<Bitmap> GetImages(IDataObject dataObject)
{ {
// Get single image, this takes the "best" match // Get single image, this takes the "best" match
Image singleImage = GetImage(dataObject); Bitmap singleImage = GetImage(dataObject);
if (singleImage != null) if (singleImage != null)
{ {
Log.InfoFormat("Got image from clipboard with size {0} and format {1}", singleImage.Size, singleImage.PixelFormat); Log.Info($"Got {singleImage.GetType()} from clipboard with size {singleImage.Size}");
yield return singleImage; yield return singleImage;
yield break;
} }
else
var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances<IFileFormatHandler>();
var supportedExtensions = fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadDrawableFromStream).ToList();
foreach (var (stream, filename) in IterateClipboardContent(dataObject))
{ {
foreach (var fileData in IterateClipboardContent(dataObject)) var extension = Path.GetExtension(filename)?.ToLowerInvariant();
if (!supportedExtensions.Contains(extension))
{ {
Image image; continue;
try
{
image = ImageHelper.FromStream(fileData);
}
catch (Exception ex)
{
Log.Error("Couldn't read file contents", ex);
continue;
}
finally
{
fileData?.Dispose();
}
// If we get here, there is an image
yield return image;
} }
// check if files are supplied Bitmap bitmap = null;
foreach (string imageFile in GetImageFilenames(dataObject))
try
{ {
Image returnImage = null; if (!fileFormatHandlers.TryLoadFromStream(stream, extension, out bitmap))
try
{ {
returnImage = ImageHelper.LoadImage(imageFile); continue;
}
catch (Exception streamImageEx)
{
Log.Error("Problem retrieving Image from clipboard.", streamImageEx);
} }
if (returnImage != null) }
catch (Exception ex)
{
Log.Error("Couldn't read file contents", ex);
continue;
}
finally
{
stream?.Dispose();
}
// If we get here, there is an image
yield return bitmap;
}
// check if files are supplied
foreach (string imageFile in GetImageFilenames(dataObject))
{
var extension = Path.GetExtension(imageFile)?.ToLowerInvariant();
if (!supportedExtensions.Contains(extension))
{
continue;
}
Bitmap bitmap = null;
using FileStream fileStream = new FileStream(imageFile, FileMode.Open, FileAccess.Read, FileShare.Read);
try
{
if (!fileFormatHandlers.TryLoadFromStream(fileStream, extension, out bitmap))
{ {
Log.InfoFormat("Got image from clipboard with size {0} and format {1}", returnImage.Size, returnImage.PixelFormat); continue;
yield return returnImage;
} }
} }
catch (Exception ex)
{
Log.Error("Couldn't read file contents", ex);
continue;
}
// If we get here, there is an image
yield return bitmap;
}
}
/// <summary>
/// Get all images (multiple if file names are available) from the dataObject
/// Returned images must be disposed by the calling code!
/// </summary>
/// <param name="dataObject"></param>
/// <returns>IEnumerable of IDrawableContainer</returns>
public static IEnumerable<IDrawableContainer> GetDrawables(IDataObject dataObject)
{
// Get single image, this takes the "best" match
IDrawableContainer singleImage = GetDrawable(dataObject);
if (singleImage != null)
{
Log.InfoFormat($"Got {singleImage.GetType()} from clipboard with size {singleImage.Size}");
yield return singleImage;
yield break;
}
var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances<IFileFormatHandler>();
var supportedExtensions = fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadDrawableFromStream).ToList();
foreach (var (stream, filename) in IterateClipboardContent(dataObject))
{
var extension = Path.GetExtension(filename)?.ToLowerInvariant();
if (!supportedExtensions.Contains(extension))
{
continue;
}
IEnumerable<IDrawableContainer> drawableContainers;
try
{
drawableContainers = fileFormatHandlers.LoadDrawablesFromStream(stream, extension);
}
catch (Exception ex)
{
Log.Error("Couldn't read file contents", ex);
continue;
}
finally
{
stream?.Dispose();
}
// If we get here, there is an image
foreach (var container in drawableContainers)
{
yield return container;
}
}
// check if files are supplied
foreach (string imageFile in GetImageFilenames(dataObject))
{
var extension = Path.GetExtension(imageFile)?.ToLowerInvariant();
if (!supportedExtensions.Contains(extension))
{
continue;
}
using FileStream fileStream = new FileStream(imageFile, FileMode.Open, FileAccess.Read, FileShare.Read);
IEnumerable<IDrawableContainer> drawableContainers;
try
{
drawableContainers = fileFormatHandlers.LoadDrawablesFromStream(fileStream, extension);
}
catch (Exception ex)
{
Log.Error("Couldn't read file contents", ex);
continue;
}
// If we get here, there is an image
foreach (var container in drawableContainers)
{
yield return container;
}
} }
} }
@ -562,51 +672,50 @@ EndSelection:<<<<<<<4
/// </summary> /// </summary>
/// <param name="dataObject"></param> /// <param name="dataObject"></param>
/// <returns>Image or null</returns> /// <returns>Image or null</returns>
private static Image GetImage(IDataObject dataObject) private static Bitmap GetImage(IDataObject dataObject)
{ {
Image returnImage = null; if (dataObject == null) return null;
if (dataObject != null)
{
IList<string> formats = GetFormats(dataObject);
string[] retrieveFormats;
// Found a weird bug, where PNG's from Outlook 2010 are clipped Bitmap returnImage = null;
// So I build some special logic to get the best format: IList<string> formats = GetFormats(dataObject);
if (formats != null && formats.Contains(FORMAT_PNG_OFFICEART) && formats.Contains(DataFormats.Dib)) string[] retrieveFormats;
// Found a weird bug, where PNG's from Outlook 2010 are clipped
// So I build some special logic to get the best format:
if (formats != null && formats.Contains(FORMAT_PNG_OFFICEART) && formats.Contains(DataFormats.Dib))
{
// Outlook ??
Log.Info("Most likely the current clipboard contents come from Outlook, as this has a problem with PNG and others we place the DIB format to the front...");
retrieveFormats = new[]
{ {
// Outlook ?? DataFormats.Dib, FORMAT_BITMAP, FORMAT_FILECONTENTS, FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF,
Log.Info("Most likely the current clipboard contents come from Outlook, as this has a problem with PNG and others we place the DIB format to the front..."); DataFormats.Tiff, FORMAT_GIF, FORMAT_HTML
retrieveFormats = new[] };
{ }
DataFormats.Dib, FORMAT_BITMAP, FORMAT_FILECONTENTS, FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF, else
DataFormats.Tiff, FORMAT_GIF, FORMAT_HTML {
}; retrieveFormats = new[]
{
FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_17, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF, DataFormats.Tiff, DataFormats.Dib, FORMAT_BITMAP,
FORMAT_FILECONTENTS, FORMAT_GIF, FORMAT_HTML
};
}
foreach (string currentFormat in retrieveFormats)
{
if (formats != null && formats.Contains(currentFormat))
{
Log.InfoFormat("Found {0}, trying to retrieve.", currentFormat);
returnImage = GetImageForFormat(currentFormat, dataObject);
} }
else else
{ {
retrieveFormats = new[] Log.DebugFormat("Couldn't find format {0}.", currentFormat);
{
FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_17, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF, DataFormats.Tiff, DataFormats.Dib, FORMAT_BITMAP,
FORMAT_FILECONTENTS, FORMAT_GIF, FORMAT_HTML
};
} }
foreach (string currentFormat in retrieveFormats) if (returnImage != null)
{ {
if (formats != null && formats.Contains(currentFormat)) return returnImage;
{
Log.InfoFormat("Found {0}, trying to retrieve.", currentFormat);
returnImage = GetImageForFormat(currentFormat, dataObject);
}
else
{
Log.DebugFormat("Couldn't find format {0}.", currentFormat);
}
if (returnImage != null)
{
return returnImage;
}
} }
} }
@ -614,15 +723,72 @@ EndSelection:<<<<<<<4
} }
/// <summary> /// <summary>
/// Helper method to try to get an image in the specified format from the dataObject /// Get an IDrawableContainer from the IDataObject, don't check for FileDrop
/// </summary>
/// <param name="dataObject"></param>
/// <returns>Image or null</returns>
private static IDrawableContainer GetDrawable(IDataObject dataObject)
{
if (dataObject == null) return null;
IDrawableContainer returnImage = null;
IList<string> formats = GetFormats(dataObject);
string[] retrieveFormats;
// Found a weird bug, where PNG's from Outlook 2010 are clipped
// So I build some special logic to get the best format:
if (formats != null && formats.Contains(FORMAT_PNG_OFFICEART) && formats.Contains(DataFormats.Dib))
{
// Outlook ??
Log.Info("Most likely the current clipboard contents come from Outlook, as this has a problem with PNG and others we place the DIB format to the front...");
retrieveFormats = new[]
{
DataFormats.Dib, FORMAT_BITMAP, FORMAT_FILECONTENTS, FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF,
DataFormats.Tiff, FORMAT_GIF, FORMAT_HTML
};
}
else
{
retrieveFormats = new[]
{
FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_17, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF, DataFormats.Tiff, DataFormats.Dib, FORMAT_BITMAP,
FORMAT_FILECONTENTS, FORMAT_GIF, FORMAT_HTML
};
}
foreach (string currentFormat in retrieveFormats)
{
if (formats != null && formats.Contains(currentFormat))
{
Log.InfoFormat("Found {0}, trying to retrieve.", currentFormat);
returnImage = GetDrawableForFormat(currentFormat, dataObject);
}
else
{
Log.DebugFormat("Couldn't find format {0}.", currentFormat);
}
if (returnImage != null)
{
return returnImage;
}
}
return null;
}
/// <summary>
/// Helper method to try to get an Bitmap in the specified format from the dataObject
/// the DIB reader should solve some issues /// the DIB reader should solve some issues
/// It also supports Format17/DibV5, by using the following information: https://stackoverflow.com/a/14335591 /// It also supports Format17/DibV5, by using the following information: https://stackoverflow.com/a/14335591
/// </summary> /// </summary>
/// <param name="format">string with the format</param> /// <param name="format">string with the format</param>
/// <param name="dataObject">IDataObject</param> /// <param name="dataObject">IDataObject</param>
/// <returns>Image or null</returns> /// <returns>Bitmap or null</returns>
private static Image GetImageForFormat(string format, IDataObject dataObject) private static Bitmap GetImageForFormat(string format, IDataObject dataObject)
{ {
Bitmap bitmap = null;
if (format == FORMAT_HTML) if (format == FORMAT_HTML)
{ {
var textObject = ContentAsString(dataObject, FORMAT_HTML, Encoding.UTF8); var textObject = ContentAsString(dataObject, FORMAT_HTML, Encoding.UTF8);
@ -638,10 +804,10 @@ EndSelection:<<<<<<<4
var srcAttribute = imgNode.Attributes["src"]; var srcAttribute = imgNode.Attributes["src"];
var imageUrl = srcAttribute.Value; var imageUrl = srcAttribute.Value;
Log.Debug(imageUrl); Log.Debug(imageUrl);
var image = NetworkHelper.DownloadImage(imageUrl); bitmap = NetworkHelper.DownloadImage(imageUrl);
if (image != null) if (bitmap != null)
{ {
return image; return bitmap;
} }
} }
} }
@ -652,111 +818,80 @@ EndSelection:<<<<<<<4
var imageStream = clipboardObject as MemoryStream; var imageStream = clipboardObject as MemoryStream;
if (!IsValidStream(imageStream)) if (!IsValidStream(imageStream))
{ {
// TODO: add "HTML Format" support here... return clipboardObject as Bitmap;
return clipboardObject as Image;
} }
var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances<IFileFormatHandler>();
if (CoreConfig.EnableSpecialDIBClipboardReader) // From here, imageStream is a valid stream
if (fileFormatHandlers.TryLoadFromStream(imageStream, format, out bitmap))
{ {
if (format == FORMAT_17 || format == DataFormats.Dib) return bitmap;
}
return null;
}
/// <summary>
/// Helper method to try to get an IDrawableContainer in the specified format from the dataObject
/// the DIB reader should solve some issues
/// It also supports Format17/DibV5, by using the following information: https://stackoverflow.com/a/14335591
/// </summary>
/// <param name="format">string with the format</param>
/// <param name="dataObject">IDataObject</param>
/// <returns>IDrawableContainer or null</returns>
private static IDrawableContainer GetDrawableForFormat(string format, IDataObject dataObject)
{
IDrawableContainer drawableContainer = null;
if (format == FORMAT_HTML)
{
var textObject = ContentAsString(dataObject, FORMAT_HTML, Encoding.UTF8);
if (textObject != null)
{ {
Log.Info("Found DIB stream, trying to process it."); var doc = new HtmlDocument();
try doc.LoadHtml(textObject);
var imgNodes = doc.DocumentNode.SelectNodes("//img");
if (imgNodes != null)
{ {
if (imageStream != null) foreach (var imgNode in imgNodes)
{ {
byte[] dibBuffer = new byte[imageStream.Length]; var srcAttribute = imgNode.Attributes["src"];
_ = imageStream.Read(dibBuffer, 0, dibBuffer.Length); var imageUrl = srcAttribute.Value;
var infoHeader = BinaryStructHelper.FromByteArray<BITMAPINFOHEADERV5>(dibBuffer); Log.Debug(imageUrl);
if (!infoHeader.IsDibV5) drawableContainer = NetworkHelper.DownloadImageAsDrawableContainer(imageUrl);
if (drawableContainer != null)
{ {
Log.InfoFormat("Using special DIB <v5 format reader with biCompression {0}", infoHeader.biCompression); return drawableContainer;
int fileHeaderSize = Marshal.SizeOf(typeof(BITMAPFILEHEADER));
uint infoHeaderSize = infoHeader.biSize;
int fileSize = (int) (fileHeaderSize + infoHeader.biSize + infoHeader.biSizeImage);
var fileHeader = new BITMAPFILEHEADER
{
bfType = BITMAPFILEHEADER.BM,
bfSize = fileSize,
bfReserved1 = 0,
bfReserved2 = 0,
bfOffBits = (int) (fileHeaderSize + infoHeaderSize + infoHeader.biClrUsed * 4)
};
byte[] fileHeaderBytes = BinaryStructHelper.ToByteArray(fileHeader);
using var bitmapStream = new MemoryStream();
bitmapStream.Write(fileHeaderBytes, 0, fileHeaderSize);
bitmapStream.Write(dibBuffer, 0, dibBuffer.Length);
bitmapStream.Seek(0, SeekOrigin.Begin);
var image = ImageHelper.FromStream(bitmapStream);
if (image != null)
{
return image;
}
}
else
{
Log.Info("Using special DIBV5 / Format17 format reader");
// CF_DIBV5
IntPtr gcHandle = IntPtr.Zero;
try
{
GCHandle handle = GCHandle.Alloc(dibBuffer, GCHandleType.Pinned);
gcHandle = GCHandle.ToIntPtr(handle);
return
new Bitmap(infoHeader.biWidth, infoHeader.biHeight,
-(int) (infoHeader.biSizeImage / infoHeader.biHeight),
infoHeader.biBitCount == 32 ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb,
new IntPtr(handle.AddrOfPinnedObject().ToInt32() + infoHeader.OffsetToPixels +
(infoHeader.biHeight - 1) * (int) (infoHeader.biSizeImage / infoHeader.biHeight))
);
}
catch (Exception ex)
{
Log.Error("Problem retrieving Format17 from clipboard.", ex);
}
finally
{
if (gcHandle == IntPtr.Zero)
{
GCHandle.FromIntPtr(gcHandle).Free();
}
}
} }
} }
} }
catch (Exception dibEx)
{
Log.Error("Problem retrieving DIB from clipboard.", dibEx);
}
} }
} }
else
{
Log.Info("Skipping special DIB format reader as it's disabled in the configuration.");
}
try object clipboardObject = GetFromDataObject(dataObject, format);
var imageStream = clipboardObject as MemoryStream;
if (!IsValidStream(imageStream))
{ {
if (imageStream != null) // TODO: add text based, like "HTML Format" support here...
// TODO: solve the issue that we do not have a factory for the ImageContainer
/*var image = clipboardObject as Image;
if (image != null)
{ {
imageStream.Seek(0, SeekOrigin.Begin); return new ImageContainer(this)
var tmpImage = ImageHelper.FromStream(imageStream);
if (tmpImage != null)
{ {
Log.InfoFormat("Got image with clipboard format {0} from the clipboard.", format); Image = image,
return tmpImage; Left = x,
} Top = y
};
} }
} return clipboardObject as Image;
catch (Exception streamImageEx) */
{ return null;
Log.Error($"Problem retrieving {format} from clipboard.", streamImageEx);
} }
return null; // From here, imageStream is a valid stream
var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances<IFileFormatHandler>();
return fileFormatHandlers.LoadDrawablesFromStream(imageStream, format).FirstOrDefault();
} }
/// <summary> /// <summary>
@ -840,7 +975,7 @@ EndSelection:<<<<<<<4
{ {
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false); SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false);
// Create the image which is going to be saved so we don't create it multiple times // Create the image which is going to be saved so we don't create it multiple times
disposeImage = ImageOutput.CreateImageFromSurface(surface, outputSettings, out imageToSave); disposeImage = ImageIO.CreateImageFromSurface(surface, outputSettings, out imageToSave);
try try
{ {
// Create PNG stream // Create PNG stream
@ -849,7 +984,7 @@ EndSelection:<<<<<<<4
pngStream = new MemoryStream(); pngStream = new MemoryStream();
// PNG works for e.g. Powerpoint // PNG works for e.g. Powerpoint
SurfaceOutputSettings pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false); SurfaceOutputSettings pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false);
ImageOutput.SaveToStream(imageToSave, null, pngStream, pngOutputSettings); ImageIO.SaveToStream(imageToSave, null, pngStream, pngOutputSettings);
pngStream.Seek(0, SeekOrigin.Begin); pngStream.Seek(0, SeekOrigin.Begin);
// Set the PNG stream // Set the PNG stream
dataObject.SetData(FORMAT_PNG, false, pngStream); dataObject.SetData(FORMAT_PNG, false, pngStream);
@ -866,11 +1001,18 @@ EndSelection:<<<<<<<4
{ {
// Create the stream for the clipboard // Create the stream for the clipboard
dibStream = new MemoryStream(); dibStream = new MemoryStream();
var dibBytes = ((Bitmap)imageToSave).ConvertToDib(); var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances<IFileFormatHandler>();
dibStream.Write(dibBytes,0, dibBytes.Length);
// Set the DIB to the clipboard DataObject if (!fileFormatHandlers.TrySaveToStream((Bitmap)imageToSave, dibStream, DataFormats.Dib))
dataObject.SetData(DataFormats.Dib, false, dibStream); {
dibStream.Dispose();
dibStream = null;
}
else
{
// Set the DIB to the clipboard DataObject
dataObject.SetData(DataFormats.Dib, false, dibStream);
}
} }
} }
catch (Exception dibEx) catch (Exception dibEx)
@ -887,20 +1029,17 @@ EndSelection:<<<<<<<4
dibV5Stream = new MemoryStream(); dibV5Stream = new MemoryStream();
// Create the BITMAPINFOHEADER // Create the BITMAPINFOHEADER
var header = new BITMAPINFOHEADERV5(imageToSave.Width, imageToSave.Height, 32) var header = BitmapV5Header.Create(imageToSave.Width, imageToSave.Height, 32);
{ // Make sure we have BI_BITFIELDS, this seems to be normal for Format17?
// Make sure we have BI_BITFIELDS, this seems to be normal for Format17? header.Compression = BitmapCompressionMethods.BI_BITFIELDS;
biCompression = BI_COMPRESSION.BI_BITFIELDS
};
// Create a byte[] to write // Create a byte[] to write
byte[] headerBytes = BinaryStructHelper.ToByteArray(header); byte[] headerBytes = BinaryStructHelper.ToByteArray(header);
// Write the BITMAPINFOHEADER to the stream // Write the BITMAPINFOHEADER to the stream
dibV5Stream.Write(headerBytes, 0, headerBytes.Length); dibV5Stream.Write(headerBytes, 0, headerBytes.Length);
// As we have specified BI_COMPRESSION.BI_BITFIELDS, the BitfieldColorMask needs to be added // As we have specified BI_COMPRESSION.BI_BITFIELDS, the BitfieldColorMask needs to be added
// This also makes sure the default values are set
BitfieldColorMask colorMask = new BitfieldColorMask(); BitfieldColorMask colorMask = new BitfieldColorMask();
// Make sure the values are set
colorMask.InitValues();
// Create the byte[] from the struct // Create the byte[] from the struct
byte[] colorMaskBytes = BinaryStructHelper.ToByteArray(colorMask); byte[] colorMaskBytes = BinaryStructHelper.ToByteArray(colorMask);
Array.Reverse(colorMaskBytes); Array.Reverse(colorMaskBytes);
@ -924,7 +1063,7 @@ EndSelection:<<<<<<<4
// Set the HTML // Set the HTML
if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.HTML)) if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.HTML))
{ {
string tmpFile = ImageOutput.SaveToTmpFile(surface, new SurfaceOutputSettings(OutputFormat.png, 100, false), null); string tmpFile = ImageIO.SaveToTmpFile(surface, new SurfaceOutputSettings(OutputFormat.png, 100, false), null);
string html = GetHtmlString(surface, tmpFile); string html = GetHtmlString(surface, tmpFile);
dataObject.SetText(html, TextDataFormat.Html); dataObject.SetText(html, TextDataFormat.Html);
} }
@ -942,11 +1081,11 @@ EndSelection:<<<<<<<4
// Check if we can use the previously used image // Check if we can use the previously used image
if (imageToSave.PixelFormat != PixelFormat.Format8bppIndexed) if (imageToSave.PixelFormat != PixelFormat.Format8bppIndexed)
{ {
ImageOutput.SaveToStream(imageToSave, surface, tmpPngStream, pngOutputSettings); ImageIO.SaveToStream(imageToSave, surface, tmpPngStream, pngOutputSettings);
} }
else else
{ {
ImageOutput.SaveToStream(surface, tmpPngStream, pngOutputSettings); ImageIO.SaveToStream(surface, tmpPngStream, pngOutputSettings);
} }
html = GetHtmlDataUrlString(surface, tmpPngStream); html = GetHtmlDataUrlString(surface, tmpPngStream);
@ -991,7 +1130,7 @@ EndSelection:<<<<<<<4
private static byte[] BitmapToByteArray(Bitmap bitmap) private static byte[] BitmapToByteArray(Bitmap bitmap)
{ {
// Lock the bitmap's bits. // Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height); var rect = new NativeRect(0, 0, bitmap.Width, bitmap.Height);
BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat); BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat);
int absStride = Math.Abs(bmpData.Stride); int absStride = Math.Abs(bmpData.Stride);
@ -1125,15 +1264,15 @@ EndSelection:<<<<<<<4
public static IEnumerable<string> GetImageFilenames(IDataObject dataObject) public static IEnumerable<string> GetImageFilenames(IDataObject dataObject)
{ {
string[] dropFileNames = (string[])dataObject.GetData(DataFormats.FileDrop); string[] dropFileNames = (string[])dataObject.GetData(DataFormats.FileDrop);
if (dropFileNames != null && dropFileNames.Length > 0) if (dropFileNames is not { Length: > 0 }) return Enumerable.Empty<string>();
{ var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances<IFileFormatHandler>();
return dropFileNames
.Where(filename => !string.IsNullOrEmpty(filename)) var supportedExtensions = fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadFromStream).ToList();
.Where(Path.HasExtension) return dropFileNames
.Where(filename => ImageHelper.StreamConverters.Keys.Contains(Path.GetExtension(filename).ToLowerInvariant().Substring(1))); .Where(filename => !string.IsNullOrEmpty(filename))
} .Where(Path.HasExtension)
.Where(filename => supportedExtensions.Contains(Path.GetExtension(filename)));
return Enumerable.Empty<string>();
} }
/// <summary> /// <summary>

View file

@ -24,9 +24,9 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Windows.Forms; using System.Windows.Forms;
using Dapplo.Windows.Common.Structs;
using Greenshot.Base.Core.Enums; using Greenshot.Base.Core.Enums;
using Greenshot.Base.IniFile; using Greenshot.Base.IniFile;
using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces;
@ -59,7 +59,7 @@ namespace Greenshot.Base.Core
[IniProperty("IEHotkey", Description = "Hotkey for starting the IE capture", DefaultValue = "Shift + Ctrl + PrintScreen")] [IniProperty("IEHotkey", Description = "Hotkey for starting the IE capture", DefaultValue = "Shift + Ctrl + PrintScreen")]
public string IEHotkey { get; set; } public string IEHotkey { get; set; }
[IniProperty("ClipboardHotkey", Description = "Hotkey for opening the clipboard contents into the editor")] [IniProperty("ClipboardHotkey", Description = "Hotkey for opening the clipboard contents into the editor", ExcludeIfNull = true)]
public string ClipboardHotkey { get; set; } public string ClipboardHotkey { get; set; }
[IniProperty("IsFirstLaunch", Description = "Is this the first time launch?", DefaultValue = "true")] [IniProperty("IsFirstLaunch", Description = "Is this the first time launch?", DefaultValue = "true")]
@ -322,17 +322,17 @@ namespace Greenshot.Base.Core
public bool ProcessEXIFOrientation { get; set; } public bool ProcessEXIFOrientation { get; set; }
[IniProperty("LastCapturedRegion", Description = "The last used region, for reuse in the capture last region")] [IniProperty("LastCapturedRegion", Description = "The last used region, for reuse in the capture last region")]
public Rectangle LastCapturedRegion { get; set; } public NativeRect LastCapturedRegion { get; set; }
[IniProperty("Win10BorderCrop", Description = "The capture is cropped with these settings, e.g. when you don't want to color around it -1,-1"), DefaultValue("0,0")] [IniProperty("Win10BorderCrop", Description = "The capture is cropped with these settings, e.g. when you don't want to color around it -1,-1"), DefaultValue("0,0")]
public Size Win10BorderCrop { get; set; } public NativeSize Win10BorderCrop { get; set; }
private Size _iconSize; private NativeSize _iconSize;
[IniProperty("BaseIconSize", [IniProperty("BaseIconSize",
Description = "Defines the base size of the icons (e.g. for the buttons in the editor), default value 16,16 and it's scaled to the current DPI", Description = "Defines the base size of the icons (e.g. for the buttons in the editor), default value 16,16 and it's scaled to the current DPI",
DefaultValue = "16,16")] DefaultValue = "16,16")]
public Size IconSize public NativeSize IconSize
{ {
get { return _iconSize; } get { return _iconSize; }
set set
@ -369,13 +369,11 @@ namespace Greenshot.Base.Core
} }
} }
} }
public Size ScaledIconSize => DpiHelper.ScaleWithCurrentDpi(_iconSize); [IniProperty("WebRequestTimeout", Description = "The connect timeout value for web requests, these are seconds", DefaultValue = "100")]
[IniProperty("WebRequestTimeout", Description = "The connect timeout value for webrequets, these are seconds", DefaultValue = "100")]
public int WebRequestTimeout { get; set; } public int WebRequestTimeout { get; set; }
[IniProperty("WebRequestReadWriteTimeout", Description = "The read/write timeout value for webrequets, these are seconds", DefaultValue = "100")] [IniProperty("WebRequestReadWriteTimeout", Description = "The read/write timeout value for web requests, these are seconds", DefaultValue = "100")]
public int WebRequestReadWriteTimeout { get; set; } public int WebRequestReadWriteTimeout { get; set; }
public bool UseLargeIcons => IconSize.Width >= 32 || IconSize.Height >= 32; public bool UseLargeIcons => IconSize.Width >= 32 || IconSize.Height >= 32;
@ -390,89 +388,64 @@ namespace Greenshot.Base.Core
return ExperimentalFeatures != null && ExperimentalFeatures.Contains(experimentalFeature); return ExperimentalFeatures != null && ExperimentalFeatures.Contains(experimentalFeature);
} }
private string CreateOutputFilePath()
{
if (IniConfig.IsPortable)
{
string pafOutputFilePath = Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots");
if (!Directory.Exists(pafOutputFilePath))
{
try
{
Directory.CreateDirectory(pafOutputFilePath);
return pafOutputFilePath;
}
catch (Exception ex)
{
// Problem creating directory, fallback to Desktop
LOG.Warn(ex);
}
}
else
{
return pafOutputFilePath;
}
}
return Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
}
/// <summary> /// <summary>
/// Supply values we can't put as defaults /// Supply values we can't put as defaults
/// </summary> /// </summary>
/// <param name="property">The property to return a default for</param> /// <param name="property">The property to return a default for</param>
/// <returns>object with the default value for the supplied property</returns> /// <returns>object with the default value for the supplied property</returns>
public override object GetDefault(string property) public override object GetDefault(string property) =>
{ property switch
switch (property)
{ {
case nameof(ExcludePlugins): nameof(ExcludePlugins) => new List<string>(),
case nameof(IncludePlugins): nameof(IncludePlugins) => new List<string>(),
return new List<string>(); nameof(OutputFileAsFullpath) => IniConfig.IsPortable ? Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots\dummy.png") : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "dummy.png"),
case nameof(OutputFileAsFullpath): nameof(OutputFilePath) => CreateOutputFilePath(),
if (IniConfig.IsPortable) nameof(DWMBackgroundColor) => Color.Transparent,
{ nameof(ActiveTitleFixes) => new List<string> {
return Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots\dummy.png"); "Firefox",
} "IE",
"Chrome"
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "dummy.png"); },
case nameof(OutputFilePath): nameof(TitleFixMatcher) => new Dictionary<string, string> {
if (IniConfig.IsPortable) { "Firefox", " - Mozilla Firefox.*" },
{ { "IE", " - (Microsoft|Windows) Internet Explorer.*" },
string pafOutputFilePath = Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots"); { "Chrome", " - Google Chrome.*" }
if (!Directory.Exists(pafOutputFilePath)) },
{ nameof(TitleFixReplacer) => new Dictionary<string, string> {
try { "Firefox", string.Empty },
{ { "IE", string.Empty },
Directory.CreateDirectory(pafOutputFilePath); { "Chrome", string.Empty }
return pafOutputFilePath; },
} _ => null
catch (Exception ex) };
{
LOG.Warn(ex);
// Problem creating directory, fallback to Desktop
}
}
else
{
return pafOutputFilePath;
}
}
return Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
case nameof(DWMBackgroundColor):
return Color.Transparent;
case nameof(ActiveTitleFixes):
return new List<string>
{
"Firefox",
"IE",
"Chrome"
};
case nameof(TitleFixMatcher):
return new Dictionary<string, string>
{
{
"Firefox", " - Mozilla Firefox.*"
},
{
"IE", " - (Microsoft|Windows) Internet Explorer.*"
},
{
"Chrome", " - Google Chrome.*"
}
};
case nameof(TitleFixReplacer):
return new Dictionary<string, string>
{
{
"Firefox", string.Empty
},
{
"IE", string.Empty
},
{
"Chrome", string.Empty
}
};
}
return null;
}
/// <summary> /// <summary>
/// This method will be called before converting the property, making to possible to correct a certain value /// This method will be called before converting the property, making to possible to correct a certain value
/// Can be used when migration is needed /// Can be used when migration is needed
@ -542,8 +515,9 @@ namespace Greenshot.Base.Core
OutputFileAutoReduceColors = false; OutputFileAutoReduceColors = false;
} }
bool isUpgradeFrom12 = LastSaveWithVersion?.StartsWith("1.2") ?? false;
// Fix for excessive feed checking // Fix for excessive feed checking
if (UpdateCheckInterval != 0 && UpdateCheckInterval <= 7 && LastSaveWithVersion.StartsWith("1.2")) if (UpdateCheckInterval != 0 && UpdateCheckInterval <= 7 && isUpgradeFrom12)
{ {
UpdateCheckInterval = 14; UpdateCheckInterval = 14;
} }

View file

@ -1,134 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using Greenshot.Base.UnmanagedHelpers;
namespace Greenshot.Base.Core
{
/// <summary>
/// Though Greenshot implements the specs for the DIB image format,
/// it seems to cause a lot of issues when using the clipboard.
/// There is some research done about the DIB on the clipboard, this code is based upon the information
/// <a href="https://stackoverflow.com/questions/44177115/copying-from-and-to-clipboard-loses-image-transparency">here</a>
/// </summary>
internal static class DibHelper
{
private const double DpiToPelsPerMeter = 39.3701;
/// <summary>
/// Converts the Bitmap to a Device Independent Bitmap format of type BITFIELDS.
/// </summary>
/// <param name="sourceBitmap">Bitmap to convert to DIB</param>
/// <returns>byte{} with the image converted to DIB</returns>
public static byte[] ConvertToDib(this Bitmap sourceBitmap)
{
if (sourceBitmap == null) throw new ArgumentNullException(nameof(sourceBitmap));
var area = new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height);
// If the supplied format doesn't match 32bpp, we need to convert it first, and dispose the new bitmap afterwards
bool needsDisposal = false;
if (sourceBitmap.PixelFormat != PixelFormat.Format32bppArgb)
{
needsDisposal = true;
var clonedImage = ImageHelper.CreateEmptyLike(sourceBitmap, Color.Transparent, PixelFormat.Format32bppArgb);
using (var graphics = Graphics.FromImage(clonedImage))
{
graphics.DrawImage(sourceBitmap, area);
}
sourceBitmap = clonedImage;
}
// All the pixels take this many bytes:
var bitmapSize = 4 * sourceBitmap.Width * sourceBitmap.Height;
// The bitmap info hear takes this many bytes:
var bitmapInfoHeaderSize = Marshal.SizeOf(typeof(BITMAPINFOHEADER));
// The bitmap info size is the header + 3 RGBQUADs
var bitmapInfoSize = bitmapInfoHeaderSize + 3 * Marshal.SizeOf(typeof(RGBQUAD));
// Create a byte [] to contain the complete DIB (with .NET 5 and upwards, we could write the pixels directly to a stream)
var fullBmpBytes = new byte[bitmapInfoSize + bitmapSize];
// Get a span for this, this simplifies the code a bit
var fullBmpSpan = fullBmpBytes.AsSpan();
// Cast the span to be of type BITMAPINFOHEADER so we can assign values
// TODO: in .NET 6 we could do a AsRef, and even write to a stream directly
var bitmapInfoHeader = MemoryMarshal.Cast<byte, BITMAPINFOHEADER>(fullBmpSpan);
// Fill up the bitmap info header
bitmapInfoHeader[0].biSize = (uint)bitmapInfoHeaderSize;
bitmapInfoHeader[0].biWidth = sourceBitmap.Width;
bitmapInfoHeader[0].biHeight = sourceBitmap.Height;
bitmapInfoHeader[0].biPlanes = 1;
bitmapInfoHeader[0].biBitCount = 32;
bitmapInfoHeader[0].biCompression = BI_COMPRESSION.BI_BITFIELDS;
bitmapInfoHeader[0].biSizeImage = (uint)bitmapSize;
bitmapInfoHeader[0].biXPelsPerMeter = (int)(sourceBitmap.HorizontalResolution * DpiToPelsPerMeter);
bitmapInfoHeader[0].biYPelsPerMeter = (int)(sourceBitmap.VerticalResolution * DpiToPelsPerMeter);
// Specify the color masks applied to the Int32 pixel value to get the R, G and B values.
var rgbQuads = MemoryMarshal.Cast<byte, RGBQUAD>(fullBmpSpan.Slice(Marshal.SizeOf(typeof(BITMAPINFOHEADER))));
rgbQuads[0].rgbRed = 255;
rgbQuads[1].rgbGreen = 255;
rgbQuads[2].rgbBlue = 255;
// Now copy the lines, in reverse (bmp is upside down) to the byte array
var sourceBitmapData = sourceBitmap.LockBits(area, ImageLockMode.ReadOnly, sourceBitmap.PixelFormat);
try
{
// Get a span for the real bitmap bytes, which starts after the bitmapinfo (header + 3xRGBQuad)
var bitmapSpan = fullBmpSpan.Slice(bitmapInfoSize);
// Make sure we also have a span to copy from, by taking the pointer from the locked bitmap
Span<byte> bitmapSourceSpan;
unsafe
{
bitmapSourceSpan = new Span<byte>(sourceBitmapData.Scan0.ToPointer(), sourceBitmapData.Stride * sourceBitmapData.Height);
}
// Loop over all the bitmap lines
for (int destinationY = 0; destinationY < sourceBitmap.Height; destinationY++)
{
// Calculate the y coordinate for the bottom up. (flipping the image)
var sourceY = (sourceBitmap.Height - 1) - destinationY;
// Make a Span for the source bitmap pixels
var sourceLine = bitmapSourceSpan.Slice(sourceBitmapData.Stride * sourceY, 4 * sourceBitmap.Width);
// Make a Span for the destination dib pixels
var destinationLine = bitmapSpan.Slice(destinationY * 4 * sourceBitmap.Width);
sourceLine.CopyTo(destinationLine);
}
}
finally
{
sourceBitmap.UnlockBits(sourceBitmapData);
}
// If we created a new bitmap, we need to dispose this
if (needsDisposal)
{
sourceBitmap.Dispose();
}
return fullBmpBytes;
}
}
}

View file

@ -1,203 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using Greenshot.Base.Core.Enums;
using Greenshot.Base.UnmanagedHelpers;
using Greenshot.Base.UnmanagedHelpers.Enums;
using Greenshot.Base.UnmanagedHelpers.Structs;
namespace Greenshot.Base.Core
{
/// <summary>
/// This handles DPI changes see
/// <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266.aspx">Writing DPI-Aware Desktop and Win32 Applications</a>
/// </summary>
public static class DpiHelper
{
/// <summary>
/// This is the default DPI for the screen
/// </summary>
public const uint DefaultScreenDpi = 96;
/// <summary>
/// Retrieve the current DPI for the UI element which is related to this DpiHandler
/// </summary>
public static uint Dpi { get; private set; } = WindowsVersion.IsWindows10OrLater ? GetDpiForSystem() : DefaultScreenDpi;
/// <summary>
/// Calculate a DPI scale factor
/// </summary>
/// <param name="dpi">uint</param>
/// <returns>double</returns>
public static float DpiScaleFactor(uint dpi)
{
if (dpi == 0)
{
dpi = Dpi;
}
return (float) dpi / DefaultScreenDpi;
}
/// <summary>
/// Scale the supplied number according to the supplied dpi
/// </summary>
/// <param name="someNumber">double with e.g. the width 16 for 16x16 images</param>
/// <param name="dpi">current dpi, normal is 96.</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>double with the scaled number</returns>
public static float ScaleWithDpi(float someNumber, uint dpi, Func<float, float> scaleModifier = null)
{
var dpiScaleFactor = DpiScaleFactor(dpi);
if (scaleModifier != null)
{
dpiScaleFactor = scaleModifier(dpiScaleFactor);
}
return dpiScaleFactor * someNumber;
}
/// <summary>
/// Scale the supplied Size according to the supplied dpi
/// </summary>
/// <param name="size">Size to resize</param>
/// <param name="dpi">current dpi, normal is 96.</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>NativeSize scaled</returns>
public static Size ScaleWithDpi(Size size, uint dpi, Func<float, float> scaleModifier = null)
{
var dpiScaleFactor = DpiScaleFactor(dpi);
if (scaleModifier != null)
{
dpiScaleFactor = scaleModifier(dpiScaleFactor);
}
return new Size((int) (dpiScaleFactor * size.Width), (int) (dpiScaleFactor * size.Height));
}
/// <summary>
/// Scale the supplied NativeSize to the current dpi
/// </summary>
/// <param name="size">NativeSize to scale</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>NativeSize scaled</returns>
public static Size ScaleWithCurrentDpi(Size size, Func<float, float> scaleModifier = null)
{
return ScaleWithDpi(size, Dpi, scaleModifier);
}
/// <summary>
/// Return the DPI for the screen which the location is located on
/// </summary>
/// <param name="location">POINT</param>
/// <returns>uint</returns>
public static uint GetDpi(POINT location)
{
if (!WindowsVersion.IsWindows81OrLater)
{
return DefaultScreenDpi;
}
RECT rect = new RECT(location.X, location.Y, 1, 1);
IntPtr hMonitor = User32.MonitorFromRect(ref rect, User32.MONITOR_DEFAULTTONEAREST);
var result = GetDpiForMonitor(hMonitor, MonitorDpiType.EffectiveDpi, out var dpiX, out var dpiY);
if (result.Succeeded())
{
return dpiX;
}
return DefaultScreenDpi;
}
/// <summary>
/// Retrieve the DPI value for the supplied window handle
/// </summary>
/// <param name="hWnd">IntPtr</param>
/// <returns>dpi value</returns>
public static uint GetDpi(IntPtr hWnd)
{
if (!User32.IsWindow(hWnd))
{
return DefaultScreenDpi;
}
// Use the easiest method, but this only works for Windows 10
if (WindowsVersion.IsWindows10OrLater)
{
return GetDpiForWindow(hWnd);
}
// Use the second easiest method, but this only works for Windows 8.1 or later
if (WindowsVersion.IsWindows81OrLater)
{
var hMonitor = User32.MonitorFromWindow(hWnd, MonitorFrom.DefaultToNearest);
// ReSharper disable once UnusedVariable
var result = GetDpiForMonitor(hMonitor, MonitorDpiType.EffectiveDpi, out var dpiX, out var dpiY);
if (result.Succeeded())
{
return dpiX;
}
}
// Fallback to the global DPI settings
using var hdc = SafeWindowDcHandle.FromWindow(hWnd);
if (hdc == null)
{
return DefaultScreenDpi;
}
return (uint) GDI32.GetDeviceCaps(hdc, DeviceCaps.LOGPIXELSX);
}
/// <summary>
/// See more at <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/mt748624(v=vs.85).aspx">GetDpiForWindow function</a>
/// Returns the dots per inch (dpi) value for the associated window.
/// </summary>
/// <param name="hWnd">IntPtr</param>
/// <returns>uint with dpi</returns>
[DllImport("user32.dll")]
private static extern uint GetDpiForWindow(IntPtr hWnd);
/// <summary>
/// See
/// <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx">GetDpiForMonitor function</a>
/// Queries the dots per inch (dpi) of a display.
/// </summary>
/// <param name="hMonitor">IntPtr</param>
/// <param name="dpiType">MonitorDpiType</param>
/// <param name="dpiX">out int for the horizontal dpi</param>
/// <param name="dpiY">out int for the vertical dpi</param>
/// <returns>true if all okay</returns>
[DllImport("shcore.dll", SetLastError = true)]
private static extern HResult GetDpiForMonitor(IntPtr hMonitor, MonitorDpiType dpiType, out uint dpiX, out uint dpiY);
/// <summary>
/// See <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/mt748623(v=vs.85).aspx">GetDpiForSystem function</a>
/// Returns the system DPI.
/// </summary>
/// <returns>uint with the system DPI</returns>
[DllImport("user32.dll")]
private static extern uint GetDpiForSystem();
}
}

View file

@ -3,6 +3,8 @@ using System.ComponentModel;
using System.Drawing; using System.Drawing;
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;
using Dapplo.Windows.Common.Extensions;
using Dapplo.Windows.Common.Structs;
using Greenshot.Base.Effects; using Greenshot.Base.Effects;
namespace Greenshot.Base.Core namespace Greenshot.Base.Core
@ -136,16 +138,16 @@ namespace Greenshot.Base.Core
break; break;
case "ShadowOffset": case "ShadowOffset":
Point shadowOffset = new Point(); NativePoint shadowOffset = new NativePoint();
string[] coordinates = pair[1].Split(','); string[] coordinates = pair[1].Split(',');
if (int.TryParse(coordinates[0], out var shadowOffsetX)) if (int.TryParse(coordinates[0], out var shadowOffsetX))
{ {
shadowOffset.X = shadowOffsetX; shadowOffset = shadowOffset.ChangeX(shadowOffsetX);
} }
if (int.TryParse(coordinates[1], out var shadowOffsetY)) if (int.TryParse(coordinates[1], out var shadowOffsetY))
{ {
shadowOffset.Y = shadowOffsetY; shadowOffset = shadowOffset.ChangeY(shadowOffsetY);
} }
effect.ShadowOffset = shadowOffset; effect.ShadowOffset = shadowOffset;

View file

@ -19,17 +19,18 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
using System; namespace Greenshot.Base.Core.Enums
using System.Runtime.InteropServices;
namespace Greenshot.Base.UnmanagedHelpers.Structs
{ {
[StructLayout(LayoutKind.Sequential)] internal enum ExifOrientations : byte
public struct CursorInfo
{ {
public int cbSize; Unknown = 0,
public int flags; TopLeft = 1,
public IntPtr hCursor; TopRight = 2,
public POINT ptScreenPos; BottomRight = 3,
BottomLeft = 4,
LeftTop = 5,
RightTop = 6,
RightBottom = 7,
LeftBottom = 8,
} }
} }

View file

@ -1,33 +0,0 @@
// Greenshot - a free and open source screenshot tool
// Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
//
// For more information see: https://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
using System.Diagnostics.CodeAnalysis;
namespace Greenshot.Base.Core.Enums
{
/// <summary>
/// The HRESULT represents Windows error codes
/// See <a href="https://en.wikipedia.org/wiki/HRESULT">wikipedia</a>
/// </summary>
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum HResult
{
S_OK = 0,
}
}

View file

@ -1,41 +0,0 @@
// Copyright (c) Dapplo and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
namespace Greenshot.Base.Core.Enums
{
/// <summary>
/// See
/// <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dn280511(v=vs.85).aspx">
/// MONITOR_DPI_TYPE
/// enumeration
/// </a>
/// </summary>
[Flags]
public enum MonitorDpiType
{
/// <summary>
/// The effective DPI.
/// This value should be used when determining the correct scale factor for scaling UI elements.
/// This incorporates the scale factor set by the user for this specific display.
/// </summary>
EffectiveDpi = 0,
/// <summary>
/// The angular DPI.
/// This DPI ensures rendering at a compliant angular resolution on the screen.
/// This does not include the scale factor set by the user for this specific display
/// </summary>
AngularDpi = 1,
/// <summary>
/// The raw DPI.
/// This value is the linear DPI of the screen as measured on the screen itself.
/// Use this value when you want to read the pixel density and not the recommended scaling setting.
/// This does not include the scale factor set by the user for this specific display and is not guaranteed to be a
/// supported DPI value.
/// </summary>
RawDpi = 2
}
}

View file

@ -1,31 +0,0 @@
// Copyright (c) Dapplo and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
namespace Greenshot.Base.Core.Enums
{
/// <summary>
/// Flags for the MonitorFromRect / MonitorFromWindow "flags" field
/// see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd145063(v=vs.85).aspx">MonitorFromRect function</a>
/// or see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd145064(v=vs.85).aspx">MonitorFromWindow function</a>
/// </summary>
[Flags]
public enum MonitorFrom : uint
{
/// <summary>
/// Returns a handle to the display monitor that is nearest to the rectangle.
/// </summary>
DefaultToNearest = 0,
/// <summary>
/// Returns NULL. (why??)
/// </summary>
DefaultToNull = 1,
/// <summary>
/// Returns a handle to the primary display monitor.
/// </summary>
DefaultToPrimary = 2
}
}

View file

@ -31,6 +31,7 @@ namespace Greenshot.Base.Core.Enums
jpg, jpg,
png, png,
tiff, tiff,
jxr,
greenshot, greenshot,
ico ico
} }

View file

@ -1,38 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
namespace Greenshot.Base.Core.Enums
{
[Flags]
public enum PrintWindowFlags : uint
{
/// <summary>Render the entire window.</summary>
PW_ENTIREWINDOW = 0,
/// <summary>Only the client area of the window is copied to hdcBlt. By default, the entire window is copied.</summary>
PW_CLIENTONLY = 1,
/// <summary>Undocumented</summary>
PW_RENDERFULLCONTENT = 0x00000002,
}
}

View file

@ -23,8 +23,12 @@ using System;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using Dapplo.Windows.Kernel32;
using Dapplo.Windows.Kernel32.Enums;
using Dapplo.Windows.Kernel32.Structs;
using Dapplo.Windows.User32;
using Dapplo.Windows.Common.Extensions;
using Greenshot.Base.IniFile; using Greenshot.Base.IniFile;
using Greenshot.Base.UnmanagedHelpers;
using Microsoft.Win32; using Microsoft.Win32;
namespace Greenshot.Base.Core namespace Greenshot.Base.Core
@ -163,7 +167,7 @@ namespace Greenshot.Base.Core
} }
// Get some important information for fixing GDI related Problems // Get some important information for fixing GDI related Problems
environment.AppendFormat("GDI object count: {0}", User32.GetGuiResourcesGDICount()); environment.AppendFormat("GDI object count: {0}", User32Api.GetGuiResourcesGdiCount());
if (newline) if (newline)
{ {
environment.AppendLine(); environment.AppendLine();
@ -173,7 +177,7 @@ namespace Greenshot.Base.Core
environment.Append(", "); environment.Append(", ");
} }
environment.AppendFormat("User object count: {0}", User32.GetGuiResourcesUserCount()); environment.AppendFormat("User object count: {0}", User32Api.GetGuiResourcesUserCount());
} }
else else
{ {
@ -291,33 +295,33 @@ namespace Greenshot.Base.Core
string edition = string.Empty; string edition = string.Empty;
OperatingSystem osVersion = Environment.OSVersion; OperatingSystem osVersion = Environment.OSVersion;
OSVERSIONINFOEX osVersionInfo = OSVERSIONINFOEX.Create(); var osVersionInfo = OsVersionInfoEx.Create();
if (GetVersionEx(ref osVersionInfo)) if (Kernel32Api.GetVersionEx(ref osVersionInfo))
{ {
int majorVersion = osVersion.Version.Major; int majorVersion = osVersion.Version.Major;
int minorVersion = osVersion.Version.Minor; int minorVersion = osVersion.Version.Minor;
byte productType = osVersionInfo.ProductType; var productType = osVersionInfo.ProductType;
ushort suiteMask = osVersionInfo.SuiteMask; var suiteMask = osVersionInfo.SuiteMask;
if (majorVersion == 4) if (majorVersion == 4)
{ {
if (productType == VER_NT_WORKSTATION) if (productType == WindowsProductTypes.VER_NT_WORKSTATION)
{ {
// Windows NT 4.0 Workstation // Windows NT 4.0 Workstation
edition = "Workstation"; edition = "Workstation";
} }
else if (productType == VER_NT_SERVER) else if (productType == WindowsProductTypes.VER_NT_SERVER)
{ {
edition = (suiteMask & VER_SUITE_ENTERPRISE) != 0 ? "Enterprise Server" : "Standard Server"; edition = (suiteMask & WindowsSuites.Enterprise) != 0 ? "Enterprise Server" : "Standard Server";
} }
} }
else if (majorVersion == 5) else if (majorVersion == 5)
{ {
if (productType == VER_NT_WORKSTATION) if (productType == WindowsProductTypes.VER_NT_WORKSTATION)
{ {
if ((suiteMask & VER_SUITE_PERSONAL) != 0) if ((suiteMask & WindowsSuites.Personal) != 0)
{ {
// Windows XP Home Edition // Windows XP Home Edition
edition = "Home"; edition = "Home";
@ -328,16 +332,16 @@ namespace Greenshot.Base.Core
edition = "Professional"; edition = "Professional";
} }
} }
else if (productType == VER_NT_SERVER) else if (productType == WindowsProductTypes.VER_NT_SERVER)
{ {
if (minorVersion == 0) if (minorVersion == 0)
{ {
if ((suiteMask & VER_SUITE_DATACENTER) != 0) if ((suiteMask & WindowsSuites.DataCenter) != 0)
{ {
// Windows 2000 Datacenter Server // Windows 2000 Datacenter Server
edition = "Datacenter Server"; edition = "Datacenter Server";
} }
else if ((suiteMask & VER_SUITE_ENTERPRISE) != 0) else if ((suiteMask & WindowsSuites.Enterprise) != 0)
{ {
// Windows 2000 Advanced Server // Windows 2000 Advanced Server
edition = "Advanced Server"; edition = "Advanced Server";
@ -350,17 +354,17 @@ namespace Greenshot.Base.Core
} }
else else
{ {
if ((suiteMask & VER_SUITE_DATACENTER) != 0) if ((suiteMask & WindowsSuites.DataCenter) != 0)
{ {
// Windows Server 2003 Datacenter Edition // Windows Server 2003 Datacenter Edition
edition = "Datacenter"; edition = "Datacenter";
} }
else if ((suiteMask & VER_SUITE_ENTERPRISE) != 0) else if ((suiteMask & WindowsSuites.Enterprise) != 0)
{ {
// Windows Server 2003 Enterprise Edition // Windows Server 2003 Enterprise Edition
edition = "Enterprise"; edition = "Enterprise";
} }
else if ((suiteMask & VER_SUITE_BLADE) != 0) else if ((suiteMask & WindowsSuites.Blade) != 0)
{ {
// Windows Server 2003 Web Edition // Windows Server 2003 Web Edition
edition = "Web Edition"; edition = "Web Edition";
@ -376,122 +380,9 @@ namespace Greenshot.Base.Core
else if (majorVersion == 6) else if (majorVersion == 6)
{ {
if (GetProductInfo(majorVersion, minorVersion, osVersionInfo.ServicePackMajor, osVersionInfo.ServicePackMinor, out var ed)) if (Kernel32Api.GetProductInfo(majorVersion, minorVersion, osVersionInfo.ServicePackMajor, osVersionInfo.ServicePackMinor, out var windowsProduct))
{ {
switch (ed) edition = windowsProduct.GetEnumDescription();
{
case PRODUCT_BUSINESS:
edition = "Business";
break;
case PRODUCT_BUSINESS_N:
edition = "Business N";
break;
case PRODUCT_CLUSTER_SERVER:
edition = "HPC Edition";
break;
case PRODUCT_DATACENTER_SERVER:
edition = "Datacenter Server";
break;
case PRODUCT_DATACENTER_SERVER_CORE:
edition = "Datacenter Server (core installation)";
break;
case PRODUCT_ENTERPRISE:
edition = "Enterprise";
break;
case PRODUCT_ENTERPRISE_N:
edition = "Enterprise N";
break;
case PRODUCT_ENTERPRISE_SERVER:
edition = "Enterprise Server";
break;
case PRODUCT_ENTERPRISE_SERVER_CORE:
edition = "Enterprise Server (core installation)";
break;
case PRODUCT_ENTERPRISE_SERVER_CORE_V:
edition = "Enterprise Server without Hyper-V (core installation)";
break;
case PRODUCT_ENTERPRISE_SERVER_IA64:
edition = "Enterprise Server for Itanium-based Systems";
break;
case PRODUCT_ENTERPRISE_SERVER_V:
edition = "Enterprise Server without Hyper-V";
break;
case PRODUCT_HOME_BASIC:
edition = "Home Basic";
break;
case PRODUCT_HOME_BASIC_N:
edition = "Home Basic N";
break;
case PRODUCT_HOME_PREMIUM:
edition = "Home Premium";
break;
case PRODUCT_HOME_PREMIUM_N:
edition = "Home Premium N";
break;
case PRODUCT_HYPERV:
edition = "Microsoft Hyper-V Server";
break;
case PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT:
edition = "Windows Essential Business Management Server";
break;
case PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING:
edition = "Windows Essential Business Messaging Server";
break;
case PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY:
edition = "Windows Essential Business Security Server";
break;
case PRODUCT_SERVER_FOR_SMALLBUSINESS:
edition = "Windows Essential Server Solutions";
break;
case PRODUCT_SERVER_FOR_SMALLBUSINESS_V:
edition = "Windows Essential Server Solutions without Hyper-V";
break;
case PRODUCT_SMALLBUSINESS_SERVER:
edition = "Windows Small Business Server";
break;
case PRODUCT_STANDARD_SERVER:
edition = "Standard Server";
break;
case PRODUCT_STANDARD_SERVER_CORE:
edition = "Standard Server (core installation)";
break;
case PRODUCT_STANDARD_SERVER_CORE_V:
edition = "Standard Server without Hyper-V (core installation)";
break;
case PRODUCT_STANDARD_SERVER_V:
edition = "Standard Server without Hyper-V";
break;
case PRODUCT_STARTER:
edition = "Starter";
break;
case PRODUCT_STORAGE_ENTERPRISE_SERVER:
edition = "Enterprise Storage Server";
break;
case PRODUCT_STORAGE_EXPRESS_SERVER:
edition = "Express Storage Server";
break;
case PRODUCT_STORAGE_STANDARD_SERVER:
edition = "Standard Storage Server";
break;
case PRODUCT_STORAGE_WORKGROUP_SERVER:
edition = "Workgroup Storage Server";
break;
case PRODUCT_UNDEFINED:
edition = "Unknown product";
break;
case PRODUCT_ULTIMATE:
edition = "Ultimate";
break;
case PRODUCT_ULTIMATE_N:
edition = "Ultimate N";
break;
case PRODUCT_WEB_SERVER:
edition = "Web Server";
break;
case PRODUCT_WEB_SERVER_CORE:
edition = "Web Server (core installation)";
break;
}
} }
} }
} }
@ -518,13 +409,13 @@ namespace Greenshot.Base.Core
string name = "unknown"; string name = "unknown";
OperatingSystem osVersion = Environment.OSVersion; OperatingSystem osVersion = Environment.OSVersion;
OSVERSIONINFOEX osVersionInfo = OSVERSIONINFOEX.Create(); var osVersionInfo = OsVersionInfoEx.Create();
if (GetVersionEx(ref osVersionInfo)) if (Kernel32Api.GetVersionEx(ref osVersionInfo))
{ {
int majorVersion = osVersion.Version.Major; int majorVersion = osVersion.Version.Major;
int minorVersion = osVersion.Version.Minor; int minorVersion = osVersion.Version.Minor;
byte productType = osVersionInfo.ProductType; var productType = osVersionInfo.ProductType;
ushort suiteMask = osVersionInfo.SuiteMask; var suiteMask = osVersionInfo.SuiteMask;
switch (osVersion.Platform) switch (osVersion.Platform)
{ {
case PlatformID.Win32Windows: case PlatformID.Win32Windows:
@ -563,10 +454,10 @@ namespace Greenshot.Base.Core
case 4: case 4:
switch (productType) switch (productType)
{ {
case 1: case WindowsProductTypes.VER_NT_WORKSTATION:
name = "Windows NT 4.0"; name = "Windows NT 4.0";
break; break;
case 3: case WindowsProductTypes.VER_NT_SERVER:
name = "Windows NT 4.0 Server"; name = "Windows NT 4.0 Server";
break; break;
} }
@ -581,18 +472,18 @@ namespace Greenshot.Base.Core
case 1: case 1:
name = suiteMask switch name = suiteMask switch
{ {
0x0200 => "Windows XP Professional", WindowsSuites.Personal => "Windows XP Professional",
_ => "Windows XP" _ => "Windows XP"
}; };
break; break;
case 2: case 2:
name = suiteMask switch name = suiteMask switch
{ {
0x0200 => "Windows XP Professional x64", WindowsSuites.Personal => "Windows XP Professional x64",
0x0002 => "Windows Server 2003 Enterprise", WindowsSuites.Enterprise => "Windows Server 2003 Enterprise",
0x0080 => "Windows Server 2003 Data Center", WindowsSuites.DataCenter => "Windows Server 2003 Data Center",
0x0400 => "Windows Server 2003 Web Edition", WindowsSuites.Blade => "Windows Server 2003 Web Edition",
0x8000 => "Windows Home Server", WindowsSuites.WHServer => "Windows Home Server",
_ => "Windows Server 2003" _ => "Windows Server 2003"
}; };
break; break;
@ -605,14 +496,14 @@ namespace Greenshot.Base.Core
case 0: case 0:
name = productType switch name = productType switch
{ {
3 => "Windows Server 2008", WindowsProductTypes.VER_NT_SERVER => "Windows Server 2008",
_ => "Windows Vista" _ => "Windows Vista"
}; };
break; break;
case 1: case 1:
name = productType switch name = productType switch
{ {
3 => "Windows Server 2008 R2", WindowsProductTypes.VER_NT_SERVER => "Windows Server 2008 R2",
_ => "Windows 7" _ => "Windows 7"
}; };
break; break;
@ -640,134 +531,6 @@ namespace Greenshot.Base.Core
} }
} }
[DllImport("Kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetProductInfo(
int osMajorVersion,
int osMinorVersion,
int spMajorVersion,
int spMinorVersion,
out int edition);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetVersionEx(ref OSVERSIONINFOEX osVersionInfo);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private unsafe struct OSVERSIONINFOEX
{
/// <summary>
/// The size of this data structure, in bytes. Set this member to sizeof(OSVERSIONINFOEX).
/// </summary>
private int _dwOSVersionInfoSize;
private readonly int _dwMajorVersion;
private readonly int _dwMinorVersion;
private readonly int _dwBuildNumber;
private readonly int _dwPlatformId;
private fixed char _szCSDVersion[128];
private readonly short _wServicePackMajor;
private readonly short _wServicePackMinor;
private readonly ushort _wSuiteMask;
private readonly byte _wProductType;
private readonly byte _wReserved;
/// A null-terminated string, such as "Service Pack 3", that indicates the latest Service Pack installed on the system.
/// If no Service Pack has been installed, the string is empty.
/// </summary>
public string ServicePackVersion
{
get
{
fixed (char* servicePackVersion = _szCSDVersion)
{
return new string(servicePackVersion);
}
}
}
/// <summary>
/// The major version number of the latest Service Pack installed on the system. For example, for Service Pack 3, the
/// major version number is 3.
/// If no Service Pack has been installed, the value is zero.
/// </summary>
public short ServicePackMajor => _wServicePackMajor;
/// <summary>
/// The minor version number of the latest Service Pack installed on the system. For example, for Service Pack 3, the
/// minor version number is 0.
/// </summary>
public short ServicePackMinor => _wServicePackMinor;
/// <summary>
/// A bit mask that identifies the product suites available on the system. This member can be a combination of the
/// following values.
/// </summary>
public ushort SuiteMask => _wSuiteMask;
/// <summary>
/// Any additional information about the system.
/// </summary>
public byte ProductType => _wProductType;
/// <summary>
/// Factory for an empty OsVersionInfoEx
/// </summary>
/// <returns>OSVERSIONINFOEX</returns>
public static OSVERSIONINFOEX Create()
{
return new OSVERSIONINFOEX
{
_dwOSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEX))
};
}
}
private const int PRODUCT_UNDEFINED = 0x00000000;
private const int PRODUCT_ULTIMATE = 0x00000001;
private const int PRODUCT_HOME_BASIC = 0x00000002;
private const int PRODUCT_HOME_PREMIUM = 0x00000003;
private const int PRODUCT_ENTERPRISE = 0x00000004;
private const int PRODUCT_HOME_BASIC_N = 0x00000005;
private const int PRODUCT_BUSINESS = 0x00000006;
private const int PRODUCT_STANDARD_SERVER = 0x00000007;
private const int PRODUCT_DATACENTER_SERVER = 0x00000008;
private const int PRODUCT_SMALLBUSINESS_SERVER = 0x00000009;
private const int PRODUCT_ENTERPRISE_SERVER = 0x0000000A;
private const int PRODUCT_STARTER = 0x0000000B;
private const int PRODUCT_DATACENTER_SERVER_CORE = 0x0000000C;
private const int PRODUCT_STANDARD_SERVER_CORE = 0x0000000D;
private const int PRODUCT_ENTERPRISE_SERVER_CORE = 0x0000000E;
private const int PRODUCT_ENTERPRISE_SERVER_IA64 = 0x0000000F;
private const int PRODUCT_BUSINESS_N = 0x00000010;
private const int PRODUCT_WEB_SERVER = 0x00000011;
private const int PRODUCT_CLUSTER_SERVER = 0x00000012;
private const int PRODUCT_STORAGE_EXPRESS_SERVER = 0x00000014;
private const int PRODUCT_STORAGE_STANDARD_SERVER = 0x00000015;
private const int PRODUCT_STORAGE_WORKGROUP_SERVER = 0x00000016;
private const int PRODUCT_STORAGE_ENTERPRISE_SERVER = 0x00000017;
private const int PRODUCT_SERVER_FOR_SMALLBUSINESS = 0x00000018;
private const int PRODUCT_HOME_PREMIUM_N = 0x0000001A;
private const int PRODUCT_ENTERPRISE_N = 0x0000001B;
private const int PRODUCT_ULTIMATE_N = 0x0000001C;
private const int PRODUCT_WEB_SERVER_CORE = 0x0000001D;
private const int PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT = 0x0000001E;
private const int PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY = 0x0000001F;
private const int PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING = 0x00000020;
private const int PRODUCT_SERVER_FOR_SMALLBUSINESS_V = 0x00000023;
private const int PRODUCT_STANDARD_SERVER_V = 0x00000024;
private const int PRODUCT_ENTERPRISE_SERVER_V = 0x00000026;
private const int PRODUCT_STANDARD_SERVER_CORE_V = 0x00000028;
private const int PRODUCT_ENTERPRISE_SERVER_CORE_V = 0x00000029;
private const int PRODUCT_HYPERV = 0x0000002A;
private const int VER_NT_WORKSTATION = 1;
private const int VER_NT_SERVER = 3;
private const int VER_SUITE_ENTERPRISE = 2;
private const int VER_SUITE_DATACENTER = 128;
private const int VER_SUITE_PERSONAL = 512;
private const int VER_SUITE_BLADE = 1024;
/// <summary> /// <summary>
/// Gets the service pack information of the operating system running on this computer. /// Gets the service pack information of the operating system running on this computer.
/// </summary> /// </summary>
@ -776,9 +539,9 @@ namespace Greenshot.Base.Core
get get
{ {
string servicePack = string.Empty; string servicePack = string.Empty;
OSVERSIONINFOEX osVersionInfo = OSVERSIONINFOEX.Create(); OsVersionInfoEx osVersionInfo = OsVersionInfoEx.Create();
if (GetVersionEx(ref osVersionInfo)) if (Kernel32Api.GetVersionEx(ref osVersionInfo))
{ {
servicePack = osVersionInfo.ServicePackVersion; servicePack = osVersionInfo.ServicePackVersion;
} }
@ -787,6 +550,7 @@ namespace Greenshot.Base.Core
} }
} }
/// <summary>
/// Gets the full version string of the operating system running on this computer. /// Gets the full version string of the operating system running on this computer.
/// </summary> /// </summary>
public static string VersionString public static string VersionString

View file

@ -23,6 +23,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using Dapplo.Windows.Common.Extensions;
using Dapplo.Windows.Common.Structs;
namespace Greenshot.Base.Core namespace Greenshot.Base.Core
{ {
@ -84,7 +86,7 @@ namespace Greenshot.Base.Core
/// <summary> /// <summary>
/// Size of the underlying image /// Size of the underlying image
/// </summary> /// </summary>
Size Size { get; } NativeSize Size { get; }
/// <summary> /// <summary>
/// Height of the image area that this fastbitmap covers /// Height of the image area that this fastbitmap covers
@ -127,19 +129,19 @@ namespace Greenshot.Base.Core
bool HasAlphaChannel { get; } bool HasAlphaChannel { get; }
/// <summary> /// <summary>
/// Draw the stored bitmap to the destionation bitmap at the supplied point /// Draw the stored bitmap to the destination bitmap at the supplied point
/// </summary> /// </summary>
/// <param name="graphics">Graphics</param> /// <param name="graphics">Graphics</param>
/// <param name="destination">Point with location</param> /// <param name="destination">NativePoint with location</param>
void DrawTo(Graphics graphics, Point destination); void DrawTo(Graphics graphics, NativePoint destination);
/// <summary> /// <summary>
/// Draw the stored Bitmap on the Destination bitmap with the specified rectangle /// Draw the stored Bitmap on the Destination bitmap with the specified rectangle
/// Be aware that the stored bitmap will be resized to the specified rectangle!! /// Be aware that the stored bitmap will be resized to the specified rectangle!!
/// </summary> /// </summary>
/// <param name="graphics">Graphics</param> /// <param name="graphics">Graphics</param>
/// <param name="destinationRect">Rectangle with destination</param> /// <param name="destinationRect">NativeRect with destination</param>
void DrawTo(Graphics graphics, Rectangle destinationRect); void DrawTo(Graphics graphics, NativeRect destinationRect);
/// <summary> /// <summary>
/// Return true if the coordinates are inside the FastBitmap /// Return true if the coordinates are inside the FastBitmap
@ -214,7 +216,7 @@ namespace Greenshot.Base.Core
/// </summary> /// </summary>
public interface IFastBitmapWithClip : IFastBitmap public interface IFastBitmapWithClip : IFastBitmap
{ {
Rectangle Clip { get; set; } NativeRect Clip { get; set; }
bool InvertClip { get; set; } bool InvertClip { get; set; }
@ -267,14 +269,14 @@ namespace Greenshot.Base.Core
public const int ColorIndexB = 2; public const int ColorIndexB = 2;
public const int ColorIndexA = 3; public const int ColorIndexA = 3;
protected Rectangle Area; protected NativeRect Area;
/// <summary> /// <summary>
/// If this is set to true, the bitmap will be disposed when disposing the IFastBitmap /// If this is set to true, the bitmap will be disposed when disposing the IFastBitmap
/// </summary> /// </summary>
public bool NeedsDispose { get; set; } public bool NeedsDispose { get; set; }
public Rectangle Clip { get; set; } public NativeRect Clip { get; set; }
public bool InvertClip { get; set; } public bool InvertClip { get; set; }
@ -290,7 +292,7 @@ namespace Greenshot.Base.Core
public static IFastBitmap Create(Bitmap source) public static IFastBitmap Create(Bitmap source)
{ {
return Create(source, Rectangle.Empty); return Create(source, NativeRect.Empty);
} }
public void SetResolution(float horizontal, float vertical) public void SetResolution(float horizontal, float vertical)
@ -303,44 +305,37 @@ namespace Greenshot.Base.Core
/// The supplied rectangle specifies the area for which the FastBitmap does its thing /// The supplied rectangle specifies the area for which the FastBitmap does its thing
/// </summary> /// </summary>
/// <param name="source">Bitmap to access</param> /// <param name="source">Bitmap to access</param>
/// <param name="area">Rectangle which specifies the area to have access to, can be Rectangle.Empty for the whole image</param> /// <param name="area">NativeRect which specifies the area to have access to, can be NativeRect.Empty for the whole image</param>
/// <returns>IFastBitmap</returns> /// <returns>IFastBitmap</returns>
public static IFastBitmap Create(Bitmap source, Rectangle area) public static IFastBitmap Create(Bitmap source, NativeRect area) =>
{ source.PixelFormat switch
switch (source.PixelFormat)
{ {
case PixelFormat.Format8bppIndexed: PixelFormat.Format8bppIndexed => new FastChunkyBitmap(source, area),
return new FastChunkyBitmap(source, area); PixelFormat.Format24bppRgb => new Fast24RgbBitmap(source, area),
case PixelFormat.Format24bppRgb: PixelFormat.Format32bppRgb => new Fast32RgbBitmap(source, area),
return new Fast24RgbBitmap(source, area); PixelFormat.Format32bppArgb => new Fast32ArgbBitmap(source, area),
case PixelFormat.Format32bppRgb: PixelFormat.Format32bppPArgb => new Fast32ArgbBitmap(source, area),
return new Fast32RgbBitmap(source, area); _ => throw new NotSupportedException($"Not supported PixelFormat {source.PixelFormat}")
case PixelFormat.Format32bppArgb: };
case PixelFormat.Format32bppPArgb:
return new Fast32ArgbBitmap(source, area);
default:
throw new NotSupportedException($"Not supported Pixelformat {source.PixelFormat}");
}
}
/// <summary> /// <summary>
/// Factory for creating a FastBitmap as a destination for the source /// Factory for creating a FastBitmap as a destination for the source
/// </summary> /// </summary>
/// <param name="source">Bitmap to clone</param> /// <param name="source">Bitmap to clone</param>
/// <param name="pixelFormat">new Pixelformat</param> /// <param name="pixelFormat">new PixelFormat</param>
/// <returns>IFastBitmap</returns> /// <returns>IFastBitmap</returns>
public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat) public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat)
{ {
return CreateCloneOf(source, pixelFormat, Rectangle.Empty); return CreateCloneOf(source, pixelFormat, NativeRect.Empty);
} }
/// <summary> /// <summary>
/// Factory for creating a FastBitmap as a destination for the source /// Factory for creating a FastBitmap as a destination for the source
/// </summary> /// </summary>
/// <param name="source">Bitmap to clone</param> /// <param name="source">Bitmap to clone</param>
/// <param name="area">Area of the bitmap to access, can be Rectangle.Empty for the whole</param> /// <param name="area">Area of the bitmap to access, can be NativeRect.Empty for the whole</param>
/// <returns>IFastBitmap</returns> /// <returns>IFastBitmap</returns>
public static IFastBitmap CreateCloneOf(Image source, Rectangle area) public static IFastBitmap CreateCloneOf(Image source, NativeRect area)
{ {
return CreateCloneOf(source, PixelFormat.DontCare, area); return CreateCloneOf(source, PixelFormat.DontCare, area);
} }
@ -350,9 +345,9 @@ namespace Greenshot.Base.Core
/// </summary> /// </summary>
/// <param name="source">Bitmap to clone</param> /// <param name="source">Bitmap to clone</param>
/// <param name="pixelFormat">Pixelformat of the cloned bitmap</param> /// <param name="pixelFormat">Pixelformat of the cloned bitmap</param>
/// <param name="area">Area of the bitmap to access, can be Rectangle.Empty for the whole</param> /// <param name="area">Area of the bitmap to access, can be NativeRect.Empty for the whole</param>
/// <returns>IFastBitmap</returns> /// <returns>IFastBitmap</returns>
public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat, Rectangle area) public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat, NativeRect area)
{ {
Bitmap destination = ImageHelper.CloneArea(source, area, pixelFormat); Bitmap destination = ImageHelper.CloneArea(source, area, pixelFormat);
FastBitmap fastBitmap = Create(destination) as FastBitmap; FastBitmap fastBitmap = Create(destination) as FastBitmap;
@ -369,11 +364,11 @@ namespace Greenshot.Base.Core
/// <summary> /// <summary>
/// Factory for creating a FastBitmap as a destination /// Factory for creating a FastBitmap as a destination
/// </summary> /// </summary>
/// <param name="newSize"></param> /// <param name="newSize">NativeSize</param>
/// <param name="pixelFormat"></param> /// <param name="pixelFormat">PixelFormat</param>
/// <param name="backgroundColor"></param> /// <param name="backgroundColor">Color</param>
/// <returns>IFastBitmap</returns> /// <returns>IFastBitmap</returns>
public static IFastBitmap CreateEmpty(Size newSize, PixelFormat pixelFormat, Color backgroundColor) public static IFastBitmap CreateEmpty(NativeSize newSize, PixelFormat pixelFormat, Color backgroundColor)
{ {
Bitmap destination = ImageHelper.CreateEmpty(newSize.Width, newSize.Height, pixelFormat, backgroundColor, 96f, 96f); Bitmap destination = ImageHelper.CreateEmpty(newSize.Width, newSize.Height, pixelFormat, backgroundColor, 96f, 96f);
IFastBitmap fastBitmap = Create(destination); IFastBitmap fastBitmap = Create(destination);
@ -385,14 +380,14 @@ namespace Greenshot.Base.Core
/// Constructor which stores the image and locks it when called /// Constructor which stores the image and locks it when called
/// </summary> /// </summary>
/// <param name="bitmap">Bitmap</param> /// <param name="bitmap">Bitmap</param>
/// <param name="area">Rectangle</param> /// <param name="area">NativeRect</param>
protected FastBitmap(Bitmap bitmap, Rectangle area) protected FastBitmap(Bitmap bitmap, NativeRect area)
{ {
Bitmap = bitmap; Bitmap = bitmap;
Rectangle bitmapArea = new Rectangle(Point.Empty, bitmap.Size); var bitmapArea = new NativeRect(NativePoint.Empty, bitmap.Size);
if (area != Rectangle.Empty) if (area != NativeRect.Empty)
{ {
area.Intersect(bitmapArea); area = area.Intersect(bitmapArea);
Area = area; Area = area;
} }
else else
@ -413,11 +408,11 @@ namespace Greenshot.Base.Core
/// <summary> /// <summary>
/// Return the size of the image /// Return the size of the image
/// </summary> /// </summary>
public Size Size public NativeSize Size
{ {
get get
{ {
if (Area == Rectangle.Empty) if (Area == NativeRect.Empty)
{ {
return Bitmap.Size; return Bitmap.Size;
} }
@ -433,7 +428,7 @@ namespace Greenshot.Base.Core
{ {
get get
{ {
if (Area == Rectangle.Empty) if (Area == NativeRect.Empty)
{ {
return Bitmap.Width; return Bitmap.Width;
} }
@ -449,7 +444,7 @@ namespace Greenshot.Base.Core
{ {
get get
{ {
if (Area == Rectangle.Empty) if (Area == NativeRect.Empty)
{ {
return Bitmap.Height; return Bitmap.Height;
} }
@ -596,13 +591,13 @@ namespace Greenshot.Base.Core
} }
/// <summary> /// <summary>
/// Draw the stored bitmap to the destionation bitmap at the supplied point /// Draw the stored bitmap to the destination bitmap at the supplied point
/// </summary> /// </summary>
/// <param name="graphics"></param> /// <param name="graphics"></param>
/// <param name="destination"></param> /// <param name="destination"></param>
public void DrawTo(Graphics graphics, Point destination) public void DrawTo(Graphics graphics, NativePoint destination)
{ {
DrawTo(graphics, new Rectangle(destination, Area.Size)); DrawTo(graphics, new NativeRect(destination, Area.Size));
} }
/// <summary> /// <summary>
@ -610,8 +605,8 @@ namespace Greenshot.Base.Core
/// Be aware that the stored bitmap will be resized to the specified rectangle!! /// Be aware that the stored bitmap will be resized to the specified rectangle!!
/// </summary> /// </summary>
/// <param name="graphics"></param> /// <param name="graphics"></param>
/// <param name="destinationRect"></param> /// <param name="destinationRect">NativeRect</param>
public void DrawTo(Graphics graphics, Rectangle destinationRect) public void DrawTo(Graphics graphics, NativeRect destinationRect)
{ {
// Make sure this.bitmap is unlocked, if it was locked // Make sure this.bitmap is unlocked, if it was locked
bool isLocked = BitsLocked; bool isLocked = BitsLocked;
@ -715,7 +710,7 @@ namespace Greenshot.Base.Core
} }
/// <summary> /// <summary>
/// This is the implementation of the FastBitmat for the 8BPP pixelformat /// This is the implementation of the FastBitmap for the 8BPP pixelformat
/// </summary> /// </summary>
public unsafe class FastChunkyBitmap : FastBitmap public unsafe class FastChunkyBitmap : FastBitmap
{ {
@ -723,7 +718,7 @@ namespace Greenshot.Base.Core
private readonly Color[] _colorEntries; private readonly Color[] _colorEntries;
private readonly Dictionary<Color, byte> _colorCache = new Dictionary<Color, byte>(); private readonly Dictionary<Color, byte> _colorCache = new Dictionary<Color, byte>();
public FastChunkyBitmap(Bitmap source, Rectangle area) : base(source, area) public FastChunkyBitmap(Bitmap source, NativeRect area) : base(source, area)
{ {
_colorEntries = Bitmap.Palette.Entries; _colorEntries = Bitmap.Palette.Entries;
} }
@ -825,7 +820,7 @@ namespace Greenshot.Base.Core
/// </summary> /// </summary>
public unsafe class Fast24RgbBitmap : FastBitmap public unsafe class Fast24RgbBitmap : FastBitmap
{ {
public Fast24RgbBitmap(Bitmap source, Rectangle area) : base(source, area) public Fast24RgbBitmap(Bitmap source, NativeRect area) : base(source, area)
{ {
} }
@ -891,7 +886,7 @@ namespace Greenshot.Base.Core
/// </summary> /// </summary>
public unsafe class Fast32RgbBitmap : FastBitmap public unsafe class Fast32RgbBitmap : FastBitmap
{ {
public Fast32RgbBitmap(Bitmap source, Rectangle area) : base(source, area) public Fast32RgbBitmap(Bitmap source, NativeRect area) : base(source, area)
{ {
} }
@ -961,7 +956,7 @@ namespace Greenshot.Base.Core
public Color BackgroundBlendColor { get; set; } public Color BackgroundBlendColor { get; set; }
public Fast32ArgbBitmap(Bitmap source, Rectangle area) : base(source, area) public Fast32ArgbBitmap(Bitmap source, NativeRect area) : base(source, area)
{ {
BackgroundBlendColor = Color.White; BackgroundBlendColor = Color.White;
} }

View file

@ -22,7 +22,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
using Greenshot.Base.UnmanagedHelpers.Structs; using Dapplo.Windows.Common.Structs;
namespace Greenshot.Base.Core namespace Greenshot.Base.Core
{ {
@ -30,8 +30,8 @@ namespace Greenshot.Base.Core
{ {
public FileDescriptorFlags Flags { get; set; } public FileDescriptorFlags Flags { get; set; }
public Guid ClassId { get; set; } public Guid ClassId { get; set; }
public SIZE Size { get; set; } public NativeSize Size { get; set; }
public POINT Point { get; set; } public NativePoint Point { get; set; }
public FileAttributes FileAttributes { get; set; } public FileAttributes FileAttributes { get; set; }
public DateTime CreationTime { get; set; } public DateTime CreationTime { get; set; }
public DateTime LastAccessTime { get; set; } public DateTime LastAccessTime { get; set; }
@ -46,9 +46,9 @@ namespace Greenshot.Base.Core
//ClassID //ClassID
ClassId = new Guid(reader.ReadBytes(16)); ClassId = new Guid(reader.ReadBytes(16));
//Size //Size
Size = new SIZE(reader.ReadInt32(), reader.ReadInt32()); Size = new NativeSize(reader.ReadInt32(), reader.ReadInt32());
//Point //Point
Point = new POINT(reader.ReadInt32(), reader.ReadInt32()); Point = new NativePoint(reader.ReadInt32(), reader.ReadInt32());
//FileAttributes //FileAttributes
FileAttributes = (FileAttributes)reader.ReadUInt32(); FileAttributes = (FileAttributes)reader.ReadUInt32();
//CreationTime //CreationTime

View file

@ -0,0 +1,163 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Base.Interfaces.Plugin;
namespace Greenshot.Base.Core.FileFormatHandlers
{
/// <summary>
/// This is the registry where all IFileFormatHandler are registered and can be used
/// </summary>
public static class FileFormatHandlerExtensions
{
/// <summary>
/// Make sure we handle the input extension always the same, by "normalizing" it
/// </summary>
/// <param name="extension">string</param>
/// <returns>string</returns>
public static string NormalizeExtension(string extension)
{
if (string.IsNullOrEmpty(extension))
{
return null;
}
extension = extension.ToLowerInvariant();
return !extension.StartsWith(".") ? $".{extension}" : extension;
}
/// <summary>
/// Return the extensions that the provided IFileFormatHandlers can accept for the specified action
/// </summary>
/// <param name="fileFormatHandlers">IEnumerable{IFileFormatHandler}</param>
/// <param name="fileFormatHandlerAction"></param>
/// <returns></returns>
public static IEnumerable<string> ExtensionsFor(this IEnumerable<IFileFormatHandler> fileFormatHandlers, FileFormatHandlerActions fileFormatHandlerAction)
{
return fileFormatHandlers.Where(ffh => ffh.SupportedExtensions.ContainsKey(fileFormatHandlerAction)).SelectMany(ffh => ffh.SupportedExtensions[fileFormatHandlerAction]).Distinct().OrderBy(e => e);
}
/// <summary>
/// Extension method to check if a certain IFileFormatHandler supports a certain action with a specific extension
/// </summary>
/// <param name="fileFormatHandler">IFileFormatHandler</param>
/// <param name="fileFormatHandlerAction">FileFormatHandlerActions</param>
/// <param name="extension">string</param>
/// <returns>bool</returns>
public static bool Supports(this IFileFormatHandler fileFormatHandler, FileFormatHandlerActions fileFormatHandlerAction, string extension)
{
extension = NormalizeExtension(extension);
return fileFormatHandler.SupportedExtensions.ContainsKey(fileFormatHandlerAction) && fileFormatHandler.SupportedExtensions[fileFormatHandlerAction].Contains(extension);
}
/// <summary>
/// This wrapper method for TrySaveToStream will do:
/// Find all the IFileFormatHandler which support the action for the supplied extension.
/// Take the first, to call the TrySaveToStream on.
/// </summary>
/// <param name="fileFormatHandlers">IEnumerable{IFileFormatHandler}</param>
/// <param name="bitmap">Bitmap</param>
/// <param name="destination">Stream</param>
/// <param name="extension">string</param>
/// <param name="surface">ISurface</param>
/// <returns>bool</returns>
public static bool TrySaveToStream(this IEnumerable<IFileFormatHandler> fileFormatHandlers, Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null)
{
extension = NormalizeExtension(extension);
var saveFileFormatHandlers = fileFormatHandlers
.Where(ffh => ffh.Supports(FileFormatHandlerActions.LoadFromStream, extension))
.OrderBy(ffh => ffh.PriorityFor(FileFormatHandlerActions.LoadFromStream, extension)).ToList();
if (!saveFileFormatHandlers.Any())
{
return false;
}
foreach (var fileFormatHandler in saveFileFormatHandlers)
{
if (fileFormatHandler.TrySaveToStream(bitmap, destination, extension, surface))
{
return true;
}
}
return false;
}
/// <summary>
/// Try to load a drawable container from the stream
/// </summary>
/// <param name="fileFormatHandlers">IEnumerable{IFileFormatHandler}</param>
/// <param name="stream">Stream</param>
/// <param name="extension">string</param>
/// <param name="parentSurface">ISurface</param>
/// <returns>IEnumerable{IDrawableContainer}</returns>
public static IEnumerable<IDrawableContainer> LoadDrawablesFromStream(this IEnumerable<IFileFormatHandler> fileFormatHandlers, Stream stream, string extension, ISurface parentSurface = null)
{
extension = NormalizeExtension(extension);
var loadfileFormatHandler = fileFormatHandlers
.Where(ffh => ffh.Supports(FileFormatHandlerActions.LoadDrawableFromStream, extension))
.OrderBy(ffh => ffh.PriorityFor(FileFormatHandlerActions.LoadDrawableFromStream, extension))
.FirstOrDefault();
if (loadfileFormatHandler != null)
{
return loadfileFormatHandler.LoadDrawablesFromStream(stream, extension, parentSurface);
}
return Enumerable.Empty<IDrawableContainer>();
}
/// <summary>
/// Try to load a Bitmap from the stream
/// </summary>
/// <param name="fileFormatHandlers">IEnumerable{IFileFormatHandler}</param>
/// <param name="stream">Stream</param>
/// <param name="extension">string</param>
/// <param name="bitmap">Bitmap out</param>
/// <returns>bool true if it was successful</returns>
public static bool TryLoadFromStream(this IEnumerable<IFileFormatHandler> fileFormatHandlers, Stream stream, string extension, out Bitmap bitmap)
{
extension = NormalizeExtension(extension);
var loadFileFormatHandler = fileFormatHandlers
.Where(ffh => ffh.Supports(FileFormatHandlerActions.LoadFromStream, extension))
.OrderBy(ffh => ffh.PriorityFor(FileFormatHandlerActions.LoadFromStream, extension))
.FirstOrDefault();
if (loadFileFormatHandler == null)
{
bitmap = null;
return false;
}
return loadFileFormatHandler.TryLoadFromStream(stream, extension, out bitmap);
}
}
}

View file

@ -1,52 +0,0 @@
// Greenshot - a free and open source screenshot tool
// Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
//
// For more information see: https://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
using System.Diagnostics.Contracts;
using Greenshot.Base.Core.Enums;
namespace Greenshot.Base.Core
{
/// <summary>
/// Extensions to handle the HResult
/// </summary>
public static class HResultExtensions
{
/// <summary>
/// Test if the HResult represents a fail
/// </summary>
/// <param name="hResult">HResult</param>
/// <returns>bool</returns>
[Pure]
public static bool Failed(this HResult hResult)
{
return hResult < 0;
}
/// <summary>
/// Test if the HResult represents a success
/// </summary>
/// <param name="hResult">HResult</param>
/// <returns>bool</returns>
[Pure]
public static bool Succeeded(this HResult hResult)
{
return hResult >= HResult.S_OK;
}
}
}

View file

@ -61,7 +61,7 @@ namespace Greenshot.Base.Core
float HorizontalResolution { get; } float HorizontalResolution { get; }
/// <summary> /// <summary>
/// Unterlying image, or an on demand rendered version with different attributes as the original /// Underlying image, or an on demand rendered version with different attributes as the original
/// </summary> /// </summary>
Image Image { get; } Image Image { get; }
} }

View file

@ -24,28 +24,24 @@ using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO; using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Dapplo.Windows.Common.Extensions;
using Dapplo.Windows.Common.Structs;
using Dapplo.Windows.Gdi32;
using Greenshot.Base.Core.Enums;
using Greenshot.Base.Effects; using Greenshot.Base.Effects;
using Greenshot.Base.IniFile; using Greenshot.Base.IniFile;
using Greenshot.Base.Interfaces;
using Greenshot.Base.UnmanagedHelpers;
using log4net; using log4net;
using Brush = System.Drawing.Brush;
using Color = System.Drawing.Color;
using Matrix = System.Drawing.Drawing2D.Matrix;
using Pen = System.Drawing.Pen;
using PixelFormat = System.Drawing.Imaging.PixelFormat;
namespace Greenshot.Base.Core namespace Greenshot.Base.Core
{ {
internal enum ExifOrientations : byte
{
Unknown = 0,
TopLeft = 1,
TopRight = 2,
BottomRight = 3,
BottomLeft = 4,
LeftTop = 5,
RightTop = 6,
RightBottom = 7,
LeftBottom = 8,
}
/// <summary> /// <summary>
/// Description of ImageHelper. /// Description of ImageHelper.
/// </summary> /// </summary>
@ -55,83 +51,6 @@ namespace Greenshot.Base.Core
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>(); private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
private const int ExifOrientationId = 0x0112; private const int ExifOrientationId = 0x0112;
static ImageHelper()
{
StreamConverters["greenshot"] = (stream, s) =>
{
var surface = SimpleServiceProvider.Current.GetInstance<Func<ISurface>>().Invoke();
return surface.GetImageForExport();
};
// Add a SVG converter
StreamConverters["svg"] = (stream, s) =>
{
stream.Position = 0;
try
{
return SvgImage.FromStream(stream).Image;
}
catch (Exception ex)
{
Log.Error("Can't load SVG", ex);
}
return null;
};
static Image DefaultConverter(Stream stream, string s)
{
stream.Position = 0;
using var tmpImage = Image.FromStream(stream, true, true);
Log.DebugFormat("Loaded bitmap with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat);
return Clone(tmpImage, PixelFormat.Format32bppArgb);
}
// Fallback
StreamConverters[string.Empty] = DefaultConverter;
StreamConverters["gif"] = DefaultConverter;
StreamConverters["bmp"] = DefaultConverter;
StreamConverters["jpg"] = DefaultConverter;
StreamConverters["jpeg"] = DefaultConverter;
StreamConverters["png"] = DefaultConverter;
StreamConverters["wmf"] = DefaultConverter;
StreamConverters["ico"] = (stream, extension) =>
{
// Icon logic, try to get the Vista icon, else the biggest possible
try
{
using Image tmpImage = ExtractVistaIcon(stream);
if (tmpImage != null)
{
return Clone(tmpImage, PixelFormat.Format32bppArgb);
}
}
catch (Exception vistaIconException)
{
Log.Warn("Can't read icon", vistaIconException);
}
try
{
// No vista icon, try normal icon
stream.Position = 0;
// We create a copy of the bitmap, so everything else can be disposed
using Icon tmpIcon = new Icon(stream, new Size(1024, 1024));
using Image tmpImage = tmpIcon.ToBitmap();
return Clone(tmpImage, PixelFormat.Format32bppArgb);
}
catch (Exception iconException)
{
Log.Warn("Can't read icon", iconException);
}
stream.Position = 0;
return DefaultConverter(stream, extension);
};
}
public static IDictionary<string, Func<Stream, string, Image>> StreamConverters { get; } = new Dictionary<string, Func<Stream, string, Image>>();
/// <summary> /// <summary>
/// Make sure the image is orientated correctly /// Make sure the image is orientated correctly
@ -238,7 +157,7 @@ namespace Greenshot.Base.Core
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
Rectangle rectDestination = new Rectangle(0, 0, thumbWidth, thumbHeight); NativeRect rectDestination = new NativeRect(0, 0, thumbWidth, thumbHeight);
graphics.DrawImage(image, rectDestination, 0, 0, srcWidth, srcHeight, GraphicsUnit.Pixel); graphics.DrawImage(image, rectDestination, 0, 0, srcWidth, srcHeight, GraphicsUnit.Pixel);
} }
@ -246,18 +165,18 @@ namespace Greenshot.Base.Core
} }
/// <summary> /// <summary>
/// Crops the image to the specified rectangle /// Crops the image to the specified NativeRect
/// </summary> /// </summary>
/// <param name="image">Image to crop</param> /// <param name="image">Image to crop</param>
/// <param name="cropRectangle">Rectangle with bitmap coordinates, will be "intersected" to the bitmap</param> /// <param name="cropNativeRect">NativeRect with bitmap coordinates, will be "intersected" to the bitmap</param>
public static bool Crop(ref Image image, ref Rectangle cropRectangle) public static bool Crop(ref Image image, ref NativeRect cropNativeRect)
{ {
if (image is Bitmap && (image.Width * image.Height > 0)) if (image is Bitmap && (image.Width * image.Height > 0))
{ {
cropRectangle.Intersect(new Rectangle(0, 0, image.Width, image.Height)); cropNativeRect = cropNativeRect.Intersect(new NativeRect(0, 0, image.Width, image.Height));
if (cropRectangle.Width != 0 || cropRectangle.Height != 0) if (cropNativeRect.Width != 0 || cropNativeRect.Height != 0)
{ {
Image returnImage = CloneArea(image, cropRectangle, PixelFormat.DontCare); Image returnImage = CloneArea(image, cropNativeRect, PixelFormat.DontCare);
image.Dispose(); image.Dispose();
image = returnImage; image = returnImage;
return true; return true;
@ -269,24 +188,26 @@ namespace Greenshot.Base.Core
} }
/// <summary> /// <summary>
/// Private helper method for the FindAutoCropRectangle /// Private helper method for the FindAutoCropNativeRect
/// </summary> /// </summary>
/// <param name="fastBitmap"></param> /// <param name="fastBitmap">IFastBitmap</param>
/// <param name="colorPoint"></param> /// <param name="colorPoint">NativePoint</param>
/// <param name="cropDifference"></param> /// <param name="cropDifference">int</param>
/// <returns>Rectangle</returns> /// <param name="area">NativeRect with optional area to scan in</param>
private static Rectangle FindAutoCropRectangle(IFastBitmap fastBitmap, Point colorPoint, int cropDifference) /// <returns>NativeRect</returns>
private static NativeRect FindAutoCropNativeRect(IFastBitmap fastBitmap, NativePoint colorPoint, int cropDifference, NativeRect? area = null)
{ {
Rectangle cropRectangle = Rectangle.Empty; area ??= new NativeRect(0, 0, fastBitmap.Width, fastBitmap.Height);
NativeRect cropNativeRect = NativeRect.Empty;
Color referenceColor = fastBitmap.GetColorAt(colorPoint.X, colorPoint.Y); Color referenceColor = fastBitmap.GetColorAt(colorPoint.X, colorPoint.Y);
Point min = new Point(int.MaxValue, int.MaxValue); NativePoint min = new NativePoint(int.MaxValue, int.MaxValue);
Point max = new Point(int.MinValue, int.MinValue); NativePoint max = new NativePoint(int.MinValue, int.MinValue);
if (cropDifference > 0) if (cropDifference > 0)
{ {
for (int y = 0; y < fastBitmap.Height; y++) for (int y = area.Value.Top; y < area.Value.Bottom; y++)
{ {
for (int x = 0; x < fastBitmap.Width; x++) for (int x = area.Value.Left; x < area.Value.Right; x++)
{ {
Color currentColor = fastBitmap.GetColorAt(x, y); Color currentColor = fastBitmap.GetColorAt(x, y);
int diffR = Math.Abs(currentColor.R - referenceColor.R); int diffR = Math.Abs(currentColor.R - referenceColor.R);
@ -297,18 +218,18 @@ namespace Greenshot.Base.Core
continue; continue;
} }
if (x < min.X) min.X = x; if (x < min.X) min = min.ChangeX(x);
if (y < min.Y) min.Y = y; if (y < min.Y) min = min.ChangeY(y);
if (x > max.X) max.X = x; if (x > max.X) max = max.ChangeX(x);
if (y > max.Y) max.Y = y; if (y > max.Y) max = max.ChangeY(y);
} }
} }
} }
else else
{ {
for (int y = 0; y < fastBitmap.Height; y++) for (int y = area.Value.Top; y < area.Value.Bottom; y++)
{ {
for (int x = 0; x < fastBitmap.Width; x++) for (int x = area.Value.Left; x < area.Value.Right; x++)
{ {
Color currentColor = fastBitmap.GetColorAt(x, y); Color currentColor = fastBitmap.GetColorAt(x, y);
if (!referenceColor.Equals(currentColor)) if (!referenceColor.Equals(currentColor))
@ -316,41 +237,44 @@ namespace Greenshot.Base.Core
continue; continue;
} }
if (x < min.X) min.X = x; if (x < min.X) min = min.ChangeX(x);
if (y < min.Y) min.Y = y; if (y < min.Y) min = min.ChangeY(y);
if (x > max.X) max.X = x; if (x > max.X) max = max.ChangeX(x);
if (y > max.Y) max.Y = y; if (y > max.Y) max = max.ChangeY(y);
} }
} }
} }
if (!(Point.Empty.Equals(min) && max.Equals(new Point(fastBitmap.Width - 1, fastBitmap.Height - 1)))) if (!(NativePoint.Empty.Equals(min) && max.Equals(new NativePoint(area.Value.Width - 1, area.Value.Height - 1))))
{ {
if (!(min.X == int.MaxValue || min.Y == int.MaxValue || max.X == int.MinValue || min.X == int.MinValue)) if (!(min.X == int.MaxValue || min.Y == int.MaxValue || max.X == int.MinValue || min.X == int.MinValue))
{ {
cropRectangle = new Rectangle(min.X, min.Y, max.X - min.X + 1, max.Y - min.Y + 1); cropNativeRect = new NativeRect(min.X, min.Y, max.X - min.X + 1, max.Y - min.Y + 1);
} }
} }
return cropRectangle; return cropNativeRect;
} }
/// <summary> /// <summary>
/// Get a rectangle for the image which crops the image of all colors equal to that on 0,0 /// Get a NativeRect for the image which crops the image of all colors equal to that on 0,0
/// </summary> /// </summary>
/// <param name="image"></param> /// <param name="image">Image</param>
/// <param name="cropDifference"></param> /// <param name="cropDifference">int</param>
/// <returns>Rectangle</returns> /// <param name="area">NativeRect with optional area</param>
public static Rectangle FindAutoCropRectangle(Image image, int cropDifference) /// <returns>NativeRect</returns>
public static NativeRect FindAutoCropRectangle(Image image, int cropDifference, NativeRect? area = null)
{ {
Rectangle cropRectangle = Rectangle.Empty; area ??= new NativeRect(0, 0, image.Width, image.Height);
var checkPoints = new List<Point> NativeRect cropNativeRect = NativeRect.Empty;
var checkPoints = new List<NativePoint>
{ {
new Point(0, 0), new(area.Value.Left, area.Value.Top),
new Point(0, image.Height - 1), new(area.Value.Left, area.Value.Bottom - 1),
new Point(image.Width - 1, 0), new(area.Value.Right - 1, area.Value.Top),
new Point(image.Width - 1, image.Height - 1) new(area.Value.Right - 1, area.Value.Bottom - 1)
}; };
// Top Left // Top Left
// Bottom Left // Bottom Left
// Top Right // Top Right
@ -358,138 +282,17 @@ namespace Greenshot.Base.Core
using (IFastBitmap fastBitmap = FastBitmap.Create((Bitmap) image)) using (IFastBitmap fastBitmap = FastBitmap.Create((Bitmap) image))
{ {
// find biggest area // find biggest area
foreach (Point checkPoint in checkPoints) foreach (var checkPoint in checkPoints)
{ {
var currentRectangle = FindAutoCropRectangle(fastBitmap, checkPoint, cropDifference); var currentNativeRect = FindAutoCropNativeRect(fastBitmap, checkPoint, cropDifference, area);
if (currentRectangle.Width * currentRectangle.Height > cropRectangle.Width * cropRectangle.Height) if (currentNativeRect.Width * currentNativeRect.Height > cropNativeRect.Width * cropNativeRect.Height)
{ {
cropRectangle = currentRectangle; cropNativeRect = currentNativeRect;
} }
} }
} }
return cropRectangle; return cropNativeRect;
}
/// <summary>
/// Load an image from file
/// </summary>
/// <param name="filename"></param>
/// <returns></returns>
public static Image LoadImage(string filename)
{
if (string.IsNullOrEmpty(filename))
{
return null;
}
if (!File.Exists(filename))
{
return null;
}
Image fileImage;
Log.InfoFormat("Loading image from file {0}", filename);
// Fixed lock problem Bug #3431881
using (Stream imageFileStream = File.OpenRead(filename))
{
fileImage = FromStream(imageFileStream, Path.GetExtension(filename));
}
if (fileImage != null)
{
Log.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", filename, fileImage.Width, fileImage.Height, fileImage.PixelFormat,
fileImage.HorizontalResolution, fileImage.VerticalResolution);
}
return fileImage;
}
/// <summary>
/// Based on: https://www.codeproject.com/KB/cs/IconExtractor.aspx
/// And a hint from: https://www.codeproject.com/KB/cs/IconLib.aspx
/// </summary>
/// <param name="iconStream">Stream with the icon information</param>
/// <returns>Bitmap with the Vista Icon (256x256)</returns>
private static Bitmap ExtractVistaIcon(Stream iconStream)
{
const int sizeIconDir = 6;
const int sizeIconDirEntry = 16;
Bitmap bmpPngExtracted = null;
try
{
byte[] srcBuf = new byte[iconStream.Length];
iconStream.Read(srcBuf, 0, (int) iconStream.Length);
int iCount = BitConverter.ToInt16(srcBuf, 4);
for (int iIndex = 0; iIndex < iCount; iIndex++)
{
int iWidth = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex];
int iHeight = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex + 1];
if (iWidth == 0 && iHeight == 0)
{
int iImageSize = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 8);
int iImageOffset = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 12);
using MemoryStream destStream = new MemoryStream();
destStream.Write(srcBuf, iImageOffset, iImageSize);
destStream.Seek(0, SeekOrigin.Begin);
bmpPngExtracted = new Bitmap(destStream); // This is PNG! :)
break;
}
}
}
catch
{
return null;
}
return bmpPngExtracted;
}
/// <summary>
/// See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms648069%28v=vs.85%29.aspx
/// </summary>
/// <param name="location">The file (EXE or DLL) to get the icon from</param>
/// <param name="index">Index of the icon</param>
/// <param name="takeLarge">true if the large icon is wanted</param>
/// <returns>Icon</returns>
public static Icon ExtractAssociatedIcon(string location, int index, bool takeLarge)
{
Shell32.ExtractIconEx(location, index, out var large, out var small, 1);
Icon returnIcon = null;
bool isLarge = false;
bool isSmall = false;
try
{
if (takeLarge && !IntPtr.Zero.Equals(large))
{
returnIcon = Icon.FromHandle(large);
isLarge = true;
}
else if (!IntPtr.Zero.Equals(small))
{
returnIcon = Icon.FromHandle(small);
isSmall = true;
}
else if (!IntPtr.Zero.Equals(large))
{
returnIcon = Icon.FromHandle(large);
isLarge = true;
}
}
finally
{
if (isLarge && !IntPtr.Zero.Equals(small))
{
User32.DestroyIcon(small);
}
if (isSmall && !IntPtr.Zero.Equals(large))
{
User32.DestroyIcon(large);
}
}
return returnIcon;
} }
/// <summary> /// <summary>
@ -497,7 +300,7 @@ namespace Greenshot.Base.Core
/// </summary> /// </summary>
/// <param name="sourceImage">Bitmap</param> /// <param name="sourceImage">Bitmap</param>
/// <param name="effect">IEffect</param> /// <param name="effect">IEffect</param>
/// <param name="matrix"></param> /// <param name="matrix">Matrix</param>
/// <returns>Bitmap</returns> /// <returns>Bitmap</returns>
public static Image ApplyEffect(Image sourceImage, IEffect effect, Matrix matrix) public static Image ApplyEffect(Image sourceImage, IEffect effect, Matrix matrix)
{ {
@ -543,7 +346,7 @@ namespace Greenshot.Base.Core
/// </summary> /// </summary>
/// <param name="path">Path to draw to</param> /// <param name="path">Path to draw to</param>
/// <param name="points">Points for the lines to draw</param> /// <param name="points">Points for the lines to draw</param>
private static void DrawLines(GraphicsPath path, List<Point> points) private static void DrawLines(GraphicsPath path, List<NativePoint> points)
{ {
path.AddLine(points[0], points[1]); path.AddLine(points[0], points[1]);
for (int i = 0; i < points.Count - 1; i++) for (int i = 0; i < points.Count - 1; i++)
@ -563,20 +366,19 @@ namespace Greenshot.Base.Core
/// <returns>Changed bitmap</returns> /// <returns>Changed bitmap</returns>
public static Image CreateTornEdge(Image sourceImage, int toothHeight, int horizontalToothRange, int verticalToothRange, bool[] edges) public static Image CreateTornEdge(Image sourceImage, int toothHeight, int horizontalToothRange, int verticalToothRange, bool[] edges)
{ {
Image returnImage = CreateEmpty(sourceImage.Width, sourceImage.Height, PixelFormat.Format32bppArgb, Color.Empty, sourceImage.HorizontalResolution, Image returnImage = CreateEmpty(sourceImage.Width, sourceImage.Height, PixelFormat.Format32bppArgb, Color.Empty, sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
sourceImage.VerticalResolution);
using (var path = new GraphicsPath()) using (var path = new GraphicsPath())
{ {
Random random = new Random(); Random random = new Random();
int horizontalRegions = (int) Math.Round((float) sourceImage.Width / horizontalToothRange); int horizontalRegions = (int) Math.Round((float) sourceImage.Width / horizontalToothRange);
int verticalRegions = (int) Math.Round((float) sourceImage.Height / verticalToothRange); int verticalRegions = (int) Math.Round((float) sourceImage.Height / verticalToothRange);
Point topLeft = new Point(0, 0); var topLeft = new NativePoint(0, 0);
Point topRight = new Point(sourceImage.Width, 0); var topRight = new NativePoint(sourceImage.Width, 0);
Point bottomLeft = new Point(0, sourceImage.Height); var bottomLeft = new NativePoint(0, sourceImage.Height);
Point bottomRight = new Point(sourceImage.Width, sourceImage.Height); var bottomRight = new NativePoint(sourceImage.Width, sourceImage.Height);
List<Point> points = new List<Point>(); var points = new List<NativePoint>();
if (edges[0]) if (edges[0])
{ {
@ -587,15 +389,15 @@ namespace Greenshot.Base.Core
} }
else else
{ {
points.Add(new Point(random.Next(1, toothHeight), random.Next(1, toothHeight))); points.Add(new NativePoint(random.Next(1, toothHeight), random.Next(1, toothHeight)));
} }
for (int i = 1; i < horizontalRegions - 1; i++) for (int i = 1; i < horizontalRegions - 1; i++)
{ {
points.Add(new Point(i * horizontalToothRange, random.Next(1, toothHeight))); points.Add(new NativePoint(i * horizontalToothRange, random.Next(1, toothHeight)));
} }
points.Add(new Point(sourceImage.Width - random.Next(1, toothHeight), random.Next(1, toothHeight))); points.Add(new NativePoint(sourceImage.Width - random.Next(1, toothHeight), random.Next(1, toothHeight)));
} }
else else
{ {
@ -609,10 +411,10 @@ namespace Greenshot.Base.Core
{ {
for (int i = 1; i < verticalRegions - 1; i++) for (int i = 1; i < verticalRegions - 1; i++)
{ {
points.Add(new Point(sourceImage.Width - random.Next(1, toothHeight), i * verticalToothRange)); points.Add(new NativePoint(sourceImage.Width - random.Next(1, toothHeight), i * verticalToothRange));
} }
points.Add(new Point(sourceImage.Width - random.Next(1, toothHeight), sourceImage.Height - random.Next(1, toothHeight))); points.Add(new NativePoint(sourceImage.Width - random.Next(1, toothHeight), sourceImage.Height - random.Next(1, toothHeight)));
} }
else else
{ {
@ -627,10 +429,10 @@ namespace Greenshot.Base.Core
{ {
for (int i = 1; i < horizontalRegions - 1; i++) for (int i = 1; i < horizontalRegions - 1; i++)
{ {
points.Add(new Point(sourceImage.Width - i * horizontalToothRange, sourceImage.Height - random.Next(1, toothHeight))); points.Add(new NativePoint(sourceImage.Width - i * horizontalToothRange, sourceImage.Height - random.Next(1, toothHeight)));
} }
points.Add(new Point(random.Next(1, toothHeight), sourceImage.Height - random.Next(1, toothHeight))); points.Add(new NativePoint(random.Next(1, toothHeight), sourceImage.Height - random.Next(1, toothHeight)));
} }
else else
{ {
@ -646,7 +448,7 @@ namespace Greenshot.Base.Core
// One fewer as the end point is the starting point // One fewer as the end point is the starting point
for (int i = 1; i < verticalRegions - 1; i++) for (int i = 1; i < verticalRegions - 1; i++)
{ {
points.Add(new Point(random.Next(1, toothHeight), points[points.Count - 1].Y - verticalToothRange)); points.Add(new NativePoint(random.Next(1, toothHeight), points[points.Count - 1].Y - verticalToothRange));
} }
} }
else else
@ -967,19 +769,18 @@ namespace Greenshot.Base.Core
/// <param name="applySize"></param> /// <param name="applySize"></param>
/// <param name="rect"></param> /// <param name="rect"></param>
/// <param name="invert"></param> /// <param name="invert"></param>
/// <returns></returns> /// <returns>NativeRect</returns>
public static Rectangle CreateIntersectRectangle(Size applySize, Rectangle rect, bool invert) public static NativeRect CreateIntersectRectangle(NativeSize applySize, NativeRect rect, bool invert)
{ {
Rectangle myRect; NativeRect myRect;
if (invert) if (invert)
{ {
myRect = new Rectangle(0, 0, applySize.Width, applySize.Height); myRect = new NativeRect(0, 0, applySize.Width, applySize.Height);
} }
else else
{ {
Rectangle applyRect = new Rectangle(0, 0, applySize.Width, applySize.Height); NativeRect applyRect = new NativeRect(0, 0, applySize.Width, applySize.Height);
myRect = new Rectangle(rect.X, rect.Y, rect.Width, rect.Height); myRect = new NativeRect(rect.X, rect.Y, rect.Width, rect.Height).Intersect(applyRect);
myRect.Intersect(applyRect);
} }
return myRect; return myRect;
@ -995,11 +796,9 @@ namespace Greenshot.Base.Core
/// <param name="shadowOffset"></param> /// <param name="shadowOffset"></param>
/// <param name="matrix">The transform matrix which describes how the elements need to be transformed to stay at the same location</param> /// <param name="matrix">The transform matrix which describes how the elements need to be transformed to stay at the same location</param>
/// <returns>Bitmap with the shadow, is bigger than the sourceBitmap!!</returns> /// <returns>Bitmap with the shadow, is bigger than the sourceBitmap!!</returns>
public static Bitmap CreateShadow(Image sourceBitmap, float darkness, int shadowSize, Point shadowOffset, Matrix matrix, PixelFormat targetPixelformat) public static Bitmap CreateShadow(Image sourceBitmap, float darkness, int shadowSize, NativePoint shadowOffset, Matrix matrix, PixelFormat targetPixelformat)
{ {
Point offset = shadowOffset; NativePoint offset = shadowOffset.Offset(shadowSize - 1, shadowSize - 1);
offset.X += shadowSize - 1;
offset.Y += shadowSize - 1;
matrix.Translate(offset.X, offset.Y, MatrixOrder.Append); matrix.Translate(offset.X, offset.Y, MatrixOrder.Append);
// Create a new "clean" image // Create a new "clean" image
Bitmap returnImage = CreateEmpty(sourceBitmap.Width + shadowSize * 2, sourceBitmap.Height + shadowSize * 2, targetPixelformat, Color.Empty, Bitmap returnImage = CreateEmpty(sourceBitmap.Width + shadowSize * 2, sourceBitmap.Height + shadowSize * 2, targetPixelformat, Color.Empty,
@ -1010,7 +809,7 @@ namespace Greenshot.Base.Core
shadowSize++; shadowSize++;
} }
bool useGdiBlur = GDIplus.IsBlurPossible(shadowSize); bool useGdiBlur = GdiPlusApi.IsBlurPossible(shadowSize);
// Create "mask" for the shadow // Create "mask" for the shadow
ColorMatrix maskMatrix = new ColorMatrix ColorMatrix maskMatrix = new ColorMatrix
{ {
@ -1027,20 +826,20 @@ namespace Greenshot.Base.Core
maskMatrix.Matrix33 = darkness; maskMatrix.Matrix33 = darkness;
} }
Rectangle shadowRectangle = new Rectangle(new Point(shadowSize, shadowSize), sourceBitmap.Size); NativeRect shadowNativeRect = new NativeRect(new NativePoint(shadowSize, shadowSize), sourceBitmap.Size);
ApplyColorMatrix((Bitmap) sourceBitmap, Rectangle.Empty, returnImage, shadowRectangle, maskMatrix); ApplyColorMatrix((Bitmap) sourceBitmap, NativeRect.Empty, returnImage, shadowNativeRect, maskMatrix);
// blur "shadow", apply to whole new image // blur "shadow", apply to whole new image
if (useGdiBlur) if (useGdiBlur)
{ {
// Use GDI Blur // Use GDI Blur
Rectangle newImageRectangle = new Rectangle(0, 0, returnImage.Width, returnImage.Height); NativeRect newImageNativeRect = new NativeRect(0, 0, returnImage.Width, returnImage.Height);
GDIplus.ApplyBlur(returnImage, newImageRectangle, shadowSize + 1, false); GdiPlusApi.ApplyBlur(returnImage, newImageNativeRect, shadowSize + 1, false);
} }
else else
{ {
// try normal software blur // try normal software blur
//returnImage = CreateBlur(returnImage, newImageRectangle, true, shadowSize, 1d, false, newImageRectangle); //returnImage = CreateBlur(returnImage, newImageNativeRect, true, shadowSize, 1d, false, newImageNativeRect);
ApplyBoxBlur(returnImage, shadowSize); ApplyBoxBlur(returnImage, shadowSize);
} }
@ -1104,18 +903,18 @@ namespace Greenshot.Base.Core
/// <param name="colorMatrix">ColorMatrix to apply</param> /// <param name="colorMatrix">ColorMatrix to apply</param>
public static void ApplyColorMatrix(Bitmap source, ColorMatrix colorMatrix) public static void ApplyColorMatrix(Bitmap source, ColorMatrix colorMatrix)
{ {
ApplyColorMatrix(source, Rectangle.Empty, source, Rectangle.Empty, colorMatrix); ApplyColorMatrix(source, NativeRect.Empty, source, NativeRect.Empty, colorMatrix);
} }
/// <summary> /// <summary>
/// Apply a color matrix by copying from the source to the destination /// Apply a color matrix by copying from the source to the destination
/// </summary> /// </summary>
/// <param name="source">Image to copy from</param> /// <param name="source">Image to copy from</param>
/// <param name="sourceRect">Rectangle to copy from</param> /// <param name="sourceRect">NativeRect to copy from</param>
/// <param name="destRect">Rectangle to copy to</param> /// <param name="destRect">NativeRect to copy to</param>
/// <param name="dest">Image to copy to</param> /// <param name="dest">Image to copy to</param>
/// <param name="colorMatrix">ColorMatrix to apply</param> /// <param name="colorMatrix">ColorMatrix to apply</param>
public static void ApplyColorMatrix(Bitmap source, Rectangle sourceRect, Bitmap dest, Rectangle destRect, ColorMatrix colorMatrix) public static void ApplyColorMatrix(Bitmap source, NativeRect sourceRect, Bitmap dest, NativeRect destRect, ColorMatrix colorMatrix)
{ {
using ImageAttributes imageAttributes = new ImageAttributes(); using ImageAttributes imageAttributes = new ImageAttributes();
imageAttributes.ClearColorMatrix(); imageAttributes.ClearColorMatrix();
@ -1127,15 +926,15 @@ namespace Greenshot.Base.Core
/// Apply a color matrix by copying from the source to the destination /// Apply a color matrix by copying from the source to the destination
/// </summary> /// </summary>
/// <param name="source">Image to copy from</param> /// <param name="source">Image to copy from</param>
/// <param name="sourceRect">Rectangle to copy from</param> /// <param name="sourceRect">NativeRect to copy from</param>
/// <param name="destRect">Rectangle to copy to</param> /// <param name="destRect">NativeRect to copy to</param>
/// <param name="dest">Image to copy to</param> /// <param name="dest">Image to copy to</param>
/// <param name="imageAttributes">ImageAttributes to apply</param> /// <param name="imageAttributes">ImageAttributes to apply</param>
public static void ApplyImageAttributes(Bitmap source, Rectangle sourceRect, Bitmap dest, Rectangle destRect, ImageAttributes imageAttributes) public static void ApplyImageAttributes(Bitmap source, NativeRect sourceRect, Bitmap dest, NativeRect destRect, ImageAttributes imageAttributes)
{ {
if (sourceRect == Rectangle.Empty) if (sourceRect == NativeRect.Empty)
{ {
sourceRect = new Rectangle(0, 0, source.Width, source.Height); sourceRect = new NativeRect(0, 0, source.Width, source.Height);
} }
if (dest == null) if (dest == null)
@ -1143,9 +942,9 @@ namespace Greenshot.Base.Core
dest = source; dest = source;
} }
if (destRect == Rectangle.Empty) if (destRect == NativeRect.Empty)
{ {
destRect = new Rectangle(0, 0, dest.Width, dest.Height); destRect = new NativeRect(0, 0, dest.Width, dest.Height);
} }
using Graphics graphics = Graphics.FromImage(dest); using Graphics graphics = Graphics.FromImage(dest);
@ -1194,7 +993,7 @@ namespace Greenshot.Base.Core
public static Image CreateBorder(Image sourceImage, int borderSize, Color borderColor, PixelFormat targetPixelformat, Matrix matrix) public static Image CreateBorder(Image sourceImage, int borderSize, Color borderColor, PixelFormat targetPixelformat, Matrix matrix)
{ {
// "return" the shifted offset, so the caller can e.g. move elements // "return" the shifted offset, so the caller can e.g. move elements
Point offset = new Point(borderSize, borderSize); NativePoint offset = new NativePoint(borderSize, borderSize);
matrix.Translate(offset.X, offset.Y, MatrixOrder.Append); matrix.Translate(offset.X, offset.Y, MatrixOrder.Append);
// Create a new "clean" image // Create a new "clean" image
@ -1209,7 +1008,7 @@ namespace Greenshot.Base.Core
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
using (GraphicsPath path = new GraphicsPath()) using (GraphicsPath path = new GraphicsPath())
{ {
path.AddRectangle(new Rectangle(borderSize >> 1, borderSize >> 1, newImage.Width - borderSize, newImage.Height - borderSize)); path.AddRectangle(new NativeRect(borderSize >> 1, borderSize >> 1, newImage.Width - borderSize, newImage.Height - borderSize));
using Pen pen = new Pen(borderColor, borderSize) using Pen pen = new Pen(borderColor, borderSize)
{ {
LineJoin = LineJoin.Round, LineJoin = LineJoin.Round,
@ -1289,7 +1088,7 @@ namespace Greenshot.Base.Core
sourceImage.VerticalResolution); sourceImage.VerticalResolution);
using (ImageAttributes adjustAttributes = CreateAdjustAttributes(brightness, contrast, gamma)) using (ImageAttributes adjustAttributes = CreateAdjustAttributes(brightness, contrast, gamma))
{ {
ApplyImageAttributes((Bitmap) sourceImage, Rectangle.Empty, newBitmap, Rectangle.Empty, adjustAttributes); ApplyImageAttributes((Bitmap) sourceImage, NativeRect.Empty, newBitmap, NativeRect.Empty, adjustAttributes);
} }
return newBitmap; return newBitmap;
@ -1355,7 +1154,7 @@ namespace Greenshot.Base.Core
return (Image) sourceImage.Clone(); return (Image) sourceImage.Clone();
} }
return CloneArea(sourceImage, Rectangle.Empty, PixelFormat.DontCare); return CloneArea(sourceImage, NativeRect.Empty, PixelFormat.DontCare);
} }
/// <summary> /// <summary>
@ -1366,7 +1165,7 @@ namespace Greenshot.Base.Core
/// <returns>Bitmap with clone image data</returns> /// <returns>Bitmap with clone image data</returns>
public static Bitmap Clone(Image sourceBitmap, PixelFormat targetFormat) public static Bitmap Clone(Image sourceBitmap, PixelFormat targetFormat)
{ {
return CloneArea(sourceBitmap, Rectangle.Empty, targetFormat); return CloneArea(sourceBitmap, NativeRect.Empty, targetFormat);
} }
/// <summary> /// <summary>
@ -1377,26 +1176,26 @@ namespace Greenshot.Base.Core
/// 2) When going from a transparent to a non transparent bitmap, we draw the background white! /// 2) When going from a transparent to a non transparent bitmap, we draw the background white!
/// </summary> /// </summary>
/// <param name="sourceImage">Source bitmap to clone</param> /// <param name="sourceImage">Source bitmap to clone</param>
/// <param name="sourceRect">Rectangle to copy from the source, use Rectangle.Empty for all</param> /// <param name="sourceRect">NativeRect to copy from the source, use NativeRect.Empty for all</param>
/// <param name="targetFormat">Target Format, use PixelFormat.DontCare if you want the original (or a default if the source PixelFormat is not supported)</param> /// <param name="targetFormat">Target Format, use PixelFormat.DontCare if you want the original (or a default if the source PixelFormat is not supported)</param>
/// <returns></returns> /// <returns></returns>
public static Bitmap CloneArea(Image sourceImage, Rectangle sourceRect, PixelFormat targetFormat) public static Bitmap CloneArea(Image sourceImage, NativeRect sourceRect, PixelFormat targetFormat)
{ {
Bitmap newImage; Bitmap newImage;
Rectangle bitmapRect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height); NativeRect bitmapRect = new NativeRect(0, 0, sourceImage.Width, sourceImage.Height);
// Make sure the source is not Rectangle.Empty // Make sure the source is not NativeRect.Empty
if (Rectangle.Empty.Equals(sourceRect)) if (NativeRect.Empty.Equals(sourceRect))
{ {
sourceRect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height); sourceRect = new NativeRect(0, 0, sourceImage.Width, sourceImage.Height);
} }
else else
{ {
sourceRect.Intersect(bitmapRect); sourceRect = sourceRect.Intersect(bitmapRect);
} }
// If no pixelformat is supplied // If no pixelformat is supplied
if (PixelFormat.DontCare == targetFormat || PixelFormat.Undefined == targetFormat) if (targetFormat is PixelFormat.DontCare or PixelFormat.Undefined)
{ {
if (SupportsPixelFormat(sourceImage.PixelFormat)) if (SupportsPixelFormat(sourceImage.PixelFormat))
{ {
@ -1517,10 +1316,10 @@ namespace Greenshot.Base.Core
/// <param name="height"></param> /// <param name="height"></param>
/// <param name="format"></param> /// <param name="format"></param>
/// <param name="backgroundColor">The color to fill with, or Color.Empty to take the default depending on the pixel format</param> /// <param name="backgroundColor">The color to fill with, or Color.Empty to take the default depending on the pixel format</param>
/// <param name="horizontalResolution"></param> /// <param name="horizontalResolution">float</param>
/// <param name="verticalResolution"></param> /// <param name="verticalResolution">float</param>
/// <returns>Bitmap</returns> /// <returns>Bitmap</returns>
public static Bitmap CreateEmpty(int width, int height, PixelFormat format, Color backgroundColor, float horizontalResolution, float verticalResolution) public static Bitmap CreateEmpty(int width, int height, PixelFormat format, Color backgroundColor, float horizontalResolution = 96f, float verticalResolution = 96f)
{ {
// Create a new "clean" image // Create a new "clean" image
Bitmap newImage = new Bitmap(width, height, format); Bitmap newImage = new Bitmap(width, height, format);
@ -1704,105 +1503,113 @@ namespace Greenshot.Base.Core
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
using ImageAttributes wrapMode = new ImageAttributes(); using ImageAttributes wrapMode = new ImageAttributes();
wrapMode.SetWrapMode(WrapMode.TileFlipXY); wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(sourceImage, new Rectangle(destX, destY, destWidth, destHeight), 0, 0, sourceImage.Width, sourceImage.Height, GraphicsUnit.Pixel, wrapMode); graphics.DrawImage(sourceImage, new NativeRect(destX, destY, destWidth, destHeight), 0, 0, sourceImage.Width, sourceImage.Height, GraphicsUnit.Pixel, wrapMode);
} }
return newImage; return newImage;
} }
/// <summary> /// <summary>
/// Load a Greenshot surface from a stream /// Rotate the image
/// </summary> /// </summary>
/// <param name="surfaceFileStream">Stream</param> /// <param name="image">Input image</param>
/// <param name="returnSurface"></param> /// <param name="rotationAngle">Angle in degrees</param>
/// <returns>ISurface</returns> /// <returns>Rotated image</returns>
public static ISurface LoadGreenshotSurface(Stream surfaceFileStream, ISurface returnSurface) public static Image Rotate(this Image image, float rotationAngle)
{ {
Image fileImage; var bitmap = CreateEmptyLike(image, Color.Transparent);
// Fixed problem that the bitmap stream is disposed... by Cloning the image
// This also ensures the bitmap is correctly created
// We create a copy of the bitmap, so everything else can be disposed using var graphics = Graphics.FromImage(bitmap);
surfaceFileStream.Position = 0; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
using (Image tmpImage = Image.FromStream(surfaceFileStream, true, true))
{
Log.DebugFormat("Loaded .greenshot file with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat);
fileImage = Clone(tmpImage);
}
// Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor) graphics.TranslateTransform((float)bitmap.Width / 2, (float)bitmap.Height / 2);
const int markerSize = 14; graphics.RotateTransform(rotationAngle);
surfaceFileStream.Seek(-markerSize, SeekOrigin.End); graphics.TranslateTransform(-(float)bitmap.Width / 2, -(float)bitmap.Height / 2);
using (StreamReader streamReader = new StreamReader(surfaceFileStream))
{
var greenshotMarker = streamReader.ReadToEnd();
if (!greenshotMarker.StartsWith("Greenshot"))
{
throw new ArgumentException("Stream is not a Greenshot file!");
}
Log.InfoFormat("Greenshot file format: {0}", greenshotMarker); graphics.DrawImage(image, new NativePoint(0, 0));
const int filesizeLocation = 8 + markerSize;
surfaceFileStream.Seek(-filesizeLocation, SeekOrigin.End);
using BinaryReader reader = new BinaryReader(surfaceFileStream);
long bytesWritten = reader.ReadInt64();
surfaceFileStream.Seek(-(bytesWritten + filesizeLocation), SeekOrigin.End);
returnSurface.LoadElementsFromStream(surfaceFileStream);
}
if (fileImage != null) return bitmap;
{
returnSurface.Image = fileImage;
Log.InfoFormat("Information about .greenshot file: {0}x{1}-{2} Resolution {3}x{4}", fileImage.Width, fileImage.Height, fileImage.PixelFormat,
fileImage.HorizontalResolution, fileImage.VerticalResolution);
}
return returnSurface;
} }
/// <summary> /// <summary>
/// Create an image from a stream, if an extension is supplied more formats are supported. /// Map a System.Drawing.Imaging.PixelFormat to a System.Windows.Media.PixelFormat
/// </summary> /// </summary>
/// <param name="stream">Stream</param> /// <param name="pixelFormat">System.Drawing.Imaging.PixelFormat</param>
/// <param name="extension"></param> /// <returns>System.Windows.Media.PixelFormat</returns>
/// <returns>Image</returns> /// <exception cref="NotSupportedException"></exception>
public static Image FromStream(Stream stream, string extension = null) public static System.Windows.Media.PixelFormat Map(this PixelFormat pixelFormat) =>
pixelFormat switch
{
PixelFormat.Format32bppArgb => PixelFormats.Bgra32,
PixelFormat.Format24bppRgb => PixelFormats.Bgr24,
PixelFormat.Format32bppRgb => PixelFormats.Bgr32,
_ => throw new NotSupportedException($"Can't map {pixelFormat}.")
};
/// <summary>
/// Map a System.Windows.Media.PixelFormat to a System.Drawing.Imaging.PixelFormat
/// </summary>
/// <param name="pixelFormat">System.Windows.Media.PixelFormat</param>
/// <returns>System.Drawing.Imaging.PixelFormat</returns>
/// <exception cref="NotSupportedException"></exception>
public static PixelFormat Map(this System.Windows.Media.PixelFormat pixelFormat)
{ {
if (stream == null) if (pixelFormat == PixelFormats.Bgra32)
{ {
return null; return PixelFormat.Format32bppArgb;
}
if (pixelFormat == PixelFormats.Bgr24)
{
return PixelFormat.Format24bppRgb;
}
if (pixelFormat == PixelFormats.Bgr32)
{
return PixelFormat.Format32bppRgb;
} }
if (!string.IsNullOrEmpty(extension)) throw new NotSupportedException($"Can't map {pixelFormat}.");
}
/// <summary>
/// Convert a Bitmap to a BitmapSource
/// </summary>
/// <param name="bitmap">Bitmap</param>
/// <returns>BitmapSource</returns>
public static BitmapSource ToBitmapSource(this Bitmap bitmap)
{
var bitmapData = bitmap.LockBits(new NativeRect(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
BitmapSource bitmapSource;
try
{ {
extension = extension.Replace(".", string.Empty); bitmapSource = BitmapSource.Create(
bitmapData.Width, bitmapData.Height,
bitmap.HorizontalResolution, bitmap.VerticalResolution,
bitmap.PixelFormat.Map(), null,
bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride);
}
finally
{
bitmap.UnlockBits(bitmapData);
} }
// Make sure we can try multiple times return bitmapSource;
if (!stream.CanSeek) }
{
var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
stream = memoryStream;
}
Image returnImage = null; /// <summary>
if (StreamConverters.TryGetValue(extension ?? string.Empty, out var converter)) /// Convert a BitmapSource to a Bitmap
{ /// </summary>
returnImage = converter(stream, extension); /// <param name="bitmapSource">BitmapSource</param>
} /// <returns>Bitmap</returns>
public static Bitmap ToBitmap(this BitmapSource bitmapSource)
{
var pixelFormat = bitmapSource.Format.Map();
// Fallback Bitmap bitmap = new Bitmap(bitmapSource.PixelWidth, bitmapSource.PixelHeight, pixelFormat);
if (returnImage == null) BitmapData data = bitmap.LockBits(new NativeRect(NativePoint.Empty, bitmap.Size), ImageLockMode.WriteOnly, pixelFormat);
{ bitmapSource.CopyPixels(Int32Rect.Empty, data.Scan0, data.Height * data.Stride, data.Stride);
// We create a copy of the bitmap, so everything else can be disposed bitmap.UnlockBits(data);
stream.Position = 0; return bitmap;
using var tmpImage = Image.FromStream(stream, true, true);
Log.DebugFormat("Loaded bitmap with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat);
returnImage = Clone(tmpImage, PixelFormat.Format32bppArgb);
}
return returnImage;
} }
} }
} }

View file

@ -20,12 +20,11 @@
*/ */
using System; using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@ -33,20 +32,20 @@ using System.Text.RegularExpressions;
using System.Windows.Forms; using System.Windows.Forms;
using Greenshot.Base.Controls; using Greenshot.Base.Controls;
using Greenshot.Base.Core.Enums; using Greenshot.Base.Core.Enums;
using Greenshot.Base.Core.FileFormatHandlers;
using Greenshot.Base.IniFile; using Greenshot.Base.IniFile;
using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Plugin; using Greenshot.Base.Interfaces.Plugin;
using log4net; using log4net;
using Encoder = System.Drawing.Imaging.Encoder;
namespace Greenshot.Base.Core namespace Greenshot.Base.Core
{ {
/// <summary> /// <summary>
/// Description of ImageOutput. /// This contains all io related logic for image
/// </summary> /// </summary>
public static class ImageOutput public static class ImageIO
{ {
private static readonly ILog Log = LogManager.GetLogger(typeof(ImageOutput)); private static readonly ILog Log = LogManager.GetLogger(typeof(ImageIO));
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>(); private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
private static readonly int PROPERTY_TAG_SOFTWARE_USED = 0x0131; private static readonly int PROPERTY_TAG_SOFTWARE_USED = 0x0131;
private static readonly Cache<string, string> TmpFileCache = new Cache<string, string>(10 * 60 * 60, RemoveExpiredTmpFile); private static readonly Cache<string, string> TmpFileCache = new Cache<string, string>(10 * 60 * 60, RemoveExpiredTmpFile);
@ -54,7 +53,7 @@ namespace Greenshot.Base.Core
/// <summary> /// <summary>
/// Creates a PropertyItem (Metadata) to store with the image. /// Creates a PropertyItem (Metadata) to store with the image.
/// For the possible ID's see: https://msdn.microsoft.com/de-de/library/system.drawing.imaging.propertyitem.id(v=vs.80).aspx /// For the possible ID's see: https://msdn.microsoft.com/de-de/library/system.drawing.imaging.propertyitem.id(v=vs.80).aspx
/// This code uses Reflection to create a PropertyItem, although it's not adviced it's not as stupid as having a image in the project so we can read a PropertyItem from that! /// This code uses Reflection to create a PropertyItem, although it's not advised it's not as stupid as having a image in the project so we can read a PropertyItem from that!
/// </summary> /// </summary>
/// <param name="id">ID</param> /// <param name="id">ID</param>
/// <param name="text">Text</param> /// <param name="text">Text</param>
@ -124,102 +123,21 @@ namespace Greenshot.Base.Core
try try
{ {
var imageFormat = outputSettings.Format switch // Check if we want to use a memory stream, to prevent issues with non seekable streams
{
OutputFormat.bmp => ImageFormat.Bmp,
OutputFormat.gif => ImageFormat.Gif,
OutputFormat.jpg => ImageFormat.Jpeg,
OutputFormat.tiff => ImageFormat.Tiff,
OutputFormat.ico => ImageFormat.Icon,
_ => ImageFormat.Png
};
Log.DebugFormat("Saving image to stream with Format {0} and PixelFormat {1}", imageFormat, imageToSave.PixelFormat);
// Check if we want to use a memory stream, to prevent issues with non seakable streams
// The save is made to the targetStream, this is directed to either the MemoryStream or the original // The save is made to the targetStream, this is directed to either the MemoryStream or the original
Stream targetStream = stream; Stream targetStream = stream;
if (!stream.CanSeek) if (!stream.CanSeek)
{ {
useMemoryStream = true; useMemoryStream = true;
Log.Warn("Using memorystream prevent an issue with saving to a non seekable stream."); Log.Warn("Using a memory stream prevent an issue with saving to a non seekable stream.");
memoryStream = new MemoryStream(); memoryStream = new MemoryStream();
targetStream = memoryStream; targetStream = memoryStream;
} }
if (Equals(imageFormat, ImageFormat.Jpeg)) var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances<IFileFormatHandler>();
if (!fileFormatHandlers.TrySaveToStream(imageToSave as Bitmap, targetStream, outputSettings.Format.ToString(), surface, outputSettings))
{ {
bool foundEncoder = false; return;
foreach (ImageCodecInfo imageCodec in ImageCodecInfo.GetImageEncoders())
{
if (imageCodec.FormatID == imageFormat.Guid)
{
EncoderParameters parameters = new EncoderParameters(1)
{
Param =
{
[0] = new EncoderParameter(Encoder.Quality, outputSettings.JPGQuality)
}
};
// Removing transparency if it's not supported in the output
if (Image.IsAlphaPixelFormat(imageToSave.PixelFormat))
{
Image nonAlphaImage = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb);
AddTag(nonAlphaImage);
nonAlphaImage.Save(targetStream, imageCodec, parameters);
nonAlphaImage.Dispose();
}
else
{
AddTag(imageToSave);
imageToSave.Save(targetStream, imageCodec, parameters);
}
foundEncoder = true;
break;
}
}
if (!foundEncoder)
{
throw new ApplicationException("No JPG encoder found, this should not happen.");
}
}
else if (Equals(imageFormat, ImageFormat.Icon))
{
// FEATURE-916: Added Icon support
IList<Image> images = new List<Image>
{
imageToSave
};
WriteIcon(stream, images);
}
else
{
bool needsDispose = false;
// Removing transparency if it's not supported in the output
if (!Equals(imageFormat, ImageFormat.Png) && Image.IsAlphaPixelFormat(imageToSave.PixelFormat))
{
imageToSave = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb);
needsDispose = true;
}
AddTag(imageToSave);
// Added for OptiPNG
bool processed = false;
if (Equals(imageFormat, ImageFormat.Png) && !string.IsNullOrEmpty(CoreConfig.OptimizePNGCommand))
{
processed = ProcessPngImageExternally(imageToSave, targetStream);
}
if (!processed)
{
imageToSave.Save(targetStream, imageFormat);
}
if (needsDispose)
{
imageToSave.Dispose();
}
} }
// If we used a memory stream, we need to stream the memory stream to the original stream. // If we used a memory stream, we need to stream the memory stream to the original stream.
@ -227,21 +145,6 @@ namespace Greenshot.Base.Core
{ {
memoryStream.WriteTo(stream); memoryStream.WriteTo(stream);
} }
// Output the surface elements, size and marker to the stream
if (outputSettings.Format != OutputFormat.greenshot)
{
return;
}
using MemoryStream tmpStream = new MemoryStream();
long bytesWritten = surface.SaveElementsToStream(tmpStream);
using BinaryWriter writer = new BinaryWriter(tmpStream);
writer.Write(bytesWritten);
Version v = Assembly.GetExecutingAssembly().GetName().Version;
byte[] marker = Encoding.ASCII.GetBytes($"Greenshot{v.Major:00}.{v.Minor:00}");
writer.Write(marker);
tmpStream.WriteTo(stream);
} }
finally finally
{ {
@ -249,89 +152,6 @@ namespace Greenshot.Base.Core
} }
} }
/// <summary>
/// Write the passed Image to a tmp-file and call an external process, than read the file back and write it to the targetStream
/// </summary>
/// <param name="imageToProcess">Image to pass to the external process</param>
/// <param name="targetStream">stream to write the processed image to</param>
/// <returns></returns>
private static bool ProcessPngImageExternally(Image imageToProcess, Stream targetStream)
{
if (string.IsNullOrEmpty(CoreConfig.OptimizePNGCommand))
{
return false;
}
if (!File.Exists(CoreConfig.OptimizePNGCommand))
{
Log.WarnFormat("Can't find 'OptimizePNGCommand' {0}", CoreConfig.OptimizePNGCommand);
return false;
}
string tmpFileName = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".png");
try
{
using (FileStream tmpStream = File.Create(tmpFileName))
{
Log.DebugFormat("Writing png to tmp file: {0}", tmpFileName);
imageToProcess.Save(tmpStream, ImageFormat.Png);
if (Log.IsDebugEnabled)
{
Log.DebugFormat("File size before processing {0}", new FileInfo(tmpFileName).Length);
}
}
if (Log.IsDebugEnabled)
{
Log.DebugFormat("Starting : {0}", CoreConfig.OptimizePNGCommand);
}
ProcessStartInfo processStartInfo = new ProcessStartInfo(CoreConfig.OptimizePNGCommand)
{
Arguments = string.Format(CoreConfig.OptimizePNGCommandArguments, tmpFileName),
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
};
using Process process = Process.Start(processStartInfo);
if (process != null)
{
process.WaitForExit();
if (process.ExitCode == 0)
{
if (Log.IsDebugEnabled)
{
Log.DebugFormat("File size after processing {0}", new FileInfo(tmpFileName).Length);
Log.DebugFormat("Reading back tmp file: {0}", tmpFileName);
}
byte[] processedImage = File.ReadAllBytes(tmpFileName);
targetStream.Write(processedImage, 0, processedImage.Length);
return true;
}
Log.ErrorFormat("Error while processing PNG image: {0}", process.ExitCode);
Log.ErrorFormat("Output: {0}", process.StandardOutput.ReadToEnd());
Log.ErrorFormat("Error: {0}", process.StandardError.ReadToEnd());
}
}
catch (Exception e)
{
Log.Error("Error while processing PNG image: ", e);
}
finally
{
if (File.Exists(tmpFileName))
{
Log.DebugFormat("Cleaning up tmp file: {0}", tmpFileName);
File.Delete(tmpFileName);
}
}
return false;
}
/// <summary> /// <summary>
/// Create an image from a surface with the settings from the output settings applied /// Create an image from a surface with the settings from the output settings applied
/// </summary> /// </summary>
@ -429,20 +249,18 @@ namespace Greenshot.Base.Core
/// Add the greenshot property! /// Add the greenshot property!
/// </summary> /// </summary>
/// <param name="imageToSave"></param> /// <param name="imageToSave"></param>
private static void AddTag(Image imageToSave) public static void AddTag(this Image imageToSave)
{ {
// Create meta-data // Create meta-data
PropertyItem softwareUsedPropertyItem = CreatePropertyItem(PROPERTY_TAG_SOFTWARE_USED, "Greenshot"); PropertyItem softwareUsedPropertyItem = CreatePropertyItem(PROPERTY_TAG_SOFTWARE_USED, "Greenshot");
if (softwareUsedPropertyItem != null) if (softwareUsedPropertyItem == null) return;
try
{ {
try imageToSave.SetPropertyItem(softwareUsedPropertyItem);
{ }
imageToSave.SetPropertyItem(softwareUsedPropertyItem); catch (Exception)
} {
catch (Exception) Log.WarnFormat("Couldn't set property {0}", softwareUsedPropertyItem.Id);
{
Log.WarnFormat("Couldn't set property {0}", softwareUsedPropertyItem.Id);
}
} }
} }
@ -463,7 +281,7 @@ namespace Greenshot.Base.Core
// Fixed lock problem Bug #3431881 // Fixed lock problem Bug #3431881
using (Stream surfaceFileStream = File.OpenRead(fullPath)) using (Stream surfaceFileStream = File.OpenRead(fullPath))
{ {
returnSurface = ImageHelper.LoadGreenshotSurface(surfaceFileStream, returnSurface); returnSurface = LoadGreenshotSurface(surfaceFileStream, returnSurface);
} }
if (returnSurface != null) if (returnSurface != null)
@ -547,27 +365,25 @@ namespace Greenshot.Base.Core
using (SaveImageFileDialog saveImageFileDialog = new SaveImageFileDialog(captureDetails)) using (SaveImageFileDialog saveImageFileDialog = new SaveImageFileDialog(captureDetails))
{ {
DialogResult dialogResult = saveImageFileDialog.ShowDialog(); DialogResult dialogResult = saveImageFileDialog.ShowDialog();
if (dialogResult.Equals(DialogResult.OK)) if (!dialogResult.Equals(DialogResult.OK)) return returnValue;
try
{ {
try string fileNameWithExtension = saveImageFileDialog.FileNameWithExtension;
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(FormatForFilename(fileNameWithExtension));
if (CoreConfig.OutputFilePromptQuality)
{ {
string fileNameWithExtension = saveImageFileDialog.FileNameWithExtension; QualityDialog qualityDialog = new QualityDialog(outputSettings);
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(FormatForFilename(fileNameWithExtension)); qualityDialog.ShowDialog();
if (CoreConfig.OutputFilePromptQuality) }
{
QualityDialog qualityDialog = new QualityDialog(outputSettings);
qualityDialog.ShowDialog();
}
// TODO: For now we always overwrite, should be changed // TODO: For now we always overwrite, should be changed
Save(surface, fileNameWithExtension, true, outputSettings, CoreConfig.OutputFileCopyPathToClipboard); Save(surface, fileNameWithExtension, true, outputSettings, CoreConfig.OutputFileCopyPathToClipboard);
returnValue = fileNameWithExtension; returnValue = fileNameWithExtension;
IniConfig.Save(); IniConfig.Save();
} }
catch (ExternalException) catch (ExternalException)
{ {
MessageBox.Show(Language.GetFormattedString("error_nowriteaccess", saveImageFileDialog.FileName).Replace(@"\\", @"\"), Language.GetString("error")); MessageBox.Show(Language.GetFormattedString("error_nowriteaccess", saveImageFileDialog.FileName).Replace(@"\\", @"\"), Language.GetString("error"));
}
} }
} }
@ -709,91 +525,131 @@ namespace Greenshot.Base.Core
} }
/// <summary> /// <summary>
/// Write the images to the stream as icon /// Load an image from file
/// Every image is resized to 256x256 (but the content maintains the aspect ratio)
/// </summary> /// </summary>
/// <param name="stream">Stream to write to</param> /// <param name="filename"></param>
/// <param name="images">List of images</param> /// <returns></returns>
public static void WriteIcon(Stream stream, IList<Image> images) public static Image LoadImage(string filename)
{ {
var binaryWriter = new BinaryWriter(stream); if (string.IsNullOrEmpty(filename))
//
// ICONDIR structure
//
binaryWriter.Write((short) 0); // reserved
binaryWriter.Write((short) 1); // image type (icon)
binaryWriter.Write((short) images.Count); // number of images
IList<Size> imageSizes = new List<Size>();
IList<MemoryStream> encodedImages = new List<MemoryStream>();
foreach (var image in images)
{ {
// Pick the best fit return null;
var sizes = new[]
{
16, 32, 48
};
int size = 256;
foreach (var possibleSize in sizes)
{
if (image.Width <= possibleSize && image.Height <= possibleSize)
{
size = possibleSize;
break;
}
}
var imageStream = new MemoryStream();
if (image.Width == size && image.Height == size)
{
using var clonedImage = ImageHelper.Clone(image, PixelFormat.Format32bppArgb);
clonedImage.Save(imageStream, ImageFormat.Png);
imageSizes.Add(new Size(size, size));
}
else
{
// Resize to the specified size, first make sure the image is 32bpp
using var clonedImage = ImageHelper.Clone(image, PixelFormat.Format32bppArgb);
using var resizedImage = ImageHelper.ResizeImage(clonedImage, true, true, Color.Empty, size, size, null);
resizedImage.Save(imageStream, ImageFormat.Png);
imageSizes.Add(resizedImage.Size);
}
imageStream.Seek(0, SeekOrigin.Begin);
encodedImages.Add(imageStream);
} }
// if (!File.Exists(filename))
// ICONDIRENTRY structure
//
const int iconDirSize = 6;
const int iconDirEntrySize = 16;
var offset = iconDirSize + (images.Count * iconDirEntrySize);
for (int i = 0; i < images.Count; i++)
{ {
var imageSize = imageSizes[i]; return null;
// Write the width / height, 0 means 256
binaryWriter.Write(imageSize.Width == 256 ? (byte) 0 : (byte) imageSize.Width);
binaryWriter.Write(imageSize.Height == 256 ? (byte) 0 : (byte) imageSize.Height);
binaryWriter.Write((byte) 0); // no pallete
binaryWriter.Write((byte) 0); // reserved
binaryWriter.Write((short) 0); // no color planes
binaryWriter.Write((short) 32); // 32 bpp
binaryWriter.Write((int) encodedImages[i].Length); // image data length
binaryWriter.Write(offset);
offset += (int) encodedImages[i].Length;
} }
binaryWriter.Flush(); Image fileImage;
// Log.InfoFormat("Loading image from file {0}", filename);
// Write image data // Fixed lock problem Bug #3431881
// using (Stream imageFileStream = File.OpenRead(filename))
foreach (var encodedImage in encodedImages)
{ {
encodedImage.WriteTo(stream); fileImage = FromStream(imageFileStream, Path.GetExtension(filename));
encodedImage.Dispose();
} }
if (fileImage != null)
{
Log.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", filename, fileImage.Width, fileImage.Height, fileImage.PixelFormat,
fileImage.HorizontalResolution, fileImage.VerticalResolution);
}
return fileImage;
}
/// <summary>
/// Create an image from a stream, if an extension is supplied more formats are supported.
/// </summary>
/// <param name="stream">Stream</param>
/// <param name="extension"></param>
/// <returns>Image</returns>
public static Image FromStream(Stream stream, string extension = null)
{
if (stream == null)
{
return null;
}
if (!string.IsNullOrEmpty(extension))
{
extension = extension.Replace(".", string.Empty);
}
var startingPosition = stream.Position;
// Make sure we can try multiple times
if (!stream.CanSeek)
{
var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
stream = memoryStream;
// As we are if a different stream, which starts at 0, change the starting position
startingPosition = 0;
}
var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances<IFileFormatHandler>();
foreach (var fileFormatHandler in fileFormatHandlers
.Where(ffh => ffh.Supports(FileFormatHandlerActions.LoadFromStream, extension))
.OrderBy(ffh => ffh.PriorityFor(FileFormatHandlerActions.LoadFromStream, extension)))
{
stream.Seek(startingPosition, SeekOrigin.Begin);
if (fileFormatHandler.TryLoadFromStream(stream, extension, out var bitmap))
{
return bitmap;
}
}
return null;
}
/// <summary>
/// Load a Greenshot surface from a stream
/// </summary>
/// <param name="surfaceFileStream">Stream</param>
/// <param name="returnSurface"></param>
/// <returns>ISurface</returns>
public static ISurface LoadGreenshotSurface(Stream surfaceFileStream, ISurface returnSurface)
{
Image fileImage;
// Fixed problem that the bitmap stream is disposed... by Cloning the image
// This also ensures the bitmap is correctly created
// We create a copy of the bitmap, so everything else can be disposed
surfaceFileStream.Position = 0;
using (Image tmpImage = Image.FromStream(surfaceFileStream, true, true))
{
Log.DebugFormat("Loaded .greenshot file with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat);
fileImage = ImageHelper.Clone(tmpImage);
}
// Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor)
const int markerSize = 14;
surfaceFileStream.Seek(-markerSize, SeekOrigin.End);
using (StreamReader streamReader = new StreamReader(surfaceFileStream))
{
var greenshotMarker = streamReader.ReadToEnd();
if (!greenshotMarker.StartsWith("Greenshot"))
{
throw new ArgumentException("Stream is not a Greenshot file!");
}
Log.InfoFormat("Greenshot file format: {0}", greenshotMarker);
const int filesizeLocation = 8 + markerSize;
surfaceFileStream.Seek(-filesizeLocation, SeekOrigin.End);
using BinaryReader reader = new BinaryReader(surfaceFileStream);
long bytesWritten = reader.ReadInt64();
surfaceFileStream.Seek(-(bytesWritten + filesizeLocation), SeekOrigin.End);
returnSurface.LoadElementsFromStream(surfaceFileStream);
}
if (fileImage != null)
{
returnSurface.Image = fileImage;
Log.InfoFormat("Information about .greenshot file: {0}x{1}-{2} Resolution {3}x{4}", fileImage.Width, fileImage.Height, fileImage.PixelFormat,
fileImage.HorizontalResolution, fileImage.VerticalResolution);
}
return returnSurface;
} }
} }
} }

View file

@ -1,77 +0,0 @@
using System.Drawing;
using System.Drawing.Imaging;
namespace Greenshot.Base.Core
{
/// <summary>
/// Wrap an image, make it resizeable
/// </summary>
public class ImageWrapper : IImage
{
// Underlying image, is used to generate a resized version of it when needed
private readonly Image _image;
private Image _imageClone;
public ImageWrapper(Image image)
{
// Make sure the orientation is set correctly so Greenshot can process the image correctly
ImageHelper.Orientate(image);
_image = image;
Width = _image.Width;
Height = _image.Height;
}
public void Dispose()
{
_image.Dispose();
_imageClone?.Dispose();
}
/// <summary>
/// Height of the image, can be set to change
/// </summary>
public int Height { get; set; }
/// <summary>
/// Width of the image, can be set to change.
/// </summary>
public int Width { get; set; }
/// <summary>
/// Size of the image
/// </summary>
public Size Size => new Size(Width, Height);
/// <summary>
/// Pixelformat of the underlying image
/// </summary>
public PixelFormat PixelFormat => Image.PixelFormat;
public float HorizontalResolution => Image.HorizontalResolution;
public float VerticalResolution => Image.VerticalResolution;
public Image Image
{
get
{
if (_imageClone == null)
{
if (_image.Height == Height && _image.Width == Width)
{
return _image;
}
}
if (_imageClone?.Height == Height && _imageClone?.Width == Width)
{
return _imageClone;
}
// Calculate new image clone
_imageClone?.Dispose();
_imageClone = ImageHelper.ResizeImage(_image, false, Width, Height, null);
return _imageClone;
}
}
}
}

View file

@ -470,7 +470,7 @@ namespace Greenshot.Base.Core
languageFile languageFile
}; };
LanguageFiles.Add(languageFile.Ietf, currentFiles); LanguageFiles.Add(languageFile.Ietf, currentFiles);
Log.InfoFormat("Added language definition {0} from: {1}", languageFile.Description, languageFile.Filepath); Log.DebugFormat("Added language definition {0} from: {1}", languageFile.Description, languageFile.Filepath);
} }
} }
} }

View file

@ -24,11 +24,14 @@ using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq;
using System.Net; using System.Net;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Greenshot.Base.Core.FileFormatHandlers;
using Greenshot.Base.IniFile; using Greenshot.Base.IniFile;
using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Base.Interfaces.Plugin; using Greenshot.Base.Interfaces.Plugin;
using log4net; using log4net;
@ -87,24 +90,14 @@ namespace Greenshot.Base.Core
} }
/// <summary> /// <summary>
/// Download the uri to Bitmap /// Download the uri to build an IDrawableContainer
/// </summary> /// </summary>
/// <param name="url">Of an image</param> /// <param name="url">Of an image</param>
/// <returns>Bitmap</returns> /// <returns>IDrawableContainer</returns>
public static Image DownloadImage(string url) public static IDrawableContainer DownloadImageAsDrawableContainer(string url)
{ {
var extensions = new StringBuilder(); var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances<IFileFormatHandler>();
foreach (var extension in ImageHelper.StreamConverters.Keys) var extensions = string.Join("|", fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadFromStream));
{
if (string.IsNullOrEmpty(extension))
{
continue;
}
extensions.AppendFormat(@"\.{0}|", extension);
}
extensions.Length--;
var imageUrlRegex = new Regex($@"(http|https)://.*(?<extension>{extensions})"); var imageUrlRegex = new Regex($@"(http|https)://.*(?<extension>{extensions})");
var match = imageUrlRegex.Match(url); var match = imageUrlRegex.Match(url);
@ -113,7 +106,12 @@ namespace Greenshot.Base.Core
using var memoryStream = GetAsMemoryStream(url); using var memoryStream = GetAsMemoryStream(url);
try try
{ {
return ImageHelper.FromStream(memoryStream, match.Success ? match.Groups["extension"]?.Value : null); var extension = match.Success ? match.Groups["extension"]?.Value : null;
var drawableContainer = fileFormatHandlers.LoadDrawablesFromStream(memoryStream, extension).FirstOrDefault();
if (drawableContainer != null)
{
return drawableContainer;
}
} }
catch (Exception) catch (Exception)
{ {
@ -136,7 +134,71 @@ namespace Greenshot.Base.Core
} }
using var memoryStream2 = GetAsMemoryStream(match.Value); using var memoryStream2 = GetAsMemoryStream(match.Value);
return ImageHelper.FromStream(memoryStream2, match.Groups["extension"]?.Value);
var extension = match.Success ? match.Groups["extension"]?.Value : null;
var drawableContainer = fileFormatHandlers.LoadDrawablesFromStream(memoryStream2, extension).FirstOrDefault();
if (drawableContainer != null)
{
return drawableContainer;
}
}
}
catch (Exception e)
{
Log.Error("Problem downloading the image from: " + url, e);
}
return null;
}
/// <summary>
/// Download the uri to create a Bitmap
/// </summary>
/// <param name="url">Of an image</param>
/// <returns>Bitmap</returns>
public static Bitmap DownloadImage(string url)
{
var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances<IFileFormatHandler>();
var extensions = string.Join("|", fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadFromStream));
var imageUrlRegex = new Regex($@"(http|https)://.*(?<extension>{extensions})");
var match = imageUrlRegex.Match(url);
try
{
using var memoryStream = GetAsMemoryStream(url);
try
{
if (fileFormatHandlers.TryLoadFromStream(memoryStream, match.Success ? match.Groups["extension"]?.Value : null, out var bitmap))
{
return bitmap;
}
}
catch (Exception)
{
// If we arrive here, the image loading didn't work, try to see if the response has a http(s) URL to an image and just take this instead.
string content;
using (var streamReader = new StreamReader(memoryStream, Encoding.UTF8, true))
{
content = streamReader.ReadLine();
}
if (string.IsNullOrEmpty(content))
{
throw;
}
match = imageUrlRegex.Match(content);
if (!match.Success)
{
throw;
}
using var memoryStream2 = GetAsMemoryStream(match.Value);
if (fileFormatHandlers.TryLoadFromStream(memoryStream2, match.Success ? match.Groups["extension"]?.Value : null, out var bitmap))
{
return bitmap;
}
} }
} }
catch (Exception e) catch (Exception e)
@ -670,7 +732,7 @@ namespace Greenshot.Base.Core
public string ToBase64String(Base64FormattingOptions formattingOptions) public string ToBase64String(Base64FormattingOptions formattingOptions)
{ {
using MemoryStream stream = new MemoryStream(); using MemoryStream stream = new MemoryStream();
ImageOutput.SaveToStream(_surface, stream, _outputSettings); ImageIO.SaveToStream(_surface, stream, _outputSettings);
return Convert.ToBase64String(stream.GetBuffer(), 0, (int) stream.Length, formattingOptions); return Convert.ToBase64String(stream.GetBuffer(), 0, (int) stream.Length, formattingOptions);
} }
@ -682,7 +744,7 @@ namespace Greenshot.Base.Core
public byte[] ToByteArray() public byte[] ToByteArray()
{ {
using MemoryStream stream = new MemoryStream(); using MemoryStream stream = new MemoryStream();
ImageOutput.SaveToStream(_surface, stream, _outputSettings); ImageIO.SaveToStream(_surface, stream, _outputSettings);
return stream.ToArray(); return stream.ToArray();
} }
@ -698,7 +760,7 @@ namespace Greenshot.Base.Core
string header = $"--{boundary}\r\nContent-Disposition: form-data; name=\"{name}\"; filename=\"{Filename ?? name}\";\r\nContent-Type: {ContentType}\r\n\r\n"; string header = $"--{boundary}\r\nContent-Disposition: form-data; name=\"{name}\"; filename=\"{Filename ?? name}\";\r\nContent-Type: {ContentType}\r\n\r\n";
formDataStream.Write(Encoding.UTF8.GetBytes(header), 0, Encoding.UTF8.GetByteCount(header)); formDataStream.Write(Encoding.UTF8.GetBytes(header), 0, Encoding.UTF8.GetByteCount(header));
ImageOutput.SaveToStream(_surface, formDataStream, _outputSettings); ImageIO.SaveToStream(_surface, formDataStream, _outputSettings);
} }
/// <summary> /// <summary>
@ -708,7 +770,7 @@ namespace Greenshot.Base.Core
public void WriteToStream(Stream dataStream) public void WriteToStream(Stream dataStream)
{ {
// Write the file data directly to the Stream, rather than serializing it to a string. // Write the file data directly to the Stream, rather than serializing it to a string.
ImageOutput.SaveToStream(_surface, dataStream, _outputSettings); ImageIO.SaveToStream(_surface, dataStream, _outputSettings);
} }
/// <summary> /// <summary>

View file

@ -24,9 +24,10 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Linq;
using System.Windows.Forms; using System.Windows.Forms;
using Dapplo.Windows.Icons;
using Greenshot.Base.IniFile; using Greenshot.Base.IniFile;
using Greenshot.Base.UnmanagedHelpers;
using log4net; using log4net;
using Microsoft.Win32; using Microsoft.Win32;
@ -39,9 +40,9 @@ namespace Greenshot.Base.Core
{ {
private static readonly ILog Log = LogManager.GetLogger(typeof(PluginUtils)); private static readonly ILog Log = LogManager.GetLogger(typeof(PluginUtils));
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>(); private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
private const string PathKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\";
private static readonly IDictionary<string, Image> ExeIconCache = new Dictionary<string, Image>(); private static readonly IDictionary<string, Image> ExeIconCache = new Dictionary<string, Image>();
private const string PathKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\";
static PluginUtils() static PluginUtils()
{ {
CoreConfig.PropertyChanged += OnIconSizeChanged; CoreConfig.PropertyChanged += OnIconSizeChanged;
@ -84,7 +85,7 @@ namespace Greenshot.Base.Core
if (key != null) if (key != null)
{ {
// "" is the default key, which should point to the requested location // "" is the default key, which should point to the requested location
return (string) key.GetValue(string.Empty); return (string)key.GetValue(string.Empty);
} }
} }
@ -113,7 +114,7 @@ namespace Greenshot.Base.Core
/// </summary> /// </summary>
/// <param name="path">path to the exe or dll</param> /// <param name="path">path to the exe or dll</param>
/// <param name="index">index of the icon</param> /// <param name="index">index of the icon</param>
/// <returns>Bitmap with the icon or null if something happended</returns> /// <returns>Bitmap with the icon or null if something happened</returns>
public static Image GetCachedExeIcon(string path, int index) public static Image GetCachedExeIcon(string path, int index)
{ {
string cacheKey = $"{path}:{index}"; string cacheKey = $"{path}:{index}";
@ -148,7 +149,7 @@ namespace Greenshot.Base.Core
/// </summary> /// </summary>
/// <param name="path">path to the exe or dll</param> /// <param name="path">path to the exe or dll</param>
/// <param name="index">index of the icon</param> /// <param name="index">index of the icon</param>
/// <returns>Bitmap with the icon or null if something happended</returns> /// <returns>Bitmap with the icon or null if something happened</returns>
private static Bitmap GetExeIcon(string path, int index) private static Bitmap GetExeIcon(string path, int index)
{ {
if (!File.Exists(path)) if (!File.Exists(path))
@ -158,20 +159,11 @@ namespace Greenshot.Base.Core
try try
{ {
using (Icon appIcon = ImageHelper.ExtractAssociatedIcon(path, index, CoreConfig.UseLargeIcons)) var appIcon = IconHelper.ExtractAssociatedIcon<Bitmap>(path, index, CoreConfig.UseLargeIcons);
if (appIcon != null)
{ {
if (appIcon != null) Log.DebugFormat("Loaded icon for {0}, with dimensions {1}x{2}", path, appIcon.Width, appIcon.Height);
{ return appIcon;
return appIcon.ToBitmap();
}
}
using (Icon appIcon = Shell32.GetFileIcon(path, CoreConfig.UseLargeIcons ? Shell32.IconSize.Large : Shell32.IconSize.Small, false))
{
if (appIcon != null)
{
return appIcon.ToBitmap();
}
} }
} }
catch (Exception exIcon) catch (Exception exIcon)
@ -195,27 +187,25 @@ namespace Greenshot.Base.Core
// Try to find a separator, so we insert ourselves after it // Try to find a separator, so we insert ourselves after it
for (int i = 0; i < contextMenu.Items.Count; i++) for (int i = 0; i < contextMenu.Items.Count; i++)
{ {
if (contextMenu.Items[i].GetType() == typeof(ToolStripSeparator)) if (contextMenu.Items[i].GetType() != typeof(ToolStripSeparator)) continue;
// Check if we need to add a new separator, which is done if the first found has a Tag with the value "PluginsAreAddedBefore"
if ("PluginsAreAddedBefore".Equals(contextMenu.Items[i].Tag))
{ {
// Check if we need to add a new separator, which is done if the first found has a Tag with the value "PluginsAreAddedBefore" var separator = new ToolStripSeparator
if ("PluginsAreAddedBefore".Equals(contextMenu.Items[i].Tag))
{ {
var separator = new ToolStripSeparator Tag = "PluginsAreAddedAfter",
{ Size = new Size(305, 6)
Tag = "PluginsAreAddedAfter", };
Size = new Size(305, 6) contextMenu.Items.Insert(i, separator);
};
contextMenu.Items.Insert(i, separator);
}
else if (!"PluginsAreAddedAfter".Equals(contextMenu.Items[i].Tag))
{
continue;
}
contextMenu.Items.Insert(i + 1, item);
addedItem = true;
break;
} }
else if (!"PluginsAreAddedAfter".Equals(contextMenu.Items[i].Tag))
{
continue;
}
contextMenu.Items.Insert(i + 1, item);
addedItem = true;
break;
} }
// If we didn't insert the item, we just add it... // If we didn't insert the item, we just add it...

View file

@ -10,22 +10,19 @@ namespace Greenshot.Base.Core
/// </summary> /// </summary>
public class SimpleServiceProvider : IServiceLocator public class SimpleServiceProvider : IServiceLocator
{ {
private readonly Dictionary<Type, List<object>> _services = new Dictionary<Type, List<object>>(); private readonly Dictionary<Type, IList<object>> _services = new();
public static IServiceLocator Current { get; } = new SimpleServiceProvider(); public static IServiceLocator Current { get; } = new SimpleServiceProvider();
public IEnumerable<TService> GetAllInstances<TService>() public IReadOnlyList<TService> GetAllInstances<TService>()
{ {
var typeOfService = typeof(TService); var typeOfService = typeof(TService);
if (!_services.TryGetValue(typeOfService, out var results)) if (!_services.TryGetValue(typeOfService, out var results))
{ {
yield break; return Array.Empty<TService>();
} }
foreach (TService result in results) return results.Cast<TService>().ToArray();
{
yield return result;
}
} }
public TService GetInstance<TService>() public TService GetInstance<TService>()

View file

@ -1,117 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using Svg;
namespace Greenshot.Base.Core
{
/// <summary>
/// Create an image look like of the SVG
/// </summary>
public sealed class SvgImage : IImage
{
private readonly SvgDocument _svgDocument;
private Image _imageClone;
/// <summary>
/// Factory to create via a stream
/// </summary>
/// <param name="stream">Stream</param>
/// <returns>IImage</returns>
public static IImage FromStream(Stream stream)
{
return new SvgImage(stream);
}
/// <summary>
/// Default constructor
/// </summary>
/// <param name="stream"></param>
public SvgImage(Stream stream)
{
_svgDocument = SvgDocument.Open<SvgDocument>(stream);
Height = (int) _svgDocument.ViewBox.Height;
Width = (int) _svgDocument.ViewBox.Width;
}
/// <summary>
/// Height of the image, can be set to change
/// </summary>
public int Height { get; set; }
/// <summary>
/// Width of the image, can be set to change.
/// </summary>
public int Width { get; set; }
/// <summary>
/// Size of the image
/// </summary>
public Size Size => new Size(Width, Height);
/// <summary>
/// Pixelformat of the underlying image
/// </summary>
public PixelFormat PixelFormat => Image.PixelFormat;
/// <summary>
/// Horizontal resolution of the underlying image
/// </summary>
public float HorizontalResolution => Image.HorizontalResolution;
/// <summary>
/// Vertical resolution of the underlying image
/// </summary>
public float VerticalResolution => Image.VerticalResolution;
/// <summary>
/// Underlying image, or an on demand rendered version with different attributes as the original
/// </summary>
public Image Image
{
get
{
if (_imageClone?.Height == Height && _imageClone?.Width == Width)
{
return _imageClone;
}
// Calculate new image clone
_imageClone?.Dispose();
_imageClone = ImageHelper.CreateEmpty(Width, Height, PixelFormat.Format32bppArgb, Color.Transparent, 96, 96);
_svgDocument.Draw((Bitmap) _imageClone);
return _imageClone;
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
_imageClone?.Dispose();
}
}
}

View file

@ -26,10 +26,20 @@ using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Windows.Forms; using System.Windows.Forms;
using Dapplo.Windows.Common.Extensions;
using Dapplo.Windows.Common.Structs;
using Dapplo.Windows.Gdi32;
using Dapplo.Windows.Gdi32.Enums;
using Dapplo.Windows.Gdi32.SafeHandles;
using Dapplo.Windows.Gdi32.Structs;
using Dapplo.Windows.Icons;
using Dapplo.Windows.Icons.SafeHandles;
using Dapplo.Windows.Kernel32;
using Dapplo.Windows.User32;
using Dapplo.Windows.User32.Enums;
using Dapplo.Windows.User32.Structs;
using Greenshot.Base.IniFile; using Greenshot.Base.IniFile;
using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces;
using Greenshot.Base.UnmanagedHelpers;
using Greenshot.Base.UnmanagedHelpers.Structs;
using log4net; using log4net;
namespace Greenshot.Base.Core namespace Greenshot.Base.Core
@ -42,35 +52,6 @@ namespace Greenshot.Base.Core
private static readonly ILog Log = LogManager.GetLogger(typeof(WindowCapture)); private static readonly ILog Log = LogManager.GetLogger(typeof(WindowCapture));
private static readonly CoreConfiguration Configuration = IniConfig.GetIniSection<CoreConfiguration>(); private static readonly CoreConfiguration Configuration = IniConfig.GetIniSection<CoreConfiguration>();
/// <summary>
/// Used to cleanup the unmanaged resource in the iconInfo for the CaptureCursor method
/// </summary>
/// <param name="hObject"></param>
/// <returns></returns>
[DllImport("gdi32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DeleteObject(IntPtr hObject);
/// <summary>
/// Get the bounds of all screens combined.
/// </summary>
/// <returns>A Rectangle of the bounds of the entire display area.</returns>
public static Rectangle GetScreenBounds()
{
int left = 0, top = 0, bottom = 0, right = 0;
foreach (Screen screen in Screen.AllScreens)
{
left = Math.Min(left, screen.Bounds.X);
top = Math.Min(top, screen.Bounds.Y);
int screenAbsRight = screen.Bounds.X + screen.Bounds.Width;
int screenAbsBottom = screen.Bounds.Y + screen.Bounds.Height;
right = Math.Max(right, screenAbsRight);
bottom = Math.Max(bottom, screenAbsBottom);
}
return new Rectangle(left, top, (right + Math.Abs(left)), (bottom + Math.Abs(top)));
}
/// <summary> /// <summary>
/// Retrieves the cursor location safely, accounting for DPI settings in Vista/Windows 7. This implementation /// Retrieves the cursor location safely, accounting for DPI settings in Vista/Windows 7. This implementation
/// can conveniently be used when the cursor location is needed to deal with a fullscreen bitmap. /// can conveniently be used when the cursor location is needed to deal with a fullscreen bitmap.
@ -78,9 +59,9 @@ namespace Greenshot.Base.Core
/// <returns> /// <returns>
/// Point with cursor location, relative to the top left corner of the monitor setup (which itself might actually not be on any screen) /// Point with cursor location, relative to the top left corner of the monitor setup (which itself might actually not be on any screen)
/// </returns> /// </returns>
public static Point GetCursorLocationRelativeToScreenBounds() public static NativePoint GetCursorLocationRelativeToScreenBounds()
{ {
return GetLocationRelativeToScreenBounds(User32.GetCursorLocation()); return GetLocationRelativeToScreenBounds(User32Api.GetCursorLocation());
} }
/// <summary> /// <summary>
@ -90,12 +71,10 @@ namespace Greenshot.Base.Core
/// </summary> /// </summary>
/// <param name="locationRelativeToScreenOrigin"></param> /// <param name="locationRelativeToScreenOrigin"></param>
/// <returns>Point</returns> /// <returns>Point</returns>
public static Point GetLocationRelativeToScreenBounds(Point locationRelativeToScreenOrigin) public static NativePoint GetLocationRelativeToScreenBounds(NativePoint locationRelativeToScreenOrigin)
{ {
Point ret = locationRelativeToScreenOrigin; NativeRect bounds = DisplayInfo.ScreenBounds;
Rectangle bounds = GetScreenBounds(); return locationRelativeToScreenOrigin.Offset(-bounds.X, -bounds.Y);
ret.Offset(-bounds.X, -bounds.Y);
return ret;
} }
/// <summary> /// <summary>
@ -110,36 +89,26 @@ namespace Greenshot.Base.Core
capture = new Capture(); capture = new Capture();
} }
var cursorInfo = new CursorInfo(); var cursorInfo = CursorInfo.Create();
cursorInfo.cbSize = Marshal.SizeOf(cursorInfo); if (!NativeCursorMethods.GetCursorInfo(ref cursorInfo)) return capture;
if (!User32.GetCursorInfo(out cursorInfo)) return capture; if (cursorInfo.Flags != CursorInfoFlags.Showing) return capture;
if (cursorInfo.flags != User32.CURSOR_SHOWING) return capture;
using SafeIconHandle safeIcon = User32.CopyIcon(cursorInfo.hCursor); using SafeIconHandle safeIcon = NativeIconMethods.CopyIcon(cursorInfo.CursorHandle);
if (!User32.GetIconInfo(safeIcon, out var iconInfo)) return capture; if (!NativeIconMethods.GetIconInfo(safeIcon, out var iconInfo)) return capture;
Point cursorLocation = User32.GetCursorLocation(); NativePoint cursorLocation = User32Api.GetCursorLocation();
// Align cursor location to Bitmap coordinates (instead of Screen coordinates) // Align cursor location to Bitmap coordinates (instead of Screen coordinates)
var x = cursorLocation.X - iconInfo.xHotspot - capture.ScreenBounds.X; var x = cursorLocation.X - iconInfo.Hotspot.X - capture.ScreenBounds.X;
var y = cursorLocation.Y - iconInfo.yHotspot - capture.ScreenBounds.Y; var y = cursorLocation.Y - iconInfo.Hotspot.Y - capture.ScreenBounds.Y;
// Set the location // Set the location
capture.CursorLocation = new Point(x, y); capture.CursorLocation = new NativePoint(x, y);
using (Icon icon = Icon.FromHandle(safeIcon.DangerousGetHandle())) using (Icon icon = Icon.FromHandle(safeIcon.DangerousGetHandle()))
{ {
capture.Cursor = icon; capture.Cursor = icon;
} }
iconInfo.BitmaskBitmapHandle.Dispose();
if (iconInfo.hbmMask != IntPtr.Zero) iconInfo.ColorBitmapHandle.Dispose();
{
DeleteObject(iconInfo.hbmMask);
}
if (iconInfo.hbmColor != IntPtr.Zero)
{
DeleteObject(iconInfo.hbmColor);
}
return capture; return capture;
} }
@ -161,11 +130,11 @@ namespace Greenshot.Base.Core
/// Helper method to create an exception that might explain what is wrong while capturing /// Helper method to create an exception that might explain what is wrong while capturing
/// </summary> /// </summary>
/// <param name="method">string with current method</param> /// <param name="method">string with current method</param>
/// <param name="captureBounds">Rectangle of what we want to capture</param> /// <param name="captureBounds">NativeRect of what we want to capture</param>
/// <returns></returns> /// <returns></returns>
private static Exception CreateCaptureException(string method, Rectangle captureBounds) private static Exception CreateCaptureException(string method, NativeRect captureBounds)
{ {
Exception exceptionToThrow = User32.CreateWin32Exception(method); Exception exceptionToThrow = User32Api.CreateWin32Exception(method);
if (!captureBounds.IsEmpty) if (!captureBounds.IsEmpty)
{ {
exceptionToThrow.Data.Add("Height", captureBounds.Height); exceptionToThrow.Data.Add("Height", captureBounds.Height);
@ -233,9 +202,9 @@ namespace Greenshot.Base.Core
/// This method will use User32 code to capture the specified captureBounds from the screen /// This method will use User32 code to capture the specified captureBounds from the screen
/// </summary> /// </summary>
/// <param name="capture">ICapture where the captured Bitmap will be stored</param> /// <param name="capture">ICapture where the captured Bitmap will be stored</param>
/// <param name="captureBounds">Rectangle with the bounds to capture</param> /// <param name="captureBounds">NativeRect with the bounds to capture</param>
/// <returns>A Capture Object with a part of the Screen as an Image</returns> /// <returns>A Capture Object with a part of the Screen as an Image</returns>
public static ICapture CaptureRectangle(ICapture capture, Rectangle captureBounds) public static ICapture CaptureRectangle(ICapture capture, NativeRect captureBounds)
{ {
if (capture == null) if (capture == null)
{ {
@ -271,9 +240,9 @@ namespace Greenshot.Base.Core
/// This method will use User32 code to capture the specified captureBounds from the screen /// This method will use User32 code to capture the specified captureBounds from the screen
/// </summary> /// </summary>
/// <param name="capture">ICapture where the captured Bitmap will be stored</param> /// <param name="capture">ICapture where the captured Bitmap will be stored</param>
/// <param name="captureBounds">Rectangle with the bounds to capture</param> /// <param name="captureBounds">NativeRect with the bounds to capture</param>
/// <returns>A Capture Object with a part of the Screen as an Image</returns> /// <returns>A Capture Object with a part of the Screen as an Image</returns>
public static ICapture CaptureRectangleFromDesktopScreen(ICapture capture, Rectangle captureBounds) public static ICapture CaptureRectangleFromDesktopScreen(ICapture capture, NativeRect captureBounds)
{ {
if (capture == null) if (capture == null)
{ {
@ -288,9 +257,9 @@ namespace Greenshot.Base.Core
/// <summary> /// <summary>
/// This method will use User32 code to capture the specified captureBounds from the screen /// This method will use User32 code to capture the specified captureBounds from the screen
/// </summary> /// </summary>
/// <param name="captureBounds">Rectangle with the bounds to capture</param> /// <param name="captureBounds">NativeRect with the bounds to capture</param>
/// <returns>Bitmap which is captured from the screen at the location specified by the captureBounds</returns> /// <returns>Bitmap which is captured from the screen at the location specified by the captureBounds</returns>
public static Bitmap CaptureRectangle(Rectangle captureBounds) public static Bitmap CaptureRectangle(NativeRect captureBounds)
{ {
Bitmap returnBitmap = null; Bitmap returnBitmap = null;
if (captureBounds.Height <= 0 || captureBounds.Width <= 0) if (captureBounds.Height <= 0 || captureBounds.Width <= 0)
@ -321,7 +290,7 @@ namespace Greenshot.Base.Core
} }
// create a device context we can copy to // create a device context we can copy to
using SafeCompatibleDCHandle safeCompatibleDcHandle = GDI32.CreateCompatibleDC(desktopDcHandle); using SafeCompatibleDcHandle safeCompatibleDcHandle = Gdi32Api.CreateCompatibleDC(desktopDcHandle);
// Check if the device context is there, if not throw an error with as much info as possible! // Check if the device context is there, if not throw an error with as much info as possible!
if (safeCompatibleDcHandle.IsInvalid) if (safeCompatibleDcHandle.IsInvalid)
{ {
@ -332,13 +301,13 @@ namespace Greenshot.Base.Core
} }
// Create BITMAPINFOHEADER for CreateDIBSection // Create BITMAPINFOHEADER for CreateDIBSection
BITMAPINFOHEADERV5 bmi = new BITMAPINFOHEADERV5(captureBounds.Width, captureBounds.Height, 24); var bitmapInfoHeader = BitmapV5Header.Create(captureBounds.Width, captureBounds.Height, 24);
// Make sure the last error is set to 0 // Make sure the last error is set to 0
Win32.SetLastError(0); Kernel32Api.SetLastError(0);
// create a bitmap we can copy it to, using GetDeviceCaps to get the width/height // create a bitmap we can copy it to, using GetDeviceCaps to get the width/height
using SafeDibSectionHandle safeDibSectionHandle = GDI32.CreateDIBSection(desktopDcHandle, ref bmi, BITMAPINFOHEADERV5.DIB_RGB_COLORS, out _, IntPtr.Zero, 0); using SafeDibSectionHandle safeDibSectionHandle = Gdi32Api.CreateDIBSection(desktopDcHandle, ref bitmapInfoHeader, DibColors.RgbColors, out _, IntPtr.Zero, 0);
if (safeDibSectionHandle.IsInvalid) if (safeDibSectionHandle.IsInvalid)
{ {
// Get Exception before the error is lost // Get Exception before the error is lost
@ -355,8 +324,8 @@ namespace Greenshot.Base.Core
{ {
// bitblt over (make copy) // bitblt over (make copy)
// ReSharper disable once BitwiseOperatorOnEnumWithoutFlags // ReSharper disable once BitwiseOperatorOnEnumWithoutFlags
GDI32.BitBlt(safeCompatibleDcHandle, 0, 0, captureBounds.Width, captureBounds.Height, desktopDcHandle, captureBounds.X, captureBounds.Y, Gdi32Api.BitBlt(safeCompatibleDcHandle, 0, 0, captureBounds.Width, captureBounds.Height, desktopDcHandle, captureBounds.X, captureBounds.Y,
CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt); RasterOperations.SourceCopy | RasterOperations.CaptureBlt);
} }
// get a .NET image object for it // get a .NET image object for it
@ -388,7 +357,7 @@ namespace Greenshot.Base.Core
} }
// If the region is not empty, we have "offscreenContent" // If the region is not empty, we have "offscreenContent"
using Graphics screenGraphics = Graphics.FromHwnd(User32.GetDesktopWindow()); using Graphics screenGraphics = Graphics.FromHwnd(User32Api.GetDesktopWindow());
offscreenContent = !captureRegion.IsEmpty(screenGraphics); offscreenContent = !captureRegion.IsEmpty(screenGraphics);
} }
@ -397,17 +366,16 @@ namespace Greenshot.Base.Core
{ {
using Bitmap tmpBitmap = Image.FromHbitmap(safeDibSectionHandle.DangerousGetHandle()); using Bitmap tmpBitmap = Image.FromHbitmap(safeDibSectionHandle.DangerousGetHandle());
// Create a new bitmap which has a transparent background // Create a new bitmap which has a transparent background
returnBitmap = ImageHelper.CreateEmpty(tmpBitmap.Width, tmpBitmap.Height, PixelFormat.Format32bppArgb, Color.Transparent, returnBitmap = ImageHelper.CreateEmpty(tmpBitmap.Width, tmpBitmap.Height, PixelFormat.Format32bppArgb, Color.Transparent, tmpBitmap.HorizontalResolution, tmpBitmap.VerticalResolution);
tmpBitmap.HorizontalResolution, tmpBitmap.VerticalResolution);
// Content will be copied here // Content will be copied here
using Graphics graphics = Graphics.FromImage(returnBitmap); using Graphics graphics = Graphics.FromImage(returnBitmap);
// For all screens copy the content to the new bitmap // For all screens copy the content to the new bitmap
foreach (Screen screen in Screen.AllScreens)
foreach (var displayInfo in DisplayInfo.AllDisplayInfos)
{ {
Rectangle screenBounds = screen.Bounds; // Make sure the bounds are offset to the capture bounds
// Make sure the bounds are offsetted to the capture bounds var displayBounds = displayInfo.Bounds.Offset(-captureBounds.X, -captureBounds.Y);
screenBounds.Offset(-captureBounds.X, -captureBounds.Y); graphics.DrawImage(tmpBitmap, displayBounds, displayBounds.X, displayBounds.Y, displayBounds.Width, displayBounds.Height, GraphicsUnit.Pixel);
graphics.DrawImage(tmpBitmap, screenBounds, screenBounds.X, screenBounds.Y, screenBounds.Width, screenBounds.Height, GraphicsUnit.Pixel);
} }
} }
else else

File diff suppressed because it is too large Load diff

View file

@ -21,8 +21,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using Dapplo.Windows.User32;
using Greenshot.Base.UnmanagedHelpers;
namespace Greenshot.Base.Core namespace Greenshot.Base.Core
{ {
@ -44,15 +43,13 @@ namespace Greenshot.Base.Core
public WindowsEnumerator GetWindows(IntPtr hWndParent, string classname) public WindowsEnumerator GetWindows(IntPtr hWndParent, string classname)
{ {
Items = new List<WindowDetails>(); Items = new List<WindowDetails>();
User32.EnumChildWindows(hWndParent, WindowEnum, IntPtr.Zero); User32Api.EnumChildWindows(hWndParent, OnWindowEnum, IntPtr.Zero);
bool hasParent = !IntPtr.Zero.Equals(hWndParent); bool hasParent = !IntPtr.Zero.Equals(hWndParent);
string parentText = null; string parentText = null;
if (hasParent) if (hasParent)
{ {
var title = new StringBuilder(260, 260); parentText = User32Api.GetText(hWndParent);
User32.GetWindowText(hWndParent, title, title.Capacity);
parentText = title.ToString();
} }
var windows = new List<WindowDetails>(); var windows = new List<WindowDetails>();
@ -74,17 +71,6 @@ namespace Greenshot.Base.Core
return this; return this;
} }
/// <summary>
/// The enum Windows callback.
/// </summary>
/// <param name="hWnd">Window Handle</param>
/// <param name="lParam">Application defined value</param>
/// <returns>1 to continue enumeration, 0 to stop</returns>
private int WindowEnum(IntPtr hWnd, int lParam)
{
return OnWindowEnum(hWnd) ? 1 : 0;
}
/// <summary> /// <summary>
/// Called whenever a new window is about to be added /// Called whenever a new window is about to be added
/// by the Window enumeration called from GetWindows. /// by the Window enumeration called from GetWindows.
@ -94,8 +80,9 @@ namespace Greenshot.Base.Core
/// be empty. /// be empty.
/// </summary> /// </summary>
/// <param name="hWnd">Window handle to add</param> /// <param name="hWnd">Window handle to add</param>
/// <param name="lParam"></param>
/// <returns>True to continue enumeration, False to stop</returns> /// <returns>True to continue enumeration, False to stop</returns>
private bool OnWindowEnum(IntPtr hWnd) private bool OnWindowEnum(IntPtr hWnd, IntPtr lParam)
{ {
if (!WindowDetails.IsIgnoreHandle(hWnd)) if (!WindowDetails.IsIgnoreHandle(hWnd))
{ {

View file

@ -20,7 +20,7 @@
*/ */
using System.Windows.Forms; using System.Windows.Forms;
using Greenshot.Base.UnmanagedHelpers.Enums; using Dapplo.Windows.Messages.Enumerations;
using log4net; using log4net;
namespace Greenshot.Base.Core namespace Greenshot.Base.Core
@ -51,16 +51,13 @@ namespace Greenshot.Base.Core
public static bool PreFilterMessageExternal(ref Message m) public static bool PreFilterMessageExternal(ref Message m)
{ {
WindowsMessages message = (WindowsMessages) m.Msg; WindowsMessages message = (WindowsMessages) m.Msg;
if (message == WindowsMessages.WM_INPUTLANGCHANGEREQUEST || message == WindowsMessages.WM_INPUTLANGCHANGE) if (message != WindowsMessages.WM_INPUTLANGCHANGEREQUEST && message != WindowsMessages.WM_INPUTLANGCHANGE) return false;
{
LOG.WarnFormat("Filtering: {0}, {1:X} - {2:X} - {3:X}", message, m.LParam.ToInt64(), m.WParam.ToInt64(), m.HWnd.ToInt64());
// For now we always return true
return true;
// But it could look something like this:
//return (m.LParam.ToInt64() | 0x7FFFFFFF) != 0;
}
return false; LOG.DebugFormat("Filtering: {0}, {1:X} - {2:X} - {3:X}", message, m.LParam.ToInt64(), m.WParam.ToInt64(), m.HWnd.ToInt64());
// For now we always return true
return true;
// But it could look something like this:
//return (m.LParam.ToInt64() | 0x7FFFFFFF) != 0;
} }
} }
} }

View file

@ -23,6 +23,7 @@ using System.ComponentModel;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using Dapplo.Windows.Common.Structs;
using Greenshot.Base.Core; using Greenshot.Base.Core;
namespace Greenshot.Base.Effects namespace Greenshot.Base.Effects
@ -42,7 +43,7 @@ namespace Greenshot.Base.Effects
public int ShadowSize { get; set; } public int ShadowSize { get; set; }
public Point ShadowOffset { get; set; } public NativePoint ShadowOffset { get; set; }
public virtual void Reset() public virtual void Reset()
{ {

View file

@ -3,12 +3,22 @@
<PropertyGroup> <PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Dapplo.HttpExtensions.JsonNet" Version="1.0.16" /> <PackageReference Include="Dapplo.HttpExtensions.JsonNet" Version="1.1.2" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.42" /> <PackageReference Include="Dapplo.Windows.Clipboard" Version="1.0.28" />
<PackageReference Include="log4net" version="2.0.14" /> <PackageReference Include="Dapplo.Windows.Dpi" Version="1.0.28" />
<PackageReference Include="Svg" Version="3.4.0" /> <PackageReference Include="Dapplo.Windows.Gdi32" Version="1.0.28" />
<PackageReference Include="Dapplo.Windows.Icons" Version="1.0.28" />
<PackageReference Include="Dapplo.Windows.Kernel32" Version="1.0.28" />
<PackageReference Include="Dapplo.Windows.Multimedia" Version="1.0.28" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.46" />
<PackageReference Include="log4net" version="2.0.15" />
<PackageReference Include="Svg" Version="3.4.3" />
<Reference Include="Accessibility" /> <Reference Include="Accessibility" />
<Reference Include="CustomMarshalers" /> <Reference Include="CustomMarshalers" />
</ItemGroup> </ItemGroup>

View file

@ -1,151 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Greenshot.Base.UnmanagedHelpers.Enums;
namespace Greenshot.Base.Hooking
{
/// <summary>
/// The WinEventHook can register handlers to become important windows events
/// This makes it possible to know a.o. when a window is created, moved, updated and closed.
/// </summary>
public class WindowsEventHook : IDisposable
{
private readonly WinEventDelegate _winEventHandler;
private GCHandle _gcHandle;
/// <summary>
/// Used with Register hook
/// </summary>
/// <param name="eventType"></param>
/// <param name="hWnd"></param>
/// <param name="idObject"></param>
/// <param name="idChild"></param>
/// <param name="dwEventThread"></param>
/// <param name="dwmsEventTime"></param>
public delegate void WinEventHandler(WinEvent eventType, IntPtr hWnd, EventObjects idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
/// <summary>
/// Create a WindowsEventHook object
/// </summary>
public WindowsEventHook()
{
_winEventHandler = WinEventDelegateHandler;
_gcHandle = GCHandle.Alloc(_winEventHandler);
}
[DllImport("user32", SetLastError = true)]
private static extern bool UnhookWinEvent(IntPtr hWinEventHook);
[DllImport("user32", SetLastError = true)]
private static extern IntPtr SetWinEventHook(WinEvent eventMin, WinEvent eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, int idProcess, int idThread,
WinEventHookFlags dwFlags);
/// <summary>
/// Used with SetWinEventHook
/// </summary>
/// <param name="hWinEventHook"></param>
/// <param name="eventType"></param>
/// <param name="hWnd"></param>
/// <param name="idObject"></param>
/// <param name="idChild"></param>
/// <param name="dwEventThread"></param>
/// <param name="dwmsEventTime"></param>
private delegate void WinEventDelegate(IntPtr hWinEventHook, WinEvent eventType, IntPtr hWnd, EventObjects idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
private readonly IDictionary<IntPtr, WinEventHandler> _winEventHandlers = new Dictionary<IntPtr, WinEventHandler>();
/// <summary>
/// Are hooks active?
/// </summary>
public bool IsHooked => _winEventHandlers.Count > 0;
/// <summary>
/// Hook a WinEvent
/// </summary>
/// <param name="winEvent"></param>
/// <param name="winEventHandler"></param>
/// <returns>true if success</returns>
public void Hook(WinEvent winEvent, WinEventHandler winEventHandler)
{
Hook(winEvent, winEvent, winEventHandler);
}
/// <summary>
/// Hook a WinEvent
/// </summary>
/// <param name="winEventStart"></param>
/// <param name="winEventEnd"></param>
/// <param name="winEventHandler"></param>
public void Hook(WinEvent winEventStart, WinEvent winEventEnd, WinEventHandler winEventHandler)
{
var hookPtr = SetWinEventHook(winEventStart, winEventEnd, IntPtr.Zero, _winEventHandler, 0, 0,
WinEventHookFlags.WINEVENT_SKIPOWNPROCESS | WinEventHookFlags.WINEVENT_OUTOFCONTEXT);
_winEventHandlers.Add(hookPtr, winEventHandler);
}
/// <summary>
/// Remove all hooks
/// </summary>
private void Unhook()
{
foreach (var hookPtr in _winEventHandlers.Keys)
{
if (hookPtr != IntPtr.Zero)
{
UnhookWinEvent(hookPtr);
}
}
_winEventHandlers.Clear();
_gcHandle.Free();
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Unhook();
}
/// <summary>
/// Call the WinEventHandler for this event
/// </summary>
/// <param name="hWinEventHook"></param>
/// <param name="eventType"></param>
/// <param name="hWnd"></param>
/// <param name="idObject"></param>
/// <param name="idChild"></param>
/// <param name="dwEventThread"></param>
/// <param name="dwmsEventTime"></param>
private void WinEventDelegateHandler(IntPtr hWinEventHook, WinEvent eventType, IntPtr hWnd, EventObjects idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
if (_winEventHandlers.TryGetValue(hWinEventHook, out var handler))
{
handler(eventType, hWnd, idObject, idChild, dwEventThread, dwmsEventTime);
}
}
}
}

View file

@ -1,180 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using Greenshot.Base.Core;
using Greenshot.Base.UnmanagedHelpers.Enums;
namespace Greenshot.Base.Hooking
{
/// <summary>
/// Event arguments for the WindowOpenCloseEvent
/// </summary>
public class WindowOpenCloseEventArgs : EventArgs
{
public bool IsOpen { get; set; }
/// <summary>
/// HWnd of the window which has a changed title
/// </summary>
public IntPtr HWnd { get; set; }
/// <summary>
/// Title which is changed
/// </summary>
public string Title { get; set; }
public string ClassName { get; set; }
}
/// <summary>
/// Delegate for the window open close event
/// </summary>
/// <param name="eventArgs"></param>
public delegate void WindowOpenCloseEventDelegate(WindowOpenCloseEventArgs eventArgs);
/// <summary>
/// Monitor all new and destroyed windows
/// </summary>
public sealed class WindowsOpenCloseMonitor : IDisposable
{
private WindowsEventHook _hook;
private readonly object _lockObject = new object();
// ReSharper disable once InconsistentNaming
private event WindowOpenCloseEventDelegate _windowOpenCloseEvent;
/// <summary>
/// Add / remove event handler to the title monitor
/// </summary>
public event WindowOpenCloseEventDelegate WindowOpenCloseChangeEvent
{
add
{
lock (_lockObject)
{
if (_hook == null)
{
_hook = new WindowsEventHook();
_hook.Hook(WinEvent.EVENT_OBJECT_CREATE, WinEvent.EVENT_OBJECT_DESTROY, WinEventHandler);
}
_windowOpenCloseEvent += value;
}
}
remove
{
lock (_lockObject)
{
_windowOpenCloseEvent -= value;
if (_windowOpenCloseEvent == null || _windowOpenCloseEvent.GetInvocationList().Length == 0)
{
if (_hook != null)
{
_hook.Dispose();
_hook = null;
}
}
}
}
}
/// <summary>
/// WinEventDelegate for the creation and destruction
/// </summary>
/// <param name="eventType"></param>
/// <param name="hWnd"></param>
/// <param name="idObject"></param>
/// <param name="idChild"></param>
/// <param name="dwEventThread"></param>
/// <param name="dwmsEventTime"></param>
private void WinEventHandler(WinEvent eventType, IntPtr hWnd, EventObjects idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
if (hWnd == IntPtr.Zero || idObject != EventObjects.OBJID_WINDOW)
{
return;
}
if (eventType == WinEvent.EVENT_OBJECT_CREATE)
{
if (_windowOpenCloseEvent != null)
{
var windowsDetails = new WindowDetails(hWnd);
_windowOpenCloseEvent(new WindowOpenCloseEventArgs
{
HWnd = hWnd,
IsOpen = true,
Title = windowsDetails.Text,
ClassName = windowsDetails.ClassName
});
}
}
if (eventType == WinEvent.EVENT_OBJECT_DESTROY)
{
_windowOpenCloseEvent?.Invoke(new WindowOpenCloseEventArgs
{
HWnd = hWnd,
IsOpen = false
});
}
}
private bool _disposedValue; // To detect redundant calls
/// <summary>
/// Dispose the underlying hook
/// </summary>
public void Dispose(bool disposing)
{
if (_disposedValue)
{
return;
}
lock (_lockObject)
{
_hook?.Dispose();
}
_disposedValue = true;
}
/// <summary>
/// Make sure the finalizer disposes the underlying hook
/// </summary>
~WindowsOpenCloseMonitor()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(false);
}
/// <summary>
/// Dispose the underlying hook
/// </summary>
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
GC.SuppressFinalize(this);
}
}
}

View file

@ -1,165 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using Greenshot.Base.Core;
using Greenshot.Base.UnmanagedHelpers.Enums;
namespace Greenshot.Base.Hooking
{
/// <summary>
/// Event arguments for the TitleChangeEvent
/// </summary>
public class TitleChangeEventArgs : EventArgs
{
/// <summary>
/// HWnd of the window which has a changed title
/// </summary>
public IntPtr HWnd { get; set; }
/// <summary>
/// Title which is changed
/// </summary>
public string Title { get; set; }
}
/// <summary>
/// Delegate for the title change event
/// </summary>
/// <param name="eventArgs"></param>
public delegate void TitleChangeEventDelegate(TitleChangeEventArgs eventArgs);
/// <summary>
/// Monitor all title changes
/// </summary>
public sealed class WindowsTitleMonitor : IDisposable
{
private WindowsEventHook _hook;
private readonly object _lockObject = new object();
// ReSharper disable once InconsistentNaming
private event TitleChangeEventDelegate _titleChangeEvent;
/// <summary>
/// Add / remove event handler to the title monitor
/// </summary>
public event TitleChangeEventDelegate TitleChangeEvent
{
add
{
lock (_lockObject)
{
if (_hook == null)
{
_hook = new WindowsEventHook();
_hook.Hook(WinEvent.EVENT_OBJECT_NAMECHANGE, WinEventHandler);
}
_titleChangeEvent += value;
}
}
remove
{
lock (_lockObject)
{
_titleChangeEvent -= value;
if (_titleChangeEvent == null || _titleChangeEvent.GetInvocationList().Length == 0)
{
if (_hook != null)
{
_hook.Dispose();
_hook = null;
}
}
}
}
}
/// <summary>
/// WinEventDelegate for the creation & destruction
/// </summary>
/// <param name="eventType"></param>
/// <param name="hWnd"></param>
/// <param name="idObject"></param>
/// <param name="idChild"></param>
/// <param name="dwEventThread"></param>
/// <param name="dwmsEventTime"></param>
private void WinEventHandler(WinEvent eventType, IntPtr hWnd, EventObjects idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
if (hWnd == IntPtr.Zero || idObject != EventObjects.OBJID_WINDOW)
{
return;
}
if (eventType == WinEvent.EVENT_OBJECT_NAMECHANGE)
{
if (_titleChangeEvent != null)
{
string newTitle = new WindowDetails(hWnd).Text;
_titleChangeEvent(new TitleChangeEventArgs
{
HWnd = hWnd,
Title = newTitle
});
}
}
}
private bool _disposedValue; // To detect redundant calls
/// <summary>
/// Dispose the underlying hook
/// </summary>
public void Dispose(bool disposing)
{
if (_disposedValue)
{
return;
}
lock (_lockObject)
{
_hook?.Dispose();
}
_disposedValue = true;
}
/// <summary>
/// Make sure the finalizer disposes the underlying hook
/// </summary>
~WindowsTitleMonitor()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(false);
}
/// <summary>
/// Dispose the underlying hook
/// </summary>
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
GC.SuppressFinalize(this);
}
}
}

View file

@ -22,6 +22,7 @@
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Windows.Forms; using System.Windows.Forms;
using Dapplo.Windows.Common.Structs;
namespace Greenshot.Base.Interfaces.Drawing.Adorners namespace Greenshot.Base.Interfaces.Drawing.Adorners
{ {
@ -35,7 +36,7 @@ namespace Greenshot.Base.Interfaces.Drawing.Adorners
/// <summary> /// <summary>
/// These are the bounds of the adorner /// These are the bounds of the adorner
/// </summary> /// </summary>
Rectangle Bounds { get; } NativeRect Bounds { get; }
/// <summary> /// <summary>
/// The current edit status, this is needed to locate the adorner to send events to /// The current edit status, this is needed to locate the adorner to send events to
@ -53,7 +54,7 @@ namespace Greenshot.Base.Interfaces.Drawing.Adorners
/// </summary> /// </summary>
/// <param name="point">Point to test</param> /// <param name="point">Point to test</param>
/// <returns>true if so</returns> /// <returns>true if so</returns>
bool HitTest(Point point); bool HitTest(NativePoint point);
/// <summary> /// <summary>
/// Handle the MouseDown event /// Handle the MouseDown event
@ -97,6 +98,21 @@ namespace Greenshot.Base.Interfaces.Drawing.Adorners
/// Adjust UI elements to the supplied DPI settings /// Adjust UI elements to the supplied DPI settings
/// </summary> /// </summary>
/// <param name="dpi"></param> /// <param name="dpi"></param>
void AdjustToDpi(uint dpi); void AdjustToDpi(int dpi);
/// <summary>
/// The color of the lines around the adorner
/// </summary>
Color OutlineColor { get; set; }
/// <summary>
/// The color of the fill of the adorner
/// </summary>
Color FillColor { get; set; }
/// <summary>
/// This is to TAG the adorner so we know the type
/// </summary>
string Tag { get; set; }
} }
} }

View file

@ -22,15 +22,23 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Windows.Forms;
using Dapplo.Windows.Common.Structs;
using Greenshot.Base.Interfaces.Drawing.Adorners; using Greenshot.Base.Interfaces.Drawing.Adorners;
namespace Greenshot.Base.Interfaces.Drawing namespace Greenshot.Base.Interfaces.Drawing
{ {
public interface IDrawableContainer : INotifyPropertyChanged, IDisposable public interface IDrawableContainer : INotifyPropertyChanged, IDisposable
{ {
/// <summary>
/// The parent surface where this IDrawableContainer is on
/// </summary>
ISurface Parent { get; set; } ISurface Parent { get; set; }
/// <summary>
/// Is this IDrawableContainer selected on the surface
/// </summary>
bool Selected { get; set; } bool Selected { get; set; }
int Left { get; set; } int Left { get; set; }
@ -41,28 +49,43 @@ namespace Greenshot.Base.Interfaces.Drawing
int Height { get; set; } int Height { get; set; }
Point Location { get; } NativePoint Location { get; }
Size Size { get; } NativeSize Size { get; }
Rectangle Bounds { get; } NativeRect Bounds { get; }
Rectangle DrawingBounds { get; } NativeRect DrawingBounds { get; }
void ApplyBounds(RectangleF newBounds); void ApplyBounds(NativeRectFloat newBounds);
bool HasFilters { get; } bool HasFilters { get; }
EditStatus Status { get; set; } EditStatus Status { get; set; }
void Invalidate(); void Invalidate();
bool ClickableAt(int x, int y); bool ClickableAt(int x, int y);
void MoveBy(int x, int y); void MoveBy(int x, int y);
void Transform(Matrix matrix); void Transform(Matrix matrix);
bool HandleMouseDown(int x, int y); bool HandleMouseDown(int x, int y);
void HandleMouseUp(int x, int y); void HandleMouseUp(int x, int y);
bool HandleMouseMove(int x, int y); bool HandleMouseMove(int x, int y);
bool InitContent(); bool InitContent();
/// <summary>
/// Defines if the drawable container participates in undo / redo
/// </summary>
bool IsUndoable { get; }
void MakeBoundsChangeUndoable(bool allowMerge); void MakeBoundsChangeUndoable(bool allowMerge);
EditStatus DefaultEditMode { get; } EditStatus DefaultEditMode { get; }
/// <summary> /// <summary>
@ -70,10 +93,23 @@ namespace Greenshot.Base.Interfaces.Drawing
/// </summary> /// </summary>
IList<IAdorner> Adorners { get; } IList<IAdorner> Adorners { get; }
/// <summary>
/// Is confirm/cancel possible for this container
/// </summary>
bool IsConfirmable { get; }
/// <summary> /// <summary>
/// Adjust UI elements to the supplied DPI settings /// Adjust UI elements to the supplied DPI settings
/// </summary> /// </summary>
/// <param name="dpi">uint</param> /// <param name="dpi">int</param>
void AdjustToDpi(uint dpi); void AdjustToDpi(int dpi);
/// <summary>
/// Enable a way for elements to add a context menu entry
/// </summary>
/// <param name="menu">ContextMenuStrip</param>
/// <param name="surface">ISurface</param>
/// <param name="mouseEventArgs">MouseEventArgs</param>
void AddContextMenuItems(ContextMenuStrip menu, ISurface surface, MouseEventArgs mouseEventArgs);
} }
} }

View file

@ -24,6 +24,7 @@ using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Windows.Forms; using System.Windows.Forms;
using Dapplo.Windows.Common.Structs;
namespace Greenshot.Base.Interfaces.Drawing namespace Greenshot.Base.Interfaces.Drawing
{ {
@ -35,16 +36,16 @@ namespace Greenshot.Base.Interfaces.Drawing
ISurface Parent { get; set; } ISurface Parent { get; set; }
EditStatus Status { get; set; } EditStatus Status { get; set; }
Rectangle DrawingBounds { get; } NativeRect DrawingBounds { get; }
void MakeBoundsChangeUndoable(bool allowMerge); void MakeBoundsChangeUndoable(bool allowMerge);
void Transform(Matrix matrix); void Transform(Matrix matrix);
void MoveBy(int dx, int dy); void MoveBy(int dx, int dy);
bool ClickableAt(int x, int y); bool ClickableAt(int x, int y);
IDrawableContainer ClickableElementAt(int x, int y); IDrawableContainer ClickableElementAt(int x, int y);
void OnDoubleClick(); void OnDoubleClick();
bool HasIntersectingFilters(Rectangle clipRectangle); bool HasIntersectingFilters(NativeRect clipRectangle);
bool IntersectsWith(Rectangle clipRectangle); bool IntersectsWith(NativeRect clipRectangle);
void Draw(Graphics g, Bitmap bitmap, RenderMode renderMode, Rectangle clipRectangle); void Draw(Graphics g, Bitmap bitmap, RenderMode renderMode, NativeRect clipRectangle);
void SetForegroundColor(Color color); void SetForegroundColor(Color color);
void SetBackgroundColor(Color color); void SetBackgroundColor(Color color);
int IncreaseLineThickness(int increaseBy); int IncreaseLineThickness(int increaseBy);
@ -58,6 +59,6 @@ namespace Greenshot.Base.Interfaces.Drawing
void PushElementsToBottom(IDrawableContainerList elements); void PushElementsToBottom(IDrawableContainerList elements);
void ShowContextMenu(MouseEventArgs e, ISurface surface); void ShowContextMenu(MouseEventArgs e, ISurface surface);
void HandleFieldChangedEvent(object sender, FieldChangedEventArgs e); void HandleFieldChangedEvent(object sender, FieldChangedEventArgs e);
void AdjustToDpi(uint dpi); void AdjustToDpi(int dpi);
} }
} }

View file

@ -19,16 +19,18 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
using System.Diagnostics.CodeAnalysis; namespace Greenshot.Base.Interfaces.Drawing
namespace Greenshot.Base.UnmanagedHelpers.Enums
{ {
/// <summary> /// <summary>
/// See <a href="https://docs.microsoft.com/en-gb/windows/win32/winauto/object-identifiers">Object Identifiers</a> /// The IFieldAggregator defines the connections between fields and containers
/// </summary> /// </summary>
[SuppressMessage("ReSharper", "InconsistentNaming")] public interface IFieldAggregator
public enum EventObjects
{ {
OBJID_WINDOW = 0 void UnbindElement(IDrawableContainer dc);
void BindElements(IDrawableContainerList dcs);
void BindElement(IDrawableContainer dc);
IField GetField(IFieldType fieldType);
event FieldChangedEventHandler FieldChanged;
} }
} }

View file

@ -21,6 +21,7 @@
using System; using System;
using System.Drawing; using System.Drawing;
using Dapplo.Windows.Common.Structs;
namespace Greenshot.Base.Interfaces namespace Greenshot.Base.Interfaces
{ {
@ -47,7 +48,7 @@ namespace Greenshot.Base.Interfaces
/// <summary> /// <summary>
/// Bounds on the screen from which the capture comes /// Bounds on the screen from which the capture comes
/// </summary> /// </summary>
Rectangle ScreenBounds { get; set; } NativeRect ScreenBounds { get; set; }
/// <summary> /// <summary>
/// The cursor /// The cursor
@ -62,18 +63,18 @@ namespace Greenshot.Base.Interfaces
/// <summary> /// <summary>
/// Location of the cursor /// Location of the cursor
/// </summary> /// </summary>
Point CursorLocation { get; set; } NativePoint CursorLocation { get; set; }
/// <summary> /// <summary>
/// Location of the capture /// Location of the capture
/// </summary> /// </summary>
Point Location { get; set; } NativePoint Location { get; set; }
/// <summary> /// <summary>
/// Crops the capture to the specified rectangle (with Bitmap coordinates!) /// Crops the capture to the specified rectangle (with Bitmap coordinates!)
/// </summary> /// </summary>
/// <param name="cropRectangle">Rectangle with bitmap coordinates</param> /// <param name="cropRectangle">NativeRect with bitmap coordinates</param>
bool Crop(Rectangle cropRectangle); bool Crop(NativeRect cropRectangle);
/// <summary> /// <summary>
/// Apply a translate to the mouse location. e.g. needed for crop /// Apply a translate to the mouse location. e.g. needed for crop

View file

@ -0,0 +1,88 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Base.Interfaces.Plugin;
namespace Greenshot.Base.Interfaces
{
/// <summary>
/// The possible actions a IFileFormatHandler might support
/// </summary>
public enum FileFormatHandlerActions
{
SaveToStream,
LoadFromStream,
LoadDrawableFromStream
}
/// <summary>
/// This interface is for code to implement the loading and saving of certain file formats
/// </summary>
public interface IFileFormatHandler
{
/// <summary>
/// Registry for all the extensions this IFileFormatHandler support
/// </summary>
IDictionary<FileFormatHandlerActions, IReadOnlyCollection<string>> SupportedExtensions { get; }
/// <summary>
/// Priority (from high int.MinValue, low int.MaxValue) of this IFileFormatHandler for the specified action and extension
/// This should be used to sort the possible IFileFormatHandler
/// </summary>
/// <param name="fileFormatHandlerAction">FileFormatHandlerActions</param>
/// <param name="extension">string</param>
/// <returns>int specifying the priority for the action and extension</returns>
public int PriorityFor(FileFormatHandlerActions fileFormatHandlerAction, string extension);
/// <summary>
/// Try to save the specified bitmap to the stream in the format belonging to the extension
/// </summary>
/// <param name="bitmap">Bitmap</param>
/// <param name="destination">Stream</param>
/// <param name="extension">extension</param>
/// <param name="surface">ISurface with the elements for those file types which can store a surface (.greenshot)</param>
/// <param name="surfaceOutputSettings">SurfaceOutputSettings</param>
/// <returns>bool true if it was successful</returns>
public bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null);
/// <summary>
///
/// </summary>
/// <param name="stream"></param>
/// <param name="extension"></param>
/// <param name="bitmap"></param>
/// <returns>bool true if it was successful</returns>
public bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap);
/// <summary>
/// Try to load a drawable container from the stream
/// </summary>
/// <param name="stream">Stream</param>
/// <param name="extension">string</param>
/// <param name="parentSurface">ISurface</param>
/// <returns>IEnumerable{IDrawableContainer}</returns>
public IEnumerable<IDrawableContainer> LoadDrawablesFromStream(Stream stream, string extension, ISurface parentSurface = null);
}
}

View file

@ -1,4 +1,4 @@
/* /*
* Greenshot - a free and open source screenshot tool * Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
* *
@ -19,15 +19,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
using System; namespace Greenshot.Base.Interfaces
namespace Greenshot.Base.UnmanagedHelpers
{ {
/// <summary> /// <summary>
/// Used with EnumWindows or EnumChildWindows /// IProvideDeviceDpi can provide the current DPI for a component
/// </summary> /// </summary>
/// <param name="hWnd">IntPtr</param> public interface IProvideDeviceDpi
/// <param name="lParam">int</param> {
/// <returns>int</returns> /// <summary>
public delegate int EnumWindowsProc(IntPtr hWnd, int lParam); /// A simple getter for the current DPI
/// </summary>
int DeviceDpi { get; }
}
} }

View file

@ -32,7 +32,7 @@ namespace Greenshot.Base.Interfaces
/// </summary> /// </summary>
/// <typeparam name="TService">Service to find</typeparam> /// <typeparam name="TService">Service to find</typeparam>
/// <returns>IEnumerable{TService}</returns> /// <returns>IEnumerable{TService}</returns>
IEnumerable<TService> GetAllInstances<TService>(); IReadOnlyList<TService> GetAllInstances<TService>();
/// <summary> /// <summary>
/// Get the only instance of the specified service /// Get the only instance of the specified service

View file

@ -21,8 +21,10 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO; using System.IO;
using System.Windows.Forms; using System.Windows.Forms;
using Dapplo.Windows.Common.Structs;
using Greenshot.Base.Core; using Greenshot.Base.Core;
using Greenshot.Base.Effects; using Greenshot.Base.Effects;
using Greenshot.Base.Interfaces.Drawing; using Greenshot.Base.Interfaces.Drawing;
@ -100,13 +102,49 @@ namespace Greenshot.Base.Interfaces
long SaveElementsToStream(Stream stream); long SaveElementsToStream(Stream stream);
void LoadElementsFromStream(Stream stream); void LoadElementsFromStream(Stream stream);
/// <summary>
/// Provides the selected elements
/// </summary>
IDrawableContainerList SelectedElements { get; }
/// <summary>
/// Is there an element selected on the surface?
/// </summary>
bool HasSelectedElements { get; } bool HasSelectedElements { get; }
/// <summary>
/// Remove all selected elements
/// </summary>
void RemoveSelectedElements(); void RemoveSelectedElements();
/// <summary>
/// Cut the selected elements to the clipboard
/// </summary>
void CutSelectedElements(); void CutSelectedElements();
/// <summary>
/// Copy the selected elements to the clipboard
/// </summary>
void CopySelectedElements(); void CopySelectedElements();
/// <summary>
/// Paste the elements from the clipboard
/// </summary>
void PasteElementFromClipboard(); void PasteElementFromClipboard();
/// <summary>
/// Duplicate the selected elements
/// </summary>
void DuplicateSelectedElements(); void DuplicateSelectedElements();
/// <summary>
/// Deselected the specified element
/// </summary>
void DeselectElement(IDrawableContainer container, bool generateEvents = true); void DeselectElement(IDrawableContainer container, bool generateEvents = true);
/// <summary>
/// Deselected all elements
/// </summary>
void DeselectAllElements(); void DeselectAllElements();
/// <summary> /// <summary>
@ -148,8 +186,8 @@ namespace Greenshot.Base.Interfaces
/// Invalidates the specified region of the Surface. /// Invalidates the specified region of the Surface.
/// Takes care of the Surface zoom level, accepts rectangle in the coordinate space of the Image. /// Takes care of the Surface zoom level, accepts rectangle in the coordinate space of the Image.
/// </summary> /// </summary>
/// <param name="rectangleToInvalidate">Bounding rectangle for updated elements, in the coordinate space of the Image.</param> /// <param name="rectangleToInvalidate">NativeRect Bounding rectangle for updated elements, in the coordinate space of the Image.</param>
void InvalidateElements(Rectangle rectangleToInvalidate); void InvalidateElements(NativeRect rectangleToInvalidate);
bool Modified { get; set; } bool Modified { get; set; }
string LastSaveFullPath { get; set; } string LastSaveFullPath { get; set; }
@ -177,29 +215,54 @@ namespace Greenshot.Base.Interfaces
Fraction ZoomFactor { get; set; } Fraction ZoomFactor { get; set; }
/// <summary> /// <summary>
/// Translate a point from image coorditate space to surface coordinate space. /// Translate a point from image coordinate space to surface coordinate space.
/// </summary> /// </summary>
/// <param name="point">A point in the coordinate space of the image.</param> /// <param name="point">A point in the coordinate space of the image.</param>
Point ToSurfaceCoordinates(Point point); NativePoint ToSurfaceCoordinates(NativePoint point);
/// <summary> /// <summary>
/// Translate a rectangle from image coorditate space to surface coordinate space. /// Translate a rectangle from image coordinate space to surface coordinate space.
/// </summary> /// </summary>
/// <param name="rc">A rectangle in the coordinate space of the image.</param> /// <param name="rc">NativeRect in the coordinate space of the image.</param>
Rectangle ToSurfaceCoordinates(Rectangle rc); NativeRect ToSurfaceCoordinates(NativeRect rc);
/// <summary> /// <summary>
/// Translate a point from surface coorditate space to image coordinate space. /// Translate a point from surface coordinate space to image coordinate space.
/// </summary> /// </summary>
/// <param name="point">A point in the coordinate space of the surface.</param> /// <param name="point">NativePoint in the coordinate space of the surface.</param>
Point ToImageCoordinates(Point point); NativePoint ToImageCoordinates(NativePoint point);
/// <summary> /// <summary>
/// Translate a rectangle from surface coorditate space to image coordinate space. /// Translate a NativeRect from surface coordinate space to image coordinate space.
/// </summary> /// </summary>
/// <param name="rc">A rectangle in the coordinate space of the surface.</param> /// <param name="rc">NativeRect in the coordinate space of the surface.</param>
Rectangle ToImageCoordinates(Rectangle rc); NativeRect ToImageCoordinates(NativeRect rc);
/// <summary>
/// Make it possible to undo the specified IMemento
/// </summary>
/// <param name="memento">IMemento</param>
/// <param name="allowMerge">bool to specify if the action can be merged, e.g. we do not want an undo for every part of a resize</param>
void MakeUndoable(IMemento memento, bool allowMerge); void MakeUndoable(IMemento memento, bool allowMerge);
/// <summary>
/// The IFieldAggregator
/// </summary>
IFieldAggregator FieldAggregator { get; }
/// <summary>
/// This reverses a change of the background image
/// </summary>
/// <param name="previous">Image</param>
/// <param name="matrix">Matrix</param>
void UndoBackgroundChange(Image previous, Matrix matrix);
/// <summary>
/// The most recent DPI value that was used
/// </summary>
public int CurrentDpi
{
get;
}
} }
} }

View file

@ -1,4 +1,26 @@
using System.Drawing; /*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using Dapplo.Windows.Common.Extensions;
using Dapplo.Windows.Common.Structs;
namespace Greenshot.Base.Interfaces.Ocr namespace Greenshot.Base.Interfaces.Ocr
{ {
@ -7,7 +29,7 @@ namespace Greenshot.Base.Interfaces.Ocr
/// </summary> /// </summary>
public class Line public class Line
{ {
private Rectangle? _calculatedBounds; private NativeRect? _calculatedBounds;
/// <summary> /// <summary>
/// Constructor will preallocate the number of words /// Constructor will preallocate the number of words
@ -35,18 +57,18 @@ namespace Greenshot.Base.Interfaces.Ocr
/// <summary> /// <summary>
/// Calculate the bounds of the words /// Calculate the bounds of the words
/// </summary> /// </summary>
/// <returns>Rectangle</returns> /// <returns>NativeRect</returns>
private Rectangle CalculateBounds() private NativeRect CalculateBounds()
{ {
if (Words.Length == 0) if (Words.Length == 0)
{ {
return Rectangle.Empty; return NativeRect.Empty;
} }
var result = Words[0].Bounds; var result = Words[0].Bounds;
for (var index = 0; index < Words.Length; index++) for (var index = 0; index < Words.Length; index++)
{ {
result = Rectangle.Union(result, Words[index].Bounds); result = result.Union(Words[index].Bounds);
} }
return result; return result;
@ -55,7 +77,7 @@ namespace Greenshot.Base.Interfaces.Ocr
/// <summary> /// <summary>
/// Return the calculated bounds for the whole line /// Return the calculated bounds for the whole line
/// </summary> /// </summary>
public Rectangle CalculatedBounds public NativeRect CalculatedBounds
{ {
get { return _calculatedBounds ??= CalculateBounds(); } get { return _calculatedBounds ??= CalculateBounds(); }
} }
@ -69,9 +91,7 @@ namespace Greenshot.Base.Interfaces.Ocr
{ {
foreach (var word in Words) foreach (var word in Words)
{ {
var location = word.Bounds; word.Bounds = word.Bounds.Offset(x, y);
location.Offset(x, y);
word.Bounds = location;
} }
_calculatedBounds = null; _calculatedBounds = null;

View file

@ -1,4 +1,25 @@
using System.Collections.Generic; /*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;

View file

@ -1,4 +1,25 @@
using System.Drawing; /*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using Dapplo.Windows.Common.Structs;
namespace Greenshot.Base.Interfaces.Ocr namespace Greenshot.Base.Interfaces.Ocr
{ {
@ -15,6 +36,6 @@ namespace Greenshot.Base.Interfaces.Ocr
/// <summary> /// <summary>
/// The bounds of the word /// The bounds of the word
/// </summary> /// </summary>
public Rectangle Bounds { get; set; } public NativeRect Bounds { get; set; }
} }
} }

View file

@ -1,6 +1,6 @@
/* /*
* Greenshot - a free and open source screenshot tool * Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom
* *
* For more information see: https://getgreenshot.org/ * For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
@ -19,25 +19,28 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
using System.Diagnostics.CodeAnalysis; using System;
namespace Greenshot.Base.UnmanagedHelpers.Enums namespace Greenshot.Base.Interfaces.Plugin
{ {
/// <summary> /// <summary>
/// Used by GDI32.GetDeviceCaps /// Attribute to specify a custom plugin identifier at assembly level
/// See <a href="https://docs.microsoft.com/en-gb/windows/win32/api/wingdi/nf-wingdi-getdevicecaps">GetDeviceCaps</a>
/// </summary> /// </summary>
[SuppressMessage("ReSharper", "InconsistentNaming")] [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
public enum DeviceCaps public class AssemblyPluginIdentifierAttribute : Attribute
{ {
/// <summary> /// <summary>
/// Logical pixels inch in X /// The identifier used for the plugin in configuration
/// </summary> /// </summary>
LOGPIXELSX = 88, public string Identifier { get; }
/// <summary> /// <summary>
/// Current vertical refresh rate of the display device (for displays only) in Hz /// Constructor for the plugin identifier attribute
/// </summary> /// </summary>
VREFRESH = 116 /// <param name="identifier">The identifier for the plugin in configuration</param>
public AssemblyPluginIdentifierAttribute(string identifier)
{
Identifier = identifier;
}
} }
} }

View file

@ -1,4 +1,25 @@
using System.Collections.Generic; /*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System.Collections.Generic;
using Greenshot.Base.Core; using Greenshot.Base.Core;
using Greenshot.Base.Core.Enums; using Greenshot.Base.Core.Enums;
using Greenshot.Base.Effects; using Greenshot.Base.Effects;

View file

@ -20,7 +20,6 @@
*/ */
using System; using System;
using System.Drawing;
namespace Greenshot.Base.Interfaces namespace Greenshot.Base.Interfaces
{ {

View file

@ -20,7 +20,6 @@
*/ */
using System; using System;
using System.Drawing;
namespace Greenshot.Base.Interfaces namespace Greenshot.Base.Interfaces
{ {

View file

@ -1,123 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using Greenshot.Base.Core;
using Greenshot.Base.Core.Enums;
using Greenshot.Base.UnmanagedHelpers.Enums;
using Greenshot.Base.UnmanagedHelpers.Structs;
using Microsoft.Win32;
namespace Greenshot.Base.UnmanagedHelpers
{
/// <summary>
/// Desktop Window Manager helper code
/// </summary>
public static class DWM
{
// DWM
[DllImport("dwmapi", SetLastError = true)]
public static extern int DwmRegisterThumbnail(IntPtr dest, IntPtr src, out IntPtr thumb);
[DllImport("dwmapi", SetLastError = true)]
public static extern int DwmUnregisterThumbnail(IntPtr thumb);
[DllImport("dwmapi", SetLastError = true)]
public static extern HResult DwmQueryThumbnailSourceSize(IntPtr thumb, out SIZE size);
[DllImport("dwmapi", SetLastError = true)]
public static extern HResult DwmUpdateThumbnailProperties(IntPtr hThumb, ref DWM_THUMBNAIL_PROPERTIES props);
// Deprecated as of Windows 8 Release Preview
[DllImport("dwmapi", SetLastError = true)]
public static extern int DwmIsCompositionEnabled(out bool enabled);
[DllImport("dwmapi", SetLastError = true)]
public static extern int DwmGetWindowAttribute(IntPtr hWnd, DWMWINDOWATTRIBUTE dwAttribute, out RECT lpRect, int size);
[DllImport("dwmapi", SetLastError = true)]
public static extern int DwmGetWindowAttribute(IntPtr hWnd, DWMWINDOWATTRIBUTE dwAttribute, out bool pvAttribute, int cbAttribute);
// Key to ColorizationColor for DWM
private const string COLORIZATION_COLOR_KEY = @"SOFTWARE\Microsoft\Windows\DWM";
/// <summary>
/// Checks if the window is cloaked, this should solve some issues with the window selection code
/// </summary>
/// <param name="hWnd">IntPtr as hWmd</param>
/// <returns>bool</returns>
public static bool IsWindowCloaked(IntPtr hWnd)
{
if (!WindowsVersion.IsWindows8OrLater)
{
return false;
}
DwmGetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_CLOAKED, out bool isCloaked, Marshal.SizeOf(typeof(bool)));
return isCloaked;
}
/// <summary>
/// Helper method for an easy DWM check
/// </summary>
/// <returns>bool true if DWM is available AND active</returns>
public static bool IsDwmEnabled
{
get
{
// According to: https://technet.microsoft.com/en-us/subscriptions/aa969538%28v=vs.85%29.aspx
// And: https://docs.microsoft.com/en-gb/windows/win32/api/dwmapi/nf-dwmapi-dwmenablecomposition
// DMW is always enabled on Windows 8! So return true and save a check! ;-)
if (WindowsVersion.IsWindows8OrLater)
{
return true;
}
if (WindowsVersion.IsWindowsVistaOrLater)
{
DwmIsCompositionEnabled(out var dwmEnabled);
return dwmEnabled;
}
return false;
}
}
public static Color ColorizationColor
{
get
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(COLORIZATION_COLOR_KEY, false))
{
object dwordValue = key?.GetValue("ColorizationColor");
if (dwordValue != null)
{
return Color.FromArgb((int) dwordValue);
}
}
return Color.White;
}
}
}
}

View file

@ -1,32 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System.Diagnostics.CodeAnalysis;
namespace Greenshot.Base.UnmanagedHelpers.Enums
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum ClassLongIndex
{
GCL_HICON = -14, // a handle to the icon associated with the class.
GCL_HICONSM = -34, // a handle to the small icon associated with the class.
}
}

View file

@ -1,46 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System.Diagnostics.CodeAnalysis;
namespace Greenshot.Base.UnmanagedHelpers.Enums
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum DWMWINDOWATTRIBUTE : uint
{
DWMWA_NCRENDERING_ENABLED = 1,
DWMWA_NCRENDERING_POLICY,
DWMWA_TRANSITIONS_FORCEDISABLED,
DWMWA_ALLOW_NCPAINT,
DWMWA_CAPTION_BUTTON_BOUNDS,
DWMWA_NONCLIENT_RTL_LAYOUT,
DWMWA_FORCE_ICONIC_REPRESENTATION,
DWMWA_FLIP3D_POLICY,
DWMWA_EXTENDED_FRAME_BOUNDS, // This is the one we need for retrieving the Window size since Windows Vista
DWMWA_HAS_ICONIC_BITMAP, // Since Windows 7
DWMWA_DISALLOW_PEEK, // Since Windows 7
DWMWA_EXCLUDED_FROM_PEEK, // Since Windows 7
DWMWA_CLOAK, // Since Windows 8
DWMWA_CLOAKED, // Since Windows 8
DWMWA_FREEZE_REPRESENTATION, // Since Windows 8
DWMWA_LAST
}
}

View file

@ -1,111 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System.Runtime.InteropServices;
using Greenshot.Base.UnmanagedHelpers.Structs;
namespace Greenshot.Base.UnmanagedHelpers.Enums
{
/// <summary>
/// See <a href="https://docs.microsoft.com/en-gb/windows/win32/api/dwmapi/ns-dwmapi-dwm_thumbnail_properties">DWM_THUMBNAIL_PROPERTIES</a>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct DWM_THUMBNAIL_PROPERTIES
{
// A bitwise combination of DWM thumbnail constant values that indicates which members of this structure are set.
public int dwFlags;
// The area in the destination window where the thumbnail will be rendered.
public RECT rcDestination;
// The region of the source window to use as the thumbnail. By default, the entire window is used as the thumbnail.
public RECT rcSource;
// The opacity with which to render the thumbnail. 0 is fully transparent while 255 is fully opaque. The default value is 255.
public byte opacity;
// TRUE to make the thumbnail visible; otherwise, FALSE. The default is FALSE.
public bool fVisible;
// TRUE to use only the thumbnail source's client area; otherwise, FALSE. The default is FALSE.
public bool fSourceClientAreaOnly;
public RECT Destination
{
set
{
dwFlags |= DWM_TNP_RECTDESTINATION;
rcDestination = value;
}
}
public RECT Source
{
set
{
dwFlags |= DWM_TNP_RECTSOURCE;
rcSource = value;
}
}
public byte Opacity
{
set
{
dwFlags |= DWM_TNP_OPACITY;
opacity = value;
}
}
public bool Visible
{
set
{
dwFlags |= DWM_TNP_VISIBLE;
fVisible = value;
}
}
public bool SourceClientAreaOnly
{
set
{
dwFlags |= DWM_TNP_SOURCECLIENTAREAONLY;
fSourceClientAreaOnly = value;
}
}
// A value for the rcDestination member has been specified.
public const int DWM_TNP_RECTDESTINATION = 0x00000001;
// A value for the rcSource member has been specified.
public const int DWM_TNP_RECTSOURCE = 0x00000002;
// A value for the opacity member has been specified.
public const int DWM_TNP_OPACITY = 0x00000004;
// A value for the fVisible member has been specified.
public const int DWM_TNP_VISIBLE = 0x00000008;
// A value for the fSourceClientAreaOnly member has been specified.
public const int DWM_TNP_SOURCECLIENTAREAONLY = 0x00000010;
}
}

View file

@ -1,45 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Diagnostics.CodeAnalysis;
namespace Greenshot.Base.UnmanagedHelpers.Enums
{
[Flags]
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum DesktopAccessRight : uint
{
DESKTOP_READOBJECTS = 0x00000001,
DESKTOP_CREATEWINDOW = 0x00000002,
DESKTOP_CREATEMENU = 0x00000004,
DESKTOP_HOOKCONTROL = 0x00000008,
DESKTOP_JOURNALRECORD = 0x00000010,
DESKTOP_JOURNALPLAYBACK = 0x00000020,
DESKTOP_ENUMERATE = 0x00000040,
DESKTOP_WRITEOBJECTS = 0x00000080,
DESKTOP_SWITCHDESKTOP = 0x00000100,
GENERIC_ALL = (DESKTOP_READOBJECTS | DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU |
DESKTOP_HOOKCONTROL | DESKTOP_JOURNALRECORD | DESKTOP_JOURNALPLAYBACK |
DESKTOP_ENUMERATE | DESKTOP_WRITEOBJECTS | DESKTOP_SWITCHDESKTOP)
};
}

View file

@ -1,71 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Diagnostics.CodeAnalysis;
namespace Greenshot.Base.UnmanagedHelpers.Enums
{
[Flags]
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum ExtendedWindowStyleFlags : uint
{
WS_EX_DLGMODALFRAME = 0x00000001,
WS_EX_NOPARENTNOTIFY = 0x00000004,
WS_EX_TOPMOST = 0x00000008,
WS_EX_ACCEPTFILES = 0x00000010,
WS_EX_TRANSPARENT = 0x00000020,
//#if(WINVER >= 0x0400)
WS_EX_MDICHILD = 0x00000040,
WS_EX_TOOLWINDOW = 0x00000080,
WS_EX_WINDOWEDGE = 0x00000100,
WS_EX_CLIENTEDGE = 0x00000200,
WS_EX_CONTEXTHELP = 0x00000400,
WS_EX_RIGHT = 0x00001000,
WS_EX_LEFT = 0x00000000,
WS_EX_RTLREADING = 0x00002000,
WS_EX_LTRREADING = 0x00000000,
WS_EX_LEFTSCROLLBAR = 0x00004000,
WS_EX_RIGHTSCROLLBAR = 0x00000000,
WS_EX_CONTROLPARENT = 0x00010000,
WS_EX_STATICEDGE = 0x00020000,
WS_EX_APPWINDOW = 0x00040000,
//WS_EX_OVERLAPPEDWINDOW = (WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE),
//WS_EX_PALETTEWINDOW = (WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST),
WS_EX_LAYERED = 0x00080000,
WS_EX_NOINHERITLAYOUT = 0x00100000, // Disable inheritence of mirroring by children
WS_EX_NOREDIRECTIONBITMAP = 0x00200000, //The window does not render to a redirection surface. This is for windows that do not have visible content or that use mechanisms other than surfaces to provide their visual.
WS_EX_LAYOUTRTL = 0x00400000, // Right to left mirroring
/// <summary>
/// Paints all descendants of a window in bottom-to-top painting order using double-buffering.
/// Bottom-to-top painting order allows a descendent window to have translucency (alpha) and transparency (color-key) effects, but only if the descendent window also has the WS_EX_TRANSPARENT bit set.
/// Double-buffering allows the window and its descendents to be painted without flicker.
/// This cannot be used if the window has a class style of either CS_OWNDC or CS_CLASSDC.
/// </summary>
WS_EX_COMPOSITED = 0x02000000,
WS_EX_NOACTIVATE = 0x08000000 // A top-level window created with this style does not become the foreground window when the user clicks it. The system does not bring this window to the foreground when the user minimizes or closes the foreground window.
}
}

View file

@ -1,110 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System.Diagnostics.CodeAnalysis;
namespace Greenshot.Base.UnmanagedHelpers.Enums
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum ShowWindowCommand : uint
{
/// <summary>
/// Hides the window and activates another window.
/// </summary>
Hide = 0,
/// <summary>
/// Activates and displays a window. If the window is minimized or
/// maximized, the system restores it to its original size and position.
/// An application should specify this flag when displaying the window
/// for the first time.
/// </summary>
Normal = 1,
/// <summary>
/// Activates the window and displays it as a minimized window.
/// </summary>
ShowMinimized = 2,
/// <summary>
/// Maximizes the specified window.
/// </summary>
Maximize = 3, // is this the right value?
/// <summary>
/// Activates the window and displays it as a maximized window.
/// </summary>
ShowMaximized = 3,
/// <summary>
/// Displays a window in its most recent size and position. This value
/// is similar to <see cref="Normal"/>, except
/// the window is not actived.
/// </summary>
ShowNoActivate = 4,
/// <summary>
/// Activates the window and displays it in its current size and position.
/// </summary>
Show = 5,
/// <summary>
/// Minimizes the specified window and activates the next top-level
/// window in the Z order.
/// </summary>
Minimize = 6,
/// <summary>
/// Displays the window as a minimized window. This value is similar to
/// <see cref="ShowMinimized"/>, except the
/// window is not activated.
/// </summary>
ShowMinNoActive = 7,
/// <summary>
/// Displays the window in its current size and position. This value is
/// similar to <see cref="Show"/>, except the
/// window is not activated.
/// </summary>
ShowNA = 8,
/// <summary>
/// Activates and displays the window. If the window is minimized or
/// maximized, the system restores it to its original size and position.
/// An application should specify this flag when restoring a minimized window.
/// </summary>
Restore = 9,
/// <summary>
/// Sets the show state based on the SW_* value specified in the
/// STARTUPINFO structure passed to the CreateProcess function by the
/// program that started the application.
/// </summary>
ShowDefault = 10,
/// <summary>
/// <b>Windows 2000/XP:</b> Minimizes a window, even if the thread
/// that owns the window is not responding. This flag should only be
/// used when minimizing windows from a different thread.
/// </summary>
ForceMinimize = 11
}
}

View file

@ -1,39 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Diagnostics.CodeAnalysis;
namespace Greenshot.Base.UnmanagedHelpers.Enums
{
/// <summary>
/// See: https://msdn.microsoft.com/en-us/library/aa909766.aspx
/// </summary>
[Flags]
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum SoundFlags
{
SND_ASYNC = 0x0001, // play asynchronously
SND_MEMORY = 0x0004, // pszSound points to a memory file
SND_NOSTOP = 0x0010, // don't stop any currently playing sound
SND_NOWAIT = 0x00002000, // don't wait if the driver is busy
}
}

View file

@ -1,89 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace Greenshot.Base.UnmanagedHelpers.Enums
{
/// <summary>
/// A Win32 error code.
/// </summary>
public enum Win32Error : uint
{
Success = 0x0,
InvalidFunction = 0x1,
FileNotFound = 0x2,
PathNotFound = 0x3,
TooManyOpenFiles = 0x4,
AccessDenied = 0x5,
InvalidHandle = 0x6,
ArenaTrashed = 0x7,
NotEnoughMemory = 0x8,
InvalidBlock = 0x9,
BadEnvironment = 0xa,
BadFormat = 0xb,
InvalidAccess = 0xc,
InvalidData = 0xd,
OutOfMemory = 0xe,
InvalidDrive = 0xf,
CurrentDirectory = 0x10,
NotSameDevice = 0x11,
NoMoreFiles = 0x12,
WriteProtect = 0x13,
BadUnit = 0x14,
NotReady = 0x15,
BadCommand = 0x16,
Crc = 0x17,
BadLength = 0x18,
Seek = 0x19,
NotDosDisk = 0x1a,
SectorNotFound = 0x1b,
OutOfPaper = 0x1c,
WriteFault = 0x1d,
ReadFault = 0x1e,
GenFailure = 0x1f,
SharingViolation = 0x20,
LockViolation = 0x21,
WrongDisk = 0x22,
SharingBufferExceeded = 0x24,
HandleEof = 0x26,
HandleDiskFull = 0x27,
NotSupported = 0x32,
RemNotList = 0x33,
DupName = 0x34,
BadNetPath = 0x35,
NetworkBusy = 0x36,
DevNotExist = 0x37,
TooManyCmds = 0x38,
FileExists = 0x50,
CannotMake = 0x52,
AlreadyAssigned = 0x55,
InvalidPassword = 0x56,
InvalidParameter = 0x57,
NetWriteFault = 0x58,
NoProcSlots = 0x59,
TooManySemaphores = 0x64,
ExclSemAlreadyOwned = 0x65,
SemIsSet = 0x66,
TooManySemRequests = 0x67,
InvalidAtInterruptTime = 0x68,
SemOwnerDied = 0x69,
SemUserLimit = 0x6a
}
}

View file

@ -1,37 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System.Diagnostics.CodeAnalysis;
namespace Greenshot.Base.UnmanagedHelpers.Enums
{
/// <summary>
/// Used for User32.SetWinEventHook
/// See MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/dd318066%28v=vs.85%29.aspx
/// </summary>
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum WinEvent : uint
{
EVENT_OBJECT_CREATE = 32768,
EVENT_OBJECT_DESTROY = 32769,
EVENT_OBJECT_NAMECHANGE = 32780,
}
}

View file

@ -1,18 +0,0 @@
using System;
using System.Diagnostics.CodeAnalysis;
namespace Greenshot.Base.UnmanagedHelpers.Enums
{
/// <summary>
/// Used for User32.SetWinEventHook
/// See: https://msdn.microsoft.com/en-us/library/windows/desktop/dd373640%28v=vs.85%29.aspx
/// </summary>
[SuppressMessage("ReSharper", "InconsistentNaming"), Flags]
public enum WinEventHookFlags
{
WINEVENT_SKIPOWNTHREAD = 1,
WINEVENT_SKIPOWNPROCESS = 2,
WINEVENT_OUTOFCONTEXT = 0,
WINEVENT_INCONTEXT = 4
}
}

View file

@ -1,32 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System.Diagnostics.CodeAnalysis;
namespace Greenshot.Base.UnmanagedHelpers.Enums
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum WindowLongIndex
{
GWL_EXSTYLE = -20, // Sets a new extended window style.
GWL_STYLE = -16, // Sets a new window style.
}
}

View file

@ -1,42 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Diagnostics.CodeAnalysis;
namespace Greenshot.Base.UnmanagedHelpers.Enums
{
[Flags]
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum WindowPlacementFlags : uint
{
// The coordinates of the minimized window may be specified.
// This flag must be specified if the coordinates are set in the ptMinPosition member.
WPF_SETMINPOSITION = 0x0001,
// If the calling thread and the thread that owns the window are attached to different input queues, the system posts the request to the thread that owns the window. This prevents the calling thread from blocking its execution while other threads process the request.
WPF_ASYNCWINDOWPLACEMENT = 0x0004,
// The restored window will be maximized, regardless of whether it was maximized before it was minimized. This setting is only valid the next time the window is restored. It does not change the default restoration behavior.
// This flag is only valid when the SW_SHOWMINIMIZED value is specified for the showCmd member.
WPF_RESTORETOMAXIMIZED = 0x0002
}
}

View file

@ -1,36 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Diagnostics.CodeAnalysis;
namespace Greenshot.Base.UnmanagedHelpers.Enums
{
[Flags]
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum WindowPos
{
SWP_NOACTIVATE =
0x0010, // Does not activate the window. If this flag is not set, the window is activated and moved to the top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter parameter).
SWP_NOMOVE = 0x0002, //Retains the current position (ignores X and Y parameters).
SWP_NOSIZE = 0x0001, // Retains the current size (ignores the cx and cy parameters).
}
}

View file

@ -1,52 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Diagnostics.CodeAnalysis;
namespace Greenshot.Base.UnmanagedHelpers.Enums
{
/// <summary>
/// Window Style Flags
/// </summary>
[Flags]
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum WindowStyleFlags : int
{
//WS_OVERLAPPED = 0x00000000,
WS_POPUP = -2147483648,
WS_CHILD = 0x40000000,
WS_MINIMIZE = 0x20000000,
WS_VISIBLE = 0x10000000,
WS_DISABLED = 0x08000000,
WS_CLIPSIBLINGS = 0x04000000,
WS_CLIPCHILDREN = 0x02000000,
WS_MAXIMIZE = 0x01000000,
WS_BORDER = 0x00800000,
WS_DLGFRAME = 0x00400000,
WS_VSCROLL = 0x00200000,
WS_HSCROLL = 0x00100000,
WS_SYSMENU = 0x00080000,
WS_THICKFRAME = 0x00040000,
WS_GROUP = 0x00020000,
WS_TABSTOP = 0x00010000
}
}

View file

@ -1,26 +0,0 @@
using System.Diagnostics.CodeAnalysis;
namespace Greenshot.Base.UnmanagedHelpers.Enums
{
/// <summary>
/// All possible windows messages
/// See also <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms644927(v=vs.85).aspx#system_defined">here</a>
/// </summary>
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum WindowsMessages : uint
{
WM_MOUSEACTIVATE = 0x0021,
WM_INPUTLANGCHANGEREQUEST = 0x0050,
WM_INPUTLANGCHANGE = 0x0051,
/// <summary>
/// Sent to a window to retrieve a handle to the large or small icon associated with a window. The system displays the large icon in the ALT+TAB dialog, and the small icon in the window caption.
/// A window receives this message through its WindowProc function.
/// <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms632625.aspx">WM_GETICON message</a>
/// </summary>
WM_GETICON = 0x007F,
WM_CHAR = 0x0102,
WM_SYSCOMMAND = 0x0112
}
}

View file

@ -1,596 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Security;
using Greenshot.Base.UnmanagedHelpers.Enums;
using Microsoft.Win32.SafeHandles;
namespace Greenshot.Base.UnmanagedHelpers
{
public static class GDIExtensions
{
/// <summary>
/// Check if all the corners of the rectangle are visible in the specified region.
/// Not a perfect check, but this currently a workaround for checking if a window is completely visible
/// </summary>
/// <param name="region"></param>
/// <param name="rectangle"></param>
/// <returns></returns>
public static bool AreRectangleCornersVisisble(this Region region, Rectangle rectangle)
{
Point topLeft = new Point(rectangle.X, rectangle.Y);
Point topRight = new Point(rectangle.X + rectangle.Width, rectangle.Y);
Point bottomLeft = new Point(rectangle.X, rectangle.Y + rectangle.Height);
Point bottomRight = new Point(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height);
bool topLeftVisible = region.IsVisible(topLeft);
bool topRightVisible = region.IsVisible(topRight);
bool bottomLeftVisible = region.IsVisible(bottomLeft);
bool bottomRightVisible = region.IsVisible(bottomRight);
return topLeftVisible && topRightVisible && bottomLeftVisible && bottomRightVisible;
}
/// <summary>
/// Get a SafeHandle for the GetHdc, so one can use using to automatically cleanup the devicecontext
/// </summary>
/// <param name="graphics"></param>
/// <returns>SafeDeviceContextHandle</returns>
public static SafeDeviceContextHandle GetSafeDeviceContext(this Graphics graphics)
{
return SafeDeviceContextHandle.FromGraphics(graphics);
}
}
/// <summary>
/// Abstract class SafeObjectHandle which contains all handles that are cleaned with DeleteObject
/// </summary>
public abstract class SafeObjectHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[DllImport("gdi32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DeleteObject(IntPtr hObject);
protected SafeObjectHandle(bool ownsHandle) : base(ownsHandle)
{
}
protected override bool ReleaseHandle()
{
return DeleteObject(handle);
}
}
/// <summary>
/// A hbitmap SafeHandle implementation
/// </summary>
public class SafeHBitmapHandle : SafeObjectHandle
{
/// <summary>
/// Needed for marshalling return values
/// </summary>
[SecurityCritical]
public SafeHBitmapHandle() : base(true)
{
}
[SecurityCritical]
public SafeHBitmapHandle(IntPtr preexistingHandle) : base(true)
{
SetHandle(preexistingHandle);
}
}
/// <summary>
/// A hRegion SafeHandle implementation
/// </summary>
public class SafeRegionHandle : SafeObjectHandle
{
/// <summary>
/// Needed for marshalling return values
/// </summary>
[SecurityCritical]
public SafeRegionHandle() : base(true)
{
}
[SecurityCritical]
public SafeRegionHandle(IntPtr preexistingHandle) : base(true)
{
SetHandle(preexistingHandle);
}
}
/// <summary>
/// A dibsection SafeHandle implementation
/// </summary>
public class SafeDibSectionHandle : SafeObjectHandle
{
/// <summary>
/// Needed for marshalling return values
/// </summary>
[SecurityCritical]
public SafeDibSectionHandle() : base(true)
{
}
[SecurityCritical]
public SafeDibSectionHandle(IntPtr preexistingHandle) : base(true)
{
SetHandle(preexistingHandle);
}
}
/// <summary>
/// A select object safehandle implementation
/// This impl will select the passed SafeHandle to the HDC and replace the returned value when disposing
/// </summary>
public class SafeSelectObjectHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[DllImport("gdi32", SetLastError = true)]
private static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
private readonly SafeHandle _hdc;
/// <summary>
/// Needed for marshalling return values
/// </summary>
[SecurityCritical]
public SafeSelectObjectHandle() : base(true)
{
}
[SecurityCritical]
public SafeSelectObjectHandle(SafeDCHandle hdc, SafeHandle newHandle) : base(true)
{
_hdc = hdc;
SetHandle(SelectObject(hdc.DangerousGetHandle(), newHandle.DangerousGetHandle()));
}
protected override bool ReleaseHandle()
{
SelectObject(_hdc.DangerousGetHandle(), handle);
return true;
}
}
public abstract class SafeDCHandle : SafeHandleZeroOrMinusOneIsInvalid
{
protected SafeDCHandle(bool ownsHandle) : base(ownsHandle)
{
}
}
/// <summary>
/// A CompatibleDC SafeHandle implementation
/// </summary>
public class SafeCompatibleDCHandle : SafeDCHandle
{
[DllImport("gdi32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DeleteDC(IntPtr hDC);
/// <summary>
/// Needed for marshalling return values
/// </summary>
[SecurityCritical]
public SafeCompatibleDCHandle() : base(true)
{
}
[SecurityCritical]
public SafeCompatibleDCHandle(IntPtr preexistingHandle) : base(true)
{
SetHandle(preexistingHandle);
}
public SafeSelectObjectHandle SelectObject(SafeHandle newHandle)
{
return new SafeSelectObjectHandle(this, newHandle);
}
protected override bool ReleaseHandle()
{
return DeleteDC(handle);
}
}
/// <summary>
/// A DeviceContext SafeHandle implementation
/// </summary>
public class SafeDeviceContextHandle : SafeDCHandle
{
private readonly Graphics _graphics;
/// <summary>
/// Needed for marshalling return values
/// </summary>
[SecurityCritical]
public SafeDeviceContextHandle() : base(true)
{
}
[SecurityCritical]
public SafeDeviceContextHandle(Graphics graphics, IntPtr preexistingHandle) : base(true)
{
_graphics = graphics;
SetHandle(preexistingHandle);
}
protected override bool ReleaseHandle()
{
_graphics.ReleaseHdc(handle);
return true;
}
public static SafeDeviceContextHandle FromGraphics(Graphics graphics)
{
return new SafeDeviceContextHandle(graphics, graphics.GetHdc());
}
}
/// <summary>
/// GDI32 Helpers
/// </summary>
public static class GDI32
{
[DllImport("gdi32", SetLastError = true)]
public static extern bool BitBlt(SafeHandle hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, SafeHandle hdcSrc, int nXSrc, int nYSrc, CopyPixelOperation dwRop);
[DllImport("gdi32", SetLastError = true)]
public static extern SafeCompatibleDCHandle CreateCompatibleDC(SafeHandle hDC);
[DllImport("gdi32", SetLastError = true)]
public static extern SafeDibSectionHandle CreateDIBSection(SafeHandle hdc, ref BITMAPINFOHEADERV5 bmi, uint usage, out IntPtr bits, IntPtr hSection, uint dwOffset);
[DllImport("gdi32", SetLastError = true)]
public static extern SafeRegionHandle CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
[DllImport("gdi32", SetLastError = true)]
public static extern uint GetPixel(SafeHandle hdc, int nXPos, int nYPos);
[DllImport("gdi32", SetLastError = true)]
public static extern int GetDeviceCaps(SafeHandle hdc, DeviceCaps nIndex);
}
[StructLayout(LayoutKind.Sequential, Pack = 2)]
public struct BITMAPFILEHEADER
{
public static readonly short BM = 0x4d42; // BM
public short bfType;
public int bfSize;
public short bfReserved1;
public short bfReserved2;
public int bfOffBits;
}
[StructLayout(LayoutKind.Sequential)]
public struct BitfieldColorMask
{
public uint blue;
public uint green;
public uint red;
public void InitValues()
{
red = (uint) 255 << 8;
green = (uint) 255 << 16;
blue = (uint) 255 << 24;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct CIEXYZ
{
public uint ciexyzX; //FXPT2DOT30
public uint ciexyzY; //FXPT2DOT30
public uint ciexyzZ; //FXPT2DOT30
public CIEXYZ(uint FXPT2DOT30)
{
ciexyzX = FXPT2DOT30;
ciexyzY = FXPT2DOT30;
ciexyzZ = FXPT2DOT30;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct RGBQUAD
{
public byte rgbBlue;
public byte rgbGreen;
public byte rgbRed;
public byte rgbReserved;
}
[StructLayout(LayoutKind.Sequential)]
public struct CIEXYZTRIPLE
{
public CIEXYZ ciexyzRed;
public CIEXYZ ciexyzGreen;
public CIEXYZ ciexyzBlue;
}
public enum BI_COMPRESSION : uint
{
BI_RGB = 0, // Uncompressed
BI_RLE8 = 1, // RLE 8BPP
BI_RLE4 = 2, // RLE 4BPP
BI_BITFIELDS =
3, // Specifies that the bitmap is not compressed and that the color table consists of three DWORD color masks that specify the red, green, and blue components, respectively, of each pixel. This is valid when used with 16- and 32-bpp bitmaps.
BI_JPEG = 4, // Indicates that the image is a JPEG image.
BI_PNG = 5 // Indicates that the image is a PNG image.
}
[StructLayout(LayoutKind.Explicit)]
public struct BITMAPINFOHEADER
{
[FieldOffset(0)] public uint biSize;
[FieldOffset(4)] public int biWidth;
[FieldOffset(8)] public int biHeight;
[FieldOffset(12)] public ushort biPlanes;
[FieldOffset(14)] public ushort biBitCount;
[FieldOffset(16)] public BI_COMPRESSION biCompression;
[FieldOffset(20)] public uint biSizeImage;
[FieldOffset(24)] public int biXPelsPerMeter;
[FieldOffset(28)] public int biYPelsPerMeter;
[FieldOffset(32)] public uint biClrUsed;
[FieldOffset(36)] public uint biClrImportant;
public const int DIB_RGB_COLORS = 0;
public BITMAPINFOHEADER(int width, int height, ushort bpp)
{
biSize = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADER)); // BITMAPINFOHEADER < DIBV4 is 40 bytes
biPlanes = 1; // Should allways be 1
biCompression = BI_COMPRESSION.BI_RGB;
biWidth = width;
biHeight = height;
biBitCount = bpp;
biSizeImage = (uint)(width * height * (bpp >> 3));
biXPelsPerMeter = 0;
biYPelsPerMeter = 0;
biClrUsed = 0;
biClrImportant = 0;
}
public bool IsDibV4
{
get
{
uint sizeOfBMI = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADERV4));
return biSize >= sizeOfBMI;
}
}
public bool IsDibV5
{
get
{
uint sizeOfBMI = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADERV5));
return biSize >= sizeOfBMI;
}
}
public uint OffsetToPixels
{
get
{
if (biCompression == BI_COMPRESSION.BI_BITFIELDS)
{
// Add 3x4 bytes for the bitfield color mask
return biSize + 3 * 4;
}
return biSize;
}
}
}
[StructLayout(LayoutKind.Explicit)]
public struct BITMAPINFOHEADERV4
{
[FieldOffset(0)] public uint biSize;
[FieldOffset(4)] public int biWidth;
[FieldOffset(8)] public int biHeight;
[FieldOffset(12)] public ushort biPlanes;
[FieldOffset(14)] public ushort biBitCount;
[FieldOffset(16)] public BI_COMPRESSION biCompression;
[FieldOffset(20)] public uint biSizeImage;
[FieldOffset(24)] public int biXPelsPerMeter;
[FieldOffset(28)] public int biYPelsPerMeter;
[FieldOffset(32)] public uint biClrUsed;
[FieldOffset(36)] public uint biClrImportant;
[FieldOffset(40)] public uint bV4RedMask;
[FieldOffset(44)] public uint bV4GreenMask;
[FieldOffset(48)] public uint bV4BlueMask;
[FieldOffset(52)] public uint bV4AlphaMask;
[FieldOffset(56)] public uint bV4CSType;
[FieldOffset(60)] public CIEXYZTRIPLE bV4Endpoints;
[FieldOffset(96)] public uint bV4GammaRed;
[FieldOffset(100)] public uint bV4GammaGreen;
[FieldOffset(104)] public uint bV4GammaBlue;
public const int DIB_RGB_COLORS = 0;
public BITMAPINFOHEADERV4(int width, int height, ushort bpp)
{
biSize = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADERV4)); // BITMAPINFOHEADER < DIBV5 is 40 bytes
biPlanes = 1; // Should allways be 1
biCompression = BI_COMPRESSION.BI_RGB;
biWidth = width;
biHeight = height;
biBitCount = bpp;
biSizeImage = (uint)(width * height * (bpp >> 3));
biXPelsPerMeter = 0;
biYPelsPerMeter = 0;
biClrUsed = 0;
biClrImportant = 0;
// V4
bV4RedMask = (uint)255 << 16;
bV4GreenMask = (uint)255 << 8;
bV4BlueMask = 255;
bV4AlphaMask = (uint)255 << 24;
bV4CSType = 0x73524742; // LCS_sRGB
bV4Endpoints = new CIEXYZTRIPLE
{
ciexyzBlue = new CIEXYZ(0),
ciexyzGreen = new CIEXYZ(0),
ciexyzRed = new CIEXYZ(0)
};
bV4GammaRed = 0;
bV4GammaGreen = 0;
bV4GammaBlue = 0;
}
public bool IsDibV4
{
get
{
uint sizeOfBMI = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADERV4));
return biSize >= sizeOfBMI;
}
}
public bool IsDibV5
{
get
{
uint sizeOfBMI = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADERV5));
return biSize >= sizeOfBMI;
}
}
public uint OffsetToPixels
{
get
{
if (biCompression == BI_COMPRESSION.BI_BITFIELDS)
{
// Add 3x4 bytes for the bitfield color mask
return biSize + 3 * 4;
}
return biSize;
}
}
}
[StructLayout(LayoutKind.Explicit)]
public struct BITMAPINFOHEADERV5
{
[FieldOffset(0)] public uint biSize;
[FieldOffset(4)] public int biWidth;
[FieldOffset(8)] public int biHeight;
[FieldOffset(12)] public ushort biPlanes;
[FieldOffset(14)] public ushort biBitCount;
[FieldOffset(16)] public BI_COMPRESSION biCompression;
[FieldOffset(20)] public uint biSizeImage;
[FieldOffset(24)] public int biXPelsPerMeter;
[FieldOffset(28)] public int biYPelsPerMeter;
[FieldOffset(32)] public uint biClrUsed;
[FieldOffset(36)] public uint biClrImportant;
[FieldOffset(40)] public uint bV4RedMask;
[FieldOffset(44)] public uint bV4GreenMask;
[FieldOffset(48)] public uint bV4BlueMask;
[FieldOffset(52)] public uint bV4AlphaMask;
[FieldOffset(56)] public uint bV4CSType;
[FieldOffset(60)] public CIEXYZTRIPLE bV4Endpoints;
[FieldOffset(96)] public uint bV4GammaRed;
[FieldOffset(100)] public uint bV4GammaGreen;
[FieldOffset(104)] public uint bV4GammaBlue;
[FieldOffset(108)] public uint bV5Intent; // Rendering intent for bitmap
[FieldOffset(112)] public uint bV5ProfileData;
[FieldOffset(116)] public uint bV5ProfileSize;
[FieldOffset(120)] public uint bV5Reserved;
public const int DIB_RGB_COLORS = 0;
public BITMAPINFOHEADERV5(int width, int height, ushort bpp)
{
biSize = (uint) Marshal.SizeOf(typeof(BITMAPINFOHEADERV5)); // BITMAPINFOHEADER < DIBV5 is 40 bytes
biPlanes = 1; // Should allways be 1
biCompression = BI_COMPRESSION.BI_RGB;
biWidth = width;
biHeight = height;
biBitCount = bpp;
biSizeImage = (uint) (width * height * (bpp >> 3));
biXPelsPerMeter = 0;
biYPelsPerMeter = 0;
biClrUsed = 0;
biClrImportant = 0;
// V4
bV4RedMask = (uint) 255 << 16;
bV4GreenMask = (uint) 255 << 8;
bV4BlueMask = 255;
bV4AlphaMask = (uint) 255 << 24;
bV4CSType = 0x73524742; // LCS_sRGB
bV4Endpoints = new CIEXYZTRIPLE
{
ciexyzBlue = new CIEXYZ(0),
ciexyzGreen = new CIEXYZ(0),
ciexyzRed = new CIEXYZ(0)
};
bV4GammaRed = 0;
bV4GammaGreen = 0;
bV4GammaBlue = 0;
// V5
bV5Intent = 4;
bV5ProfileData = 0;
bV5ProfileSize = 0;
bV5Reserved = 0;
}
public bool IsDibV4
{
get
{
uint sizeOfBMI = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADERV4));
return biSize >= sizeOfBMI;
}
}
public bool IsDibV5
{
get
{
uint sizeOfBMI = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADERV5));
return biSize >= sizeOfBMI;
}
}
public uint OffsetToPixels
{
get
{
if (biCompression == BI_COMPRESSION.BI_BITFIELDS)
{
// Add 3x4 bytes for the bitfield color mask
return biSize + 3 * 4;
}
return biSize;
}
}
}
}

View file

@ -1,387 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Reflection;
using System.Runtime.InteropServices;
using Greenshot.Base.UnmanagedHelpers.Structs;
using log4net;
namespace Greenshot.Base.UnmanagedHelpers
{
/// <summary>
/// Contains members that specify the nature of a Gaussian blur.
/// </summary>
/// <remarks>Cannot be pinned with GCHandle due to bool value.</remarks>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct BlurParams
{
/// <summary>
/// Real number that specifies the blur radius (the radius of the Gaussian convolution kernel) in
/// pixels. The radius must be in the range 0 through 255. As the radius increases, the resulting
/// bitmap becomes more blurry.
/// </summary>
public float Radius;
/// <summary>
/// Boolean value that specifies whether the bitmap expands by an amount equal to the blur radius.
/// If TRUE, the bitmap expands by an amount equal to the radius so that it can have soft edges.
/// If FALSE, the bitmap remains the same size and the soft edges are clipped.
/// </summary>
public bool ExpandEdges;
}
/// <summary>
/// GDI Plus unit description.
/// </summary>
public enum GpUnit
{
/// <summary>
/// World coordinate (non-physical unit).
/// </summary>
UnitWorld,
/// <summary>
/// Variable - for PageTransform only.
/// </summary>
UnitDisplay,
/// <summary>
/// Each unit is one device pixel.
/// </summary>
UnitPixel,
/// <summary>
/// Each unit is a printer's point, or 1/72 inch.
/// </summary>
UnitPoint,
/// <summary>
/// Each unit is 1 inch.
/// </summary>
UnitInch,
/// <summary>
/// Each unit is 1/300 inch.
/// </summary>
UnitDocument,
/// <summary>
/// Each unit is 1 millimeter.
/// </summary>
UnitMillimeter
}
/// <summary>
/// GDIplus Helpers
/// </summary>
public static class GDIplus
{
private static readonly ILog Log = LogManager.GetLogger(typeof(GDIplus));
[DllImport("gdiplus.dll", SetLastError = true, ExactSpelling = true)]
private static extern int GdipBitmapApplyEffect(IntPtr bitmap, IntPtr effect, ref RECT rectOfInterest, bool useAuxData, IntPtr auxData, int auxDataSize);
[DllImport("gdiplus.dll", SetLastError = true, ExactSpelling = true)]
private static extern int GdipDrawImageFX(IntPtr graphics, IntPtr bitmap, ref RECTF source, IntPtr matrix, IntPtr effect, IntPtr imageAttributes, GpUnit srcUnit);
[DllImport("gdiplus.dll", SetLastError = true, ExactSpelling = true)]
private static extern int GdipSetEffectParameters(IntPtr effect, IntPtr parameters, uint size);
[DllImport("gdiplus.dll", SetLastError = true, ExactSpelling = true)]
private static extern int GdipCreateEffect(Guid guid, out IntPtr effect);
[DllImport("gdiplus.dll", SetLastError = true, ExactSpelling = true)]
private static extern int GdipDeleteEffect(IntPtr effect);
private static readonly Guid BlurEffectGuid = new Guid("{633C80A4-1843-482B-9EF2-BE2834C5FDD4}");
// Constant "FieldInfo" for getting the nativeImage from the Bitmap
private static readonly FieldInfo FIELD_INFO_NATIVE_IMAGE = typeof(Bitmap).GetField("nativeImage", BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic);
// Constant "FieldInfo" for getting the NativeGraphics from the Graphics
private static readonly FieldInfo FIELD_INFO_NATIVE_GRAPHICS =
typeof(Graphics).GetField("nativeGraphics", BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic);
// Constant "FieldInfo" for getting the nativeMatrix from the Matrix
private static readonly FieldInfo FIELD_INFO_NATIVE_MATRIX =
typeof(Matrix).GetField("nativeMatrix", BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic);
// Constant "FieldInfo" for getting the nativeImageAttributes from the ImageAttributes
private static readonly FieldInfo FIELD_INFO_NATIVE_IMAGEATTRIBUTES =
typeof(ImageAttributes).GetField("nativeImageAttributes", BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic);
private static bool _isBlurEnabled = Environment.OSVersion.Version.Major >= 6;
/// <summary>
/// Get the nativeImage field from the bitmap
/// </summary>
/// <param name="bitmap"></param>
/// <returns>IntPtr</returns>
private static IntPtr GetNativeImage(Bitmap bitmap)
{
if (bitmap == null)
{
return IntPtr.Zero;
}
return (IntPtr) FIELD_INFO_NATIVE_IMAGE.GetValue(bitmap);
}
/// <summary>
/// Get the NativeGraphics field from the graphics
/// </summary>
/// <param name="graphics"></param>
/// <returns>IntPtr</returns>
private static IntPtr GetNativeGraphics(Graphics graphics)
{
if (graphics == null)
{
return IntPtr.Zero;
}
return (IntPtr) FIELD_INFO_NATIVE_GRAPHICS.GetValue(graphics);
}
/// <summary>
/// Get the nativeMatrix field from the matrix
/// </summary>
/// <param name="matrix"></param>
/// <returns>IntPtr</returns>
private static IntPtr GetNativeMatrix(Matrix matrix)
{
if (matrix == null)
{
return IntPtr.Zero;
}
return (IntPtr) FIELD_INFO_NATIVE_MATRIX.GetValue(matrix);
}
/// <summary>
/// Get the nativeImageAttributes field from the ImageAttributes
/// </summary>
/// <param name="imageAttributes"></param>
/// <returns>IntPtr</returns>
private static IntPtr GetNativeImageAttributes(ImageAttributes imageAttributes)
{
if (imageAttributes == null)
{
return IntPtr.Zero;
}
return (IntPtr) FIELD_INFO_NATIVE_IMAGEATTRIBUTES.GetValue(imageAttributes);
}
/// <summary>
/// Returns if a GDIPlus blur can be made for the supplied radius.
/// This accounts for the "bug" I reported here: https://social.technet.microsoft.com/Forums/en/w8itprogeneral/thread/99ddbe9d-556d-475a-8bab-84e25aa13a2c
/// </summary>
/// <param name="radius"></param>
/// <returns>false if blur is not possible</returns>
public static bool IsBlurPossible(int radius)
{
if (!_isBlurEnabled)
{
return false;
}
if (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor < 2)
{
return true;
}
return Environment.OSVersion.Version.Major > 6 && radius >= 20;
}
/// <summary>
/// Use the GDI+ blur effect on the bitmap
/// </summary>
/// <param name="destinationBitmap">Bitmap to apply the effect to</param>
/// <param name="area">Rectangle to apply the blur effect to</param>
/// <param name="radius">0-255</param>
/// <param name="expandEdges">bool true if the edges are expanded with the radius</param>
/// <returns>false if there is no GDI+ available or an exception occurred</returns>
public static bool ApplyBlur(Bitmap destinationBitmap, Rectangle area, int radius, bool expandEdges)
{
if (!IsBlurPossible(radius))
{
return false;
}
IntPtr hBlurParams = IntPtr.Zero;
IntPtr hEffect = IntPtr.Zero;
try
{
// Create the GDI+ BlurEffect, using the Guid
int status = GdipCreateEffect(BlurEffectGuid, out hEffect);
if (status != 0)
{
return false;
}
// Create a BlurParams struct and set the values
var blurParams = new BlurParams
{
Radius = radius,
ExpandEdges = expandEdges
};
// Allocate space in unmanaged memory
hBlurParams = Marshal.AllocHGlobal(Marshal.SizeOf(blurParams));
// Copy the structure to the unmanaged memory
Marshal.StructureToPtr(blurParams, hBlurParams, false);
// Set the blurParams to the effect
GdipSetEffectParameters(hEffect, hBlurParams, (uint) Marshal.SizeOf(blurParams));
// Somewhere it said we can use destinationBitmap.GetHbitmap(), this doesn't work!!
// Get the private nativeImage property from the Bitmap
IntPtr hBitmap = GetNativeImage(destinationBitmap);
// Create a RECT from the Rectangle
RECT rec = new RECT(area);
// Apply the effect to the bitmap in the specified area
GdipBitmapApplyEffect(hBitmap, hEffect, ref rec, false, IntPtr.Zero, 0);
// Everything worked, return true
return true;
}
catch (Exception ex)
{
_isBlurEnabled = false;
Log.Error("Problem using GdipBitmapApplyEffect: ", ex);
return false;
}
finally
{
try
{
if (hEffect != IntPtr.Zero)
{
// Delete the effect
GdipDeleteEffect(hEffect);
}
if (hBlurParams != IntPtr.Zero)
{
// Free the memory
Marshal.FreeHGlobal(hBlurParams);
}
}
catch (Exception ex)
{
_isBlurEnabled = false;
Log.Error("Problem cleaning up ApplyBlur: ", ex);
}
}
}
/// <summary>
/// Draw the image on the graphics with GDI+ blur effect
/// </summary>
/// <returns>false if there is no GDI+ available or an exception occurred</returns>
public static bool DrawWithBlur(Graphics graphics, Bitmap image, Rectangle source, Matrix transform, ImageAttributes imageAttributes, int radius, bool expandEdges)
{
if (!IsBlurPossible(radius))
{
return false;
}
IntPtr hBlurParams = IntPtr.Zero;
IntPtr hEffect = IntPtr.Zero;
try
{
// Create the GDI+ BlurEffect, using the Guid
int status = GdipCreateEffect(BlurEffectGuid, out hEffect);
if (status != 0)
{
return false;
}
// Create a BlurParams struct and set the values
var blurParams = new BlurParams
{
Radius = radius,
ExpandEdges = false
};
//blurParams.Padding = radius;
// Allocate space in unmanaged memory
hBlurParams = Marshal.AllocHGlobal(Marshal.SizeOf(blurParams));
// Copy the structure to the unmanaged memory
Marshal.StructureToPtr(blurParams, hBlurParams, true);
// Set the blurParams to the effect
GdipSetEffectParameters(hEffect, hBlurParams, (uint) Marshal.SizeOf(blurParams));
// Somewhere it said we can use destinationBitmap.GetHbitmap(), this doesn't work!!
// Get the private nativeImage property from the Bitmap
IntPtr hBitmap = GetNativeImage(image);
IntPtr hGraphics = GetNativeGraphics(graphics);
IntPtr hMatrix = GetNativeMatrix(transform);
IntPtr hAttributes = GetNativeImageAttributes(imageAttributes);
// Create a RECT from the Rectangle
RECTF sourceRecf = new RECTF(source);
// Apply the effect to the bitmap in the specified area
GdipDrawImageFX(hGraphics, hBitmap, ref sourceRecf, hMatrix, hEffect, hAttributes, GpUnit.UnitPixel);
// Everything worked, return true
return true;
}
catch (Exception ex)
{
_isBlurEnabled = false;
Log.Error("Problem using GdipDrawImageFX: ", ex);
return false;
}
finally
{
try
{
if (hEffect != IntPtr.Zero)
{
// Delete the effect
GdipDeleteEffect(hEffect);
}
if (hBlurParams != IntPtr.Zero)
{
// Free the memory
Marshal.FreeHGlobal(hBlurParams);
}
}
catch (Exception ex)
{
_isBlurEnabled = false;
Log.Error("Problem cleaning up DrawWithBlur: ", ex);
}
}
}
}
}

View file

@ -1,135 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Runtime.InteropServices;
using System.Text;
using Greenshot.Base.UnmanagedHelpers.Enums;
namespace Greenshot.Base.UnmanagedHelpers
{
/// <summary>
/// Description of Kernel32.
/// </summary>
public class Kernel32
{
public const uint ATTACHCONSOLE_ATTACHPARENTPROCESS = 0x0ffffffff; // default value if not specifing a process ID
[DllImport("kernel32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AllocConsole();
[DllImport("kernel32", SetLastError = true)]
public static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
[DllImport("kernel32", SetLastError = true)]
public static extern uint SuspendThread(IntPtr hThread);
[DllImport("kernel32", SetLastError = true)]
public static extern int ResumeThread(IntPtr hThread);
[DllImport("kernel32", SetLastError = true)]
public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool QueryFullProcessImageName(IntPtr hProcess, uint dwFlags, StringBuilder lpExeName, ref uint lpdwSize);
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern uint QueryDosDevice(string lpDeviceName, StringBuilder lpTargetPath, uint uuchMax);
[DllImport("kernel32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int GetPackageFullName(IntPtr hProcess, ref Int32 packageFullNameLength, StringBuilder fullName);
/// <summary>
/// Method to get the process path
/// </summary>
/// <param name="processid"></param>
/// <returns></returns>
public static string GetProcessPath(int processid)
{
StringBuilder _PathBuffer = new StringBuilder(512);
// Try the GetModuleFileName method first since it's the fastest.
// May return ACCESS_DENIED (due to VM_READ flag) if the process is not owned by the current user.
// Will fail if we are compiled as x86 and we're trying to open a 64 bit process...not allowed.
IntPtr hprocess = OpenProcess(ProcessAccessFlags.QueryInformation | ProcessAccessFlags.VMRead, false, processid);
if (hprocess != IntPtr.Zero)
{
try
{
if (PsAPI.GetModuleFileNameEx(hprocess, IntPtr.Zero, _PathBuffer, (uint) _PathBuffer.Capacity) > 0)
{
return _PathBuffer.ToString();
}
}
finally
{
CloseHandle(hprocess);
}
}
hprocess = OpenProcess(ProcessAccessFlags.QueryInformation, false, processid);
if (hprocess != IntPtr.Zero)
{
try
{
// Try this method for Vista or higher operating systems
uint size = (uint) _PathBuffer.Capacity;
if ((Environment.OSVersion.Version.Major >= 6) && (QueryFullProcessImageName(hprocess, 0, _PathBuffer, ref size) && (size > 0)))
{
return _PathBuffer.ToString();
}
// Try the GetProcessImageFileName method
if (PsAPI.GetProcessImageFileName(hprocess, _PathBuffer, (uint) _PathBuffer.Capacity) > 0)
{
string dospath = _PathBuffer.ToString();
foreach (string drive in Environment.GetLogicalDrives())
{
if (QueryDosDevice(drive.TrimEnd('\\'), _PathBuffer, (uint) _PathBuffer.Capacity) > 0)
{
if (dospath.StartsWith(_PathBuffer.ToString()))
{
return drive + dospath.Remove(0, _PathBuffer.Length);
}
}
}
}
}
finally
{
CloseHandle(hprocess);
}
}
return string.Empty;
}
}
}

View file

@ -1,56 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using log4net;
namespace Greenshot.Base.UnmanagedHelpers
{
/// <summary>
/// Description of PsAPI.
/// </summary>
public class PsAPI
{
private static readonly ILog LOG = LogManager.GetLogger(typeof(PsAPI));
[DllImport("psapi", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, StringBuilder lpFilename, uint nSize);
[DllImport("psapi", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern uint GetProcessImageFileName(IntPtr hProcess, StringBuilder lpImageFileName, uint nSize);
[DllImport("psapi")]
private static extern int EmptyWorkingSet(IntPtr hwProc);
/// <summary>
/// Make the process use less memory by emptying the working set
/// </summary>
public static void EmptyWorkingSet()
{
LOG.Info("Calling EmptyWorkingSet");
using Process currentProcess = Process.GetCurrentProcess();
EmptyWorkingSet(currentProcess.Handle);
}
}
}

View file

@ -1,45 +0,0 @@
using System;
using System.Security.Permissions;
using Greenshot.Base.UnmanagedHelpers.Enums;
using log4net;
using Microsoft.Win32.SafeHandles;
namespace Greenshot.Base.UnmanagedHelpers
{
/// <summary>
/// A SafeHandle class implementation for the current input desktop
/// </summary>
public class SafeCurrentInputDesktopHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private static readonly ILog LOG = LogManager.GetLogger(typeof(SafeCurrentInputDesktopHandle));
public SafeCurrentInputDesktopHandle() : base(true)
{
IntPtr hDesktop = User32.OpenInputDesktop(0, true, DesktopAccessRight.GENERIC_ALL);
if (hDesktop != IntPtr.Zero)
{
SetHandle(hDesktop);
if (User32.SetThreadDesktop(hDesktop))
{
LOG.DebugFormat("Switched to desktop {0}", hDesktop);
}
else
{
LOG.WarnFormat("Couldn't switch to desktop {0}", hDesktop);
LOG.Error(User32.CreateWin32Exception("SetThreadDesktop"));
}
}
else
{
LOG.Warn("Couldn't get current desktop.");
LOG.Error(User32.CreateWin32Exception("OpenInputDesktop"));
}
}
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
protected override bool ReleaseHandle()
{
return User32.CloseDesktop(handle);
}
}
}

View file

@ -1,33 +0,0 @@
using System;
using System.Security;
using System.Security.Permissions;
using Microsoft.Win32.SafeHandles;
namespace Greenshot.Base.UnmanagedHelpers
{
/// <summary>
/// A SafeHandle class implementation for the hIcon
/// </summary>
public class SafeIconHandle : SafeHandleZeroOrMinusOneIsInvalid
{
/// <summary>
/// Needed for marshalling return values
/// </summary>
[SecurityCritical]
public SafeIconHandle() : base(true)
{
}
public SafeIconHandle(IntPtr hIcon) : base(true)
{
SetHandle(hIcon);
}
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
protected override bool ReleaseHandle()
{
return User32.DestroyIcon(handle);
}
}
}

View file

@ -1,66 +0,0 @@
using System;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using Microsoft.Win32.SafeHandles;
namespace Greenshot.Base.UnmanagedHelpers
{
/// <summary>
/// A WindowDC SafeHandle implementation
/// </summary>
public class SafeWindowDcHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[DllImport("user32", SetLastError = true)]
private static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("user32", SetLastError = true)]
private static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
private readonly IntPtr _hWnd;
/// <summary>
/// Needed for marshalling return values
/// </summary>
public SafeWindowDcHandle() : base(true)
{
}
[SecurityCritical]
public SafeWindowDcHandle(IntPtr hWnd, IntPtr preexistingHandle) : base(true)
{
_hWnd = hWnd;
SetHandle(preexistingHandle);
}
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
protected override bool ReleaseHandle()
{
bool returnValue = ReleaseDC(_hWnd, handle);
return returnValue;
}
/// <summary>
/// Creates a DC as SafeWindowDcHandle for the whole of the specified hWnd
/// </summary>
/// <param name="hWnd">IntPtr</param>
/// <returns>SafeWindowDcHandle</returns>
public static SafeWindowDcHandle FromWindow(IntPtr hWnd)
{
if (hWnd == IntPtr.Zero)
{
return null;
}
var hDcDesktop = GetWindowDC(hWnd);
return new SafeWindowDcHandle(hWnd, hDcDesktop);
}
public static SafeWindowDcHandle FromDesktop()
{
IntPtr hWndDesktop = User32.GetDesktopWindow();
IntPtr hDCDesktop = GetWindowDC(hWndDesktop);
return new SafeWindowDcHandle(hWndDesktop, hDCDesktop);
}
}
}

View file

@ -1,123 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
namespace Greenshot.Base.UnmanagedHelpers
{
/// <summary>
/// Description of Shell32.
/// </summary>
public static class Shell32
{
[DllImport("shell32", CharSet = CharSet.Unicode)]
public static extern int ExtractIconEx(string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons);
[DllImport("shell32", CharSet = CharSet.Unicode)]
private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, uint uFlags);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct SHFILEINFO
{
public readonly IntPtr hIcon;
public readonly int iIcon;
public readonly uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public readonly string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public readonly string szTypeName;
};
// Browsing for directory.
private const uint SHGFI_ICON = 0x000000100; // get icon
private const uint SHGFI_LINKOVERLAY = 0x000008000; // put a link overlay on icon
private const uint SHGFI_LARGEICON = 0x000000000; // get large icon
private const uint SHGFI_SMALLICON = 0x000000001; // get small icon
private const uint SHGFI_USEFILEATTRIBUTES = 0x000000010; // use passed dwFileAttribute
private const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
/// <summary>
/// Options to specify the size of icons to return.
/// </summary>
public enum IconSize
{
/// <summary>
/// Specify large icon - 32 pixels by 32 pixels.
/// </summary>
Large = 0,
/// <summary>
/// Specify small icon - 16 pixels by 16 pixels.
/// </summary>
Small = 1
}
/// <summary>
/// Returns an icon for a given file extension - indicated by the name parameter.
/// See: https://msdn.microsoft.com/en-us/library/windows/desktop/bb762179(v=vs.85).aspx
/// </summary>
/// <param name="filename">Filename</param>
/// <param name="size">Large or small</param>
/// <param name="linkOverlay">Whether to include the link icon</param>
/// <returns>System.Drawing.Icon</returns>
public static Icon GetFileIcon(string filename, IconSize size, bool linkOverlay)
{
SHFILEINFO shfi = new SHFILEINFO();
// SHGFI_USEFILEATTRIBUTES makes it simulate, just gets the icon for the extension
uint flags = SHGFI_ICON | SHGFI_USEFILEATTRIBUTES;
if (linkOverlay)
{
flags += SHGFI_LINKOVERLAY;
}
// Check the size specified for return.
if (IconSize.Small == size)
{
flags += SHGFI_SMALLICON;
}
else
{
flags += SHGFI_LARGEICON;
}
SHGetFileInfo(Path.GetFileName(filename), FILE_ATTRIBUTE_NORMAL, ref shfi, (uint) Marshal.SizeOf(shfi), flags);
// Only return an icon if we really got one
if (shfi.hIcon != IntPtr.Zero)
{
// Copy (clone) the returned icon to a new object, thus allowing us to clean-up properly
Icon icon = (Icon) Icon.FromHandle(shfi.hIcon).Clone();
// Cleanup
User32.DestroyIcon(shfi.hIcon);
return icon;
}
return null;
}
}
}

View file

@ -1,66 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Runtime.InteropServices;
namespace Greenshot.Base.UnmanagedHelpers.Structs
{
[StructLayout(LayoutKind.Sequential), Serializable()]
public struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
X = x;
Y = y;
}
public POINT(Point point)
{
X = point.X;
Y = point.Y;
}
public static implicit operator Point(POINT p)
{
return new Point(p.X, p.Y);
}
public static implicit operator POINT(Point p)
{
return new POINT(p.X, p.Y);
}
public Point ToPoint()
{
return new Point(X, Y);
}
public override string ToString()
{
return X + "," + Y;
}
}
}

View file

@ -1,176 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Runtime.InteropServices;
namespace Greenshot.Base.UnmanagedHelpers.Structs
{
[StructLayout(LayoutKind.Sequential), Serializable()]
public struct RECT
{
private int _Left;
private int _Top;
private int _Right;
private int _Bottom;
public RECT(RECT rectangle)
: this(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom)
{
}
public RECT(Rectangle rectangle)
: this(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom)
{
}
public RECT(int left, int top, int right, int bottom)
{
_Left = left;
_Top = top;
_Right = right;
_Bottom = bottom;
}
public int X
{
get { return _Left; }
set { _Left = value; }
}
public int Y
{
get { return _Top; }
set { _Top = value; }
}
public int Left
{
get { return _Left; }
set { _Left = value; }
}
public int Top
{
get { return _Top; }
set { _Top = value; }
}
public int Right
{
get { return _Right; }
set { _Right = value; }
}
public int Bottom
{
get { return _Bottom; }
set { _Bottom = value; }
}
public int Height
{
get { return _Bottom - _Top; }
set { _Bottom = value - _Top; }
}
public int Width
{
get { return _Right - _Left; }
set { _Right = value + _Left; }
}
public Point Location
{
get { return new Point(Left, Top); }
set
{
_Left = value.X;
_Top = value.Y;
}
}
public Size Size
{
get { return new Size(Width, Height); }
set
{
_Right = value.Width + _Left;
_Bottom = value.Height + _Top;
}
}
public static implicit operator Rectangle(RECT rectangle)
{
return new Rectangle(rectangle.Left, rectangle.Top, rectangle.Width, rectangle.Height);
}
public static implicit operator RECT(Rectangle rectangle)
{
return new RECT(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom);
}
public static bool operator ==(RECT rectangle1, RECT rectangle2)
{
return rectangle1.Equals(rectangle2);
}
public static bool operator !=(RECT rectangle1, RECT rectangle2)
{
return !rectangle1.Equals(rectangle2);
}
public override string ToString()
{
return "{Left: " + _Left + "; " + "Top: " + _Top + "; Right: " + _Right + "; Bottom: " + _Bottom + "}";
}
public override int GetHashCode()
{
return ToString().GetHashCode();
}
public bool Equals(RECT rectangle)
{
return rectangle.Left == _Left && rectangle.Top == _Top && rectangle.Right == _Right && rectangle.Bottom == _Bottom;
}
public Rectangle ToRectangle()
{
return new Rectangle(Left, Top, Width, Height);
}
public override bool Equals(object Object)
{
if (Object is RECT)
{
return Equals((RECT) Object);
}
else if (Object is Rectangle)
{
return Equals(new RECT((Rectangle) Object));
}
return false;
}
}
}

View file

@ -1,131 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System.Drawing;
using System.Runtime.InteropServices;
namespace Greenshot.Base.UnmanagedHelpers.Structs
{
/// <summary>
/// A floating point GDI Plus width/hight based rectangle.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct RECTF
{
/// <summary>
/// The X corner location of the rectangle.
/// </summary>
public float X;
/// <summary>
/// The Y corner location of the rectangle.
/// </summary>
public float Y;
/// <summary>
/// The width of the rectangle.
/// </summary>
public float Width;
/// <summary>
/// The height of the rectangle.
/// </summary>
public float Height;
/// <summary>
/// Creates a new GDI Plus rectangle.
/// </summary>
/// <param name="x">The X corner location of the rectangle.</param>
/// <param name="y">The Y corner location of the rectangle.</param>
/// <param name="width">The width of the rectangle.</param>
/// <param name="height">The height of the rectangle.</param>
public RECTF(float x, float y, float width, float height)
{
X = x;
Y = y;
Width = width;
Height = height;
}
/// <summary>
/// Creates a new GDI Plus rectangle from a System.Drawing.RectangleF.
/// </summary>
/// <param name="rect">The rectangle to base this GDI Plus rectangle on.</param>
public RECTF(RectangleF rect)
{
X = rect.X;
Y = rect.Y;
Width = rect.Width;
Height = rect.Height;
}
/// <summary>
/// Creates a new GDI Plus rectangle from a System.Drawing.Rectangle.
/// </summary>
/// <param name="rect">The rectangle to base this GDI Plus rectangle on.</param>
public RECTF(Rectangle rect)
{
X = rect.X;
Y = rect.Y;
Width = rect.Width;
Height = rect.Height;
}
/// <summary>
/// Returns a RectangleF for this GDI Plus rectangle.
/// </summary>
/// <returns>A System.Drawing.RectangleF structure.</returns>
public RectangleF ToRectangle()
{
return new RectangleF(X, Y, Width, Height);
}
/// <summary>
/// Returns a RectangleF for a GDI Plus rectangle.
/// </summary>
/// <param name="rect">The GDI Plus rectangle to get the RectangleF for.</param>
/// <returns>A System.Drawing.RectangleF structure.</returns>
public static RectangleF ToRectangle(RECTF rect)
{
return rect.ToRectangle();
}
/// <summary>
/// Returns a GDI Plus rectangle for a RectangleF structure.
/// </summary>
/// <param name="rect">The RectangleF to get the GDI Plus rectangle for.</param>
/// <returns>A GDI Plus rectangle structure.</returns>
public static RECTF FromRectangle(RectangleF rect)
{
return new RECTF(rect);
}
/// <summary>
/// Returns a GDI Plus rectangle for a Rectangle structure.
/// </summary>
/// <param name="rect">The Rectangle to get the GDI Plus rectangle for.</param>
/// <returns>A GDI Plus rectangle structure.</returns>
public static RECTF FromRectangle(Rectangle rect)
{
return new RECTF(rect);
}
}
}

View file

@ -1,38 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Runtime.InteropServices;
namespace Greenshot.Base.UnmanagedHelpers.Structs
{
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct SCROLLINFO
{
public int cbSize;
public int fMask;
public int nMin;
public int nMax;
public int nPage;
public int nPos;
public int nTrackPos;
}
}

View file

@ -1,52 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Runtime.InteropServices;
namespace Greenshot.Base.UnmanagedHelpers.Structs
{
/// <summary>
/// The structure for the WindowInfo
/// See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms632610%28v=vs.85%29.aspx
/// </summary>
[StructLayout(LayoutKind.Sequential), Serializable]
public struct WindowInfo
{
public uint cbSize;
public RECT rcWindow;
public RECT rcClient;
public uint dwStyle;
public uint dwExStyle;
public uint dwWindowStatus;
public uint cxWindowBorders;
public uint cyWindowBorders;
public ushort atomWindowType;
public ushort wCreatorVersion;
// Allows automatic initialization of "cbSize" with "new WINDOWINFO(null/true/false)".
public WindowInfo(bool? filler) : this()
{
cbSize = (uint) (Marshal.SizeOf(typeof(WindowInfo)));
}
}
}

View file

@ -1,80 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Runtime.InteropServices;
using Greenshot.Base.UnmanagedHelpers.Enums;
namespace Greenshot.Base.UnmanagedHelpers.Structs
{
/// <summary>
/// Contains information about the placement of a window on the screen.
/// </summary>
[StructLayout(LayoutKind.Sequential), Serializable()]
public struct WindowPlacement
{
/// <summary>
/// The length of the structure, in bytes. Before calling the GetWindowPlacement or SetWindowPlacement functions, set this member to sizeof(WINDOWPLACEMENT).
/// <para>
/// GetWindowPlacement and SetWindowPlacement fail if this member is not set correctly.
/// </para>
/// </summary>
public int Length;
/// <summary>
/// Specifies flags that control the position of the minimized window and the method by which the window is restored.
/// </summary>
public WindowPlacementFlags Flags;
/// <summary>
/// The current show state of the window.
/// </summary>
public ShowWindowCommand ShowCmd;
/// <summary>
/// The coordinates of the window's upper-left corner when the window is minimized.
/// </summary>
public POINT MinPosition;
/// <summary>
/// The coordinates of the window's upper-left corner when the window is maximized.
/// </summary>
public POINT MaxPosition;
/// <summary>
/// The window's coordinates when the window is in the restored position.
/// </summary>
public RECT NormalPosition;
/// <summary>
/// Gets the default (empty) value.
/// </summary>
public static WindowPlacement Default
{
get
{
WindowPlacement result = new WindowPlacement();
result.Length = Marshal.SizeOf(result);
return result;
}
}
}
}

Some files were not shown because too many files have changed in this diff Show more