diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7dfdccd1..d527c805 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -110,6 +110,8 @@ target_sources(charls "${CMAKE_CURRENT_LIST_DIR}/color_transform.hpp" "${CMAKE_CURRENT_LIST_DIR}/conditional_static_cast.hpp" "${CMAKE_CURRENT_LIST_DIR}/constants.hpp" + "${CMAKE_CURRENT_LIST_DIR}/copy_from_line_buffer.hpp" + "${CMAKE_CURRENT_LIST_DIR}/copy_to_line_buffer.hpp" "${CMAKE_CURRENT_LIST_DIR}/default_traits.hpp" "${CMAKE_CURRENT_LIST_DIR}/golomb_lut.hpp" "${CMAKE_CURRENT_LIST_DIR}/golomb_lut.cpp" @@ -125,8 +127,6 @@ target_sources(charls "${CMAKE_CURRENT_LIST_DIR}/lossless_traits.hpp" "${CMAKE_CURRENT_LIST_DIR}/make_scan_codec.hpp" "${CMAKE_CURRENT_LIST_DIR}/make_scan_codec.cpp" - "${CMAKE_CURRENT_LIST_DIR}/process_decoded_line.hpp" - "${CMAKE_CURRENT_LIST_DIR}/process_encoded_line.hpp" "${CMAKE_CURRENT_LIST_DIR}/quantization_lut.hpp" "${CMAKE_CURRENT_LIST_DIR}/quantization_lut.cpp" "${CMAKE_CURRENT_LIST_DIR}/regular_mode_context.hpp" diff --git a/src/CharLS.vcxproj b/src/CharLS.vcxproj index 059c9abe..da0cb3d7 100644 --- a/src/CharLS.vcxproj +++ b/src/CharLS.vcxproj @@ -243,7 +243,7 @@ - + @@ -256,7 +256,7 @@ - + diff --git a/src/CharLS.vcxproj.filters b/src/CharLS.vcxproj.filters index effad107..2e1daa6f 100644 --- a/src/CharLS.vcxproj.filters +++ b/src/CharLS.vcxproj.filters @@ -102,10 +102,10 @@ Header Files - + Header Files - + Header Files diff --git a/src/color_transform.hpp b/src/color_transform.hpp index ed212694..2f3137d1 100644 --- a/src/color_transform.hpp +++ b/src/color_transform.hpp @@ -13,36 +13,9 @@ inline bool color_transformation_possible(const frame_info& frame) noexcept } // This file defines simple classes that define (lossless) color transforms. -// They are invoked in process_line.h to convert between decoded values and the internal line buffers. +// They are used to convert between decoded values and the internal line buffers. // Color transforms work best for computer generated images, but are outside the official JPEG-LS specifications. -template -struct transform_none_impl -{ - static_assert(std::is_integral_v, "Integral required."); - - using sample_type = SampleType; - - FORCE_INLINE triplet operator()(const int v1, const int v2, const int v3) const noexcept - { - return {v1, v2, v3}; - } - - FORCE_INLINE quad operator()(const int v1, const int v2, const int v3, const int v4) const noexcept - { - return {v1, v2, v3, v4}; - } -}; - - -template -struct transform_none final : transform_none_impl -{ - static_assert(std::is_integral_v, "Integral required."); - - using inverse = transform_none_impl; -}; - template struct transform_hp1 final @@ -60,7 +33,7 @@ struct transform_hp1 final { FORCE_INLINE triplet operator()(const int v1, const int v2, const int v3) const noexcept { - return {static_cast(v1 + v2 - range_ / 2), v2, static_cast(v3 + v2 - range_ / 2)}; + return {static_cast(v1 + v2 - range_ / 2), static_cast(v2), static_cast(v3 + v2 - range_ / 2)}; } }; @@ -78,7 +51,8 @@ struct transform_hp2 final FORCE_INLINE triplet operator()(const int red, const int green, const int blue) const noexcept { - return {static_cast(red - green + range_ / 2), green, static_cast(blue - ((red + green) >> 1) - range_ / 2)}; + return {static_cast(red - green + range_ / 2), static_cast(green), + static_cast(blue - ((red + green) >> 1) - range_ / 2)}; } struct inverse final diff --git a/src/copy_from_line_buffer.hpp b/src/copy_from_line_buffer.hpp new file mode 100644 index 00000000..ba3696dc --- /dev/null +++ b/src/copy_from_line_buffer.hpp @@ -0,0 +1,163 @@ +// Copyright (c) Team CharLS. +// SPDX-License-Identifier: BSD-3-Clause + +#pragma once + +#include "color_transform.hpp" +#include "scan_codec.hpp" + +#include + +// During decoding, CharLS process one line at a time. +// Conversions include color transforms, line interleaved vs sample interleaved, masking out unused bits, +// accounting for line padding etc. + +namespace charls { + +using copy_from_line_buffer_fn = void (*)(const void* source, void* destination, size_t pixel_count) noexcept; + +template +class copy_from_line_buffer final +{ +public: + [[nodiscard]] + static copy_from_line_buffer_fn get_copy_function(const interleave_mode interleave_mode, const int32_t component_count, + const color_transformation color_transformation) noexcept + { + switch (interleave_mode) + { + case interleave_mode::none: + return ©_samples; + + case interleave_mode::line: + if (component_count == 3) + { + switch (color_transformation) + { + case color_transformation::none: + return ©_line_3_components; + + case color_transformation::hp1: + return ©_line_3_components_transform>; + + case color_transformation::hp2: + return ©_line_3_components_transform>; + + case color_transformation::hp3: + return ©_line_3_components_transform>; + } + } + + ASSERT(component_count == 4); + return ©_line_4_components; + + case interleave_mode::sample: + if (component_count == 3) + { + switch (color_transformation) + { + case color_transformation::none: + return ©_pixels_3_components; + + case color_transformation::hp1: + return ©_pixels_3_components_transform>; + + case color_transformation::hp2: + return ©_pixels_3_components_transform>; + + case color_transformation::hp3: + return ©_pixels_3_components_transform>; + } + } + + ASSERT(component_count == 4); + return ©_pixels_4_components; + } + + unreachable(); + } + +private: + using sample_type = SampleType; + + static void copy_samples(const void* source, void* destination, const size_t pixel_count) noexcept + { + memcpy(destination, source, pixel_count * sizeof(sample_type)); + } + + static void copy_line_3_components(const void* source, void* destination, const size_t pixel_count) noexcept + { + auto* s{static_cast(source)}; + auto* d{static_cast*>(destination)}; + const size_t pixel_stride{pixel_count_to_pixel_stride(pixel_count)}; + + for (size_t i{}; i != pixel_count; ++i) + { + d[i] = {s[i], s[i + pixel_stride], s[i + 2 * pixel_stride]}; + } + } + + template + static void copy_line_3_components_transform(const void* source, void* destination, const size_t pixel_count) noexcept + { + copy_line_3_components_transform_impl(source, destination, pixel_count, typename Transform::inverse{}); + } + + template + static void copy_line_3_components_transform_impl(const void* source, void* destination, const size_t pixel_count, + Transform transform) noexcept + { + auto* s{static_cast(source)}; + auto* d{static_cast*>(destination)}; + const size_t pixel_stride{pixel_count_to_pixel_stride(pixel_count)}; + + for (size_t i{}; i != pixel_count; ++i) + { + d[i] = transform(s[i], s[i + pixel_stride], s[i + 2 * pixel_stride]); + } + } + + static void copy_line_4_components(const void* source, void* destination, const size_t pixel_count) noexcept + { + auto* s{static_cast(source)}; + auto* d{static_cast*>(destination)}; + const size_t pixel_stride{pixel_count_to_pixel_stride(pixel_count)}; + + for (size_t i{}; i != pixel_count; ++i) + { + d[i] = {s[i], s[i + pixel_stride], s[i + 2 * pixel_stride], s[i + 3 * pixel_stride]}; + } + } + + static void copy_pixels_3_components(const void* source, void* destination, const size_t pixel_count) noexcept + { + memcpy(destination, source, pixel_count * sizeof(triplet)); + } + + template + static void copy_pixels_3_components_transform(const void* source, void* destination, const size_t pixel_count) noexcept + { + copy_pixels_3_components_transform_impl(source, destination, pixel_count, typename Transform::inverse{}); + } + + template + static void copy_pixels_3_components_transform_impl(const void* source, void* destination, const size_t pixel_count, + Transform transform) noexcept + { + auto* s{static_cast*>(source)}; + auto* d{static_cast*>(destination)}; + + for (size_t i{}; i != pixel_count; ++i) + { + const auto pixel{s[i]}; + d[i] = transform(pixel.v1, pixel.v2, pixel.v3); + } + } + + static void copy_pixels_4_components(const void* source, void* destination, const size_t pixel_count) noexcept + { + memcpy(destination, source, pixel_count * sizeof(quad)); + } +}; + +} // namespace charls diff --git a/src/copy_to_line_buffer.hpp b/src/copy_to_line_buffer.hpp new file mode 100644 index 00000000..ef1d826e --- /dev/null +++ b/src/copy_to_line_buffer.hpp @@ -0,0 +1,220 @@ +// Copyright (c) Team CharLS. +// SPDX-License-Identifier: BSD-3-Clause + +#pragma once + +#include "color_transform.hpp" +#include "scan_codec.hpp" + +#include + + +// During encoding, CharLS processes one line at a time. The different implementations +// convert the uncompressed format to and from the internal format for encoding. +// Conversions include color transforms, line interleaved vs sample interleaved, masking out unused bits, +// accounting for line padding etc. + +namespace charls { + +using copy_to_line_buffer_fn = void (*)(const void* source, void* destination, size_t pixel_count, uint32_t mask) noexcept; + +template +class copy_to_line_buffer final +{ +public: + [[nodiscard]] + static copy_to_line_buffer_fn get_copy_function(const interleave_mode interleave_mode, const int32_t component_count, + const int32_t bits_per_sample, + const color_transformation color_transformation) noexcept + { + switch (interleave_mode) + { + case interleave_mode::none: { + const bool mask_needed{bits_per_sample != sizeof(sample_type) * 8}; + return mask_needed ? ©_samples_masked : ©_samples; + } + + case interleave_mode::line: + if (component_count == 3) + { + switch (color_transformation) + { + case color_transformation::none: + return ©_line_3_components; + + case color_transformation::hp1: + return ©_line_3_components_transform>; + + case color_transformation::hp2: + return ©_line_3_components_transform>; + + case color_transformation::hp3: + return ©_line_3_components_transform>; + } + } + + ASSERT(component_count == 4); + return ©_line_4_components; + + case interleave_mode::sample: + if (component_count == 3) + { + switch (color_transformation) + { + case color_transformation::none: + return ©_pixels_3_components; + + case color_transformation::hp1: + return ©_pixels_3_components_transform>; + + case color_transformation::hp2: + return ©_pixels_3_components_transform>; + + case color_transformation::hp3: + return ©_pixels_3_components_transform>; + } + } + + ASSERT(component_count == 4); + return ©_pixels_4_components; + } + + unreachable(); + } + +private: + using sample_type = SampleType; + + static void copy_samples(const void* source, void* destination, const size_t pixel_count, uint32_t /*mask*/) noexcept + { + memcpy(destination, source, pixel_count * sizeof(sample_type)); + } + + static void copy_samples_masked(const void* source, void* destination, const size_t pixel_count, uint32_t mask) noexcept + { + auto* s{static_cast(source)}; + auto* d{static_cast(destination)}; + const auto m{static_cast(mask)}; + + for (size_t i{}; i != pixel_count; ++i) + { + d[i] = (s[i] & m); + } + } + + static void copy_line_3_components(const void* source, void* destination, const size_t pixel_count, + uint32_t mask) noexcept + { + auto* s{static_cast*>(source)}; + auto* d{static_cast(destination)}; + const size_t pixel_stride{pixel_count_to_pixel_stride(pixel_count)}; + const auto m{static_cast(mask)}; + + for (size_t i{}; i != pixel_count; ++i) + { + const auto pixel{s[i]}; + + d[i] = pixel.v1 & m; + d[i + pixel_stride] = pixel.v2 & m; + d[i + (2 * pixel_stride)] = pixel.v3 & m; + } + } + + template + static void copy_line_3_components_transform(const void* source, void* destination, const size_t pixel_count, + uint32_t /*mask*/) noexcept + { + copy_line_3_components_transform_impl(source, destination, pixel_count, Transform{}); + } + + template + static void copy_line_3_components_transform_impl(const void* source, void* destination, const size_t pixel_count, + Transform transform) noexcept + { + auto* s{static_cast*>(source)}; + auto* d{static_cast(destination)}; + const size_t pixel_stride{pixel_count_to_pixel_stride(pixel_count)}; + + for (size_t i{}; i != pixel_count; ++i) + { + const auto pixel{s[i]}; + const auto transformed{transform(pixel.v1, pixel.v2, pixel.v3)}; + + d[i] = transformed.v1; + d[i + pixel_stride] = transformed.v2; + d[i + (2 * pixel_stride)] = transformed.v3; + } + } + + static void copy_line_4_components(const void* source, void* destination, const size_t pixel_count, + uint32_t mask) noexcept + { + auto* s{static_cast*>(source)}; + auto* d{static_cast(destination)}; + const auto m{static_cast(mask)}; + const size_t pixel_stride{pixel_count_to_pixel_stride(pixel_count)}; + + for (size_t i{}; i != pixel_count; ++i) + { + const auto pixel{s[i]}; + + d[i] = pixel.v1 & m; + d[i + pixel_stride] = pixel.v2 & m; + d[i + (2 * pixel_stride)] = pixel.v3 & m; + d[i + (3 * pixel_stride)] = pixel.v4 & m; + } + } + + static void copy_pixels_3_components(const void* source, void* destination, const size_t pixel_count, + uint32_t mask) noexcept + { + auto* s{static_cast*>(source)}; + auto* d{static_cast*>(destination)}; + const auto m{static_cast(mask)}; + + for (size_t i{}; i != pixel_count; ++i) + { + const auto pixel{s[i]}; + d[i] = {static_cast(pixel.v1 & m), static_cast(pixel.v2 & m), + static_cast(pixel.v3 & m)}; + } + } + + template + static void copy_pixels_3_components_transform(const void* source, void* destination, const size_t pixel_count, + uint32_t /*mask*/) noexcept + { + copy_pixels_3_components_transform_impl(source, destination, pixel_count, Transform{}); + } + + template + static void copy_pixels_3_components_transform_impl(const void* source, void* destination, const size_t pixel_count, + Transform transform) noexcept + { + auto* s{static_cast*>(source)}; + auto* d{static_cast*>(destination)}; + + for (size_t i{}; i != pixel_count; ++i) + { + const auto pixel{s[i]}; + d[i] = transform(pixel.v1, pixel.v2, pixel.v3); + } + } + + static void copy_pixels_4_components(const void* source, void* destination, const size_t pixel_count, + uint32_t mask) noexcept + { + auto* s{static_cast*>(source)}; + auto* d{static_cast*>(destination)}; + const auto m{static_cast(mask)}; + + for (size_t i{}; i != pixel_count; ++i) + { + const auto pixel{s[i]}; + d[i] = {static_cast(pixel.v1 & m), static_cast(pixel.v2 & m), + static_cast(pixel.v3 & m), static_cast(pixel.v4 & m)}; + } + } +}; + +} // namespace charls diff --git a/src/process_decoded_line.hpp b/src/process_decoded_line.hpp deleted file mode 100644 index 8ef43f75..00000000 --- a/src/process_decoded_line.hpp +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -#pragma once - -#include "util.hpp" - -#include - -// During decoding, CharLS process one line at a time. -// Conversions include color transforms, line interleaved vs sample interleaved, masking out unused bits, -// accounting for line padding etc. - -namespace charls { - -struct process_decoded_line -{ - virtual ~process_decoded_line() = default; - - virtual void new_line_decoded(const void* source, size_t pixel_count, size_t source_stride) = 0; - -protected: - process_decoded_line() = default; - process_decoded_line(const process_decoded_line&) = default; - process_decoded_line(process_decoded_line&&) = default; - process_decoded_line& operator=(const process_decoded_line&) = default; - process_decoded_line& operator=(process_decoded_line&&) = default; -}; - - -class process_decoded_single_component final : public process_decoded_line -{ -public: - process_decoded_single_component(std::byte* destination, const size_t destination_stride, - const size_t bytes_per_pixel) noexcept : - destination_{destination}, destination_stride_{destination_stride}, bytes_per_pixel_{bytes_per_pixel} - { - ASSERT(bytes_per_pixel == sizeof(std::byte) || bytes_per_pixel == sizeof(uint16_t)); - } - - void new_line_decoded(const void* source, const size_t pixel_count, size_t /* source_stride */) noexcept override - { - memcpy(destination_, source, pixel_count * bytes_per_pixel_); - destination_ += destination_stride_; - } - -private: - std::byte* destination_; - size_t destination_stride_; - size_t bytes_per_pixel_; -}; - - -template -void transform_line(triplet* destination, const triplet* source, const size_t pixel_count, - const TransformType& transform) noexcept -{ - for (size_t i{}; i < pixel_count; ++i) - { - destination[i] = transform(source[i].v1, source[i].v2, source[i].v3); - } -} - - -template -void transform_line(quad* destination, const quad* source, const size_t pixel_count) noexcept -{ - for (size_t i{}; i < pixel_count; ++i) - { - destination[i] = source[i]; - } -} - - -template -void transform_line_to_quad(const SampleType* source, const size_t pixel_stride_in, quad* destination, - const size_t pixel_stride) noexcept -{ - const auto pixel_count{std::min(pixel_stride, pixel_stride_in)}; - - for (size_t i{}; i < pixel_count; ++i) - { - destination[i] = {source[i], source[i + pixel_stride_in], source[i + 2 * pixel_stride_in], - source[i + 3 * pixel_stride_in]}; - } -} - - -template -void transform_line_to_triplet(const SampleType* source, const size_t pixel_stride_in, triplet* destination, - const size_t pixel_stride, const TransformType& transform) noexcept -{ - const auto pixel_count{std::min(pixel_stride, pixel_stride_in)}; - - for (size_t i{}; i < pixel_count; ++i) - { - destination[i] = transform(source[i], source[i + pixel_stride_in], source[i + 2 * pixel_stride_in]); - } -} - - -template -class process_decoded_transformed final : public process_decoded_line -{ -public: - process_decoded_transformed(std::byte* destination, const size_t destination_stride, const int32_t component_count, - const interleave_mode interleave_mode) noexcept : - destination_{destination}, - destination_stride_{destination_stride}, - component_count_{component_count}, - interleave_mode_{interleave_mode} - { - } - - void new_line_decoded(const void* source, const size_t pixel_count, const size_t source_stride) noexcept override - { - decode_transform(source, destination_, pixel_count, source_stride); - destination_ += destination_stride_; - } - - void decode_transform(const void* source, void* destination, const size_t pixel_count, - const size_t source_stride) noexcept - { - if (component_count_ == 3) - { - if (interleave_mode_ == interleave_mode::sample) - { - transform_line(static_cast*>(destination), - static_cast*>(source), pixel_count, inverse_transform_); - } - else - { - transform_line_to_triplet(static_cast(source), source_stride, - static_cast*>(destination), pixel_count, inverse_transform_); - } - } - else if (component_count_ == 4) - { - if (interleave_mode_ == interleave_mode::sample) - { - transform_line(static_cast*>(destination), static_cast*>(source), - pixel_count); - } - else if (interleave_mode_ == interleave_mode::line) - { - transform_line_to_quad(static_cast(source), source_stride, - static_cast*>(destination), pixel_count); - } - } - } - -private: - using sample_type = typename TransformType::sample_type; - - std::byte* destination_; - size_t destination_stride_; - int32_t component_count_; - interleave_mode interleave_mode_; - typename TransformType::inverse inverse_transform_{}; -}; - -} // namespace charls diff --git a/src/process_encoded_line.hpp b/src/process_encoded_line.hpp deleted file mode 100644 index 6fa81263..00000000 --- a/src/process_encoded_line.hpp +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -#pragma once - -#include "util.hpp" - -#include - - -// During encoding, CharLS process one line at a time. The different implementations -// convert the uncompressed format to and from the internal format for encoding. -// Conversions include color transforms, line interleaved vs sample interleaved, masking out unused bits, -// accounting for line padding etc. - -namespace charls { - -struct process_encoded_line -{ - virtual ~process_encoded_line() = default; - - virtual void new_line_requested(void* destination, size_t pixel_count, size_t destination_stride) = 0; - -protected: - process_encoded_line() = default; - process_encoded_line(const process_encoded_line&) = default; - process_encoded_line(process_encoded_line&&) = default; - process_encoded_line& operator=(const process_encoded_line&) = default; - process_encoded_line& operator=(process_encoded_line&&) = default; -}; - - -class process_encoded_single_component final : public process_encoded_line -{ -public: - process_encoded_single_component(const std::byte* source, const size_t source_stride, - const size_t bytes_per_pixel) noexcept : - source_{source}, source_stride_{source_stride}, bytes_per_pixel_{bytes_per_pixel} - { - ASSERT(bytes_per_pixel == sizeof(std::byte) || bytes_per_pixel == sizeof(uint16_t)); - } - - void new_line_requested(void* destination, const size_t pixel_count, - size_t /* destination_stride */) noexcept(false) override - { - memcpy(destination, source_, pixel_count * bytes_per_pixel_); - source_ += source_stride_; - } - -private: - const std::byte* source_; - size_t source_stride_; - size_t bytes_per_pixel_; -}; - - -class process_encoded_single_component_masked final : public process_encoded_line -{ -public: - process_encoded_single_component_masked(const void* source, const size_t source_stride, const size_t bytes_per_pixel, - const uint32_t bits_per_sample) noexcept : - source_{source}, - source_stride_{source_stride}, - bytes_per_pixel_{bytes_per_pixel}, - mask_{(1U << bits_per_sample) - 1U}, - single_byte_pixel_{bytes_per_pixel_ == sizeof(std::byte)} - { - ASSERT(bytes_per_pixel == sizeof(std::byte) || bytes_per_pixel == sizeof(uint16_t)); - } - - void new_line_requested(void* destination, const size_t pixel_count, - const size_t /* destination_stride */) noexcept(false) override - { - if (single_byte_pixel_) - { - const auto* pixel_source{static_cast(source_)}; - auto* pixel_destination{static_cast(destination)}; - for (size_t i{}; i != pixel_count; ++i) - { - pixel_destination[i] = pixel_source[i] & static_cast(mask_); - } - } - else - { - const auto* pixel_source{static_cast(source_)}; - auto* pixel_destination{static_cast(destination)}; - for (size_t i{}; i != pixel_count; ++i) - { - pixel_destination[i] = static_cast(pixel_source[i] & mask_); - } - } - - source_ = static_cast(source_) + source_stride_; - } - -private: - const void* source_; - size_t source_stride_; - size_t bytes_per_pixel_; - uint32_t mask_; - bool single_byte_pixel_; -}; - - -template -void transform_line(triplet* destination, const triplet* source, const size_t pixel_count, - const TransformType& transform, const uint32_t mask) noexcept -{ - for (size_t i{}; i < pixel_count; ++i) - { - destination[i] = transform(source[i].v1 & mask, source[i].v2 & mask, source[i].v3 & mask); - } -} - - -template -void transform_line(quad* destination, const quad* source, const size_t pixel_count, - const uint32_t mask) noexcept -{ - for (size_t i{}; i < pixel_count; ++i) - { - destination[i] = {static_cast(source[i].v1 & mask), static_cast(source[i].v2 & mask), - static_cast(source[i].v3 & mask), static_cast(source[i].v4 & mask)}; - } -} - - -template -void transform_triplet_to_line(const triplet* source, const size_t pixel_stride_in, SampleType* destination, - const size_t pixel_stride, const TransformType& transform, const uint32_t mask) noexcept -{ - const auto pixel_count{std::min(pixel_stride, pixel_stride_in)}; - - for (size_t i{}; i < pixel_count; ++i) - { - const triplet color{source[i]}; - const triplet color_transformed{transform(color.v1 & mask, color.v2 & mask, color.v3 & mask)}; - - destination[i] = color_transformed.v1; - destination[i + pixel_stride] = color_transformed.v2; - destination[i + 2 * pixel_stride] = color_transformed.v3; - } -} - - -template -void transform_quad_to_line(const quad* source, const size_t pixel_stride_in, SampleType* destination, - const size_t pixel_stride, const uint32_t mask) noexcept -{ - const auto pixel_count{std::min(pixel_stride, pixel_stride_in)}; - - for (size_t i{}; i < pixel_count; ++i) - { - const quad& color{source[i]}; - - destination[i] = static_cast(color.v1 & mask); - destination[i + pixel_stride] = static_cast(color.v2 & mask); - destination[i + 2 * pixel_stride] = static_cast(color.v3 & mask); - destination[i + 3 * pixel_stride] = static_cast(color.v4 & mask); - } -} - - -template -class process_encoded_transformed final : public process_encoded_line -{ -public: - process_encoded_transformed(const std::byte* const source, const size_t stride, const frame_info& frame, - const interleave_mode interleave_mode) noexcept : - source_{source}, - stride_{stride}, - mask_{(1U << frame.bits_per_sample) - 1U}, - component_count_{frame.component_count}, - interleave_mode_{interleave_mode} - { - } - - void new_line_requested(void* destination, const size_t pixel_count, - const size_t destination_stride) noexcept(false) override - { - encode_transform(source_, destination, pixel_count, destination_stride); - source_ += stride_; - } - - void encode_transform(const void* source, void* destination, const size_t pixel_count, - const size_t destination_stride) noexcept - { - if (component_count_ == 3) - { - if (interleave_mode_ == interleave_mode::sample) - { - transform_line(static_cast*>(destination), - static_cast*>(source), pixel_count, transform_, mask_); - } - else - { - transform_triplet_to_line(static_cast*>(source), pixel_count, - static_cast(destination), destination_stride, transform_, mask_); - } - } - else if (component_count_ == 4) - { - if (interleave_mode_ == interleave_mode::sample) - { - transform_line(static_cast*>(destination), static_cast*>(source), - pixel_count, mask_); - } - else if (interleave_mode_ == interleave_mode::line) - { - transform_quad_to_line(static_cast*>(source), pixel_count, - static_cast(destination), destination_stride, mask_); - } - } - } - -private: - using sample_type = typename TransformType::sample_type; - - const std::byte* source_; - size_t stride_; - uint32_t mask_; - int32_t component_count_; - interleave_mode interleave_mode_; - TransformType transform_{}; -}; - -} // namespace charls diff --git a/src/scan_codec.hpp b/src/scan_codec.hpp index 4c76c877..a7bde87a 100644 --- a/src/scan_codec.hpp +++ b/src/scan_codec.hpp @@ -86,6 +86,14 @@ const int8_t* initialize_quantization_lut(const Traits& traits, const int32_t th } +[[nodiscard]] +constexpr size_t pixel_count_to_pixel_stride(const size_t pixel_count) noexcept +{ + // The line buffer is allocated with 2 extra pixels for the edges. + return pixel_count + 2; +} + + /// /// Base class for scan_encoder and scan_decoder /// Contains the variables and methods that are identical for the encoding/decoding process and can be shared. diff --git a/src/scan_decoder.hpp b/src/scan_decoder.hpp index cf246dd5..e9565dc7 100644 --- a/src/scan_decoder.hpp +++ b/src/scan_decoder.hpp @@ -6,13 +6,12 @@ #include "charls/jpegls_error.hpp" #include "jpeg_marker_code.hpp" -#include "process_decoded_line.hpp" +#include "copy_from_line_buffer.hpp" #include "scan_codec.hpp" -#include "util.hpp" #include "span.hpp" +#include "util.hpp" #include -#include namespace charls { @@ -62,9 +61,9 @@ class scan_decoder : protected scan_codec read_cache_ = read_cache_ << bit_count; } - void on_line_end(const void* source, const size_t pixel_count, const size_t pixel_stride) const + void copy_line_buffer_to_destination(const void* source, void* destination, const size_t pixel_count) const noexcept { - process_line_->new_line_decoded(source, pixel_count, pixel_stride); + copy_from_line_buffer_(source, static_cast(destination), pixel_count); } void end_scan() @@ -232,7 +231,7 @@ class scan_decoder : protected scan_codec impl::throw_jpegls_error(jpegls_errc::restart_marker_not_found); } - std::unique_ptr process_line_; + copy_from_line_buffer_fn copy_from_line_buffer_{}; private: using cache_t = size_t; diff --git a/src/scan_decoder_impl.hpp b/src/scan_decoder_impl.hpp index e21707fe..60dee5c2 100644 --- a/src/scan_decoder_impl.hpp +++ b/src/scan_decoder_impl.hpp @@ -24,11 +24,12 @@ class scan_decoder_impl final : public scan_decoder quantization_ = initialize_quantization_lut(traits_, t1_, t2_, t3_, quantization_lut_); reset_parameters(traits_.range); + copy_from_line_buffer_ = copy_from_line_buffer::get_copy_function( + parameters.interleave_mode, frame_info.component_count, parameters.transformation); } size_t decode_scan(const span source, std::byte* destination, const size_t stride) override { - process_line_ = create_process_line(destination, stride); const auto* scan_begin{to_address(source.begin())}; @@ -40,43 +41,13 @@ class scan_decoder_impl final : public scan_decoder parameters_.restart_interval = frame_info().height; } - decode_lines(); + decode_lines(destination, stride); end_scan(); return get_actual_position() - scan_begin; } private: - // Factory function for ProcessLine objects to copy/transform un encoded pixels to/from our scan line buffers. - std::unique_ptr create_process_line(std::byte* destination, const size_t stride) - { - if (parameters().interleave_mode == interleave_mode::none) - { - return std::make_unique(destination, stride, sizeof(pixel_type)); - } - - switch (parameters().transformation) - { - case color_transformation::none: - return std::make_unique>>( - destination, stride, frame_info().component_count, parameters().interleave_mode); - case color_transformation::hp1: - ASSERT(color_transformation_possible(frame_info())); - return std::make_unique>>( - destination, stride, frame_info().component_count, parameters().interleave_mode); - case color_transformation::hp2: - ASSERT(color_transformation_possible(frame_info())); - return std::make_unique>>( - destination, stride, frame_info().component_count, parameters().interleave_mode); - case color_transformation::hp3: - ASSERT(color_transformation_possible(frame_info())); - return std::make_unique>>( - destination, stride, frame_info().component_count, parameters().interleave_mode); - } - - unreachable(); - } - [[nodiscard]] FORCE_INLINE int32_t quantize_gradient(const int32_t di) const noexcept { @@ -87,7 +58,7 @@ class scan_decoder_impl final : public scan_decoder // In ILV_SAMPLE mode, multiple components are handled in do_line // In ILV_LINE mode, a call to do_line is made for every component // In ILV_NONE mode, do_scan is called for each component - void decode_lines() + void decode_lines(std::byte* destination, const size_t stride) { const uint32_t pixel_stride{width_ + 2U}; const size_t component_count{ @@ -135,7 +106,8 @@ class scan_decoder_impl final : public scan_decoder previous_line_ += pixel_stride; } - on_line_end(current_line_ + 1 - (component_count * pixel_stride), width_, pixel_stride); + copy_line_buffer_to_destination(current_line_ + 1 - (component_count * pixel_stride), destination, width_); + destination += stride; } if (line == frame_info().height) diff --git a/src/scan_encoder.hpp b/src/scan_encoder.hpp index a7061e74..28faf2cd 100644 --- a/src/scan_encoder.hpp +++ b/src/scan_encoder.hpp @@ -4,12 +4,10 @@ #pragma once #include "jpeg_marker_code.hpp" -#include "process_encoded_line.hpp" +#include "copy_to_line_buffer.hpp" #include "scan_codec.hpp" #include "span.hpp" -#include - namespace charls { /// @@ -30,11 +28,17 @@ class scan_encoder : protected scan_codec virtual size_t encode_scan(const std::byte* source, size_t stride, span destination) = 0; protected: - using scan_codec::scan_codec; + scan_encoder(const charls::frame_info& frame_info, const jpegls_pc_parameters& pc_parameters, + const coding_parameters& parameters, const copy_to_line_buffer_fn copy_to_line_buffer) noexcept : + scan_codec(frame_info, pc_parameters, parameters), + copy_to_line_buffer_{copy_to_line_buffer}, + mask_{(1U << frame_info.bits_per_sample) - 1} + { + } - void on_line_begin(void* destination, const size_t pixel_count, const size_t pixel_stride) const + void copy_source_to_line_buffer(const std::byte* source, void* destination, const size_t pixel_count) const noexcept { - process_line_->new_line_requested(destination, pixel_count, pixel_stride); + copy_to_line_buffer_(source, destination, pixel_count, mask_); } void initialize(const span destination) noexcept @@ -155,12 +159,13 @@ class scan_encoder : protected scan_codec append_to_bit_stream((1U << bit_count) - 1U, bit_count); } - std::unique_ptr process_line_; + copy_to_line_buffer_fn copy_to_line_buffer_{}; private: unsigned int bit_buffer_{}; int32_t free_bit_count_{sizeof bit_buffer_ * 8}; size_t compressed_length_{}; + uint32_t mask_; // encoding std::byte* position_{}; diff --git a/src/scan_encoder_impl.hpp b/src/scan_encoder_impl.hpp index 5ba9bd98..7c281211 100644 --- a/src/scan_encoder_impl.hpp +++ b/src/scan_encoder_impl.hpp @@ -4,9 +4,7 @@ #pragma once #include "coding_parameters.hpp" -#include "color_transform.hpp" #include "jpegls_algorithm.hpp" -#include "process_encoded_line.hpp" #include "scan_encoder.hpp" namespace charls { @@ -15,12 +13,15 @@ template class scan_encoder_impl final : public scan_encoder { public: - using pixel_type = typename Traits::pixel_type; using sample_type = typename Traits::sample_type; + using pixel_type = typename Traits::pixel_type; scan_encoder_impl(const charls::frame_info& frame_info, const jpegls_pc_parameters& pc_parameters, const coding_parameters& parameters, const Traits& traits) : - scan_encoder{frame_info, pc_parameters, parameters}, traits_{traits} + scan_encoder{frame_info, pc_parameters, parameters, + copy_to_line_buffer::get_copy_function(parameters.interleave_mode, frame_info.component_count, + frame_info.bits_per_sample, parameters.transformation) + }, traits_{traits} { ASSERT(traits_.is_valid()); @@ -30,52 +31,14 @@ class scan_encoder_impl final : public scan_encoder size_t encode_scan(const std::byte* source, const size_t stride, const span destination) override { - process_line_ = create_process_line(source, stride); - initialize(destination); - encode_lines(); + encode_lines(source, stride); end_scan(); return get_length(); } private: - // Factory function for ProcessLine objects to copy/transform un encoded pixels to/from our scan line buffers. - std::unique_ptr create_process_line(const std::byte* source, const size_t stride) - { - if (parameters().interleave_mode == interleave_mode::none) - { - if (frame_info().bits_per_sample == sizeof(sample_type) * 8) - { - return std::make_unique(source, stride, sizeof(pixel_type)); - } - - return std::make_unique(source, stride, sizeof(pixel_type), - frame_info().bits_per_sample); - } - - switch (parameters().transformation) - { - case color_transformation::none: - return std::make_unique>>(source, stride, frame_info(), - parameters().interleave_mode); - case color_transformation::hp1: - ASSERT(color_transformation_possible(frame_info())); - return std::make_unique>>(source, stride, frame_info(), - parameters().interleave_mode); - case color_transformation::hp2: - ASSERT(color_transformation_possible(frame_info())); - return std::make_unique>>(source, stride, frame_info(), - parameters().interleave_mode); - case color_transformation::hp3: - ASSERT(color_transformation_possible(frame_info())); - return std::make_unique>>(source, stride, frame_info(), - parameters().interleave_mode); - } - - unreachable(); - } - [[nodiscard]] FORCE_INLINE int32_t quantize_gradient(const int32_t di) const noexcept { @@ -86,7 +49,7 @@ class scan_encoder_impl final : public scan_encoder // In ILV_SAMPLE mode, multiple components are handled in do_line // In ILV_LINE mode, a call to do_line is made for every component // In ILV_NONE mode, do_scan is called for each component - void encode_lines() + void encode_lines(const std::byte* source, const size_t stride) { const uint32_t pixel_stride{width_ + 2U}; const size_t component_count{ @@ -104,7 +67,8 @@ class scan_encoder_impl final : public scan_encoder std::swap(previous_line_, current_line_); } - on_line_begin(current_line_ + 1, width_, pixel_stride); + copy_source_to_line_buffer(source, current_line_ + 1, width_); + source = source + stride; for (size_t component{}; component < component_count; ++component) { diff --git a/src/util.hpp b/src/util.hpp index 680d3bc6..01c82e9c 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -106,19 +106,12 @@ inline CHARLS_NO_INLINE jpegls_errc to_jpegls_errc() noexcept } -template -struct triplet +template +struct triplet final { - triplet() = default; - - triplet(int32_t x1, int32_t x2, int32_t x3) noexcept : - v1(static_cast(x1)), v2(static_cast(x2)), v3(static_cast(x3)) - { - } - - SampleType v1{}; - SampleType v2{}; - SampleType v3{}; + T v1{}; + T v2{}; + T v3{}; [[nodiscard]] friend constexpr bool @@ -129,23 +122,13 @@ struct triplet }; -template +template struct quad final { - quad() = default; - - quad(const int32_t x1, const int32_t x2, const int32_t x3, const int32_t x4) noexcept : - v1(static_cast(x1)), - v2(static_cast(x2)), - v3(static_cast(x3)), - v4(static_cast(x4)) - { - } - - SampleType v1{}; - SampleType v2{}; - SampleType v3{}; - SampleType v4{}; + T v1{}; + T v2{}; + T v3{}; + T v4{}; [[nodiscard]] friend constexpr bool diff --git a/unittest/compliance_test.cpp b/unittest/compliance_test.cpp index c1904971..d35e074c 100644 --- a/unittest/compliance_test.cpp +++ b/unittest/compliance_test.cpp @@ -74,14 +74,14 @@ TEST_CLASS(compliance_test) decode_encode_file("DataFiles/t8c2e3.jls", "DataFiles/test8.ppm"); } - TEST_METHOD(decode_encode_color_8_bit_interleave_line_lossless_non_default) // NOLINT + TEST_METHOD(decode_encode_color_8_bit_interleave_none_lossless_non_default) // NOLINT { // ISO 14495-1: official test image 9 (T87_test-1-2-3-4-5-6.zip) // NON-DEFAULT parameters T1=T2=T3=9,RESET=31. decode_encode_file("DataFiles/t8nde0.jls", "DataFiles/test8bs2.pgm"); } - TEST_METHOD(decode_encode_color_8_bit_interleave_line_near_lossless_3_non_default) // NOLINT + TEST_METHOD(decode_encode_color_8_bit_interleave_none_near_lossless_3_non_default) // NOLINT { // ISO 14495-1: official test image 10 (T87_test-1-2-3-4-5-6.zip) // NON-DEFAULT parameters T1=T2=T3=9,RESET=31. diff --git a/unittest/scan_encoder_tester.hpp b/unittest/scan_encoder_tester.hpp index 3629b2aa..72e5a2dc 100644 --- a/unittest/scan_encoder_tester.hpp +++ b/unittest/scan_encoder_tester.hpp @@ -11,7 +11,7 @@ class scan_encoder_tester final : scan_encoder { public: explicit scan_encoder_tester(const charls::frame_info& frame_info, const coding_parameters& parameters) noexcept : - scan_encoder(frame_info, {}, parameters) + scan_encoder(frame_info, {}, parameters, nullptr) { }