12 #include <fmt/format.h>
26 static constexpr readSignedPtrType readSignedPtrs[16] = {
27 &BitStreamView::readSigned<DCC::bitsWidthTable[0]>,
28 &BitStreamView::readSigned<DCC::bitsWidthTable[1]>,
29 &BitStreamView::readSigned<DCC::bitsWidthTable[2]>,
30 &BitStreamView::readSigned<DCC::bitsWidthTable[3]>,
31 &BitStreamView::readSigned<DCC::bitsWidthTable[4]>,
32 &BitStreamView::readSigned<DCC::bitsWidthTable[5]>,
33 &BitStreamView::readSigned<DCC::bitsWidthTable[6]>,
34 &BitStreamView::readSigned<DCC::bitsWidthTable[7]>,
35 &BitStreamView::readSigned<DCC::bitsWidthTable[8]>,
36 &BitStreamView::readSigned<DCC::bitsWidthTable[9]>,
37 &BitStreamView::readSigned<DCC::bitsWidthTable[10]>,
38 &BitStreamView::readSigned<DCC::bitsWidthTable[11]>,
39 &BitStreamView::readSigned<DCC::bitsWidthTable[12]>,
40 &BitStreamView::readSigned<DCC::bitsWidthTable[13]>,
41 &BitStreamView::readSigned<DCC::bitsWidthTable[14]>,
42 &BitStreamView::readSigned<DCC::bitsWidthTable[15]>};
47 stream = std::move(streamPtr);
48 if (stream && stream->good()) {
49 return extractHeaderAndOffsets();
54 bool DCC::extractHeaderAndOffsets()
57 assert(stream->tell() == 0);
59 assert(stream->size() < std::numeric_limits<int32_t>::max());
61 stream->readRaw(header.
version);
64 stream->readRaw(header.
tag);
69 for (uint32_t dir = 0; dir < header.
directions; dir++)
73 return stream->good();
76 size_t DCC::getDirectionSize(uint32_t dirIndex)
81 static bool readDirHeader(DCC::DirectionHeader& dirHeader, BitStreamView& bitStream)
83 dirHeader.outsizeCoded = bitStream.readUnsigned(32);
84 dirHeader.hasRawPixelEncoding = bitStream.readBool();
85 dirHeader.compressEqualCells = bitStream.readBool();
86 dirHeader.variable0Bits = bitStream.readUnsigned8OrLess(4);
87 dirHeader.widthBits = bitStream.readUnsigned8OrLess(4);
88 dirHeader.heightBits = bitStream.readUnsigned8OrLess(4);
89 dirHeader.xOffsetBits = bitStream.readUnsigned8OrLess(4);
90 dirHeader.yOffsetBits = bitStream.readUnsigned8OrLess(4);
91 dirHeader.optionalBytesBits = bitStream.readUnsigned8OrLess(4);
92 dirHeader.codedBytesBits = bitStream.readUnsigned8OrLess(4);
93 return bitStream.good();
96 static bool readFrameHeaders(uint32_t nbFrames, DCC::Direction& outDir, BitStreamView& bitStream)
99 const DCC::DirectionHeader& dirHeader = outDir.header;
101 outDir.frameHeaders.resize(nbFrames);
102 for (DCC::FrameHeader& fHdr : outDir.frameHeaders)
106 fHdr.variable0 = bitStream.readUnsigned(bitsWidthTable[dirHeader.variable0Bits]);
107 fHdr.width = bitStream.readUnsigned(bitsWidthTable[dirHeader.widthBits]);
108 fHdr.height = bitStream.readUnsigned(bitsWidthTable[dirHeader.heightBits]);
109 fHdr.xOffset = (bitStream.*readSignedPtrs[dirHeader.xOffsetBits])();
110 fHdr.yOffset = (bitStream.*readSignedPtrs[dirHeader.yOffsetBits])();
112 fHdr.optionalBytes = bitStream.readUnsigned(bitsWidthTable[dirHeader.optionalBytesBits]);
113 fHdr.codedBytes = bitStream.readUnsigned(bitsWidthTable[dirHeader.codedBytesBits]);
114 fHdr.frameBottomUp = bitStream.readBool();
116 assert(fHdr.width < 0x700000);
117 assert(fHdr.height < 0x700000);
118 fHdr.extents.xLower = fHdr.xOffset;
119 fHdr.extents.xUpper = fHdr.xOffset + int32_t(fHdr.width);
121 if (fHdr.frameBottomUp) {
122 assert(
false &&
"Please report the name of the DCC file to the devs!");
123 fHdr.extents.yLower = fHdr.yOffset;
125 fHdr.extents.yUpper = fHdr.yOffset + int32_t(fHdr.height);
129 fHdr.extents.yLower = fHdr.yOffset - int32_t(fHdr.height) + 1;
131 fHdr.extents.yUpper = fHdr.yOffset + 1;
136 for (DCC::FrameHeader& frameHeader : outDir.frameHeaders)
138 if (frameHeader.optionalBytes) {
139 assert(
false &&
"Please report the name of the DCC file to the devs!");
140 bitStream.alignToByte();
141 bitStream.skip(frameHeader.optionalBytes * CHAR_BIT);
144 return bitStream.good();
149 constexpr
size_t pbCellMaxPixelSize = 4u;
158 struct PixelBufferEntry
160 static constexpr
size_t nbValues = 4;
161 uint8_t values[nbValues];
166 using CellSize = uint8_t;
168 size_t firstPixelBufferEntry;
174 Vector<bool> cellSameAsPrevious;
175 Vector<CellSize> cellWidths;
176 Vector<CellSize> cellHeights;
179 FrameData(
const DCC::Direction& dir,
const DCC::FrameHeader& frameHeader,
180 IImageProvider<uint8_t>& imgProvider)
181 : firstPixelBufferEntry(0)
183 offsetX = uint16_t(frameHeader.extents.xLower - dir.extents.xLower);
184 offsetY = uint16_t(frameHeader.extents.yLower - dir.extents.yLower);
187 const uint16_t widthFirstColumn = 4 - (
offsetX % 4);
188 const uint16_t frameWidth = uint16_t(frameHeader.extents.width());
189 if ((frameWidth - widthFirstColumn) <= 1)
194 uint16_t tmp = frameWidth - widthFirstColumn - 1;
195 nbCellsX = 2 + (tmp / 4);
196 if ((tmp % 4) == 0) nbCellsX--;
199 const uint16_t heightFirstRow = 4 - (
offsetY % 4);
200 const uint16_t frameHeight = uint16_t(frameHeader.extents.height());
201 if ((frameHeight - heightFirstRow) <= 1)
205 uint16_t tmp = frameHeight - heightFirstRow - 1;
206 nbCellsY = 2 + (tmp / 4);
207 if ((tmp % 4) == 0) nbCellsY--;
210 cellSameAsPrevious.resize(
size_t(nbCellsX) *
size_t(nbCellsY));
213 cellWidths.resize(nbCellsX, 4);
214 cellHeights.resize(nbCellsY, 4);
217 cellWidths[0] = CellSize(frameWidth);
221 cellWidths[0] = CellSize(widthFirstColumn);
223 const size_t nbColumnsExcludingFirstAndLast = nbCellsX - 2;
224 const size_t widthExcludingFirstAndLastColumns = 4 * nbColumnsExcludingFirstAndLast;
225 cellWidths[nbCellsX - 1] =
226 CellSize(frameWidth - (widthFirstColumn + widthExcludingFirstAndLastColumns));
230 cellHeights[0] = CellSize(frameHeight);
233 cellHeights[0] = CellSize(heightFirstRow);
235 const size_t nbRowsExcludingFirstAndLast = nbCellsY - 2;
236 const size_t heightExcludingFirstAndLastRows = 4 * nbRowsExcludingFirstAndLast;
237 cellHeights[nbCellsY - 1] =
238 CellSize(frameHeight - (heightFirstRow + heightExcludingFirstAndLastRows));
241 imageView = imgProvider.getNewImage(frameWidth, frameHeight);
247 const DCC::Direction& dirRef;
249 Vector<uint8_t> codeToPixelValue;
251 BitStreamView equalCellBitStream;
252 BitStreamView pixelMaskBitStream;
253 BitStreamView rawPixelUsageBitStream;
254 BitStreamView rawPixelCodesBitStream;
255 BitStreamView pixelCodesDisplacementBitStream;
258 size_t nbPixelBufferCellsX;
259 size_t nbPixelBufferCellsY;
261 Vector<FrameData> framesData;
263 DirectionData(
const DCC::Direction& dir, BitStreamView& bitStream,
size_t nbFramesPerDir,
264 IImageProvider<uint8_t>& imgProvider)
265 : dirRef(dir), nbFrames(nbFramesPerDir)
267 uint32_t equalCellsBitStreamSize = 0;
268 uint32_t pixelMaskBitStreamSize = 0;
269 uint32_t encodingTypeBitsreamSize = 0;
270 uint32_t rawPixelCodesBitStreamSize = 0;
272 if (dir.header.compressEqualCells) {
273 equalCellsBitStreamSize = bitStream.readUnsigned(20);
276 pixelMaskBitStreamSize = bitStream.readUnsigned(20);
278 if (dir.header.hasRawPixelEncoding) {
279 encodingTypeBitsreamSize = bitStream.readUnsigned(20);
280 rawPixelCodesBitStreamSize = bitStream.readUnsigned(20);
288 codeToPixelValue.reserve(256);
289 for (
size_t i = 0; i < 256; i++)
291 const bool pixelValueUsed = bitStream.readBool();
292 if (pixelValueUsed) codeToPixelValue.push_back(uint8_t(i));
297 assert(!dir.header.compressEqualCells || equalCellsBitStreamSize);
298 equalCellBitStream = bitStream.createSubView(equalCellsBitStreamSize);
299 bitStream.skip(equalCellsBitStreamSize);
301 pixelMaskBitStream = bitStream.createSubView(pixelMaskBitStreamSize);
302 bitStream.skip(pixelMaskBitStreamSize);
304 assert(!dir.header.hasRawPixelEncoding ||
305 (encodingTypeBitsreamSize && rawPixelCodesBitStreamSize));
306 rawPixelUsageBitStream = bitStream.createSubView(encodingTypeBitsreamSize);
307 bitStream.skip(encodingTypeBitsreamSize);
309 rawPixelCodesBitStream = bitStream.createSubView(rawPixelCodesBitStreamSize);
310 bitStream.skip(rawPixelCodesBitStreamSize);
313 pixelCodesDisplacementBitStream =
314 bitStream.createSubView(bitStream.bufferSizeInBits() - bitStream.tell());
316 const size_t dirWidth = size_t(dir.extents.width());
317 const size_t dirHeight = size_t(dir.extents.height());
323 nbPixelBufferCellsX = 1u + (dirWidth - 1u) / pbCellMaxPixelSize;
324 nbPixelBufferCellsY = 1u + (dirHeight - 1u) / pbCellMaxPixelSize;
326 framesData.reserve(nbFrames);
328 for (
size_t frameIndex = 0; frameIndex < nbFrames; ++frameIndex)
330 framesData.emplace_back(dir, dir.frameHeaders[frameIndex], imgProvider);
337 for (
size_t frameIndex = 0; frameIndex < nbFrames; ++frameIndex)
339 if (!framesData[frameIndex].
imageView.isValid())
return false;
345 using PixelCodesStack = std::array<uint8_t, PixelBufferEntry::nbValues>;
349 int decodePixelCodesStack(DirectionData& data, uint8_t pixelMask, PixelCodesStack& pixelCodesStack)
351 if (!pixelMask)
return 0;
352 const uint16_t nbPixelsInMask = Utils::popCount(uint16_t(pixelMask));
355 const bool decodeRaw = data.rawPixelUsageBitStream.bufferSizeInBits() > 0 &&
356 data.rawPixelUsageBitStream.readBool();
358 uint8_t lastPixelCode = 0;
359 size_t curPixelIdx = 0;
361 for (curPixelIdx = 0; curPixelIdx < nbPixelsInMask; curPixelIdx++)
363 uint8_t& curPixelCode = pixelCodesStack[curPixelIdx];
366 curPixelCode = data.rawPixelCodesBitStream.readUnsigned8OrLess(8);
371 curPixelCode = lastPixelCode;
372 uint8_t pixelDisplacement;
375 pixelDisplacement = data.pixelCodesDisplacementBitStream.readUnsigned8OrLess(4);
376 curPixelCode += pixelDisplacement;
377 }
while (pixelDisplacement == 0xF);
381 if (curPixelCode == lastPixelCode) {
389 lastPixelCode = curPixelCode;
392 return int(curPixelIdx);
395 void decodeFrameStage1(DirectionData& data, FrameData& frameData, Vector<size_t>& pixelBuffer,
396 Vector<PixelBufferEntry>& pbEntries)
399 const size_t frameCellOffsetX = frameData.offsetX / 4;
400 const size_t frameCellOffsetY = frameData.offsetY / 4;
403 for (
size_t y = 0; y < frameData.nbCellsY; y++)
405 const size_t curCellY = frameCellOffsetY + y;
406 for (
size_t x = 0; x < frameData.nbCellsX; x++)
408 const size_t curCellX = frameCellOffsetX + x;
410 const size_t curPbCellIndex = curCellX + curCellY * data.nbPixelBufferCellsX;
411 const size_t curFrameCellIndex = x + y * frameData.nbCellsX;
413 size_t& lastPixelEntryIndexForCell = pixelBuffer[curPbCellIndex];
415 bool sameAsPreviousCell =
false;
416 uint8_t pixelMask = 0x0F;
419 if (lastPixelEntryIndexForCell < pbEntries.size()) {
421 if (data.dirRef.header.compressEqualCells) {
424 sameAsPreviousCell = data.equalCellBitStream.readBool();
426 if (!sameAsPreviousCell) {
427 pixelMask = data.pixelMaskBitStream.readUnsigned8OrLess(4);
432 frameData.cellSameAsPrevious[curFrameCellIndex] = sameAsPreviousCell;
434 if (!sameAsPreviousCell) {
437 PixelCodesStack pixelCodesStack = {};
438 int nbPixelsDecoded = decodePixelCodesStack(data, pixelMask, pixelCodesStack);
440 PixelBufferEntry previousEntryForCell;
441 if (lastPixelEntryIndexForCell < pbEntries.size()) {
442 previousEntryForCell = pbEntries[lastPixelEntryIndexForCell];
447 assert(pixelMask == 0xF);
451 PixelBufferEntry newEntry;
453 int curIndex = nbPixelsDecoded - 1;
454 for (
size_t i = 0; i < PixelBufferEntry::nbValues; i++)
457 if (pixelMask & (1u << i)) {
460 pixelCode = pixelCodesStack[size_t(curIndex--)];
467 newEntry.values[i] = data.codeToPixelValue[pixelCode];
472 newEntry.values[i] = previousEntryForCell.values[i];
476 lastPixelEntryIndexForCell = pbEntries.size();
478 pbEntries.push_back(newEntry);
484 void decodeDirectionStage1(DirectionData& data, Vector<PixelBufferEntry>& pbEntries)
488 constexpr
size_t invalidIndex = std::numeric_limits<size_t>::max();
489 const size_t pixelBufferNbCells = data.nbPixelBufferCellsX * data.nbPixelBufferCellsY;
490 Vector<size_t> pixelBuffer(pixelBufferNbCells, invalidIndex);
494 for (
size_t frameIndex = 0; frameIndex < data.nbFrames; ++frameIndex)
496 FrameData& frameData = data.framesData[frameIndex];
497 frameData.firstPixelBufferEntry = pbEntries.size();
498 decodeFrameStage1(data, frameData, pixelBuffer, pbEntries);
502 void decodeDirectionStage2(DirectionData& data,
const Vector<PixelBufferEntry>& pbEntries)
505 BitStreamView& pixelCodeIndices = data.pixelCodesDisplacementBitStream;
507 const size_t pbWidth = size_t(data.dirRef.extents.width());
508 const size_t pbHeight = size_t(data.dirRef.extents.height());
509 const size_t pbStride = pbWidth;
510 const size_t nbPixelBufferCells = data.nbPixelBufferCellsX * data.nbPixelBufferCellsY;
512 Vector<Cell> pixelBufferCells(nbPixelBufferCells, Cell{0xF, 0xF});
513 Vector<uint8_t> pixelBufferColors(pbStride * pbHeight);
514 ImageView<uint8_t> pBuffer{pixelBufferColors.data(), pbWidth, pbHeight, pbStride};
517 for (
size_t frameIndex = 0; frameIndex < data.nbFrames; ++frameIndex)
519 const FrameData& frameData = data.framesData[frameIndex];
520 size_t pbEntryIndex = frameData.firstPixelBufferEntry;
522 size_t pbCellPosY = frameData.offsetY;
523 for (
size_t cellY = 0; cellY < frameData.nbCellsY; cellY++)
525 size_t pbCellPosX = frameData.offsetX;
526 for (
size_t cellX = 0; cellX < frameData.nbCellsX; cellX++)
528 const size_t frameCellIndex = cellX + cellY * frameData.nbCellsX;
530 frameCell.width = frameData.cellWidths[cellX];
531 frameCell.height = frameData.cellHeights[cellY];
533 const size_t pbCellIndex =
534 (pbCellPosX / pbCellMaxPixelSize) +
535 (pbCellPosY / pbCellMaxPixelSize) * data.nbPixelBufferCellsX;
536 Cell& pbCell = pixelBufferCells[pbCellIndex];
538 if (frameData.cellSameAsPrevious[frameCellIndex]) {
539 if ((frameCell.width != pbCell.width) || (frameCell.height != pbCell.height)) {
545 pBuffer.fillBytes(pbCellPosX, pbCellPosY, frameCell.width, frameCell.height,
556 const auto& pixelValues = pbEntries[pbEntryIndex++].values;
558 if (pixelValues[0] == pixelValues[1]) {
560 pBuffer.fillBytes(pbCellPosX, pbCellPosY, frameCell.width, frameCell.height,
565 int nbBitsToRead = 0;
566 if (pixelValues[1] == pixelValues[2]) {
578 for (
size_t y = 0; y < frameCell.height; y++)
580 for (
size_t x = 0; x < frameCell.width; x++)
582 const uint8_t pixelCodeIndex =
583 pixelCodeIndices.readUnsigned8OrLess(nbBitsToRead);
586 const uint8_t pixelValue = pixelValues[pixelCodeIndex];
588 pBuffer(pbCellPosX + x, pbCellPosY + y) = pixelValue;
595 pbCellPosX += frameCell.width;
597 pbCellPosY += frameData.cellHeights[cellY];
600 const ImageView<uint8_t> frameImageView = frameData.imageView;
601 const ImageView<uint8_t> pbFrameView = pBuffer.subView(
602 frameData.offsetX, frameData.offsetY, frameImageView.width, frameImageView.height);
603 assert(frameImageView.isValid() && pbFrameView.isValid());
605 pbFrameView.copyTo(frameImageView);
608 #define DEBUG_EXPORT_PPM 0
610 #define EXPORT_FULL_SIZE false
612 auto filename = fmt::format(
"test{}.ppm", frameIndex);
613 Utils::exportToPGM(filename.c_str(), EXPORT_FULL_SIZE ? pBuffer : frameImageView);
620 if (dirIndex >= header.
directions)
return false;
622 const size_t directionEncodedSize = getDirectionSize(dirIndex);
623 Vector<uint8_t> buffer(directionEncodedSize);
625 stream->read(buffer.data(), directionEncodedSize);
626 assert(stream->good());
627 BitStreamView bitStream(buffer.data(), directionEncodedSize * CHAR_BIT);
630 if (!readDirHeader(dirHeader, bitStream))
return false;
632 if (!readFrameHeaders(header.
framesPerDir, outDir, bitStream))
return false;
636 DirectionData data{outDir, bitStream, header.
framesPerDir, imgProvider};
637 if (!data.isValid())
return false;
639 Vector<PixelBufferEntry> pbEntries;
641 size_t estimatedNbEntries =
642 (header.
framesPerDir * data.nbPixelBufferCellsX * data.nbPixelBufferCellsY) / 4;
643 pbEntries.reserve(estimatedNbEntries);
646 decodeDirectionStage1(data, pbEntries);
648 decodeDirectionStage2(data, pbEntries);
651 assert(data.equalCellBitStream.tell() == data.equalCellBitStream.sizeInBits());
652 assert(data.pixelMaskBitStream.tell() == data.pixelMaskBitStream.sizeInBits());
653 assert(data.rawPixelUsageBitStream.tell() == data.rawPixelUsageBitStream.sizeInBits());
654 assert(data.rawPixelCodesBitStream.tell() == data.rawPixelCodesBitStream.sizeInBits());
656 assert(data.pixelCodesDisplacementBitStream.bitPositionInBuffer() + 7_z >=
657 data.pixelCodesDisplacementBitStream.bufferSizeInBits());
659 return bitStream.good();
ImageView< uint8_t > imageView
Output buffer image view.
uint16_t offsetX
X Offset relative to the whole direction bounding box.
static constexpr unsigned bitsWidthTable[16]
An array that maps an encoded 4-bit size to the real size in bits.
bool readDirection(Direction &outDir, uint32_t dirIndex, IImageProvider< uint8_t > &imgProvider)
Decodes a direction of the file into memory.
Vector< uint32_t > directionsOffsets
Offset of each direction header in the file, follows the Header.
uint16_t offsetY
Y Offset relative to the whole direction bounding box.
Implements image manipulation helpers.
An interface of a class that can provide images views.
Implementation of a DCC file decoder.
bool initDecoder(StreamPtr &&streamPtr)
Start decoding the stream and preparing data.
Provides access to a variable bitsize values stream.