+template <class T>
+void LogicSegment::downsampleTmain(const T*&in, T &acc, T &prev)
+{
+ // Accumulate one sample at a time
+ for (uint64_t i = 0; i < MipMapScaleFactor; i++) {
+ T sample = *in++;
+ acc |= prev ^ sample;
+ prev = sample;
+ }
+}
+
+template <>
+void LogicSegment::downsampleTmain<uint8_t>(const uint8_t*&in, uint8_t &acc, uint8_t &prev)
+{
+ // Handle 8 bit samples in 32 bit steps
+ uint32_t prev32 = prev | prev << 8 | prev << 16 | prev << 24;
+ uint32_t acc32 = acc;
+ const uint32_t *in32 = (const uint32_t*)in;
+ for (uint64_t i = 0; i < MipMapScaleFactor; i += 4) {
+ uint32_t sample32 = *in32++;
+ acc32 |= prev32 ^ sample32;
+ prev32 = sample32;
+ }
+ // Reduce result back to uint8_t
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ prev = (prev32 >> 24) & 0xff; // MSB is last
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ prev = prev32 & 0xff; // LSB is last
+#else
+#error Endianness unknown
+#endif
+ acc |= acc32 & 0xff;
+ acc |= (acc32 >> 8) & 0xff;
+ acc |= (acc32 >> 16) & 0xff;
+ acc |= (acc32 >> 24) & 0xff;
+ in = (const uint8_t*)in32;
+}
+
+template <>
+void LogicSegment::downsampleTmain<uint16_t>(const uint16_t*&in, uint16_t &acc, uint16_t &prev)
+{
+ // Handle 16 bit samples in 32 bit steps
+ uint32_t prev32 = prev | prev << 16;
+ uint32_t acc32 = acc;
+ const uint32_t *in32 = (const uint32_t*)in;
+ for (uint64_t i = 0; i < MipMapScaleFactor; i += 2) {
+ uint32_t sample32 = *in32++;
+ acc32 |= prev32 ^ sample32;
+ prev32 = sample32;
+ }
+ // Reduce result back to uint16_t
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ prev = (prev32 >> 16) & 0xffff; // MSB is last
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ prev = prev32 & 0xffff; // LSB is last
+#else
+#error Endian unknown
+#endif
+ acc |= acc32 & 0xffff;
+ acc |= (acc32 >> 16) & 0xffff;
+ in = (const uint16_t*)in32;
+}
+
+template <class T>
+void LogicSegment::downsampleT(const uint8_t *in_, uint8_t *&out_, uint64_t len)
+{
+ const T *in = (const T*)in_;
+ T *out = (T*)out_;
+ T prev = last_append_sample_;
+ T acc = last_append_accumulator_;
+
+ // Try to complete the previous downsample
+ if (last_append_extra_) {
+ while (last_append_extra_ < MipMapScaleFactor && len > 0) {
+ T sample = *in++;
+ acc |= prev ^ sample;
+ prev = sample;
+ last_append_extra_++;
+ len--;
+ }
+ if (!len) {
+ // Not enough samples available to complete downsample
+ last_append_sample_ = prev;
+ last_append_accumulator_ = acc;
+ return;
+ }
+ // We have a complete downsample
+ *out++ = acc;
+ acc = 0;
+ last_append_extra_ = 0;
+ }
+
+ // Handle complete blocks of MipMapScaleFactor samples
+ while (len >= MipMapScaleFactor) {
+ downsampleTmain<T>(in, acc, prev);
+ len -= MipMapScaleFactor;
+ // Output downsample
+ *out++ = acc;
+ acc = 0;
+ }
+
+ // Process remainder, not enough for a complete sample
+ while (len > 0) {
+ T sample = *in++;
+ acc |= prev ^ sample;
+ prev = sample;
+ last_append_extra_++;
+ len--;
+ }
+
+ // Update context
+ last_append_sample_ = prev;
+ last_append_accumulator_ = acc;
+ out_ = (uint8_t *)out;
+}
+
+void LogicSegment::downsampleGeneric(const uint8_t *in, uint8_t *&out, uint64_t len)
+{
+ // Downsample using the generic unpack_sample()
+ // which can handle any width between 1 and 8 bytes
+ uint64_t prev = last_append_sample_;
+ uint64_t acc = last_append_accumulator_;
+
+ // Try to complete the previous downsample
+ if (last_append_extra_) {
+ while (last_append_extra_ < MipMapScaleFactor && len > 0) {
+ const uint64_t sample = unpack_sample(in);
+ in += unit_size_;
+ acc |= prev ^ sample;
+ prev = sample;
+ last_append_extra_++;
+ len--;
+ }
+ if (!len) {
+ // Not enough samples available to complete downsample
+ last_append_sample_ = prev;
+ last_append_accumulator_ = acc;
+ return;
+ }
+ // We have a complete downsample
+ pack_sample(out, acc);
+ out += unit_size_;
+ acc = 0;
+ last_append_extra_ = 0;
+ }
+
+ // Handle complete blocks of MipMapScaleFactor samples
+ while (len >= MipMapScaleFactor) {
+ // Accumulate one sample at a time
+ for (uint64_t i = 0; i < MipMapScaleFactor; i++) {
+ const uint64_t sample = unpack_sample(in);
+ in += unit_size_;
+ acc |= prev ^ sample;
+ prev = sample;
+ }
+ len -= MipMapScaleFactor;
+ // Output downsample
+ pack_sample(out, acc);
+ out += unit_size_;
+ acc = 0;
+ }
+
+ // Process remainder, not enough for a complete sample
+ while (len > 0) {
+ const uint64_t sample = unpack_sample(in);
+ in += unit_size_;
+ acc |= prev ^ sample;
+ prev = sample;
+ last_append_extra_++;
+ len--;
+ }
+
+ // Update context
+ last_append_sample_ = prev;
+ last_append_accumulator_ = acc;
+}
+