如何在AE插件中使用Cairo的CAIRO_FORMAT_RGBA128F实现32位色彩输出
适配CAIRO_FORMAT_RGBA128F的After Effects插件代码改写
首先明确核心差异:
- CAIRO_FORMAT_RGBA128F每个像素由**4个64位浮点数(double)**组成,顺序为
R, G, B, A,分量范围0.0~1.0 - 需要切换使用After Effects的
PF_PixelFloat类型处理32位浮点色彩通道,避免精度损失 - 内存访问必须严格对齐,否则会触发崩溃
改写后的完整代码
// 针对浮点像素的采样函数,返回PF_PixelFloat指针 PF_PixelFloat* sampleIntegralFloat(PF_EffectWorld& def, int x, int y) { return reinterpret_cast<PF_PixelFloat*>( reinterpret_cast<char*>(def.data) + (y * def.rowbytes) + (x * sizeof(PF_PixelFloat)) ); } PF_Err cairoCopyRGBA128F(void* refcon, A_long threadInd, A_long itemNum, A_long iterTotal) { cairoRefcon* data = reinterpret_cast<cairoRefcon*>(refcon); int rowIdx = static_cast<int>(itemNum); PF_Err err = PF_Err_NONE; // 确保输出是浮点格式的PF_EffectWorld if (data->output.pixel_format != PF_PixelFormat_FLOAT32) { return PF_Err_BAD_FORMAT; } // Cairo RGBA128F的行指针:每个像素是4个double,顺序R/G/B/A double* cairoRowPtr = reinterpret_cast<double*>( data->data + rowIdx * data->stride ); // AE输出的行指针 PF_PixelFloat* aeRowPtr = sampleIntegralFloat(data->output, 0, rowIdx); const int width = static_cast<int>(data->output.width); for (int x = 0; x < width; ++x) { // 读取Cairo的浮点分量 double cairoR = cairoRowPtr[x * 4 + 0]; double cairoG = cairoRowPtr[x * 4 + 1]; double cairoB = cairoRowPtr[x * 4 + 2]; double cairoA = cairoRowPtr[x * 4 + 3]; // 处理预乘/非预乘转换(与原8位代码逻辑保持一致) if (cairoA > 1e-6) { // 避免除以极小值 aeRowPtr[x].red = static_cast<float>(cairoR / cairoA); aeRowPtr[x].green = static_cast<float>(cairoG / cairoA); aeRowPtr[x].blue = static_cast<float>(cairoB / cairoA); } else { aeRowPtr[x].red = aeRowPtr[x].green = aeRowPtr[x].blue = 0.0f; } aeRowPtr[x].alpha = static_cast<float>(cairoA); // 约束AE浮点通道值在有效范围(0.0~1.0) aeRowPtr[x].red = std::clamp(aeRowPtr[x].red, 0.0f, 1.0f); aeRowPtr[x].green = std::clamp(aeRowPtr[x].green, 0.0f, 1.0f); aeRowPtr[x].blue = std::clamp(aeRowPtr[x].blue, 0.0f, 1.0f); aeRowPtr[x].alpha = std::clamp(aeRowPtr[x].alpha, 0.0f, 1.0f); } return err; }
关键注意事项(崩溃排查要点)
- 输出格式初始化:必须在插件的
SetRenderFormat或初始化阶段,将output的pixel_format设置为PF_PixelFormat_FLOAT32,否则内存访问会错位崩溃 - Cairo Surface创建:创建RGBA128F表面时,必须使用正确的stride,推荐直接让Cairo计算:
cairo_surface_t* surface = cairo_image_surface_create( CAIRO_FORMAT_RGBA128F, width, height ); int stride = cairo_image_surface_get_stride(surface); - 内存对齐:RGBA128F要求内存按8字节对齐,Cairo创建的surface会自动满足,但如果是手动分配内存,必须使用
aligned_alloc或平台对齐函数 - 线程安全:AE的线程回调按行分配任务,只要保证每行数据独立访问,就不会出现线程冲突问题
内容的提问来源于stack exchange,提问作者Rich95
相关产品推荐
相关产品推荐

