LibGfx/TIFF: Add support for images with CCITT3 1D compression
This compression (tag Compression=2) is not very popular on its own, but
a base to implement CCITT3 2D and CCITT4 compressions.
As the format has no real benefits, it is quite hard to find an app that
accepts tho encode that for you. So I used the following program that
calls `libtiff` directly:
```cpp
#include <vector>
#include <cstdlib>
#include <iostream>
#include <tiffio.h>
// An array containing 0 and 1 of length width * height.
extern std::vector<uint8_t> array;
int main() {
// From: https://stackoverflow.com/a/34257789
TIFF *image = TIFFOpen("input.tif", "w");
int const width = 400;
int const height = 300;
TIFFSetField(image, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(image, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(image, TIFFTAG_PHOTOMETRIC, 0);
TIFFSetField(image, TIFFTAG_COMPRESSION, COMPRESSION_CCITTRLE);
TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, 1);
TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField(image, TIFFTAG_ROWSPERSTRIP, 1);
std::vector<uint8_t> scan_line(width / 8 + 8, 0);
int count = 0;
for (int i = 0; i < height; i++) {
std::fill(scan_line.begin(), scan_line.end(), 0);
for (int x = 0; x < width; ++x) {
uint8_t eight_pixels = scan_line.at(x / 8);
eight_pixels = eight_pixels << 1;
eight_pixels |= !array.at(i * width + x);
scan_line.at(x / 8) = eight_pixels;
}
int bytes = int(width / 8.0 + 0.5);
if (TIFFWriteScanline(image, scan_line.data(), i, bytes) != 1)
std::cerr << "Something went wrong\n";
}
TIFFClose(image);
}
```
2023-11-25 16:34:56 -05:00
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2023, Lucas Chollet <lucas.chollet@serenityos.org>
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <AK/Array.h>
|
|
|
|
|
#include <AK/BitStream.h>
|
|
|
|
|
#include <AK/MemoryStream.h>
|
|
|
|
|
#include <LibGfx/Color.h>
|
|
|
|
|
#include <LibGfx/ImageFormats/CCITTDecoder.h>
|
|
|
|
|
|
|
|
|
|
namespace Gfx::CCITT {
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
struct Code {
|
|
|
|
|
u16 run_length {};
|
|
|
|
|
u8 code_length {};
|
|
|
|
|
u16 code {};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Table 2/T.4 – Terminating codes
|
|
|
|
|
constexpr Array white_terminating_codes = {
|
|
|
|
|
Code { 0, 8, 0b00110101 },
|
|
|
|
|
Code { 1, 6, 0b000111 },
|
|
|
|
|
Code { 2, 4, 0b0111 },
|
|
|
|
|
Code { 3, 4, 0b1000 },
|
|
|
|
|
Code { 4, 4, 0b1011 },
|
|
|
|
|
Code { 5, 4, 0b1100 },
|
|
|
|
|
Code { 6, 4, 0b1110 },
|
|
|
|
|
Code { 7, 4, 0b1111 },
|
|
|
|
|
Code { 8, 5, 0b10011 },
|
|
|
|
|
Code { 9, 5, 0b10100 },
|
|
|
|
|
Code { 10, 5, 0b00111 },
|
|
|
|
|
Code { 11, 5, 0b01000 },
|
|
|
|
|
Code { 12, 6, 0b001000 },
|
|
|
|
|
Code { 13, 6, 0b000011 },
|
|
|
|
|
Code { 14, 6, 0b110100 },
|
|
|
|
|
Code { 15, 6, 0b110101 },
|
|
|
|
|
Code { 16, 6, 0b101010 },
|
|
|
|
|
Code { 17, 6, 0b101011 },
|
|
|
|
|
Code { 18, 7, 0b0100111 },
|
|
|
|
|
Code { 19, 7, 0b0001100 },
|
|
|
|
|
Code { 20, 7, 0b0001000 },
|
|
|
|
|
Code { 21, 7, 0b0010111 },
|
|
|
|
|
Code { 22, 7, 0b0000011 },
|
|
|
|
|
Code { 23, 7, 0b0000100 },
|
|
|
|
|
Code { 24, 7, 0b0101000 },
|
|
|
|
|
Code { 25, 7, 0b0101011 },
|
|
|
|
|
Code { 26, 7, 0b0010011 },
|
|
|
|
|
Code { 27, 7, 0b0100100 },
|
|
|
|
|
Code { 28, 7, 0b0011000 },
|
|
|
|
|
Code { 29, 8, 0b00000010 },
|
|
|
|
|
Code { 30, 8, 0b00000011 },
|
|
|
|
|
Code { 31, 8, 0b00011010 },
|
|
|
|
|
Code { 32, 8, 0b00011011 },
|
|
|
|
|
Code { 33, 8, 0b00010010 },
|
|
|
|
|
Code { 34, 8, 0b00010011 },
|
|
|
|
|
Code { 35, 8, 0b00010100 },
|
|
|
|
|
Code { 36, 8, 0b00010101 },
|
|
|
|
|
Code { 37, 8, 0b00010110 },
|
|
|
|
|
Code { 38, 8, 0b00010111 },
|
|
|
|
|
Code { 39, 8, 0b00101000 },
|
|
|
|
|
Code { 40, 8, 0b00101001 },
|
|
|
|
|
Code { 41, 8, 0b00101010 },
|
|
|
|
|
Code { 42, 8, 0b00101011 },
|
|
|
|
|
Code { 43, 8, 0b00101100 },
|
|
|
|
|
Code { 44, 8, 0b00101101 },
|
|
|
|
|
Code { 45, 8, 0b00000100 },
|
|
|
|
|
Code { 46, 8, 0b00000101 },
|
|
|
|
|
Code { 47, 8, 0b00001010 },
|
|
|
|
|
Code { 48, 8, 0b00001011 },
|
|
|
|
|
Code { 49, 8, 0b01010010 },
|
|
|
|
|
Code { 50, 8, 0b01010011 },
|
|
|
|
|
Code { 51, 8, 0b01010100 },
|
|
|
|
|
Code { 52, 8, 0b01010101 },
|
|
|
|
|
Code { 53, 8, 0b00100100 },
|
|
|
|
|
Code { 54, 8, 0b00100101 },
|
|
|
|
|
Code { 55, 8, 0b01011000 },
|
|
|
|
|
Code { 56, 8, 0b01011001 },
|
|
|
|
|
Code { 57, 8, 0b01011010 },
|
|
|
|
|
Code { 58, 8, 0b01011011 },
|
|
|
|
|
Code { 59, 8, 0b01001010 },
|
|
|
|
|
Code { 60, 8, 0b01001011 },
|
|
|
|
|
Code { 61, 8, 0b00110010 },
|
|
|
|
|
Code { 62, 8, 0b00110011 },
|
|
|
|
|
Code { 63, 8, 0b00110100 },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Table 2/T.4 – Terminating codes
|
|
|
|
|
constexpr Array black_terminating_codes = {
|
|
|
|
|
Code { 0, 10, 0b0000110111 },
|
|
|
|
|
Code { 1, 3, 0b010 },
|
|
|
|
|
Code { 2, 2, 0b11 },
|
|
|
|
|
Code { 3, 2, 0b10 },
|
|
|
|
|
Code { 4, 3, 0b011 },
|
|
|
|
|
Code { 5, 4, 0b0011 },
|
|
|
|
|
Code { 6, 4, 0b0010 },
|
|
|
|
|
Code { 7, 5, 0b00011 },
|
|
|
|
|
Code { 8, 6, 0b000101 },
|
|
|
|
|
Code { 9, 6, 0b000100 },
|
|
|
|
|
Code { 10, 7, 0b0000100 },
|
|
|
|
|
Code { 11, 7, 0b0000101 },
|
|
|
|
|
Code { 12, 7, 0b0000111 },
|
|
|
|
|
Code { 13, 8, 0b00000100 },
|
|
|
|
|
Code { 14, 8, 0b00000111 },
|
|
|
|
|
Code { 15, 9, 0b000011000 },
|
|
|
|
|
Code { 16, 10, 0b0000010111 },
|
|
|
|
|
Code { 17, 10, 0b0000011000 },
|
|
|
|
|
Code { 18, 10, 0b0000001000 },
|
|
|
|
|
Code { 19, 11, 0b00001100111 },
|
|
|
|
|
Code { 20, 11, 0b00001101000 },
|
|
|
|
|
Code { 21, 11, 0b00001101100 },
|
|
|
|
|
Code { 22, 11, 0b00000110111 },
|
|
|
|
|
Code { 23, 11, 0b00000101000 },
|
|
|
|
|
Code { 24, 11, 0b00000010111 },
|
|
|
|
|
Code { 25, 11, 0b00000011000 },
|
|
|
|
|
Code { 26, 12, 0b000011001010 },
|
|
|
|
|
Code { 27, 12, 0b000011001011 },
|
|
|
|
|
Code { 28, 12, 0b000011001100 },
|
|
|
|
|
Code { 29, 12, 0b000011001101 },
|
|
|
|
|
Code { 30, 12, 0b000001101000 },
|
|
|
|
|
Code { 31, 12, 0b000001101001 },
|
|
|
|
|
Code { 32, 12, 0b000001101010 },
|
|
|
|
|
Code { 33, 12, 0b000001101011 },
|
|
|
|
|
Code { 34, 12, 0b000011010010 },
|
|
|
|
|
Code { 35, 12, 0b000011010011 },
|
|
|
|
|
Code { 36, 12, 0b000011010100 },
|
|
|
|
|
Code { 37, 12, 0b000011010101 },
|
|
|
|
|
Code { 38, 12, 0b000011010110 },
|
|
|
|
|
Code { 39, 12, 0b000011010111 },
|
|
|
|
|
Code { 40, 12, 0b000001101100 },
|
|
|
|
|
Code { 41, 12, 0b000001101101 },
|
|
|
|
|
Code { 42, 12, 0b000011011010 },
|
|
|
|
|
Code { 43, 12, 0b000011011011 },
|
|
|
|
|
Code { 44, 12, 0b000001010100 },
|
|
|
|
|
Code { 45, 12, 0b000001010101 },
|
|
|
|
|
Code { 46, 12, 0b000001010110 },
|
|
|
|
|
Code { 47, 12, 0b000001010111 },
|
|
|
|
|
Code { 48, 12, 0b000001100100 },
|
|
|
|
|
Code { 49, 12, 0b000001100101 },
|
|
|
|
|
Code { 50, 12, 0b000001010010 },
|
|
|
|
|
Code { 51, 12, 0b000001010011 },
|
|
|
|
|
Code { 52, 12, 0b000000100100 },
|
|
|
|
|
Code { 53, 12, 0b000000110111 },
|
|
|
|
|
Code { 54, 12, 0b000000111000 },
|
|
|
|
|
Code { 55, 12, 0b000000100111 },
|
|
|
|
|
Code { 56, 12, 0b000000101000 },
|
|
|
|
|
Code { 57, 12, 0b000001011000 },
|
|
|
|
|
Code { 58, 12, 0b000001011001 },
|
|
|
|
|
Code { 59, 12, 0b000000101011 },
|
|
|
|
|
Code { 60, 12, 0b000000101100 },
|
|
|
|
|
Code { 61, 12, 0b000001011010 },
|
|
|
|
|
Code { 62, 12, 0b000001100110 },
|
|
|
|
|
Code { 63, 12, 0b000001100111 },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Table 3a/T.4 – Make-up codes
|
|
|
|
|
constexpr Array white_make_up_codes = {
|
|
|
|
|
Code { 64, 5, 0b11011 },
|
|
|
|
|
Code { 128, 5, 0b10010 },
|
|
|
|
|
Code { 192, 6, 0b010111 },
|
|
|
|
|
Code { 256, 7, 0b0110111 },
|
|
|
|
|
Code { 320, 8, 0b00110110 },
|
|
|
|
|
Code { 384, 8, 0b00110111 },
|
|
|
|
|
Code { 448, 8, 0b01100100 },
|
|
|
|
|
Code { 512, 8, 0b01100101 },
|
|
|
|
|
Code { 576, 8, 0b01101000 },
|
|
|
|
|
Code { 640, 8, 0b01100111 },
|
|
|
|
|
Code { 704, 9, 0b011001100 },
|
|
|
|
|
Code { 768, 9, 0b011001101 },
|
|
|
|
|
Code { 832, 9, 0b011010010 },
|
|
|
|
|
Code { 896, 9, 0b011010011 },
|
|
|
|
|
Code { 960, 9, 0b011010100 },
|
|
|
|
|
Code { 1024, 9, 0b011010101 },
|
|
|
|
|
Code { 1088, 9, 0b011010110 },
|
|
|
|
|
Code { 1152, 9, 0b011010111 },
|
|
|
|
|
Code { 1216, 9, 0b011011000 },
|
|
|
|
|
Code { 1280, 9, 0b011011001 },
|
|
|
|
|
Code { 1344, 9, 0b011011010 },
|
|
|
|
|
Code { 1408, 9, 0b011011011 },
|
|
|
|
|
Code { 1472, 9, 0b010011000 },
|
|
|
|
|
Code { 1536, 9, 0b010011001 },
|
|
|
|
|
Code { 1600, 9, 0b010011010 },
|
|
|
|
|
Code { 1664, 6, 0b011000 },
|
|
|
|
|
Code { 1728, 9, 0b010011011 },
|
|
|
|
|
Code { 0xE0L, 12, 0b000000000001 },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Table 3a/T.4 – Make-up codes
|
|
|
|
|
constexpr Array black_make_up_codes = {
|
|
|
|
|
Code { 64, 10, 0b0000001111 },
|
|
|
|
|
Code { 128, 12, 0b000011001000 },
|
|
|
|
|
Code { 192, 12, 0b000011001001 },
|
|
|
|
|
Code { 256, 12, 0b000001011011 },
|
|
|
|
|
Code { 320, 12, 0b000000110011 },
|
|
|
|
|
Code { 384, 12, 0b000000110100 },
|
|
|
|
|
Code { 448, 12, 0b000000110101 },
|
|
|
|
|
Code { 512, 13, 0b0000001101100 },
|
|
|
|
|
Code { 576, 13, 0b0000001101101 },
|
|
|
|
|
Code { 640, 13, 0b0000001001010 },
|
|
|
|
|
Code { 704, 13, 0b0000001001011 },
|
|
|
|
|
Code { 768, 13, 0b0000001001100 },
|
|
|
|
|
Code { 832, 13, 0b0000001001101 },
|
|
|
|
|
Code { 896, 13, 0b0000001110010 },
|
|
|
|
|
Code { 960, 13, 0b0000001110011 },
|
|
|
|
|
Code { 1024, 13, 0b0000001110100 },
|
|
|
|
|
Code { 1088, 13, 0b0000001110101 },
|
|
|
|
|
Code { 1152, 13, 0b0000001110110 },
|
|
|
|
|
Code { 1216, 13, 0b0000001110111 },
|
|
|
|
|
Code { 1280, 13, 0b0000001010010 },
|
|
|
|
|
Code { 1344, 13, 0b0000001010011 },
|
|
|
|
|
Code { 1408, 13, 0b0000001010100 },
|
|
|
|
|
Code { 1472, 13, 0b0000001010101 },
|
|
|
|
|
Code { 1536, 13, 0b0000001011010 },
|
|
|
|
|
Code { 1600, 13, 0b0000001011011 },
|
|
|
|
|
Code { 1664, 13, 0b0000001100100 },
|
|
|
|
|
Code { 1728, 13, 0b0000001100101 },
|
|
|
|
|
Code { 0xE0L, 11, 0b00000000000 },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Table 3b/T.4 – Make-up codes
|
|
|
|
|
constexpr Array common_make_up_codes = {
|
|
|
|
|
Code { 1792, 11, 0b00000001000 },
|
|
|
|
|
Code { 1856, 11, 0b00000001100 },
|
|
|
|
|
Code { 1920, 11, 0b00000001101 },
|
|
|
|
|
Code { 1984, 12, 0b000000010010 },
|
|
|
|
|
Code { 2048, 12, 0b000000010011 },
|
|
|
|
|
Code { 2112, 12, 0b000000010100 },
|
|
|
|
|
Code { 2176, 12, 0b000000010101 },
|
|
|
|
|
Code { 2240, 12, 0b000000010110 },
|
|
|
|
|
Code { 2304, 12, 0b000000010111 },
|
|
|
|
|
Code { 2368, 12, 0b000000011100 },
|
|
|
|
|
Code { 2432, 12, 0b000000011101 },
|
|
|
|
|
Code { 2496, 12, 0b000000011110 },
|
|
|
|
|
Code { 2560, 12, 0b000000011111 },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<size_t Size>
|
|
|
|
|
Optional<Code> get_code_from_table(Array<Code, Size> const& array, u16 code_word, u8 code_size)
|
|
|
|
|
{
|
|
|
|
|
for (auto const& code : array) {
|
|
|
|
|
if (code.code_length == code_size && code.code == code_word)
|
|
|
|
|
return code;
|
|
|
|
|
}
|
|
|
|
|
return OptionalNone {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Optional<Code> get_markup_code(Color color, u16 code_word, u8 code_size)
|
|
|
|
|
{
|
|
|
|
|
if (auto maybe_value = get_code_from_table(common_make_up_codes, code_word, code_size); maybe_value.has_value())
|
|
|
|
|
return maybe_value.value();
|
|
|
|
|
|
|
|
|
|
if (color == Color::NamedColor::White)
|
|
|
|
|
return get_code_from_table(white_make_up_codes, code_word, code_size);
|
|
|
|
|
return get_code_from_table(black_make_up_codes, code_word, code_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Optional<Code> get_terminal_code(Color color, u16 code_word, u8 code_size)
|
|
|
|
|
{
|
|
|
|
|
if (color == Color::NamedColor::White)
|
|
|
|
|
return get_code_from_table(white_terminating_codes, code_word, code_size);
|
|
|
|
|
return get_code_from_table(black_terminating_codes, code_word, code_size);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-14 23:08:26 -05:00
|
|
|
|
constexpr auto const ccitt_white = Color::NamedColor::White;
|
|
|
|
|
constexpr auto const ccitt_black = Color::NamedColor::Black;
|
|
|
|
|
|
2024-01-14 19:15:01 -05:00
|
|
|
|
ErrorOr<void> decode_single_ccitt3_1d_line(BigEndianInputBitStream& input_bit_stream, BigEndianOutputBitStream& decoded_bits, u32 image_width)
|
LibGfx/TIFF: Add support for images with CCITT3 1D compression
This compression (tag Compression=2) is not very popular on its own, but
a base to implement CCITT3 2D and CCITT4 compressions.
As the format has no real benefits, it is quite hard to find an app that
accepts tho encode that for you. So I used the following program that
calls `libtiff` directly:
```cpp
#include <vector>
#include <cstdlib>
#include <iostream>
#include <tiffio.h>
// An array containing 0 and 1 of length width * height.
extern std::vector<uint8_t> array;
int main() {
// From: https://stackoverflow.com/a/34257789
TIFF *image = TIFFOpen("input.tif", "w");
int const width = 400;
int const height = 300;
TIFFSetField(image, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(image, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(image, TIFFTAG_PHOTOMETRIC, 0);
TIFFSetField(image, TIFFTAG_COMPRESSION, COMPRESSION_CCITTRLE);
TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, 1);
TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField(image, TIFFTAG_ROWSPERSTRIP, 1);
std::vector<uint8_t> scan_line(width / 8 + 8, 0);
int count = 0;
for (int i = 0; i < height; i++) {
std::fill(scan_line.begin(), scan_line.end(), 0);
for (int x = 0; x < width; ++x) {
uint8_t eight_pixels = scan_line.at(x / 8);
eight_pixels = eight_pixels << 1;
eight_pixels |= !array.at(i * width + x);
scan_line.at(x / 8) = eight_pixels;
}
int bytes = int(width / 8.0 + 0.5);
if (TIFFWriteScanline(image, scan_line.data(), i, bytes) != 1)
std::cerr << "Something went wrong\n";
}
TIFFClose(image);
}
```
2023-11-25 16:34:56 -05:00
|
|
|
|
{
|
2024-02-11 23:14:44 -05:00
|
|
|
|
// We always flip the color when entering the loop, so let's initialize the
|
|
|
|
|
// color with black to make the first marker actually be white.
|
|
|
|
|
Color current_color { ccitt_black };
|
2024-01-14 19:15:01 -05:00
|
|
|
|
u32 run_length = 0;
|
LibGfx/TIFF: Add support for images with CCITT3 1D compression
This compression (tag Compression=2) is not very popular on its own, but
a base to implement CCITT3 2D and CCITT4 compressions.
As the format has no real benefits, it is quite hard to find an app that
accepts tho encode that for you. So I used the following program that
calls `libtiff` directly:
```cpp
#include <vector>
#include <cstdlib>
#include <iostream>
#include <tiffio.h>
// An array containing 0 and 1 of length width * height.
extern std::vector<uint8_t> array;
int main() {
// From: https://stackoverflow.com/a/34257789
TIFF *image = TIFFOpen("input.tif", "w");
int const width = 400;
int const height = 300;
TIFFSetField(image, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(image, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(image, TIFFTAG_PHOTOMETRIC, 0);
TIFFSetField(image, TIFFTAG_COMPRESSION, COMPRESSION_CCITTRLE);
TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, 1);
TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField(image, TIFFTAG_ROWSPERSTRIP, 1);
std::vector<uint8_t> scan_line(width / 8 + 8, 0);
int count = 0;
for (int i = 0; i < height; i++) {
std::fill(scan_line.begin(), scan_line.end(), 0);
for (int x = 0; x < width; ++x) {
uint8_t eight_pixels = scan_line.at(x / 8);
eight_pixels = eight_pixels << 1;
eight_pixels |= !array.at(i * width + x);
scan_line.at(x / 8) = eight_pixels;
}
int bytes = int(width / 8.0 + 0.5);
if (TIFFWriteScanline(image, scan_line.data(), i, bytes) != 1)
std::cerr << "Something went wrong\n";
}
TIFFClose(image);
}
```
2023-11-25 16:34:56 -05:00
|
|
|
|
u32 column = 0;
|
|
|
|
|
|
2024-02-15 00:21:50 -05:00
|
|
|
|
while (column < image_width) {
|
LibGfx/TIFF: Add support for images with CCITT3 1D compression
This compression (tag Compression=2) is not very popular on its own, but
a base to implement CCITT3 2D and CCITT4 compressions.
As the format has no real benefits, it is quite hard to find an app that
accepts tho encode that for you. So I used the following program that
calls `libtiff` directly:
```cpp
#include <vector>
#include <cstdlib>
#include <iostream>
#include <tiffio.h>
// An array containing 0 and 1 of length width * height.
extern std::vector<uint8_t> array;
int main() {
// From: https://stackoverflow.com/a/34257789
TIFF *image = TIFFOpen("input.tif", "w");
int const width = 400;
int const height = 300;
TIFFSetField(image, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(image, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(image, TIFFTAG_PHOTOMETRIC, 0);
TIFFSetField(image, TIFFTAG_COMPRESSION, COMPRESSION_CCITTRLE);
TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, 1);
TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField(image, TIFFTAG_ROWSPERSTRIP, 1);
std::vector<uint8_t> scan_line(width / 8 + 8, 0);
int count = 0;
for (int i = 0; i < height; i++) {
std::fill(scan_line.begin(), scan_line.end(), 0);
for (int x = 0; x < width; ++x) {
uint8_t eight_pixels = scan_line.at(x / 8);
eight_pixels = eight_pixels << 1;
eight_pixels |= !array.at(i * width + x);
scan_line.at(x / 8) = eight_pixels;
}
int bytes = int(width / 8.0 + 0.5);
if (TIFFWriteScanline(image, scan_line.data(), i, bytes) != 1)
std::cerr << "Something went wrong\n";
}
TIFFClose(image);
}
```
2023-11-25 16:34:56 -05:00
|
|
|
|
if (run_length > 0) {
|
|
|
|
|
run_length--;
|
2024-01-14 19:15:01 -05:00
|
|
|
|
TRY(decoded_bits.write_bits(current_color == ccitt_white ? 0u : 1u, 1));
|
LibGfx/TIFF: Add support for images with CCITT3 1D compression
This compression (tag Compression=2) is not very popular on its own, but
a base to implement CCITT3 2D and CCITT4 compressions.
As the format has no real benefits, it is quite hard to find an app that
accepts tho encode that for you. So I used the following program that
calls `libtiff` directly:
```cpp
#include <vector>
#include <cstdlib>
#include <iostream>
#include <tiffio.h>
// An array containing 0 and 1 of length width * height.
extern std::vector<uint8_t> array;
int main() {
// From: https://stackoverflow.com/a/34257789
TIFF *image = TIFFOpen("input.tif", "w");
int const width = 400;
int const height = 300;
TIFFSetField(image, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(image, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(image, TIFFTAG_PHOTOMETRIC, 0);
TIFFSetField(image, TIFFTAG_COMPRESSION, COMPRESSION_CCITTRLE);
TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, 1);
TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField(image, TIFFTAG_ROWSPERSTRIP, 1);
std::vector<uint8_t> scan_line(width / 8 + 8, 0);
int count = 0;
for (int i = 0; i < height; i++) {
std::fill(scan_line.begin(), scan_line.end(), 0);
for (int x = 0; x < width; ++x) {
uint8_t eight_pixels = scan_line.at(x / 8);
eight_pixels = eight_pixels << 1;
eight_pixels |= !array.at(i * width + x);
scan_line.at(x / 8) = eight_pixels;
}
int bytes = int(width / 8.0 + 0.5);
if (TIFFWriteScanline(image, scan_line.data(), i, bytes) != 1)
std::cerr << "Something went wrong\n";
}
TIFFClose(image);
}
```
2023-11-25 16:34:56 -05:00
|
|
|
|
|
|
|
|
|
++column;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
current_color = current_color == ccitt_white ? ccitt_black : ccitt_white;
|
|
|
|
|
|
|
|
|
|
u8 size {};
|
|
|
|
|
u16 potential_code {};
|
|
|
|
|
while (size < 14) {
|
|
|
|
|
potential_code <<= 1;
|
2024-01-14 19:15:01 -05:00
|
|
|
|
potential_code |= TRY(input_bit_stream.read_bit());
|
LibGfx/TIFF: Add support for images with CCITT3 1D compression
This compression (tag Compression=2) is not very popular on its own, but
a base to implement CCITT3 2D and CCITT4 compressions.
As the format has no real benefits, it is quite hard to find an app that
accepts tho encode that for you. So I used the following program that
calls `libtiff` directly:
```cpp
#include <vector>
#include <cstdlib>
#include <iostream>
#include <tiffio.h>
// An array containing 0 and 1 of length width * height.
extern std::vector<uint8_t> array;
int main() {
// From: https://stackoverflow.com/a/34257789
TIFF *image = TIFFOpen("input.tif", "w");
int const width = 400;
int const height = 300;
TIFFSetField(image, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(image, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(image, TIFFTAG_PHOTOMETRIC, 0);
TIFFSetField(image, TIFFTAG_COMPRESSION, COMPRESSION_CCITTRLE);
TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, 1);
TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField(image, TIFFTAG_ROWSPERSTRIP, 1);
std::vector<uint8_t> scan_line(width / 8 + 8, 0);
int count = 0;
for (int i = 0; i < height; i++) {
std::fill(scan_line.begin(), scan_line.end(), 0);
for (int x = 0; x < width; ++x) {
uint8_t eight_pixels = scan_line.at(x / 8);
eight_pixels = eight_pixels << 1;
eight_pixels |= !array.at(i * width + x);
scan_line.at(x / 8) = eight_pixels;
}
int bytes = int(width / 8.0 + 0.5);
if (TIFFWriteScanline(image, scan_line.data(), i, bytes) != 1)
std::cerr << "Something went wrong\n";
}
TIFFClose(image);
}
```
2023-11-25 16:34:56 -05:00
|
|
|
|
size++;
|
|
|
|
|
|
|
|
|
|
if (auto const maybe_markup = get_markup_code(current_color, potential_code, size); maybe_markup.has_value()) {
|
|
|
|
|
run_length += maybe_markup->run_length;
|
|
|
|
|
// OK, let's reset the loop to read a terminal code now
|
|
|
|
|
size = 0;
|
|
|
|
|
potential_code = 0;
|
|
|
|
|
} else if (auto const maybe_terminal = get_terminal_code(current_color, potential_code, size); maybe_terminal.has_value()) {
|
|
|
|
|
run_length += maybe_terminal->run_length;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (size == 14)
|
|
|
|
|
return Error::from_string_literal("TIFFImageDecoderPlugin: Invalid CCITT code");
|
|
|
|
|
|
|
|
|
|
if (column + run_length > image_width)
|
|
|
|
|
return Error::from_string_literal("TIFFImageDecoderPlugin: CCITT codes encode for more that a line");
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-10 00:57:25 -05:00
|
|
|
|
TRY(decoded_bits.align_to_byte_boundary());
|
|
|
|
|
|
2024-01-14 19:15:01 -05:00
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-18 00:29:46 -05:00
|
|
|
|
static ErrorOr<void> read_eol(BigEndianInputBitStream& bit_stream, Group3Options::UseFillBits use_fill_bits)
|
2024-01-15 00:56:21 -05:00
|
|
|
|
{
|
|
|
|
|
constexpr u16 EOL = 0b0000'0000'0001;
|
2024-01-18 00:29:46 -05:00
|
|
|
|
|
|
|
|
|
if (use_fill_bits == Group3Options::UseFillBits::Yes) {
|
|
|
|
|
// TIFF specification, description of the T4Options tag:
|
|
|
|
|
// "Fill bits have been added as necessary before EOL codes such that
|
|
|
|
|
// EOL always ends on a byte boundary, thus ensuring an EOL-sequence of 1 byte
|
|
|
|
|
// preceded by a zero nibble: xxxx-0000 0000-0001."
|
|
|
|
|
auto const to_skip = (12 + bit_stream.bits_until_next_byte_boundary()) % 8;
|
|
|
|
|
TRY(bit_stream.read_bits(to_skip));
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-15 00:56:21 -05:00
|
|
|
|
auto const read = TRY(bit_stream.read_bits<u16>(12));
|
|
|
|
|
if (read != EOL)
|
|
|
|
|
return Error::from_string_literal("CCITTDecoder: Invalid EndOfLine code");
|
|
|
|
|
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-14 19:15:01 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ErrorOr<ByteBuffer> decode_ccitt_rle(ReadonlyBytes bytes, u32 image_width, u32 image_height)
|
|
|
|
|
{
|
|
|
|
|
auto strip_stream = make<FixedMemoryStream>(bytes);
|
|
|
|
|
auto bit_stream = make<BigEndianInputBitStream>(MaybeOwned<Stream>(*strip_stream));
|
|
|
|
|
|
2024-02-10 00:57:25 -05:00
|
|
|
|
// Note: We put image_height extra-space to handle at most one alignment to byte boundary per line.
|
|
|
|
|
ByteBuffer decoded_bytes = TRY(ByteBuffer::create_zeroed(ceil_div(image_width * image_height, 8) + image_height));
|
2024-01-14 19:15:01 -05:00
|
|
|
|
auto output_stream = make<FixedMemoryStream>(decoded_bytes.bytes());
|
|
|
|
|
auto decoded_bits = make<BigEndianOutputBitStream>(MaybeOwned<Stream>(*output_stream));
|
|
|
|
|
|
|
|
|
|
while (!bit_stream->is_eof()) {
|
|
|
|
|
TRY(decode_single_ccitt3_1d_line(*bit_stream, *decoded_bits, image_width));
|
|
|
|
|
|
|
|
|
|
bit_stream->align_to_byte_boundary();
|
|
|
|
|
}
|
|
|
|
|
|
LibGfx/TIFF: Add support for images with CCITT3 1D compression
This compression (tag Compression=2) is not very popular on its own, but
a base to implement CCITT3 2D and CCITT4 compressions.
As the format has no real benefits, it is quite hard to find an app that
accepts tho encode that for you. So I used the following program that
calls `libtiff` directly:
```cpp
#include <vector>
#include <cstdlib>
#include <iostream>
#include <tiffio.h>
// An array containing 0 and 1 of length width * height.
extern std::vector<uint8_t> array;
int main() {
// From: https://stackoverflow.com/a/34257789
TIFF *image = TIFFOpen("input.tif", "w");
int const width = 400;
int const height = 300;
TIFFSetField(image, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(image, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(image, TIFFTAG_PHOTOMETRIC, 0);
TIFFSetField(image, TIFFTAG_COMPRESSION, COMPRESSION_CCITTRLE);
TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, 1);
TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField(image, TIFFTAG_ROWSPERSTRIP, 1);
std::vector<uint8_t> scan_line(width / 8 + 8, 0);
int count = 0;
for (int i = 0; i < height; i++) {
std::fill(scan_line.begin(), scan_line.end(), 0);
for (int x = 0; x < width; ++x) {
uint8_t eight_pixels = scan_line.at(x / 8);
eight_pixels = eight_pixels << 1;
eight_pixels |= !array.at(i * width + x);
scan_line.at(x / 8) = eight_pixels;
}
int bytes = int(width / 8.0 + 0.5);
if (TIFFWriteScanline(image, scan_line.data(), i, bytes) != 1)
std::cerr << "Something went wrong\n";
}
TIFFClose(image);
}
```
2023-11-25 16:34:56 -05:00
|
|
|
|
return decoded_bytes;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-15 00:56:21 -05:00
|
|
|
|
ErrorOr<ByteBuffer> decode_ccitt_group3(ReadonlyBytes bytes, u32 image_width, u32 image_height, Group3Options const& options)
|
|
|
|
|
{
|
|
|
|
|
auto strip_stream = make<FixedMemoryStream>(bytes);
|
|
|
|
|
auto bit_stream = make<BigEndianInputBitStream>(MaybeOwned<Stream>(*strip_stream));
|
|
|
|
|
|
2024-02-10 00:57:25 -05:00
|
|
|
|
// Note: We put image_height extra-space to handle at most one alignment to byte boundary per line.
|
|
|
|
|
ByteBuffer decoded_bytes = TRY(ByteBuffer::create_zeroed(ceil_div(image_width * image_height, 8) + image_height));
|
2024-01-15 00:56:21 -05:00
|
|
|
|
auto output_stream = make<FixedMemoryStream>(decoded_bytes.bytes());
|
|
|
|
|
auto decoded_bits = make<BigEndianOutputBitStream>(MaybeOwned<Stream>(*output_stream));
|
|
|
|
|
|
|
|
|
|
if (options.dimensions == Group3Options::Mode::OneDimension) {
|
|
|
|
|
// 4.1.2 End-of-line (EOL)
|
|
|
|
|
// This code word follows each line of data. It is a unique code word that can never be found within a
|
|
|
|
|
// valid line of data; therefore, resynchronization after an error burst is possible.
|
|
|
|
|
// In addition, this signal will occur prior to the first data line of a page.
|
|
|
|
|
// ---
|
|
|
|
|
// NOTE: For whatever reason, the last EOL doesn't seem to be included
|
|
|
|
|
|
|
|
|
|
for (u32 i = 0; i < image_height; ++i) {
|
2024-01-18 00:29:46 -05:00
|
|
|
|
TRY(read_eol(*bit_stream, options.use_fill_bits));
|
2024-01-15 00:56:21 -05:00
|
|
|
|
TRY(decode_single_ccitt3_1d_line(*bit_stream, *decoded_bits, image_width));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return decoded_bytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Error::from_string_literal("CCITT3 2D is not implemented yet :^(");
|
|
|
|
|
}
|
|
|
|
|
|
LibGfx/TIFF: Add support for images with CCITT3 1D compression
This compression (tag Compression=2) is not very popular on its own, but
a base to implement CCITT3 2D and CCITT4 compressions.
As the format has no real benefits, it is quite hard to find an app that
accepts tho encode that for you. So I used the following program that
calls `libtiff` directly:
```cpp
#include <vector>
#include <cstdlib>
#include <iostream>
#include <tiffio.h>
// An array containing 0 and 1 of length width * height.
extern std::vector<uint8_t> array;
int main() {
// From: https://stackoverflow.com/a/34257789
TIFF *image = TIFFOpen("input.tif", "w");
int const width = 400;
int const height = 300;
TIFFSetField(image, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(image, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(image, TIFFTAG_PHOTOMETRIC, 0);
TIFFSetField(image, TIFFTAG_COMPRESSION, COMPRESSION_CCITTRLE);
TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, 1);
TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField(image, TIFFTAG_ROWSPERSTRIP, 1);
std::vector<uint8_t> scan_line(width / 8 + 8, 0);
int count = 0;
for (int i = 0; i < height; i++) {
std::fill(scan_line.begin(), scan_line.end(), 0);
for (int x = 0; x < width; ++x) {
uint8_t eight_pixels = scan_line.at(x / 8);
eight_pixels = eight_pixels << 1;
eight_pixels |= !array.at(i * width + x);
scan_line.at(x / 8) = eight_pixels;
}
int bytes = int(width / 8.0 + 0.5);
if (TIFFWriteScanline(image, scan_line.data(), i, bytes) != 1)
std::cerr << "Something went wrong\n";
}
TIFFClose(image);
}
```
2023-11-25 16:34:56 -05:00
|
|
|
|
}
|