360免费创建个人网站,网站备案资质,什么网站可以做自考试题,机刷推广软件mupdf除了解析PDF功能之外#xff0c;还有一个强大的功能就是渲染文字和图像#xff0c;本文介绍mupdf渲染过程中涉及到的颜色问题#xff1a;包括颜色空间#xff0c;颜色转换#xff0c;lcms的使用。
1.初始化 mupdf初始化第一步是实例化fz_context *ctx#xff0c;fz…mupdf除了解析PDF功能之外还有一个强大的功能就是渲染文字和图像本文介绍mupdf渲染过程中涉及到的颜色问题包括颜色空间颜色转换lcms的使用。
1.初始化 mupdf初始化第一步是实例化fz_context *ctxfz_context是Mupdf最基本的数据结构它是文件句柄fz_context结构体fz_colorspace_context *colorspace成员变量就是颜色空间内容。
实例化函数fz_new_context初始化了一些列Mupdf操作代码fz_new_colorspace_context(ctx)初始化MUPDF使用的颜色空间
void fz_new_colorspace_context(fz_context *ctx)
{ctx-colorspace fz_malloc_struct(ctx, fz_colorspace_context);ctx-colorspace-ctx_refs 1;set_no_icc(ctx-colorspace);
#ifdef NO_ICCfz_set_cmm_engine(ctx, NULL);
#elsefz_set_cmm_engine(ctx, fz_cmm_engine_lcms);
#endif
}fz_new_colorspace_context内部使用了NO_ICC宏定义通过宏定义确定mupdf使用的颜色空间是否基于ICC文件关于fz_set_cmm_engine函数是否基于ICC也给出了明确的实现
if (engine){cct-gray fz_new_icc_colorspace(ctx, FZ_ICC_PROFILE_GRAY, 1, NULL);cct-rgb fz_new_icc_colorspace(ctx, FZ_ICC_PROFILE_RGB, 3, NULL);cct-bgr fz_new_icc_colorspace(ctx, FZ_ICC_PROFILE_BGR, 3, NULL);cct-cmyk fz_new_icc_colorspace(ctx, FZ_ICC_PROFILE_CMYK, 4, NULL);cct-lab fz_new_icc_colorspace(ctx, FZ_ICC_PROFILE_LAB, 3, NULL);}elseset_no_icc(cct);
对于icc方式mupdf内部使用了5种颜色空间分别是gray, rgb, bgr,cmyk,lab;各类颜色空间初始化方式
fz_colorspace *
fz_new_icc_colorspace(fz_context *ctx, const char *name, int num, fz_buffer *buf)
{fz_colorspace *cs NULL;fz_iccprofile *profile;int is_lab 0;enum fz_colorspace_type type FZ_COLORSPACE_NONE;int flags FZ_COLORSPACE_IS_ICC;profile fz_malloc_struct(ctx, fz_iccprofile);fz_try(ctx){if (buf NULL){size_t size;const unsigned char *data;data fz_lookup_icc(ctx, name, size);profile-buffer fz_new_buffer_from_shared_data(ctx, data, size);is_lab (strcmp(name, FZ_ICC_PROFILE_LAB) 0);profile-bgr (strcmp(name, FZ_ICC_PROFILE_BGR) 0);flags | FZ_COLORSPACE_IS_DEVICE;}else{profile-buffer fz_keep_buffer(ctx, buf);}fz_cmm_init_profile(ctx, profile);XXXXXXfz_md5_icc(ctx, profile);XXXXXXcs fz_new_colorspace(ctx, name, type, flags, profile-num_devcomp, NULL, NULL, NULL, is_lab ? clamp_lab_icc : clamp_default_icc, free_icc, profile, sizeof(profile));return cs;
#endif
}
mupdf使用fz_iccprofile结构表示一个icc文件的解析结果fz_new_icc_colorspace函数内部有两个重要步骤第一个是fz_lookup_icc解析icc文件生成数据
const unsigned char *
fz_lookup_icc(fz_context *ctx, const char *name, size_t *size)
{
#ifndef NO_ICCif (fz_get_cmm_engine(ctx) NULL)return *size 0, NULL;if (!strcmp(name, FZ_ICC_PROFILE_GRAY)) {extern const int fz_resources_icc_gray_icc_size;extern const unsigned char fz_resources_icc_gray_icc[];*size fz_resources_icc_gray_icc_size;return fz_resources_icc_gray_icc;}if (!strcmp(name, FZ_ICC_PROFILE_RGB) || !strcmp(name, FZ_ICC_PROFILE_BGR)) {extern const int fz_resources_icc_rgb_icc_size;extern const unsigned char fz_resources_icc_rgb_icc[];*size fz_resources_icc_rgb_icc_size;return fz_resources_icc_rgb_icc;}if (!strcmp(name, FZ_ICC_PROFILE_CMYK)) {extern const int fz_resources_icc_cmyk_icc_size;extern const unsigned char fz_resources_icc_cmyk_icc[];*size fz_resources_icc_cmyk_icc_size;return fz_resources_icc_cmyk_icc;}if (!strcmp(name, FZ_ICC_PROFILE_LAB)) {extern const int fz_resources_icc_lab_icc_size;extern const unsigned char fz_resources_icc_lab_icc[];*size fz_resources_icc_lab_icc_size;return fz_resources_icc_lab_icc;}
#endifreturn *size 0, NULL;
}
这里用CRAY颜色空间举例找到fz_resources_icc_gray_icc变量的定义它是一个全局变量
const int fz_resources_icc_gray_icc_size 416; const unsigned char fz_resources_icc_gray_icc[] { 0,0,1,160,0,0,0,0,2,16,0,0,109,110,116,114,71,82,65,89,88,89,90,32,0,0,0, 0,0,0,0,0,0,0,0,0,97,99,115,112,65,80,80,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,246,214,0,1,0,0,0,0,211,45,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,5,100,101,115,99,0,0,0,192,0,0,0,125,99,112,114,116,0,0,1,64,0,0,0,40, 119,116,112,116,0,0,1,104,0,0,0,20,98,107,112,116,0,0,1,124,0,0,0,20,107, 84,82,67,0,0,1,144,0,0,0,14,100,101,115,99,0,0,0,0,0,0,0,35,65,114,116,105, 102,101,120,32,83,111,102,116,119,97,114,101,32,115,71,114,97,121,32,73,67, 67,32,80,114,111,102,105,108,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,116,101,120,116,0,0,0,0, 67,111,112,121,114,105,103,104,116,32,65,114,116,105,102,101,120,32,83,111, 102,116,119,97,114,101,32,50,48,49,49,0,88,89,90,32,0,0,0,0,0,0,243,84,0, 1,0,0,0,1,22,207,88,89,90,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,99,117,114,118, 0,0,0,0,0,0,0,1,1,205,0,0,};
到这里可以明白这是gray icc文件内容它是Mupdf自定义的icc文件其他颜色空间icc文件也是同理。fz_lookup_icc生成profile内容之后第二个就是fz_cmm_init_profile(ctx, profile)了他内部调用了fz_lcms_init_profile(fz_cmm_instance *instance, fz_iccprofile *profile)
static void
fz_lcms_init_profile(fz_cmm_instance *instance, fz_iccprofile *profile)
{cmsContext cmm_ctx (cmsContext)instance;fz_context *ctx (fz_context *)cmsGetContextUserData(cmm_ctx);size_t size;unsigned char *data;DEBUG_LCMS_MEM(( Create Profile Start:: mupdf ctx %p lcms ctx %p \n, (void*)ctx, (void*)cmm_ctx));size fz_buffer_storage(ctx, profile-buffer, data);profile-cmm_handle cmsOpenProfileFromMemTHR(cmm_ctx, data, (cmsUInt32Number)size);if (profile-cmm_handle NULL){profile-num_devcomp 0;fz_throw(ctx, FZ_ERROR_GENERIC, cmsOpenProfileFromMem failed);}profile-num_devcomp fz_lcms_num_devcomps(cmm_ctx, profile);DEBUG_LCMS_MEM(( Create Profile End:: mupdf ctx %p lcms ctx %p profile %p profile_cmm %p \n, (void*)ctx, (void*)cmm_ctx, (void*)profile, (void*)profile-cmm_handle));
}
cmsOpenProfileFromMemTHR是lcms的函数用于颜色空间的转换他初始化了一个基于icc的的文件句柄fz_cmm_init_profile结束后cs fz_new_colorspace将profile赋给颜色空间这样一个gray的颜色空间初始化完毕了。
2 颜色转换 mupdf在渲染文字和图像的时候都要对颜色进行转换因为设备的颜色空间和pdf文件样本的颜色空间可能存在不一致情况如果不进行转换会出现显示效果和原PDF文件不一致情况。
mupdf颜色转换函数static fz_overprint * resolve_color(fz_context *ctx, fz_overprint *op, const float *color, fz_colorspace *colorspace, float alpha, const fz_color_params *color_params, unsigned char *colorbv, fz_pixmap *dest)需要传入输入颜色值输入颜色空间alpha值PDF颜色参数输出颜色值目标图片内部调用了fz_convert_color它有两部先查找合适的颜色转换器然后使用颜色转换器对颜色进行转换
void
fz_convert_color(fz_context *ctx, const fz_color_params *params, const fz_colorspace *is, const fz_colorspace *ds, float *dv, const fz_colorspace *ss, const float *sv)
{fz_color_converter cc;fz_find_color_converter(ctx, cc, is, ds, ss, params);cc.convert(ctx, cc, dv, sv);fz_drop_color_converter(ctx, cc);
}关于查找过程如果非ICC模式直接使用颜色值转换方式
if (ds default_gray)cc-convert rgb2g;else if (ds default_bgr)cc-convert rgb2bgr;else if (ds default_cmyk)cc-convert rgb2cmyk;elsecc-convert std_conv_color;
如果是基于ICC模式使用icc_conv_color函数在使用icc_conv_color转换之前需要做一个工作就是要建立一个转换的句柄cc-link fz_get_icc_link(ctx, ds, 0, ss_base, 0, is, params, 2, 0, cc-n);关于fz_get_icc_link下面给出一组调用堆栈关系 void
fz_lcms_init_link(fz_cmm_instance *instance, fz_icclink *link, const fz_iccprofile *dst, int dst_extras, const fz_iccprofile *src, int src_extras, const fz_iccprofile *prf, const fz_color_params *rend, int cmm_flags, int num_bytes, int copy_spots)
{cmsContext cmm_ctx (cmsContext)instance;fz_context *ctx (fz_context *)cmsGetContextUserData(cmm_ctx);cmsUInt32Number src_data_type, des_data_type;cmsColorSpaceSignature src_cs, des_cs;int src_num_chan, des_num_chan;int lcms_src_cs, lcms_des_cs;unsigned int flag cmsFLAGS_LOWRESPRECALC | cmm_flags;DEBUG_LCMS_MEM(( Create Link Start:: mupdf ctx %p lcms ctx %p src %p des %p \n, (void*)ctx, (void*)cmm_ctx, (void*)src-cmm_handle, (void*)dst-cmm_handle));/* src */src_cs cmsGetColorSpace(cmm_ctx, src-cmm_handle);lcms_src_cs _cmsLCMScolorSpace(cmm_ctx, src_cs);if (lcms_src_cs 0)lcms_src_cs 0;src_num_chan cmsChannelsOf(cmm_ctx, src_cs);src_data_type (COLORSPACE_SH(lcms_src_cs) | CHANNELS_SH(src_num_chan) | DOSWAP_SH(src-bgr) | SWAPFIRST_SH(src-bgr (src_extras ! 0)) | BYTES_SH(num_bytes) | EXTRA_SH(src_extras));/* dst */des_cs cmsGetColorSpace(cmm_ctx, dst-cmm_handle);lcms_des_cs _cmsLCMScolorSpace(cmm_ctx, des_cs);if (lcms_des_cs 0)lcms_des_cs 0;des_num_chan cmsChannelsOf(cmm_ctx, des_cs);des_data_type (COLORSPACE_SH(lcms_des_cs) | CHANNELS_SH(des_num_chan) | DOSWAP_SH(dst-bgr) | SWAPFIRST_SH(dst-bgr (dst_extras ! 0)) | BYTES_SH(num_bytes) | EXTRA_SH(dst_extras));/* flags */if (rend-bp)flag | cmsFLAGS_BLACKPOINTCOMPENSATION;if (copy_spots)flag | cmsFLAGS_COPY_ALPHA;link-depth num_bytes;link-src_extras src_extras;link-dst_extras dst_extras;link-copy_spots copy_spots;if (prf NULL){link-cmm_handle cmsCreateTransformTHR(cmm_ctx, src-cmm_handle, src_data_type, dst-cmm_handle, des_data_type, rend-ri, flag);if (!link-cmm_handle)fz_throw(ctx, FZ_ERROR_GENERIC, cmsCreateTransform failed);}
}
颜色空间转换句柄生成核心函数fz_lcms_init_link依然是调用lcms接口cmsCreateTransformTHR它需要输入源颜色空间lcms句柄源颜色空间数据格式关于数据格式lcms提供了宏定义这里主要使用了颜色空间值COLORSPACE_SH通道数CHANNELS_SH采样比特多少字节的颜色值BYTES_SH。颜色空间和通道数的获取也是使用了lcms接口cmsGetColorSpace_cmsLCMScolorSpace和cmsChannelsOf。
转换句柄生成之后就要调用icc_conv_color进行颜色转换
void
fz_lcms_transform_color(fz_cmm_instance *instance, fz_icclink *link, unsigned short *dst, const unsigned short *src)
{cmsContext cmm_ctx (cmsContext)instance;cmsHTRANSFORM hTransform (cmsHTRANSFORM) link-cmm_handle;cmsDoTransform(cmm_ctx, hTransform, src, dst, 1);
}
icc_conv_color调用了fz_lcms_transform_color其内部调用的cmsDoTransform也是lcms接口结束之后一次颜色和颜色空间的转换也结束了最后生成和目标颜色空间对应的颜色值。
3 总结 对于Mupdf颜色转换总体过程总结如下 颜色空间初始化-初始化mupdf定义的5种颜色空间-判断是否icc模式如果是-使用mupdf定义pro文件初始化数据-调用lcms函数生成profile句柄。 颜色空间转换-生成转换器-判断是否icc模式如果是)-创建icc转换句柄-调用lcms生成句柄-根据转换器转换颜色空间-调用lcms转换接口。 以上都是基于icc模式的流程对于非icc模式流程则简单许多不需要调用lcms接口直接使用mupdf颜色值转换即可流程也和icc差不多这里不做介绍 上面介绍了MUPDF颜色转换的全部流程但是还有很多细节没有写到还有非icc模式的转换颜色空间hash表去重存储颜色空间md5比对颜色值预处理等。