jpeg2k jpeg2000 jp2图像的编码和解码封装

以下是针对 PDFCoreJPEG2000Manager 类的介绍大纲,采用技术文档的标准结构,突出核心功能和设计要点:


​PDFCoreJPEG2000Manager 模块技术文档大纲​

​1. 模块概述​
  • ​定位​​:专业级 JPEG2000 编解码功能封装
  • ​核心能力​​:
    • 高性能 JPEG2000 图像解码(支持 RGB/RGBA)
    • 可配置质量的图像编码(JP2/J2K/JPX)
    • 零内存拷贝的元数据提取
  • ​设计目标​​:
    • 兼容历史接口(CompressToJp2/GetJ2kImageInfo
    • 提供类型安全的现代 C++ API
    • 异常安全的资源管理
​2. 核心功能分解​
​2.1 图像解码​
  • 支持格式:J2K/JP2/JPT
  • 输出模式:
    • DecodeToRGB:强制 RGB/RGBA 输出
    • GetImageInfo:仅解析头部元数据(快 3-5 倍)
  • 特殊处理:
    • Alpha 通道白背景混合(可选)
    • 自动色彩空间转换(sRGB/YCC/Gray)
​2.2 图像编码​
  • 质量参数:1-100 可调(视觉无损阈值 80+)
  • 支持格式:
    • PDFCore_FS_JPEG2K_CODEC_JP2(推荐)
    • PDFCore_FS_JPEG2K_CODEC_J2K(低开销)
    • PDFCore_FS_JPEG2K_CODEC_JPX(扩展功能)
  • 内存布局要求:
    • RGB:连续 R-G-B 存储
    • RGBA:带预乘 Alpha
​2.3 元数据提取​
  • 关键信息:
    • 宽高/位深/色彩空间
    • 压缩方式签名
    • 样本符号标识
  • 性能优化:
    • 避免全图解码
    • 流式头部解析
​3. 关键设计决策​
  • ​资源管理​​:
    • RAII 自动释放 OpenJPEG 对象
    • 输出数据使用 std::vector 自动管理
  • ​错误处理​​:
    • 非法参数:std::invalid_argument
    • 编解码失败:std::runtime_error
    • 格式限制:强枚举类型约束
  • ​兼容性适配​​:
    • 保留旧版 C 风格接口
    • 提供枚举转换工具函数
​4. 典型使用场景​
// 场景1:快速获取图像信息
PDFCoreJPEG2000Manager manager;
uint32_t w, h; 
std::string colorSpace;
manager.GetImageInfo(j2kData, dataSize, w, h, ..., colorSpace);

// 场景2:高质量转码
auto rgbData = manager.DecodeToRGB(..., 4); // 输出RGBA
auto jp2Data = manager.EncodeToJPEG2000(rgbData.data(), w, h, 95);
​5. 性能优化建议​
  • ​解码优化​​:
    • 不需要像素数据时优先用 GetImageInfo
    • 明确指定 requestComponents 减少转换
  • ​编码优化​​:
    • 质量参数超过 90 时收益递减
    • 大图建议分块处理(>8K 分辨率)
​6. 限制与注意事项​
  • ​硬性限制​​:
    • 不支持渐进式解码(JPP)
    • 位深>8bit 需预处理
  • ​内存安全​​:
    • 输入数据必须有效至调用结束

    • 输出缓冲区预分配建议:

      std::vector output;
      output.reserve(width*height*3); // 防止多次扩容
      
​7. 扩展规划​
  • 待实现功能:
    • ROI(感兴趣区域)解码
    • 多线程编解码支持
    • ICC 色彩配置嵌入

该大纲可作为:

  1. API 使用手册的基础框架
  2. 开发人员快速上手指南
  3. 后续扩展的功能清单
    需要进一步展开任何章节可随时补充具体实现细节。

头文件

#ifndef __JPX_WRAP__
#define __JPX_WRAP__

#include 
#include 
#include 
#include 

/**
 * @file jpx_wrap.h
 * @brief JPEG2000 图像编解码功能封装
 * @note 本模块提供 JPEG2000 图像的压缩、解压缩及元数据提取功能
 * @warning 修改需谨慎,必须经过兼容性测试
 */

 // 原始函数声明(兼容旧接口)
void CompressToJp2(const std::vector<uint8_t>& source_image,
    const int& width,
    const int& height,
    const int& quality,
    const int& format,
    std::vector<uint8_t>& target_image);

int GetJ2kImageInfo(std::vector<uint8_t>& image_data,
    int& image_format,
    int& image_width,
    int& image_height,
    int& image_channels,
    int& image_bits_per_component,
    int& image_colorspace,
    bool white_background);

/**
 * @class PDFCoreJPEG2000Manager
 * @brief 增强版 JPEG2000 编解码管理器
 *
 * 提供完整的 JPEG2000 图像处理能力,包括:
 * - 图像解码(支持 RGB/RGBA 输出)
 * - 图像编码(支持质量参数控制)
 * - 元数据提取(不解码像素数据)
 * - 格式转换功能
 *
 * @version 2.1
 * @author sylar ding
 * @date 2025.7.9
 * @copyright 内部使用,禁止未经授权的修改 Maintained by sylar ding. Do not modify without permission
 */
class PDFCoreJPEG2000Manager {
public:
    /**
     * @enum PDFCore_FS_JPEG2K_CODEC_FORMAT
     * @brief JPEG2000 格式类型定义
     */
    enum class PDFCore_FS_JPEG2K_CODEC_FORMAT : int {
        PDFCore_FS_JPEG2K_CODEC_UNKNOWN = -1, ///< 未知格式标识
        PDFCore_FS_JPEG2K_CODEC_J2K = 0,      ///< 原始码流格式(全功能支持)
        PDFCore_FS_JPEG2K_CODEC_JP2 = 1,      ///< 标准 JP2 文件格式(全功能支持)
        PDFCore_FS_JPEG2K_CODEC_JPT = 2,      ///< JPT 传输流(仅解码)
        PDFCore_FS_JPEG2K_CODEC_JPX = 3,      ///< 扩展 JPX 格式(仅编码)
        PDFCore_FS_JPEG2K_CODEC_JPP = 4       ///< JPP 渐进流(预留)
    };

    ~PDFCoreJPEG2000Manager() = default;
    PDFCoreJPEG2000Manager() = default;

    /**
     * @brief 解码 JPEG2000 数据到标准 RGB 格式
     * @param j2kData 压缩数据指针(必须有效)
     * @param dataSize 数据长度(必须 >0)
     * @param[out] width 图像宽度(像素)
     * @param[out] height 图像高度(像素)
     * @param[out] componentCount 实际通道数(1/3/4)
     * @param[out] bitsPerSample 位深度(通常8/16)
     * @param[out] isSigned 样本符号标识
     * @param[out] colorSpace 色彩空间描述
     * @param[out] compression 压缩方式描述
     * @param requestComponents 输出通道请求(1=GRAY, 3=RGB, 4=RGBA)
     * @return 解码后的像素数据(连续存储)
     * @throws std::invalid_argument 参数无效
     * @throws std::runtime_error 解码失败
     *
     * @note 对于带Alpha通道的图像:
     * - 当requestComponents=3时会自动混合白色背景
     * - 输出缓冲区大小 = width*height*requestComponents
     */
    std::vector<uint8_t> DecodeToRGB(
        const uint8_t* j2kData,
        size_t dataSize,
        uint32_t& width,
        uint32_t& height,
        uint16_t& componentCount,
        uint8_t& bitsPerSample,
        bool& isSigned,
        std::string& colorSpace,
        std::string& compression,
        uint8_t requestComponents = 3
    );

    /**
     * @brief 快速获取图像元数据(不解码像素)
     * @param j2kData 压缩数据指针
     * @param dataSize 数据长度
     * @param[out] width 图像宽度
     * @param[out] height 图像高度
     * @param[out] componentCount 通道数
     * @param[out] bitsPerSample 位深度
     * @param[out] isSigned 符号标识
     * @param[out] colorSpace 色彩空间
     * @param[out] compression 压缩方式
     *
     * @note 此方法比DecodeToRGB快3-5倍
     * @warning 不验证像素数据有效性
     */
    void GetImageInfo(
        const uint8_t* j2kData,
        size_t dataSize,
        uint32_t& width,
        uint32_t& height,
        uint16_t& componentCount,
        uint8_t& bitsPerSample,
        bool& isSigned,
        std::string& colorSpace,
        std::string& compression
    );

    /**
     * @brief 编码RGB数据为JPEG2000格式
     * @param rgbData RGB像素数据(必须连续存储)
     * @param width 图像宽度(>0)
     * @param height 图像高度(>0)
     * @param quality 压缩质量(1-100)
     * @param format 输出格式(默认为JP2)
     * @return 压缩后的数据
     * @throws std::logic_error 编码过程错误
     *
     * @note 输入数据布局须为:
     * - 3通道:R0G0B0 R1G1B1...
     * - 4通道:R0G0B0A0 R1G1B1A1...
     */
    std::vector<uint8_t> EncodeToJPEG2000(
        const uint8_t* rgbData,
        int width,
        int height,
        int quality,
        PDFCore_FS_JPEG2K_CODEC_FORMAT format = PDFCore_FS_JPEG2K_CODEC_FORMAT::PDFCore_FS_JPEG2K_CODEC_JP2
    );
};

#endif /* __JPX_WRAP__ */

实现文件

//#ifndef __JPEG2k__
//#define __JPEG2k__

#include "../fx_libopenjpeg/libopenjpeg20/openjpeg.h"
#include"fxcodec/fx_codec_jpx_wrap.h"
#include 
#include 
#include 
#include 
#include

/// 定义默认内存流初始大小
constexpr size_t kDefaultMemStreamInitSize = 1024 * 16;

/**
 * @brief 流接口
 */
struct OpjStreamInterface {
	/**
	 * @brief 从流中读取指定长度的数据。
	 * @param p_buffer 数据缓冲区指针。
	 * @param p_nb_bytes 要读取的字节数。
	 * @return 实际读取的字节数。
	 */
	virtual OPJ_SIZE_T Read(void* p_buffer, OPJ_SIZE_T p_nb_bytes) const = 0;

	/**
	 * @brief 向流中写入指定长度的数据。
	 * @param p_buffer 数据缓冲区指针。
	 * @param p_nb_bytes 要写入的字节数。
	 * @return 实际写入的字节数。
	 */
	virtual OPJ_SIZE_T Write(void* p_buffer, OPJ_SIZE_T p_nb_bytes) = 0;

	/**
	 * @brief 设置流的游标到指定位置(相对于起始位置)。
	 * @param p_nb_bytes 相对于起始位置的偏移量。
	 * @return 成功返回 true,失败返回 false。
	 */
	virtual OPJ_BOOL Seek(OPJ_OFF_T p_nb_bytes) const = 0;

	/**
	 * @brief 在流中跳过指定数量的字节。
	 * @param p_nb_bytes 要跳过的字节数。
	 * @return 实际跳过的字节数。
	 */
	virtual OPJ_OFF_T Skip(OPJ_OFF_T p_nb_bytes) const = 0;

	/**
	 * @brief 返回流的长度。
	 * @return 流的长度。
	 */
	virtual OPJ_UINT64 StreamLength() const = 0;

	/**
	 * @brief 返回流内存数据地址。
	 * @return 内存数据地址。
	 */
	virtual uint8_t* StreamData() const = 0;

	/**
	 * @brief 关闭流。
	 */
	virtual void Close() = 0;

	/**
	 * @brief 判断流是否为只读流。
	 * @return 如果是只读流返回 true,否则返回 false。
	 */
	virtual OPJ_BOOL IsReadStream() const = 0;

	/// 析构函数
	virtual ~OpjStreamInterface() = default;
};

/**
 * @brief 抽象内存流类
 */
class OpjStreamMemAbstract : public OpjStreamInterface {
protected:
	/// 指向流起始位置的指针
	mutable uint8_t* start_;
	/// 指向流结束位置的指针
	mutable uint8_t* last_;
	/// 指向当前游标的指针
	mutable uint8_t* cursor_;

public:
	/**
	 * @brief 写入指定长度的数据到流中。
	 * @param p_buffer 数据缓冲区指针。
	 * @param p_nb_bytes 要写入的字节数。
	 * @return 实际写入的字节数。
	 */
	virtual OPJ_SIZE_T Write(void* p_buffer, OPJ_SIZE_T p_nb_bytes) override = 0;

	/**
	 * @brief 返回流内存数据地址。
	 * @return 内存数据地址。
	 */
	virtual uint8_t* StreamData() const override = 0;

	/**
	 * @brief 判断流是否为只读流。
	 * @return 如果是只读流返回 true,否则返回 false。
	 */
	virtual OPJ_BOOL IsReadStream() const override = 0;

	/**
	 * @brief 从流中读取指定长度的数据。
	 * @param p_buffer 数据缓冲区指针。
	 * @param p_nb_bytes 要读取的字节数。
	 * @return 实际读取的字节数。
	 */
	virtual OPJ_SIZE_T Read(void* p_buffer, OPJ_SIZE_T p_nb_bytes) const override = 0;

	/**
	 * @brief 设置流的游标到指定位置(相对于起始位置)。
	 * @param p_nb_bytes 相对于起始位置的偏移量。
	 * @return 成功返回 true,失败返回 false。
	 */
	virtual OPJ_BOOL Seek(OPJ_OFF_T p_nb_bytes) const override;

	/**
	 * @brief 在流中跳过指定数量的字节。
	 * @param p_nb_bytes 要跳过的字节数。
	 * @return 实际跳过的字节数。
	 */
	virtual OPJ_OFF_T Skip(OPJ_OFF_T p_nb_bytes) const override;

	/**
	 * @brief 返回流的长度。
	 * @return 流的长度。
	 */
	virtual OPJ_UINT64 StreamLength() const override;

	/**
	 * @brief 关闭流。
	 */
	virtual void Close() override {}

	/// 析构函数
	virtual ~OpjStreamMemAbstract() override = default;
};

/**
 * @brief 内存输出流类
 */
class OpjStreamMemOutput : public OpjStreamMemAbstract, private std::vector<uint8_t> {
private:
	/// 指向 vector 结尾的指针
	uint8_t* end_;
	using base = std::vector<uint8_t>;

public:
	/**
	 * @brief 默认构造函数。
	 */
	OpjStreamMemOutput();

	/**
	 * @brief 带初始化容量的构造函数。
	 * @param init_capacity 初始化容量。
	 */
	explicit OpjStreamMemOutput(size_t init_capacity);

	/**
	 * @brief 从流中读取指定长度的数据。
	 * @param p_buffer 数据缓冲区指针。
	 * @param p_nb_bytes 要读取的字节数。
	 * @return 实际读取的字节数。
	 */
	OPJ_SIZE_T Read(void* p_buffer, OPJ_SIZE_T p_nb_bytes) const override;

	/**
	 * @brief 向流中写入指定长度的数据。
	 * @param p_buffer 数据缓冲区指针。
	 * @param p_nb_bytes 要写入的字节数。
	 * @return 实际写入的字节数。
	 */
	OPJ_SIZE_T Write(void* p_buffer, OPJ_SIZE_T p_nb_bytes) override;

	/**
	 * @brief 返回流内存数据地址。
	 * @return 内存数据地址。
	 */
	uint8_t* StreamData() const override;

	/**
	 * @brief 判断流是否为只读流。
	 * @return 如果是只读流返回 true,否则返回 false。
	 */
	OPJ_BOOL IsReadStream() const override;

	/**
	 * @brief 关闭流。
	 */
	void Close() override;

	/**
	 * @brief 将输出的压缩数据封装为 vector 并返回。
	 * @return 包含压缩数据的 vector。
	 */
	std::vector<uint8_t> AsVector();
};

/**
 * @brief 内存输入流类
 * 实现了 OpjStreamInterface 中的 read, write, stream_data, is_read_stream 方法
 */
class OpjStreamMemInput : public OpjStreamMemAbstract {
private:
	/// 输入数据指针
	const uint8_t* data_;
	/// 输入数据大小
	const size_t size_;

public:
	/**
	 * @brief 构造函数。
	 * @param data 输入数据指针。
	 * @param size 输入数据大小。
	 */
	OpjStreamMemInput(const void* data, size_t size);

	/**
	 * @brief 从流中读取指定长度的数据。
	 * @param p_buffer 数据缓冲区指针。
	 * @param p_nb_bytes 要读取的字节数。
	 * @return 实际读取的字节数。
	 */
	OPJ_SIZE_T Read(void* p_buffer, OPJ_SIZE_T p_nb_bytes) const override;

	/**
	 * @brief 向流中写入指定长度的数据。
	 * @param p_buffer 数据缓冲区指针。
	 * @param p_nb_bytes 要写入的字节数。
	 * @return 实际写入的字节数。
	 */
	OPJ_SIZE_T Write(void* p_buffer, OPJ_SIZE_T p_nb_bytes) override;

	/**
	 * @brief 返回流内存数据地址。
	 * @return 内存数据地址。
	 */
	uint8_t* StreamData() const override;

	/**
	 * @brief 判断流是否为只读流。
	 * @return 如果是只读流返回 true,否则返回 false。
	 */
	OPJ_BOOL IsReadStream() const;
};

/**
 * @brief 从流中读取指定长度的数据。
 * @param p_buffer 数据缓冲区指针。
 * @param p_nb_bytes 要读取的字节数。
 * @param stream_instance 流实例指针。
 * @return 实际读取的字节数。
 */
OPJ_SIZE_T OpjStreamInterfaceRead(void* p_buffer, OPJ_SIZE_T p_nb_bytes, OpjStreamInterface* stream_instance);

/**
 * @brief 向流中写入指定长度的数据。
 * @param p_buffer 数据缓冲区指针。
 * @param p_nb_bytes 要写入的字节数。
 * @param stream_instance 流实例指针。
 * @return 实际写入的字节数。
 */
OPJ_SIZE_T OpjStreamInterfaceWrite(void* p_buffer, OPJ_SIZE_T p_nb_bytes, OpjStreamInterface* stream_instance);

/**
 * @brief 在流中跳过指定数量的字节。
 * @param p_nb_bytes 要跳过的字节数。
 * @param stream_instance 流实例指针。
 * @return 实际跳过的字节数。
 */
OPJ_OFF_T OpjStreamInterfaceSkip(OPJ_OFF_T p_nb_bytes, OpjStreamInterface* stream_instance);

/**
 * @brief 设置流的游标到指定位置(相对于起始位置)。
 * @param p_nb_bytes 相对于起始位置的偏移量。
 * @param stream_instance 流实例指针。
 * @return 成功返回 true,失败返回 false。
 */
OPJ_BOOL OpjStreamInterfaceSeek(OPJ_OFF_T p_nb_bytes, OpjStreamInterface* stream_instance);

/**
 * @brief 关闭流。
 * @param stream_instance 流实例指针。
 */
void OpjStreamInterfaceClose(OpjStreamInterface* stream_instance);

// 假设 opj_stream_t 是在其他地方定义的
/**
 * @brief 创建一个流实例。
 * @param stream 流接口引用。
 * @param p_size 流大小。
 * @return 流实例指针。
 */
opj_stream_t* OpjStreamCreateSi(OpjStreamInterface& stream, OPJ_SIZE_T p_size);

/**
 * @brief 创建一个默认大小的流实例。
 * @param stream 流接口引用。
 * @return 流实例指针。
 */
opj_stream_t* OpjStreamCreateDefaultSi(OpjStreamInterface& stream);

/*****	opj_stream_mem_abstract	实现部分************************************************************/
OPJ_BOOL OpjStreamMemAbstract::Seek(OPJ_OFF_T p_nb_bytes) const {
	if (p_nb_bytes >= 0) {
		cursor_ = start_ + p_nb_bytes;
		return OPJ_TRUE;
	}
	return OPJ_FALSE;
}

OPJ_OFF_T OpjStreamMemAbstract::Skip(OPJ_OFF_T p_nb_bytes) const {
	// 这个函数设计是有问题的,当p_nb_bytes为-1时返回会产生歧义,
	// 但openjpeg中opj_skip_from_file就是这么写的
	// opj_stream_skip_fn定义的返回也是bool
	// 所以也只能按其接口要求这么定义
	auto nc = cursor_ + p_nb_bytes;
	if (nc >= start_) {
		cursor_ = nc;
		return p_nb_bytes;
	}
	return (OPJ_OFF_T)-1;
}

OPJ_UINT64 OpjStreamMemAbstract::StreamLength() const {
	return static_cast<OPJ_UINT64>(last_ - start_);
}

/*****	OpjStreamMemOutput	实现部分************************************************************/
 /**
	 * @brief 默认构造函数。
	 */
OpjStreamMemOutput::OpjStreamMemOutput() : OpjStreamMemOutput(kDefaultMemStreamInitSize) {}
/**
   * @brief 带初始化容量的构造函数。
   * @param init_capacity 初始化容量。
   */
OpjStreamMemOutput::OpjStreamMemOutput(size_t init_capacity) : base(init_capacity) {
	start_ = StreamData();
	end_ = start_ + base::size();
	cursor_ = start_;
	last_ = start_;
	/*start_ = StreamData();
	end_ = StreamData() + size();
	cursor_ = StreamData();
	last_ = StreamData();*/
}

/**
 * @brief 从流中读取指定长度的数据。
 * @param p_buffer 数据缓冲区指针。
 * @param p_nb_bytes 要读取的字节数。
 * @return 实际读取的字节数。
 */
OPJ_SIZE_T OpjStreamMemOutput::Read(void* p_buffer, OPJ_SIZE_T p_nb_bytes) const {
	// 输出流不能读取
	return 0;
}
/**
 * @brief 向流中写入指定长度的数据。
 * @param p_buffer 数据缓冲区指针。
 * @param p_nb_bytes 要写入的字节数。
 * @return 实际写入的字节数。
 */
OPJ_SIZE_T OpjStreamMemOutput::Write(void* p_buffer, OPJ_SIZE_T p_nb_bytes) {
	// 计算当前缓冲区中剩余空间的大小。
	auto available_space = static_cast<OPJ_SIZE_T>(end_ - cursor_);

	// 检查可用空间是否足够存放要写入的字节数。
	if (p_nb_bytes > available_space) {
		// 如果空间不足,计算当前缓冲区指针位置的偏移。
		auto cur_offset = cursor_ - start_;
		// 记录最后一个字节后的偏移量。
		auto last_offset = last_ - start_;
		// 计算新的缓冲区大小,需要确保足够放下新数据。(最少是两倍扩容), (std::max)是为了避免被宏转义
		OPJ_SIZE_T new_size = base::size() + (std::max)(p_nb_bytes - available_space, base::size());

		// 调整缓冲区的大小。
		base::resize(new_size);

		// 重新计算并更新指针以反映缓冲区的变化。
		start_ = StreamData();
		end_ = start_ + base::size();
		last_ = start_ + last_offset;
		cursor_ = start_ + cur_offset;
	}

	// 将数据从输入缓冲区复制到内部缓冲区中。
	std::memcpy(cursor_, p_buffer, p_nb_bytes);
	// 更新指针位置以反映新数据的末尾。
	auto new_cursor_position = cursor_ + p_nb_bytes;

	// 如果新数据位置超出了之前最后一个字节的位置,进行填充操作。(为了保持数据的连续性以及防止使用未初始化内存(未定义行为),我们需要将任何新的空白区域填补为明确的值(如零)。)
	if (new_cursor_position > last_) {
		// 如果当前指针超过了之前的最后一个字节位置,用零填充缺口部分。
		if (cursor_ > last_) {
			std::memset(last_, 0, cursor_ - last_);
		}
		// 更新 `last` 位置以反映新的结束位置。
		last_ = new_cursor_position;
	}

	// 更新当前指针位置。
	cursor_ = new_cursor_position;
	// 返回写入的字节数。
	return p_nb_bytes;
}
/**
	* @brief 返回流内存数据地址。
	* @return 内存数据地址。
	*/
uint8_t* OpjStreamMemOutput::StreamData() const {
	return const_cast<uint8_t*>(base::data());
}

/**
 * @brief 判断流是否为只读流。
 * @return 如果是只读流返回 true,否则返回 false。
 */
OPJ_BOOL OpjStreamMemOutput::IsReadStream() const { return 0; }

/**
 * @brief 关闭流。
 */
void OpjStreamMemOutput::Close() {
	base::resize(0);
}

/**
 * @brief 将输出的压缩数据封装为 vector 并返回。
 * @return 包含压缩数据的 vector。
 */
std::vector<uint8_t> OpjStreamMemOutput::AsVector() {
	return std::vector<uint8_t>(StreamData(), StreamData() + StreamLength());
}

/*****	opj_stream_mem_input	实现部分************************************************************/

	/**
	 * @brief 构造函数。
	 * @param data 输入数据指针。
	 * @param size 输入数据大小。
	 */
OpjStreamMemInput::OpjStreamMemInput(const void* data, size_t size)
	: data_(reinterpret_cast<const uint8_t*>(data)), size_(size) {
	if (data_ == nullptr) {
		// 处理空指针的情况
		start_ = nullptr;
		cursor_ = nullptr;
		last_ = nullptr;
	}
	else {
		start_ = const_cast<uint8_t*>(data_);
		cursor_ = start_;
		last_ = start_ + size_;
	}
}
/**
	 * @brief 从流中读取指定长度的数据。
	 * @param p_buffer 数据缓冲区指针。
	 * @param p_nb_bytes 要读取的字节数。
	 * @return 实际读取的字节数。
	 */
OPJ_SIZE_T OpjStreamMemInput::Read(void* p_buffer, OPJ_SIZE_T p_nb_bytes) const {
	if (last_ > cursor_) {
		auto len = std::min(static_cast<OPJ_SIZE_T>(last_ - cursor_), p_nb_bytes);
		if (len > 0) {
			std::memcpy(p_buffer, cursor_, len);
			cursor_ += len;
			return len;
		}
	}
	return static_cast<OPJ_SIZE_T>(-1);
}
/**
	* @brief 向流中写入指定长度的数据。
	* @param p_buffer 数据缓冲区指针。
	* @param p_nb_bytes 要写入的字节数。
	* @return 实际写入的字节数。
	*/
OPJ_SIZE_T OpjStreamMemInput::Write(void* p_buffer, OPJ_SIZE_T p_nb_bytes) {
	// 输入流不能写入
	return 0;
}

/**
 * @brief 返回流内存数据地址。
 * @return 内存数据地址。
 */
uint8_t* OpjStreamMemInput::StreamData() const {
	return const_cast<uint8_t*>(data_);
}

/**
 * @brief 判断流是否为只读流。
 * @return 如果是只读流返回 true,否则返回 false。
 */
OPJ_BOOL OpjStreamMemInput::IsReadStream() const { return true; }
/*****	opj_stream_mem_input	实现部分******* 为了适配 openjpeg的 c 接口************/

void OpjStreamInterfaceClose(OpjStreamInterface* stream_instance) {
	stream_instance->Close();
}

OPJ_BOOL OpjStreamInterfaceSeek(OPJ_OFF_T p_nb_bytes, OpjStreamInterface* stream_instance) {
	return stream_instance->Seek(p_nb_bytes);
}

OPJ_OFF_T OpjStreamInterfaceSkip(OPJ_OFF_T p_nb_bytes, OpjStreamInterface* stream_instance) {
	return stream_instance->Skip(p_nb_bytes);
}

OPJ_SIZE_T OpjStreamInterfaceWrite(void* p_buffer, OPJ_SIZE_T p_nb_bytes, OpjStreamInterface* stream_instance) {
	return stream_instance->Write(p_buffer, p_nb_bytes);
}

OPJ_SIZE_T OpjStreamInterfaceRead(void* p_buffer, OPJ_SIZE_T p_nb_bytes, OpjStreamInterface* stream_instance) {
	return stream_instance->Read(p_buffer, p_nb_bytes);
}

opj_stream_t* OpjStreamCreateSi(OpjStreamInterface& stream, OPJ_SIZE_T p_size) {
	opj_stream_t* l_stream = opj_stream_create(p_size, stream.IsReadStream());
	if (l_stream) {
		opj_stream_set_user_data_v3(l_stream, std::addressof(stream), (opj_stream_free_user_data_fn)(OpjStreamInterfaceClose));
		opj_stream_set_user_data_length(l_stream, stream.StreamLength());
		opj_stream_set_read_function(l_stream, (opj_stream_read_fn)(OpjStreamInterfaceRead));
		opj_stream_set_write_function(l_stream, (opj_stream_write_fn)(OpjStreamInterfaceWrite));
		opj_stream_set_skip_function(l_stream, (opj_stream_skip_fn)(OpjStreamInterfaceSkip));
		opj_stream_set_seek_function(l_stream, (opj_stream_seek_fn)(OpjStreamInterfaceSeek));
		return l_stream;
	}
}

opj_stream_t* OpjStreamCreateDefaultSi(OpjStreamInterface& stream) {
	return OpjStreamCreateSi(stream, OPJ_J2K_STREAM_CHUNK_SIZE);
}

/*******************************************开始使用上面的内存解码接口,解码图像**********************************/
/**
 * @brief 枚举定义 JPEG2000 编解码格式
 *
 * 该枚举类定义了支持的不同 JPEG2000 编解码格式。
 * - 包括 J2K、JP2、JPT 等标准和专用的 JPEG2000 格式。
 */
enum class FS_JPEG2K_CODEC_FORMAT : int {
	FS_JPEG2K_CODEC_UNKNOWN = -1, /**< 未知格式,作为占位符 */

	FS_JPEG2K_CODEC_J2K = 0,     /**< JPEG-2000 codestream 格式(支持读写) */
	FS_JPEG2K_CODEC_JP2 = 1,     /**< JP2 文件格式(支持读写) */
	FS_JPEG2K_CODEC_JPT = 2,     /**< JPT 流格式(JPEG 2000, JPIP,仅支持读取) */
	FS_JPEG2K_CODEC_JPX = 3,     /**< JPX 文件格式(JPEG 2000 Part-2,仅待编码) */

	FS_JPEG2K_CODEC_JPP = 4      /**< JPP 流格式(JPEG 2000, JPIP,待编码) */
};

/**
 * @brief 错误回调函数
 *
 * 输出错误信息到标准错误流(stderr)。
 *
 * @param[in] msg 错误信息的消息字符串
 * @param[in] client_data 客户端数据(目前未使用)
 */
void error_callback(const char* msg, void* client_data) {
	fprintf(stderr, "[ERROR] %s\n", msg);
}

/**
 * @brief 警告回调函数
 *
 * 输出警告信息到标准错误流(stderr)。
 *
 * @param[in] msg 警告信息的消息字符串
 * @param[in] client_data 客户端数据(目前未使用)
 */
void warning_callback(const char* msg, void* client_data) {
	fprintf(stderr, "[WARNING] %s\n", msg);
}

/**
 * @brief 信息回调函数
 *
 * 输出普通信息到标准输出流(stdout)。
 *
 * @param[in] msg 信息消息的字符串
 * @param[in] client_data 客户端数据(目前未使用)
 */
void info_callback(const char* msg, void* client_data) {
	fprintf(stdout, "[INFO] %s\n", msg);
}

/**
 * @brief 从图像数据中解码并返回解码后的 JPEG2000 图像结构。
 *
 * 此函数用于解码给定的 JPEG2000 图像数据,并返回解码后的图像结构。根据传入的图像格式,函数会选择合适的解码器进行解码操作。
 * 支持的图像格式包括 J2K、JP2 和 JPT 格式。解码过程中,如果出现任何错误或图像格式不受支持,函数将返回空指针。
 *
 * @param[in] image_data 输入的 JPEG2000 图像数据,以字节向量的形式传入。
 * @param[in] img_format 输入的图像格式,指定了解码器的类型。可以是以下值之一:
 * - FS_JPEG2K_CODEC_J2K
 * - FS_JPEG2K_CODEC_JP2
 * - FS_JPEG2K_CODEC_JPT
 *
 * @return opj_image_t* 解码后的图像结构指针,成功时返回图像结构,失败时返回空指针。
 * - 返回的图像结构包含了图像的所有基本信息,如宽度、高度、分辨率、颜色空间、图像数据等。
 * - 如果图像解码失败或不支持的格式,将返回 `nullptr`。
 *
 * @note 函数内部将使用默认解码参数配置解码器,并设置相应的回调函数来处理信息、警告和错误。如果图像格式为 J2K、JP2 或 JPT,函数将执行解码操作。
 *       若图像包含瓦片数据,函数会根据需要选择解码指定的瓦片。
 */
 //这个函数用来解码JPEG2000格式的图像数据,并返回解码后的图像结构,如果解码失败则返回空指针。
static opj_image_t* GetImageStruct(std::vector<uint8_t>& image_data, FS_JPEG2K_CODEC_FORMAT img_format)
{
	// 检查输入的图像数据是否为空
	if (image_data.empty()) {
		return nullptr; // 无数据可处理,直接返回空指针
	}

	// 从图像数据中初始化内存输入流
	OpjStreamMemInput src(image_data.data(), image_data.size());
	opj_dparameters_t parameters;

	// 设置解码器的默认参数
	opj_set_default_decoder_parameters(&parameters);
	parameters.decod_format = static_cast<int>(img_format); // 设置解码格式

	// 创建默认的流接口
	auto l_stream = OpjStreamCreateDefaultSi(src);

	// 检查支持的图像格式
	if (img_format != FS_JPEG2K_CODEC_FORMAT::FS_JPEG2K_CODEC_J2K &&
		img_format != FS_JPEG2K_CODEC_FORMAT::FS_JPEG2K_CODEC_JP2 &&
		img_format != FS_JPEG2K_CODEC_FORMAT::FS_JPEG2K_CODEC_JPT)
	{
		return nullptr; // 不支持的图像格式
	}

	// 创建解压缩编解码器
	opj_codec_t* l_codec = opj_create_decompress(static_cast<OPJ_CODEC_FORMAT>(parameters.decod_format));
	if (!l_codec) {
		return nullptr; // 创建解码器失败
	}

	// 设置回调处理程序
	opj_set_info_handler(l_codec, info_callback, nullptr);
	opj_set_warning_handler(l_codec, warning_callback, nullptr);
	opj_set_error_handler(l_codec, error_callback, nullptr);

	// 配置解码器
	if (!opj_setup_decoder(l_codec, &parameters)) {
		opj_destroy_codec(l_codec); // 解码器配置失败时释放资源
		return nullptr;
	}

	opj_image_t* image = nullptr; // 用于存储解码后的图像
	// 读取图像的头信息
	if (!opj_read_header(l_stream, l_codec, &image)) {
		opj_destroy_codec(l_codec); // 读取头信息失败时释放资源
		return nullptr;
	}

	bool success = false;
	// 根据是否指定了瓦片来选择解码方式
	if (parameters.nb_tile_to_decode == 0) {
		success = opj_set_decode_area(l_codec, image,
			static_cast<OPJ_INT32>(parameters.DA_x0),
			static_cast<OPJ_INT32>(parameters.DA_y0),
			static_cast<OPJ_INT32>(parameters.DA_x1),
			static_cast<OPJ_INT32>(parameters.DA_y1)) &&
			opj_decode(l_codec, l_stream, image) &&
			opj_end_decompress(l_codec, l_stream);
	}
	else {
		success = opj_get_decoded_tile(l_codec, l_stream, image, parameters.tile_index);
	}

	// 清理编解码器并返回解码结果
	opj_destroy_codec(l_codec);

	return success ? image : nullptr; // 成功解码返回图像,失败返回空指针
}

/**
 * @brief 获取 JPEG2000 图像的基本信息,并将图像数据存储为 RGB 格式。
 *
 * 此函数从 JPEG2000 图像数据中提取图像宽度、高度、通道数、每个通道的位深度、色彩空间等信息,并将图像数据转换为 RGB 格式。
 * 若图像包含透明度通道(Alpha 通道),则可选择将其与白色背景进行混合。
 *
 * @param[out] image_data 输出图像数据(RGB 格式),即解码后的图像数据。
 * @param[in] image_format 输入的图像格式,用于指定 JPEG2000 解码器的格式。
 * @param[out] image_width 输出图像宽度(像素)。
 * @param[out] image_height 输出图像高度(像素)。
 * @param[out] image_channels 输出图像的通道数(通常为 3 或 4)。
 * @param[out] image_bits_per_component 输出每个通道的位深度(通常为 8)。
 * @param[out] image_colorspace 输出图像的色彩空间信息。
 * @param[in] white_background 是否使用白色背景填充透明度(默认值为 false)。如果为 true,透明度将与白色背景进行混合。
 *
 * @return int 返回值:
 * - 0:表示成功获取图像信息并解码图像。
 * - -1:表示图像解码失败或图像组件不匹配。
 */
int GetJ2kImageInfo(std::vector<uint8_t>& image_data, int& image_format, int& image_width, int& image_height, int& image_channels, int& image_bits_per_component, int& image_colorspace, bool white_background)
{
	auto opj_image_struct = GetImageStruct(image_data, static_cast<FS_JPEG2K_CODEC_FORMAT>(image_format));

	if (!opj_image_struct || opj_image_struct->numcomps == 0) {
		return -1;
	}

	// 校验每个组件的参数
	auto w0 = opj_image_struct->comps[0].w;
	auto h0 = opj_image_struct->comps[0].h;
	auto prec0 = opj_image_struct->comps[0].prec;
	for (int i = 1; i < opj_image_struct->numcomps; ++i) {
		auto& comp = opj_image_struct->comps[i];
		if (comp.w != w0 || comp.h != h0 || comp.prec > 8 || comp.bpp > 8) {
			return -1;  // 这里可以根据需求决定如何处理不匹配的情况
		}
	}

	image_width = w0;
	image_height = h0;
	image_channels = opj_image_struct->numcomps;
	image_bits_per_component = prec0;
	image_colorspace = opj_image_struct->color_space;

	// 如果通道数大于4,去除透明度通道
	bool has_alpha = image_channels > 3;
	if (has_alpha) {
		image_channels = 3; // 只保留RGB通道
	}

	auto image_total_size = image_width * image_height * image_channels;
	image_data.resize(image_total_size);

	// 复制图像数据到向量
	size_t index = 0;
	for (size_t y = 0; y < image_height; ++y) {
		for (size_t x = 0; x < image_width; ++x) {
			if (has_alpha && white_background) {
				// 获取透明度通道的值
				uint8_t alpha = static_cast<uint8_t>(opj_image_struct->comps[3].data[y * image_width + x]);
				for (size_t ch = 0; ch < 3; ++ch) {
					uint8_t original_pixel = static_cast<uint8_t>(opj_image_struct->comps[ch].data[y * image_width + x]);
					uint8_t white_background = 255; // 白色背景
					uint8_t mixed_pixel = (original_pixel * alpha + white_background * (255 - alpha)) / 255;
					image_data[index++] = mixed_pixel;
				}
			}
			else {
				for (size_t ch = 0; ch < 3; ++ch) {
					image_data[index++] = static_cast<uint8_t>(opj_image_struct->comps[ch].data[y * image_width + x]);
				}
			}
		}
	}

	return 0;
}


/**
 * @brief 创建并初始化用于编码 RGB 图像的 JPEG 2000 编码参数。
 *
 * 该函数根据传入的图像质量和图像格式设置 JPEG 2000 编码器的默认参数,
 * 并根据用户指定的质量参数调整编码器的行为。
 *
 * @param image_quality 图像的压缩质量(范围:0-100),越高代表质量越好,文件越大。
 *                      如果值大于 100,会将其限制为 100。
 * @param image_format 要编码的图像格式。支持 JPEG-2000 格式的不同变种(如 J2K、JP2 等)。
 *
 * @return 返回一个包含编码参数的 `opj_cparameters_t` 智能指针。该指针需要在编码过程中传递给编码器。
 */
 //这个函数用来编码 RGB 像素 为 JP2格式的图像
std::unique_ptr<opj_cparameters_t> CreateEncoderParameters(int image_quality, FS_JPEG2K_CODEC_FORMAT image_format) {
	auto parameters = std::make_unique<opj_cparameters_t>();
	opj_set_default_encoder_parameters(parameters.get());

	// 设置编码层数为 1
	parameters->tcp_numlayers = 1;
	// 设置失真比为给定的图像质量,确保最大值不超过 100
	parameters->tcp_distoratio[0] = static_cast<float>(std::min(image_quality, 100));
	// 设置固定质量编码模式
	parameters->cp_fixed_quality = 1;
	// 设置编码格式(如 J2K、JP2 等)
	parameters->cod_format = static_cast<int>(image_format);

	return parameters;
}

/**
 * @brief 构建一个 OpenJPEG 图像结构(opj_image_t)。
 *
 * 该函数接受一个包含 RGB 图像像素数据的向量,并根据给定的宽度、高度和编码参数
 * 构建一个 OpenJPEG 图像结构。图像的颜色空间设置为 SRGB,且每个通道的精度和位深度
 * 都设置为 8 位。图像数据被逐行处理并填充到 OpenJPEG 图像结构的组件中。
 *
 * @param source_image 输入的 RGB 图像数据,格式为 8 位每通道的顺序数据。
 * @param width 图像的宽度(像素)。
 * @param height 图像的高度(像素)。
 * @param parameters 编码器的参数,包含图像的偏移量、子采样率等信息。
 *
 * @return 返回构建好的 OpenJPEG 图像结构(opj_image_t*),如果参数无效或创建失败,则返回 nullptr。
 */

 // 构建图像,填充图像组件数据
opj_image_t* ConstructImage(const std::vector<uint8_t>& source_image, int width, int height, const opj_cparameters_t* parameters) {
	// 检查参数是否为空,若为空则返回 nullptr
	if (!parameters) {
		return nullptr;
	}

	// 定义图像的颜色空间为 SRGB
	const OPJ_COLOR_SPACE color_space = OPJ_CLRSPC_SRGB;
	// 定义 RGB 图像的通道数为 3
	const int channels = 3;

	// 创建并初始化一个大小为 3 的 opj_image_cmptparm_t 向量来保存每个图像组件的参数
	std::vector<opj_image_cmptparm_t> cmptparm(channels);

	// 填充每个组件的参数
	for (int i = 0; i < channels; ++i) {
		// 设置每个组件的精度为 8 位
		cmptparm[i].prec = 8;
		// 设置每个组件的位深度为 8
		cmptparm[i].bpp = 8;
		// 设置每个组件的符号为 0(无符号)
		cmptparm[i].sgnd = 0;
		// 设置子采样率在 X 方向上的步长
		cmptparm[i].dx = static_cast<OPJ_UINT32>(parameters->subsampling_dx);
		// 设置子采样率在 Y 方向上的步长
		cmptparm[i].dy = static_cast<OPJ_UINT32>(parameters->subsampling_dy);
		// 设置组件的宽度
		cmptparm[i].w = static_cast<OPJ_UINT32>(width);
		// 设置组件的高度
		cmptparm[i].h = static_cast<OPJ_UINT32>(height);
	}

	// 使用创建的图像组件参数,创建一个新的图像对象
	opj_image_t* image = opj_image_create(static_cast<OPJ_UINT32>(channels), cmptparm.data(), color_space);
	// 如果图像创建失败,返回 nullptr
	if (!image) {
		return nullptr;
	}

	// 设置图像的偏移量和边界
	image->x0 = static_cast<OPJ_UINT32>(parameters->image_offset_x0);
	image->y0 = static_cast<OPJ_UINT32>(parameters->image_offset_y0);
	// 计算图像的右下角坐标
	image->x1 = image->x0 + (width - 1) * parameters->subsampling_dx + 1;
	image->y1 = image->y0 + (height - 1) * parameters->subsampling_dy + 1;

	// 计算每行像素数据的跨度
	const int row_stride = width * channels; // 对于 RGB 图像,每行数据的大小
	// 索引变量,用来遍历每个像素的数据
	int index = 0;
	// 遍历图像的每一行
	for (int y = 0; y < height; ++y) {
		// 获取当前行的起始地址
		const uint8_t* scanline = source_image.data() + y * row_stride;
		// 遍历当前行的每一个像素
		for (int x = 0; x < width; ++x) {
			// 获取当前像素的 RGB 数据
			const uint8_t* pixel = scanline + x * channels;
			// 将 RGB 数据复制到图像的组件中
			for (int ch = 0; ch < channels; ++ch) {
				image->comps[ch].data[index] = static_cast<OPJ_INT32>(pixel[ch]);
			}
			// 增加索引,处理下一个像素
			++index;
		}
	}

	// 返回构造好的图像对象
	return image;
}

/**
 * @brief 将输入的 RGB 图像压缩为 JP2 格式。
 *
 * 该函数接受 RGB 图像数据,并根据给定的质量和格式将其压缩为 JP2(JPEG 2000)格式。
 * 压缩过程包括初始化编码参数、构建图像结构、设置编码器、创建流并执行压缩操作。
 *
 * @param source_image 输入的 RGB 图像数据。
 * @param width 图像的宽度(像素)。
 * @param height 图像的高度(像素)。
 * @param quality 压缩质量,取值范围为 1 到 100。
 * @param format 压缩格式,指定 JPEG 2000 格式类型。
 * @param target_image 输出的压缩后 JP2 格式图像数据。
 */
void CompressToJp2(const std::vector<uint8_t>& source_image, const int& width, const int& height,
	const int& quality, const int& format, std::vector<uint8_t>& target_image) {
	// 创建内存输出流,用于存储压缩后的图像数据
	OpjStreamMemOutput dest;

	// 创建编码器参数
	auto parameters = CreateEncoderParameters(quality, static_cast<FS_JPEG2K_CODEC_FORMAT>(format));
	if (!parameters) {
		// 错误处理: 参数初始化失败
		return;
	}

	// 构建 OpenJPEG 图像结构
	opj_image_t* image = ConstructImage(source_image, width, height, parameters.get());
	if (!image) {
		// 错误处理: 图像构建失败
		return;
	}

	// 设置 JP2 格式的注释信息
	std::unique_ptr<char[]> comment;
	if (!parameters->cp_comment) {
		// 如果没有设置注释,创建默认注释
		const char comment_template[] = "Created by OpenJPEG version ";
		const std::string version = opj_version(); // 获取 OpenJPEG 版本
		comment = std::make_unique<char[]>(strlen(comment_template) + version.size() + 1);
		sprintf(comment.get(), "%s%s", comment_template, version.c_str());
		parameters->cp_comment = comment.get();  // 将注释信息设置到参数中
	}

	// 创建 JPEG 2000 编码器
	opj_codec_t* codec = opj_create_compress(static_cast<CODEC_FORMAT>(parameters->cod_format));
	if (!codec) {
		// 错误处理: 编码器创建失败
		opj_image_destroy(image);
		return;
	}

	// 设置编码器的回调函数
	opj_set_info_handler(codec, info_callback, nullptr);
	opj_set_warning_handler(codec, warning_callback, nullptr);
	opj_set_error_handler(codec, error_callback, nullptr);

	// 设置编码器并创建流
	opj_setup_encoder(codec, parameters.get(), image);
	opj_stream_t* stream = OpjStreamCreateDefaultSi(dest);
	if (!stream) {
		// 错误处理: 流创建失败
		opj_destroy_codec(codec);
		opj_image_destroy(image);
		return;
	}

	// 开始图像编码,压缩图像数据
	bool success = opj_start_compress(codec, image, stream) &&
		opj_encode(codec, stream) &&
		opj_end_compress(codec, stream);

	if (!success) {
		// 错误处理: 编码失败
	}

	// 清理资源
	opj_stream_destroy(stream);  // 销毁流
	opj_destroy_codec(codec);    // 销毁编码器
	opj_image_destroy(image);    // 销毁图像

	// 将压缩后的图像数据存储到目标输出
	target_image = std::move(dest.AsVector());
}


#include 

#include 
#include 
#include 
#include 
//#include 
#include 

class JPEG2000Manager {
public:
	// 图像元数据结构
	struct JPEG2000ImageInfo {
		uint32_t width;
		uint32_t height;
		uint16_t componentCount;  // 颜色分量数 (1=灰度, 3=RGB, 4=RGBA)
		uint8_t bitsPerSample;    // 每个样本的位数 (通常8或16)
		bool isSigned;            // 样本是否带符号
		std::string colorSpace;   // 色彩空间描述
		std::string compression;  // 压缩方式描述
	};

	// 构造函数:初始化编码器
	JPEG2000Manager() = default;

	// 编码RGB数据为JPEG 2000格式
	std::vector<uint8_t> EncodeToJPEG2000(
		const uint8_t* rgbData,
		int width,
		int height,
		int quality = 80,
		FS_JPEG2K_CODEC_FORMAT format = FS_JPEG2K_CODEC_FORMAT::FS_JPEG2K_CODEC_JP2
	) {
		// 1. 验证输入参数
		if (!rgbData || width <= 0 || height <= 0) {
			throw std::invalid_argument("Invalid image parameters");
		}

		// 2. 准备编码参数
		auto parameters = CreateEncoderParameters(quality, format);

		// 3. 构建OpenJPEG图像结构
		std::vector<uint8_t> sourceImage(rgbData, rgbData + width * height * 3);
		opj_image_t* image = ConstructImage(sourceImage, width, height, parameters.get());
		if (!image) {
			throw std::runtime_error("Failed to construct image");
		}

		// 4. 创建内存输出流
		OpjStreamMemOutput outputStream;

		// 5. 执行编码
		bool success = PerformEncoding(image, parameters.get(), outputStream);

		// 6. 清理资源
		opj_image_destroy(image);

		if (!success) {
			throw std::runtime_error("JPEG 2000 encoding failed");
		}

		// 7. 返回编码后的数据
		return outputStream.AsVector();
	}
	/**
	* @brief 解码 JPEG2000 图像并获取元数据
	* @param j2kData JPEG2000 压缩数据指针
	* @param dataSize 数据大小
	* @param[out] output 输出的RGB/RGBA像素数据
	* @param whiteBackground 是否用白色背景混合透明通道
	* @return JPEG2000ImageInfo 包含图像元数据的结构体
	* @throws std::runtime_error 解码失败时抛出
	*/
	JPEG2000ImageInfo DecodeImage(
		const uint8_t* j2kData,
		size_t dataSize,
		std::vector<uint8_t>& output,
		bool whiteBackground = false
	) {
		// 1. 自动检测格式
		auto format = DetectFormat(j2kData, dataSize);

		// 2. 解码图像
		auto image = DecodeToImageStruct(j2kData, dataSize, format);
		if (!image) {
			throw std::runtime_error("Failed to decode JPEG2000 image");
		}

		// 3. 提取元数据
		JPEG2000ImageInfo info = ExtractImageInfo(image);

		// 4. 转换为RGB/RGBA
		output = ConvertImageData(image, whiteBackground);

		// 5. 清理资源
		opj_image_destroy(image);

		return info;
	}
	/**
* @brief 获取 JPEG2000 图像的元数据信息
*
* 该函数通过解析 JPEG2000 压缩数据,提取图像的宽度、高度、通道数、位深度等元数据信息。
* 函数内部会自动检测图像格式(JP2/J2K),并仅解码头部信息以提高性能。
*
* @param[in] j2kData JPEG2000 压缩数据指针
* @param[in] dataSize 压缩数据长度(字节数)
* @return JPEG2000ImageInfo 包含完整图像元数据的结构体
**
**/
	JPEG2000ImageInfo GetImageInfo(
		const uint8_t* j2kData,
		size_t dataSize
	) {
		// 1. 自动检测格式
		auto format = DetectFormat(j2kData, dataSize);

		// 2. 解码图像
		auto image = DecodeToImageStruct(j2kData, dataSize, format);
		if (!image) {
			throw std::runtime_error("Failed to decode JPEG2000 image");
		}
		// 3. 提取元数据
		JPEG2000ImageInfo info = ExtractImageInfo(image);
		// 4. 清理资源
		opj_image_destroy(image);

		return info;
	}
private:
	// 边界保护函数
	int clamp(int value, int min, int max) {
		return (std::max)(min, (std::min)(value, max));
	}
	// 创建编码参数
	std::unique_ptr<opj_cparameters_t> CreateEncoderParameters(int quality, FS_JPEG2K_CODEC_FORMAT format) {
		auto params = std::make_unique<opj_cparameters_t>();
		opj_set_default_encoder_parameters(params.get());

		params->tcp_numlayers = 1;
		params->tcp_distoratio[0] = static_cast<float>(clamp(quality, 1, 100));
		params->cp_fixed_quality = 1;
		params->cod_format = static_cast<int>(format);

		return params;
	}

	// 构建OpenJPEG图像结构
	opj_image_t* ConstructImage(const std::vector<uint8_t>& source, int width, int height,
		const opj_cparameters_t* params) {
		const OPJ_COLOR_SPACE colorspace = OPJ_CLRSPC_SRGB;
		const int channels = 3;

		// 设置组件参数
		std::vector<opj_image_cmptparm_t> cmptparm(channels);
		for (int i = 0; i < channels; ++i) {
			cmptparm[i] = {
				static_cast<OPJ_UINT32>(params->subsampling_dx),  // dx
				static_cast<OPJ_UINT32>(params->subsampling_dy),  // dy
				static_cast<OPJ_UINT32>(width),                   // w
				static_cast<OPJ_UINT32>(height),                  // h
				static_cast<OPJ_UINT32>(params->image_offset_x0), // x0
				static_cast<OPJ_UINT32>(params->image_offset_y0), // y0
				8,  // prec (精度)
				8,  // bpp (位深度)
				0   // sgnd (无符号)
			};
		}
		// 创建图像
		opj_image_t* image = opj_image_create(channels, cmptparm.data(), colorspace);
		if (!image) return nullptr;

		// 设置图像边界
		image->x0 = params->image_offset_x0;
		image->y0 = params->image_offset_y0;
		image->x1 = image->x0 + (width - 1) * params->subsampling_dx + 1;
		image->y1 = image->y0 + (height - 1) * params->subsampling_dy + 1;

		// 填充图像数据
		const int row_stride = width * channels;
		for (int y = 0, idx = 0; y < height; ++y) {
			const uint8_t* row = source.data() + y * row_stride;
			for (int x = 0; x < width; ++x, ++idx) {
				const uint8_t* pixel = row + x * channels;
				for (int ch = 0; ch < channels; ++ch) {
					image->comps[ch].data[idx] = pixel[ch];
				}
			}
		}

		return image;
	}

	// 执行编码过程
	bool PerformEncoding(opj_image_t* image, opj_cparameters_t* params, OpjStreamMemOutput& output) {
		// 创建编码器
		opj_codec_t* codec = opj_create_compress(static_cast<OPJ_CODEC_FORMAT>(params->cod_format));
		if (!codec) return false;

		// 设置回调
		opj_set_info_handler(codec, [](const char* msg, void*) {
			std::cout << "[INFO] " << msg << std::endl;
			}, nullptr);

		opj_set_warning_handler(codec, [](const char* msg, void*) {
			std::cerr << "[WARN] " << msg << std::endl;
			}, nullptr);

		opj_set_error_handler(codec, [](const char* msg, void*) {
			std::cerr << "[ERROR] " << msg << std::endl;
			}, nullptr);

		// 配置编码器
		if (!opj_setup_encoder(codec, params, image)) {
			opj_destroy_codec(codec);
			return false;
		}

		// 创建流
		opj_stream_t* stream = OpjStreamCreateDefaultSi(output);
		if (!stream) {
			opj_destroy_codec(codec);
			return false;
		}

		// 执行编码
		bool success = opj_start_compress(codec, image, stream) &&
			opj_encode(codec, stream) &&
			opj_end_compress(codec, stream);

		// 清理资源
		opj_stream_destroy(stream);
		opj_destroy_codec(codec);

		return success;
	}
	// 内部解码实现
	opj_image_t* DecodeToImageStruct(const uint8_t* data, size_t size, OPJ_CODEC_FORMAT format) {
		OpjStreamMemInput stream(data, size);
		auto l_stream = OpjStreamCreateDefaultSi(stream);

		opj_dparameters_t params;
		opj_set_default_decoder_parameters(&params);

		auto codec = opj_create_decompress(format);
		opj_set_error_handler(codec, [](const char* msg, void*) {
			throw std::runtime_error(msg);
			}, nullptr);

		opj_image_t* image = nullptr;
		if (!opj_read_header(l_stream, codec, &image) || !image) {
			opj_destroy_codec(codec);
			throw std::runtime_error("Header read failed");
		}

		if (!opj_decode(codec, l_stream, image)) {
			opj_image_destroy(image);
			opj_destroy_codec(codec);
			throw std::runtime_error("Decoding failed");
		}

		opj_destroy_codec(codec);
		return image;
	}
	// 格式检测
	OPJ_CODEC_FORMAT DetectFormat(const uint8_t* data, size_t size) {
		if (size > 12 && memcmp(data + 4, "\x6A\x50\x20\x20", 4) == 0) {
			return OPJ_CODEC_JP2; // JP2格式
		}
		if (size > 4 && memcmp(data, "\xFF\x4F\xFF\x51", 4) == 0) {
			return OPJ_CODEC_J2K; // J2K格式
		}
		throw std::runtime_error("Unrecognized JPEG2000 format");
	}


	// 元数据提取
	JPEG2000ImageInfo ExtractImageInfo(opj_image_t* image) {
		JPEG2000ImageInfo info;
		info.width = image->comps[0].w;
		info.height = image->comps[0].h;
		info.componentCount = image->numcomps;
		info.bitsPerSample = image->comps[0].prec;
		info.isSigned = image->comps[0].sgnd;
		info.colorSpace = ColorSpaceToString(image->color_space);
		return info;
	}

	// 色彩空间描述
	std::string ColorSpaceToString(OPJ_COLOR_SPACE space) {
		switch (space) {
		case OPJ_CLRSPC_SRGB: return "sRGB";
		case OPJ_CLRSPC_GRAY: return "Grayscale";
		case OPJ_CLRSPC_SYCC: return "YCC";
		default: return "Unknown";
		}
	}

	// 数据转换
	std::vector<uint8_t> ConvertImageData(opj_image_t* image, bool whiteBg) {
		const int width = image->comps[0].w;
		const int height = image->comps[0].h;
		const bool hasAlpha = image->numcomps >= 4;
		const int outChannels = hasAlpha ? 4 : 3;

		std::vector<uint8_t> pixels(width * height * outChannels);
		size_t idx = 0;

		for (int y = 0; y < height; ++y) {
			for (int x = 0; x < width; ++x) {
				const size_t pos = y * width + x;

				// 处理RGB通道
				for (int c = 0; c < 3; ++c) {
					int val = image->comps[c].data[pos];
					if (hasAlpha && whiteBg) {
						uint8_t alpha = image->comps[3].data[pos];
						val = (val * alpha + 255 * (255 - alpha)) / 255;
					}
					pixels[idx++] = static_cast<uint8_t>(val);
				}

				// 处理Alpha通道
				if (hasAlpha) {
					pixels[idx++] = static_cast<uint8_t>(image->comps[3].data[pos]);
				}
			}
		}

		return pixels;
	}

};
//
//// 使用示例
//int main() {
//	try {
//		// 准备测试图像数据 (8x8 RGB)
//		const int width = 8, height = 8;
//		std::vector testImage(width * height * 3);
//		for (int y = 0; y < height; y++) {
//			for (int x = 0; x < width; x++) {
//				int idx = (y * width + x) * 3;
//				testImage[idx] = x * 32;     // R
//				testImage[idx + 1] = y * 32;   // G
//				testImage[idx + 2] = 128;      // B
//			}
//		}
//
//		// 编码为JPEG 2000
//		JPEG2000Encoder encoder;
//		auto jp2Data = encoder.encodeToJPEG2000(
//			testImage.data(), width, height,
//			85, FS_JPEG2K_CODEC_FORMAT::FS_JPEG2K_CODEC_JP2
//		);
//
//		std::cout << "Encoded JPEG 2000 size: " << jp2Data.size() << " bytes\n";
//
//		// 保存到文件
//		std::ofstream out("output.jp2", std::ios::binary);
//		out.write(reinterpret_cast(jp2Data.data()), jp2Data.size());
//
//	}
//	catch (const std::exception& e) {
//		std::cerr << "Error: " << e.what() << std::endl;
//		return 1;
//	}
//
//	return 0;
//}

/**
 * @brief 将 PDFCore 格式枚举安全转换为标准格式枚举
 *
 * 该函数提供从 PDFCore 内部格式到标准 JPEG 2000 格式的单向转换,
 * 确保格式兼容性并处理所有可能的输入值。
 *
 * @param pdfCoreFormat PDFCore 内部格式枚举值
 * @return 对应的标准格式枚举值
 *
 * @throws std::invalid_argument 当输入为无效枚举值时抛出
 *
 * @note 转换是单向的,不支持反向转换
 * @warning 对于 PDFCore_FS_JPEG2K_CODEC_UNKNOWN 会转换为 FS_JPEG2K_CODEC_UNKNOWN
 *
 * 转换规则:
 * - 相同数值的枚举直接转换
 * - 未知格式保持 UNKNOWN
 * - 其他无效值抛出异常
 */
FS_JPEG2K_CODEC_FORMAT static ConvertPDFCoreToStandardFormat(
	PDFCoreJPEG2000Manager::PDFCore_FS_JPEG2K_CODEC_FORMAT pdfCoreFormat)
{
	switch (pdfCoreFormat) {
		// 直接对应转换
	case PDFCoreJPEG2000Manager::PDFCore_FS_JPEG2K_CODEC_FORMAT::PDFCore_FS_JPEG2K_CODEC_UNKNOWN:
		return FS_JPEG2K_CODEC_FORMAT::FS_JPEG2K_CODEC_UNKNOWN;
	case PDFCoreJPEG2000Manager::PDFCore_FS_JPEG2K_CODEC_FORMAT::PDFCore_FS_JPEG2K_CODEC_J2K:
		return FS_JPEG2K_CODEC_FORMAT::FS_JPEG2K_CODEC_J2K;
	case PDFCoreJPEG2000Manager::PDFCore_FS_JPEG2K_CODEC_FORMAT::PDFCore_FS_JPEG2K_CODEC_JP2:
		return FS_JPEG2K_CODEC_FORMAT::FS_JPEG2K_CODEC_JP2;
	case PDFCoreJPEG2000Manager::PDFCore_FS_JPEG2K_CODEC_FORMAT::PDFCore_FS_JPEG2K_CODEC_JPT:
		return FS_JPEG2K_CODEC_FORMAT::FS_JPEG2K_CODEC_JPT;
	case PDFCoreJPEG2000Manager::PDFCore_FS_JPEG2K_CODEC_FORMAT::PDFCore_FS_JPEG2K_CODEC_JPX:
		return FS_JPEG2K_CODEC_FORMAT::FS_JPEG2K_CODEC_JPX;
	case PDFCoreJPEG2000Manager::PDFCore_FS_JPEG2K_CODEC_FORMAT::PDFCore_FS_JPEG2K_CODEC_JPP:
		return FS_JPEG2K_CODEC_FORMAT::FS_JPEG2K_CODEC_JPP;

		// 处理意外值
	default:
		throw std::invalid_argument(
			"Invalid PDFCore JPEG2000 format value: " +
			std::to_string(static_cast<int>(pdfCoreFormat))
		);
	}
}

std::vector<uint8_t> PDFCoreJPEG2000Manager::EncodeToJPEG2000(const uint8_t* rgbData, int width, int height, int quality, PDFCore_FS_JPEG2K_CODEC_FORMAT format)
{
	
	FS_JPEG2K_CODEC_FORMAT inner_format = ConvertPDFCoreToStandardFormat(format);

	JPEG2000Manager encoder;
			auto jp2Data = encoder.EncodeToJPEG2000(
				rgbData, width, height,
				quality, inner_format
			);
}

std::vector<uint8_t> PDFCoreJPEG2000Manager::DecodeToRGB(const uint8_t* j2kData, size_t dataSize, uint32_t& width, uint32_t& height, uint16_t& componentCount, uint8_t& bitsPerSample, bool& isSigned, std::string& colorSpace, std::string& compression,uint8_t requestComponents)
{
	JPEG2000Manager jpeg2k_manager;
	std::vector<uint8_t> rgb_output;

	
	JPEG2000Manager::JPEG2000ImageInfo jpeg2k_info;

	jpeg2k_info = jpeg2k_manager.DecodeImage(j2kData,dataSize, rgb_output);

	// 填充输出参数
	width = jpeg2k_info.width;
	height = jpeg2k_info.height;
	componentCount = jpeg2k_info.componentCount;
	bitsPerSample = jpeg2k_info.bitsPerSample;
	isSigned = jpeg2k_info.isSigned;
	colorSpace = jpeg2k_info.colorSpace;
	compression = jpeg2k_info.compression;
}

void PDFCoreJPEG2000Manager::GetImageInfo(const uint8_t* j2kData, size_t dataSize, uint32_t& width, uint32_t& height, uint16_t& componentCount, uint8_t& bitsPerSample, bool& isSigned, std::string& colorSpace, std::string& compression)
{
	JPEG2000Manager jpeg2k_manager;
	jpeg2k_manager.GetImageInfo(j2kData, dataSize);
	JPEG2000Manager::JPEG2000ImageInfo jpeg2k_info;
	// 填充输出参数
	width = jpeg2k_info.width;
	height = jpeg2k_info.height;
	componentCount = jpeg2k_info.componentCount;
	bitsPerSample = jpeg2k_info.bitsPerSample;
	isSigned = jpeg2k_info.isSigned;
	colorSpace = jpeg2k_info.colorSpace;
	compression = jpeg2k_info.compression;
}

你可能感兴趣的:(jpeg2k,jpeg2k,jpeg2000,jp2,图像编码)