Reputation: 351
I am currently using Cairo for 8-bit rendering with the following code in my After Effects plug-in, which works just fine using the CAIRO_FORMAT_ARGB32 image surface:
// Write pixel 8-bit
PF_Pixel* sampleIntegral8(PF_EffectWorld& def, int x, int y) {
return (PF_Pixel*)((char*)def.data +
(y * def.rowbytes) +
(x * sizeof(PF_Pixel)));
}
PF_Err cairoCopy8(void* refcon, A_long threadInd, A_long itemNum,
A_long iterTotal)
{
cairoRefcon* data = (cairoRefcon*)refcon;
int i = itemNum;
PF_Err err = PF_Err_NONE;
uint32_t* rowP;
PF_Pixel8* worldPtr = sampleIntegral8(data->output, 0, i);
rowP = (uint32_t*)(data->data + i * data->stride);
for (int x = 0; x < data->output.width; x++) {
worldPtr->alpha = rowP[x] >> 24;
if (worldPtr->alpha)
{
float rf = A_u_char(rowP[x] >> 16) / (float)worldPtr->alpha;
float gf = A_u_char(rowP[x] >> 8) / (float)worldPtr->alpha;
float bf = A_u_char(rowP[x] >> 0) / (float)worldPtr->alpha;
worldPtr->red = A_u_char(rf * PF_MAX_CHAN8);
worldPtr->green = A_u_char(gf * PF_MAX_CHAN8);
worldPtr->blue = A_u_char(bf * PF_MAX_CHAN8);
}
worldPtr++;
}
return err;
}
However, I would like to be able to use the CAIRO_FORMAT_RGBA128F image surface which uses 4 floats, and therefore allows much more flexible 32-bit color. There is info about this here: https://cairographics.org/manual/cairo-Image-Surfaces.html#cairo-format-t
The problem is that I have tried adapting the 8-bit code in many different ways without success to use floats and, unfortunately, I couldn't find any examples on how to use this floating point image surface. I get lots of crashing with any attempt I make and I believe it is partly due to the fact that the code needs to be radically adjusted but, again, without any existing examples, it's very tough to do.
Would anyone be able to help adapt the code below using floats for the CAIRO_FORMAT_RGBA128F image surface?
Upvotes: 0
Views: 57
Reputation: 9877
I couldn't find any examples on how to use this floating point image surface
I gave it a go and the result seems to work immediately. This program paints something random to an image surface and then prints its contents. Once via an ARGB32 surface and once with RGBA128F.
#include <cairo.h>
#include <stdio.h>
#include <stdint.h>
//! Draw something to the surface
static void fill_it(cairo_surface_t *s) {
cairo_t *cr = cairo_create(s);
cairo_set_source_rgb(cr, 1, 0.75, 0);
cairo_paint(cr);
cairo_rectangle(cr, 0, 0, 1, 1);
cairo_set_source_rgb(cr, 0.5, 0, 0);
cairo_fill(cr);
cairo_rectangle(cr, 1, 0, 1, 1);
cairo_set_source_rgb(cr, 0.3333, 1, 0);
cairo_fill(cr);
cairo_rectangle(cr, 0, 1, 1, 1);
cairo_set_source_rgb(cr, 0, 0.1234, 1);
cairo_fill(cr);
cairo_destroy(cr);
}
static float uint8_to_float(uint8_t value) {
return value * 1.0 / 255.0;
}
static void print_it_argb32(cairo_surface_t *s) {
int width = cairo_image_surface_get_width(s);
int height = cairo_image_surface_get_width(s);
int stride = cairo_image_surface_get_stride(s);
unsigned char* data = cairo_image_surface_get_data(s);
for (int row = 0; row < height; row++) {
for (int column = 0; column < width; column++) {
uint32_t pixel = ((uint32_t*) data)[column];
printf("Pixel r=%g g=%g b=%g a=%g\n", uint8_to_float(pixel >> 16), uint8_to_float(pixel >> 8), uint8_to_float(pixel), uint8_to_float(pixel >> 24));
}
data += stride;
}
}
static void print_it_rgba128(cairo_surface_t *s) {
int width = cairo_image_surface_get_width(s);
int height = cairo_image_surface_get_width(s);
int stride = cairo_image_surface_get_stride(s);
unsigned char* data = cairo_image_surface_get_data(s);
for (int row = 0; row < height; row++) {
for (int column = 0; column < width; column++) {
float* pixels = ((float*) data) + column * 4;
printf("Pixel r=%g g=%g b=%g a=%g\n", pixels[0], pixels[1], pixels[2], pixels[3]);
}
data += stride;
}
}
static void print_it(cairo_surface_t *s) {
switch (cairo_image_surface_get_format(s)) {
case CAIRO_FORMAT_ARGB32:
return print_it_argb32(s);
case CAIRO_FORMAT_RGBA128F:
return print_it_rgba128(s);
default:
puts("Unsupported format");
}
}
static void try_it(cairo_format_t format) {
cairo_surface_t *s = cairo_image_surface_create(format, 2, 2);
fill_it(s);
print_it(s);
cairo_surface_destroy(s);
puts("");
}
int main() {
try_it(CAIRO_FORMAT_ARGB32);
try_it(CAIRO_FORMAT_RGBA128F);
}
Would anyone be able to help adapt the code below using floats for the CAIRO_FORMAT_RGBA128F image surface?
Well... random guess would be:
// Write pixel 8-bit
PF_Pixel* sampleIntegral8(PF_EffectWorld& def, int x, int y) {
return (PF_Pixel*)((char*)def.data +
(y * def.rowbytes) +
(x * sizeof(PF_Pixel)));
}
PF_Err cairoCopy8(void* refcon, A_long threadInd, A_long itemNum,
A_long iterTotal)
{
cairoRefcon* data = (cairoRefcon*)refcon;
int i = itemNum;
PF_Err err = PF_Err_NONE;
float* rowP;
PF_Pixel8* worldPtr = sampleIntegral8(data->output, 0, i);
rowP = (float*)(data->data + i * data->stride);
for (int x = 0; x < data->output.width; x++) {
float red = rowP[4*x + 0];
float green = rowP[4*x + 1];
float blue = rowP[4*x + 2];
float alpha = rowP[4*x + 3];
worldPtr->alpha = alpha * 255.0;
if (worldPtr->alpha)
{
float rf = red / alpha;
float gf = green / alpha;
float bf = blue / alpha;
worldPtr->red = A_u_char(rf * PF_MAX_CHAN8);
worldPtr->green = A_u_char(gf * PF_MAX_CHAN8);
worldPtr->blue = A_u_char(bf * PF_MAX_CHAN8);
}
worldPtr++;
}
return err;
}
But note that I have no clue what half of these lines are supposed to be.
I added a multiplication of alpha by 255 since I assume that is what PF_Pixel8
expects. At least it seems to have gotten a value in that range before.
The color components do not seem to need such a multiplication since the division by alpha corrects automatically for the different value range.
Upvotes: 0