Compare commits

..

52 Commits

Author SHA1 Message Date
Fushan Wen
485e084fa9 QImage: use rvalue overloads more
to reuse internal buffers
2023-09-16 23:29:30 +08:00
Mirco Miranda
75e1280073 hdr: options support and bugfixes
- Support for Size and Format options
- Added compile option to generate 16-bit instead of 32-bit images
- Includes MR !189 and MR !192 fixes
2023-09-11 09:08:18 +00:00
Mirco Miranda
0a6fbd88e9 xcf: fix crash (oss-fuzz issue 62075) 2023-09-07 16:12:41 +00:00
Mirco Miranda
fbf60f8bbb xcf: fix possible overflow
Port to KF6 of MR !187
2023-09-05 11:32:32 +00:00
Heiko Becker
2799382c21 Drop code for Qt < 6.5.0
...now that we depend on Qt >= 6.5.0.
2023-08-30 00:27:47 +02:00
Heiko Becker
adc7da4f41 GIT_SILENT Upgrade Qt6 version requirement to 6.5.0. 2023-08-29 23:31:26 +02:00
Daniel Novomeský
ac808679cd qoi: initialize structure before use
It was reported by Code Quality scans as Critical (CWE-457)
2023-08-29 21:04:00 +02:00
Daniel Novomeský
2aea982e9e qoi: fix reports from quality scanner
struct QoiHeader is initialized to invalid values prior use,
it’s good to detect situations when we read incomplete data.
Add include in scanlineconverter.cpp so it can be used without
change in kf5 branch.
2023-08-29 16:22:52 +02:00
Mirco Miranda
9173f02ea3 ras: rle decode
Added support for RLE compressed images. This patch makes the player almost complete allowing to read all type 1,2 and 3 RAS files. Only types 4 and 5 are missing which are images converted from TIFF and IFF.
2023-08-29 09:11:35 +00:00
Antonio Rojas
4badb3088e Support libavif 1.0
Make cmake find libavif 1.0 and adapt to API changes (which is reverting to pre 0.9.2 API)
2023-08-29 09:21:37 +02:00
Mirco Miranda
8dc685df26 qoi: write support
As a base I used the reference implementation found on the official site at https://qoiformat.org/ (MIT license).
I added a class to convert scan lines in scanlineconverter.cpp. The class takes advantage of the QImage conversion and contrary to what one might expect, with large images it improves performance (compared to converting the whole image) 😄 

In progressive mode, for each line, the following conversions (only if needed) are made before saving:
1. If the icc profile is set, the line is converted to sRGB or sRGB Linear.
2. The line is scaled to 8 bits with RGBA order.
2023-08-28 22:25:10 +00:00
Friedrich W. H. Kossebau
4bd9d5baec No longer needed to explicitly include CMakeParseArguments
NO_CHANGELOG
2023-08-28 22:54:41 +02:00
Mirco Miranda
79e8e183eb ras: code revamped
- Progressive load from file
- Added support for 1-bit image
- Images with palette are now Index (instead of RGB32)
- Added options support (Size, Format)
- Added some test cases
- Improved performance by directly accessing the scanline
- Support for more RAS extension (taken from GIMP)

The code should works "as is" also on KF5.
2023-08-28 18:02:02 +00:00
Mirco Miranda
7d63a1d8fa exr: multiple fixes
- Support for images with transparency
- Precise colorspace conversion using QT color spaces
- Set the correct resolution
- Set useful metadata
- Support for RGBX16FPx4 format in Qt 6
- Speed improvements

![image](/uploads/bb36492d71acce4995e8b4229a813031/image.png)
2023-08-28 17:30:50 +00:00
Mirco Miranda
c11c5eff4f xcf: format v12 support
This is a patch over MR !108 by @sandsmark. Martin has done a great job implementing support and I find it a shame not to use it.

I made sure that the results are the same as the current version and fixed the problems of pixels with wrong colors with color depth grater than 8 bits. I also fixed conversion errors on mask and gray images (16/32 bits).
Unfortunately the internal rendering engine of the original code is 8-bit so I always forced the output of 8-bit images to correct the problems (see image below). Since it is a plugin with a "rendering engine", the tests to seriously validate it are potentially endless (as the original version is not perfect).

Errors of the original version of the MR (right) which should no longer occur:
![image](/uploads/9ef24eb5436bd19ff1fb428242a9c119/image.png)
2023-08-28 17:27:08 +00:00
Mirco Miranda
c02bf3dbcc Readme.md: Update supported formats list
- (+) Krita (kra)
- (+) OpenRaster (ora)
- (-) XView (xv)
2023-08-20 08:04:21 +00:00
Mirco Miranda
6254529d2d qoi: fix buffer overflow
- fix buffer overflow with corrupted images without image data
- fix unable to read very small images (e.g. 1x1 px)
- new test cases added
- detect incomplete files by checking the end of streams as written in the specs
2023-08-18 14:09:00 +00:00
Mirco Miranda
35ff3efbbc hdr: improve precision
- Improve the precision of HDR format (it is a floating point format) by using the RGBA32FPx4 image format.
2023-08-13 22:19:22 +00:00
Mirco Miranda
7a0d95af92 Renamed qoi.h to qoi_p.h 2023-08-12 23:47:29 +02:00
Mirco Miranda
4c3ade04dd Minor improvements 2023-08-12 09:03:56 +02:00
Ernest Gupik
b209e54b6f Add support for the QOI image format
This MR adds read-only QOI (https://qoiformat.org/) image format support for KImageFormats.

This format has received it's MIME type inclusion in shared-mime-info 4 months ago: ff51a0a9e3

The code is based on the reference QOI implementation at https://github.com/phoboslab/qoi/blob/master/qoi.h

Official test images: https://qoiformat.org/qoi_test_images.zip

![pngvsqoi](/uploads/e386aa5057641057106e21940c770d97/pngvsqoi.png)

Also: This is my first MR to KDE ;)
2023-08-11 20:44:17 +00:00
Mirco Miranda
4dc2099fa4 Set linear color space for proper viewing 2023-07-21 14:17:11 +02:00
Mirco Miranda
491b223c15 psd: Fix UB type punning
BUGS: 471829
2023-07-16 08:07:13 +00:00
Mirco Miranda
6559bf8994 Treat 3-channel MCH images as CMY images 2023-07-03 12:34:54 +02:00
Friedrich W. H. Kossebau
34ed3bad27 Add explicit moc includes to sources for moc-covered headers
* speeds up incremental builds as changes to a header will not always
  need the full mocs_compilation.cpp for all the target's headers rebuild,
  while having a moc file sourced into a source file only adds minor
  extra costs, due to small own code and the used headers usually
  already covered by the source file, being for the same class/struct
* seems to not slow down clean builds, due to empty mocs_compilation.cpp
  resulting in those quickly processed, while the minor extra cost of the
  sourced moc files does not outweigh that in summary.
  Measured times actually improved by some percent points.
  (ideally CMake would just skip empty mocs_compilation.cpp & its object
  file one day)
* enables compiler to see all methods of a class in same compilation unit
  to do some sanity checks
* potentially more inlining in general, due to more in the compilation unit
* allows to keep using more forward declarations in the header, as with the
  moc code being sourced into the cpp file there definitions can be ensured
  and often are already for the needs of the normal class methods
2023-07-02 03:08:44 +02:00
Daniel Novomeský
9c579fc1f8 jxl: add support for libjxl v0.9, drop support for old 0.6.1 2023-06-27 14:32:08 +02:00
Mirco Miranda
93adb22632 raw: change the use of the quality parameter
- Standardized the quality parameter between 0-100
- The value -1 is the default value of Qt plugins and is managed
- Negative values other than -1 are used as flags to activate the custom mode
2023-05-17 17:45:23 +00:00
Mirco Miranda
d57ff91f8b pcx: multiple fixes (2)
- 1-bit writer: checks where is black and use NOT operator only if needed
- Fix images with witdh == 65536(*)
- Checks result of disk writes and reads on all formats

(*) PCX formats support images with with of 65536 but only if the header field bytesPerLine is valid (no overflow). This means that the width 65536 is supported on 1bpp images only.
The previous version of the plugins wrote an image with width of 65536px in the wrong way and it was unable to read it (wrong image returned). I verified that Photoshop and Gimp weren't able to read the image either.
2023-05-12 08:53:50 +00:00
Mirco Miranda
edd6adcbac Avoid unnecessary conversions 2023-05-10 15:34:14 +02:00
Mirco Miranda
d787c12727 RGB/SGI writer: fix alpha detection and image limit size 2023-05-10 11:43:21 +00:00
Mirco Miranda
c9fec5e408 TGA writer: fix alpha detection and performance improvements 2023-05-10 11:43:21 +00:00
Mirco Miranda
e60dfd4968 pcx: multiple fixes
- Fix wrong RGB channel order if image format is other than (A)RGB32
- Write right resolution
- Set right resolution on image load
- Return false on write error
- Save images with depth greater than 24-bits
2023-05-10 11:43:04 +00:00
Mirco Miranda
f5a9dd46d2 Removed unused jxl.desktop file 2023-04-22 09:31:30 +02:00
Mirco Miranda
41f0411b62 PSD: fix test failure on some systems and fix wrong check on alpha conversion
- Fix PSD alpha conversion
- Fix autoread test to use also TIFF images
2023-04-18 22:14:41 +00:00
Gary Wang
e1a3751936 CMake: make use of qt_add_plugin for plugins
Currently it will only produce shared library binaries as plugin.
This could make building static imageformats plugin much easier.

Related: https://invent.kde.org/frameworks/kimageformats/-/issues/4
2023-03-30 03:42:41 +00:00
Mirco Miranda
402dfb5de3 psd: Fix alpha blending
PSD files are saved with as alpha premultiplied. The problem is that alpha refers to white instead of black so it requires transformation formulas. Then, to conver PS premultiplied to QImage premultiplied you have to use the following formula:

* V = Alpha + Vps - Max (C, M, Y, K, R, G, B, Gray, L\* components)
* V = Vps + (Alpha - Max + 1) / 2 (a\*, b\* components)

Where Max is the maximum value depending on the image depth and Vps is the valued read from the file.
2023-03-29 17:55:55 +00:00
Éric Gillet
a3f7c03b61 heif: Add format read-write support in README 2023-02-15 08:36:24 +00:00
Freya Lupen
20cec27ae8 Fix writing TGA alpha depth flag
Correctly write alpha channel depth as 8-bit.
2023-02-01 16:26:31 -06:00
Volker Krause
d34c1668aa Bump Qt deprecation level to 6.4 2023-02-01 17:16:45 +01:00
Mirco Miranda
560d0483ae Replace deprecated functions
Improve the code by replacing deprecated functions as suggested by the Qt documentation.
2023-01-31 21:32:30 +00:00
Mirco Miranda
085c9c4841 HDR format removed from RAW plugin 2023-01-30 21:58:36 +00:00
Daniel Novomeský
b654f20ece heif: reject invalid files with zero size 2023-01-29 16:16:52 +01:00
Mirco Miranda
21211cd63b psd: LAB conversion speed improved
Using an approximated pow function improves the conversion speed of LAB images by more than 50% without an appreciable visual difference.
2023-01-27 23:19:48 +00:00
Mirco Miranda
5cc7a2b45c psd: native 32-bits RGB support
This patch maps RGB 32-bits PSD to QImage::Format_RGBA32FPx4 or QImage::Format_RGBX32FPx4.
2023-01-26 23:17:09 +00:00
Volker Krause
4451737d2f Remove Qt 5 support 2023-01-24 17:15:14 +01:00
Mirco Miranda
01ab0876f1 Debug code removed 2023-01-22 11:04:48 +01:00
Mirco Miranda
a67dcac7d1 LAB/CMYK conversion speed improved by ~10% 2023-01-22 10:57:54 +01:00
Nicolas Fella
cef8d08ad4 Bump KF_DEP_VERSION for KF6 2023-01-21 13:30:04 +01:00
Nicolas Fella
a4b9dd9400 Rename CMake targets/config/libraries for KF6 2023-01-20 23:58:58 +01:00
Nicolas Fella
6e6c1ab5f4 Require Qt6
master is for Qt6-based development now

Qt5 development continues in the 'kf5' branch
2023-01-18 23:37:26 +01:00
Nicolas Fella
f205adf2e0 Remove Qt5 CI
master is Qt6 now
2023-01-18 23:37:26 +01:00
Mirco Miranda
14742cb502 PCX: Fix reading of the extended palette
The VGA palette starts 769 bytes before the end of the file. There may be PADs between the end of the image and the start of the palette.

BUG: 463951
2023-01-11 22:56:38 +00:00
81 changed files with 634 additions and 848 deletions

View File

@@ -2,11 +2,7 @@
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
include: include:
- project: sysadmin/ci-utilities - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux-qt6.yml
file: - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android-qt6.yml
- /gitlab-templates/linux.yml - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd-qt6.yml
- /gitlab-templates/linux-static.yml - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/windows-qt6.yml
- /gitlab-templates/android.yml
- /gitlab-templates/freebsd.yml
- /gitlab-templates/windows.yml
- /gitlab-templates/windows-static.yml

View File

@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.16)
project(KImageFormats) project(KImageFormats)
include(FeatureSummary) include(FeatureSummary)
find_package(ECM 5.116.0 NO_MODULE) find_package(ECM 5.240.0 NO_MODULE)
set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules") set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules")
feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES)
@@ -19,11 +19,11 @@ include(ECMDeprecationSettings)
include(CheckIncludeFiles) include(CheckIncludeFiles)
include(FindPkgConfig) include(FindPkgConfig)
set(REQUIRED_QT_VERSION 5.15.2) set(REQUIRED_QT_VERSION 6.5.0)
find_package(Qt${QT_MAJOR_VERSION}Gui ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE) find_package(Qt6Gui ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
find_package(KF5Archive) find_package(KF6Archive)
set_package_properties(KF5Archive PROPERTIES set_package_properties(KF6Archive PROPERTIES
TYPE OPTIONAL TYPE OPTIONAL
PURPOSE "Required for the QImage plugin for Krita and OpenRaster images" PURPOSE "Required for the QImage plugin for Krita and OpenRaster images"
) )
@@ -32,12 +32,12 @@ set_package_properties(KF5Archive PROPERTIES
# this available in PATH # this available in PATH
set(BUILD_EPS_PLUGIN FALSE) set(BUILD_EPS_PLUGIN FALSE)
if (UNIX) if (UNIX)
find_package(Qt${QT_MAJOR_VERSION}PrintSupport ${REQUIRED_QT_VERSION} NO_MODULE) find_package(Qt6PrintSupport ${REQUIRED_QT_VERSION} NO_MODULE)
set_package_properties(Qt${QT_MAJOR_VERSION}PrintSupport PROPERTIES set_package_properties(Qt6PrintSupport PROPERTIES
PURPOSE "Required for the QImage plugin for EPS images" PURPOSE "Required for the QImage plugin for EPS images"
TYPE OPTIONAL TYPE OPTIONAL
) )
if (TARGET Qt${QT_MAJOR_VERSION}::PrintSupport) if (TARGET Qt6::PrintSupport)
set(BUILD_EPS_PLUGIN TRUE) set(BUILD_EPS_PLUGIN TRUE)
endif() endif()
endif() endif()
@@ -81,8 +81,8 @@ set_package_properties(LibRaw PROPERTIES
) )
ecm_set_disabled_deprecation_versions( ecm_set_disabled_deprecation_versions(
QT 5.15.2 QT 6.4
KF 5.95 KF 5.102
) )
add_subdirectory(src) add_subdirectory(src)

View File

@@ -16,22 +16,24 @@ The following image formats have read-only support:
- Animated Windows cursors (ani) - Animated Windows cursors (ani)
- Camera RAW images (arw, cr2, cr3, dcs, dng, ...) - Camera RAW images (arw, cr2, cr3, dcs, dng, ...)
- Gimp (xcf) - Gimp (xcf)
- Krita (kra)
- OpenEXR (exr) - OpenEXR (exr)
- OpenRaster (ora)
- Photoshop documents (psd, psb, pdd, psdt) - Photoshop documents (psd, psb, pdd, psdt)
- Radiance HDR (hdr) - Radiance HDR (hdr)
- Sun Raster (ras) - Sun Raster (im1, im8, im24, im32, ras, sun)
The following image formats have read and write support: The following image formats have read and write support:
- AV1 Image File Format (AVIF) - AV1 Image File Format (AVIF)
- Encapsulated PostScript (eps) - Encapsulated PostScript (eps)
- High Efficiency Image File Format (heif). Can be enabled with the KIMAGEFORMATS_HEIF build option.
- JPEG XL (jxl) - JPEG XL (jxl)
- Personal Computer Exchange (pcx) - Personal Computer Exchange (pcx)
- Quite OK Image format (qoi) - Quite OK Image format (qoi)
- SGI images (rgb, rgba, sgi, bw) - SGI images (rgb, rgba, sgi, bw)
- Softimage PIC (pic) - Softimage PIC (pic)
- Targa (tga): supports more formats than Qt's version - Targa (tga): supports more formats than Qt's version
- XView (xv)
## Contributing ## Contributing

View File

@@ -1,5 +1,3 @@
#find_package(Qt5Test ${REQUIRED_QT_VERSION} NO_MODULE)
include(ECMMarkAsTest) include(ECMMarkAsTest)
add_definitions(-DPLUGIN_DIR="${CMAKE_CURRENT_BINARY_DIR}/../bin") add_definitions(-DPLUGIN_DIR="${CMAKE_CURRENT_BINARY_DIR}/../bin")
@@ -14,7 +12,7 @@ macro(kimageformats_read_tests)
if (NOT TARGET readtest) if (NOT TARGET readtest)
add_executable(readtest readtest.cpp) add_executable(readtest readtest.cpp)
target_link_libraries(readtest Qt${QT_MAJOR_VERSION}::Gui) target_link_libraries(readtest Qt6::Gui)
target_compile_definitions(readtest target_compile_definitions(readtest
PRIVATE IMAGEDIR="${CMAKE_CURRENT_SOURCE_DIR}/read") PRIVATE IMAGEDIR="${CMAKE_CURRENT_SOURCE_DIR}/read")
ecm_mark_as_test(readtest) ecm_mark_as_test(readtest)
@@ -37,7 +35,7 @@ macro(kimageformats_write_tests)
if (NOT TARGET writetest) if (NOT TARGET writetest)
add_executable(writetest writetest.cpp) add_executable(writetest writetest.cpp)
target_link_libraries(writetest Qt${QT_MAJOR_VERSION}::Gui) target_link_libraries(writetest Qt6::Gui)
target_compile_definitions(writetest target_compile_definitions(writetest
PRIVATE IMAGEDIR="${CMAKE_CURRENT_SOURCE_DIR}/write") PRIVATE IMAGEDIR="${CMAKE_CURRENT_SOURCE_DIR}/write")
ecm_mark_as_test(writetest) ecm_mark_as_test(writetest)
@@ -75,7 +73,7 @@ kimageformats_read_tests(
tga tga
) )
if (KF5Archive_FOUND) if (KF6Archive_FOUND)
kimageformats_read_tests( kimageformats_read_tests(
kra kra
ora ora
@@ -99,12 +97,6 @@ if (LibHeif_FOUND)
kimageformats_write_tests(FUZZ 1 kimageformats_write_tests(FUZZ 1
heif-nodatacheck-lossless heif-nodatacheck-lossless
) )
if (LibHeif_VERSION VERSION_GREATER_EQUAL "1.17.0")
kimageformats_read_tests(FUZZ 1
hej2
)
endif()
endif() endif()
if (LibJXL_FOUND AND LibJXLThreads_FOUND) if (LibJXL_FOUND AND LibJXLThreads_FOUND)
@@ -154,19 +146,19 @@ if (LibRaw_FOUND)
) )
endif() endif()
find_package(Qt${QT_MAJOR_VERSION}Test ${REQUIRED_QT_VERSION} CONFIG QUIET) find_package(Qt6Test ${REQUIRED_QT_VERSION} CONFIG QUIET)
if(NOT TARGET Qt${QT_MAJOR_VERSION}::Test) if(NOT TARGET Qt6::Test)
message(STATUS "Qt${QT_MAJOR_VERSION}Test not found, some autotests will not be built.") message(STATUS "Qt6Test not found, some autotests will not be built.")
return() return()
endif() endif()
add_executable(pictest pictest.cpp) add_executable(pictest pictest.cpp)
target_link_libraries(pictest Qt${QT_MAJOR_VERSION}::Gui Qt${QT_MAJOR_VERSION}::Test) target_link_libraries(pictest Qt6::Gui Qt6::Test)
ecm_mark_as_test(pictest) ecm_mark_as_test(pictest)
add_test(NAME kimageformats-pic COMMAND pictest) add_test(NAME kimageformats-pic COMMAND pictest)
add_executable(anitest anitest.cpp) add_executable(anitest anitest.cpp)
target_link_libraries(anitest Qt${QT_MAJOR_VERSION}::Gui Qt${QT_MAJOR_VERSION}::Test) target_link_libraries(anitest Qt6::Gui Qt6::Test)
ecm_mark_as_test(anitest) ecm_mark_as_test(anitest)
add_test(NAME kimageformats-ani COMMAND anitest) add_test(NAME kimageformats-ani COMMAND anitest)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 112 KiB

View File

@@ -162,13 +162,13 @@ int main(int argc, char **argv)
if (!fi.suffix().compare("png", Qt::CaseInsensitive) || !fi.suffix().compare("tif", Qt::CaseInsensitive)) { if (!fi.suffix().compare("png", Qt::CaseInsensitive) || !fi.suffix().compare("tif", Qt::CaseInsensitive)) {
continue; continue;
} }
int suffixPos = fi.filePath().count() - suffix.count(); int suffixPos = fi.filePath().size() - suffix.size();
QString inputfile = fi.filePath(); QString inputfile = fi.filePath();
QString fmt = QStringLiteral("png"); QString fmt = QStringLiteral("png");
QString expfile = fi.filePath().replace(suffixPos, suffix.count(), fmt); QString expfile = fi.filePath().replace(suffixPos, suffix.size(), fmt);
if (!QFile::exists(expfile)) { // try with tiff if (!QFile::exists(expfile)) { // try with tiff
fmt = QStringLiteral("tif"); fmt = QStringLiteral("tif");
expfile = fi.filePath().replace(suffixPos, suffix.count(), fmt); expfile = fi.filePath().replace(suffixPos, suffix.size(), fmt);
} }
QString expfilename = QFileInfo(expfile).fileName(); QString expfilename = QFileInfo(expfile).fileName();

View File

@@ -85,8 +85,8 @@ int main(int argc, char **argv)
if (parser.isSet(ignoreDataCheck)) { if (parser.isSet(ignoreDataCheck)) {
pngfile = fi.filePath(); pngfile = fi.filePath();
} else { } else {
int suffixPos = fi.filePath().count() - suffix.count(); int suffixPos = fi.filePath().size() - suffix.size();
pngfile = fi.filePath().replace(suffixPos, suffix.count(), QStringLiteral("png")); pngfile = fi.filePath().replace(suffixPos, suffix.size(), QStringLiteral("png"));
} }
QString pngfilename = QFileInfo(pngfile).fileName(); QString pngfilename = QFileInfo(pngfile).fileName();

View File

@@ -11,9 +11,10 @@ function(kimageformats_add_plugin plugin)
message(FATAL_ERROR "kimageformats_add_plugin called without SOURCES parameter") message(FATAL_ERROR "kimageformats_add_plugin called without SOURCES parameter")
endif() endif()
add_library(${plugin} MODULE ${KIF_ADD_PLUGIN_SOURCES}) qt_add_plugin(${plugin} PLUGIN_TYPE imageformats)
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/imageformats") target_sources(${plugin} PRIVATE ${KIF_ADD_PLUGIN_SOURCES})
target_link_libraries(${plugin} Qt${QT_MAJOR_VERSION}::Gui) set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/imageformats)
target_link_libraries(${plugin} PRIVATE Qt6::Gui)
install(TARGETS ${plugin} DESTINATION ${KDE_INSTALL_QTPLUGINDIR}/imageformats) install(TARGETS ${plugin} DESTINATION ${KDE_INSTALL_QTPLUGINDIR}/imageformats)
endfunction() endfunction()
@@ -21,176 +22,105 @@ endfunction()
kimageformats_add_plugin(kimg_ani SOURCES ani.cpp) kimageformats_add_plugin(kimg_ani SOURCES ani.cpp)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES ani.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
if (TARGET avif) if (TARGET avif)
kimageformats_add_plugin(kimg_avif SOURCES "avif.cpp") kimageformats_add_plugin(kimg_avif SOURCES "avif.cpp")
target_link_libraries(kimg_avif "avif") target_link_libraries(kimg_avif PRIVATE "avif")
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES avif.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
endif()
##################################
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES dds-qt.desktop RENAME dds.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif() endif()
################################## ##################################
if (BUILD_EPS_PLUGIN) if (BUILD_EPS_PLUGIN)
if (TARGET Qt${QT_MAJOR_VERSION}::PrintSupport) if (TARGET Qt6::PrintSupport)
kimageformats_add_plugin(kimg_eps SOURCES eps.cpp) kimageformats_add_plugin(kimg_eps SOURCES eps.cpp)
target_link_libraries(kimg_eps Qt${QT_MAJOR_VERSION}::PrintSupport) target_link_libraries(kimg_eps PRIVATE Qt6::PrintSupport)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES eps.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
endif() endif()
endif() endif()
################################## ##################################
if (QT_MAJOR_VERSION STREQUAL "5")
# need this for Qt's version of the plugin
install(FILES jp2.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
##################################
if(OpenEXR_FOUND) if(OpenEXR_FOUND)
kimageformats_add_plugin(kimg_exr SOURCES exr.cpp) kimageformats_add_plugin(kimg_exr SOURCES exr.cpp)
if(TARGET OpenEXR::OpenEXR) if(TARGET OpenEXR::OpenEXR)
target_link_libraries(kimg_exr OpenEXR::OpenEXR) target_link_libraries(kimg_exr PRIVATE OpenEXR::OpenEXR)
else() else()
if(OpenEXR_VERSION_STRING VERSION_LESS 2.3.0) if(OpenEXR_VERSION_STRING VERSION_LESS 2.3.0)
# Older OpenEXR versions use dynamic exception specifications, so # Older OpenEXR versions use dynamic exception specifications, so
# cannot use C++17 with them # cannot use C++17 with them
set_target_properties(kimg_exr PROPERTIES CXX_STANDARD 14) set_target_properties(kimg_exr PROPERTIES CXX_STANDARD 14)
endif() endif()
target_link_libraries(kimg_exr OpenEXR::IlmImf) target_link_libraries(kimg_exr PRIVATE OpenEXR::IlmImf)
endif() endif()
kde_target_enable_exceptions(kimg_exr PRIVATE) kde_target_enable_exceptions(kimg_exr PRIVATE)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES exr.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
endif() endif()
################################## ##################################
kimageformats_add_plugin(kimg_hdr SOURCES hdr.cpp) kimageformats_add_plugin(kimg_hdr SOURCES hdr.cpp)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES hdr.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
if (LibHeif_FOUND) if (LibHeif_FOUND)
kimageformats_add_plugin(kimg_heif SOURCES heif.cpp) kimageformats_add_plugin(kimg_heif SOURCES heif.cpp)
target_link_libraries(kimg_heif PkgConfig::LibHeif) target_link_libraries(kimg_heif PRIVATE PkgConfig::LibHeif)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES heif.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
endif() endif()
################################## ##################################
if (LibJXL_FOUND AND LibJXLThreads_FOUND) if (LibJXL_FOUND AND LibJXLThreads_FOUND)
kimageformats_add_plugin(kimg_jxl SOURCES jxl.cpp) kimageformats_add_plugin(kimg_jxl SOURCES jxl.cpp)
target_link_libraries(kimg_jxl PkgConfig::LibJXL PkgConfig::LibJXLThreads) target_link_libraries(kimg_jxl PRIVATE PkgConfig::LibJXL PkgConfig::LibJXLThreads)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES jxl.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
endif() endif()
################################## ##################################
kimageformats_add_plugin(kimg_pcx SOURCES pcx.cpp) kimageformats_add_plugin(kimg_pcx SOURCES pcx.cpp)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES pcx.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
kimageformats_add_plugin(kimg_pic SOURCES pic.cpp) kimageformats_add_plugin(kimg_pic SOURCES pic.cpp)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES pic.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
kimageformats_add_plugin(kimg_psd SOURCES psd.cpp) kimageformats_add_plugin(kimg_psd SOURCES psd.cpp)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES psd.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
kimageformats_add_plugin(kimg_qoi SOURCES qoi.cpp scanlineconverter.cpp) kimageformats_add_plugin(kimg_qoi SOURCES qoi.cpp scanlineconverter.cpp)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES qoi.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
kimageformats_add_plugin(kimg_ras SOURCES ras.cpp) kimageformats_add_plugin(kimg_ras SOURCES ras.cpp)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES ras.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
kimageformats_add_plugin(kimg_rgb SOURCES rgb.cpp) kimageformats_add_plugin(kimg_rgb SOURCES rgb.cpp)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES rgb.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
kimageformats_add_plugin(kimg_tga SOURCES tga.cpp) kimageformats_add_plugin(kimg_tga SOURCES tga.cpp)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES tga.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
kimageformats_add_plugin(kimg_xcf SOURCES xcf.cpp) kimageformats_add_plugin(kimg_xcf SOURCES xcf.cpp)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES xcf.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
if (LibRaw_FOUND) if (LibRaw_FOUND)
kimageformats_add_plugin(kimg_raw SOURCES raw.cpp) kimageformats_add_plugin(kimg_raw SOURCES raw.cpp)
kde_enable_exceptions() kde_enable_exceptions()
target_link_libraries(kimg_raw LibRaw::LibRaw) target_link_libraries(kimg_raw PRIVATE LibRaw::LibRaw)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES raw.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
endif() endif()
################################## ##################################
if (KF5Archive_FOUND) if (KF6Archive_FOUND)
kimageformats_add_plugin(kimg_kra SOURCES kra.cpp) kimageformats_add_plugin(kimg_kra SOURCES kra.cpp)
target_link_libraries(kimg_kra KF5::Archive) target_link_libraries(kimg_kra PRIVATE KF6::Archive)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES kra.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
kimageformats_add_plugin(kimg_ora SOURCES ora.cpp) kimageformats_add_plugin(kimg_ora SOURCES ora.cpp)
target_link_libraries(kimg_ora KF5::Archive) target_link_libraries(kimg_ora PRIVATE KF6::Archive)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES ora.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
endif() endif()

View File

@@ -90,7 +90,7 @@ bool ANIHandler::read(QImage *outImage)
} }
const QByteArray frameSizeData = device()->read(sizeof(quint32_le)); const QByteArray frameSizeData = device()->read(sizeof(quint32_le));
if (frameSizeData.count() != sizeof(quint32_le)) { if (frameSizeData.size() != sizeof(quint32_le)) {
return false; return false;
} }
@@ -384,7 +384,7 @@ bool ANIHandler::ensureScanned() const
// TODO should we check that the number of rate entries matches nSteps? // TODO should we check that the number of rate entries matches nSteps?
auto *dataPtr = data.data(); auto *dataPtr = data.data();
QVector<int> list; QVector<int> list;
for (int i = 0; i < data.count(); i += sizeof(quint32_le)) { for (int i = 0; i < data.size(); i += sizeof(quint32_le)) {
const auto entry = *(reinterpret_cast<const quint32_le *>(dataPtr + i)); const auto entry = *(reinterpret_cast<const quint32_le *>(dataPtr + i));
list.append(entry); list.append(entry);
} }

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=ani
X-KDE-MimeType=application/x-navi-animation
X-KDE-Read=true
X-KDE-Write=false

View File

@@ -16,34 +16,9 @@
#include <cfloat> #include <cfloat>
/*
Quality range - compression/subsampling
100 - lossless RGB compression
< KIMG_AVIF_QUALITY_BEST, 100 ) - YUV444 color subsampling
< KIMG_AVIF_QUALITY_HIGH, KIMG_AVIF_QUALITY_BEST ) - YUV422 color subsampling
< 0, KIMG_AVIF_QUALITY_HIGH ) - YUV420 color subsampling
< 0, KIMG_AVIF_QUALITY_LOW ) - lossy compression of alpha channel
*/
#ifndef KIMG_AVIF_DEFAULT_QUALITY
#define KIMG_AVIF_DEFAULT_QUALITY 68
#endif
#ifndef KIMG_AVIF_QUALITY_BEST
#define KIMG_AVIF_QUALITY_BEST 90
#endif
#ifndef KIMG_AVIF_QUALITY_HIGH
#define KIMG_AVIF_QUALITY_HIGH 80
#endif
#ifndef KIMG_AVIF_QUALITY_LOW
#define KIMG_AVIF_QUALITY_LOW 51
#endif
QAVIFHandler::QAVIFHandler() QAVIFHandler::QAVIFHandler()
: m_parseState(ParseAvifNotParsed) : m_parseState(ParseAvifNotParsed)
, m_quality(KIMG_AVIF_DEFAULT_QUALITY) , m_quality(52)
, m_container_width(0) , m_container_width(0)
, m_container_height(0) , m_container_height(0)
, m_rawAvifData(AVIF_DATA_EMPTY) , m_rawAvifData(AVIF_DATA_EMPTY)
@@ -355,10 +330,6 @@ bool QAVIFHandler::decode_one_frame()
avifRGBImage rgb; avifRGBImage rgb;
avifRGBImageSetDefaults(&rgb, m_decoder->image); avifRGBImageSetDefaults(&rgb, m_decoder->image);
#if AVIF_VERSION >= 1000000
rgb.maxThreads = m_decoder->maxThreads;
#endif
if (m_decoder->image->depth > 8) { if (m_decoder->image->depth > 8) {
rgb.depth = 16; rgb.depth = 16;
rgb.format = AVIF_RGB_FORMAT_RGBA; rgb.format = AVIF_RGB_FORMAT_RGBA;
@@ -544,17 +515,9 @@ bool QAVIFHandler::write(const QImage &image)
} }
} }
if (m_quality > 100) {
m_quality = 100;
} else if (m_quality < 0) {
m_quality = KIMG_AVIF_DEFAULT_QUALITY;
}
#if AVIF_VERSION < 1000000
int maxQuantizer = AVIF_QUANTIZER_WORST_QUALITY * (100 - qBound(0, m_quality, 100)) / 100; int maxQuantizer = AVIF_QUANTIZER_WORST_QUALITY * (100 - qBound(0, m_quality, 100)) / 100;
int minQuantizer = 0; int minQuantizer = 0;
int maxQuantizerAlpha = 0; int maxQuantizerAlpha = 0;
#endif
avifResult res; avifResult res;
bool save_grayscale; // true - monochrome, false - colors bool save_grayscale; // true - monochrome, false - colors
@@ -600,15 +563,13 @@ bool QAVIFHandler::write(const QImage &image)
break; break;
} }
#if AVIF_VERSION < 1000000 // quality settings
// deprecated quality settings
if (maxQuantizer > 20) { if (maxQuantizer > 20) {
minQuantizer = maxQuantizer - 20; minQuantizer = maxQuantizer - 20;
if (maxQuantizer > 40) { // we decrease quality of alpha channel here if (maxQuantizer > 40) { // we decrease quality of alpha channel here
maxQuantizerAlpha = maxQuantizer - 40; maxQuantizerAlpha = maxQuantizer - 40;
} }
} }
#endif
if (save_grayscale && !image.hasAlphaChannel()) { // we are going to save grayscale image without alpha channel if (save_grayscale && !image.hasAlphaChannel()) { // we are going to save grayscale image without alpha channel
if (save_depth > 8) { if (save_depth > 8) {
@@ -681,8 +642,8 @@ bool QAVIFHandler::write(const QImage &image)
QImage tmpcolorimage = image.convertToFormat(tmpformat); QImage tmpcolorimage = image.convertToFormat(tmpformat);
avifPixelFormat pixel_format = AVIF_PIXEL_FORMAT_YUV420; avifPixelFormat pixel_format = AVIF_PIXEL_FORMAT_YUV420;
if (m_quality >= KIMG_AVIF_QUALITY_HIGH) { if (maxQuantizer < 20) {
if (m_quality >= KIMG_AVIF_QUALITY_BEST) { if (maxQuantizer < 10) {
pixel_format = AVIF_PIXEL_FORMAT_YUV444; // best quality pixel_format = AVIF_PIXEL_FORMAT_YUV444; // best quality
} else { } else {
pixel_format = AVIF_PIXEL_FORMAT_YUV422; // high quality pixel_format = AVIF_PIXEL_FORMAT_YUV422; // high quality
@@ -842,8 +803,6 @@ bool QAVIFHandler::write(const QImage &image)
avifRWData raw = AVIF_DATA_EMPTY; avifRWData raw = AVIF_DATA_EMPTY;
avifEncoder *encoder = avifEncoderCreate(); avifEncoder *encoder = avifEncoderCreate();
encoder->maxThreads = qBound(1, QThread::idealThreadCount(), 64); encoder->maxThreads = qBound(1, QThread::idealThreadCount(), 64);
#if AVIF_VERSION < 1000000
encoder->minQuantizer = minQuantizer; encoder->minQuantizer = minQuantizer;
encoder->maxQuantizer = maxQuantizer; encoder->maxQuantizer = maxQuantizer;
@@ -851,17 +810,6 @@ bool QAVIFHandler::write(const QImage &image)
encoder->minQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS; encoder->minQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS;
encoder->maxQuantizerAlpha = maxQuantizerAlpha; encoder->maxQuantizerAlpha = maxQuantizerAlpha;
} }
#else
encoder->quality = m_quality;
if (image.hasAlphaChannel()) {
if (m_quality >= KIMG_AVIF_QUALITY_LOW) {
encoder->qualityAlpha = 100;
} else {
encoder->qualityAlpha = 100 - (KIMG_AVIF_QUALITY_LOW - m_quality) / 2;
}
}
#endif
encoder->speed = 6; encoder->speed = 6;
@@ -918,7 +866,7 @@ void QAVIFHandler::setOption(ImageOption option, const QVariant &value)
if (m_quality > 100) { if (m_quality > 100) {
m_quality = 100; m_quality = 100;
} else if (m_quality < 0) { } else if (m_quality < 0) {
m_quality = KIMG_AVIF_DEFAULT_QUALITY; m_quality = 52;
} }
return; return;
default: default:
@@ -1091,11 +1039,6 @@ int QAVIFHandler::loopCount() const
return 0; return 0;
} }
#if AVIF_VERSION >= 1000000
if (m_decoder->repetitionCount >= 0) {
return m_decoder->repetitionCount;
}
#endif
// Endless loop to work around https://github.com/AOMediaCodec/libavif/issues/347 // Endless loop to work around https://github.com/AOMediaCodec/libavif/issues/347
return -1; return -1;
} }

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=avif
X-KDE-MimeType=image/avif
X-KDE-Read=true
X-KDE-Write=true

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=dds
X-KDE-MimeType=image/x-dds
X-KDE-Read=true
X-KDE-Write=true

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=eps,epsi,epsf
X-KDE-MimeType=image/x-eps
X-KDE-Read=true
X-KDE-Write=true

View File

@@ -54,9 +54,7 @@
#include <QImage> #include <QImage>
#include <QImageIOPlugin> #include <QImageIOPlugin>
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
#include <QTimeZone> #include <QTimeZone>
#endif
// Allow the code to works on all QT versions supported by KDE // Allow the code to works on all QT versions supported by KDE
// project (Qt 5.15 and Qt 6.x) to easy backports fixes. // project (Qt 5.15 and Qt 6.x) to easy backports fixes.
@@ -194,11 +192,7 @@ bool EXRHandler::read(QImage *outImage)
} }
auto dateTime = QDateTime::fromString(QString::fromStdString(capDate->value()), QStringLiteral("yyyy:MM:dd HH:mm:ss")); auto dateTime = QDateTime::fromString(QString::fromStdString(capDate->value()), QStringLiteral("yyyy:MM:dd HH:mm:ss"));
if (dateTime.isValid()) { if (dateTime.isValid()) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
dateTime.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(off)); dateTime.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(off));
#else
dateTime.setOffsetFromUtc(off);
#endif
image.setText(QStringLiteral("Date"), dateTime.toString(Qt::ISODate)); image.setText(QStringLiteral("Date"), dateTime.toString(Qt::ISODate));
} }
} }
@@ -255,7 +249,7 @@ bool EXRHandler::read(QImage *outImage)
#endif // !EXR_ALLOW_LINEAR_COLORSPACE #endif // !EXR_ALLOW_LINEAR_COLORSPACE
#endif // !EXR_USE_LEGACY_CONVERSIONS #endif // !EXR_USE_LEGACY_CONVERSIONS
*outImage = image; *outImage = std::move(image);
return true; return true;
} catch (const std::exception &exc) { } catch (const std::exception &exc) {

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=exr
X-KDE-MimeType=image/x-exr
X-KDE-Read=true
X-KDE-Write=false

View File

@@ -11,12 +11,18 @@
#include <QColorSpace> #include <QColorSpace>
#include <QDataStream> #include <QDataStream>
#include <QFloat16>
#include <QImage> #include <QImage>
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QRegularExpressionMatch> #include <QRegularExpressionMatch>
#include <QDebug> #include <QDebug>
/* *** HDR_HALF_QUALITY ***
* If defined, a 16-bits float image is created, otherwise a 32-bits float ones (default).
*/
//#define HDR_HALF_QUALITY // default commented -> you should define it in your cmake file
typedef unsigned char uchar; typedef unsigned char uchar;
Q_LOGGING_CATEGORY(HDRPLUGIN, "kf.imageformats.plugins.hdr", QtWarningMsg) Q_LOGGING_CATEGORY(HDRPLUGIN, "kf.imageformats.plugins.hdr", QtWarningMsg)
@@ -27,12 +33,6 @@ namespace // Private.
#define MINELEN 8 // minimum scanline length for encoding #define MINELEN 8 // minimum scanline length for encoding
#define MAXELEN 0x7fff // maximum scanline length for encoding #define MAXELEN 0x7fff // maximum scanline length for encoding
static inline uchar ClipToByte(float value)
{
// we know value is positive.
return uchar(std::min(value + 0.5f, 255.0f));
}
// read an old style line from the hdr image file // read an old style line from the hdr image file
// if 'first' is true the first byte is already read // if 'first' is true the first byte is already read
static bool Read_Old_Line(uchar *image, int width, QDataStream &s) static bool Read_Old_Line(uchar *image, int width, QDataStream &s)
@@ -75,7 +75,8 @@ static bool Read_Old_Line(uchar *image, int width, QDataStream &s)
return true; return true;
} }
static void RGBE_To_QRgbLine(uchar *image, QRgb *scanline, int width) template<class float_T>
void RGBE_To_QRgbLine(uchar *image, float_T *scanline, int width)
{ {
for (int j = 0; j < width; j++) { for (int j = 0; j < width; j++) {
// v = ldexp(1.0, int(image[3]) - 128); // v = ldexp(1.0, int(image[3]) - 128);
@@ -87,12 +88,25 @@ static void RGBE_To_QRgbLine(uchar *image, QRgb *scanline, int width)
v = 1.0f / float(1 << -e); v = 1.0f / float(1 << -e);
} }
scanline[j] = qRgb(ClipToByte(float(image[0]) * v), ClipToByte(float(image[1]) * v), ClipToByte(float(image[2]) * v)); auto j4 = j * 4;
auto vn = v / 255.0f;
scanline[j4] = float_T(std::min(float(image[0]) * vn, 1.0f));
scanline[j4 + 1] = float_T(std::min(float(image[1]) * vn, 1.0f));
scanline[j4 + 2] = float_T(std::min(float(image[2]) * vn, 1.0f));
scanline[j4 + 3] = float_T(1.0f);
image += 4; image += 4;
} }
} }
QImage::Format imageFormat()
{
#ifdef HDR_HALF_QUALITY
return QImage::Format_RGBX16FPx4;
#else
return QImage::Format_RGBX32FPx4;
#endif
}
// Load the HDR image. // Load the HDR image.
static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &img) static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &img)
{ {
@@ -100,7 +114,7 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
uchar code; uchar code;
// Create dst image. // Create dst image.
img = imageAlloc(width, height, QImage::Format_RGB32); img = imageAlloc(width, height, imageFormat());
if (img.isNull()) { if (img.isNull()) {
qCDebug(HDRPLUGIN) << "Couldn't create image with size" << width << height << "and format RGB32"; qCDebug(HDRPLUGIN) << "Couldn't create image with size" << width << height << "and format RGB32";
return false; return false;
@@ -108,10 +122,14 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
QByteArray lineArray; QByteArray lineArray;
lineArray.resize(4 * width); lineArray.resize(4 * width);
uchar *image = (uchar *)lineArray.data(); uchar *image = reinterpret_cast<uchar *>(lineArray.data());
for (int cline = 0; cline < height; cline++) { for (int cline = 0; cline < height; cline++) {
QRgb *scanline = (QRgb *)img.scanLine(cline); #ifdef HDR_HALF_QUALITY
auto scanline = reinterpret_cast<qfloat16 *>(img.scanLine(cline));
#else
auto scanline = reinterpret_cast<float *>(img.scanLine(cline));
#endif
// determine scanline type // determine scanline type
if ((width < MINELEN) || (MAXELEN < width)) { if ((width < MINELEN) || (MAXELEN < width)) {
@@ -193,9 +211,7 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
return true; return true;
} }
} // namespace static QSize readHeaderSize(QIODevice *device)
bool HDRHandler::read(QImage *outImage)
{ {
int len; int len;
QByteArray line(MAXLINE + 1, Qt::Uninitialized); QByteArray line(MAXLINE + 1, Qt::Uninitialized);
@@ -203,7 +219,7 @@ bool HDRHandler::read(QImage *outImage)
// Parse header // Parse header
do { do {
len = device()->readLine(line.data(), MAXLINE); len = device->readLine(line.data(), MAXLINE);
if (line.startsWith("FORMAT=")) { if (line.startsWith("FORMAT=")) {
format = line.mid(7, len - 7 - 1 /*\n*/); format = line.mid(7, len - 7 - 1 /*\n*/);
@@ -213,10 +229,10 @@ bool HDRHandler::read(QImage *outImage)
if (format != "32-bit_rle_rgbe") { if (format != "32-bit_rle_rgbe") {
qCDebug(HDRPLUGIN) << "Unknown HDR format:" << format; qCDebug(HDRPLUGIN) << "Unknown HDR format:" << format;
return false; return QSize();
} }
len = device()->readLine(line.data(), MAXLINE); len = device->readLine(line.data(), MAXLINE);
line.resize(len); line.resize(len);
/* /*
@@ -236,21 +252,30 @@ bool HDRHandler::read(QImage *outImage)
QRegularExpressionMatch match = resolutionRegExp.match(QString::fromLatin1(line)); QRegularExpressionMatch match = resolutionRegExp.match(QString::fromLatin1(line));
if (!match.hasMatch()) { if (!match.hasMatch()) {
qCDebug(HDRPLUGIN) << "Invalid HDR file, the first line after the header didn't have the expected format:" << line; qCDebug(HDRPLUGIN) << "Invalid HDR file, the first line after the header didn't have the expected format:" << line;
return false; return QSize();
} }
if ((match.captured(1).at(1) != u'Y') || (match.captured(3).at(1) != u'X')) { if ((match.captured(1).at(1) != u'Y') || (match.captured(3).at(1) != u'X')) {
qCDebug(HDRPLUGIN) << "Unsupported image orientation in HDR file."; qCDebug(HDRPLUGIN) << "Unsupported image orientation in HDR file.";
return QSize();
}
return QSize(match.captured(4).toInt(), match.captured(2).toInt());
}
} // namespace
bool HDRHandler::read(QImage *outImage)
{
QDataStream s(device());
QSize size = readHeaderSize(s.device());
if (!size.isValid()) {
return false; return false;
} }
const int width = match.captured(4).toInt();
const int height = match.captured(2).toInt();
QDataStream s(device());
QImage img; QImage img;
if (!LoadHDR(s, width, height, img)) { if (!LoadHDR(s, size.width(), size.height(), img)) {
// qDebug() << "Error loading HDR file."; // qDebug() << "Error loading HDR file.";
return false; return false;
} }
@@ -258,10 +283,44 @@ bool HDRHandler::read(QImage *outImage)
// By setting the linear color space, programs that support profiles display HDR files as in GIMP and Photoshop. // By setting the linear color space, programs that support profiles display HDR files as in GIMP and Photoshop.
img.setColorSpace(QColorSpace(QColorSpace::SRgbLinear)); img.setColorSpace(QColorSpace(QColorSpace::SRgbLinear));
*outImage = img; *outImage = std::move(img);
return true; return true;
} }
bool HDRHandler::supportsOption(ImageOption option) const
{
if (option == QImageIOHandler::Size) {
return true;
}
if (option == QImageIOHandler::ImageFormat) {
return true;
}
return false;
}
QVariant HDRHandler::option(ImageOption option) const
{
QVariant v;
if (option == QImageIOHandler::Size) {
if (auto d = device()) {
// transactions works on both random and sequential devices
d->startTransaction();
auto size = readHeaderSize(d);
d->rollbackTransaction();
if (size.isValid()) {
v = QVariant::fromValue(size);
}
}
}
if (option == QImageIOHandler::ImageFormat) {
v = QVariant::fromValue(imageFormat());
}
return v;
}
HDRHandler::HDRHandler() HDRHandler::HDRHandler()
{ {
} }
@@ -282,7 +341,20 @@ bool HDRHandler::canRead(QIODevice *device)
return false; return false;
} }
return device->peek(11) == "#?RADIANCE\n" || device->peek(7) == "#?RGBE\n"; // the .pic taken from official test cases does not start with this string but can be loaded.
if(device->peek(11) == "#?RADIANCE\n" || device->peek(7) == "#?RGBE\n") {
return true;
}
// allow to load offical test cases: https://radsite.lbl.gov/radiance/framed.html
device->startTransaction();
QSize size = readHeaderSize(device);
device->rollbackTransaction();
if (size.isValid()) {
return true;
}
return false;
} }
QImageIOPlugin::Capabilities HDRPlugin::capabilities(QIODevice *device, const QByteArray &format) const QImageIOPlugin::Capabilities HDRPlugin::capabilities(QIODevice *device, const QByteArray &format) const

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=hdr
X-KDE-MimeType=image/x-hdr
X-KDE-Read=true
X-KDE-Write=false

View File

@@ -18,6 +18,9 @@ public:
bool canRead() const override; bool canRead() const override;
bool read(QImage *outImage) override; bool read(QImage *outImage) override;
bool supportsOption(QImageIOHandler::ImageOption option) const override;
QVariant option(QImageIOHandler::ImageOption option) const override;
static bool canRead(QIODevice *device); static bool canRead(QIODevice *device);
}; };

View File

@@ -22,7 +22,6 @@ size_t HEIFHandler::m_initialized_count = 0;
bool HEIFHandler::m_plugins_queried = false; bool HEIFHandler::m_plugins_queried = false;
bool HEIFHandler::m_heif_decoder_available = false; bool HEIFHandler::m_heif_decoder_available = false;
bool HEIFHandler::m_heif_encoder_available = false; bool HEIFHandler::m_heif_encoder_available = false;
bool HEIFHandler::m_hej2_decoder_available = false;
extern "C" { extern "C" {
static struct heif_error heifhandler_write_callback(struct heif_context * /* ctx */, const void *data, size_t size, void *userdata) static struct heif_error heifhandler_write_callback(struct heif_context * /* ctx */, const void *data, size_t size, void *userdata)
@@ -60,25 +59,12 @@ HEIFHandler::HEIFHandler()
bool HEIFHandler::canRead() const bool HEIFHandler::canRead() const
{ {
if (m_parseState == ParseHeicNotParsed) { if (m_parseState == ParseHeicNotParsed && !canRead(device())) {
QIODevice *dev = device();
if (dev) {
const QByteArray header = dev->peek(28);
if (HEIFHandler::isSupportedBMFFType(header)) {
setFormat("heif");
return true;
}
if (HEIFHandler::isSupportedHEJ2(header)) {
setFormat("hej2");
return true;
}
}
return false; return false;
} }
if (m_parseState != ParseHeicError) { if (m_parseState != ParseHeicError) {
setFormat("heif");
return true; return true;
} }
return false; return false;
@@ -314,6 +300,17 @@ bool HEIFHandler::write_helper(const QImage &image)
return true; return true;
} }
bool HEIFHandler::canRead(QIODevice *device)
{
if (!device) {
qWarning("HEIFHandler::canRead() called with no device");
return false;
}
const QByteArray header = device->peek(28);
return HEIFHandler::isSupportedBMFFType(header);
}
bool HEIFHandler::isSupportedBMFFType(const QByteArray &header) bool HEIFHandler::isSupportedBMFFType(const QByteArray &header)
{ {
if (header.size() < 28) { if (header.size() < 28) {
@@ -353,22 +350,6 @@ bool HEIFHandler::isSupportedBMFFType(const QByteArray &header)
return false; return false;
} }
bool HEIFHandler::isSupportedHEJ2(const QByteArray &header)
{
if (header.size() < 28) {
return false;
}
const char *buffer = header.constData();
if (qstrncmp(buffer + 4, "ftyp", 4) == 0) {
if (qstrncmp(buffer + 8, "j2ki", 4) == 0) {
return true;
}
}
return false;
}
QVariant HEIFHandler::option(ImageOption option) const QVariant HEIFHandler::option(ImageOption option) const
{ {
if (option == Quality) { if (option == Quality) {
@@ -444,7 +425,7 @@ bool HEIFHandler::ensureDecoder()
} }
const QByteArray buffer = device()->readAll(); const QByteArray buffer = device()->readAll();
if (!HEIFHandler::isSupportedBMFFType(buffer) && !HEIFHandler::isSupportedHEJ2(buffer)) { if (!HEIFHandler::isSupportedBMFFType(buffer)) {
m_parseState = ParseHeicError; m_parseState = ParseHeicError;
return false; return false;
} }
@@ -833,9 +814,6 @@ bool HEIFHandler::isHeifDecoderAvailable()
} }
#endif #endif
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
m_hej2_decoder_available = heif_have_decoder_for_format(heif_compression_JPEG2000);
#endif
m_heif_encoder_available = heif_have_encoder_for_format(heif_compression_HEVC); m_heif_encoder_available = heif_have_encoder_for_format(heif_compression_HEVC);
m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC); m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC);
m_plugins_queried = true; m_plugins_queried = true;
@@ -861,9 +839,6 @@ bool HEIFHandler::isHeifEncoderAvailable()
} }
#endif #endif
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
m_hej2_decoder_available = heif_have_decoder_for_format(heif_compression_JPEG2000);
#endif
m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC); m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC);
m_heif_encoder_available = heif_have_encoder_for_format(heif_compression_HEVC); m_heif_encoder_available = heif_have_encoder_for_format(heif_compression_HEVC);
m_plugins_queried = true; m_plugins_queried = true;
@@ -878,34 +853,6 @@ bool HEIFHandler::isHeifEncoderAvailable()
return m_heif_encoder_available; return m_heif_encoder_available;
} }
bool HEIFHandler::isHej2DecoderAvailable()
{
QMutexLocker locker(&getHEIFHandlerMutex());
if (!m_plugins_queried) {
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
if (m_initialized_count == 0) {
heif_init(nullptr);
}
#endif
m_heif_encoder_available = heif_have_encoder_for_format(heif_compression_HEVC);
m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC);
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
m_hej2_decoder_available = heif_have_decoder_for_format(heif_compression_JPEG2000);
#endif
m_plugins_queried = true;
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
if (m_initialized_count == 0) {
heif_deinit();
}
#endif
}
return m_hej2_decoder_available;
}
void HEIFHandler::startHeifLib() void HEIFHandler::startHeifLib()
{ {
#if LIBHEIF_HAVE_VERSION(1, 13, 0) #if LIBHEIF_HAVE_VERSION(1, 13, 0)
@@ -954,15 +901,6 @@ QImageIOPlugin::Capabilities HEIFPlugin::capabilities(QIODevice *device, const Q
} }
return format_cap; return format_cap;
} }
if (format == "hej2") {
Capabilities format_cap;
if (HEIFHandler::isHej2DecoderAvailable()) {
format_cap |= CanRead;
}
return format_cap;
}
if (!format.isEmpty()) { if (!format.isEmpty()) {
return {}; return {};
} }
@@ -971,16 +909,8 @@ QImageIOPlugin::Capabilities HEIFPlugin::capabilities(QIODevice *device, const Q
} }
Capabilities cap; Capabilities cap;
if (device->isReadable()) { if (device->isReadable() && HEIFHandler::canRead(device) && HEIFHandler::isHeifDecoderAvailable()) {
const QByteArray header = device->peek(28); cap |= CanRead;
if (HEIFHandler::isSupportedBMFFType(header) && HEIFHandler::isHeifDecoderAvailable()) {
cap |= CanRead;
}
if (HEIFHandler::isSupportedHEJ2(header) && HEIFHandler::isHej2DecoderAvailable()) {
cap |= CanRead;
}
} }
if (device->isWritable() && HEIFHandler::isHeifEncoderAvailable()) { if (device->isWritable() && HEIFHandler::isHeifEncoderAvailable()) {

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=heif
X-KDE-MimeType=image/heif
X-KDE-Read=true
X-KDE-Write=true

View File

@@ -1,4 +1,4 @@
{ {
"Keys": [ "heif", "heic", "hej2" ], "Keys": [ "heif", "heic" ],
"MimeTypes": [ "image/heif", "image/heif", "image/hej2k" ] "MimeTypes": [ "image/heif", "image/heif" ]
} }

View File

@@ -24,18 +24,17 @@ public:
bool read(QImage *image) override; bool read(QImage *image) override;
bool write(const QImage &image) override; bool write(const QImage &image) override;
static bool canRead(QIODevice *device);
QVariant option(ImageOption option) const override; QVariant option(ImageOption option) const override;
void setOption(ImageOption option, const QVariant &value) override; void setOption(ImageOption option, const QVariant &value) override;
bool supportsOption(ImageOption option) const override; bool supportsOption(ImageOption option) const override;
static bool isHeifDecoderAvailable(); static bool isHeifDecoderAvailable();
static bool isHeifEncoderAvailable(); static bool isHeifEncoderAvailable();
static bool isHej2DecoderAvailable();
static bool isSupportedBMFFType(const QByteArray &header);
static bool isSupportedHEJ2(const QByteArray &header);
private: private:
static bool isSupportedBMFFType(const QByteArray &header);
bool ensureParsed() const; bool ensureParsed() const;
bool ensureDecoder(); bool ensureDecoder();
@@ -58,7 +57,6 @@ private:
static bool m_plugins_queried; static bool m_plugins_queried;
static bool m_heif_decoder_available; static bool m_heif_decoder_available;
static bool m_heif_encoder_available; static bool m_heif_encoder_available;
static bool m_hej2_decoder_available;
static QMutex &getHEIFHandlerMutex(); static QMutex &getHEIFHandlerMutex();
}; };

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=jp2
X-KDE-MimeType=image/jp2
X-KDE-Read=true
X-KDE-Write=true

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=jxl
X-KDE-MimeType=image/jxl
X-KDE-Read=true
X-KDE-Write=true

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=kra
X-KDE-MimeType=application/x-krita
X-KDE-Read=true
X-KDE-Write=false

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=ora
X-KDE-MimeType=image/openraster
X-KDE-Read=true
X-KDE-Write=false

View File

@@ -383,8 +383,7 @@ static bool readImage8(QImage &img, QDataStream &s, const PCXHEADER &header)
while (flag != 12 && s.status() == QDataStream::Ok) { while (flag != 12 && s.status() == QDataStream::Ok) {
s >> flag; s >> flag;
} }
} } else {
else {
device->seek(device->size() - 769); device->seek(device->size() - 769);
s >> flag; s >> flag;
} }
@@ -613,7 +612,7 @@ static bool writeImage24(QImage &img, QDataStream &s, PCXHEADER &header)
QByteArray b_buf(header.width(), 0); QByteArray b_buf(header.width(), 0);
for (int y = 0; y < header.height(); ++y) { for (int y = 0; y < header.height(); ++y) {
auto p = (QRgb*)img.scanLine(y); auto p = (QRgb *)img.scanLine(y);
for (int x = 0; x < header.width(); ++x) { for (int x = 0; x < header.width(); ++x) {
QRgb rgb = *p++; QRgb rgb = *p++;
@@ -684,7 +683,7 @@ bool PCXHandler::read(QImage *outImage)
img.setDotsPerMeterX(qRound(header.HDpi / 25.4 * 1000)); img.setDotsPerMeterX(qRound(header.HDpi / 25.4 * 1000));
img.setDotsPerMeterY(qRound(header.YDpi / 25.4 * 1000)); img.setDotsPerMeterY(qRound(header.YDpi / 25.4 * 1000));
*outImage = img; *outImage = std::move(img);
return true; return true;
} }

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=pcx
X-KDE-MimeType=image/x-pcx
X-KDE-Read=true
X-KDE-Write=true

View File

@@ -255,7 +255,7 @@ bool SoftimagePICHandler::read(QImage *image)
} }
} }
*image = img; *image = std::move(img);
m_state = Ready; m_state = Ready;
return true; return true;

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=pic
X-KDE-MimeType=image/x-pic
X-KDE-Read=true
X-KDE-Write=true

View File

@@ -20,13 +20,13 @@
/* /*
* Limitations of the current code: * Limitations of the current code:
* - 32-bit float image are converted to 16-bit integer image. * - Color spaces other than RGB/Grayscale cannot be read due to lack of QImage
* - Other color spaces cannot directly be read due to lack of QImage support for * support. Where possible, a conversion to RGB is done:
* color spaces other than RGB (and Grayscale). Where possible, a conversion
* to RGB is done:
* - CMYK images are converted using an approximated way that ignores the color * - CMYK images are converted using an approximated way that ignores the color
* information (ICC profile). * information (ICC profile).
* - LAB images are converted to sRGB using literature formulas. * - LAB images are converted to sRGB using literature formulas.
* - MULICHANNEL images more than 3 channels are converted as CMYK images.
* - DUOTONE images are considered as Grayscale images.
* *
* NOTE: The best way to convert between different color spaces is to use a * NOTE: The best way to convert between different color spaces is to use a
* color management engine (e.g. LittleCMS). * color management engine (e.g. LittleCMS).
@@ -733,7 +733,9 @@ static QImage::Format imageFormat(const PSDHeader &header, bool alpha)
auto format = QImage::Format_Invalid; auto format = QImage::Format_Invalid;
switch(header.color_mode) { switch(header.color_mode) {
case CM_RGB: case CM_RGB:
if (header.depth == 16 || header.depth == 32) if (header.depth == 32)
format = header.channel_count < 4 || !alpha ? QImage::Format_RGBX32FPx4 : QImage::Format_RGBA32FPx4_Premultiplied;
else if (header.depth == 16)
format = header.channel_count < 4 || !alpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64_Premultiplied; format = header.channel_count < 4 || !alpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64_Premultiplied;
else else
format = header.channel_count < 4 || !alpha ? QImage::Format_RGB888 : QImage::Format_RGBA8888_Premultiplied; format = header.channel_count < 4 || !alpha ? QImage::Format_RGB888 : QImage::Format_RGBA8888_Premultiplied;
@@ -789,11 +791,13 @@ static qint32 imageChannels(const QImage::Format& format)
return c; return c;
} }
inline quint8 xchg(quint8 v) { inline quint8 xchg(quint8 v)
{
return v; return v;
} }
inline quint16 xchg(quint16 v) { inline quint16 xchg(quint16 v)
{
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
return quint16( (v>>8) | (v<<8) ); return quint16( (v>>8) | (v<<8) );
#else #else
@@ -801,7 +805,8 @@ inline quint16 xchg(quint16 v) {
#endif #endif
} }
inline quint32 xchg(quint32 v) { inline quint32 xchg(quint32 v)
{
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
return quint32( (v>>24) | ((v & 0x00FF0000)>>8) | ((v & 0x0000FF00)<<8) | (v<<24) ); return quint32( (v>>24) | ((v & 0x00FF0000)>>8) | ((v & 0x0000FF00)<<8) | (v<<24) );
#else #else
@@ -858,8 +863,9 @@ enum class PremulConversion {
template<class T> template<class T>
inline void premulConversion(char *stride, qint32 width, qint32 ac, qint32 cn, const PremulConversion &conv) inline void premulConversion(char *stride, qint32 width, qint32 ac, qint32 cn, const PremulConversion &conv)
{ {
auto s = reinterpret_cast<T *>(stride); auto s = reinterpret_cast<T*>(stride);
auto max = qint64(std::numeric_limits<T>::max()); // NOTE: to avoid overflows, max is casted to qint64: that is possible because max is always an integer (even if T is float)
auto max = qint64(std::numeric_limits<T>::is_integer ? std::numeric_limits<T>::max() : 1);
for (qint32 c = 0; c < ac; ++c) { for (qint32 c = 0; c < ac; ++c) {
if (conv == PremulConversion::PS2P) { if (conv == PremulConversion::PS2P) {
@@ -868,14 +874,16 @@ inline void premulConversion(char *stride, qint32 width, qint32 ac, qint32 cn, c
auto alpha = *(s + xcn + ac); auto alpha = *(s + xcn + ac);
*(s + xcn + c) = *(s + xcn + c) + alpha - max; *(s + xcn + c) = *(s + xcn + c) + alpha - max;
} }
} else if (conv == PremulConversion::PS2A || (conv == PremulConversion::PSLab2A && c == 0)) { }
else if (conv == PremulConversion::PS2A || (conv == PremulConversion::PSLab2A && c == 0)) {
for (qint32 x = 0; x < width; ++x) { for (qint32 x = 0; x < width; ++x) {
auto xcn = x * cn; auto xcn = x * cn;
auto alpha = *(s + xcn + ac); auto alpha = *(s + xcn + ac);
if (alpha > 0) if (alpha > 0)
*(s + xcn + c) = ((*(s + xcn + c) + alpha - max) * max + alpha / 2) / alpha; *(s + xcn + c) = ((*(s + xcn + c) + alpha - max) * max + alpha / 2) / alpha;
} }
} else if (conv == PremulConversion::PSLab2A) { }
else if (conv == PremulConversion::PSLab2A) {
for (qint32 x = 0; x < width; ++x) { for (qint32 x = 0; x < width; ++x) {
auto xcn = x * cn; auto xcn = x * cn;
auto alpha = *(s + xcn + ac); auto alpha = *(s + xcn + ac);
@@ -898,8 +906,8 @@ inline void monoInvert(uchar *target, const char* source, qint32 bytes)
template<class T> template<class T>
inline void rawChannelsCopy(uchar *target, qint32 targetChannels, const char *source, qint32 sourceChannels, qint32 width) inline void rawChannelsCopy(uchar *target, qint32 targetChannels, const char *source, qint32 sourceChannels, qint32 width)
{ {
auto s = reinterpret_cast<const T *>(source); auto s = reinterpret_cast<const T*>(source);
auto t = reinterpret_cast<T *>(target); auto t = reinterpret_cast<T*>(target);
for (qint32 c = 0, cs = std::min(targetChannels, sourceChannels); c < cs; ++c) { for (qint32 c = 0, cs = std::min(targetChannels, sourceChannels); c < cs; ++c) {
for (qint32 x = 0; x < width; ++x) { for (qint32 x = 0; x < width; ++x) {
t[x * targetChannels + c] = s[x * sourceChannels + c]; t[x * targetChannels + c] = s[x * sourceChannels + c];
@@ -1131,15 +1139,15 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
// clang-format off // clang-format off
// checks the need of color conversion (that requires random access to the image) // checks the need of color conversion (that requires random access to the image)
auto randomAccess = (header.color_mode == CM_CMYK) || auto randomAccess = (header.color_mode == CM_CMYK) ||
(header.color_mode == CM_LABCOLOR) || (header.color_mode == CM_LABCOLOR) ||
(header.color_mode == CM_MULTICHANNEL) || (header.color_mode == CM_MULTICHANNEL) ||
(header.color_mode != CM_INDEXED && img.hasAlphaChannel()); (header.color_mode != CM_INDEXED && img.hasAlphaChannel());
// clang-format on // clang-format on
if (randomAccess) { if (randomAccess) {
// In order to make a colorspace transformation, we need all channels of a scanline // In order to make a colorspace transformation, we need all channels of a scanline
QByteArray psdScanline; QByteArray psdScanline;
psdScanline.resize(qsizetype(header.width * std::min(header.depth, quint16(16)) * header.channel_count + 7) / 8); psdScanline.resize(qsizetype(header.width * header.depth * header.channel_count + 7) / 8);
for (qint32 y = 0, h = header.height; y < h; ++y) { for (qint32 y = 0, h = header.height; y < h; ++y) {
for (qint32 c = 0; c < header.channel_count; ++c) { for (qint32 c = 0; c < header.channel_count; ++c) {
auto strideNumber = c * qsizetype(h) + y; auto strideNumber = c * qsizetype(h) + y;
@@ -1156,32 +1164,37 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
auto scanLine = reinterpret_cast<unsigned char*>(psdScanline.data()); auto scanLine = reinterpret_cast<unsigned char*>(psdScanline.data());
if (header.depth == 8) { if (header.depth == 8) {
planarToChunchy<quint8>(scanLine, rawStride.data(), header.width, c, header.channel_count); planarToChunchy<quint8>(scanLine, rawStride.data(), header.width, c, header.channel_count);
} else if (header.depth == 16) { }
else if (header.depth == 16) {
planarToChunchy<quint16>(scanLine, rawStride.data(), header.width, c, header.channel_count); planarToChunchy<quint16>(scanLine, rawStride.data(), header.width, c, header.channel_count);
} else if (header.depth == 32) { }
planarToChunchyFloatToUInt16<float>(scanLine, rawStride.data(), header.width, c, header.channel_count); else if (header.depth == 32) {
planarToChunchy<float>(scanLine, rawStride.data(), header.width, c, header.channel_count);
} }
} }
// Convert premultiplied data to unassociated data // Convert premultiplied data to unassociated data
if (img.hasAlphaChannel()) { if (img.hasAlphaChannel()) {
auto scanLine = reinterpret_cast<char*>(psdScanline.data());
if (header.color_mode == CM_CMYK) { if (header.color_mode == CM_CMYK) {
if (header.depth == 8) if (header.depth == 8)
premulConversion<quint8>(psdScanline.data(), header.width, 4, header.channel_count, PremulConversion::PS2A); premulConversion<quint8>(scanLine, header.width, 4, header.channel_count, PremulConversion::PS2A);
else if (header.depth == 16) else if (header.depth == 16)
premulConversion<quint16>(psdScanline.data(), header.width, 4, header.channel_count, PremulConversion::PS2A); premulConversion<quint16>(scanLine, header.width, 4, header.channel_count, PremulConversion::PS2A);
} }
if (header.color_mode == CM_LABCOLOR) { if (header.color_mode == CM_LABCOLOR) {
if (header.depth == 8) if (header.depth == 8)
premulConversion<quint8>(psdScanline.data(), header.width, 3, header.channel_count, PremulConversion::PSLab2A); premulConversion<quint8>(scanLine, header.width, 3, header.channel_count, PremulConversion::PSLab2A);
else if (header.depth == 16) else if (header.depth == 16)
premulConversion<quint16>(psdScanline.data(), header.width, 3, header.channel_count, PremulConversion::PSLab2A); premulConversion<quint16>(scanLine, header.width, 3, header.channel_count, PremulConversion::PSLab2A);
} }
if (header.color_mode == CM_RGB) { if (header.color_mode == CM_RGB) {
if (header.depth == 8) if (header.depth == 8)
premulConversion<quint8>(psdScanline.data(), header.width, 3, header.channel_count, PremulConversion::PS2P); premulConversion<quint8>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P);
else if (header.depth == 16 || header.depth == 32) else if (header.depth == 16)
premulConversion<quint16>(psdScanline.data(), header.width, 3, header.channel_count, PremulConversion::PS2P); premulConversion<quint16>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P);
else if (header.depth == 32)
premulConversion<float>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P);
} }
} }
@@ -1201,11 +1214,14 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
if (header.color_mode == CM_RGB) { if (header.color_mode == CM_RGB) {
if (header.depth == 8) if (header.depth == 8)
rawChannelsCopy<quint8>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width); rawChannelsCopy<quint8>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width);
else if (header.depth == 16 || header.depth == 32) else if (header.depth == 16)
rawChannelsCopy<quint16>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width); rawChannelsCopy<quint16>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width);
else if (header.depth == 32)
rawChannelsCopy<float>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width);
} }
} }
} else { }
else {
// Linear read (no position jumps): optimized code usable only for the colorspaces supported by QImage // Linear read (no position jumps): optimized code usable only for the colorspaces supported by QImage
for (qint32 c = 0; c < channel_num; ++c) { for (qint32 c = 0; c < channel_num; ++c) {
for (qint32 y = 0, h = header.height; y < h; ++y) { for (qint32 y = 0, h = header.height; y < h; ++y) {
@@ -1218,25 +1234,23 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
auto scanLine = img.scanLine(y); auto scanLine = img.scanLine(y);
if (header.depth == 1) { // Bitmap if (header.depth == 1) { // Bitmap
monoInvert(scanLine, rawStride.data(), std::min(rawStride.size(), img.bytesPerLine())); monoInvert(scanLine, rawStride.data(), std::min(rawStride.size(), img.bytesPerLine()));
} else if (header.depth == 8) { // 8-bits images: Indexed, Grayscale, RGB/RGBA }
else if (header.depth == 8) { // 8-bits images: Indexed, Grayscale, RGB/RGBA
planarToChunchy<quint8>(scanLine, rawStride.data(), header.width, c, imgChannels); planarToChunchy<quint8>(scanLine, rawStride.data(), header.width, c, imgChannels);
} else if (header.depth == 16) { // 16-bits integer images: Grayscale, RGB/RGBA }
else if (header.depth == 16) { // 16-bits integer images: Grayscale, RGB/RGBA
planarToChunchy<quint16>(scanLine, rawStride.data(), header.width, c, imgChannels); planarToChunchy<quint16>(scanLine, rawStride.data(), header.width, c, imgChannels);
} else if (header.depth == 32) { // 32-bits float images: Grayscale, RGB/RGBA (coverted to equivalent integer 16-bits) }
else if (header.depth == 32 && header.color_mode == CM_RGB) { // 32-bits float images: RGB/RGBA
planarToChunchy<float>(scanLine, rawStride.data(), header.width, c, imgChannels);
}
else if (header.depth == 32 && header.color_mode == CM_GRAYSCALE) { // 32-bits float images: Grayscale (coverted to equivalent integer 16-bits)
planarToChunchyFloatToUInt16<float>(scanLine, rawStride.data(), header.width, c, imgChannels); planarToChunchyFloatToUInt16<float>(scanLine, rawStride.data(), header.width, c, imgChannels);
} }
} }
} }
} }
// LAB conversion generates a sRGB image
if (header.color_mode == CM_LABCOLOR) {
#ifdef PSD_FAST_LAB_CONVERSION
img.setColorSpace(QColorSpace(QColorSpace::SRgbLinear));
#else
img.setColorSpace(QColorSpace(QColorSpace::SRgb));
#endif
}
// Resolution info // Resolution info
if (!setResolution(img, irs)) { if (!setResolution(img, irs)) {
@@ -1244,7 +1258,15 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
} }
// ICC profile // ICC profile
if (!setColorSpace(img, irs)) { if (header.color_mode == CM_LABCOLOR) {
// LAB conversion generates a sRGB image
#ifdef PSD_FAST_LAB_CONVERSION
img.setColorSpace(QColorSpace(QColorSpace::SRgbLinear));
#else
img.setColorSpace(QColorSpace(QColorSpace::SRgb));
#endif
}
else if (!setColorSpace(img, irs)) {
// qDebug() << "No colorspace info set!"; // qDebug() << "No colorspace info set!";
} }
@@ -1304,7 +1326,7 @@ bool PSDHandler::read(QImage *image)
return false; return false;
} }
*image = img; *image = std::move(img);
return true; return true;
} }

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=psd,psb,pdd,psdt
X-KDE-MimeType=image/vnd.adobe.photoshop
X-KDE-Read=true
X-KDE-Write=false

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=qoi
X-KDE-MimeType=image/qoi
X-KDE-Read=true
X-KDE-Write=true

View File

@@ -3,6 +3,7 @@
SPDX-FileCopyrightText: 2003 Dominik Seichter <domseichter@web.de> SPDX-FileCopyrightText: 2003 Dominik Seichter <domseichter@web.de>
SPDX-FileCopyrightText: 2004 Ignacio Castaño <castano@ludicon.com> SPDX-FileCopyrightText: 2004 Ignacio Castaño <castano@ludicon.com>
SPDX-FileCopyrightText: 2010 Troy Unrau <troy@kde.org> SPDX-FileCopyrightText: 2010 Troy Unrau <troy@kde.org>
SPDX-FileCopyrightText: 2023 Mirco Miranda <mircomir@outlook.com>
SPDX-License-Identifier: LGPL-2.0-or-later SPDX-License-Identifier: LGPL-2.0-or-later
*/ */
@@ -39,14 +40,14 @@ enum RASColorMapType {
}; };
struct RasHeader { struct RasHeader {
quint32 MagicNumber; quint32 MagicNumber = 0;
quint32 Width; quint32 Width = 0;
quint32 Height; quint32 Height = 0;
quint32 Depth; quint32 Depth = 0;
quint32 Length; quint32 Length = 0;
quint32 Type; quint32 Type = 0;
quint32 ColorMapType; quint32 ColorMapType = 0;
quint32 ColorMapLength; quint32 ColorMapLength = 0;
enum { enum {
SIZE = 32, SIZE = 32,
}; // 8 fields of four bytes each }; // 8 fields of four bytes each
@@ -80,152 +81,251 @@ static bool IsSupported(const RasHeader &head)
return false; return false;
} }
// check for an appropriate depth // check for an appropriate depth
// we support 8bit+palette, 24bit and 32bit ONLY! if (head.Depth != 1 && head.Depth != 8 && head.Depth != 24 && head.Depth != 32) {
// TODO: add support for 1bit return false;
if (!((head.Depth == 8 && head.ColorMapType == 1) || head.Depth == 24 || head.Depth == 32)) { }
if (head.Width == 0 || head.Height == 0) {
return false; return false;
} }
// the Type field adds support for RLE(BGR), RGB and other encodings // the Type field adds support for RLE(BGR), RGB and other encodings
// we support Type 1: Normal(BGR) and Type 3: Normal(RGB) ONLY! // we support Type 1: Normal(BGR), Type 2: RLE(BGR) and Type 3: Normal(RGB) ONLY!
// TODO: add support for Type 2: RLE(BGR) & Type 4,5: TIFF/IFF // TODO: add support for Type 4,5: TIFF/IFF
if (!(head.Type == 1 || head.Type == 3)) { if (!(head.Type == RAS_TYPE_STANDARD || head.Type == RAS_TYPE_RGB_FORMAT || head.Type == RAS_TYPE_BYTE_ENCODED)) {
return false;
}
// Old files didn't have Length set - reject them for now
// TODO: add length recalculation to support old files
if (!head.Length) {
return false; return false;
} }
return true; return true;
} }
static QImage::Format imageFormat(const RasHeader &header)
{
if (header.ColorMapType == RAS_COLOR_MAP_TYPE_RGB) {
return QImage::Format_Indexed8;
}
if (header.Depth == 8 && header.ColorMapType == RAS_COLOR_MAP_TYPE_NONE) {
return QImage::Format_Grayscale8;
}
if (header.Depth == 1) {
return QImage::Format_Mono;
}
return QImage::Format_RGB32;
}
class LineDecoder
{
public:
LineDecoder(QIODevice *d, const RasHeader &ras)
: device(d)
, header(ras)
{
}
QByteArray readLine(qint64 size)
{
/* *** uncompressed
*/
if (header.Type != RAS_TYPE_BYTE_ENCODED) {
return device->read(size);
}
/* *** rle compressed
* The Run-length encoding (RLE) scheme optionally used in Sun Raster
* files (Type = 0002h) is used to encode bytes of image data
* separately. RLE encoding may be found in any Sun Raster file
* regardless of the type of image data it contains.
*
* The RLE packets are typically three bytes in size:
* - The first byte is a Flag Value indicating the type of RLE packet.
* - The second byte is the Run Count.
* - The third byte is the Run Value.
*
* A Flag Value of 80h is followed by a Run Count in the range of 01h
* to FFh. The Run Value follows the Run count and is in the range of
* 00h to FFh. The pixel run is the Run Value repeated Run Count times.
* There are two exceptions to this algorithm. First, if the Run Count
* following the Flag Value is 00h, this is an indication that the run
* is a single byte in length and has a value of 80h. And second, if
* the Flag Value is not 80h, then it is assumed that the data is
* unencoded pixel data and is written directly to the output stream.
*
* source: http://www.fileformat.info/format/sunraster/egff.htm
*/
for (qsizetype psz = 0, ptr = 0; uncBuffer.size() < size;) {
rleBuffer.append(device->read(std::min(qint64(32768), size)));
qsizetype sz = rleBuffer.size();
if (psz == sz) {
break; // avoid infinite loop (data corrupted?!)
}
auto data = reinterpret_cast<uchar *>(rleBuffer.data());
for (; ptr < sz;) {
auto flag = data[ptr++];
if (flag == 0x80) {
if (ptr >= sz) {
ptr -= 1;
break;
}
auto cnt = data[ptr++];
if (cnt == 0) {
uncBuffer.append(char(0x80));
continue;
} else if (ptr >= sz) {
ptr -= 2;
break;
}
auto val = data[ptr++];
uncBuffer.append(QByteArray(1 + cnt, char(val)));
} else {
uncBuffer.append(char(flag));
}
}
if (ptr) { // remove consumed data
rleBuffer.remove(0, ptr);
ptr = 0;
}
psz = rleBuffer.size();
}
if (uncBuffer.size() < size) {
return QByteArray(); // something wrong
}
auto line = uncBuffer.mid(0, size);
uncBuffer.remove(0, line.size()); // remove consumed data
return line;
}
private:
QIODevice *device;
RasHeader header;
// RLE decoding buffers
QByteArray rleBuffer;
QByteArray uncBuffer;
};
static bool LoadRAS(QDataStream &s, const RasHeader &ras, QImage &img) static bool LoadRAS(QDataStream &s, const RasHeader &ras, QImage &img)
{ {
s.device()->seek(RasHeader::SIZE); s.device()->seek(RasHeader::SIZE);
if (ras.ColorMapLength > kMaxQVectorSize) { // The width of a scan line is always a multiple of 16 bits, padded when necessary.
qWarning() << "LoadRAS() unsupported image color map length in file header" << ras.ColorMapLength; auto rasLineSize = (qint64(ras.Width) * ras.Depth + 7) / 8;
if (rasLineSize & 1)
++rasLineSize;
if (rasLineSize > kMaxQVectorSize) {
qWarning() << "LoadRAS() unsupported line size" << rasLineSize;
return false; return false;
} }
// Read palette if needed.
QVector<quint8> palette(ras.ColorMapLength);
if (ras.ColorMapType == 1) {
for (quint32 i = 0; i < ras.ColorMapLength; ++i) {
s >> palette[i];
}
}
const int bpp = ras.Depth / 8;
if (ras.Height == 0) {
return false;
}
if (bpp == 0) {
return false;
}
if (ras.Length / ras.Height / bpp < ras.Width) {
qWarning() << "LoadRAS() mistmatch between height and width" << ras.Width << ras.Height << ras.Length << ras.Depth;
return false;
}
if (ras.Length > kMaxQVectorSize) {
qWarning() << "LoadRAS() unsupported image length in file header" << ras.Length;
return false;
}
// each line must be a factor of 16 bits, so they may contain padding
// this will be 1 if padding required, 0 otherwise
const int paddingrequired = (ras.Width * bpp % 2);
// qDebug() << "paddingrequired: " << paddingrequired;
// don't trust ras.Length
QVector<quint8> input(ras.Length);
int i = 0;
while (!s.atEnd() && i < input.size()) {
s >> input[i];
// I guess we need to find out if we're at the end of a line
if (paddingrequired && i != 0 && !(i % (ras.Width * bpp))) {
s >> input[i];
}
i++;
}
// Allocate image // Allocate image
img = imageAlloc(ras.Width, ras.Height, QImage::Format_ARGB32); img = imageAlloc(ras.Width, ras.Height, imageFormat(ras));
if (img.isNull()) { if (img.isNull()) {
return false; return false;
} }
// Reconstruct image from RGB palette if we have a palette // Read palette if needed.
// TODO: make generic so it works with 24bit or 32bit palettes if (ras.ColorMapType == RAS_COLOR_MAP_TYPE_RGB) {
if (ras.ColorMapType == 1 && ras.Depth == 8) { QVector<quint8> palette(ras.ColorMapLength);
quint8 red; for (quint32 i = 0; i < ras.ColorMapLength; ++i) {
quint8 green; s >> palette[i];
quint8 blue; }
for (quint32 y = 0; y < ras.Height; y++) { QList<QRgb> colorTable;
for (quint32 x = 0; x < ras.Width; x++) { for (quint32 i = 0, n = ras.ColorMapLength / 3; i < n; ++i) {
red = palette.value((int)input[y * ras.Width + x]); colorTable << qRgb(palette.at(i), palette.at(i + n), palette.at(i + 2 * n));
green = palette.value((int)input[y * ras.Width + x] + (ras.ColorMapLength / 3)); }
blue = palette.value((int)input[y * ras.Width + x] + 2 * (ras.ColorMapLength / 3)); for (; colorTable.size() < 256;) {
img.setPixel(x, y, qRgb(red, green, blue)); colorTable << qRgb(255, 255, 255);
} }
img.setColorTable(colorTable);
if (s.status() != QDataStream::Ok) {
return false;
} }
} }
if (ras.ColorMapType == 0 && ras.Depth == 24 && (ras.Type == 1 || ras.Type == 2)) { LineDecoder dec(s.device(), ras);
quint8 red; auto bytesPerLine = std::min(img.bytesPerLine(), qsizetype(rasLineSize));
quint8 green; for (quint32 y = 0; y < ras.Height; ++y) {
quint8 blue; auto rasLine = dec.readLine(rasLineSize);
for (quint32 y = 0; y < ras.Height; y++) { if (rasLine.size() != rasLineSize) {
for (quint32 x = 0; x < ras.Width; x++) { qWarning() << "LoadRAS() unable to read line" << y << ": the seems corrupted!";
red = input[y * 3 * ras.Width + x * 3 + 2]; return false;
green = input[y * 3 * ras.Width + x * 3 + 1];
blue = input[y * 3 * ras.Width + x * 3];
img.setPixel(x, y, qRgb(red, green, blue));
}
} }
}
if (ras.ColorMapType == 0 && ras.Depth == 24 && ras.Type == 3) { // Grayscale 1-bit / Grayscale 8-bit (never seen)
quint8 red; if (ras.ColorMapType == RAS_COLOR_MAP_TYPE_NONE && (ras.Depth == 1 || ras.Depth == 8)) {
quint8 green; for (auto &&b : rasLine) {
quint8 blue; b = ~b;
for (quint32 y = 0; y < ras.Height; y++) {
for (quint32 x = 0; x < ras.Width; x++) {
red = input[y * 3 * ras.Width + x * 3];
green = input[y * 3 * ras.Width + x * 3 + 1];
blue = input[y * 3 * ras.Width + x * 3 + 2];
img.setPixel(x, y, qRgb(red, green, blue));
} }
std::memcpy(img.scanLine(y), rasLine.constData(), bytesPerLine);
continue;
} }
}
if (ras.ColorMapType == 0 && ras.Depth == 32 && (ras.Type == 1 || ras.Type == 2)) { // Image with palette
quint8 red; if (ras.ColorMapType == RAS_COLOR_MAP_TYPE_RGB && (ras.Depth == 1 || ras.Depth == 8)) {
quint8 green; std::memcpy(img.scanLine(y), rasLine.constData(), bytesPerLine);
quint8 blue; continue;
for (quint32 y = 0; y < ras.Height; y++) {
for (quint32 x = 0; x < ras.Width; x++) {
red = input[y * 4 * ras.Width + x * 4 + 3];
green = input[y * 4 * ras.Width + x * 4 + 2];
blue = input[y * 4 * ras.Width + x * 4 + 1];
img.setPixel(x, y, qRgb(red, green, blue));
}
} }
}
if (ras.ColorMapType == 0 && ras.Depth == 32 && ras.Type == 3) { // BGR 24-bit
quint8 red; if (ras.ColorMapType == RAS_COLOR_MAP_TYPE_NONE && ras.Depth == 24 && (ras.Type == RAS_TYPE_STANDARD || ras.Type == RAS_TYPE_BYTE_ENCODED)) {
quint8 green; quint8 red;
quint8 blue; quint8 green;
for (quint32 y = 0; y < ras.Height; y++) { quint8 blue;
auto scanLine = reinterpret_cast<QRgb *>(img.scanLine(y));
for (quint32 x = 0; x < ras.Width; x++) { for (quint32 x = 0; x < ras.Width; x++) {
red = input[y * 4 * ras.Width + x * 4 + 1]; red = rasLine.at(x * 3 + 2);
green = input[y * 4 * ras.Width + x * 4 + 2]; green = rasLine.at(x * 3 + 1);
blue = input[y * 4 * ras.Width + x * 4 + 3]; blue = rasLine.at(x * 3);
img.setPixel(x, y, qRgb(red, green, blue)); *(scanLine + x) = qRgb(red, green, blue);
} }
continue;
} }
// RGB 24-bit
if (ras.ColorMapType == RAS_COLOR_MAP_TYPE_NONE && ras.Depth == 24 && ras.Type == RAS_TYPE_RGB_FORMAT) {
quint8 red;
quint8 green;
quint8 blue;
auto scanLine = reinterpret_cast<QRgb *>(img.scanLine(y));
for (quint32 x = 0; x < ras.Width; x++) {
red = rasLine.at(x * 3);
green = rasLine.at(x * 3 + 1);
blue = rasLine.at(x * 3 + 2);
*(scanLine + x) = qRgb(red, green, blue);
}
continue;
}
// BGR 32-bit (not tested: test case missing)
if (ras.ColorMapType == RAS_COLOR_MAP_TYPE_NONE && ras.Depth == 32 && (ras.Type == RAS_TYPE_STANDARD || ras.Type == RAS_TYPE_BYTE_ENCODED)) {
quint8 red;
quint8 green;
quint8 blue;
auto scanLine = reinterpret_cast<QRgb *>(img.scanLine(y));
for (quint32 x = 0; x < ras.Width; x++) {
red = rasLine.at(x * 4 + 3);
green = rasLine.at(x * 4 + 2);
blue = rasLine.at(x * 4 + 1);
*(scanLine + x) = qRgb(red, green, blue);
}
continue;
}
// RGB 32-bit (tested: test case missing due to image too large)
if (ras.ColorMapType == RAS_COLOR_MAP_TYPE_NONE && ras.Depth == 32 && ras.Type == RAS_TYPE_RGB_FORMAT) {
quint8 red;
quint8 green;
quint8 blue;
auto scanLine = reinterpret_cast<QRgb *>(img.scanLine(y));
for (quint32 x = 0; x < ras.Width; x++) {
red = rasLine.at(x * 4 + 1);
green = rasLine.at(x * 4 + 2);
blue = rasLine.at(x * 4 + 3);
*(scanLine + x) = qRgb(red, green, blue);
}
continue;
}
qWarning() << "LoadRAS() unsupported format!"
<< "ColorMapType:" << ras.ColorMapType << "Type:" << ras.Type << "Depth:" << ras.Depth;
return false;
} }
return true; return true;
@@ -283,16 +383,8 @@ bool RASHandler::read(QImage *outImage)
RasHeader ras; RasHeader ras;
s >> ras; s >> ras;
if (ras.ColorMapLength > std::numeric_limits<int>::max()) { if (ras.ColorMapLength > kMaxQVectorSize) {
return false; qWarning() << "LoadRAS() unsupported image color map length in file header" << ras.ColorMapLength;
}
// TODO: add support for old versions of RAS where Length may be zero in header
s.device()->seek(RasHeader::SIZE + ras.Length + ras.ColorMapLength);
// Check image file format. Type 2 is RLE, which causing seeking to be silly.
if (!s.atEnd() && ras.Type != 2) {
// qDebug() << "This RAS file is not valid, or an older version of the format.";
return false; return false;
} }
@@ -310,13 +402,69 @@ bool RASHandler::read(QImage *outImage)
return false; return false;
} }
*outImage = img; *outImage = std::move(img);
return true; return true;
} }
bool RASHandler::supportsOption(ImageOption option) const
{
if (option == QImageIOHandler::Size) {
return true;
}
if (option == QImageIOHandler::ImageFormat) {
return true;
}
return false;
}
QVariant RASHandler::option(ImageOption option) const
{
QVariant v;
if (option == QImageIOHandler::Size) {
if (auto d = device()) {
// transactions works on both random and sequential devices
d->startTransaction();
auto ba = d->read(RasHeader::SIZE);
d->rollbackTransaction();
QDataStream s(ba);
s.setByteOrder(QDataStream::BigEndian);
RasHeader header;
s >> header;
if (s.status() == QDataStream::Ok && IsSupported(header)) {
v = QVariant::fromValue(QSize(header.Width, header.Height));
}
}
}
if (option == QImageIOHandler::ImageFormat) {
if (auto d = device()) {
// transactions works on both random and sequential devices
d->startTransaction();
auto ba = d->read(RasHeader::SIZE);
d->rollbackTransaction();
QDataStream s(ba);
s.setByteOrder(QDataStream::BigEndian);
RasHeader header;
s >> header;
if (s.status() == QDataStream::Ok && IsSupported(header)) {
v = QVariant::fromValue(imageFormat(header));
}
}
}
return v;
}
QImageIOPlugin::Capabilities RASPlugin::capabilities(QIODevice *device, const QByteArray &format) const QImageIOPlugin::Capabilities RASPlugin::capabilities(QIODevice *device, const QByteArray &format) const
{ {
if (format == "ras") { if (format == "im1" || format == "im8" || format == "im24" || format == "im32" || format == "ras" || format == "sun") {
return Capabilities(CanRead); return Capabilities(CanRead);
} }
if (!format.isEmpty()) { if (!format.isEmpty()) {

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=ras
X-KDE-MimeType=image/x-sun-raster
X-KDE-Read=true
X-KDE-Write=false

View File

@@ -1,4 +1,4 @@
{ {
"Keys": [ "ras" ], "Keys": [ "im1", "im8", "im24", "im32", "ras", "sun" ],
"MimeTypes": [ "image/x-sun-raster" ] "MimeTypes": [ "image/x-sun-raster", "image/x-sun-raster", "image/x-sun-raster", "image/x-sun-raster", "image/x-sun-raster", "image/x-sun-raster" ]
} }

View File

@@ -19,6 +19,9 @@ public:
bool canRead() const override; bool canRead() const override;
bool read(QImage *image) override; bool read(QImage *image) override;
bool supportsOption(QImageIOHandler::ImageOption option) const override;
QVariant option(QImageIOHandler::ImageOption option) const override;
static bool canRead(QIODevice *device); static bool canRead(QIODevice *device);
}; };

View File

@@ -14,6 +14,7 @@
#include <QDebug> #include <QDebug>
#include <QImage> #include <QImage>
#include <QSet> #include <QSet>
#include <QTimeZone>
#if defined(Q_OS_WINDOWS) && !defined(NOMINMAX) #if defined(Q_OS_WINDOWS) && !defined(NOMINMAX)
#define NOMINMAX #define NOMINMAX
@@ -222,7 +223,7 @@ QString createTag(char *asciiz, const char *tag)
QString createTimeTag(time_t time, const char *tag) QString createTimeTag(time_t time, const char *tag)
{ {
auto value = QDateTime::fromSecsSinceEpoch(time, Qt::UTC); auto value = QDateTime::fromSecsSinceEpoch(time, QTimeZone::utc());
if (value.isValid() && time > 0) { if (value.isValid() && time > 0) {
return createTag(value.toString(Qt::ISODate), tag); return createTag(value.toString(Qt::ISODate), tag);
} }
@@ -417,7 +418,7 @@ inline void rgbToRgbX(uchar *target, const uchar *source, qint32 targetSize, qin
#define C_NR(a) (((a) & 0x3) << 17) #define C_NR(a) (((a) & 0x3) << 17)
#define C_FC(a) (((a) & 0x1) << 19) #define C_FC(a) (((a) & 0x1) << 19)
#define C_SR(a) (((a) & 0x1) << 20) #define C_SR(a) (((a) & 0x1) << 20)
#define C_PRESET(a) ((a) & 0xF) #define C_FLAGS(a) (((a) & 0x1) << 31) // flags mode
#define T_IQ(a) (((a) >> 4) & 0xF) #define T_IQ(a) (((a) >> 4) & 0xF)
#define T_OC(a) (((a) >> 8) & 0xF) #define T_OC(a) (((a) >> 8) & 0xF)
@@ -429,10 +430,10 @@ inline void rgbToRgbX(uchar *target, const uchar *source, qint32 targetSize, qin
#define T_NR(a) (((a) >> 17) & 0x3) #define T_NR(a) (((a) >> 17) & 0x3)
#define T_FC(a) (((a) >> 19) & 0x1) #define T_FC(a) (((a) >> 19) & 0x1)
#define T_SR(a) (((a) >> 20) & 0x1) #define T_SR(a) (((a) >> 20) & 0x1)
#define T_PRESET(a) ((a) & 0xF) #define T_FLAGS(a) (((a) >> 31) & 0x1)
// clang-format on // clang-format on
#define DEFAULT_QUALITY (C_IQ(3) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0)) #define DEFAULT_QUALITY (C_IQ(3) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0) | C_FLAGS(1))
void setParams(QImageIOHandler *handler, LibRaw *rawProcessor) void setParams(QImageIOHandler *handler, LibRaw *rawProcessor)
{ {
@@ -443,9 +444,7 @@ void setParams(QImageIOHandler *handler, LibRaw *rawProcessor)
auto &&rawparams = rawProcessor->imgdata.rawparams; auto &&rawparams = rawProcessor->imgdata.rawparams;
#endif #endif
// Select one raw image from input file (0 - first, ...) // Select one raw image from input file (0 - first, ...)
if (handler->currentImageNumber() > -1) { rawparams.shot_select = handler->currentImageNumber();
rawparams.shot_select = handler->currentImageNumber();
}
// *** Set processing parameters // *** Set processing parameters
@@ -460,47 +459,45 @@ void setParams(QImageIOHandler *handler, LibRaw *rawProcessor)
if (handler->supportsOption(QImageIOHandler::Quality)) { if (handler->supportsOption(QImageIOHandler::Quality)) {
quality = handler->option(QImageIOHandler::Quality).toInt(); quality = handler->option(QImageIOHandler::Quality).toInt();
} }
if (quality < 0) { if (quality > -1) {
switch (quality / 10) {
case 0:
quality = C_IQ(0) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(0) | C_HS(1);
break;
case 1:
quality = C_IQ(0) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(0) | C_HS(0);
break;
case 2:
quality = C_IQ(3) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(0) | C_HS(0);
break;
case 3:
quality = C_IQ(3) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
break;
case 4:
quality = C_IQ(3) | C_OC(2) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
break;
case 5:
quality = C_IQ(3) | C_OC(4) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
break;
case 6:
quality = C_IQ(11) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(0) | C_HS(0);
break;
case 7:
quality = C_IQ(11) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
break;
case 8:
quality = C_IQ(11) | C_OC(2) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
break;
default:
quality = C_IQ(11) | C_OC(4) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
break;
}
quality |= C_FLAGS(1);
}
if (quality == -1) {
quality = DEFAULT_QUALITY; quality = DEFAULT_QUALITY;
} }
Q_ASSERT(T_FLAGS(quality));
switch (T_PRESET(quality)) {
case 0:
break;
case 1:
quality = C_IQ(0) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(0) | C_HS(1);
break;
case 2:
quality = C_IQ(0) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(0) | C_HS(0);
break;
case 3:
quality = C_IQ(3) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(0) | C_HS(0);
break;
case 4:
quality = C_IQ(3) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
break;
case 5:
quality = C_IQ(3) | C_OC(2) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
break;
case 6:
quality = C_IQ(3) | C_OC(4) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
break;
case 7:
quality = C_IQ(11) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(0) | C_HS(0);
break;
case 8:
quality = C_IQ(11) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
break;
case 9:
quality = C_IQ(11) | C_OC(2) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
break;
case 10:
quality = C_IQ(11) | C_OC(4) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
break;
default:
quality = DEFAULT_QUALITY;
break;
}
auto &&params = rawProcessor->imgdata.params; auto &&params = rawProcessor->imgdata.params;
@@ -725,7 +722,6 @@ RAWHandler::RAWHandler()
: m_imageNumber(0) : m_imageNumber(0)
, m_imageCount(0) , m_imageCount(0)
, m_quality(-1) , m_quality(-1)
, m_startPos(-1)
{ {
} }
@@ -742,15 +738,6 @@ bool RAWHandler::read(QImage *image)
{ {
auto dev = device(); auto dev = device();
// set the image position after the first run.
if (!dev->isSequential()) {
if (m_startPos < 0) {
m_startPos = dev->pos();
} else {
dev->seek(m_startPos);
}
}
// Check image file format. // Check image file format.
if (dev->atEnd()) { if (dev->atEnd()) {
return false; return false;
@@ -761,7 +748,7 @@ bool RAWHandler::read(QImage *image)
return false; return false;
} }
*image = img; *image = std::move(img);
return true; return true;
} }
@@ -832,7 +819,7 @@ bool RAWHandler::jumpToNextImage()
bool RAWHandler::jumpToImage(int imageNumber) bool RAWHandler::jumpToImage(int imageNumber)
{ {
if (imageNumber < 0 || imageNumber >= imageCount()) { if (imageNumber >= imageCount()) {
return false; return false;
} }
m_imageNumber = imageNumber; m_imageNumber = imageNumber;

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=3fr,arw,arq,bay,bmq,crw,cr2,cr3,cap,cine,cs1,dcs,dc2,dcr,dng,drf,dxo,eip,erf,fff,hdr,iiq,k25,kdc,kc2,mdc,mef,mfw,mos,mrw,nef,nrw,obm,orf,ori,pef,ptx,pxn,qtk,r3d,raf,raw,rdc,rwl,rw2,rwz,sr2,srf,srw,sti,x3f
X-KDE-MimeType=image/x-hasselblad-3fr,image/x-sony-arw,image/x-arq,image/x-bay,image/x-bmq,image/x-canon-crw,image/x-canon-cr2,image/x-canon-cr3,image/x-cap,image/x-cine,image/x-cs1,image/x-kodak-dcs,image/x-dc2,image/x-kodak-dcr,image/x-adobe-dng,image/x-drf,image/x-dxo,image/x-epson-eip,image/x-epson-erf,image/x-fff,image/x-hdr,image/x-iiq,image/x-kodak-k25,image/x-kodak-kdc,image/x-kodak-kc2,image/x-minolta-mdc,image/x-mamiya-mef,image/x-mfw,image/x-aptus-mos,image/x-minolta-mrw,image/x-nikon-nef,image/x-nikon-nrw,image/x-obm,image/x-olympus-orf,image/x-ori,image/x-pentax-pef,image/x-ptx,image/x-pxn,image/x-qtk,image/x-r3d,image/x-fuji-raf,image/x-raw,image/x-rdc,image/x-rwl,image/x-panasonic-rw2,image/x-rwz,image/x-sony-sr2,image/x-sony-srf,image/x-samsung-srw,image/x-sti,image/x-sigma-x3f
X-KDE-Read=true
X-KDE-Write=false

View File

@@ -40,30 +40,13 @@ private:
* Change the quality of the conversion. If -1, default quality is used. * Change the quality of the conversion. If -1, default quality is used.
* @note Verify that the quality change support has been compiled with supportsOption() * @note Verify that the quality change support has been compiled with supportsOption()
* *
* When the quality value is negative (but not -1), we assume we want to work with flags according to the following scheme:
* 3 2 1 0 * 3 2 1 0
* 1 0 9 8 7 6 5 4 3 2 1 0 9 87 6 5 4 3 2 1098 7654 3210 * 1 0 9 8 7 6 5 4 3 2 1 0 9 87 6 5 4 3 2 1098 7654 3210
* _ _ _ _ _ _ _ _ _ _ _ S F NN E H B A W CCCC IIII PPPP * 1 _ _ _ _ _ _ _ _ _ _ S F NN E H B A W CCCC IIII ____
* *
* Where: * Where:
* * _: reserved (should be zero)
* _: reserved
* P: preset values: *** if set, other flags are ignored! ***
* - 0: Use other flags (no preset)
* - 1: I = 0, C = 1, B = 0, W = 1, A = 1, H = 1 (Linear, sRGB, 8-bits, Camera White, Auto White, Half-size)
* - 2: I = 0, C = 1, B = 0, W = 1, A = 1, H = 0 (Linear, sRGB, 8-bits, Camera White, Auto White)
* - 3: I = 3, C = 1, B = 0, W = 1, A = 1, H = 0 (AHD, sRGB, 8-bits, Camera White, Auto White)
* - 4: I = 3, C = 1, B = 1, W = 1, A = 1, H = 0 (AHD, sRGB, 16-bits, Camera White, Auto White)
* - 5: I = 3, C = 2, B = 1, W = 1, A = 1, H = 0 (AHD, Adobe, 16-bits, Camera White, Auto White)
* - 6: I = 3, C = 4, B = 1, W = 1, A = 1, H = 0 (AHD, ProPhoto, 16-bits, Camera White, Auto White)
* - 7: I = 11, C = 1, B = 0, W = 1, A = 1, H = 0 (DHT, sRGB, 8-bits, Camera White, Auto White)
* - 8: I = 11, C = 1, B = 1, W = 1, A = 1, H = 0 (DHT, sRGB, 16-bits, Camera White, Auto White)
* - 9: I = 11, C = 2, B = 1, W = 1, A = 1, H = 0 (DHT, Adobe, 16-bits, Camera White, Auto White)
* - 10: I = 11, C = 4, B = 1, W = 1, A = 1, H = 0 (DHT, ProPhoto, 16-bits, Camera White, Auto White)
* - 11: reserved
* - 12: reserved
* - 13: reserved
* - 14: reserved
* - 15: reserved
* I: interpolation quality (0 - linear, 1 - VNG, 2 - PPG, 3 - AHD, 4 - DCB, 11 - DHT, 12 - AAHD) * I: interpolation quality (0 - linear, 1 - VNG, 2 - PPG, 3 - AHD, 4 - DCB, 11 - DHT, 12 - AAHD)
* C: output colorspace (0 - raw, 1 - sRGB, 2 - Adobe, 3 - Wide, 4 - ProPhoto, 5 - XYZ, 6 - ACES, 7 - DCI-P3, 8 - Rec2020) * C: output colorspace (0 - raw, 1 - sRGB, 2 - Adobe, 3 - Wide, 4 - ProPhoto, 5 - XYZ, 6 - ACES, 7 - DCI-P3, 8 - Rec2020)
* W: use camera white balace (0 - off, 1 - on) * W: use camera white balace (0 - off, 1 - on)
@@ -74,16 +57,23 @@ private:
* N: FBDD noise reduction (0 - off, 1 - light, 2 - full) * N: FBDD noise reduction (0 - off, 1 - light, 2 - full)
* F: Interpolate RGGB as four colors (0 - off, 1 - on) * F: Interpolate RGGB as four colors (0 - off, 1 - on)
* S: Don't stretch or rotate raw pixels (0 - rotate and stretch, 1 - don't rotate and stretch) * S: Don't stretch or rotate raw pixels (0 - rotate and stretch, 1 - don't rotate and stretch)
*
* @note It is safe to set both W and A: W is used if camera white balance is found, otherwise A is used. * @note It is safe to set both W and A: W is used if camera white balance is found, otherwise A is used.
*
* When quality is a positive value, a value between 0 and 100 is expected. The values are interpreted as follows:
* - 00-09: I = 0, C = 1, B = 0, W = 1, A = 1, H = 1 (Linear, sRGB, 8-bits, Camera White, Auto White, Half-size)
* - 10-19: I = 0, C = 1, B = 0, W = 1, A = 1, H = 0 (Linear, sRGB, 8-bits, Camera White, Auto White)
* - 20-29: I = 3, C = 1, B = 0, W = 1, A = 1, H = 0 (AHD, sRGB, 8-bits, Camera White, Auto White)
* - 30-39: I = 3, C = 1, B = 1, W = 1, A = 1, H = 0 (AHD, sRGB, 16-bits, Camera White, Auto White) [Default]
* - 40-49: I = 3, C = 2, B = 1, W = 1, A = 1, H = 0 (AHD, Adobe, 16-bits, Camera White, Auto White)
* - 50-59: I = 3, C = 4, B = 1, W = 1, A = 1, H = 0 (AHD, ProPhoto, 16-bits, Camera White, Auto White)
* - 60-69: I = 11, C = 1, B = 0, W = 1, A = 1, H = 0 (DHT, sRGB, 8-bits, Camera White, Auto White)
* - 70-79: I = 11, C = 1, B = 1, W = 1, A = 1, H = 0 (DHT, sRGB, 16-bits, Camera White, Auto White)
* - 80-89: I = 11, C = 2, B = 1, W = 1, A = 1, H = 0 (DHT, Adobe, 16-bits, Camera White, Auto White)
* - >= 90: I = 11, C = 4, B = 1, W = 1, A = 1, H = 0 (DHT, ProPhoto, 16-bits, Camera White, Auto White)
*
* When the quality is -1, default quality is used.
*/ */
qint32 m_quality; qint32 m_quality;
/*!
* \brief m_startPos
* The initial device position to allow multi image load (cache value).
*/
qint64 m_startPos;
}; };
class RAWPlugin : public QImageIOPlugin class RAWPlugin : public QImageIOPlugin

View File

@@ -678,9 +678,9 @@ bool SGIImage::writeImage(const QImage &image)
} }
if (hasAlpha && img.format() != QImage::Format_ARGB32) { if (hasAlpha && img.format() != QImage::Format_ARGB32) {
img = img.convertToFormat(QImage::Format_ARGB32); img.convertTo(QImage::Format_ARGB32);
} else if (!hasAlpha && img.format() != QImage::Format_RGB32) { } else if (!hasAlpha && img.format() != QImage::Format_RGB32) {
img = img.convertToFormat(QImage::Format_RGB32); img.convertTo(QImage::Format_RGB32);
} }
if (img.isNull()) { if (img.isNull()) {
// qDebug() << "can't convert image to depth 32"; // qDebug() << "can't convert image to depth 32";

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=rgb,rgba,bw,sgi
X-KDE-MimeType=image/x-rgb
X-KDE-Read=true
X-KDE-Write=true

View File

@@ -88,6 +88,10 @@ static QDataStream &operator>>(QDataStream &s, TgaHeader &head)
s >> head.height; s >> head.height;
s >> head.pixel_size; s >> head.pixel_size;
s >> head.flags; s >> head.flags;
/*qDebug() << "id_length: " << head.id_length << " - colormap_type: " << head.colormap_type << " - image_type: " << head.image_type;
qDebug() << "colormap_index: " << head.colormap_index << " - colormap_length: " << head.colormap_length << " - colormap_size: " << head.colormap_size;
qDebug() << "x_origin: " << head.x_origin << " - y_origin: " << head.y_origin << " - width:" << head.width << " - height:" << head.height << " - pixelsize:
" << head.pixel_size << " - flags: " << head.flags;*/
return s; return s;
} }
@@ -113,10 +117,6 @@ static bool IsSupported(const TgaHeader &head)
if (head.pixel_size != 8 && head.pixel_size != 16 && head.pixel_size != 24 && head.pixel_size != 32) { if (head.pixel_size != 8 && head.pixel_size != 16 && head.pixel_size != 24 && head.pixel_size != 32) {
return false; return false;
} }
// If the colormap_type field is set to zero, indicating that no color map exists, then colormap_size, colormap_index and colormap_length should be set to zero.
if (head.colormap_type == 0 && (head.colormap_size != 0 || head.colormap_index != 0 || head.colormap_length != 0)) {
return false;
}
return true; return true;
} }
@@ -170,57 +170,10 @@ struct TgaHeaderInfo {
} }
}; };
static QImage::Format imageFormat(const TgaHeader &head)
{
auto format = QImage::Format_Invalid;
if (IsSupported(head)) {
// Bits 0-3 are the numbers of alpha bits (can be zero!)
const int numAlphaBits = head.flags & 0xf;
// However alpha exists only in the 32 bit format.
if ((head.pixel_size == 32) && (head.flags & 0xf)) {
if (numAlphaBits <= 8) {
format = QImage::Format_ARGB32;
}
}
else {
format = QImage::Format_RGB32;
}
}
return format;
}
/*!
* \brief peekHeader
* Reads the header but does not change the position in the device.
*/
static bool peekHeader(QIODevice *device, TgaHeader &header)
{
qint64 oldPos = device->pos();
QByteArray head = device->read(TgaHeader::SIZE);
int readBytes = head.size();
if (device->isSequential()) {
for (int pos = readBytes - 1; pos >= 0; --pos) {
device->ungetChar(head[pos]);
}
} else {
device->seek(oldPos);
}
if (readBytes < TgaHeader::SIZE) {
return false;
}
QDataStream stream(head);
stream.setByteOrder(QDataStream::LittleEndian);
stream >> header;
return true;
}
static bool LoadTGA(QDataStream &s, const TgaHeader &tga, QImage &img) static bool LoadTGA(QDataStream &s, const TgaHeader &tga, QImage &img)
{ {
img = imageAlloc(tga.width, tga.height, imageFormat(tga)); // Create image.
img = imageAlloc(tga.width, tga.height, QImage::Format_RGB32);
if (img.isNull()) { if (img.isNull()) {
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(tga.width, tga.height); qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(tga.width, tga.height);
return false; return false;
@@ -228,7 +181,21 @@ static bool LoadTGA(QDataStream &s, const TgaHeader &tga, QImage &img)
TgaHeaderInfo info(tga); TgaHeaderInfo info(tga);
// Bits 0-3 are the numbers of alpha bits (can be zero!)
const int numAlphaBits = tga.flags & 0xf; const int numAlphaBits = tga.flags & 0xf;
// However alpha exists only in the 32 bit format.
if ((tga.pixel_size == 32) && (tga.flags & 0xf)) {
img = imageAlloc(tga.width, tga.height, QImage::Format_ARGB32);
if (img.isNull()) {
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(tga.width, tga.height);
return false;
}
if (numAlphaBits > 8) {
return false;
}
}
uint pixel_size = (tga.pixel_size / 8); uint pixel_size = (tga.pixel_size / 8);
qint64 size = qint64(tga.width) * qint64(tga.height) * pixel_size; qint64 size = qint64(tga.width) * qint64(tga.height) * pixel_size;
@@ -424,28 +391,26 @@ bool TGAHandler::read(QImage *outImage)
{ {
// qDebug() << "Loading TGA file!"; // qDebug() << "Loading TGA file!";
auto d = device(); QDataStream s(device());
TgaHeader tga;
if (!peekHeader(d, tga) || !IsSupported(tga)) {
// qDebug() << "This TGA file is not valid.";
return false;
}
if (d->isSequential()) {
d->read(TgaHeader::SIZE + tga.id_length);
} else {
d->seek(TgaHeader::SIZE + tga.id_length);
}
QDataStream s(d);
s.setByteOrder(QDataStream::LittleEndian); s.setByteOrder(QDataStream::LittleEndian);
// Read image header.
TgaHeader tga;
s >> tga;
s.device()->seek(TgaHeader::SIZE + tga.id_length);
// Check image file format. // Check image file format.
if (s.atEnd()) { if (s.atEnd()) {
// qDebug() << "This TGA file is not valid."; // qDebug() << "This TGA file is not valid.";
return false; return false;
} }
// Check supported file types.
if (!IsSupported(tga)) {
// qDebug() << "This TGA file is not supported.";
return false;
}
QImage img; QImage img;
bool result = LoadTGA(s, tga, img); bool result = LoadTGA(s, tga, img);
@@ -454,7 +419,7 @@ bool TGAHandler::read(QImage *outImage)
return false; return false;
} }
*outImage = img; *outImage = std::move(img);
return true; return true;
} }
@@ -503,42 +468,6 @@ bool TGAHandler::write(const QImage &image)
return true; return true;
} }
bool TGAHandler::supportsOption(ImageOption option) const
{
if (option == QImageIOHandler::Size) {
return true;
}
if (option == QImageIOHandler::ImageFormat) {
return true;
}
return false;
}
QVariant TGAHandler::option(ImageOption option) const
{
QVariant v;
if (option == QImageIOHandler::Size) {
if (auto d = device()) {
TgaHeader header;
if (peekHeader(d, header) && IsSupported(header)) {
v = QVariant::fromValue(QSize(header.width, header.height));
}
}
}
if (option == QImageIOHandler::ImageFormat) {
if (auto d = device()) {
TgaHeader header;
if (peekHeader(d, header) && IsSupported(header)) {
v = QVariant::fromValue(imageFormat(header));
}
}
}
return v;
}
bool TGAHandler::canRead(QIODevice *device) bool TGAHandler::canRead(QIODevice *device)
{ {
if (!device) { if (!device) {
@@ -562,12 +491,10 @@ bool TGAHandler::canRead(QIODevice *device)
return false; return false;
} }
QDataStream stream(head);
stream.setByteOrder(QDataStream::LittleEndian);
TgaHeader tga; TgaHeader tga;
if (!peekHeader(device, tga)) { stream >> tga;
qWarning("TGAHandler::canRead() error while reading the header");
return false;
}
return IsSupported(tga); return IsSupported(tga);
} }

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=tga
X-KDE-MimeType=image/x-tga
X-KDE-Read=true
X-KDE-Write=true

View File

@@ -19,9 +19,6 @@ public:
bool read(QImage *image) override; bool read(QImage *image) override;
bool write(const QImage &image) override; bool write(const QImage &image) override;
bool supportsOption(QImageIOHandler::ImageOption option) const override;
QVariant option(QImageIOHandler::ImageOption option) const override;
static bool canRead(QIODevice *device); static bool canRead(QIODevice *device);
}; };

View File

@@ -11,10 +11,7 @@
#include <limits> #include <limits>
#include <QImage> #include <QImage>
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#include <QImageIOHandler> #include <QImageIOHandler>
#endif
// QVector uses some extra space for stuff, hence the 32 here suggested by Thiago Macieira // QVector uses some extra space for stuff, hence the 32 here suggested by Thiago Macieira
static constexpr int kMaxQVectorSize = std::numeric_limits<int>::max() - 32; static constexpr int kMaxQVectorSize = std::numeric_limits<int>::max() - 32;
@@ -24,13 +21,9 @@ static constexpr int kMaxQVectorSize = std::numeric_limits<int>::max() - 32;
inline QImage imageAlloc(const QSize &size, const QImage::Format &format) inline QImage imageAlloc(const QSize &size, const QImage::Format &format)
{ {
QImage img; QImage img;
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
img = QImage(size, format);
#else
if (!QImageIOHandler::allocateImage(size, format, &img)) { if (!QImageIOHandler::allocateImage(size, format, &img)) {
img = QImage(); // paranoia img = QImage(); // paranoia
} }
#endif
return img; return img;
} }

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=QImageIOPlugins
X-KDE-ImageFormat=xcf
X-KDE-MimeType=image/x-xcf
X-KDE-Read=true
X-KDE-Write=false

View File

@@ -5,7 +5,7 @@ include(ECMMarkAsTest)
macro(kimageformats_executable_tests) macro(kimageformats_executable_tests)
foreach(_testname ${ARGN}) foreach(_testname ${ARGN})
add_executable(${_testname} ${_testname}.cpp) add_executable(${_testname} ${_testname}.cpp)
target_link_libraries(${_testname} Qt${QT_MAJOR_VERSION}::Gui) target_link_libraries(${_testname} Qt6::Gui)
ecm_mark_as_test(${_testname}) ecm_mark_as_test(${_testname})
endforeach(_testname) endforeach(_testname)
endmacro() endmacro()

View File

@@ -103,7 +103,7 @@ int main(int argc, char **argv)
QTextStream(stderr) << "Unknown QImage data format " << parser.value(qimgformat) << '\n'; QTextStream(stderr) << "Unknown QImage data format " << parser.value(qimgformat) << '\n';
return 4; return 4;
} }
img = img.convertToFormat(qformat); img.convertTo(qformat);
} }
qint64 written = output.write(reinterpret_cast<const char *>(img.bits()), img.sizeInBytes()); qint64 written = output.write(reinterpret_cast<const char *>(img.bits()), img.sizeInBytes());
if (written != img.sizeInBytes()) { if (written != img.sizeInBytes()) {