cpp
复制代码
#if 1
#ifndef JBIG2_DECODER_HPP
#define JBIG2_DECODER_HPP
#include <cstdint>
#include <vector>
#include <memory>
#include <string>
#include <stdexcept>
// jbig2dec 头文件
extern "C" {
#include <jbig2.h>
}
/**
* @brief JBIG2解码异常类
*/
class JBIG2DecoderException : public std::runtime_error {
public:
explicit JBIG2DecoderException(const std::string& msg)
: std::runtime_error(msg) {}
explicit JBIG2DecoderException(const char* msg)
: std::runtime_error(msg) {}
};
/**
* @brief JBIG2图像信息结构
*/
struct JBIG2ImageInfo {
int width = 0; // 图像宽度(像素)
int height = 0; // 图像高度(像素)
int xres = 72; // 水平分辨率(DPI)
int yres = 72; // 垂直分辨率(DPI)
int bitsPerPixel = 1; // 每像素位数(JBIG2总是1)
bool isMultiPage = false;// 是否为多页文档
int pageCount = 0; // 总页数
};
/**
* @brief JBIG2解码器类
*
* 提供JBIG2图像的解码功能,支持单页和多页文档
*/
class JBIG2Decoder {
public:
/**
* @brief 构造函数
* @param invertPixels 是否反转像素值(黑变白,白变黑)
* JBIG2标准中0通常表示白,1表示黑
* 但某些系统可能相反
*/
explicit JBIG2Decoder(bool invertPixels = false);
/**
* @brief 析构函数
*/
~JBIG2Decoder();
// 禁用拷贝构造和赋值
JBIG2Decoder(const JBIG2Decoder&) = delete;
JBIG2Decoder& operator=(const JBIG2Decoder&) = delete;
// 移动构造和赋值
JBIG2Decoder(JBIG2Decoder&& other) noexcept;
JBIG2Decoder& operator=(JBIG2Decoder&& other) noexcept;
/**
* @brief 从内存缓冲区解码JBIG2数据
* @param data JBIG2数据指针
* @param length 数据长度(字节)
* @return 解码的页面数
* @throws JBIG2DecoderException 解码失败时抛出
*/
int decode(const uint8_t* data, size_t length);
/**
* @brief 从文件解码JBIG2数据
* @param filename 文件名
* @return 解码的页面数
* @throws JBIG2DecoderException 解码失败时抛出
*/
int decodeFromFile(const std::string& filename);
/**
* @brief 获取文档总页数
* @return 页数,如果未解码返回0
*/
int getPageCount() const;
/**
* @brief 获取指定页的图像信息
* @param pageNum 页码(0-based)
* @return 图像信息结构
* @throws JBIG2DecoderException 页码无效或未解码时抛出
*/
JBIG2ImageInfo getImageInfo(int pageNum = 0) const;
/**
* @brief 获取指定页的位图数据
*
* 返回的位图数据为8位灰度格式(0=黑, 255=白)
* 每行数据连续存储,无填充字节
*
* @param pageNum 页码(0-based)
* @return 8位灰度位图数据向量
* @throws JBIG2DecoderException 页码无效或未解码时抛出
*/
std::vector<uint8_t> getBitmapData(int pageNum = 0) const;
/**
* @brief 获取指定页的1位位图数据
*
* 返回的位图数据为1位格式,每字节存储8个像素
* 每个像素的MSB在前(bit 7 = 第一个像素)
*
* @param pageNum 页码(0-based)
* @return 1位位图数据向量
* @throws JBIG2DecoderException 页码无效或未解码时抛出
*/
std::vector<uint8_t> getBitmapData1Bit(int pageNum = 0) const;
/**
* @brief 保存指定页为PNG文件
*
* 需要编译时链接libpng库,使用条件编译
*
* @param pageNum 页码(0-based)
* @param filename 输出文件名
* @return 成功返回true
* @throws JBIG2DecoderException 保存失败时抛出
*/
bool saveAsPNG(int pageNum, const std::string& filename) const;
/**
* @brief 保存指定页为PBM(Portable BitMap)文件
*
* PBM是简单的二值图像格式,许多图像查看器都支持
*
* @param pageNum 页码(0-based)
* @param filename 输出文件名
* @return 成功返回true
* @throws JBIG2DecoderException 保存失败时抛出
*/
bool saveAsPBM(int pageNum, const std::string& filename) const;
/**
* @brief 清除所有解码数据
*/
void clear();
/**
* @brief 检查是否已解码数据
* @return 已解码返回true
*/
bool isDecoded() const;
/**
* @brief 设置像素反转选项
* @param invert 是否反转像素值
*/
void setInvertPixels(bool invert);
/**
* @brief 获取像素反转选项
* @return 当前反转设置
*/
bool getInvertPixels() const;
/**
* @brief 获取最后一条错误信息
* @return 错误信息字符串
*/
std::string getLastError() const;
private:
// jbig2dec上下文结构
struct JBIG2Context {
Jbig2Ctx* ctx = nullptr;
~JBIG2Context() {
if (ctx) jbig2_ctx_free(ctx);
}
};
// 解码后的页面数据
struct PageData {
Jbig2Image* image = nullptr;
JBIG2ImageInfo info;
Jbig2Ctx* ctx = nullptr; // 添加上下文指针
PageData() : image(nullptr), ctx(nullptr) {}
PageData(Jbig2Image* img, Jbig2Ctx* context)
: image(img), ctx(context) {
}
~PageData() {
if (image && ctx) {
jbig2_release_page(ctx, image);
}
}
// 禁用拷贝
PageData(const PageData&) = delete;
PageData& operator=(const PageData&) = delete;
// 允许移动
PageData(PageData&& other) noexcept
: image(other.image), info(std::move(other.info)), ctx(other.ctx) {
other.image = nullptr;
other.ctx = nullptr;
}
PageData& operator=(PageData&& other) noexcept {
if (this != &other) {
if (image && ctx) {
jbig2_release_page(ctx, image);
}
image = other.image;
info = std::move(other.info);
ctx = other.ctx;
other.image = nullptr;
other.ctx = nullptr;
}
return *this;
}
};
// 静态回调函数
static void errorCallback(void* data, const char* msg,
Jbig2Severity severity, uint32_t seg_idx);
// 内部解码方法
void decodeInternal(const uint8_t* data, size_t length);
// 收集所有页面
void collectAllPages();
// 转换像素数据
std::vector<uint8_t> convertTo8Bit(const PageData& page) const;
std::vector<uint8_t> convertTo1Bit(const PageData& page) const;
// 成员变量
std::unique_ptr<JBIG2Context> context_;
std::vector<PageData> pages_; // 改为存储对象而不是指针
bool invertPixels_;
std::string lastError_;
bool isDecoded_;
};
#endif // JBIG2_DECODER_HPP
#endif
cpp
复制代码
#if 1
#include "JBIG2Decoder.h"
#include <fstream>
#include <sstream>
#include <cstring>
#include <algorithm>
#define USE_LIBPNG
// PNG保存功能需要libpng库
#ifdef USE_LIBPNG
#include <png.h>
#endif
JBIG2Decoder::JBIG2Decoder(bool invertPixels)
: invertPixels_(invertPixels), isDecoded_(false) {
}
JBIG2Decoder::~JBIG2Decoder() {
clear();
}
JBIG2Decoder::JBIG2Decoder(JBIG2Decoder&& other) noexcept
: context_(std::move(other.context_)),
pages_(std::move(other.pages_)),
invertPixels_(other.invertPixels_),
lastError_(std::move(other.lastError_)),
isDecoded_(other.isDecoded_) {
other.isDecoded_ = false;
}
JBIG2Decoder& JBIG2Decoder::operator=(JBIG2Decoder&& other) noexcept {
if (this != &other) {
clear();
context_ = std::move(other.context_);
pages_ = std::move(other.pages_);
invertPixels_ = other.invertPixels_;
lastError_ = std::move(other.lastError_);
isDecoded_ = other.isDecoded_;
other.isDecoded_ = false;
}
return *this;
}
void JBIG2Decoder::errorCallback(void* data, const char* msg,
Jbig2Severity severity, uint32_t seg_idx) {
JBIG2Decoder* decoder = static_cast<JBIG2Decoder*>(data);
if (!decoder) return;
std::ostringstream oss;
switch (severity) {
case JBIG2_SEVERITY_FATAL:
oss << "FATAL error: ";
break;
case JBIG2_SEVERITY_WARNING:
oss << "Warning: ";
break;
case JBIG2_SEVERITY_INFO:
oss << "Info: ";
break;
case JBIG2_SEVERITY_DEBUG:
oss << "Debug: ";
break;
default:
oss << "Unknown: ";
break;
}
oss << msg << " (segment " << seg_idx << ")";
decoder->lastError_ = oss.str();
// 致命错误需要抛出异常
if (severity == JBIG2_SEVERITY_FATAL) {
throw JBIG2DecoderException(oss.str());
}
}
int JBIG2Decoder::decode(const uint8_t* data, size_t length) {
clear();
try {
decodeInternal(data, length);
collectAllPages();
isDecoded_ = true;
return static_cast<int>(pages_.size());
}
catch (const JBIG2DecoderException&) {
clear();
throw;
}
catch (const std::exception& e) {
clear();
throw JBIG2DecoderException(std::string("Unexpected error: ") + e.what());
}
}
int JBIG2Decoder::decodeFromFile(const std::string& filename) {
// 读取文件
std::ifstream file(filename, std::ios::binary | std::ios::ate);
if (!file) {
throw JBIG2DecoderException("Cannot open file: " + filename);
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<uint8_t> buffer(size);
if (!file.read(reinterpret_cast<char*>(buffer.data()), size)) {
throw JBIG2DecoderException("Cannot read file: " + filename);
}
return decode(buffer.data(), static_cast<size_t>(size));
}
void JBIG2Decoder::decodeInternal(const uint8_t* data, size_t length) {
// 创建上下文
context_ = std::make_unique<JBIG2Context>();
// 使用默认分配器,使用默认选项0而不是JBIG2_OPTIONS_EMBEDDED
context_->ctx = jbig2_ctx_new(nullptr, (Jbig2Options)0,
nullptr, errorCallback, this);
if (!context_->ctx) {
throw JBIG2DecoderException("Failed to create JBIG2 context");
}
// 输入数据
if (jbig2_data_in(context_->ctx, data, length) < 0) {
throw JBIG2DecoderException("Failed to process JBIG2 data");
}
// 完成页面
if (jbig2_complete_page(context_->ctx) < 0) {
std::string error = "Failed to complete JBIG2 page";
if (!lastError_.empty()) {
error += ": " + lastError_;
}
throw JBIG2DecoderException(error);
}
}
void JBIG2Decoder::collectAllPages() {
if (!context_ || !context_->ctx) {
return;
}
// 收集所有页面
Jbig2Image* image;
int pageNum = 0;
while ((image = jbig2_page_out(context_->ctx)) != nullptr) {
// 创建PageData对象,传入正确的上下文指针
PageData pageData(image, context_->ctx);
// 填充图像信息
pageData.info.width = image->width;
pageData.info.height = image->height;
pageData.info.xres = 72; // JBIG2通常不包含分辨率信息
pageData.info.yres = 72;
pageData.info.bitsPerPixel = 1;
pageData.info.pageCount = ++pageNum;
pages_.push_back(std::move(pageData));
}
// 更新多页信息
if (!pages_.empty()) {
bool isMultiPage = pages_.size() > 1;
for (auto& page : pages_) {
page.info.isMultiPage = isMultiPage;
page.info.pageCount = static_cast<int>(pages_.size());
}
}
}
int JBIG2Decoder::getPageCount() const {
return static_cast<int>(pages_.size());
}
JBIG2ImageInfo JBIG2Decoder::getImageInfo(int pageNum) const {
if (!isDecoded_ || pages_.empty()) {
throw JBIG2DecoderException("No JBIG2 data decoded");
}
if (pageNum < 0 || pageNum >= static_cast<int>(pages_.size())) {
throw JBIG2DecoderException("Invalid page number");
}
return pages_[pageNum].info;
}
std::vector<uint8_t> JBIG2Decoder::getBitmapData(int pageNum) const {
if (!isDecoded_ || pages_.empty()) {
throw JBIG2DecoderException("No JBIG2 data decoded");
}
if (pageNum < 0 || pageNum >= static_cast<int>(pages_.size())) {
throw JBIG2DecoderException("Invalid page number");
}
return convertTo8Bit(pages_[pageNum]);
}
std::vector<uint8_t> JBIG2Decoder::getBitmapData1Bit(int pageNum) const {
if (!isDecoded_ || pages_.empty()) {
throw JBIG2DecoderException("No JBIG2 data decoded");
}
if (pageNum < 0 || pageNum >= static_cast<int>(pages_.size())) {
throw JBIG2DecoderException("Invalid page number");
}
return convertTo1Bit(pages_[pageNum]);
}
//
//std::vector<uint8_t> JBIG2Decoder::convertTo8Bit(const PageData& page) const {
// const Jbig2Image* image = page.image;
// if (!image || !image->data) {
// return {};
// }
//
// int width = image->width;
// int height = image->height;
// int stride = image->stride;
//
// std::vector<uint8_t> result(width * height);
//
// for (int y = 0; y < height; ++y) {
// const uint8_t* src = image->data + y * stride;
// uint8_t* dst = result.data() + y * width;
//
// for (int x = 0; x < width; ++x) {
// int byteIndex = x / 8;
// int bitIndex = 7 - (x % 8); // JBIG2使用MSB在前
//
// bool bit = (src[byteIndex] >> bitIndex) & 1;
//
// // 应用像素反转
// if (invertPixels_) {
// bit = !bit;
// }
//
// dst[x] = bit ? 255 : 0; // 1=白(255), 0=黑(0)
// }
// }
//
// return result;
//}
//
//std::vector<uint8_t> JBIG2Decoder::convertTo1Bit(const PageData& page) const {
// const Jbig2Image* image = page.image;
// if (!image || !image->data) {
// return {};
// }
//
// int width = image->width;
// int height = image->height;
// int stride = image->stride;
//
// // 计算1位格式的stride(向上取整到字节)
// int dstStride = (width + 7) / 8;
// std::vector<uint8_t> result(dstStride * height, 0);
//
// for (int y = 0; y < height; ++y) {
// const uint8_t* src = image->data + y * stride;
// uint8_t* dst = result.data() + y * dstStride;
//
// for (int x = 0; x < width; ++x) {
// int srcByteIndex = x / 8;
// int srcBitIndex = 7 - (x % 8);
//
// int dstByteIndex = x / 8;
// int dstBitIndex = 7 - (x % 8);
//
// bool bit = (src[srcByteIndex] >> srcBitIndex) & 1;
//
// // 应用像素反转
// if (invertPixels_) {
// bit = !bit;
// }
//
// if (bit) {
// dst[dstByteIndex] |= (1 << dstBitIndex);
// }
// }
// }
//
// return result;
//}
std::vector<uint8_t> JBIG2Decoder::convertTo8Bit(const PageData& page) const {
const Jbig2Image* image = page.image;
if (!image || !image->data) {
return {};
}
int width = image->width;
int height = image->height;
int stride = image->stride;
std::vector<uint8_t> result(width * height);
for (int y = 0; y < height; ++y) {
const uint8_t* src = image->data + y * stride;
uint8_t* dst = result.data() + y * width;
for (int x = 0; x < width; ++x) {
int byteIndex = x / 8;
int bitIndex = 7 - (x % 8); // JBIG2使用MSB在前
bool bit = (src[byteIndex] >> bitIndex) & 1;
// 修正:1=黑色(0), 0=白色(255)
// 然后根据invertPixels_选项决定是否反转
uint8_t pixelValue;
if (bit) { // 原始位为1,表示黑色
pixelValue = 0; // 黑色 = 0
}
else { // 原始位为0,表示白色
pixelValue = 255; // 白色 = 255
}
// 应用像素反转选项
if (invertPixels_) {
pixelValue = 255 - pixelValue; // 反转:0变255,255变0
}
dst[x] = pixelValue;
}
}
return result;
}
std::vector<uint8_t> JBIG2Decoder::convertTo1Bit(const PageData& page) const {
const Jbig2Image* image = page.image;
if (!image || !image->data) {
return {};
}
int width = image->width;
int height = image->height;
int stride = image->stride;
// 计算1位格式的stride(向上取整到字节)
int dstStride = (width + 7) / 8;
std::vector<uint8_t> result(dstStride * height, 0);
for (int y = 0; y < height; ++y) {
const uint8_t* src = image->data + y * stride;
uint8_t* dst = result.data() + y * dstStride;
for (int x = 0; x < width; ++x) {
int srcByteIndex = x / 8;
int srcBitIndex = 7 - (x % 8);
int dstByteIndex = x / 8;
int dstBitIndex = 7 - (x % 8);
bool bit = (src[srcByteIndex] >> srcBitIndex) & 1;
// 修正:1=黑色(位值1),0=白色(位值0)
bool outputBit = bit; // 默认:1=黑,0=白
// 应用像素反转选项
if (invertPixels_) {
outputBit = !bit; // 反转:黑变白,白变黑
}
if (outputBit) { // 1 = 黑色
dst[dstByteIndex] |= (1 << dstBitIndex);
}
// 注意:白色(0)不需要设置,因为我们已经初始化为0
}
}
return result;
}
bool JBIG2Decoder::saveAsPNG(int pageNum, const std::string& filename) const {
#ifdef USE_LIBPNG
if (!isDecoded_ || pages_.empty()) {
throw JBIG2DecoderException("No JBIG2 data decoded");
}
if (pageNum < 0 || pageNum >= static_cast<int>(pages_.size())) {
throw JBIG2DecoderException("Invalid page number");
}
auto bitmap = getBitmapData(pageNum);
const auto& info = pages_[pageNum].info;
FILE* fp = nullptr;
#ifdef _WIN32
errno_t err = fopen_s(&fp, filename.c_str(), "wb");
if (err != 0) fp = nullptr;
#else
fp = fopen(filename.c_str(), "wb");
#endif
if (!fp) {
throw JBIG2DecoderException("Cannot create PNG file");
}
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
nullptr, nullptr, nullptr);
if (!png) {
fclose(fp);
throw JBIG2DecoderException("Failed to create PNG write structure");
}
png_infop info_ptr = png_create_info_struct(png);
if (!info_ptr) {
png_destroy_write_struct(&png, nullptr);
fclose(fp);
throw JBIG2DecoderException("Failed to create PNG info structure");
}
if (setjmp(png_jmpbuf(png))) {
png_destroy_write_struct(&png, &info_ptr);
fclose(fp);
throw JBIG2DecoderException("Error during PNG creation");
}
png_init_io(png, fp);
// 设置PNG头
png_set_IHDR(png, info_ptr, info.width, info.height,
8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_write_info(png, info_ptr);
// 写入行数据
std::vector<uint8_t*> rowPointers(info.height);
for (int y = 0; y < info.height; ++y) {
rowPointers[y] = bitmap.data() + y * info.width;
}
png_write_image(png, rowPointers.data());
png_write_end(png, nullptr);
// 清理
png_destroy_write_struct(&png, &info_ptr);
fclose(fp);
return true;
#else
throw JBIG2DecoderException("PNG support not compiled in. Define USE_LIBPNG and link with libpng.");
#endif
}
bool JBIG2Decoder::saveAsPBM(int pageNum, const std::string& filename) const {
if (!isDecoded_ || pages_.empty()) {
throw JBIG2DecoderException("No JBIG2 data decoded");
}
if (pageNum < 0 || pageNum >= static_cast<int>(pages_.size())) {
throw JBIG2DecoderException("Invalid page number");
}
auto bitmap = getBitmapData1Bit(pageNum);
const auto& info = pages_[pageNum].info;
std::ofstream file(filename, std::ios::binary);
if (!file) {
throw JBIG2DecoderException("Cannot create PBM file");
}
// PBM头:P4表示二进制PBM,宽度和高度
file << "P4\n" << info.width << " " << info.height << "\n";
// 写入位图数据
file.write(reinterpret_cast<const char*>(bitmap.data()), bitmap.size());
return file.good();
}
void JBIG2Decoder::clear() {
pages_.clear(); // PageData析构函数会自动释放图像
context_.reset();
lastError_.clear();
isDecoded_ = false;
}
bool JBIG2Decoder::isDecoded() const {
return isDecoded_;
}
void JBIG2Decoder::setInvertPixels(bool invert) {
invertPixels_ = invert;
}
bool JBIG2Decoder::getInvertPixels() const {
return invertPixels_;
}
std::string JBIG2Decoder::getLastError() const {
return lastError_;
}
#endif