RawSpeed
fast raw decoding library
Loading...
Searching...
No Matches
BitStreamerJPEG.h
Go to the documentation of this file.
1/*
2 RawSpeed - RAW file decoder.
3
4 Copyright (C) 2017 Axel Waggershauser
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19*/
20
21#pragma once
22
23#include "rawspeedconfig.h"
24#include "adt/Array1DRef.h"
25#include "adt/Bit.h"
26#include "adt/Casts.h"
27#include "adt/Invariant.h"
31#include "io/Endianness.h"
32#include <array>
33#include <concepts>
34#include <cstddef>
35#include <cstdint>
36#include <numeric>
37
38namespace rawspeed {
39
40template <typename T>
41 requires std::signed_integral<T>
42class PosOrUnknown final {
43 T val = -1; // Start with unknown position.
44
45public:
46 PosOrUnknown() = default;
47
48 [[nodiscard]] bool has_value() const RAWSPEED_READONLY { return val >= 0; }
49
50 template <typename U>
51 requires std::same_as<U, T>
52 PosOrUnknown& operator=(U newValue) {
54 val = newValue;
56 return *this;
57 }
58
59 template <typename U>
60 requires std::same_as<U, T>
61 [[nodiscard]] T value_or(U fallback) const {
62 if (has_value())
63 return val;
64 return fallback;
65 }
66};
67
68class BitStreamerJPEG;
69
70template <> struct BitStreamerTraits<BitStreamerJPEG> final {
71 static constexpr BitOrder Tag = BitOrder::JPEG;
72
73 static constexpr bool canUseWithPrefixCodeDecoder = true;
74
75 // How many bytes can we read from the input per each fillCache(), at most?
76 // Normally, we want to read 4 bytes, but at worst each one of those can be
77 // an 0xFF byte, separated by 0x00 byte, signifying that 0xFF is a data byte.
78 static constexpr int MaxProcessBytes = 8;
79 static_assert(MaxProcessBytes == sizeof(uint64_t));
80};
81
82// The JPEG data is ordered in MSB bit order,
83// i.e. we push into the cache from the right and read it from the left
84class BitStreamerJPEG final : public BitStreamer<BitStreamerJPEG> {
86
88
89 friend void Base::fill(int nbits); // Allow it to call our `fillCache()`.
90
93 input);
94
95public:
96 using Base::Base;
97
98 [[nodiscard]] size_type getStreamPosition() const;
99};
100
101// NOTE: on average, probability of encountering an `0xFF` byte
102// is ~0.51% (1 in ~197), only ~2.02% (1 in ~50) of 4-byte blocks will contain
103// an `0xFF` byte, and out of *those* blocks, only ~0.77% (1 in ~131)
104// will contain more than one `0xFF` byte.
105
108 inputStorage) {
109 static_assert(BitStreamCacheBase::MaxGetBits >= 32, "check implementation");
111 auto input = Array1DRef<std::byte>(inputStorage.data(),
112 implicit_cast<int>(inputStorage.size()));
113 invariant(input.size() == Traits::MaxProcessBytes);
114
115 constexpr int StreamChunkBitwidth =
117
118 auto speculativeOptimisticCache = cache;
119 auto speculativeOptimisticChunk =
121 input.begin(), StreamTraits::ChunkEndianness != getHostEndianness());
122 speculativeOptimisticCache.push(speculativeOptimisticChunk,
123 StreamChunkBitwidth);
124
125 // short-cut path for the most common case (no FF marker in the next 4 bytes)
126 // this is slightly faster than the else-case alone.
127 if (std::accumulate(&input(0), &input(4), true, [](bool b, std::byte byte) {
128 return b && (byte != std::byte{0xFF});
129 })) {
130 cache = speculativeOptimisticCache;
131 return 4;
132 }
133
134 size_type p = 0;
135 for (size_type i = 0; i < 4; ++i) {
136 const int numBytesNeeded = 4 - i;
137
138 // Pre-execute most common case, where next byte is 'normal'/non-FF
139 const std::byte c0 = input(p + 0);
140 cache.push(std::to_integer<uint8_t>(c0), 8);
141 if (c0 != std::byte{0xFF}) {
142 p += 1;
143 continue; // Got normal byte.
144 }
145
146 // Found FF -> pre-execute case of FF/00, which represents an FF data byte
147 const std::byte c1 = input(p + 1);
148 if (c1 == std::byte{0x00}) {
149 // Got FF/00, where 0x00 is a stuffing byte (that should be ignored),
150 // so 0xFF is a normal byte. All good.
151 p += 2;
152 continue;
153 }
154
155 // Found FF/xx with xx != 00. This is the end of stream marker.
157
158 // That means we shouldn't have pushed last 8 bits (0xFF, from c0).
159 // We need to "unpush" them, and fill the vacant cache bits with zeros.
160
161 // First, recover the cache fill level.
162 cache.fillLevel -= 8;
163 // Now, this code is incredibly underencapsulated, and the
164 // implementation details are leaking into here. Thus, we know that
165 // all the fillLevel bits in cache are all high bits. So to "unpush"
166 // the last 8 bits, and fill the vacant cache bits with zeros, we only
167 // need to keep the high fillLevel bits. So just create a mask with only
168 // high fillLevel bits set, and 'and' the cache with it.
169 // Caution, we know fillLevel won't be 64, but it may be 0,
170 // so pick the mask-creation idiom accordingly.
171 cache.cache &= ~((~0ULL) >> cache.fillLevel);
172 cache.fillLevel = 64;
173
174 // No further reading from this buffer shall happen. Do signal that by
175 // claiming that we have consumed all the remaining bytes of the buffer.
176
177 p = getRemainingSize() + numBytesNeeded;
178 invariant(p >= 6);
179 break;
180 }
181 invariant(p >= 5);
182 return p;
183}
184
186 // The current number of bytes we consumed.
187 // When at the end of the stream pos, it points to the JPEG marker FF
188 return endOfStreamPos.value_or(getInputPosition());
189}
190
191} // namespace rawspeed
Declaration of the bitstream data structure.
#define invariant(expr)
Definition Invariant.h:27
void establishClassInvariants() const noexcept
size_type RAWSPEED_READONLY getInputPosition() const
void fill(int nbits=Cache::MaxGetBits)
size_type getStreamPosition() const
PosOrUnknown< size_type > endOfStreamPos
size_type fillCache(std::array< std::byte, BitStreamerTraits< BitStreamerJPEG >::MaxProcessBytes > input)
BitStreamer< BitStreamerJPEG > Base
T value_or(U fallback) const
PosOrUnknown & operator=(U newValue)
bool has_value() const RAWSPEED_READONLY
constexpr RAWSPEED_READNONE Ttgt implicit_cast(Tsrc value)
Definition Casts.h:32
int8_t getByteSwapped(int8_t v)
Definition Endianness.h:75
throw T(buf.data())
Endianness getHostEndianness()
Definition Endianness.h:51
constexpr unsigned RAWSPEED_READNONE bitwidth(T unused={})
Definition Bit.h:43
static constexpr int MaxGetBits
Definition BitStream.h:48