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)
{
}