Skip to content

Commit

Permalink
[Feat] Allow for swapping of image data #85 (#115)
Browse files Browse the repository at this point in the history
* Refactor Layer<T>::getMaskData() ->  Layer<T>::getMask() and mark old method as deprecated

* [Feat] Bump up warning level to /W4 /WX on MSVC (#114)

* bump c-blosc2 to 2.14.0
  • Loading branch information
EmilDohne authored Oct 16, 2024
1 parent 4060038 commit 8219e28
Show file tree
Hide file tree
Showing 60 changed files with 2,138 additions and 544 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build-wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ jobs:
python -u {project}/PhotoshopExamples/CreateSimpleDocument/create_simple_document.py &&
python -u {project}/PhotoshopExamples/ExtractImageData/extract_image_data.py &&
python -u {project}/PhotoshopExamples/ModifyLayerStructure/modify_layer_structure.py
python -u {project}/PhotoshopExamples/ReplaceImageData/replace_image_data.py
- name: Verify clean directory
run: git diff --exit-code
Expand Down
18 changes: 16 additions & 2 deletions .github/workflows/cmake-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ on:
- published

env:
BUILD_TYPE: Release
BUILD_TYPE: Debug

jobs:
test:
Expand All @@ -26,18 +26,26 @@ jobs:
os: ubuntu-24.04
compiler: gcc-13
compilercxx: g++-13
cflags: ""
cxxflags: ""
- name: Ubuntu Clang
os: ubuntu-24.04
compiler: clang
cflags: "-fsanitize=address,leak,undefined"
cxxflags: "-fsanitize=address,leak,undefined"
compilercxx: clang++
- name: Windows MSVC
os: windows-latest
compiler: msvc
compilercxx: msvc
cflags: ""
cxxflags: ""
- name: MacOS ARM GCC
os: macos-latest
compiler: gcc-13
compilercxx: g++-13
cflags: ""
cxxflags: ""

steps:
- uses: actions/checkout@v4
Expand All @@ -56,20 +64,26 @@ jobs:
env:
CC: ${{ matrix.compiler }}
CXX: ${{ matrix.compilercxx }}
CFLAGS: ${{ matrix.cflags }}
CXXFLAGS: ${{ matrix.cxxflags }}
run: |
cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DPSAPI_BUILD_DOCS=OFF -DPSAPI_BUILD_BENCHMARKS=OFF -DPSAPI_BUILD_EXAMPLES=OFF -DPSAPI_BUILD_PYTHON=OFF
- name: Build ${{ matrix.os }}-${{ matrix.compilercxx }}
env:
CC: ${{ matrix.compiler }}
CXX: ${{ matrix.compilercxx }}
CFLAGS: ${{ matrix.cflags }}
CXXFLAGS: ${{ matrix.cxxflags }}
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}

# On windows we need to copy the test files differently as it generates sub-folders
# for the copied test files
- name: Copy Testfiles on Windows
if: runner.os == 'Windows'
working-directory: ${{github.workspace}}/build/PhotoshopTest
run: xcopy ".\\${{env.BUILD_TYPE}}\\documents" ".\\documents" /E /I

- name: Test ${{ matrix.os }}-${{ matrix.compilercxx }}
working-directory: ${{github.workspace}}/build/PhotoshopTest
run: ctest -C ${{env.BUILD_TYPE}} --extra-verbose --stop-on-failure
run: ctest -C ${{env.BUILD_TYPE}} --extra-verbose --stop-on-failure --output-on-failure
59 changes: 59 additions & 0 deletions .github/workflows/cmake-valgrind.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Valgrind

on:
workflow_dispatch:
push:
branches:
- dev
pull_request:
branches:
- master
release:
types:
- published

env:
BUILD_TYPE: Debug

jobs:
valgrind:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- name: Ubuntu GCC
os: ubuntu-24.04
compiler: gcc-13
compilercxx: g++-13
- name: Ubuntu Clang
os: ubuntu-24.04
compiler: clang
compilercxx: clang++

steps:
- uses: actions/checkout@v4
with:
submodules: 'recursive'

- name: Configure CMake ${{ matrix.os }}-${{ matrix.compilercxx }}
env:
CC: ${{ matrix.compiler }}
CXX: ${{ matrix.compilercxx }}
run: |
cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DPSAPI_BUILD_DOCS=OFF -DPSAPI_BUILD_BENCHMARKS=OFF -DPSAPI_BUILD_EXAMPLES=OFF -DPSAPI_BUILD_PYTHON=OFF
- name: Build ${{ matrix.os }}-${{ matrix.compilercxx }}
env:
CC: ${{ matrix.compiler }}
CXX: ${{ matrix.compilercxx }}
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}

- name: Run Valgrind
if: matrix.os == 'ubuntu-24.04'
working-directory: ${{github.workspace}}/build/PhotoshopTest
run: |
sudo apt-get update
sudo apt-get install -y valgrind
valgrind --leak-check=full ./PhotoshopTest
continue-on-error: false
10 changes: 6 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
# Add libdeflate
add_subdirectory (thirdparty/libdeflate)
add_library(libdeflate_include INTERFACE)
target_include_directories(libdeflate_include INTERFACE thirdparty/libdeflate)
target_include_directories(libdeflate_include SYSTEM INTERFACE thirdparty/libdeflate)

# Add mio for memory-mapped IO files
add_subdirectory (thirdparty/mio)
Expand All @@ -55,11 +55,12 @@ add_subdirectory (thirdparty/c-blosc2 EXCLUDE_FROM_ALL)

# Add target for blosc2 headers
add_library(blosc2_include INTERFACE)
target_include_directories(blosc2_include INTERFACE thirdparty/c-blosc2/include)
target_include_directories(blosc2_include SYSTEM INTERFACE thirdparty/c-blosc2/include)


# Add doctest
add_library(doctest INTERFACE)
target_include_directories(doctest INTERFACE thirdparty/doctest/doctest)
target_include_directories(doctest SYSTEM INTERFACE thirdparty/doctest/doctest)

# Add simdutf for UTF conversion operations
set (SIMDUTF_TESTS OFF)
Expand All @@ -69,7 +70,7 @@ add_subdirectory (thirdparty/simdutf)

# Add span from tcb for compatibility with older compilers for python bindings
add_library(tcb_span INTERFACE)
target_include_directories(tcb_span INTERFACE thirdparty/compatibility)
target_include_directories(tcb_span SYSTEM INTERFACE thirdparty/compatibility)

# Projects
# --------------------------------------------------------------------------
Expand All @@ -90,6 +91,7 @@ if(PSAPI_BUILD_EXAMPLES)
add_subdirectory (PhotoshopExamples/ExtractImageData)
add_subdirectory (PhotoshopExamples/ModifyLayerStructure)
add_subdirectory (PhotoshopExamples/ProgressCallbacks)
add_subdirectory (PhotoshopExamples/ReplaceImageData)
endif()
if(PSAPI_BUILD_DOCS)
if(NOT PSAPI_BUILD_PYTHON)
Expand Down
5 changes: 5 additions & 0 deletions PhotoshopAPI/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ target_link_libraries(PhotoshopAPI PUBLIC Blosc2::blosc2_static blosc2_include m
if(MSVC)
target_compile_options(PhotoshopAPI PRIVATE /MP /DNOMINMAX)
target_compile_options(PhotoshopAPI PUBLIC /arch:AVX2 /Zc:__cplusplus /utf-8)
# Bump up warning levels and enable the following extra exceptions:
# w44062 is for if a switch misses some enum members.
# w44464 is for #include containing .. in the path
# w45264 is for using std:move when returning a temporary.
target_compile_options(PhotoshopAPI PRIVATE /W4 /WX /w44062 /w44464 /w45264)
else()
target_compile_options(PhotoshopAPI PUBLIC -O3)
include(CheckCXXCompilerFlag)
Expand Down
16 changes: 8 additions & 8 deletions PhotoshopAPI/src/Core/Compression/Compress_RLE.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ namespace RLE_Impl
}

// Store and return
scanlineSize = compressedData.size();
scanlineSize = static_cast<uint32_t>(compressedData.size());
return compressedData;
}

Expand Down Expand Up @@ -309,7 +309,7 @@ std::vector<uint8_t> CompressRLE(std::span<T> uncompressedData, std::span<uint8_
std::vector<std::span<const uint8_t>> uncompressedDataViews;
std::vector<std::span<uint8_t>> compressedDataViews;
std::vector<uint32_t> verticalIter;
for (int i = 0; i < height; ++i)
for (uint32_t i = 0; i < height; ++i)
{
std::span<const uint8_t> uncompressedView(reinterpret_cast<const uint8_t*>(uncompressedData.data() + width * i), width * sizeof(T));
uncompressedDataViews.push_back(uncompressedView);
Expand All @@ -332,7 +332,7 @@ std::vector<uint8_t> CompressRLE(std::span<T> uncompressedData, std::span<uint8_
std::vector<size_t> scanlineOffsets;
std::vector<uint16_t> scanlineSizes;
size_t totalSize = height * sizeof(uint16_t);
for (int y = 0; y < height; ++y)
for (uint32_t y = 0; y < height; ++y)
{
scanlineOffsets.push_back(totalSize);
scanlineSizes.push_back(static_cast<uint16_t>(compressedDataViews[y].size()));
Expand All @@ -355,7 +355,7 @@ std::vector<uint8_t> CompressRLE(std::span<T> uncompressedData, std::span<uint8_
std::vector<size_t> scanlineOffsets;
std::vector<uint32_t> scanlineSizes;
size_t totalSize = height * sizeof(uint32_t);
for (int y = 0; y < height; ++y)
for (uint32_t y = 0; y < height; ++y)
{
scanlineOffsets.push_back(totalSize);
scanlineSizes.push_back(static_cast<uint32_t>(compressedDataViews[y].size()));
Expand Down Expand Up @@ -440,13 +440,13 @@ std::vector<uint8_t> CompressRLE(std::vector<T>& uncompressedData, const FileHea
// ---------------------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------
template<typename T>
std::vector<uint8_t> CompressRLEImageDataPsd(std::vector<T>& uncompressedData, const FileHeader& header, const uint32_t width, const uint32_t height, std::vector<uint16_t>& scanlineSizes)
std::vector<uint8_t> CompressRLEImageDataPsd(std::vector<T>& uncompressedData, const uint32_t width, const uint32_t height, std::vector<uint16_t>& scanlineSizes)
{
PROFILE_FUNCTION();
endianEncodeBEArray(std::span<T>(uncompressedData));

std::vector<std::span<uint8_t>> uncompressedDataViews;
for (int i = 0; i < height; ++i)
for (uint32_t i = 0; i < height; ++i)
{
// Generate a span for each scanline
std::span<uint8_t> data(reinterpret_cast<uint8_t*>(uncompressedData.data() + width * i), width * sizeof(T));
Expand Down Expand Up @@ -480,13 +480,13 @@ std::vector<uint8_t> CompressRLEImageDataPsd(std::vector<T>& uncompressedData, c
// ---------------------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------
template<typename T>
std::vector<uint8_t> CompressRLEImageDataPsb(std::vector<T>& uncompressedData, const FileHeader& header, const uint32_t width, const uint32_t height, std::vector<uint32_t>& scanlineSizes)
std::vector<uint8_t> CompressRLEImageDataPsb(std::vector<T>& uncompressedData, const uint32_t width, const uint32_t height, std::vector<uint32_t>& scanlineSizes)
{
PROFILE_FUNCTION();
endianEncodeBEArray(std::span<T>(uncompressedData));

std::vector<std::span<uint8_t>> uncompressedDataViews;
for (int i = 0; i < height; ++i)
for (uint32_t i = 0; i < height; ++i)
{
// Generate a span for each scanline
std::span<uint8_t> data(reinterpret_cast<uint8_t*>(uncompressedData.data() + width * i), width * sizeof(T));
Expand Down
14 changes: 10 additions & 4 deletions PhotoshopAPI/src/Core/Compression/Compress_ZIP.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ namespace ZIP_Impl
if (data.size() > buffer.size() * sizeof(T))
PSAPI_LOG_ERROR("PredictionEncode", "Buffer size does not match data size, expected at least %zu bytes but got %zu instead", data.size() * sizeof(T), buffer.size());
PROFILE_FUNCTION();
for (int y = 0; y < height; ++y)
for (uint32_t y = 0; y < height; ++y)
{
// Initialize the prediction encoding for the current scanline
T prev = data[static_cast<uint64_t>(width) * y];
Expand Down Expand Up @@ -125,12 +125,18 @@ namespace ZIP_Impl
// These represent the header bytes of the zlib stream
const uint8_t compressionType = 0x78;
uint8_t compressionByte;
if (ZIP_COMPRESSION_LVL < 2)
if constexpr (ZIP_COMPRESSION_LVL < 2)
{
compressionByte = 0x01;
else if (ZIP_COMPRESSION_LVL < 6)
}
else if constexpr(ZIP_COMPRESSION_LVL < 6)
{
compressionByte = 0x5E;
else if (ZIP_COMPRESSION_LVL < 8)
}
else if constexpr (ZIP_COMPRESSION_LVL < 8)
{
compressionByte = 0x9C;
}
else
compressionByte = 0xDA;
// Manually write the zlib header
Expand Down
4 changes: 4 additions & 0 deletions PhotoshopAPI/src/Core/Compression/Compression.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ inline std::vector<uint8_t> CompressData(std::span<T> uncompressedIn, std::span<
{
if (compression == Enum::Compression::Raw)
{
if (uncompressedIn.size() == 0)
{
return {};
}
endianEncodeBEArray(uncompressedIn);
std::vector<uint8_t> data(uncompressedIn.size() * sizeof(T));
std::memcpy(reinterpret_cast<void*>(data.data()), reinterpret_cast<void*>(uncompressedIn.data()), data.size());
Expand Down
9 changes: 5 additions & 4 deletions PhotoshopAPI/src/Core/Compression/Decompress_RLE.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "Core/Struct/ByteStream.h"
#include "PhotoshopFile/FileHeader.h"
#include "Profiling/Perf/Instrumentor.h"
#include "FileUtil.h"

#include <vector>
#include <limits>
Expand Down Expand Up @@ -154,7 +155,7 @@ void DecompressRLE(ByteStream& stream, std::span<T> buffer, uint64_t offset, con
if (header.m_Version == Enum::Version::Psd)
{
std::vector<uint16_t> buff(height);
stream.read(reinterpret_cast<char*>(buff.data()), offset, height * sizeof(uint16_t));
stream.read(Util::toWritableBytes(buff), offset);
endianDecodeBEArray<uint16_t>(buff);
for (auto item : buff)
{
Expand All @@ -165,7 +166,7 @@ void DecompressRLE(ByteStream& stream, std::span<T> buffer, uint64_t offset, con
else
{
std::vector<uint32_t> buff(height);
stream.read(reinterpret_cast<char*>(buff.data()), offset, height * sizeof(uint32_t));
stream.read(Util::toWritableBytes(buff), offset);
endianDecodeBEArray<uint32_t>(buff);
for (auto item : buff)
{
Expand All @@ -190,10 +191,10 @@ void DecompressRLE(ByteStream& stream, std::span<T> buffer, uint64_t offset, con
// Generate spans for every individual scanline to decompress them individually
std::vector<std::span<const uint8_t>> compressedDataSpans(height);
std::vector<std::span<uint8_t>> decompressedDataSpans(height);
std::vector<unsigned int> verticalIter;
std::vector<uint64_t> verticalIter;
{
uint64_t compressedStartIdx = 0;
for (uint64_t i = 0; i < height; ++i)
for (uint64_t i = 0; i < static_cast<uint64_t>(height); ++i)
{
uint64_t compressedEndIdx = compressedStartIdx + scanlineSizes[i];
compressedDataSpans[i] = std::span<const uint8_t>(compressedData.begin() + compressedStartIdx, compressedData.begin() + compressedEndIdx);
Expand Down
2 changes: 1 addition & 1 deletion PhotoshopAPI/src/Core/Compression/Decompress_RLE_AVX2.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ namespace RLE_Impl
const uint8_t repeatValue = compressedData[i + 1];
__m256i ymmValue = _mm256_set1_epi8(repeatValue);

uint8_t remaining = 257 - value;
uint8_t remaining = static_cast<uint8_t>(257u - value);

// Process in chunks of 32 bytes
for (; remaining >= 32; remaining -= 32)
Expand Down
4 changes: 2 additions & 2 deletions PhotoshopAPI/src/Core/Endian/AVX2EndianByteSwap.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ void byteShuffleAVX2_BE(uint8_t* data)
// ---------------------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------
template <typename T>
inline void byteShuffleAVX2_LE(uint8_t* data)
inline void byteShuffleAVX2_LE([[maybe_unused]] uint8_t* data)
{
}

Expand All @@ -75,7 +75,7 @@ inline void byteShuffleAVX2_LE(uint8_t* data)
// ---------------------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------
template<>
inline void byteShuffleAVX2_LE<uint8_t>(uint8_t* data)
inline void byteShuffleAVX2_LE<uint8_t>([[maybe_unused]] uint8_t* data)
{
}

Expand Down
6 changes: 3 additions & 3 deletions PhotoshopAPI/src/Core/Endian/EndianByteSwapArr.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ inline std::vector<uint8_t> endianDecodeBEBinaryArray(std::vector<uint8_t>& data
// ---------------------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------
template <>
inline void endianDecodeBEArray<uint8_t>(std::vector<uint8_t>& data)
inline void endianDecodeBEArray<uint8_t>([[maybe_unused]] std::vector<uint8_t>& data)
{
}

Expand Down Expand Up @@ -294,7 +294,7 @@ void endianDecodeBEArray(std::span<T> data)
// ---------------------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------
template <>
inline void endianDecodeBEArray<uint8_t>(std::span<uint8_t> data)
inline void endianDecodeBEArray<uint8_t>([[maybe_unused]] std::span<uint8_t> data)
{
}

Expand Down Expand Up @@ -375,7 +375,7 @@ inline void endianDecodeBEArray<uint8_t>(std::span<uint8_t> data)
// ---------------------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------
template <>
inline void endianEncodeBEArray(std::span<uint8_t> data)
inline void endianEncodeBEArray([[maybe_unused]] std::span<uint8_t> data)
{
}

Expand Down
Loading

0 comments on commit 8219e28

Please sign in to comment.